Compare commits
18 Commits
df13a90107
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 54bcf7d8ae | |||
| 287d0f678a | |||
| 6807f52ac9 | |||
| ed96ac35c8 | |||
| 33239700fd | |||
| 79591e66be | |||
| 10b7ccdeb0 | |||
| d7c52e12d7 | |||
| ab5b00470c | |||
| 622b30733e | |||
| f13286aaff | |||
| f23f1930e9 | |||
| 55a50eb3d5 | |||
|
|
679edf08ba | ||
| e71140d8a2 | |||
| 5c7dbf7286 | |||
| 9f03dc27cc | |||
| d669d53a80 |
@@ -108,7 +108,13 @@
|
||||
"Bash(\"C:\\\\software\\\\apache-maven-3.8.9\\\\bin\\\\mvn.cmd\" -pl rdms-project/rdms-project-boot -am compile -DskipTests)",
|
||||
"Bash(grep -E \"\\\\.\\(sql|java|md\\)$\")",
|
||||
"Bash(xargs grep -l \"INSERT INTO.*system_menu\")",
|
||||
"Bash(Get-ChildItem *)"
|
||||
"Bash(Get-ChildItem *)",
|
||||
"Bash(Select-Object FullName)",
|
||||
"PowerShell($env:JAVA_HOME='C:\\\\Program Files\\\\Java\\\\jdk-17'; & 'C:\\\\software\\\\apache-maven-3.8.9\\\\bin\\\\mvn.cmd' -e -pl rdms-project/rdms-project-boot test \"-Dtest=ProjectExecutionServiceImplTest#changeExecutionStatus_whenCompleteTransitionMissing_shouldThrow\" 2>&1 | Select-String -Pattern \"BUILD|Tests run|FAIL|ERROR|passed\" | Select-Object -First 20)",
|
||||
"PowerShell($env:JAVA_HOME='C:\\\\Program Files\\\\Java\\\\jdk-17'; & 'C:\\\\software\\\\apache-maven-3.8.9\\\\bin\\\\mvn.cmd' -q -pl rdms-project/rdms-project-boot -am compile 2>&1 | Select-Object -Last 20)",
|
||||
"PowerShell($env:JAVA_HOME='C:\\\\Program Files\\\\Java\\\\jdk-17'; & 'C:\\\\software\\\\apache-maven-3.8.9\\\\bin\\\\mvn.cmd' -e -pl rdms-project/rdms-project-boot test -Dtest=ProjectServiceImplTest 2>&1 | Select-Object -Last 40)",
|
||||
"Bash(xargs wc -l)",
|
||||
"Bash(C:/software/mysql-8.4.9-winx64/bin/mysql.exe *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
72
CLAUDE.md
72
CLAUDE.md
@@ -9,12 +9,14 @@
|
||||
- 描述仓库现状以**当前**代码、配置、文档可验证的事实为准;不要拿历史实现、过渡方案或已废弃模型解释当前状态。
|
||||
- **输出极简**:先给结论、改动点、必要风险;用自然语言给判断和影响面,少贴代码片段;涉及代码用 `file_path:line_number` 引用;用户追问再展开。
|
||||
- **下定论需要充足证据**。疑似 bug 时先判断是否稳定复现:跑了很久没动过的功能**首次**报错,优先怀疑运行时状态污染(devtools / IDE 热替换、ApplicationContext 残留、缓存、Redis / DB 连接、JVM 静态字段被旧 context 设过等),**不要凭单次堆栈就断言代码 bug,更不要直接甩修改方案**。先给"可能原因 + 最便宜的取证步骤"(多数场景是**冷重启 JVM**),用户确认能稳定复现,再讨论代码层面的修复。同款写法在仓库其它位置存在并不能反推"也是 bug",长期能跑的代码突然失效 ≠ 代码本身错。
|
||||
- **技术风险判断(性能 / N+1 / 索引缺失 / 架构缺陷 / 并发安全 / 内存泄漏 等)与"bug 判断"同等严格**:未读到实现层不下结论。不要凭 subagent 摘要、字段名、注释或印象"顺嘴提一句风险/瓶颈/可能问题"——那也是下结论,**且杀伤力更大**:用户会基于"风险提示"决定要不要立项整改。如果当前上下文没核实到实现,就明说"这部分未核实,需要打开 X 文件确认",不要把猜测包装成"风险提示"塞出去。已识别教训:执行进度查询答完"已批量聚合无 N+1"后又凭印象抛"列表 N+1 风险",被追问才收回。
|
||||
|
||||
## 本机环境
|
||||
|
||||
- JDK:必须使用 `JDK 17`,路径 `C:\Program Files\Java\jdk-17`。不要使用 JDK 8 / 11 / 其他版本。
|
||||
- Maven:`C:\software\apache-maven-3.8.9`,命令优先用完整路径 `C:\software\apache-maven-3.8.9\bin\mvn.cmd`。不要假设 `mvn` 在 PATH。
|
||||
- 执行任何 Maven / java 命令前,先确认当前 shell 的 `JAVA_HOME` 指向 JDK 17,且 `java -version` 输出 17;否则在该命令上下文中显式切换。
|
||||
- MySQL 客户端:本机已装命令行客户端 `C:\software\mysql-8.4.9-winx64\bin\mysql.exe`,Agent 可直接连库跑 SQL(查数据、核对状态、验证库结构),无需另装工具。连接账密**不写进本文件**,以 `application-local.yaml` 的 `spring.datasource` 段为准(开发库 `192.168.1.22:13306`/`rdms_view`);**演示库是另一套,账密以用户当场给的为准**。**读随便跑;写库(DDL/DML)务必先确认是哪套库**,开发库改动按本文件§数据与 SQL 出演示库补丁,绝不擅自改演示库。调用细节(PowerShell 必须逐参数加单引号否则 host 被拆、字符集、常用查询)见 [`docs/agent/MySQL客户端连库手册.md`](./docs/agent/MySQL客户端连库手册.md)。
|
||||
|
||||
## 仓库结构
|
||||
|
||||
@@ -47,7 +49,6 @@
|
||||
不要:
|
||||
- 把后续业务长期堆进 `rdms-system`。
|
||||
- 为新增子域引入一套平行的 `application/domain/infrastructure/adapter` 分层。
|
||||
- 让外部模块直接依赖 `*-boot` 的 service 或 mapper(必须走 `*-api`)。
|
||||
|
||||
## 分层职责
|
||||
|
||||
@@ -63,7 +64,7 @@
|
||||
## 认证与跨模块调用
|
||||
|
||||
- 默认沿用 OAuth2 / Token / `LoginUser` / `login-user` 透传主链。**不要**另造 ThreadLocal / Session / 自定义 header。
|
||||
- 业务逻辑落 `*-boot`;可复用契约落 `*-api`;可复用框架能力落 `rdms-framework`。跨模块/跨服务必须通过 `*-api` 定义契约,**不要直接依赖别人的 `*-boot`**;改跨模块 API 时,`*-boot` 实现与对应 `*-api` 契约同步更新。
|
||||
- 业务逻辑落 `*-boot`;可复用契约落 `*-api`;可复用框架能力落 `rdms-framework`。跨模块/跨服务必须通过 `*-api` 定义契约,**不要直接依赖别人 `*-boot` 的 service 或 mapper(必须走 `*-api`)**;改跨模块 API 时,`*-boot` 实现与对应 `*-api` 契约同步更新。
|
||||
|
||||
### 鉴权:必须按"全域 / 对象域"分通道挂
|
||||
|
||||
@@ -79,10 +80,19 @@
|
||||
- **对象内接口绝不能挂 `@PreAuthorize("@ss.hasPermission(...)")`**。该注解走的链路在 `PermissionServiceImpl` 里强制按 GLOBAL 取角色(line 343-347)+ 强制按 GLOBAL 查菜单(line 92-94),对象域角色与对象域菜单都进不来,即使授权配置完全正确也必然 403。
|
||||
- **对象域权限校验必须落在 Service 层 `@CheckObjectPermission`**,原因:路径里 `objectId` 通常以 `#projectId`/`#productId` 等 SpEL 解析,Controller 的参数校验前置阶段不便复用;与同模块(`ProjectMemberServiceImpl` / `ProjectExecutionServiceImpl` / `ProjectExecutionAssigneeServiceImpl` / `ProjectTaskServiceImpl`)保持一致。
|
||||
- **同一接口不要两条通道叠加**。要么全域,要么对象域;叠加只会让对象域用户被全域那条卡死。
|
||||
- 列表/详情这类对象内**读路径**目前未挂 `@CheckObjectPermission`(属已识别负债,台账 TD-001),新增读接口暂沿用现状即可,不要顺手改造,等独立立项。
|
||||
- 对象内**读路径**(列表 / 详情 / 状态看板 / 聚合)同样要挂 `@CheckObjectPermission(objectType=PROJECT, permission=...PERMISSION_QUERY)`——查询同样扫库耗资源,必须按对象域鉴权。**Controller 方法层一律不挂权限注解**,新增读接口照此在 Service 层挂对象域权限;不要只在 Controller 留空,更不要误判"Controller 没注解 = 无鉴权"。
|
||||
|
||||
判定口诀:**URL 里有 `{projectId}` / `{productId}` 等对象 ID → 对象域;没有 → 全域**。
|
||||
|
||||
### 站内信通知事件(project 域统一入口)
|
||||
|
||||
业务动作要发站内信时,统一走 `framework/notify` 的 `NotifySendEvent`(`publishEvent(NotifySendEvent.of(userIds, templateCode, params))`),由全局唯一的 `NotifySendEventListener`(`@TransactionalEventListener(AFTER_COMMIT)`)做去重 / 兜底 / 调 `NotifyMessageSendApi`;不要绕过事件直接在业务 Service 里注入发送 API。
|
||||
|
||||
红线:
|
||||
|
||||
- **`NotifySendEvent` 只能在 `@Transactional` 的业务方法里 publish**。`@TransactionalEventListener` 的语义是"只在有事务的上下文里 publish 才会被消费"——在无事务方法里 publish,通知会**静默不发、不报错**。新业务接入通知前先确认触发点方法有事务;确实无事务的场景不要"顺手 publish",要么补事务,要么找用户确认方案。
|
||||
- 模板场景码登记进 `NotifyTemplateCodeConstants`,并保证 `system_notify_template` 已配同 code 模板(模板缺失时监听器只 `log.warn`,该场景通知发不出但不影响业务)。
|
||||
|
||||
## 接口语义(HTTP 动词)
|
||||
|
||||
本仓库 update 类接口默认按 RESTful 标准用 HTTP 动词区分语义,前后端必须按下表对齐,避免"前端没传字段"和"前端想清空"在后端无法区分的歧义。
|
||||
@@ -99,8 +109,32 @@
|
||||
- 新增 update 接口时,必须在 API 文档对应章节明示"PUT 全字段回传"约定;DO 上对允许 null 的字段补 `FieldStrategy.ALWAYS` 注解,并加注释说明语义来源(指向本节)。
|
||||
- 历史接口若是稀疏 PATCH 风格(传 null = 不动),保留现状但不要拓展;遇到清空诉求时按 PUT 方向重构。
|
||||
|
||||
## 用户可见错误文案(状态机动作 / 状态校验)
|
||||
|
||||
凡"状态机动作 / 状态校验失败"的业务异常,若 `message` 会被前端直接 toast 给用户:**不要把动作 / 状态的内部 code 塞进 message**(如"不支持动作【complete】")。给用户看中文展示名,技术诊断(原始 code / 入参 / 堆栈)由 `infra_api_access_log` 承载。
|
||||
|
||||
红线:
|
||||
|
||||
- service 注入 `StatusActionTextResolver`(`rdms-project-boot · service/status`),抛错前用 `actionName / statusName` 把 code 翻成中文名再填错误码占位;中文名权威源是 `rdms_object_status_transition.action_name` / `rdms_object_status_model.status_name`,**不在代码里硬编码 code→中文 映射**。
|
||||
- 错误码文案用「{}」占位中文名(正面样板 `PERSONAL_ITEM_STATUS_ACTION_NOT_ALLOWED`),不要把 code 写死进文案。
|
||||
- 新对象类型只要在状态机两张表配好 `status_name` / `action_name`,resolver 自动生效,无需改代码。
|
||||
|
||||
完整规范、落地清单与已知缺口(加班申请 `OvertimeApplicationServiceImpl` 待补)见 [`docs/architecture/用户可见错误文案规范.html`](./docs/architecture/用户可见错误文案规范.html)。来源 TD-012。
|
||||
|
||||
## 数据与 SQL
|
||||
|
||||
### 🔴 演示库同步补丁(上线前最高优先级,每次开发都要做)
|
||||
|
||||
项目已临近上线测试,**开发库与演示库两套数据库并存**:日常开发只改了开发库,演示库要靠 SQL 补丁手工同步。一旦补丁缺失或漏项,演示库就会因数据结构 / 字典 / 权限缺失导致功能异常。因此——
|
||||
|
||||
- **每次开发引起的任何数据库变动,都必须同步产出一个可直接运行的 SQL 补丁文件**,交给用户拿到演示库执行。覆盖范围:表结构(DDL)、数据字典(`system_dict_type` / `system_dict_data`)、菜单 / 角色 / 权限(`system_menu` 及权限相关表)、新功能初始化数据、索引等——**只要开发库动了,补丁就必须有**。
|
||||
- **落地位置与命名**:`docs/sql/patches/`,命名 `YYYY-MM-DD-功能名-NN.sql`(每次变更一个独立文件;当天同一功能多次变更递增 `NN` 序号)。沿用 `docs/sql/**` 不主动提交 git 的惯例。
|
||||
- **可直接执行**:纯 SQL、自包含,不依赖 IDE / 迁移工具;用户复制即可在演示库一次跑通。
|
||||
- **幂等可重复**:演示库可能已有部分数据,补丁必须可重复执行不报错(`NOT EXISTS` / `IF NOT EXISTS` 守卫;雪花 id 表按下文「种子 SQL」规则显式取 id)。
|
||||
- **只含增量**:仅本次变动,不 dump 全库、不夹带无关数据。
|
||||
- 补丁写法严格遵守下文「种子 SQL」小节(雪花 id 取值、collation 1267 陷阱)。
|
||||
- **收尾必做**:完成任何涉及 DB 的开发后,主动告诉用户「本次演示库补丁:`docs/sql/patches/xxx.sql`」并简述其内容,不要等用户来问。
|
||||
|
||||
- 新表 DO 复用现有 `BaseDO` / 审计字段风格,不要再引一套审计基类(除非该表本身明确不需要逻辑删除)。
|
||||
- **不要假设运行时自动数据库迁移**:依赖新表/新字段/新索引时,必须同步补 SQL 脚本与文档。
|
||||
- SQL 放在目标模块 `src/main/resources/sql/...`,可审阅、可单独执行。
|
||||
@@ -108,13 +142,13 @@
|
||||
|
||||
### 种子 SQL(纯 SQL INSERT 雪花 ID 表)
|
||||
|
||||
`system_dict_type` / `system_dict_data` / `system_menu` 等历史表 id 由 MyBatis-Plus 雪花算法在 Java 层生成,DDL 无 `AUTO_INCREMENT`。纯 SQL 路径(字典种子、菜单种子等)写 INSERT 必须显式提供 id,否则 MySQL 报 `1364 - Field 'id' doesn't have a default value`。
|
||||
`system_dict_type` / `system_dict_data` / `system_menu` 等历史表 id 由 MyBatis-Plus 雪花算法在 Java 层生成,DDL 无 `AUTO_INCREMENT`。纯 SQL 路径(字典种子、菜单种子、演示库补丁)写 INSERT 必须遵守三条红线,否则报错或撞库:
|
||||
|
||||
- **id 取值**:`SET @new_id = (SELECT IFNULL(MAX(id), 0) + 1 FROM xxx_table);` 然后 INSERT `SELECT @new_id, ...`。雪花 ID 单调递增,`MAX+1` 落在已用区间之后,不会与未来 Java 生成的新雪花 ID 冲突。
|
||||
- **多条连续 INSERT**:每条 INSERT **前重新取** `MAX+1`——不要用 `base+1 / +2 / +3` 一次性算多个。配合 `NOT EXISTS` 守卫,部分已存在场景(半路重跑)才不会出现两条共用一个 id。
|
||||
- **collation 1267 陷阱**:仓库历史表 collation 不统一(如 `system_dict_data` 是 `utf8mb4_unicode_ci`,新表 `rdms_task_worklog` 是 `utf8mb4_0900_ai_ci`)。**不要**用 `SET @t = 'xxx'` 存字符串再 `WHERE col = @t`——用户变量自带连接级 collation,与列 collation 撞会报 `1267 Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='`。**对策**:直接展开成字面值,MySQL 字面值会按列 collation 隐式解析,不冲突。
|
||||
- **必须显式给 id**:用 `MAX+1` 取(`SET @new_id = (SELECT IFNULL(MAX(id),0)+1 FROM xxx);`),多条连续 INSERT **每条前重新取**,否则 `1364 - Field 'id' doesn't have a default value`。
|
||||
- **必须幂等**:每条 INSERT 加 `NOT EXISTS` 守卫,可重复执行不重插。
|
||||
- **防 collation 1267**:**不要**用用户变量存字符串再比较(`SET @t='xxx'` 后 `WHERE col=@t`),仓库历史表 collation 不统一会撞 `1267 Illegal mix of collations`;直接展开成字面值。
|
||||
|
||||
样板参考:`docs/sql/rdms_task_worklog.sql:47-50`(菜单种子)+ `docs/sql/rdms_worklog_difficulty_seed.sql`(字典种子)。
|
||||
详细写法、示例与样板(`docs/sql/rdms_project_query_permission.sql`)见 [`docs/agent/种子SQL写法规范.md`](./docs/agent/种子SQL写法规范.md)。
|
||||
|
||||
## 注释与编码
|
||||
|
||||
@@ -124,9 +158,29 @@
|
||||
## 文档输出格式
|
||||
|
||||
- 新写文档默认输出 **HTML 格式**(便于浏览器直接打开、自带样式)。
|
||||
- 例外:`docs/superpowers/` 下保持 markdown(工作流约定)。
|
||||
- 例外:`docs/superpowers/` 与 `docs/agent/` 下保持 **markdown**——前者是工作流约定,后者是专给 Agent 看的操作手册(Agent 读 md 更直接,不需要浏览器样式)。
|
||||
- 历史已有的 markdown 文档不强制迁移;只有新写的按 HTML。
|
||||
|
||||
### 产出落点(每次生成前对照,避免散乱)
|
||||
|
||||
**红线:绝不把生成物放在仓库根或 `docs/` 根目录**——任何文档 / SQL / 草稿必须落到 `docs/` 下对应子目录(完整目录见 [`docs/README.md`](./docs/README.md) 场景表)。历史教训:工作台 API 文档被丢到仓库根 + docs 根导致散乱。
|
||||
|
||||
| 产出类型 | 落点 |
|
||||
|---|---|
|
||||
| 业务功能文档(设计 / 前端 API / 实现说明 / 联调) | `docs/domains/<域>/`(域=system/product/project),**平铺**、命名 `YYYY-MM-DD-功能-类型.html`,**不按功能建子目录** |
|
||||
| superpowers 工作流产物(spec / plan) | `docs/superpowers/specs/` `plans/`(保持 markdown) |
|
||||
| 跨模块约束 / 架构规范 | `docs/architecture/` |
|
||||
| starter / 框架 API 说明 | `docs/modules/` |
|
||||
| 专给 Agent 看的操作手册(从 CLAUDE.md 抽出的长篇 how-to / 命令 / 陷阱) | `docs/agent/`(CLAUDE.md 留红线 + 指针) |
|
||||
| 调研 / 对标 / 可行性 | `docs/research/` |
|
||||
| 负债台账 / 排查经验 | `docs/debt/` |
|
||||
| 演示库同步补丁 | `docs/sql/patches/`(见本文件§数据与 SQL) |
|
||||
| SQL 草稿 | `docs/sql/`(最终迁回模块 `src/main/resources/sql/`) |
|
||||
| **过程草稿 / 待确认清单 / "看一眼"稿** | **`docs/temp/`,定稿后及时删** |
|
||||
|
||||
- 正文格式按上文「文档输出格式」(默认 HTML,`superpowers/` 与 `docs/agent/` 保持 md,历史 md 不强迁)。
|
||||
- 落点对应的子目录有 `README.md` 时,新增文件**同步登记**进该 README 的文件表。
|
||||
|
||||
## 工作规则(执行前对照)
|
||||
|
||||
1. 优先做有边界的模块内改动,避免跨模块扩散。
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
# 工单需求规格说明
|
||||
|
||||
日期:2026-05-22
|
||||
|
||||
## 1. 背景
|
||||
|
||||
`rdms-project` 当前承载项目、产品、需求、执行、任务等核心交付对象。现有代码中产品需求、项目需求已经具备 `sourceType` / `sourceBizId` 来源字段,可以承接来自工单的需求派生关系;执行和任务也已经形成“项目需求 -> 执行 -> 任务”的后续交付链路。
|
||||
|
||||
本需求新增内部工单能力。工单作为独立业务对象存在,不复用需求、执行或任务主表。工单用于记录内部用户提交的诉求,经工单负责人受理后,可按归属类型派生产品需求或项目需求,并通过现有需求链路继续流转到执行、任务。
|
||||
|
||||
## 2. 目标
|
||||
|
||||
1. 支持内部用户创建普通工单或父工单。
|
||||
2. 支持父工单逐步拆分子工单,父工单只汇总,不直接处理。
|
||||
3. 支持普通工单、子工单作为最小处理单位,由指定工单负责人受理、拒绝、处理和关闭。
|
||||
4. 支持工单单归属到一个产品或一个项目。
|
||||
5. 支持产品工单派生产品需求、项目工单派生项目需求。
|
||||
6. 支持有派生需求的工单在全部需求完成后自动关闭。
|
||||
7. 支持无派生需求的工单由工单负责人手动关闭。
|
||||
|
||||
## 3. 非目标
|
||||
|
||||
1. 本期不做外部客户工单,不保留 `sourceChannel`、`externalCustomerName`、`externalContact` 等外部来源字段。
|
||||
2. 本期不做工单编号 `ticketNo`。
|
||||
3. 工单不能直接派生执行或任务。
|
||||
4. 父工单不能受理、拒绝、派生需求或手动关闭。
|
||||
5. 本期不引入流程引擎,不做可配置审批流。
|
||||
6. 本期不自动判断工单是否涉及多个产品/项目,也不自动判断归属产品或项目;这些由录入人员人工选择。
|
||||
|
||||
## 4. 核心概念
|
||||
|
||||
### 4.1 工单形态
|
||||
|
||||
使用 `ticketMode` 表达工单形态:
|
||||
|
||||
| 值 | 含义 | 是否可处理 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `single` | 普通工单 | 是 | 单归属工单,不挂父工单 |
|
||||
| `parent` | 父工单 | 否 | 原始诉求汇总单,可持续拆分子工单 |
|
||||
| `child` | 子工单 | 是 | 挂在父工单下的最小处理单位 |
|
||||
|
||||
不使用 `isParent`。`ticketMode` 比布尔字段更准确,可以区分普通工单、父工单和子工单,避免在父工单尚未创建子工单时无法识别其形态。
|
||||
|
||||
### 4.2 归属类型
|
||||
|
||||
普通工单和子工单必须单归属:
|
||||
|
||||
| `belongType` | 归属对象 | 可派生对象 | 后续链路 |
|
||||
|---|---|---|---|
|
||||
| `product` | 一个产品 | 产品需求 | 产品需求 -> 指派项目 -> 项目需求 -> 执行 -> 任务 |
|
||||
| `project` | 一个项目 | 项目需求 | 项目需求 -> 执行 -> 任务 |
|
||||
|
||||
父工单不填写 `belongType`、`productId`、`projectId`。
|
||||
|
||||
## 5. 业务流程
|
||||
|
||||
### 5.1 总流程图
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A([开始]) --> B[录入人员创建工单]
|
||||
B --> C{ticketMode}
|
||||
|
||||
C -->|parent| P1[父工单: splitting]
|
||||
P1 --> P2[列表操作列拆分子工单]
|
||||
P2 --> C1[创建子工单: pending_accept]
|
||||
P1 --> P3{是否存在子工单且全部为终态}
|
||||
P3 -->|否| P1
|
||||
P3 -->|是| P4[父工单: closed]
|
||||
P4 --> Z([结束])
|
||||
|
||||
C -->|single| S1[普通工单: pending_accept]
|
||||
C1 --> S2[工单负责人待处理]
|
||||
S1 --> S2
|
||||
S2 --> D{是否受理}
|
||||
D -->|否| R[工单: rejected]
|
||||
R --> Z
|
||||
D -->|是| E[工单: processing]
|
||||
|
||||
E --> F{是否派生需求}
|
||||
F -->|否| G[负责人填写处理结论并手动关闭]
|
||||
G --> H[工单: closed]
|
||||
H --> I[触发父工单汇总检查]
|
||||
I --> Z
|
||||
|
||||
F -->|是| J{归属类型}
|
||||
J -->|product| K[派生一个或多个产品需求]
|
||||
J -->|project| L[派生一个或多个项目需求]
|
||||
K --> M[等待派生需求全部完成]
|
||||
L --> M
|
||||
M --> N{全部派生需求完成}
|
||||
N -->|否| E
|
||||
N -->|是| O[系统自动关闭工单: closed]
|
||||
O --> I
|
||||
```
|
||||
|
||||
### 5.2 父工单流程
|
||||
|
||||
1. 录入人员创建父工单,状态为 `splitting`。
|
||||
2. 父工单只记录原始诉求和附件,不进入处理队列。
|
||||
3. 工单列表查询的操作列为父工单提供“拆分子工单”入口。
|
||||
4. 录入人员可以持续新增子工单。
|
||||
5. 父工单至少存在一个子工单,且所有子工单均进入终态后,系统自动关闭父工单。
|
||||
6. 父工单关闭后不再作为处理对象,但可继续作为历史汇总查看。
|
||||
|
||||
### 5.3 普通工单 / 子工单流程
|
||||
|
||||
1. 创建后进入 `pending_accept`。
|
||||
2. 工单负责人判断是否受理。
|
||||
3. 不受理则进入 `rejected`,需要填写拒绝原因。
|
||||
4. 受理后进入 `processing`。
|
||||
5. 处理中可以派生需求,也可以在无派生需求时填写处理结论并手动关闭。
|
||||
6. 一旦存在派生需求,工单不能手动关闭,必须等待全部派生需求完成后自动关闭。
|
||||
|
||||
## 6. 状态模型
|
||||
|
||||
### 6.1 父工单状态
|
||||
|
||||
| 状态 | 含义 | 进入方式 | 退出方式 |
|
||||
|---|---|---|---|
|
||||
| `splitting` | 拆分中 / 汇总中 | 创建父工单 | 所有子工单终态后自动关闭 |
|
||||
| `closed` | 已关闭 | 系统自动关闭 | 终态 |
|
||||
|
||||
父工单不允许进入 `pending_accept`、`rejected`、`processing`。
|
||||
|
||||
### 6.2 普通工单 / 子工单状态
|
||||
|
||||
| 状态 | 含义 | 进入方式 | 退出方式 |
|
||||
|---|---|---|---|
|
||||
| `pending_accept` | 待受理 | 创建普通工单或子工单 | 受理或拒绝 |
|
||||
| `rejected` | 已拒绝 | 工单负责人拒绝 | 终态 |
|
||||
| `processing` | 处理中 | 工单负责人受理 | 手动关闭或自动关闭 |
|
||||
| `closed` | 已关闭 | 手动关闭或派生需求全部完成后自动关闭 | 终态 |
|
||||
|
||||
终态包括 `rejected`、`closed`。
|
||||
|
||||
### 6.3 自动关闭规则
|
||||
|
||||
1. 普通工单 / 子工单存在派生需求时,只有全部派生需求完成后才自动关闭。
|
||||
2. 普通工单 / 子工单不存在派生需求时,允许工单负责人手动关闭,必须填写 `closeResult`。
|
||||
3. 父工单至少存在一个子工单,且所有子工单均为 `rejected` 或 `closed` 后,自动关闭。
|
||||
4. 父工单不直接检查需求完成情况,只汇总子工单终态。
|
||||
|
||||
## 7. 数据模型
|
||||
|
||||
### 7.1 工单主表
|
||||
|
||||
建议表名:`rdms_ticket`
|
||||
|
||||
| 字段 | 类型建议 | 必填规则 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `id` | `bigint` | 是 | 主键 |
|
||||
| `parent_id` | `bigint` | 否 | 父工单 ID;`child` 必填 |
|
||||
| `ticket_mode` | `varchar(32)` | 是 | `single` / `parent` / `child` |
|
||||
| `title` | `varchar(255)` | 是 | 工单标题 |
|
||||
| `description` | `text` | 否 | 工单描述,支持富文本 |
|
||||
| `ticket_type` | `varchar(32)` | 是 | 工单类型,字典 |
|
||||
| `priority` | `varchar(32)` | 否 | 优先级,建议复用需求优先级字典 |
|
||||
| `status_code` | `varchar(32)` | 是 | 工单状态 |
|
||||
| `submitter_id` | `bigint` | 是 | 提交人用户 ID |
|
||||
| `submitter_nickname` | `varchar(64)` | 否 | 提交人昵称快照 |
|
||||
| `owner_id` | `bigint` | `single` / `child` 必填 | 工单负责人用户 ID |
|
||||
| `owner_nickname` | `varchar(64)` | 否 | 工单负责人昵称快照 |
|
||||
| `belong_type` | `varchar(32)` | `single` / `child` 必填 | `product` / `project` |
|
||||
| `product_id` | `bigint` | 产品工单必填 | 归属产品 ID |
|
||||
| `project_id` | `bigint` | 项目工单必填 | 归属项目 ID |
|
||||
| `accept_time` | `datetime` | 否 | 受理时间 |
|
||||
| `reject_reason` | `varchar(500)` | 拒绝时必填 | 拒绝原因 |
|
||||
| `close_time` | `datetime` | 否 | 关闭时间 |
|
||||
| `close_result` | `varchar(1000)` | 手动关闭时必填 | 处理结论 |
|
||||
| `attachments` | `json` | 否 | 附件列表,沿用 `AttachmentItem` |
|
||||
|
||||
审计字段、逻辑删除字段复用现有 `BaseDO` 风格。
|
||||
|
||||
### 7.2 工单需求关联表
|
||||
|
||||
建议表名:`rdms_ticket_requirement_link`
|
||||
|
||||
| 字段 | 类型建议 | 必填规则 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `id` | `bigint` | 是 | 主键 |
|
||||
| `ticket_id` | `bigint` | 是 | 工单 ID |
|
||||
| `target_type` | `varchar(32)` | 是 | `product_requirement` / `project_requirement` |
|
||||
| `target_id` | `bigint` | 是 | 需求 ID |
|
||||
|
||||
不设置 `relationType`。本期关联表只表达“该工单派生了哪些需求”。
|
||||
|
||||
建议唯一约束:`ticket_id + target_type + target_id`。
|
||||
|
||||
## 8. 需求派生规则
|
||||
|
||||
### 8.1 产品工单派生产品需求
|
||||
|
||||
适用条件:
|
||||
|
||||
1. `ticketMode` 为 `single` 或 `child`。
|
||||
2. `belongType = product`。
|
||||
3. 工单状态为 `processing`。
|
||||
|
||||
派生结果:
|
||||
|
||||
1. 创建 `ProductRequirementDO`。
|
||||
2. `productId` 使用工单 `productId`。
|
||||
3. `sourceType = "work_order"`。
|
||||
4. `sourceBizId = ticketId`。
|
||||
5. 标题、描述、优先级、附件、提出人等字段可从工单带入,并允许派生表单二次编辑。
|
||||
6. 写入 `rdms_ticket_requirement_link`,`targetType = product_requirement`。
|
||||
|
||||
后续链路沿用现有产品需求分发到项目的能力。
|
||||
|
||||
### 8.2 项目工单派生项目需求
|
||||
|
||||
适用条件:
|
||||
|
||||
1. `ticketMode` 为 `single` 或 `child`。
|
||||
2. `belongType = project`。
|
||||
3. 工单状态为 `processing`。
|
||||
|
||||
派生结果:
|
||||
|
||||
1. 创建 `ProjectRequirementDO`。
|
||||
2. `projectId` 使用工单 `projectId`。
|
||||
3. `sourceType = "work_order"`。
|
||||
4. `sourceBizId = ticketId`。
|
||||
5. 标题、描述、优先级、附件、提出人等字段可从工单带入,并允许派生表单二次编辑。
|
||||
6. 写入 `rdms_ticket_requirement_link`,`targetType = project_requirement`。
|
||||
|
||||
后续执行和任务沿用现有项目需求、执行、任务链路。
|
||||
|
||||
### 8.3 完成判定
|
||||
|
||||
1. 产品工单只检查由该工单派生的产品需求。
|
||||
2. 项目工单只检查由该工单派生的项目需求。
|
||||
3. 需求完成态应复用现有需求状态模型的终态配置,不在工单逻辑中写死具体状态码。
|
||||
4. 派生需求数量大于 0 且全部完成时,系统自动关闭对应工单。
|
||||
|
||||
## 9. 页面与待办
|
||||
|
||||
### 9.1 我的提交
|
||||
|
||||
展示当前用户提交的工单,包括:
|
||||
|
||||
1. 父工单。
|
||||
2. 普通工单。
|
||||
3. 子工单。
|
||||
|
||||
建议支持按状态、工单类型、归属类型、归属对象、创建时间筛选。
|
||||
|
||||
### 9.2 我的待处理
|
||||
|
||||
展示当前用户负责的普通工单和子工单,不展示父工单。
|
||||
|
||||
筛选条件:
|
||||
|
||||
1. `ownerId = 当前用户`。
|
||||
2. `ticketMode in (single, child)`。
|
||||
3. `statusCode in (pending_accept, processing)`。
|
||||
|
||||
### 9.3 父工单列表操作
|
||||
|
||||
工单列表查询中,父工单行需要在操作列展示“拆分子工单”入口。该入口只对父工单提交人或具备工单管理权限的用户可见。
|
||||
|
||||
点击后进入新增子工单表单,表单需要携带父工单上下文,并要求录入人员填写子工单的归属类型、归属产品/项目、工单负责人、工单类型等处理字段。
|
||||
|
||||
### 9.4 父工单汇总展示
|
||||
|
||||
父工单汇总展示需要包含:
|
||||
|
||||
1. 原始诉求信息。
|
||||
2. 子工单列表。
|
||||
3. 子工单归属产品/项目。
|
||||
4. 子工单负责人。
|
||||
5. 子工单状态。
|
||||
6. 子工单派生需求数量与完成数量。
|
||||
7. 父工单自动关闭结果。
|
||||
|
||||
## 10. 权限规则
|
||||
|
||||
1. 创建工单走全域权限。
|
||||
2. 父工单新增子工单:父工单提交人或具备工单管理权限的用户可操作。
|
||||
3. 普通工单 / 子工单受理、拒绝、手动关闭:工单负责人或具备工单管理权限的用户可操作。
|
||||
4. 派生产品需求时,需要满足产品对象权限。
|
||||
5. 派生项目需求时,需要满足项目对象权限。
|
||||
6. 父工单不能执行受理、拒绝、派生需求、手动关闭动作。
|
||||
|
||||
## 11. 接口建议
|
||||
|
||||
接口路径建议落在 `rdms-project` 模块:
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
| `POST` | `/project/tickets` | 创建普通工单或父工单 |
|
||||
| `POST` | `/project/tickets/{parentId}/children` | 父工单新增子工单 |
|
||||
| `GET` | `/project/tickets/my-submitted/page` | 我的提交 |
|
||||
| `GET` | `/project/tickets/my-pending/page` | 我的待处理 |
|
||||
| `GET` | `/project/tickets/{id}` | 工单详情 |
|
||||
| `POST` | `/project/tickets/{id}/accept` | 受理 |
|
||||
| `POST` | `/project/tickets/{id}/reject` | 拒绝 |
|
||||
| `POST` | `/project/tickets/{id}/close` | 无派生需求时手动关闭 |
|
||||
| `POST` | `/project/tickets/{id}/derive-product-requirement` | 派生产品需求 |
|
||||
| `POST` | `/project/tickets/{id}/derive-project-requirement` | 派生项目需求 |
|
||||
|
||||
创建、更新类接口需要继续遵守仓库 HTTP 动词语义约定。部分动作使用语义化 `POST` 子动作接口,不引入 `PATCH`。
|
||||
|
||||
## 12. 校验规则
|
||||
|
||||
1. `ticketMode = parent` 时,`ownerId`、`belongType`、`productId`、`projectId` 必须为空。
|
||||
2. `ticketMode = single` 时,`ownerId`、`belongType` 必填,且 `productId` / `projectId` 按归属类型二选一。
|
||||
3. `ticketMode = child` 时,`parentId`、`ownerId`、`belongType` 必填,且父工单必须存在且 `ticketMode = parent`。
|
||||
4. 产品工单必须填写 `productId`,不能填写 `projectId`。
|
||||
5. 项目工单必须填写 `projectId`,不能填写 `productId`。
|
||||
6. 只有 `pending_accept` 状态的普通工单 / 子工单可以受理或拒绝。
|
||||
7. 只有 `processing` 状态的普通工单 / 子工单可以派生需求。
|
||||
8. 有派生需求的工单不能手动关闭。
|
||||
9. 无派生需求的 `processing` 工单可以手动关闭,必须填写 `closeResult`。
|
||||
10. 父工单不能手动关闭。
|
||||
11. 父工单至少有一个子工单,且全部子工单终态后才能自动关闭。
|
||||
|
||||
## 13. 错误处理
|
||||
|
||||
建议新增明确错误码覆盖以下场景:
|
||||
|
||||
1. 工单不存在。
|
||||
2. 工单形态非法。
|
||||
3. 父工单不能处理。
|
||||
4. 子工单父级非法。
|
||||
5. 工单归属类型非法。
|
||||
6. 产品工单不能派生项目需求。
|
||||
7. 项目工单不能派生产品需求。
|
||||
8. 工单状态不允许当前动作。
|
||||
9. 有派生需求的工单不能手动关闭。
|
||||
10. 派生需求未全部完成,工单不能自动关闭。
|
||||
|
||||
## 14. 测试重点
|
||||
|
||||
1. 创建父工单后状态为 `splitting`,且不能受理、拒绝、派生需求、手动关闭。
|
||||
2. 父工单可以持续新增多个子工单。
|
||||
3. 子工单必须单归属产品或项目。
|
||||
4. 普通工单 / 子工单创建后进入 `pending_accept`。
|
||||
5. 工单负责人拒绝后进入 `rejected`。
|
||||
6. 工单负责人受理后进入 `processing`。
|
||||
7. 无派生需求的 `processing` 工单可手动关闭。
|
||||
8. 有派生需求的工单不能手动关闭。
|
||||
9. 产品工单只能派生产品需求。
|
||||
10. 项目工单只能派生项目需求。
|
||||
11. 派生需求全部完成后自动关闭工单。
|
||||
12. 所有子工单终态后自动关闭父工单。
|
||||
|
||||
## 15. 风险与约束
|
||||
|
||||
1. 自动关闭依赖需求完成态判定,实施时必须和现有需求状态模型对齐。
|
||||
2. 产品需求分发到项目后的项目需求、执行、任务链路不属于工单直接职责,工单只追踪自己直接派生的需求。
|
||||
3. 父工单允许持续补子工单,会带来“父工单已关闭后是否允许继续补子单”的边界。本规格默认父工单关闭后不再补子单;如需重开父工单,需要单独设计重开动作。
|
||||
4. 现有前端已有 `/ticket/my-submitted`、`/ticket/my-pending` 资源入口,后端接口落地时需要与前端路由和菜单权限同步。
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.njcn.rdms.framework.common.util.json.JsonUtils;
|
||||
import com.njcn.rdms.gateway.util.SecurityFrameworkUtils;
|
||||
import com.njcn.rdms.gateway.util.WebFrameworkUtils;
|
||||
import com.njcn.rdms.module.system.enums.ErrorCodeConstants;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
@@ -30,6 +31,7 @@ import java.util.function.Function;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TokenAuthenticationFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@@ -57,8 +59,16 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public LoginUser load(String token) {
|
||||
String body = checkAccessToken(token).block();
|
||||
return buildUser(body, token);
|
||||
// 仅异步 refresh 走这里(同步链路用 getIfPresent + 直接 checkAccessToken,不触发 load)
|
||||
// 远端 token 已过期/校验失败时吞掉 ServiceException:
|
||||
// 若抛出,会被 Guava 包成 ExecutionException 并由刷新线程池作为 UncaughtException 打到日志,看起来像故障。
|
||||
try {
|
||||
String body = checkAccessToken(token).block();
|
||||
return buildUser(body, token);
|
||||
} catch (ServiceException ex) {
|
||||
log.info("[loginUserCache] 异步刷新忽略 token 校验失败:code={}, msg={}", ex.getCode(), ex.getMessage());
|
||||
return LOGIN_USER_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -14,8 +14,8 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PRODUCT_CODE_DUPLICATE = new ErrorCode(1_008_001_001, "已经存在编码为【{}】的产品");
|
||||
ErrorCode PRODUCT_NAME_DUPLICATE = new ErrorCode(1_008_001_002, "已经存在名称为【{}】的产品");
|
||||
ErrorCode PRODUCT_CODE_NOT_MODIFIABLE = new ErrorCode(1_008_001_003, "产品编码创建后不允许修改");
|
||||
ErrorCode PRODUCT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_001_004, "当前产品状态不支持动作【{}】");
|
||||
ErrorCode PRODUCT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_001_005, "动作【{}】必须填写原因");
|
||||
ErrorCode PRODUCT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_001_004, "当前产品为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode PRODUCT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_001_005, "「{}」操作必须填写原因");
|
||||
ErrorCode PRODUCT_DELETE_NAME_MISMATCH = new ErrorCode(1_008_001_006, "删除确认名称与当前产品名称不一致");
|
||||
ErrorCode PRODUCT_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_001_007, "当前产品状态不允许编辑");
|
||||
ErrorCode PRODUCT_PAUSED_ONLY_ALLOW_LIMITED_UPDATE = new ErrorCode(1_008_001_008, "产品暂停后仅允许修正描述,产品经理请通过产品团队维护");
|
||||
@@ -29,7 +29,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PRODUCT_MANAGER_TRANSFER_SOURCE_INVALID = new ErrorCode(1_008_001_017, "原产品经理信息与当前产品经理不一致");
|
||||
ErrorCode PRODUCT_MANAGER_TRANSFER_ROLE_INVALID = new ErrorCode(1_008_001_018, "原产品经理交接后的角色不能仍为产品经理");
|
||||
ErrorCode PRODUCT_MANAGER_NOT_MODIFIABLE = new ErrorCode(1_008_001_019, "产品主数据编辑不允许直接变更产品经理,请通过产品团队维护");
|
||||
ErrorCode PRODUCT_OBJECT_PERMISSION_DENIED = new ErrorCode(1_008_001_020, "当前用户不具备该产品的操作权限【{}】");
|
||||
ErrorCode PRODUCT_OBJECT_PERMISSION_DENIED = new ErrorCode(1_008_001_020, "您没有该产品的此项操作权限,请联系管理员");
|
||||
ErrorCode PRODUCT_DELETE_CONFIRM_TEXT_INVALID = new ErrorCode(1_008_001_021, "删除确认口令不正确");
|
||||
ErrorCode PRODUCT_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_001_022, "产品状态已发生变化,请刷新后重试");
|
||||
ErrorCode PRODUCT_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_001_023, "产品状态定义不存在或已停用");
|
||||
@@ -38,7 +38,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PRODUCT_INITIAL_TEAM_MEMBER_DUPLICATE = new ErrorCode(1_008_001_025, "初始团队成员存在重复");
|
||||
ErrorCode PRODUCT_INITIAL_TEAM_ROLE_INVALID = new ErrorCode(1_008_001_026, "初始团队中存在非法角色");
|
||||
ErrorCode PRODUCT_MANAGER_TRANSFER_TARGET_ROLE_DUPLICATE = new ErrorCode(1_008_001_027, "原产品经理在该产品已持有目标角色【{}】(含历史失效行),不能直接转交,请先清理后重试");
|
||||
ErrorCode PRODUCT_INTERNAL_ROLE_NOT_CONFIGURED = new ErrorCode(1_008_001_028, "内置产品角色【{}】未在 system_role 找到,请联系管理员");
|
||||
ErrorCode PRODUCT_INTERNAL_ROLE_NOT_CONFIGURED = new ErrorCode(1_008_001_028, "内置产品角色【{}】未配置,请联系管理员");
|
||||
ErrorCode PRODUCT_MEMBER_USER_INVALID = new ErrorCode(1_008_001_029, "产品成员不是有效系统用户");
|
||||
// 批量新增(POST /project/product/{id}/members/batch)专用:同一请求内 userId 重复 / 经理拦截
|
||||
ErrorCode PRODUCT_MEMBER_BATCH_USER_DUPLICATE = new ErrorCode(1_008_001_030, "请勿在批量列表中重复添加同一成员");
|
||||
@@ -48,8 +48,8 @@ public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 产品需求 1-008-002-000 ==========
|
||||
ErrorCode REQUIREMENT_NOT_EXISTS = new ErrorCode(1_008_002_000, "产品需求不存在");
|
||||
ErrorCode REQUIREMENT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_002_001, "当前需求状态不支持动作【{}】");
|
||||
ErrorCode REQUIREMENT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_002_002, "动作【{}】必须填写原因");
|
||||
ErrorCode REQUIREMENT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_002_001, "当前需求为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode REQUIREMENT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_002_002, "「{}」操作必须填写原因");
|
||||
ErrorCode REQUIREMENT_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_002_003, "需求状态已发生变化,请刷新后重试");
|
||||
ErrorCode REQUIREMENT_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_002_004, "当前需求状态不允许编辑");
|
||||
ErrorCode REQUIREMENT_STATUS_NOT_ALLOW_CLOSE = new ErrorCode(1_008_002_005, "只有已验收的需求才能关闭");
|
||||
@@ -85,8 +85,8 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_TYPE_INVALID = new ErrorCode(1_008_002_006, "项目类型不是有效字典值");
|
||||
ErrorCode PROJECT_MANAGER_USER_INVALID = new ErrorCode(1_008_002_007, "项目负责人不是有效系统用户");
|
||||
ErrorCode PROJECT_MEMBER_USER_INVALID = new ErrorCode(1_008_002_008, "项目成员不是有效系统用户");
|
||||
ErrorCode PROJECT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_002_009, "当前项目状态不支持动作【{}】");
|
||||
ErrorCode PROJECT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_002_010, "动作【{}】必须填写原因");
|
||||
ErrorCode PROJECT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_002_009, "当前项目为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode PROJECT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_002_010, "「{}」操作必须填写原因");
|
||||
ErrorCode PROJECT_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_002_011, "项目状态已发生变化,请刷新后重试");
|
||||
ErrorCode PROJECT_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_002_012, "当前项目状态不允许编辑");
|
||||
ErrorCode PROJECT_MEMBER_NOT_EXISTS = new ErrorCode(1_008_002_013, "项目成员不存在");
|
||||
@@ -97,7 +97,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_DELETE_CONFIRM_TEXT_INVALID = new ErrorCode(1_008_002_018, "删除确认口令不正确");
|
||||
ErrorCode PROJECT_DELETE_NAME_MISMATCH = new ErrorCode(1_008_002_019, "删除确认名称与当前项目名称不一致");
|
||||
ErrorCode PROJECT_NOT_ALLOW_DELETE = new ErrorCode(1_008_002_020, "当前项目不允许删除");
|
||||
ErrorCode PROJECT_OBJECT_PERMISSION_DENIED = new ErrorCode(1_008_002_021, "当前用户不具备该项目的操作权限【{}】");
|
||||
ErrorCode PROJECT_OBJECT_PERMISSION_DENIED = new ErrorCode(1_008_002_021, "您没有该项目的此项操作权限,请联系管理员");
|
||||
ErrorCode PROJECT_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_002_022, "项目状态定义不存在或已停用");
|
||||
ErrorCode PROJECT_DIRECTION_INVALID = new ErrorCode(1_008_002_023, "项目方向不是有效字典值");
|
||||
ErrorCode PROJECT_MANAGER_TRANSFER_INFO_REQUIRED = new ErrorCode(1_008_002_024, "切换项目经理时必须同时传入原项目经理用户和交接后角色");
|
||||
@@ -111,12 +111,14 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_INITIAL_TEAM_ROLE_INVALID = new ErrorCode(1_008_002_031, "初始团队中存在非法角色");
|
||||
ErrorCode PROJECT_DIRECTION_NOT_MATCH_PRODUCT = new ErrorCode(1_008_002_032, "项目方向与所属产品方向不一致");
|
||||
ErrorCode PROJECT_MANAGER_TRANSFER_TARGET_ROLE_DUPLICATE = new ErrorCode(1_008_002_033, "原项目经理在该项目已持有目标角色【{}】(含历史失效行),不能直接转交,请先清理后重试");
|
||||
ErrorCode PROJECT_INTERNAL_ROLE_NOT_CONFIGURED = new ErrorCode(1_008_002_034, "内置项目角色【{}】未在 system_role 找到,请联系管理员");
|
||||
ErrorCode PROJECT_INTERNAL_ROLE_NOT_CONFIGURED = new ErrorCode(1_008_002_034, "内置项目角色【{}】未配置,请联系管理员");
|
||||
// 批量新增(POST /project/project/{id}/members/batch)专用:同一请求内 userId 重复 / 经理拦截
|
||||
ErrorCode PROJECT_MEMBER_BATCH_USER_DUPLICATE = new ErrorCode(1_008_002_035, "请勿在批量列表中重复添加同一成员");
|
||||
ErrorCode PROJECT_MEMBER_BATCH_MANAGER_NOT_ALLOWED = new ErrorCode(1_008_002_036, "批量新增不允许指定为经理,请通过编辑成员调整");
|
||||
// 批量移出(POST /project/project/{id}/members/batch/inactive)专用:同一请求内 memberId 重复
|
||||
ErrorCode PROJECT_MEMBER_BATCH_INACTIVE_MEMBER_DUPLICATE = new ErrorCode(1_008_002_037, "请勿在批量移出列表中重复指定同一成员");
|
||||
// 项目完成前置校验(TD-015):complete 时子对象(任务/执行/需求)未全终态。{} 由 Service 动态拼全部缺口,只放中文+数字(TD-012 脱敏)
|
||||
ErrorCode PROJECT_COMPLETE_PRECONDITION_NOT_MET = new ErrorCode(1_008_002_038, "项目无法完成,请先处理:{}");
|
||||
|
||||
// ========== 执行管理 1-008-003-000 ==========
|
||||
ErrorCode PROJECT_EXECUTION_NOT_EXISTS = new ErrorCode(1_008_003_000, "执行不存在");
|
||||
@@ -126,18 +128,17 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_EXECUTION_ASSIGNEE_ALREADY_EXISTS = new ErrorCode(1_008_003_004, "该用户已是当前执行的有效协办人");
|
||||
ErrorCode PROJECT_EXECUTION_ASSIGNEE_NOT_EXISTS = new ErrorCode(1_008_003_005, "执行协办人不存在");
|
||||
ErrorCode PROJECT_EXECUTION_ASSIGNEE_NOT_ACTIVE = new ErrorCode(1_008_003_006, "当前执行协办人已失效");
|
||||
// 保留:TD-013 解锁后业务路径已不会再触发,预留用于灰度回滚关闭关联能力
|
||||
ErrorCode PROJECT_EXECUTION_REQUIREMENT_NOT_READY = new ErrorCode(1_008_003_007, "当前阶段不支持给执行绑定项目需求");
|
||||
// 1_008_003_007 原 PROJECT_EXECUTION_REQUIREMENT_NOT_READY 已随 TD-013 清理删除,号位保留空缺不复用,避免与历史前端映射冲突
|
||||
ErrorCode PROJECT_EXECUTION_NOT_ALLOW_EDIT = new ErrorCode(1_008_003_008, "当前项目状态不允许维护执行");
|
||||
ErrorCode PROJECT_EXECUTION_OWNER_HANDOFF_REQUIRED = new ErrorCode(1_008_003_009, "该项目成员仍担任未终态执行负责人,请先完成执行负责人交接");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_003_010, "执行状态定义不存在或已停用");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_003_011, "当前执行状态不支持动作【{}】");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_003_012, "动作【{}】必须填写原因");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_003_011, "当前执行为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_003_012, "「{}」操作必须填写原因");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_003_013, "执行状态已发生变化,请刷新后重试");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_003_014, "当前执行状态不允许维护执行");
|
||||
ErrorCode PROJECT_EXECUTION_TYPE_INVALID = new ErrorCode(1_008_003_015, "执行类型不是有效字典值");
|
||||
ErrorCode PROJECT_EXECUTION_ASSIGNEE_REQUIRED = new ErrorCode(1_008_003_016, "创建执行时必须至少选择一名执行协办人");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_OWNER_ONLY = new ErrorCode(1_008_003_017, "只有执行负责人才能执行【{}】动作");
|
||||
ErrorCode PROJECT_EXECUTION_STATUS_OWNER_ONLY = new ErrorCode(1_008_003_017, "只有执行负责人才能执行「{}」操作");
|
||||
ErrorCode PROJECT_EXECUTION_COMPLETE_TASKS_REQUIRED = new ErrorCode(1_008_003_018, "完成执行前,执行下所有任务必须全部完成或取消");
|
||||
ErrorCode PROJECT_EXECUTION_NOT_ALLOW_DELETE = new ErrorCode(1_008_003_019, "已完成的执行不允许删除");
|
||||
ErrorCode PROJECT_EXECUTION_DELETE_NAME_MISMATCH = new ErrorCode(1_008_003_020, "确认执行名称与实际不一致");
|
||||
@@ -153,12 +154,12 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_TASK_PARENT_INVALID = new ErrorCode(1_008_004_002, "父任务必须属于当前项目和执行");
|
||||
ErrorCode PROJECT_TASK_NOT_ALLOW_EDIT = new ErrorCode(1_008_004_003, "当前项目或执行状态不允许维护任务");
|
||||
ErrorCode PROJECT_TASK_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_004_004, "任务状态定义不存在或已停用");
|
||||
ErrorCode PROJECT_TASK_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_004_005, "当前任务状态不支持动作【{}】");
|
||||
ErrorCode PROJECT_TASK_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_004_006, "动作【{}】必须填写原因");
|
||||
ErrorCode PROJECT_TASK_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_004_005, "当前任务为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode PROJECT_TASK_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_004_006, "「{}」操作必须填写原因");
|
||||
ErrorCode PROJECT_TASK_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_004_007, "任务状态已发生变化,请刷新后重试");
|
||||
ErrorCode PROJECT_TASK_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_004_008, "当前任务状态不允许维护任务");
|
||||
ErrorCode PROJECT_TASK_COMPLETE_CHILDREN_REQUIRED = new ErrorCode(1_008_004_010, "父任务完成前,子任务必须全部完成或取消");
|
||||
ErrorCode PROJECT_TASK_STATUS_OWNER_ONLY = new ErrorCode(1_008_004_011, "只有任务负责人才能执行【{}】动作");
|
||||
ErrorCode PROJECT_TASK_STATUS_OWNER_ONLY = new ErrorCode(1_008_004_011, "只有任务负责人才能执行「{}」操作");
|
||||
ErrorCode PROJECT_TASK_NOT_ALLOW_DELETE = new ErrorCode(1_008_004_012, "已完成的任务不允许删除");
|
||||
ErrorCode PROJECT_TASK_DELETE_NAME_MISMATCH = new ErrorCode(1_008_004_013, "确认任务名称与实际不一致");
|
||||
ErrorCode PROJECT_TASK_DELETE_CONFIRM_TEXT_INVALID = new ErrorCode(1_008_004_014, "删除确认口令必须为 DELETE 或 删除");
|
||||
@@ -186,17 +187,17 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_TASK_WORKLOG_PROGRESS_NOT_MONOTONIC = new ErrorCode(1_008_006_010, "工时进度与日期顺序不一致:早段进度不得高于晚段、晚段进度不得低于早段");
|
||||
ErrorCode PROJECT_TASK_WORKLOG_DIFFICULTY_INVALID = new ErrorCode(1_008_006_011, "完成难度不在字典范围内");
|
||||
|
||||
// ========== 任务 / 工时附件 1_008_007_xxx ==========
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_TOO_MANY = new ErrorCode(1_008_007_001, "附件数量不能超过 {} 个");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_URL_INVALID = new ErrorCode(1_008_007_002, "附件地址非法,必须为 http/https URL 且长度不超过 1024");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_NAME_INVALID = new ErrorCode(1_008_007_003, "附件文件名不合法(必填且长度不超过 255)");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_TYPE_NOT_ALLOWED = new ErrorCode(1_008_007_004, "附件扩展名【{}】不在允许列表内");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_TYPE_BLOCKED = new ErrorCode(1_008_007_005, "附件类型【{}】被禁止上传");
|
||||
// ========== 任务 / 工时附件 1_008_010_xxx(原 1_008_007 与下方项目需求段撞号,迁至独立号段;新增错误码域请从 1_008_011 起) ==========
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_TOO_MANY = new ErrorCode(1_008_010_001, "附件数量不能超过 {} 个");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_URL_INVALID = new ErrorCode(1_008_010_002, "附件地址非法,必须为 http/https URL 且长度不超过 1024");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_NAME_INVALID = new ErrorCode(1_008_010_003, "附件文件名不合法(必填且长度不超过 255)");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_TYPE_NOT_ALLOWED = new ErrorCode(1_008_010_004, "附件扩展名【{}】不在允许列表内");
|
||||
ErrorCode PROJECT_TASK_ATTACHMENT_TYPE_BLOCKED = new ErrorCode(1_008_010_005, "附件类型【{}】被禁止上传");
|
||||
|
||||
// ========== 项目需求 1_008_007_xxx ==========
|
||||
ErrorCode PROJECT_REQUIREMENT_NOT_EXISTS = new ErrorCode(1_008_007_000, "项目需求不存在");
|
||||
ErrorCode PROJECT_REQUIREMENT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_007_001, "当前项目需求状态不支持动作【{}】");
|
||||
ErrorCode PROJECT_REQUIREMENT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_007_002, "动作【{}】必须填写原因");
|
||||
ErrorCode PROJECT_REQUIREMENT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_007_001, "当前项目需求为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode PROJECT_REQUIREMENT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_007_002, "「{}」操作必须填写原因");
|
||||
ErrorCode PROJECT_REQUIREMENT_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_007_003, "项目需求状态已发生变化,请刷新后重试");
|
||||
ErrorCode PROJECT_REQUIREMENT_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_007_004, "当前项目需求状态不允许编辑");
|
||||
ErrorCode PROJECT_REQUIREMENT_STATUS_NOT_ALLOW_CLOSE = new ErrorCode(1_008_007_005, "只有已验收的项目需求才能关闭");
|
||||
@@ -221,10 +222,39 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PERSONAL_ITEM_NOT_EXISTS = new ErrorCode(1_008_008_001, "个人事项不存在");
|
||||
ErrorCode PERSONAL_ITEM_OWNER_NOT_IN_EXECUTION = new ErrorCode(1_008_008_002, "个人事项负责人必须属于当前有效执行团队成员");
|
||||
ErrorCode PERSONAL_ITEM_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_008_003, "个人事项状态定义不存在或已停用");
|
||||
ErrorCode PERSONAL_ITEM_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_008_004, "当前个人事项状态不支持动作【{}】");
|
||||
ErrorCode PERSONAL_ITEM_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_008_005, "动作【{}】必须填写原因");
|
||||
ErrorCode PERSONAL_ITEM_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_008_004, "当前个人事项为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode PERSONAL_ITEM_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_008_005, "「{}」操作必须填写原因");
|
||||
ErrorCode PERSONAL_ITEM_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_008_006, "个人事项状态已发生变化,请刷新后重试");
|
||||
ErrorCode PERSONAL_ITEM_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_008_007, "当前个人事项状态不允许编辑");
|
||||
ErrorCode PERSONAL_ITEM_NOT_ALLOW_DELETE = new ErrorCode(1_008_008_008, "仅初始态(待开始)的个人事项允许删除");
|
||||
ErrorCode PERSONAL_ITEM_WRITE_FORBIDDEN = new ErrorCode(1_008_008_009, "无权修改个人事项");
|
||||
|
||||
// ========== 加班申请 1_008_009_xxx ==========
|
||||
ErrorCode OVERTIME_APPLICATION_NOT_EXISTS = new ErrorCode(1_008_009_001, "加班申请不存在");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_009_002, "加班申请状态定义不存在或已停用");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_009_003, "当前加班申请为「{}」状态,不支持「{}」操作");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_009_004, "「{}」操作必须填写原因");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_009_005, "加班申请状态已发生变化,请刷新后重试");
|
||||
ErrorCode OVERTIME_APPLICATION_APPLICANT_ONLY = new ErrorCode(1_008_009_006, "仅申请人可执行该操作");
|
||||
ErrorCode OVERTIME_APPLICATION_APPROVER_ONLY = new ErrorCode(1_008_009_007, "仅当前审核人可执行该操作");
|
||||
ErrorCode OVERTIME_APPLICATION_APPROVER_INVALID = new ErrorCode(1_008_009_008, "审核人不是有效系统用户");
|
||||
ErrorCode OVERTIME_APPLICATION_APPROVER_SELF_FORBIDDEN = new ErrorCode(1_008_009_009, "审核人不能选择申请人本人");
|
||||
ErrorCode OVERTIME_APPLICATION_READ_FORBIDDEN = new ErrorCode(1_008_009_010, "无权查看该加班申请");
|
||||
ErrorCode OVERTIME_APPLICATION_DELETE_ONLY_REJECTED = new ErrorCode(1_008_009_011, "仅已退回的加班申请允许删除");
|
||||
// ========== 工作报告 1_008_010_xxx ==========
|
||||
ErrorCode WORK_REPORT_NOT_EXISTS = new ErrorCode(1_008_010_001, "工作报告不存在");
|
||||
ErrorCode WORK_REPORT_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_010_002, "工作报告状态定义不存在或已停用");
|
||||
ErrorCode WORK_REPORT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_010_003, "当前工作报告状态不支持动作【{}】");
|
||||
ErrorCode WORK_REPORT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_010_004, "动作【{}】必须填写原因");
|
||||
ErrorCode WORK_REPORT_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_010_005, "工作报告状态已发生变化,请刷新后重试");
|
||||
ErrorCode WORK_REPORT_REPORTER_ONLY = new ErrorCode(1_008_010_006, "仅填报人可执行该操作");
|
||||
ErrorCode WORK_REPORT_APPROVER_ONLY = new ErrorCode(1_008_010_007, "仅当前审核人可执行该操作");
|
||||
ErrorCode WORK_REPORT_READ_FORBIDDEN = new ErrorCode(1_008_010_008, "无权查看该工作报告");
|
||||
ErrorCode WORK_REPORT_DIRECT_MANAGER_NOT_EXISTS = new ErrorCode(1_008_010_009, "当前用户不存在生效中的直属上级,无法提交工作报告");
|
||||
ErrorCode WORK_REPORT_PROJECT_NOT_EXISTS = new ErrorCode(1_008_010_010, "项目半月报对应的项目不存在");
|
||||
ErrorCode WORK_REPORT_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_010_011, "当前工作报告状态不允许编辑");
|
||||
ErrorCode WORK_REPORT_DELETE_NOT_ALLOWED = new ErrorCode(1_008_010_012, "当前工作报告状态不允许删除");
|
||||
ErrorCode WORK_REPORT_PERIOD_DUPLICATE = new ErrorCode(1_008_010_013, "当前周期的工作报告已存在,请勿重复创建");
|
||||
ErrorCode WORK_REPORT_APPROVAL_RECORD_NOT_EXISTS = new ErrorCode(1_008_010_014, "工作报告审核记录不存在");
|
||||
ErrorCode WORK_REPORT_PROJECT_OWNER_ONLY = new ErrorCode(1_008_010_015, "仅项目负责人可创建或维护该项目的项目半月报");
|
||||
}
|
||||
|
||||
@@ -65,6 +65,12 @@
|
||||
<artifactId>rdms-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Registry 注册中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
|
||||
@@ -39,6 +39,7 @@ public final class ObjectActivityConstants {
|
||||
public static final String PROJECT_ACTION_DELETE = "delete";
|
||||
public static final String PROJECT_ACTION_CHANGE_MANAGER = "change_manager";
|
||||
public static final String PROJECT_ACTION_AUTO_START = "auto_start";
|
||||
public static final String PROJECT_ACTION_COMPLETE = "complete";
|
||||
|
||||
// ========== 项目自动推进触发动作 ==========
|
||||
public static final String PROJECT_TRIGGER_CREATE_EXECUTION = "create_execution";
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.rdms.module.project.constant;
|
||||
|
||||
/**
|
||||
* 加班申请常量。
|
||||
*/
|
||||
public final class OvertimeApplicationConstants {
|
||||
|
||||
private OvertimeApplicationConstants() {
|
||||
}
|
||||
|
||||
public static final String BIZ_TYPE = "overtime_application";
|
||||
public static final String STATUS_OBJECT_TYPE = BIZ_TYPE;
|
||||
|
||||
public static final String STATUS_PENDING = "pending";
|
||||
public static final String STATUS_APPROVED = "approved";
|
||||
public static final String STATUS_REJECTED = "rejected";
|
||||
|
||||
/**
|
||||
* 新建即提交的业务日志动作。
|
||||
* <p>
|
||||
* 该动作仅用于状态日志、审计日志留痕,不要求在 rdms_object_status_transition 中存在显式流转配置。
|
||||
*/
|
||||
public static final String ACTION_SUBMIT = "submit";
|
||||
public static final String ACTION_RESUBMIT = "resubmit";
|
||||
public static final String ACTION_APPROVE = "approve";
|
||||
public static final String ACTION_REJECT = "reject";
|
||||
public static final String ACTION_DELETE = "delete";
|
||||
|
||||
public static final String PERMISSION_QUERY = "project:overtime-application:query";
|
||||
public static final String PERMISSION_CREATE = "project:overtime-application:create";
|
||||
public static final String PERMISSION_UPDATE = "project:overtime-application:update";
|
||||
public static final String PERMISSION_DELETE = "project:overtime-application:delete";
|
||||
public static final String PERMISSION_APPROVE = "project:overtime-application:approve";
|
||||
public static final String PERMISSION_EXPORT = "project:overtime-application:export";
|
||||
|
||||
}
|
||||
@@ -28,6 +28,11 @@ public final class ProductObjectConstants {
|
||||
*/
|
||||
public static final String CODE_PREFIX = "CNPD";
|
||||
|
||||
/**
|
||||
* 产品查询权限码。
|
||||
*/
|
||||
public static final String PERMISSION_QUERY = "project:product:query";
|
||||
|
||||
/**
|
||||
* 产品编辑权限码。
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,13 @@ public final class ProjectExecutionConstants {
|
||||
*/
|
||||
public static final String BIZ_TYPE = "project_execution";
|
||||
|
||||
/**
|
||||
* 执行读路径查询权限码(对象域,object_type='project')。
|
||||
* 覆盖执行对象所有读路径:page / status-board / detail。
|
||||
* "我参与 / 所有"视角由前端发不发 involveUserId 决定;进得来 = 看项目下全部,无此权限码直接 403。
|
||||
*/
|
||||
public static final String PERMISSION_QUERY = "project:execution:query";
|
||||
|
||||
/**
|
||||
* 创建执行权限码。
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,11 @@ public final class ProjectObjectConstants {
|
||||
*/
|
||||
public static final String CODE_PREFIX = "CNPJ";
|
||||
|
||||
/**
|
||||
* 项目查询权限码。
|
||||
*/
|
||||
public static final String PERMISSION_QUERY = "project:project:query";
|
||||
|
||||
/**
|
||||
* 项目编辑权限码。
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.njcn.rdms.module.project.constant;
|
||||
|
||||
/**
|
||||
* 项目需求(project_requirement)相关常量。
|
||||
* object_type 原为 ProjectRequirementServiceImpl 私有常量,TD-015 因项目层完成校验需跨类引用,提升为公共常量。
|
||||
*/
|
||||
public interface ProjectRequirementConstants {
|
||||
|
||||
/** 状态机 / 权限体系中的对象类型标识 */
|
||||
String OBJECT_TYPE = "project_requirement";
|
||||
}
|
||||
@@ -26,6 +26,13 @@ public final class ProjectTaskConstants {
|
||||
*/
|
||||
public static final String BIZ_TYPE = "project_task";
|
||||
|
||||
/**
|
||||
* 任务读路径查询权限码(对象域,object_type='project')。
|
||||
* 覆盖任务对象所有读路径:page / status-board / board-page / detail / summary(含跨执行 aggregate)。
|
||||
* "我参与 / 所有"视角由前端发不发 involveUserId 决定;进得来 = 看项目下全部,无此权限码直接 403。
|
||||
*/
|
||||
public static final String PERMISSION_QUERY = "project:task:query";
|
||||
|
||||
/**
|
||||
* 创建任务权限码。
|
||||
*/
|
||||
@@ -59,18 +66,6 @@ public final class ProjectTaskConstants {
|
||||
*/
|
||||
public static final String PERMISSION_DELETE = "project:task:delete";
|
||||
|
||||
/**
|
||||
* 项目任务"查看全部"权限码(对象域,object_type='project')。
|
||||
*
|
||||
* 用于跨执行视角下"项目全部任务"语义的接口/视图(page / status-board /
|
||||
* board-page / summary 的 scope=all 分支)。无此权限码时,只能看到自己作为
|
||||
* owner 或活跃协办的任务(走 VisibilityScopeResolver.resolveForProject 过滤)。
|
||||
*
|
||||
* 种子绑定:默认绑给项目负责人 + 项目创建人;普通成员、执行负责人默认不绑。
|
||||
* 实际项目中由运维在角色管理界面调整。
|
||||
*/
|
||||
public static final String PERMISSION_LIST_ALL = "project:task:list-all";
|
||||
|
||||
/**
|
||||
* 删除确认口令合法值集合;兼容大写英文 "DELETE" 与中文 "删除",前端可纯中文文案。
|
||||
* 校验时精确匹配(trim 后比对)。
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.rdms.module.project.constant;
|
||||
|
||||
/**
|
||||
* 工作报告常量。
|
||||
*/
|
||||
public final class WorkReportConstants {
|
||||
|
||||
private WorkReportConstants() {
|
||||
}
|
||||
|
||||
public static final String STATUS_OBJECT_TYPE = "work_report";
|
||||
|
||||
public static final String REPORT_TYPE_WEEKLY = "weekly";
|
||||
public static final String REPORT_TYPE_MONTHLY = "monthly";
|
||||
public static final String REPORT_TYPE_PROJECT = "project";
|
||||
|
||||
public static final String BIZ_TYPE_WEEKLY = "weekly_report";
|
||||
public static final String BIZ_TYPE_MONTHLY = "monthly_report";
|
||||
public static final String BIZ_TYPE_PROJECT = "project_report";
|
||||
|
||||
public static final String STATUS_DRAFT = "draft";
|
||||
public static final String STATUS_PENDING_APPROVAL = "pending_approval";
|
||||
public static final String STATUS_APPROVED = "approved";
|
||||
public static final String STATUS_REJECTED = "rejected";
|
||||
|
||||
public static final String ACTION_CREATE = "create";
|
||||
public static final String ACTION_UPDATE = "update";
|
||||
public static final String ACTION_DELETE = "delete";
|
||||
public static final String ACTION_SUBMIT = "submit";
|
||||
public static final String ACTION_RESUBMIT = "resubmit";
|
||||
public static final String ACTION_APPROVE = "approve";
|
||||
public static final String ACTION_REJECT = "reject";
|
||||
|
||||
public static final String PERMISSION_QUERY = "project:work-report:query";
|
||||
public static final String PERMISSION_CREATE = "project:work-report:create";
|
||||
public static final String PERMISSION_UPDATE = "project:work-report:update";
|
||||
public static final String PERMISSION_DELETE = "project:work-report:delete";
|
||||
public static final String PERMISSION_APPROVE = "project:work-report:approve";
|
||||
public static final String PERMISSION_EXPORT = "project:work-report:export";
|
||||
public static final String PERMISSION_PROJECT_OWNER = "project:work-report:project-owner";
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime;
|
||||
|
||||
import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
|
||||
import com.njcn.rdms.module.project.constant.OvertimeApplicationConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationApprovalRecordRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationSaveReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusDictRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusLogRespVO;
|
||||
import com.njcn.rdms.module.project.service.overtime.OvertimeApplicationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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.PathVariable;
|
||||
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.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 加班申请")
|
||||
@RestController
|
||||
@RequestMapping("/project/overtime-applications")
|
||||
@Validated
|
||||
public class OvertimeApplicationController {
|
||||
|
||||
@Resource
|
||||
private OvertimeApplicationService overtimeApplicationService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增加班申请并提交")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<Long> create(@Valid @RequestBody OvertimeApplicationSaveReqVO reqVO) {
|
||||
return success(overtimeApplicationService.createApplication(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "退回后修改并重新提交加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> resubmit(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody OvertimeApplicationSaveReqVO reqVO) {
|
||||
overtimeApplicationService.resubmitApplication(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取加班申请详情")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<OvertimeApplicationRespVO> get(@PathVariable("id") Long id) {
|
||||
return success(overtimeApplicationService.getApplication(id));
|
||||
}
|
||||
|
||||
@GetMapping("/status/dict")
|
||||
@Operation(summary = "获取加班申请所有状态字典")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<OvertimeApplicationStatusDictRespVO>> getStatusDict() {
|
||||
return success(overtimeApplicationService.getStatusDict());
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取我的加班申请分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PageResult<OvertimeApplicationRespVO>> page(@Valid OvertimeApplicationPageReqVO reqVO) {
|
||||
return success(overtimeApplicationService.getMyPage(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/approval-page")
|
||||
@Operation(summary = "获取待我审批的加班申请分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<PageResult<OvertimeApplicationRespVO>> approvalPage(@Valid OvertimeApplicationPageReqVO reqVO) {
|
||||
return success(overtimeApplicationService.getApprovalPage(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审核通过加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> approve(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
overtimeApplicationService.approve(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "审核退回加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> reject(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
overtimeApplicationService.reject(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除已退回的加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_DELETE + "')")
|
||||
public CommonResult<Boolean> delete(@PathVariable("id") Long id) {
|
||||
overtimeApplicationService.deleteApplication(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/status-logs")
|
||||
@Operation(summary = "获取加班申请状态日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<OvertimeApplicationStatusLogRespVO>> statusLogs(@PathVariable("id") Long id) {
|
||||
return success(overtimeApplicationService.getStatusLogs(id));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/approval-records")
|
||||
@Operation(summary = "获取加班申请审核记录")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<OvertimeApplicationApprovalRecordRespVO>> approvalRecords(@PathVariable("id") Long id) {
|
||||
return success(overtimeApplicationService.getApprovalRecords(id));
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
@Operation(summary = "导出我的加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void export(@Valid OvertimeApplicationPageReqVO reqVO, HttpServletResponse response) throws IOException {
|
||||
List<OvertimeApplicationExportVO> list = overtimeApplicationService.getExportList(reqVO);
|
||||
ExcelUtils.write(response, "加班申请_" + LocalDate.now() + ".xls", "加班申请",
|
||||
OvertimeApplicationExportVO.class, list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请审核记录 Response VO")
|
||||
@Data
|
||||
public class OvertimeApplicationApprovalRecordRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long overtimeApplicationId;
|
||||
|
||||
private Long statusLogId;
|
||||
|
||||
private Integer approvalRound;
|
||||
|
||||
private String conclusion;
|
||||
|
||||
private String opinion;
|
||||
|
||||
private Long auditorUserId;
|
||||
|
||||
private String auditorName;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class OvertimeApplicationExportVO {
|
||||
|
||||
@ExcelProperty("申请人")
|
||||
private String applicantName;
|
||||
|
||||
@ExcelProperty("加班日期")
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
@ExcelProperty("加班时长")
|
||||
private String overtimeDuration;
|
||||
|
||||
@ExcelProperty("加班原因")
|
||||
private String overtimeReason;
|
||||
|
||||
@ExcelProperty("加班内容")
|
||||
private String overtimeContent;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("审核人")
|
||||
private String approverName;
|
||||
|
||||
@ExcelProperty("提交时间")
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@ExcelProperty("审核时间")
|
||||
private LocalDateTime approvalTime;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "关键词,匹配加班原因或加班内容", example = "上线")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "申请人姓名,模糊匹配", example = "张三")
|
||||
private String applicantName;
|
||||
|
||||
@Schema(description = "审核人用户编号", example = "1001")
|
||||
private Long approverId;
|
||||
|
||||
@Schema(description = "审核人姓名,模糊匹配", example = "李四")
|
||||
private String approverName;
|
||||
|
||||
@Schema(description = "状态编码", example = "pending")
|
||||
@Size(max = 32, message = "状态编码长度不能超过32个字符")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "加班日期范围", example = "[2026-06-01, 2026-06-30]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate[] overtimeDate;
|
||||
|
||||
@Schema(description = "创建时间", example = "[2026-06-01 00:00:00, 2026-06-30 23:59:59]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请 Response VO")
|
||||
@Data
|
||||
public class OvertimeApplicationRespVO {
|
||||
|
||||
@Schema(description = "加班申请编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9001")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "申请人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3001")
|
||||
private Long applicantId;
|
||||
|
||||
@Schema(description = "申请人姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String applicantName;
|
||||
|
||||
@Schema(description = "加班日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
@Schema(description = "加班时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1天")
|
||||
private String overtimeDuration;
|
||||
|
||||
@Schema(description = "加班原因", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String overtimeReason;
|
||||
|
||||
@Schema(description = "加班内容", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String overtimeContent;
|
||||
|
||||
@Schema(description = "审核人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
|
||||
private Long approverId;
|
||||
|
||||
@Schema(description = "审核人姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||
private String approverName;
|
||||
|
||||
@Schema(description = "状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "pending")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "状态名称", example = "待审批")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "当前状态是否允许编辑", example = "false")
|
||||
private Boolean allowEdit;
|
||||
|
||||
@Schema(description = "是否终态", example = "false")
|
||||
private Boolean terminal;
|
||||
|
||||
@Schema(description = "最近一次审核意见")
|
||||
private String approvalComment;
|
||||
|
||||
@Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@Schema(description = "最近一次审核时间")
|
||||
private LocalDateTime approvalTime;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
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;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请保存 Request VO")
|
||||
@Data
|
||||
public class OvertimeApplicationSaveReqVO {
|
||||
|
||||
@Schema(description = "加班日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-06-01")
|
||||
@NotNull(message = "加班日期不能为空")
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
@Schema(description = "加班时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1天")
|
||||
@NotBlank(message = "加班时长不能为空")
|
||||
@Size(max = 30, message = "加班时长长度不能超过30个字符")
|
||||
private String overtimeDuration;
|
||||
|
||||
@Schema(description = "加班原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "版本上线保障")
|
||||
@NotBlank(message = "加班原因不能为空")
|
||||
@Size(max = 500, message = "加班原因长度不能超过500个字符")
|
||||
private String overtimeReason;
|
||||
|
||||
@Schema(description = "加班内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "处理上线验证和问题修复")
|
||||
@NotBlank(message = "加班内容不能为空")
|
||||
@Size(max = 1000, message = "加班内容长度不能超过1000个字符")
|
||||
private String overtimeContent;
|
||||
|
||||
@Schema(description = "审核人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
|
||||
@NotNull(message = "审核人不能为空")
|
||||
private Long approverId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请状态动作 Request VO")
|
||||
@Data
|
||||
public class OvertimeApplicationStatusActionReqVO {
|
||||
|
||||
@Schema(description = "动作原因或审核意见。是否必填以状态机配置为准;当前退回必填",
|
||||
example = "请补充加班内容")
|
||||
@Size(max = 1000, message = "动作原因长度不能超过 1000 个字符")
|
||||
private String reason;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 管理后台 - 加班申请状态字典 Response VO
|
||||
*/
|
||||
@Schema(description = "管理后台 - 加班申请状态字典 Response VO")
|
||||
@Data
|
||||
public class OvertimeApplicationStatusDictRespVO {
|
||||
|
||||
@Schema(description = "状态编码", example = "pending")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "状态名称", example = "待审批")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "排序值", example = "10")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "是否初始状态", example = "true")
|
||||
private Boolean initialFlag;
|
||||
|
||||
@Schema(description = "是否终态", example = "false")
|
||||
private Boolean terminalFlag;
|
||||
|
||||
@Schema(description = "是否允许编辑", example = "false")
|
||||
private Boolean allowEdit;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请状态日志 Response VO")
|
||||
@Data
|
||||
public class OvertimeApplicationStatusLogRespVO {
|
||||
|
||||
@Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "加班申请编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long applicationId;
|
||||
|
||||
@Schema(description = "动作编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "approve")
|
||||
private String actionType;
|
||||
|
||||
@Schema(description = "变更前状态", example = "pending")
|
||||
private String fromStatus;
|
||||
|
||||
@Schema(description = "变更后状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "approved")
|
||||
private String toStatus;
|
||||
|
||||
@Schema(description = "原因或审核意见")
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "操作人用户编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long operatorUserId;
|
||||
|
||||
@Schema(description = "操作人名称", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String operatorName;
|
||||
|
||||
@Schema(description = "申请人姓名快照", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String applicantNameSnapshot;
|
||||
|
||||
@Schema(description = "加班日期快照", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDate overtimeDateSnapshot;
|
||||
|
||||
@Schema(description = "加班时长快照", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String overtimeDurationSnapshot;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -46,14 +46,14 @@ public class PersonalItemController {
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建个人事项")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_CREATE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<Long> create(@Valid @RequestBody PersonalItemSaveReqVO reqVO) {
|
||||
return success(personalItemService.createItem(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新个人事项")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> update(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody PersonalItemSaveReqVO reqVO) {
|
||||
personalItemService.updateItem(id, reqVO);
|
||||
@@ -62,21 +62,21 @@ public class PersonalItemController {
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取个人事项详情")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_QUERY + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PersonalItemRespVO> get(@PathVariable("id") Long id) {
|
||||
return success(personalItemService.getItemRespVO(id));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取个人事项分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_QUERY + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PageResult<PersonalItemRespVO>> page(@Valid PersonalItemPageReqVO reqVO) {
|
||||
return success(personalItemService.getItemRespVOPage(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/change-status")
|
||||
@Operation(summary = "变更个人事项状态")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_STATUS + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_STATUS + "')")
|
||||
public CommonResult<Boolean> changeStatus(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody PersonalItemStatusActionReqVO reqVO) {
|
||||
personalItemService.changeStatus(id, reqVO);
|
||||
@@ -85,7 +85,7 @@ public class PersonalItemController {
|
||||
|
||||
@PostMapping("/{id}/worklogs")
|
||||
@Operation(summary = "新增个人事项工作日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Long> createWorklog(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody TaskWorklogSaveReqVO reqVO) {
|
||||
return success(personalItemService.createWorklog(id, reqVO));
|
||||
@@ -93,7 +93,7 @@ public class PersonalItemController {
|
||||
|
||||
@GetMapping("/{id}/worklogs")
|
||||
@Operation(summary = "获取个人事项工作日志分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_QUERY + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PageResult<TaskWorklogRespVO>> getWorklogPage(@PathVariable("id") Long id,
|
||||
@Valid TaskWorklogPageReqVO reqVO) {
|
||||
return success(personalItemService.getWorklogPage(id, reqVO));
|
||||
@@ -101,7 +101,7 @@ public class PersonalItemController {
|
||||
|
||||
@PutMapping("/{id}/worklogs/{worklogId}")
|
||||
@Operation(summary = "修改个人事项工作日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> updateWorklog(@PathVariable("id") Long id,
|
||||
@PathVariable("worklogId") Long worklogId,
|
||||
@Valid @RequestBody TaskWorklogSaveReqVO reqVO) {
|
||||
@@ -111,7 +111,7 @@ public class PersonalItemController {
|
||||
|
||||
@DeleteMapping("/{id}/worklogs/{worklogId}")
|
||||
@Operation(summary = "删除个人事项工作日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> deleteWorklog(@PathVariable("id") Long id,
|
||||
@PathVariable("worklogId") Long worklogId) {
|
||||
personalItemService.deleteWorklog(id, worklogId);
|
||||
@@ -120,7 +120,7 @@ public class PersonalItemController {
|
||||
|
||||
@DeleteMapping("/{id}/worklogs/delete-list")
|
||||
@Operation(summary = "批量删除个人事项工作日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> deleteWorklogs(@PathVariable("id") Long id,
|
||||
@RequestParam("ids") List<Long> ids) {
|
||||
personalItemService.deleteWorklogs(id, ids);
|
||||
@@ -129,7 +129,7 @@ public class PersonalItemController {
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除个人事项")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_DELETE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_DELETE + "')")
|
||||
public CommonResult<Boolean> delete(@RequestParam("id") Long id) {
|
||||
personalItemService.deleteItem(id);
|
||||
return success(true);
|
||||
@@ -137,7 +137,7 @@ public class PersonalItemController {
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Operation(summary = "批量删除个人事项")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_DELETE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_DELETE + "')")
|
||||
public CommonResult<Boolean> deleteList(@RequestParam("ids") List<Long> ids) {
|
||||
personalItemService.deleteItems(ids);
|
||||
return success(true);
|
||||
@@ -145,7 +145,7 @@ public class PersonalItemController {
|
||||
|
||||
@PostMapping("/relate-execution")
|
||||
@Operation(summary = "批量个人事项关联执行")
|
||||
@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
//@PreAuthorize("@ss.hasPermission('" + PersonalItemConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> relateExecution(@RequestParam("itemIds") List<Long> itemIds,
|
||||
@RequestParam("executionId") Long executionId) {
|
||||
personalItemService.relateExecution(itemIds, executionId);
|
||||
|
||||
@@ -3,13 +3,39 @@ package com.njcn.rdms.module.project.controller.admin.product.vo.product;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 产品入口页概览统计 Response VO")
|
||||
@Data
|
||||
public class ProductOverviewSummaryRespVO {
|
||||
|
||||
@Schema(description = "产品状态数量,按当前启用的产品状态模型返回")
|
||||
@Schema(description = "产品状态数量,按当前启用的产品状态模型返回(兼容旧前端的过渡字段,前端改造完成后移除)")
|
||||
private Map<String, Long> statusCounts;
|
||||
|
||||
@Schema(description = "「全部」口径总数 = items 各状态 count 之和(产品域当前无「全部」视图,口径同项目域:启用状态全集)", example = "12")
|
||||
private Long total;
|
||||
|
||||
@Schema(description = "状态看板项列表,覆盖状态机全部启用状态,按 sort 升序;状态机新增启用状态自动进入清单")
|
||||
private List<StatusBoardItemVO> items;
|
||||
|
||||
@Schema(description = "产品状态看板项")
|
||||
@Data
|
||||
public static class StatusBoardItemVO {
|
||||
|
||||
@Schema(description = "状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "active")
|
||||
private String statusCode;
|
||||
@Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "启用")
|
||||
private String statusName;
|
||||
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Long count;
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer sort;
|
||||
@Schema(description = "是否终态", example = "false")
|
||||
private Boolean terminal;
|
||||
@Schema(description = "是否计入「全部」(当前口径无排除项,恒为 true)", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean includeInAll;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@ package com.njcn.rdms.module.project.controller.admin.project;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.object.BeanUtils;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectContextRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectCreateWithTeamReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectDeleteReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectGroupPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectGroupPageRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectOverviewSummaryRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectSaveReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.vo.project.ProjectStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.project.ProjectDO;
|
||||
import com.njcn.rdms.module.project.service.project.ProjectService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -73,8 +73,14 @@ public class ProjectController {
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取项目分页")
|
||||
public CommonResult<PageResult<ProjectRespVO>> getProjectPage(@Valid ProjectPageReqVO pageReqVO) {
|
||||
PageResult<ProjectDO> pageResult = projectService.getProjectPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProjectRespVO.class));
|
||||
return success(projectService.getProjectPage(pageReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/group-page")
|
||||
@Operation(summary = "获取项目按产品分组分页")
|
||||
public CommonResult<ProjectGroupPageRespVO> getProjectGroupPage(@Valid ProjectGroupPageReqVO reqVO) {
|
||||
// 与 page / overview-summary 一致:全域读路径不挂权限注解,可见性由 Service 内 ObjectDataScope 数据权限承载
|
||||
return success(projectService.getProjectGroupPage(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-product")
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.execution;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.execution.vo.execution.MyProjectExecutionPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.execution.vo.execution.MyProjectExecutionRespVO;
|
||||
import com.njcn.rdms.module.project.service.project.execution.ProjectExecutionService;
|
||||
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.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 我负责的执行(跨项目)")
|
||||
@RestController
|
||||
@RequestMapping("/project/project/me/executions")
|
||||
@Validated
|
||||
public class MyExecutionController {
|
||||
|
||||
@Resource
|
||||
private ProjectExecutionService projectExecutionService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页获取当前登录用户负责的执行(跨项目,默认排除终态与进度满)")
|
||||
public CommonResult<PageResult<MyProjectExecutionRespVO>> getMyExecutionPage(@Valid MyProjectExecutionPageReqVO reqVO) {
|
||||
// 前端固定传 pageSize=-1 拉全部;负数统一归一为 PAGE_SIZE_NONE(-1),与现有执行分页接口一致
|
||||
if (reqVO.getPageSize() != null && reqVO.getPageSize() < 0) {
|
||||
reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
}
|
||||
return success(projectExecutionService.getMyExecutionPage(reqVO));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.execution.vo.execution;
|
||||
|
||||
import com.njcn.rdms.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 MyProjectExecutionPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "执行状态编码(预留,单状态精确过滤)", example = "active")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "执行名称模糊匹配关键字(预留)", example = "联调")
|
||||
private String keyword;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.execution.vo.execution;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Schema(description = "管理后台 - 我负责的执行(跨项目)Response VO")
|
||||
@Data
|
||||
public class MyProjectExecutionRespVO {
|
||||
|
||||
@Schema(description = "执行编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5001")
|
||||
private Long id;
|
||||
@Schema(description = "执行名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "后端接口联调")
|
||||
private String executionName;
|
||||
@Schema(description = "所属项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2001")
|
||||
private Long projectId;
|
||||
@Schema(description = "所属项目名称", example = "商城 V2 升级")
|
||||
private String projectName;
|
||||
@Schema(description = "执行状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "active")
|
||||
private String statusCode;
|
||||
@Schema(description = "执行状态名称", example = "进行中")
|
||||
private String statusName;
|
||||
@Schema(description = "优先级编码(字典 rdms_req_priority),0=P0(最高) ~ 3=P3(最低)", example = "0")
|
||||
private String priority;
|
||||
@Schema(description = "计划开始日期")
|
||||
private LocalDate plannedStartDate;
|
||||
@Schema(description = "计划结束日期")
|
||||
private LocalDate plannedEndDate;
|
||||
@Schema(description = "实际开始日期")
|
||||
private LocalDate actualStartDate;
|
||||
@Schema(description = "实际结束日期")
|
||||
private LocalDate actualEndDate;
|
||||
@Schema(description = "执行进度百分比 0-100", example = "68")
|
||||
private Integer progressRate;
|
||||
@Schema(description = "关联项目需求编号")
|
||||
private Long projectRequirementId;
|
||||
@Schema(description = "关联项目需求名称", example = "订单履约后端拆分(一期)")
|
||||
private String projectRequirementName;
|
||||
|
||||
}
|
||||
@@ -23,7 +23,10 @@ public class ProjectExecutionPageReqVO extends PageParam {
|
||||
@Size(max = 32, message = "执行类型长度不能超过32个字符")
|
||||
private String executionType;
|
||||
|
||||
@Schema(description = "执行负责人用户编号", example = "3001")
|
||||
@Schema(description = "我参与语义:该 userId 是 owner 或活跃协办;与 ownerId 二选一;不传 = 项目内全部执行")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配(不含协办);与 involveUserId 二选一", example = "3001")
|
||||
private Long ownerId;
|
||||
|
||||
@Schema(description = "执行状态编码", example = "pending")
|
||||
@@ -38,4 +41,8 @@ public class ProjectExecutionPageReqVO extends PageParam {
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] updateTime;
|
||||
|
||||
@Schema(description = "截止时间范围 chip:overdue(逾期)/ today(今天到期)/ thisWeek(本周到期);" +
|
||||
"基于 plannedEndDate 且排除终态执行;不传 = 不按截止时间过滤", example = "overdue")
|
||||
private String dueRange;
|
||||
|
||||
}
|
||||
|
||||
@@ -22,11 +22,18 @@ public class ProjectExecutionStatusBoardReqVO {
|
||||
@Size(max = 32, message = "执行类型长度不能超过32个字符")
|
||||
private String executionType;
|
||||
|
||||
@Schema(description = "执行负责人用户编号", example = "3001")
|
||||
@Schema(description = "我参与语义:该 userId 是 owner 或活跃协办;与 ownerId 二选一;不传 = 项目内全部执行")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配(不含协办);与 involveUserId 二选一", example = "3001")
|
||||
private Long ownerId;
|
||||
|
||||
@Schema(description = "更新时间", example = "[2026-05-01 00:00:00, 2026-05-31 23:59:59]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] updateTime;
|
||||
|
||||
@Schema(description = "截止时间范围 chip:overdue(逾期)/ today(今天到期)/ thisWeek(本周到期);" +
|
||||
"基于 plannedEndDate 且排除终态执行;不传 = 不按截止时间过滤", example = "overdue")
|
||||
private String dueRange;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.project;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject.MyProjectOwnedRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject.MyProjectPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject.MyProjectParticipatedRespVO;
|
||||
import com.njcn.rdms.module.project.service.project.MyProjectService;
|
||||
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.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 工作台「我的项目」")
|
||||
@RestController
|
||||
@RequestMapping("/project/project/me")
|
||||
@Validated
|
||||
public class MyProjectController {
|
||||
|
||||
@Resource
|
||||
private MyProjectService myProjectService;
|
||||
|
||||
@GetMapping("/participated/page")
|
||||
@Operation(summary = "分页获取当前登录用户参与的项目(作为成员)")
|
||||
public CommonResult<PageResult<MyProjectParticipatedRespVO>> getMyParticipatedPage(@Valid MyProjectPageReqVO reqVO) {
|
||||
normalizePageSize(reqVO);
|
||||
return success(myProjectService.getMyParticipatedPage(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/owned/page")
|
||||
@Operation(summary = "分页获取当前登录用户负责的项目(managerUserId=当前用户)")
|
||||
public CommonResult<PageResult<MyProjectOwnedRespVO>> getMyOwnedPage(@Valid MyProjectPageReqVO reqVO) {
|
||||
normalizePageSize(reqVO);
|
||||
return success(myProjectService.getMyOwnedPage(reqVO));
|
||||
}
|
||||
|
||||
/** 前端固定传 pageSize=-1 拉全部;负数统一归一为 PAGE_SIZE_NONE,与 MyExecutionController 一致。 */
|
||||
private void normalizePageSize(MyProjectPageReqVO reqVO) {
|
||||
if (reqVO.getPageSize() != null && reqVO.getPageSize() < 0) {
|
||||
reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 我负责的项目 Response VO")
|
||||
@Data
|
||||
public class MyProjectOwnedRespVO {
|
||||
|
||||
@Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2001")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
@Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "商城 V2 升级")
|
||||
private String name;
|
||||
@Schema(description = "项目编码", example = "MALL-V2")
|
||||
private String code;
|
||||
@Schema(description = "项目整体进度百分比 0-100", requiredMode = Schema.RequiredMode.REQUIRED, example = "70")
|
||||
private Integer progress;
|
||||
@Schema(description = "当前用户在该项目中的角色名(恒含负责人语义)", example = "项目负责人")
|
||||
private String myRole;
|
||||
@Schema(description = "项目计划结束日期 YYYY-MM-DD;未设为 null")
|
||||
private LocalDate plannedEndDate;
|
||||
@Schema(description = "项目下进行中执行数", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
|
||||
private Integer executionCount;
|
||||
@Schema(description = "项目下进行中任务数", requiredMode = Schema.RequiredMode.REQUIRED, example = "24")
|
||||
private Integer taskCount;
|
||||
@Schema(description = "项目当前有效成员数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer memberCount;
|
||||
@Schema(description = "项目下逾期任务数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer overdueCount;
|
||||
@Schema(description = "成员负载原始数据;无成员为 []", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<MemberLoadVO> members;
|
||||
|
||||
@Schema(description = "成员负载原始数据")
|
||||
@Data
|
||||
public static class MemberLoadVO {
|
||||
@Schema(description = "成员用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "101")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
@Schema(description = "成员姓名/昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String userName;
|
||||
@Schema(description = "该成员在本项目下的进行中任务数", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
|
||||
private Integer activeTaskCount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject;
|
||||
|
||||
import com.njcn.rdms.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 MyProjectPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "项目名称/编码模糊匹配关键字(预留,本期不过滤)", example = "商城")
|
||||
private String keyword;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 我参与的项目 Response VO")
|
||||
@Data
|
||||
public class MyProjectParticipatedRespVO {
|
||||
|
||||
@Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2001")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
@Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "商城 V2 升级")
|
||||
private String name;
|
||||
@Schema(description = "项目编码", example = "MALL-V2")
|
||||
private String code;
|
||||
@Schema(description = "项目状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "active")
|
||||
private String statusCode;
|
||||
@Schema(description = "项目状态名称", example = "进行中")
|
||||
private String statusName;
|
||||
@Schema(description = "项目整体进度百分比 0-100", requiredMode = Schema.RequiredMode.REQUIRED, example = "70")
|
||||
private Integer progress;
|
||||
@Schema(description = "当前用户在该项目中的角色名(主角色 / 附加角色拼接)", example = "前端负责人")
|
||||
private String myRole;
|
||||
@Schema(description = "我负责的任务总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "8")
|
||||
private Integer myTaskCount;
|
||||
@Schema(description = "我负责的未完成任务数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
private Integer myPendingTaskCount;
|
||||
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class ProjectTaskAggregateController {
|
||||
}
|
||||
|
||||
@GetMapping("/summary")
|
||||
@Operation(summary = "获取项目任务今日小条(支持 scope=mine|all)")
|
||||
@Operation(summary = "获取项目任务今日小条(involveUserId 控制是否限定 owner / 活跃协办)")
|
||||
public CommonResult<ProjectTaskSummaryRespVO> getTaskSummary(
|
||||
@PathVariable("projectId") Long projectId,
|
||||
@Valid ProjectTaskSummaryReqVO reqVO) {
|
||||
|
||||
@@ -36,7 +36,10 @@ public class ProjectTaskBoardPageReqVO extends PageParam {
|
||||
@Schema(description = "父任务编号", example = "9001")
|
||||
private Long parentTaskId;
|
||||
|
||||
@Schema(description = "任务负责人用户编号", example = "3002")
|
||||
@Schema(description = "我参与语义:该 userId 是 owner 或活跃协办;与 ownerId 二选一;不传 = 执行下全部任务")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配(不含协办);与 involveUserId 二选一", example = "3002")
|
||||
private Long ownerId;
|
||||
|
||||
@Schema(description = "更新时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]")
|
||||
|
||||
@@ -22,7 +22,10 @@ public class ProjectTaskPageReqVO extends PageParam {
|
||||
@Schema(description = "父任务编号")
|
||||
private Long parentTaskId;
|
||||
|
||||
@Schema(description = "任务负责人用户编号", example = "3002")
|
||||
@Schema(description = "我参与语义:该 userId 是 owner 或活跃协办;与 ownerId 二选一;不传 = 执行下全部任务")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配(不含协办);与 involveUserId 二选一", example = "3002")
|
||||
private Long ownerId;
|
||||
|
||||
@Schema(description = "任务状态编码", example = "pending")
|
||||
|
||||
@@ -21,7 +21,10 @@ public class ProjectTaskStatusBoardReqVO {
|
||||
@Schema(description = "父任务编号", example = "9001")
|
||||
private Long parentTaskId;
|
||||
|
||||
@Schema(description = "任务负责人用户编号", example = "3002")
|
||||
@Schema(description = "我参与语义:该 userId 是 owner 或活跃协办;与 ownerId 二选一;不传 = 执行下全部任务")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配(不含协办);与 involveUserId 二选一", example = "3002")
|
||||
private Long ownerId;
|
||||
|
||||
@Schema(description = "更新时间", example = "[2026-05-01 00:00:00, 2026-05-31 23:59:59]")
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ProjectTaskAggregateBoardPageReqVO extends PageParam {
|
||||
@Schema(description = "任务名称模糊匹配关键字")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "限定执行 id 列表;不传 = 项目内全部执行")
|
||||
@Schema(description = "限定执行 id 列表;空数组 = 明确返空;不传 = 项目内全部执行")
|
||||
private List<Long> executionIds;
|
||||
|
||||
@Schema(description = "限定任务所属执行的状态码多选;空数组 = 明确返空;不传 = 不按执行状态过滤")
|
||||
@@ -29,6 +29,9 @@ public class ProjectTaskAggregateBoardPageReqVO extends PageParam {
|
||||
@Schema(description = "我参与语义;与 ownerId 二选一")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "执行成员语义:该 userId 是执行 owner 或活跃执行协办;过滤其参与的执行下的任务。与 involveUserId(任务成员)正交,可同传;用户未参与任何执行时返空")
|
||||
private Long executionInvolveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配;与 involveUserId 二选一")
|
||||
private Long ownerId;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ProjectTaskAggregatePageReqVO extends PageParam {
|
||||
@Schema(description = "任务名称模糊匹配关键字")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "限定执行 id 列表;不传 = 项目内全部执行")
|
||||
@Schema(description = "限定执行 id 列表;空数组 = 明确返空;不传 = 项目内全部执行")
|
||||
private List<Long> executionIds;
|
||||
|
||||
@Schema(description = "限定任务所属执行的状态码多选;空数组 = 明确返空;不传 = 不按执行状态过滤")
|
||||
@@ -29,6 +29,9 @@ public class ProjectTaskAggregatePageReqVO extends PageParam {
|
||||
@Schema(description = "我参与语义:该 userId 是 owner 或活跃协办;与 ownerId 二选一")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "执行成员语义:该 userId 是执行 owner 或活跃执行协办;过滤其参与的执行下的任务。与 involveUserId(任务成员)正交,可同传;用户未参与任何执行时返空")
|
||||
private Long executionInvolveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配(不含协办);与 involveUserId 二选一")
|
||||
private Long ownerId;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public class ProjectTaskAggregateStatusBoardReqVO {
|
||||
@Schema(description = "任务名称模糊匹配关键字")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "限定执行 id 列表;不传 = 项目内全部执行")
|
||||
@Schema(description = "限定执行 id 列表;空数组 = 明确返空;不传 = 项目内全部执行")
|
||||
private List<Long> executionIds;
|
||||
|
||||
@Schema(description = "限定任务所属执行的状态码多选;空数组 = 明确返空;不传 = 不按执行状态过滤")
|
||||
@@ -26,6 +26,9 @@ public class ProjectTaskAggregateStatusBoardReqVO {
|
||||
@Schema(description = "我参与语义;与 ownerId 二选一")
|
||||
private Long involveUserId;
|
||||
|
||||
@Schema(description = "执行成员语义:该 userId 是执行 owner 或活跃执行协办;过滤其参与的执行下的任务。与 involveUserId(任务成员)正交,可同传;用户未参与任何执行时返空")
|
||||
private Long executionInvolveUserId;
|
||||
|
||||
@Schema(description = "仅作为 owner 匹配;与 involveUserId 二选一")
|
||||
private Long ownerId;
|
||||
|
||||
|
||||
@@ -8,12 +8,9 @@ import lombok.Data;
|
||||
public class ProjectTaskSummaryReqVO {
|
||||
|
||||
/**
|
||||
* 数字汇总作用域。
|
||||
* <ul>
|
||||
* <li>{@code mine}(默认):统计当前登录人 owner 或活跃协办的任务</li>
|
||||
* <li>{@code all}:统计项目内全部任务,要求 {@code project:task:list-all} 权限码</li>
|
||||
* </ul>
|
||||
* 我参与语义:传入的 userId 是 owner 或活跃协办;不传 = 项目内全部任务。
|
||||
* 切换"我参与 / 所有"由前端直接控制此字段是否携带,与其他读接口(page / status-board / board-page)契约一致。
|
||||
*/
|
||||
@Schema(description = "作用域:mine(默认) / all", example = "mine")
|
||||
private String scope;
|
||||
@Schema(description = "我参与语义:该 userId 是 owner 或活跃协办;不传 = 项目内全部")
|
||||
private Long involveUserId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.vo.project;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 项目按产品分组分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectGroupPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "关键词,匹配项目编码或项目名称", example = "CNPJ2026001")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "项目类型字典值", example = "main")
|
||||
@Size(max = 32, message = "项目类型长度不能超过32个字符")
|
||||
private String projectType;
|
||||
|
||||
@Schema(description = "所属产品编号", example = "1001")
|
||||
private Long productId;
|
||||
|
||||
@Schema(description = "项目状态编码;缺省 = 「全部」口径(状态机启用状态,不含已取消/已归档)", example = "active")
|
||||
@Size(max = 32, message = "项目状态编码长度不能超过32个字符")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "仅返回游离组(未挂产品的项目),不可与 productId 同时使用", example = "true")
|
||||
private Boolean orphanOnly;
|
||||
|
||||
@Schema(description = "每组返回项目条数上限,默认 5", example = "5")
|
||||
@Min(value = 1, message = "每组条数最小值为 1")
|
||||
@Max(value = 50, message = "每组条数最大值为 50")
|
||||
private Integer topN = 5;
|
||||
|
||||
@AssertTrue(message = "游离组筛选与产品筛选不能同时使用")
|
||||
@Schema(hidden = true)
|
||||
public boolean isOrphanFilterValid() {
|
||||
return !Boolean.TRUE.equals(orphanOnly) || productId == null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.vo.project;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 项目按产品分组分页 Response VO")
|
||||
@Data
|
||||
public class ProjectGroupPageRespVO {
|
||||
|
||||
@Schema(description = "产品组总数(分页 total,含游离组)")
|
||||
private Long total;
|
||||
|
||||
@Schema(description = "当前筛选口径下项目总数(含游离项目)")
|
||||
private Long projectTotal;
|
||||
|
||||
@Schema(description = "当前用户可见、启用/暂停状态产品去重后的方向数")
|
||||
private Integer directionCount;
|
||||
|
||||
@Schema(description = "当前筛选口径下游离项目数")
|
||||
private Long orphanTotal;
|
||||
|
||||
@Schema(description = "产品组列表:同方向相邻,游离组固定最后")
|
||||
private List<ProjectGroupRespVO> list;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.project.vo.project;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 项目产品分组 Response VO")
|
||||
@Data
|
||||
public class ProjectGroupRespVO {
|
||||
|
||||
@Schema(description = "产品编号,游离组为 null", example = "1001")
|
||||
private Long productId;
|
||||
|
||||
@Schema(description = "产品名称,游离组固定为「游离项目」", example = "智能网关")
|
||||
private String productName;
|
||||
|
||||
@Schema(description = "产品编码,游离组为 null", example = "CNPD2026001")
|
||||
private String productCode;
|
||||
|
||||
@Schema(description = "产品方向字典值,游离组为空串", example = "platform")
|
||||
private String directionCode;
|
||||
|
||||
@Schema(description = "产品负责人用户编号,游离组为 null", example = "1024")
|
||||
private Long managerUserId;
|
||||
|
||||
@Schema(description = "产品负责人昵称,游离组为 null", example = "张三")
|
||||
private String managerUserNickname;
|
||||
|
||||
@Schema(description = "当前筛选口径下该组项目总数")
|
||||
private Long projectTotal;
|
||||
|
||||
@Schema(description = "组内前 topN 条项目(更新时间倒序),字段与项目分页接口行一致")
|
||||
private List<ProjectRespVO> projects;
|
||||
|
||||
@Schema(description = "项目类型计数,恒按「全部」口径(不含已取消/已归档)统计,不随状态筛选变化")
|
||||
private Map<String, Long> typeCounts;
|
||||
|
||||
@Schema(description = "是否存在非已取消的主线(基线)项目,口径与创建唯一性校验一致")
|
||||
private Boolean hasBaseline;
|
||||
|
||||
@Schema(description = "是否游离组")
|
||||
private Boolean orphan;
|
||||
|
||||
}
|
||||
@@ -3,13 +3,42 @@ package com.njcn.rdms.module.project.controller.admin.project.vo.project;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 项目入口页概览统计 Response VO")
|
||||
@Data
|
||||
public class ProjectOverviewSummaryRespVO {
|
||||
|
||||
@Schema(description = "项目状态数量,按当前启用的项目状态模型返回")
|
||||
@Schema(description = "项目状态数量,按当前启用的项目状态模型返回(兼容旧前端的过渡字段,前端改造完成后移除)")
|
||||
private Map<String, Long> statusCounts;
|
||||
|
||||
@Schema(description = "「全部」口径总数 = items 各状态 count 之和(2026-06-11 起「全部」= 启用状态全集,作废/归档计入)", example = "48")
|
||||
private Long total;
|
||||
|
||||
@Schema(description = "状态看板项列表,覆盖状态机全部启用状态,按 sort 升序;状态机新增启用状态自动进入清单")
|
||||
private List<StatusBoardItemVO> items;
|
||||
|
||||
@Schema(description = "游离项目数:未挂产品且状态属于「全部」口径(状态机启用状态全集)")
|
||||
private Long orphanCount;
|
||||
|
||||
@Schema(description = "项目状态看板项")
|
||||
@Data
|
||||
public static class StatusBoardItemVO {
|
||||
|
||||
@Schema(description = "状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "active")
|
||||
private String statusCode;
|
||||
@Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")
|
||||
private String statusName;
|
||||
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
|
||||
private Long count;
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
|
||||
private Integer sort;
|
||||
@Schema(description = "是否终态", example = "false")
|
||||
private Boolean terminal;
|
||||
@Schema(description = "是否计入「全部」(当前口径无排除项,恒为 true)", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean includeInAll;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import com.njcn.rdms.framework.dict.validation.InDict;
|
||||
import com.njcn.rdms.module.system.enums.DictTypeConstants;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@@ -43,4 +45,16 @@ public class ProjectPageReqVO extends PageParam {
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] updateTime;
|
||||
|
||||
@Schema(description = "仅查询游离项目(未挂产品),不可与 productId 同时使用", example = "true")
|
||||
private Boolean orphanOnly;
|
||||
|
||||
@Schema(description = "项目状态编码集合,存在时优先于 statusCode 生效", example = "[\"pending\", \"active\"]")
|
||||
private List<String> statusCodes;
|
||||
|
||||
@AssertTrue(message = "游离项目筛选与产品筛选不能同时使用")
|
||||
@Schema(hidden = true)
|
||||
public boolean isOrphanFilterValid() {
|
||||
return !Boolean.TRUE.equals(orphanOnly) || productId == null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -97,10 +97,8 @@ public class ProjectRequirementRespVO {
|
||||
@Schema(description = "是否为终态", example = "false")
|
||||
private Boolean terminal;
|
||||
|
||||
@Schema(description = "需求进度(TD-016 读时聚合,service 层批量计算)。"
|
||||
+ "公式:AVG(该需求自己承接的执行进度 ∪ 直接子需求进度),"
|
||||
+ "排除 rdms_object_status_model.progress_excluded_flag=1 的执行状态(当前为 cancelled);"
|
||||
+ "无任何执行且无子需求时返回 0.00。两位小数,HALF_UP。",
|
||||
@Schema(description = "需求进度。TD-016:读时聚合计算已下线(前端当前不展示需求进度,进度仅项目/执行/任务展示),"
|
||||
+ "字段保留占位、当前恒为 null;未来需要展示需求进度时再恢复服务端聚合计算。",
|
||||
example = "0.65")
|
||||
private BigDecimal progressRate;
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common;
|
||||
|
||||
import com.njcn.rdms.framework.common.util.http.HttpUtils;
|
||||
import com.njcn.rdms.module.project.service.workreport.export.WorkReportExportFile;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class WorkReportExportResponseUtils {
|
||||
|
||||
private WorkReportExportResponseUtils() {
|
||||
}
|
||||
|
||||
public static void write(HttpServletResponse response, WorkReportExportFile file) throws IOException {
|
||||
response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(file.filename()));
|
||||
response.setContentType(file.contentType());
|
||||
response.setContentLength(file.content().length);
|
||||
response.getOutputStream().write(file.content());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.module.project.constant.WorkReportConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusDictRespVO;
|
||||
import com.njcn.rdms.module.project.service.workreport.common.WorkReportStatusService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
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.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 工作报告状态")
|
||||
@RestController
|
||||
@RequestMapping("/project/work-reports")
|
||||
@Validated
|
||||
public class WorkReportStatusController {
|
||||
|
||||
@Resource
|
||||
private WorkReportStatusService workReportStatusService;
|
||||
|
||||
@GetMapping("/status/dict")
|
||||
@Operation(summary = "获取工作报告所有状态字典")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<WorkReportStatusDictRespVO>> getStatusDict() {
|
||||
return success(workReportStatusService.getStatusDict());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 个人报告计划项 Request VO")
|
||||
@Data
|
||||
public class PersonalReportPlanItemReqVO {
|
||||
|
||||
private Integer itemNumber;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
private String targetText;
|
||||
|
||||
private Object targetJson;
|
||||
|
||||
private String supportNeed;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 个人报告计划项 Response VO")
|
||||
@Data
|
||||
public class PersonalReportPlanItemRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Integer itemNumber;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
private String targetText;
|
||||
|
||||
private Object targetJson;
|
||||
|
||||
private String supportNeed;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - 个人报告回顾项 Request VO")
|
||||
@Data
|
||||
public class PersonalReportReviewItemReqVO {
|
||||
|
||||
private Integer itemNumber;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
private BigDecimal workHours;
|
||||
|
||||
private String contentText;
|
||||
|
||||
private Object contentJson;
|
||||
|
||||
private String reflectionText;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - 个人报告回顾项 Response VO")
|
||||
@Data
|
||||
public class PersonalReportReviewItemRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Integer itemNumber;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
private BigDecimal workHours;
|
||||
|
||||
private String contentText;
|
||||
|
||||
private Object contentJson;
|
||||
|
||||
private String reflectionText;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 工作报告审核记录 Response VO")
|
||||
@Data
|
||||
public class WorkReportApprovalRecordRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long statusLogId;
|
||||
|
||||
private Integer approvalRound;
|
||||
|
||||
private String conclusion;
|
||||
|
||||
private String opinion;
|
||||
|
||||
private Long auditorUserId;
|
||||
|
||||
private String auditorName;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import com.njcn.rdms.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.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkReportBasePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "关键字")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "状态编码")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "周期开始日期范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate[] periodStartDate;
|
||||
|
||||
@Schema(description = "提交时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] submitTime;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 项目成员快照 Response VO")
|
||||
@Data
|
||||
public class WorkReportMemberSnapshotRespVO {
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String userName;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 工作报告状态动作 Request VO")
|
||||
@Data
|
||||
public class WorkReportStatusActionReqVO {
|
||||
|
||||
@Schema(description = "原因或审核意见", example = "请补充下周计划")
|
||||
@Size(max = 1000, message = "原因或审核意见长度不能超过 1000 个字符")
|
||||
private String reason;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 工作报告状态字典 Response VO")
|
||||
@Data
|
||||
public class WorkReportStatusDictRespVO {
|
||||
|
||||
@Schema(description = "状态编码", example = "draft")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "状态名称", example = "待提交")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "排序", example = "10")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "是否初始态")
|
||||
private Boolean initialFlag;
|
||||
|
||||
@Schema(description = "是否终态")
|
||||
private Boolean terminalFlag;
|
||||
|
||||
@Schema(description = "是否允许编辑")
|
||||
private Boolean allowEdit;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 工作报告状态日志 Response VO")
|
||||
@Data
|
||||
public class WorkReportStatusLogRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String reportType;
|
||||
|
||||
private Long reportId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private String fromStatus;
|
||||
|
||||
private String toStatus;
|
||||
|
||||
private String reason;
|
||||
|
||||
private Long operatorUserId;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
private String periodLabelSnapshot;
|
||||
|
||||
private String remark;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly;
|
||||
|
||||
import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
|
||||
import com.njcn.rdms.module.project.constant.WorkReportConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.WorkReportExportResponseUtils;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusLogRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportApprovalRecordRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportApproveReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportContentExportReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportDefaultDraftReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportExportVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportSaveReqVO;
|
||||
import com.njcn.rdms.module.project.service.workreport.export.WorkReportContentExportService;
|
||||
import com.njcn.rdms.module.project.service.workreport.monthly.MonthlyReportService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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.PathVariable;
|
||||
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.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 个人月报")
|
||||
@RestController
|
||||
@RequestMapping("/project/work-reports/monthly")
|
||||
@Validated
|
||||
public class MonthlyReportController {
|
||||
|
||||
@Resource
|
||||
private MonthlyReportService monthlyReportService;
|
||||
@Resource
|
||||
private WorkReportContentExportService workReportContentExportService;
|
||||
|
||||
@GetMapping("/init")
|
||||
@Operation(summary = "获取月报新建默认数据")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<MonthlyReportRespVO> initMonthlyReport() {
|
||||
return success(monthlyReportService.initMonthlyReport());
|
||||
}
|
||||
|
||||
@GetMapping("/default-draft")
|
||||
@Operation(summary = "预览月报默认稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<MonthlyReportRespVO> previewMonthlyDefaultDraft(@Valid MonthlyReportDefaultDraftReqVO reqVO) {
|
||||
return success(monthlyReportService.previewMonthlyDefaultDraft(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新建月报草稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<Long> createMonthlyReport(@Valid @RequestBody MonthlyReportSaveReqVO reqVO) {
|
||||
return success(monthlyReportService.createMonthlyReport(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "修改月报草稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> updateMonthlyReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody MonthlyReportSaveReqVO reqVO) {
|
||||
monthlyReportService.updateMonthlyReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取月报详情")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<MonthlyReportRespVO> getMonthlyReport(@PathVariable("id") Long id) {
|
||||
return success(monthlyReportService.getMonthlyReport(id));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取我的月报分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PageResult<MonthlyReportRespVO>> getMonthlyReportPage(@Valid MonthlyReportPageReqVO reqVO) {
|
||||
return success(monthlyReportService.getMonthlyReportPage(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/approval-page")
|
||||
@Operation(summary = "获取待我审批的月报分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<PageResult<MonthlyReportRespVO>> getMonthlyApprovalPage(@Valid MonthlyReportPageReqVO reqVO) {
|
||||
return success(monthlyReportService.getMonthlyApprovalPage(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/submit")
|
||||
@Operation(summary = "提交月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> submitMonthlyReport(@PathVariable("id") Long id) {
|
||||
monthlyReportService.submitMonthlyReport(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审批通过月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> approveMonthlyReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody MonthlyReportApproveReqVO reqVO) {
|
||||
monthlyReportService.approveMonthlyReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "退回月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> rejectMonthlyReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
|
||||
monthlyReportService.rejectMonthlyReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_DELETE + "')")
|
||||
public CommonResult<Boolean> deleteMonthlyReport(@PathVariable("id") Long id) {
|
||||
monthlyReportService.deleteMonthlyReport(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/status-logs")
|
||||
@Operation(summary = "获取月报状态日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<WorkReportStatusLogRespVO>> getMonthlyStatusLogs(@PathVariable("id") Long id) {
|
||||
return success(monthlyReportService.getMonthlyStatusLogs(id));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/approval-records")
|
||||
@Operation(summary = "获取月报审批记录")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<MonthlyReportApprovalRecordRespVO>> getMonthlyApprovalRecords(@PathVariable("id") Long id) {
|
||||
return success(monthlyReportService.getMonthlyApprovalRecords(id));
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
@Operation(summary = "导出我的月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportMonthlyReport(@Valid MonthlyReportPageReqVO reqVO, HttpServletResponse response)
|
||||
throws IOException {
|
||||
ExcelUtils.write(response, "个人月报_" + LocalDate.now() + ".xls", "个人月报",
|
||||
MonthlyReportExportVO.class, monthlyReportService.getMonthlyExportList(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/content-export")
|
||||
@Operation(summary = "导出月报内容 Word")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportMonthlyReportContent(@Valid @RequestBody MonthlyReportContentExportReqVO reqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
WorkReportExportResponseUtils.write(response, workReportContentExportService.exportMonthly(reqVO));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 月报审核记录 Response VO")
|
||||
@Data
|
||||
public class MonthlyReportApprovalRecordRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long statusLogId;
|
||||
|
||||
private Integer approvalRound;
|
||||
|
||||
private String conclusion;
|
||||
|
||||
private String opinion;
|
||||
|
||||
private LocalDate meetingDate;
|
||||
|
||||
private String strengthDesc;
|
||||
|
||||
private String strengthExample;
|
||||
|
||||
private String weaknessDesc;
|
||||
|
||||
private String weaknessExample;
|
||||
|
||||
private String improvementSuggestion;
|
||||
|
||||
private String performanceResult;
|
||||
|
||||
private String employeeSignName;
|
||||
|
||||
private LocalDate employeeSignedDate;
|
||||
|
||||
private String supervisorSignName;
|
||||
|
||||
private LocalDate supervisorSignedDate;
|
||||
|
||||
private Long auditorUserId;
|
||||
|
||||
private String auditorName;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 月报审核通过 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MonthlyReportApproveReqVO extends WorkReportStatusActionReqVO {
|
||||
|
||||
@Schema(description = "面谈时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
@NotNull
|
||||
private LocalDate meetingDate;
|
||||
|
||||
@Schema(description = "优势描述")
|
||||
private String strengthDesc;
|
||||
|
||||
@Schema(description = "优势行为事例")
|
||||
private String strengthExample;
|
||||
|
||||
@Schema(description = "劣势描述")
|
||||
private String weaknessDesc;
|
||||
|
||||
@Schema(description = "劣势行为事例")
|
||||
private String weaknessExample;
|
||||
|
||||
@Schema(description = "改进建议")
|
||||
private String improvementSuggestion;
|
||||
|
||||
@Schema(description = "绩效考核结果")
|
||||
@NotBlank(message = "绩效考核结果不能为空")
|
||||
private String performanceResult;
|
||||
|
||||
@Schema(description = "被考核人签名")
|
||||
private String employeeSignName;
|
||||
|
||||
@Schema(description = "被考核人签字日期")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate employeeSignedDate;
|
||||
|
||||
@Schema(description = "上级签名")
|
||||
private String supervisorSignName;
|
||||
|
||||
@Schema(description = "上级签字日期")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate supervisorSignedDate;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 月报内容导出 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MonthlyReportContentExportReqVO extends MonthlyReportPageReqVO {
|
||||
|
||||
@Schema(description = "是否按当前搜索条件全量导出")
|
||||
private Boolean exportAll;
|
||||
|
||||
@Schema(description = "选中的月报编号列表;exportAll=false 时必填")
|
||||
private List<Long> ids;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 月报默认稿预览 Request VO")
|
||||
@Data
|
||||
public class MonthlyReportDefaultDraftReqVO {
|
||||
|
||||
@NotBlank(message = "周期编码不能为空")
|
||||
private String periodKey;
|
||||
|
||||
@NotBlank(message = "周期名称不能为空")
|
||||
private String periodLabel;
|
||||
|
||||
@NotNull(message = "周期开始日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@NotNull(message = "周期结束日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodEndDate;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class MonthlyReportExportVO {
|
||||
|
||||
@ExcelProperty("填报人")
|
||||
private String reporterName;
|
||||
|
||||
@ExcelProperty("直属上级")
|
||||
private String supervisorName;
|
||||
|
||||
@ExcelProperty("周期")
|
||||
private String periodLabel;
|
||||
|
||||
@ExcelProperty("开始日期")
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@ExcelProperty("结束日期")
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@ExcelProperty("工时")
|
||||
private BigDecimal totalWorkHours;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("审核意见")
|
||||
private String approvalComment;
|
||||
|
||||
@ExcelProperty("提交时间")
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@ExcelProperty("审核时间")
|
||||
private LocalDateTime approvalTime;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportBasePageReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 月报分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MonthlyReportPageReqVO extends WorkReportBasePageReqVO {
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 月报 Response VO")
|
||||
@Data
|
||||
public class MonthlyReportRespVO {
|
||||
|
||||
private Long id;
|
||||
private Long reporterId;
|
||||
private String reporterName;
|
||||
private String reporterDeptName;
|
||||
private String reporterPostName;
|
||||
private Long supervisorUserId;
|
||||
private String supervisorName;
|
||||
private String periodKey;
|
||||
private String periodLabel;
|
||||
private LocalDate periodStartDate;
|
||||
private LocalDate periodEndDate;
|
||||
private String statusCode;
|
||||
private String statusName;
|
||||
private Boolean allowEdit;
|
||||
private Boolean terminal;
|
||||
private BigDecimal totalWorkHours;
|
||||
private String approvalComment;
|
||||
private String lastStatusReason;
|
||||
private LocalDateTime submitTime;
|
||||
private LocalDateTime approvalTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private List<PersonalReportReviewItemRespVO> reviewItems;
|
||||
private List<PersonalReportPlanItemRespVO> planItems;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 月报保存 Request VO")
|
||||
@Data
|
||||
public class MonthlyReportSaveReqVO {
|
||||
|
||||
@NotBlank(message = "周期编码不能为空")
|
||||
private String periodKey;
|
||||
|
||||
@NotBlank(message = "周期名称不能为空")
|
||||
private String periodLabel;
|
||||
|
||||
@NotNull(message = "周期开始日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@NotNull(message = "周期结束日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@Valid
|
||||
private List<PersonalReportReviewItemReqVO> reviewItems;
|
||||
|
||||
@Valid
|
||||
private List<PersonalReportPlanItemReqVO> planItems;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project;
|
||||
|
||||
import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
|
||||
import com.njcn.rdms.module.project.constant.WorkReportConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.WorkReportExportResponseUtils;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportApprovalRecordRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusLogRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportContentExportReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportDefaultDraftReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportExportVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportOwnerProjectOptionRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportSaveReqVO;
|
||||
import com.njcn.rdms.module.project.service.workreport.export.WorkReportContentExportService;
|
||||
import com.njcn.rdms.module.project.service.workreport.project.ProjectReportService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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.PathVariable;
|
||||
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.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 项目半月报")
|
||||
@RestController
|
||||
@RequestMapping("/project/work-reports/project")
|
||||
@Validated
|
||||
public class ProjectReportController {
|
||||
|
||||
@Resource
|
||||
private ProjectReportService projectReportService;
|
||||
@Resource
|
||||
private WorkReportContentExportService workReportContentExportService;
|
||||
|
||||
@GetMapping("/owner-project-options")
|
||||
@Operation(summary = "获取项目半月报负责人可选项目列表")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
|
||||
public CommonResult<List<ProjectReportOwnerProjectOptionRespVO>> getOwnerProjectOptions() {
|
||||
return success(projectReportService.getOwnerProjectOptions());
|
||||
}
|
||||
|
||||
@GetMapping("/init")
|
||||
@Operation(summary = "获取项目半月报新建默认数据")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
|
||||
public CommonResult<ProjectReportRespVO> initProjectReport(@RequestParam("projectId") Long projectId) {
|
||||
return success(projectReportService.initProjectReport(projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/default-draft")
|
||||
@Operation(summary = "预览项目半月报默认稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
|
||||
public CommonResult<ProjectReportRespVO> previewProjectDefaultDraft(@PathVariable("projectId") Long projectId,
|
||||
@Valid ProjectReportDefaultDraftReqVO reqVO) {
|
||||
return success(projectReportService.previewProjectDefaultDraft(projectId, reqVO));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新建项目半月报草稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
|
||||
public CommonResult<Long> createProjectReport(@Valid @RequestBody ProjectReportSaveReqVO reqVO) {
|
||||
return success(projectReportService.createProjectReport(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "修改项目半月报草稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
|
||||
public CommonResult<Boolean> updateProjectReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody ProjectReportSaveReqVO reqVO) {
|
||||
projectReportService.updateProjectReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取项目半月报详情")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<ProjectReportRespVO> getProjectReport(@PathVariable("id") Long id) {
|
||||
return success(projectReportService.getProjectReport(id));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取我的项目半月报分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PageResult<ProjectReportRespVO>> getProjectReportPage(@Valid ProjectReportPageReqVO reqVO) {
|
||||
return success(projectReportService.getProjectReportPage(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/approval-page")
|
||||
@Operation(summary = "获取待我审批的项目半月报分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<PageResult<ProjectReportRespVO>> getProjectApprovalPage(@Valid ProjectReportPageReqVO reqVO) {
|
||||
return success(projectReportService.getProjectApprovalPage(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/submit")
|
||||
@Operation(summary = "提交项目半月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> submitProjectReport(@PathVariable("id") Long id) {
|
||||
projectReportService.submitProjectReport(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审批通过项目半月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> approveProjectReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
|
||||
projectReportService.approveProjectReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "退回项目半月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> rejectProjectReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
|
||||
projectReportService.rejectProjectReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除项目半月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_DELETE + "')")
|
||||
public CommonResult<Boolean> deleteProjectReport(@PathVariable("id") Long id) {
|
||||
projectReportService.deleteProjectReport(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/status-logs")
|
||||
@Operation(summary = "获取项目半月报状态日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<WorkReportStatusLogRespVO>> getProjectStatusLogs(@PathVariable("id") Long id) {
|
||||
return success(projectReportService.getProjectStatusLogs(id));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/approval-records")
|
||||
@Operation(summary = "获取项目半月报审批记录")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<WorkReportApprovalRecordRespVO>> getProjectApprovalRecords(@PathVariable("id") Long id) {
|
||||
return success(projectReportService.getProjectApprovalRecords(id));
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
@Operation(summary = "导出我的项目半月报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportProjectReport(@Valid ProjectReportPageReqVO reqVO, HttpServletResponse response)
|
||||
throws IOException {
|
||||
ExcelUtils.write(response, "项目半月报_" + LocalDate.now() + ".xls", "项目半月报",
|
||||
ProjectReportExportVO.class, projectReportService.getProjectExportList(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/content-export")
|
||||
@Operation(summary = "导出项目半月报内容 Word")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportProjectReportContent(@Valid @RequestBody ProjectReportContentExportReqVO reqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
WorkReportExportResponseUtils.write(response, workReportContentExportService.exportProject(reqVO));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报内容导出 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectReportContentExportReqVO extends ProjectReportPageReqVO {
|
||||
|
||||
@Schema(description = "是否按当前搜索条件全量导出")
|
||||
private Boolean exportAll;
|
||||
|
||||
@Schema(description = "选中的项目半月报编号列表;exportAll=false 时必填")
|
||||
private List<Long> ids;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报默认稿预览 Request VO")
|
||||
@Data
|
||||
public class ProjectReportDefaultDraftReqVO {
|
||||
|
||||
@NotBlank(message = "周期编码不能为空")
|
||||
private String periodKey;
|
||||
|
||||
@NotBlank(message = "周期名称不能为空")
|
||||
private String periodLabel;
|
||||
|
||||
@NotNull(message = "周期开始日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@NotNull(message = "周期结束日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@NotNull(message = "上半月/下半月标记不能为空")
|
||||
private Integer flag;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class ProjectReportExportVO {
|
||||
|
||||
@ExcelProperty("项目名称")
|
||||
private String projectName;
|
||||
|
||||
@ExcelProperty("填报人")
|
||||
private String projectOwnerName;
|
||||
|
||||
@ExcelProperty("直属上级")
|
||||
private String supervisorName;
|
||||
|
||||
@ExcelProperty("周期")
|
||||
private String periodLabel;
|
||||
|
||||
@ExcelProperty("开始日期")
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@ExcelProperty("结束日期")
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@ExcelProperty("工时")
|
||||
private BigDecimal totalWorkHours;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("审核意见")
|
||||
private String approvalComment;
|
||||
|
||||
@ExcelProperty("提交时间")
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@ExcelProperty("审核时间")
|
||||
private LocalDateTime approvalTime;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报工作项 Request VO")
|
||||
@Data
|
||||
public class ProjectReportItemReqVO {
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
private BigDecimal workHours;
|
||||
|
||||
private String priorityCode;
|
||||
|
||||
private BigDecimal progressRate;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报工作项 Response VO")
|
||||
@Data
|
||||
public class ProjectReportItemRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
private BigDecimal workHours;
|
||||
|
||||
private String priorityCode;
|
||||
|
||||
private BigDecimal progressRate;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报负责人可选项目 Response VO")
|
||||
@Data
|
||||
public class ProjectReportOwnerProjectOptionRespVO {
|
||||
|
||||
@Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "项目编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "CNPJ2026001")
|
||||
private String projectCode;
|
||||
|
||||
@Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "客户交付项目")
|
||||
private String projectName;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportBasePageReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectReportPageReqVO extends WorkReportBasePageReqVO {
|
||||
|
||||
@Schema(description = "项目编号")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "上半月/下半月标记")
|
||||
private Integer flag;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportMemberSnapshotRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报 Response VO")
|
||||
@Data
|
||||
public class ProjectReportRespVO {
|
||||
|
||||
private Long id;
|
||||
private Long projectId;
|
||||
private String projectName;
|
||||
private Long projectOwnerId;
|
||||
private String projectOwnerName;
|
||||
private String technicalOwnerName;
|
||||
private List<WorkReportMemberSnapshotRespVO> projectMemberSnapshot;
|
||||
private Long supervisorUserId;
|
||||
private String supervisorName;
|
||||
private String periodKey;
|
||||
private String periodLabel;
|
||||
private LocalDate periodStartDate;
|
||||
private LocalDate periodEndDate;
|
||||
private Integer flag;
|
||||
private String statusCode;
|
||||
private String statusName;
|
||||
private Boolean allowEdit;
|
||||
private Boolean terminal;
|
||||
private String projectStatusDesc;
|
||||
private String projectProgressPlan;
|
||||
private String projectKeyPoints;
|
||||
private String projectProblems;
|
||||
private BigDecimal totalWorkHours;
|
||||
private String approvalComment;
|
||||
private String lastStatusReason;
|
||||
private LocalDateTime submitTime;
|
||||
private LocalDateTime approvalTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private List<ProjectReportItemRespVO> currentItems;
|
||||
private List<ProjectReportItemRespVO> nextItems;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 项目半月报保存 Request VO")
|
||||
@Data
|
||||
public class ProjectReportSaveReqVO {
|
||||
|
||||
@NotNull(message = "项目编号不能为空")
|
||||
private Long projectId;
|
||||
|
||||
@NotBlank(message = "周期编码不能为空")
|
||||
private String periodKey;
|
||||
|
||||
@NotBlank(message = "周期名称不能为空")
|
||||
private String periodLabel;
|
||||
|
||||
@NotNull(message = "周期开始日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@NotNull(message = "周期结束日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@NotNull(message = "上半月/下半月标记不能为空")
|
||||
private Integer flag;
|
||||
|
||||
private String projectStatusDesc;
|
||||
|
||||
private String projectProgressPlan;
|
||||
|
||||
private String projectKeyPoints;
|
||||
|
||||
private String projectProblems;
|
||||
|
||||
@Valid
|
||||
private List<ProjectReportItemReqVO> currentItems;
|
||||
|
||||
@Valid
|
||||
private List<ProjectReportItemReqVO> nextItems;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly;
|
||||
|
||||
import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
|
||||
import com.njcn.rdms.module.project.constant.WorkReportConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.WorkReportExportResponseUtils;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportApprovalRecordRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusLogRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportContentExportReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportDefaultDraftReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportExportVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportSaveReqVO;
|
||||
import com.njcn.rdms.module.project.service.workreport.export.WorkReportContentExportService;
|
||||
import com.njcn.rdms.module.project.service.workreport.weekly.WeeklyReportService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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.PathVariable;
|
||||
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.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 个人周报")
|
||||
@RestController
|
||||
@RequestMapping("/project/work-reports/weekly")
|
||||
@Validated
|
||||
public class WeeklyReportController {
|
||||
|
||||
@Resource
|
||||
private WeeklyReportService weeklyReportService;
|
||||
@Resource
|
||||
private WorkReportContentExportService workReportContentExportService;
|
||||
|
||||
@GetMapping("/init")
|
||||
@Operation(summary = "获取周报新建默认数据")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<WeeklyReportRespVO> initWeeklyReport() {
|
||||
return success(weeklyReportService.initWeeklyReport());
|
||||
}
|
||||
|
||||
@GetMapping("/default-draft")
|
||||
@Operation(summary = "预览周报默认稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<WeeklyReportRespVO> previewWeeklyDefaultDraft(@Valid WeeklyReportDefaultDraftReqVO reqVO) {
|
||||
return success(weeklyReportService.previewWeeklyDefaultDraft(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新建周报草稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<Long> createWeeklyReport(@Valid @RequestBody WeeklyReportSaveReqVO reqVO) {
|
||||
return success(weeklyReportService.createWeeklyReport(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "修改周报草稿")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> updateWeeklyReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody WeeklyReportSaveReqVO reqVO) {
|
||||
weeklyReportService.updateWeeklyReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取周报详情")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<WeeklyReportRespVO> getWeeklyReport(@PathVariable("id") Long id) {
|
||||
return success(weeklyReportService.getWeeklyReport(id));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取我的周报分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PageResult<WeeklyReportRespVO>> getWeeklyReportPage(@Valid WeeklyReportPageReqVO reqVO) {
|
||||
return success(weeklyReportService.getWeeklyReportPage(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/approval-page")
|
||||
@Operation(summary = "获取待我审批的周报分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<PageResult<WeeklyReportRespVO>> getWeeklyApprovalPage(@Valid WeeklyReportPageReqVO reqVO) {
|
||||
return success(weeklyReportService.getWeeklyApprovalPage(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/submit")
|
||||
@Operation(summary = "提交周报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> submitWeeklyReport(@PathVariable("id") Long id) {
|
||||
weeklyReportService.submitWeeklyReport(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审批通过周报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> approveWeeklyReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
|
||||
weeklyReportService.approveWeeklyReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "退回周报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> rejectWeeklyReport(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
|
||||
weeklyReportService.rejectWeeklyReport(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除周报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_DELETE + "')")
|
||||
public CommonResult<Boolean> deleteWeeklyReport(@PathVariable("id") Long id) {
|
||||
weeklyReportService.deleteWeeklyReport(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/status-logs")
|
||||
@Operation(summary = "获取周报状态日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<WorkReportStatusLogRespVO>> getWeeklyStatusLogs(@PathVariable("id") Long id) {
|
||||
return success(weeklyReportService.getWeeklyStatusLogs(id));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/approval-records")
|
||||
@Operation(summary = "获取周报审批记录")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<WorkReportApprovalRecordRespVO>> getWeeklyApprovalRecords(@PathVariable("id") Long id) {
|
||||
return success(weeklyReportService.getWeeklyApprovalRecords(id));
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
@Operation(summary = "导出我的周报")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportWeeklyReport(@Valid WeeklyReportPageReqVO reqVO, HttpServletResponse response)
|
||||
throws IOException {
|
||||
ExcelUtils.write(response, "个人周报_" + LocalDate.now() + ".xls", "个人周报",
|
||||
WeeklyReportExportVO.class, weeklyReportService.getWeeklyExportList(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/content-export")
|
||||
@Operation(summary = "导出周报内容 Word")
|
||||
@PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportWeeklyReportContent(@Valid @RequestBody WeeklyReportContentExportReqVO reqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
WorkReportExportResponseUtils.write(response, workReportContentExportService.exportWeekly(reqVO));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 周报内容导出 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WeeklyReportContentExportReqVO extends WeeklyReportPageReqVO {
|
||||
|
||||
@Schema(description = "是否按当前搜索条件全量导出")
|
||||
private Boolean exportAll;
|
||||
|
||||
@Schema(description = "选中的周报编号列表;exportAll=false 时必填")
|
||||
private List<Long> ids;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 周报默认稿预览 Request VO")
|
||||
@Data
|
||||
public class WeeklyReportDefaultDraftReqVO {
|
||||
|
||||
@NotBlank(message = "周期编码不能为空")
|
||||
private String periodKey;
|
||||
|
||||
@NotBlank(message = "周期名称不能为空")
|
||||
private String periodLabel;
|
||||
|
||||
@NotNull(message = "周期开始日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@NotNull(message = "周期结束日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodEndDate;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class WeeklyReportExportVO {
|
||||
|
||||
@ExcelProperty("填报人")
|
||||
private String reporterName;
|
||||
|
||||
@ExcelProperty("直属上级")
|
||||
private String supervisorName;
|
||||
|
||||
@ExcelProperty("周期")
|
||||
private String periodLabel;
|
||||
|
||||
@ExcelProperty("开始日期")
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@ExcelProperty("结束日期")
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@ExcelProperty("是否出差")
|
||||
private Boolean isBusinessTrip;
|
||||
|
||||
@ExcelProperty("出差天数")
|
||||
private BigDecimal totalTravelDays;
|
||||
|
||||
@ExcelProperty("工时")
|
||||
private BigDecimal totalWorkHours;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("审核意见")
|
||||
private String approvalComment;
|
||||
|
||||
@ExcelProperty("提交时间")
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@ExcelProperty("审核时间")
|
||||
private LocalDateTime approvalTime;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportBasePageReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 周报分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WeeklyReportPageReqVO extends WorkReportBasePageReqVO {
|
||||
|
||||
@Schema(description = "是否出差")
|
||||
private Boolean isBusinessTrip;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 周报 Response VO")
|
||||
@Data
|
||||
public class WeeklyReportRespVO {
|
||||
|
||||
private Long id;
|
||||
private Long reporterId;
|
||||
private String reporterName;
|
||||
private String reporterDeptName;
|
||||
private String reporterPostName;
|
||||
private Long supervisorUserId;
|
||||
private String supervisorName;
|
||||
private String periodKey;
|
||||
private String periodLabel;
|
||||
private LocalDate periodStartDate;
|
||||
private LocalDate periodEndDate;
|
||||
private String statusCode;
|
||||
private String statusName;
|
||||
private Boolean allowEdit;
|
||||
private Boolean terminal;
|
||||
private Boolean isBusinessTrip;
|
||||
private BigDecimal totalTravelDays;
|
||||
private BigDecimal totalWorkHours;
|
||||
private String approvalComment;
|
||||
private String lastStatusReason;
|
||||
private LocalDateTime submitTime;
|
||||
private LocalDateTime approvalTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private List<PersonalReportReviewItemRespVO> reviewItems;
|
||||
private List<PersonalReportPlanItemRespVO> planItems;
|
||||
private List<WeeklyReportTravelSegmentRespVO> travelSegments;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 周报保存 Request VO")
|
||||
@Data
|
||||
public class WeeklyReportSaveReqVO {
|
||||
|
||||
@NotBlank(message = "周期编码不能为空")
|
||||
private String periodKey;
|
||||
|
||||
@NotBlank(message = "周期名称不能为空")
|
||||
private String periodLabel;
|
||||
|
||||
@NotNull(message = "周期开始日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@NotNull(message = "周期结束日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@NotNull(message = "是否出差不能为空")
|
||||
private Boolean isBusinessTrip;
|
||||
|
||||
@Valid
|
||||
private List<PersonalReportReviewItemReqVO> reviewItems;
|
||||
|
||||
@Valid
|
||||
private List<PersonalReportPlanItemReqVO> planItems;
|
||||
|
||||
@Valid
|
||||
private List<WeeklyReportTravelSegmentReqVO> travelSegments;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
@Schema(description = "管理后台 - 周报出差分段 Request VO")
|
||||
@Data
|
||||
public class WeeklyReportTravelSegmentReqVO {
|
||||
|
||||
private Integer sort;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate startDate;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate endDate;
|
||||
|
||||
private BigDecimal travelDays;
|
||||
|
||||
private String location;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Schema(description = "管理后台 - 周报出差分段 Response VO")
|
||||
@Data
|
||||
public class WeeklyReportTravelSegmentRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Integer sort;
|
||||
|
||||
private LocalDate startDate;
|
||||
|
||||
private LocalDate endDate;
|
||||
|
||||
private BigDecimal travelDays;
|
||||
|
||||
private String location;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.overtime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 加班申请审核记录。
|
||||
*/
|
||||
@TableName("rdms_overtime_application_approval_record")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationApprovalRecordDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long overtimeApplicationId;
|
||||
|
||||
private Long statusLogId;
|
||||
|
||||
private Integer approvalRound;
|
||||
|
||||
private String conclusion;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String opinion;
|
||||
|
||||
private Long auditorUserId;
|
||||
|
||||
private String auditorName;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.overtime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 加班申请表。
|
||||
*/
|
||||
@TableName("rdms_overtime_application")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long applicantId;
|
||||
|
||||
private String applicantName;
|
||||
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
private String overtimeDuration;
|
||||
|
||||
private String overtimeReason;
|
||||
|
||||
private String overtimeContent;
|
||||
|
||||
private Long approverId;
|
||||
|
||||
private String approverName;
|
||||
|
||||
private String statusCode;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String approvalComment;
|
||||
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private LocalDateTime approvalTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.overtime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 加班申请状态日志表。
|
||||
*/
|
||||
@TableName("rdms_overtime_application_status_log")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationStatusLogDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long applicationId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private String fromStatus;
|
||||
|
||||
private String toStatus;
|
||||
|
||||
private String reason;
|
||||
|
||||
private Long operatorUserId;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
private String applicantNameSnapshot;
|
||||
|
||||
private LocalDate overtimeDateSnapshot;
|
||||
|
||||
private String overtimeDurationSnapshot;
|
||||
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 个人周报/月报计划项。
|
||||
*/
|
||||
@TableName(value = "rdms_work_report_personal_report_plan_item", autoResultMap = true)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PersonalReportPlanItemDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private String reportType;
|
||||
|
||||
private Long reportId;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private Integer itemNumber;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String targetText;
|
||||
|
||||
@TableField(typeHandler = JacksonTypeHandler.class, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private Object targetJson;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String supportNeed;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 个人周报/月报回顾项。
|
||||
*/
|
||||
@TableName(value = "rdms_work_report_personal_report_review_item", autoResultMap = true)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PersonalReportReviewItemDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private String reportType;
|
||||
|
||||
private Long reportId;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private Integer itemNumber;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal workHours;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String contentText;
|
||||
|
||||
@TableField(typeHandler = JacksonTypeHandler.class, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private Object contentJson;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String reflectionText;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 项目半月报中的项目成员快照。
|
||||
*/
|
||||
@Data
|
||||
public class WorkReportMemberSnapshotItem {
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String userName;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 工作报告状态日志。
|
||||
*/
|
||||
@TableName("rdms_work_report_status_log")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkReportStatusLogDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private String reportType;
|
||||
|
||||
private Long reportId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String fromStatus;
|
||||
|
||||
private String toStatus;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String reason;
|
||||
|
||||
private Long operatorUserId;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
private String periodLabelSnapshot;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.monthly;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 月报审核记录。
|
||||
*/
|
||||
@TableName("rdms_work_report_monthly_report_approval_record")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MonthlyReportApprovalRecordDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long monthlyReportId;
|
||||
|
||||
private Long statusLogId;
|
||||
|
||||
private Integer approvalRound;
|
||||
|
||||
private String conclusion;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String opinion;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private LocalDate meetingDate;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String strengthDesc;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String strengthExample;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String weaknessDesc;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String weaknessExample;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String improvementSuggestion;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String performanceResult;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String employeeSignName;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private LocalDate employeeSignedDate;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String supervisorSignName;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private LocalDate supervisorSignedDate;
|
||||
|
||||
private Long auditorUserId;
|
||||
|
||||
private String auditorName;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.monthly;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 工作报告-月报主表。
|
||||
*/
|
||||
@TableName("rdms_work_report_monthly_report")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MonthlyReportDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long reporterId;
|
||||
|
||||
private String reporterName;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String reporterDeptName;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String reporterPostName;
|
||||
|
||||
private Long supervisorUserId;
|
||||
|
||||
private String supervisorName;
|
||||
|
||||
private String periodKey;
|
||||
|
||||
private String periodLabel;
|
||||
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
private String statusCode;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal totalWorkHours;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private LocalDateTime approvalTime;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String approvalComment;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String lastStatusReason;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.project;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 项目半月报审核记录。
|
||||
*/
|
||||
@TableName("rdms_work_report_project_report_approval_record")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectReportApprovalRecordDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long projectReportId;
|
||||
|
||||
private Long statusLogId;
|
||||
|
||||
private Integer approvalRound;
|
||||
|
||||
private String conclusion;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String opinion;
|
||||
|
||||
private Long auditorUserId;
|
||||
|
||||
private String auditorName;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.workreport.project;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 项目半月报本期工作项。
|
||||
*/
|
||||
@TableName("rdms_work_report_project_report_current_item")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectReportCurrentItemDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long reportId;
|
||||
|
||||
private String itemTitle;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal workHours;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String priorityCode;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal progressRate;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user