This commit is contained in:
李旭明 committed 2025-11-25 15:17:17 +08:00
commit 8f471dff4b
392 files changed
+72310 -10354

No files matched your search

+4
View File
@@ -331,3 +331,7 @@ scripts/
学生调宿、退宿功能.md
学生动向信息界面(宿管员).md
prompt/
backend/fix-all-errors.sh
.gitignore
backend/fix-mapstruct-error.sh
backend/fix-compilation-errors.sh
+4 -4
View File
@@ -111,7 +111,7 @@
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-xg</artifactId>
</dependency>
</dependency>
<!-- 学工管理-学生信息管理模块 -->
<!-- <dependency>
@@ -134,7 +134,7 @@
<groupId>org.dromara</groupId>
<artifactId>ruoyi-xljk</artifactId>
<version>${revision}</version>
</dependency>
</dependency>
<!-- 学生考核管理模块 -->
<dependency>
@@ -147,7 +147,7 @@
<groupId>org.dromara</groupId>
<artifactId>ruoyi-jcbt</artifactId>
<version>${revision}</version>
</dependency>
</dependency>
<!-- 班主任考核管理模块 -->
<dependency>
@@ -161,7 +161,7 @@
<groupId>org.dromara</groupId>
<artifactId>ruoyi-stgl</artifactId>
<version>${revision}</version>
</dependency>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
@@ -12,10 +12,16 @@ server:
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
max-http-post-size: -1
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
buffer-size: 512
# 每块buffer的空间大小(标准配置:16KB,提升文件上传性能)
buffer-size: 16384
# 是否分配的直接内存
direct-buffers: true
# 请求处理超时时间(毫秒),0表示无限制,建议设置合理超时避免长时间阻塞
no-request-timeout: 600000
# 🔧 修复文件上传超时:阻塞写操作超时时间(毫秒),适用于大文件上传或慢网络环境
options:
# 阻塞写操作超时时间设置为10分钟,避免上传大文件时触发默认300秒超时
blocking-write-timeout: 600000
threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io: 8
@@ -75,10 +81,16 @@ spring:
# 文件上传
servlet:
multipart:
# 是否启用文件上传
enabled: true
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 上传文件的临时目录
location: ${java.io.tmpdir}
# 文件大小阈值,超过该阈值将写入磁盘而非内存(0表示始终写入磁盘)
file-size-threshold: 0
mvc:
# 设置静态资源路径 防止所有请求都去查静态资源
static-path-pattern: /static/**
@@ -193,7 +205,7 @@ mybatis-encryptor:
# api接口加密
api-decrypt:
# 是否开启全局接口加密
enabled: true # 恢复为true,保持其他接口的安全性
enabled: false # 临时关闭用于测试权限修复
# AES 加密头标识
headerFlag: encrypt-key
# 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
@@ -13,7 +13,11 @@ import org.dromara.common.core.utils.sql.SqlUtil;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 分页查询实体类
@@ -46,6 +50,16 @@ public class PageQuery implements Serializable {
*/
private String isAsc;
/**
* 默认排序列
*/
private String defaultOrderByColumn = "create_time";
/**
* 默认排序方向
*/
private String defaultIsAsc = "desc";
/**
* 当前记录起始索引 默认值
*/
@@ -66,10 +80,14 @@ public class PageQuery implements Serializable {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
List<OrderItem> orderItems = buildOrderItem();
List<OrderItem> orderItems = buildOrderItem(orderByColumn, isAsc);
if (CollUtil.isNotEmpty(orderItems)) {
page.addOrder(orderItems);
}
List<OrderItem> defaultOrderItems = buildDefaultOrderItem(orderItems);
if (CollUtil.isNotEmpty(defaultOrderItems)) {
page.addOrder(defaultOrderItems);
}
return page;
}
@@ -79,18 +97,21 @@ public class PageQuery implements Serializable {
* 支持的用法如下:
* {isAsc:"asc",orderByColumn:"id"} order by id asc
* {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc
* {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc
* {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc
* {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time
* desc
* {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time
* desc
*/
private List<OrderItem> buildOrderItem() {
private List<OrderItem> buildOrderItem(String orderByColumn, String isAsc) {
if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) {
return null;
return Collections.emptyList();
}
String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
orderBy = StringUtils.toUnderScoreCase(orderBy);
// 兼容前端排序类型
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
isAsc = StringUtils.replaceEach(isAsc, new String[] { "ascending", "descending" },
new String[] { "asc", "desc" });
String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);
String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
@@ -114,6 +135,29 @@ public class PageQuery implements Serializable {
return list;
}
private List<OrderItem> buildDefaultOrderItem(List<OrderItem> existingOrders) {
if (StringUtils.isBlank(defaultOrderByColumn) || StringUtils.isBlank(defaultIsAsc)) {
return Collections.emptyList();
}
List<OrderItem> defaultItems = buildOrderItem(defaultOrderByColumn, defaultIsAsc);
if (CollUtil.isEmpty(defaultItems)) {
return defaultItems;
}
Set<String> existing = CollUtil.isNotEmpty(existingOrders)
? existingOrders.stream()
.map(item -> StringUtils.blankToDefault(item.getColumn(), StringUtils.EMPTY).trim()
.toLowerCase())
.collect(Collectors.toCollection(HashSet::new))
: Collections.emptySet();
return defaultItems.stream()
.filter(item -> {
String column = StringUtils.blankToDefault(item.getColumn(), StringUtils.EMPTY).trim()
.toLowerCase();
return !existing.contains(column);
})
.collect(Collectors.toList());
}
@JsonIgnore
public Integer getFirstNum() {
return (pageNum - 1) * pageSize;
@@ -130,4 +174,20 @@ public class PageQuery implements Serializable {
this.pageNum = pageNum;
}
public PageQuery withDefaultOrder(String column, String asc) {
this.defaultOrderByColumn = column;
this.defaultIsAsc = asc;
return this;
}
public PageQuery withDefaultOrder(String column) {
return withDefaultOrder(column, "desc");
}
public PageQuery disableDefaultOrder() {
this.defaultOrderByColumn = null;
this.defaultIsAsc = null;
return this;
}
}
@@ -21,6 +21,8 @@ import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.*;
@@ -32,6 +34,7 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/**
@@ -93,10 +96,10 @@ public class OssClient {
.httpClient(NettyNioAsyncHttpClient.builder()
// 🚀 优化:连接超时时间(60秒)
.connectionTimeout(Duration.ofSeconds(60))
// 🚀 优化:读取超时时间(300秒,适合大文件下载)
.readTimeout(Duration.ofSeconds(300))
// 🚀 优化:写入超时时间(300秒,适合大文件上传)
.writeTimeout(Duration.ofSeconds(300))
// 🚀 优化:读取超时时间(600秒,适合大文件下载和慢网络环境)
.readTimeout(Duration.ofSeconds(600))
// 🚀 优化:写入超时时间(600秒,适合大文件上传和慢网络环境)
.writeTimeout(Duration.ofSeconds(600))
.build())
.build();
@@ -184,31 +187,31 @@ public class OssClient {
inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
}
try {
// 创建异步请求体(length如果为空会报错)
// 🔧 修复:创建异步请求体,设置超时时间为600秒(10分钟),适应慢网络环境
BlockingInputStreamAsyncRequestBody body = BlockingInputStreamAsyncRequestBody.builder()
.contentLength(length)
.subscribeTimeout(Duration.ofSeconds(30))
.subscribeTimeout(Duration.ofSeconds(600))
.build();
// 使用 transferManager 进行上传
Upload upload = transferManager.upload(
x -> x.requestBody(body)
.putObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(key)
.contentType(contentType)
// 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
// 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
// .acl(getAccessPolicy().getObjectCannedACL())
.build())
.build());
// 🔧 修复:构建上传请求对象
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(properties.getBucketName())
.key(key)
.contentType(contentType)
// 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
// 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
// .acl(getAccessPolicy().getObjectCannedACL())
.build();
// 将输入流写入请求体
// 🔧 修复:先发起上传请求(重要!必须在writeInputStream之前)
CompletableFuture<PutObjectResponse> future = client.putObject(putObjectRequest, body);
// 🔧 修复:在发起请求后立即写入数据流(正确顺序)
body.writeInputStream(inputStream);
// 等待文件上传操作完成
CompletedUpload uploadResult = upload.completionFuture().join();
String eTag = uploadResult.response().eTag();
PutObjectResponse response = future.join();
String eTag = response.eTag();
// 提取上传结果中的 ETag,并构建一个自定义的 UploadResult 对象
return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build();
@@ -0,0 +1,48 @@
server:
port: 9090
spring:
application:
name: ruoyi-monitor-admin
profiles:
active: @profiles.active@
logging:
config: classpath:logback-plus.xml
--- # 监控中心服务端配置
spring:
security:
user:
name: @monitor.username@
password: @monitor.password@
boot:
admin:
ui:
title: RuoYi-Vue-Plus服务监控中心
context-path: /admin
--- # Actuator 监控端点的配置项
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: ALWAYS
logfile:
external-file: ./logs/ruoyi-monitor-admin.log
--- # 监控配置
spring.boot.admin.client:
# 增加客户端开关
enabled: true
# 设置 Spring Boot Admin Server 地址
url: http://172.16.0.67:9090/admin
instance:
service-host-type: IP
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@
@@ -38,7 +38,7 @@ spring.boot.admin.client:
# 增加客户端开关
enabled: true
# 设置 Spring Boot Admin Server 地址
url: http://172.16.0.67:9090/admin
url: http://127.0.0.1:9090/admin
instance:
service-host-type: IP
metadata:
@@ -24,5 +24,8 @@ public class BatchApprovalBo {
/**
* 拒绝原因(批量拒绝时必填)
*/
private String rejectReason;
}
@@ -0,0 +1,102 @@
package org.dromara.cw.controller;
import java.util.Arrays;
import java.util.List;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cw.domain.bo.CwProjectPerformanceSelfEvaluationBo;
import org.dromara.cw.domain.vo.CwProjectPerformanceSelfEvaluationVo;
import org.dromara.cw.service.ICwMonitoringManagementService;
/**
* 运行监控管理
* 财务管理员和学校领导使用,用于查看所有人提交的绩效自评,并进行锁定/解锁操作
*
* @author ruoyi
* @date 2025-01-03
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/cw/monitoringManagement")
public class CwMonitoringManagementController extends BaseController {
private final ICwMonitoringManagementService monitoringManagementService;
/**
* 查询绩效自评列表(运行监控管理)
* 财务管理员和学校领导可以看到所有人的数据
*/
@SaCheckPermission("cw:monitoringManagement:list")
@GetMapping("/list")
public TableDataInfo<CwProjectPerformanceSelfEvaluationVo> list(CwProjectPerformanceSelfEvaluationBo bo, PageQuery pageQuery) {
return monitoringManagementService.queryPageList(bo, pageQuery);
}
/**
* 获取绩效自评详细信息(运行监控管理)
*/
@SaCheckPermission("cw:monitoringManagement:query")
@GetMapping("/{id}")
public R<CwProjectPerformanceSelfEvaluationVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(monitoringManagementService.queryById(id));
}
/**
* 锁定绩效自评(单个)
*/
@SaCheckPermission("cw:monitoringManagement:lock")
@Log(title = "运行监控管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/lock/{id}")
public R<Void> lock(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return toAjax(monitoringManagementService.lockEvaluation(id));
}
/**
* 解锁绩效自评(单个)
*/
@SaCheckPermission("cw:monitoringManagement:unlock")
@Log(title = "运行监控管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/unlock/{id}")
public R<Void> unlock(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return toAjax(monitoringManagementService.unlockEvaluation(id));
}
/**
* 批量锁定绩效自评
*/
@SaCheckPermission("cw:monitoringManagement:lock")
@Log(title = "运行监控管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/lock/batch")
public R<Void> lockBatch(@NotEmpty(message = "主键不能为空") @RequestBody Long[] ids) {
return toAjax(monitoringManagementService.lockBatch(Arrays.asList(ids)));
}
/**
* 批量解锁绩效自评
*/
@SaCheckPermission("cw:monitoringManagement:unlock")
@Log(title = "运行监控管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/unlock/batch")
public R<Void> unlockBatch(@NotEmpty(message = "主键不能为空") @RequestBody Long[] ids) {
return toAjax(monitoringManagementService.unlockBatch(Arrays.asList(ids)));
}
}
@@ -0,0 +1,85 @@
package org.dromara.cw.controller;
import java.util.List;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.cw.domain.bo.CwPerformanceMonitoringBo;
import org.dromara.cw.domain.vo.CwPerformanceMonitoringVo;
import org.dromara.cw.service.ICwPerformanceMonitoringService;
/**
* 绩效运行监控
*
* @author ruoyi
* @date 2025-11-04
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/cw/performanceMonitoring")
public class CwPerformanceMonitoringController extends BaseController {
private final ICwPerformanceMonitoringService performanceMonitoringService;
/**
* 根据自评ID查询运行监控列表
*/
@SaCheckPermission("cw:performanceMonitoring:query")
@GetMapping("/list/{evaluationId}")
public R<List<CwPerformanceMonitoringVo>> listByEvaluationId(@NotNull(message = "自评ID不能为空") @PathVariable Long evaluationId) {
return R.ok(performanceMonitoringService.queryByEvaluationId(evaluationId));
}
/**
* 获取绩效运行监控详细信息
*/
@SaCheckPermission("cw:performanceMonitoring:query")
@GetMapping("/{id}")
public R<CwPerformanceMonitoringVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(performanceMonitoringService.queryById(id));
}
/**
* 新增绩效运行监控
*/
@SaCheckPermission("cw:performanceMonitoring:add")
@Log(title = "绩效运行监控", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody CwPerformanceMonitoringBo bo) {
return toAjax(performanceMonitoringService.insertByBo(bo));
}
/**
* 修改绩效运行监控
*/
@SaCheckPermission("cw:performanceMonitoring:edit")
@Log(title = "绩效运行监控", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody CwPerformanceMonitoringBo bo) {
return toAjax(performanceMonitoringService.updateByBo(bo));
}
/**
* 删除绩效运行监控
*/
@SaCheckPermission("cw:performanceMonitoring:remove")
@Log(title = "绩效运行监控", businessType = BusinessType.DELETE)
@DeleteMapping("/{id}")
public R<Void> remove(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return toAjax(performanceMonitoringService.deleteWithValidByIds(List.of(id), true));
}
}
@@ -0,0 +1,132 @@
package org.dromara.cw.controller;
import java.util.List;
import java.util.Arrays;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cw.domain.bo.CwProjectEvaluationIndicatorBo;
import org.dromara.cw.domain.vo.CwProjectEvaluationIndicatorVo;
import org.dromara.cw.service.ICwProjectEvaluationIndicatorService;
/**
* 项目绩效自评指标明细
*
* @author ruoyi
* @date 2025-11-03
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/cw/indicator")
public class CwProjectEvaluationIndicatorController extends BaseController {
private final ICwProjectEvaluationIndicatorService indicatorService;
/**
* 查询项目绩效自评指标明细列表
*/
@SaCheckPermission("cw:indicator:list")
@GetMapping("/list")
public TableDataInfo<CwProjectEvaluationIndicatorVo> list(CwProjectEvaluationIndicatorBo bo, PageQuery pageQuery) {
return indicatorService.queryPageList(bo, pageQuery);
}
/**
* 获取项目绩效自评指标明细详细信息
*/
@SaCheckPermission("cw:indicator:query")
@GetMapping("/{id}")
public R<CwProjectEvaluationIndicatorVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(indicatorService.queryById(id));
}
/**
* 根据自评ID查询指标明细列表
*/
@SaCheckPermission("cw:indicator:query")
@GetMapping("/evaluation/{evaluationId}")
public R<List<CwProjectEvaluationIndicatorVo>> getByEvaluationId(@NotNull(message = "自评ID不能为空") @PathVariable Long evaluationId) {
return R.ok(indicatorService.queryByEvaluationId(evaluationId));
}
/**
* 根据自评ID和指标类型查询指标明细列表
*/
@SaCheckPermission("cw:indicator:query")
@GetMapping("/evaluation/{evaluationId}/type/{indicatorType}")
public R<List<CwProjectEvaluationIndicatorVo>> getByEvaluationIdAndType(
@NotNull(message = "自评ID不能为空") @PathVariable Long evaluationId,
@NotBlank(message = "指标类型不能为空") @PathVariable String indicatorType) {
return R.ok(indicatorService.queryByEvaluationIdAndType(evaluationId, indicatorType));
}
/**
* 新增项目绩效自评指标明细
*/
@SaCheckPermission("cw:indicator:add")
@Log(title = "项目绩效自评指标明细", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody CwProjectEvaluationIndicatorBo bo) {
return toAjax(indicatorService.insertByBo(bo));
}
/**
* 修改项目绩效自评指标明细
*/
@SaCheckPermission("cw:indicator:edit")
@Log(title = "项目绩效自评指标明细", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody CwProjectEvaluationIndicatorBo bo) {
return toAjax(indicatorService.updateByBo(bo));
}
/**
* 批量保存指标明细
*/
@SaCheckPermission("cw:indicator:edit")
@Log(title = "批量保存指标明细", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/batch")
public R<Void> batchSave(@RequestParam Long evaluationId, @RequestBody List<CwProjectEvaluationIndicatorBo> boList) {
return toAjax(indicatorService.batchSave(evaluationId, boList));
}
/**
* 删除项目绩效自评指标明细
*/
@SaCheckPermission("cw:indicator:remove")
@Log(title = "项目绩效自评指标明细", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(indicatorService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 导出项目绩效自评指标明细列表
*/
@SaCheckPermission("cw:indicator:export")
@Log(title = "项目绩效自评指标明细", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(CwProjectEvaluationIndicatorBo bo, HttpServletResponse response) {
List<CwProjectEvaluationIndicatorVo> list = indicatorService.queryList(bo);
ExcelUtil.exportExcel(list, "项目绩效自评指标明细", CwProjectEvaluationIndicatorVo.class, response);
}
}
@@ -0,0 +1,144 @@
package org.dromara.cw.controller;
import java.util.List;
import java.util.Arrays;
import java.util.Map;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cw.domain.bo.CwProjectMonthlyMonitoringBo;
import org.dromara.cw.domain.vo.CwProjectMonthlyMonitoringVo;
import org.dromara.cw.service.ICwProjectMonthlyMonitoringService;
/**
* 项目月度运行监控
*
* @author ruoyi
* @date 2025-11-03
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/cw/monitoring")
public class CwProjectMonthlyMonitoringController extends BaseController {
private final ICwProjectMonthlyMonitoringService monitoringService;
/**
* 查询项目月度运行监控列表
*/
@SaCheckPermission("cw:monitoring:list")
@GetMapping("/list")
public TableDataInfo<CwProjectMonthlyMonitoringVo> list(CwProjectMonthlyMonitoringBo bo, PageQuery pageQuery) {
return monitoringService.queryPageList(bo, pageQuery);
}
/**
* 获取项目月度运行监控详细信息
*/
@SaCheckPermission("cw:monitoring:query")
@GetMapping("/{id}")
public R<CwProjectMonthlyMonitoringVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(monitoringService.queryById(id));
}
/**
* 根据自评ID查询月度监控列表
*/
@SaCheckPermission("cw:monitoring:query")
@GetMapping("/evaluation/{evaluationId}")
public R<List<CwProjectMonthlyMonitoringVo>> getByEvaluationId(@NotNull(message = "自评ID不能为空") @PathVariable Long evaluationId) {
return R.ok(monitoringService.queryByEvaluationId(evaluationId));
}
/**
* 查询某个自评的全年监控数据
*/
@SaCheckPermission("cw:monitoring:query")
@GetMapping("/year/{evaluationId}/{year}")
public R<Map<Integer, CwProjectMonthlyMonitoringVo>> getYearlyMonitoring(
@NotNull(message = "自评ID不能为空") @PathVariable Long evaluationId,
@NotNull(message = "年份不能为空") @PathVariable Integer year) {
return R.ok(monitoringService.queryYearlyMonitoring(evaluationId, year));
}
/**
* 新增项目月度运行监控
*/
@SaCheckPermission("cw:monitoring:add")
@Log(title = "项目月度运行监控", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody CwProjectMonthlyMonitoringBo bo) {
return toAjax(monitoringService.insertByBo(bo));
}
/**
* 修改项目月度运行监控
*/
@SaCheckPermission("cw:monitoring:edit")
@Log(title = "项目月度运行监控", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody CwProjectMonthlyMonitoringBo bo) {
return toAjax(monitoringService.updateByBo(bo));
}
/**
* 填报月度数据
*/
@SaCheckPermission("cw:monitoring:fill")
@Log(title = "填报月度数据", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/fill")
public R<Void> fillMonthlyData(@Validated @RequestBody CwProjectMonthlyMonitoringBo bo) {
return toAjax(monitoringService.fillMonthlyData(bo));
}
/**
* 批量保存月度监控数据
*/
@SaCheckPermission("cw:monitoring:edit")
@Log(title = "批量保存月度监控数据", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/batch")
public R<Void> batchSave(@RequestParam Long evaluationId, @RequestBody List<CwProjectMonthlyMonitoringBo> boList) {
return toAjax(monitoringService.batchSave(evaluationId, boList));
}
/**
* 删除项目月度运行监控
*/
@SaCheckPermission("cw:monitoring:remove")
@Log(title = "项目月度运行监控", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(monitoringService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 导出项目月度运行监控列表
*/
@SaCheckPermission("cw:monitoring:export")
@Log(title = "项目月度运行监控", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(CwProjectMonthlyMonitoringBo bo, HttpServletResponse response) {
List<CwProjectMonthlyMonitoringVo> list = monitoringService.queryList(bo);
ExcelUtil.exportExcel(list, "项目月度运行监控", CwProjectMonthlyMonitoringVo.class, response);
}
}
@@ -0,0 +1,161 @@
package org.dromara.cw.controller;
import java.util.List;
import java.util.Arrays;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cw.domain.bo.CwProjectPerformanceSelfEvaluationBo;
import org.dromara.cw.domain.vo.CwProjectPerformanceSelfEvaluationVo;
import org.dromara.cw.service.ICwProjectPerformanceSelfEvaluationService;
import org.dromara.common.satoken.utils.LoginHelper;
/**
* 项目绩效自评
*
* @author ruoyi
* @date 2025-11-03
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/cw/evaluation")
public class CwProjectPerformanceSelfEvaluationController extends BaseController {
private final ICwProjectPerformanceSelfEvaluationService evaluationService;
/**
* 查询项目绩效自评列表
*/
@SaCheckPermission("cw:evaluation:list")
@GetMapping("/list")
public TableDataInfo<CwProjectPerformanceSelfEvaluationVo> list(CwProjectPerformanceSelfEvaluationBo bo, PageQuery pageQuery) {
return evaluationService.queryPageList(bo, pageQuery);
}
/**
* 获取项目绩效自评详细信息
*/
@SaCheckPermission("cw:evaluation:query")
@GetMapping("/{id}")
public R<CwProjectPerformanceSelfEvaluationVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(evaluationService.queryById(id));
}
/**
* 新增项目绩效自评
*/
@SaCheckPermission("cw:evaluation:add")
@Log(title = "项目绩效自评", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Long> add(@Validated(AddGroup.class) @RequestBody CwProjectPerformanceSelfEvaluationBo bo) {
boolean result = evaluationService.insertByBo(bo);
if (result) {
return R.ok("新增成功", bo.getId());
}
return R.fail("新增失败");
}
/**
* 修改项目绩效自评
*/
@SaCheckPermission("cw:evaluation:edit")
@Log(title = "项目绩效自评", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody CwProjectPerformanceSelfEvaluationBo bo) {
return toAjax(evaluationService.updateByBo(bo));
}
/**
* 保存草稿
*/
@SaCheckPermission("cw:evaluation:add")
@Log(title = "保存自评草稿", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/saveDraft")
public R<Void> saveDraft(@RequestBody CwProjectPerformanceSelfEvaluationBo bo) {
return toAjax(evaluationService.saveDraft(bo));
}
/**
* 提交自评
*/
@SaCheckPermission("cw:evaluation:submit")
@Log(title = "提交自评", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/submit/{id}")
public R<Void> submit(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return toAjax(evaluationService.submitEvaluation(id));
}
/**
* 审核自评
*/
@SaCheckPermission("cw:evaluation:approve")
@Log(title = "审核自评", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/approve/{id}")
public R<Void> approve(@NotNull(message = "主键不能为空") @PathVariable Long id,
@RequestParam String opinion,
@RequestParam Boolean approved) {
return toAjax(evaluationService.approveEvaluation(id, opinion, approved));
}
/**
* 删除项目绩效自评
*/
@SaCheckPermission("cw:evaluation:remove")
@Log(title = "项目绩效自评", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(evaluationService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 查询我的自评列表
*/
@SaCheckPermission("cw:evaluation:list")
@GetMapping("/my")
public R<List<CwProjectPerformanceSelfEvaluationVo>> myEvaluations() {
return R.ok(evaluationService.queryMyEvaluations(LoginHelper.getUserId()));
}
/**
* 查询待审核自评列表
*/
@SaCheckPermission("cw:evaluation:approve")
@GetMapping("/pending")
public R<List<CwProjectPerformanceSelfEvaluationVo>> pendingApprovals() {
return R.ok(evaluationService.queryPendingApprovals());
}
/**
* 导出项目绩效自评列表
*/
@SaCheckPermission("cw:evaluation:export")
@Log(title = "项目绩效自评", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(CwProjectPerformanceSelfEvaluationBo bo, HttpServletResponse response) {
List<CwProjectPerformanceSelfEvaluationVo> list = evaluationService.queryList(bo);
ExcelUtil.exportExcel(list, "项目绩效自评", CwProjectPerformanceSelfEvaluationVo.class, response);
}
}
@@ -0,0 +1,82 @@
package org.dromara.cw.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* 绩效运行监控对象 cw_performance_monitoring
*
* @author ruoyi
* @date 2025-11-04
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("cw_performance_monitoring")
public class CwPerformanceMonitoring extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 绩效自评ID
*/
private Long evaluationId;
/**
* 项目申报ID
*/
private Long projectApplicationId;
/**
* 监控周期
*/
private String monitoringPeriod;
/**
* 资金使用情况
*/
private String fundUsage;
/**
* 绩效目标完成情况
*/
private String performanceTargetCompletion;
/**
* 绩效指标完成情况(JSON数组)
*/
private String indicators;
/**
* 存在问题
*/
private String existingProblems;
/**
* 指标差异率分析和说明
*/
private String indicatorVarianceAnalysis;
/**
* 下一步工作建议和打算
*/
private String nextStepSuggestions;
/**
* 填报人ID
*/
private Long fillBy;
/**
* 填报人姓名
*/
private String fillByName;
}
@@ -0,0 +1,90 @@
package org.dromara.cw.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
/**
* 项目绩效自评指标明细对象 cw_project_evaluation_indicator
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("cw_project_evaluation_indicator")
public class CwProjectEvaluationIndicator extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 绩效自评ID
*/
private Long evaluationId;
/**
* 指标类别(decision-决策/process-过程/output-产出/benefit-效益)
*/
private String indicatorCategory;
/**
* 指标名称
*/
private String indicatorName;
/**
* 指标类型(quantity-数量/quality-质量/time-时效/cost-成本)
*/
private String indicatorType;
/**
* 目标值
*/
private String targetValue;
/**
* 实际完成值
*/
private String actualValue;
/**
* 完成率(%)
*/
private BigDecimal completionRate;
/**
* 指标分值
*/
private BigDecimal standardScore;
/**
* 实际得分
*/
private BigDecimal actualScore;
/**
* 完成情况说明
*/
private String completionDescription;
/**
* 排序号
*/
private Integer sortOrder;
/**
* 逻辑删除标志(0-正常 1-删除)
*/
@TableLogic
private Integer deleted;
}
@@ -0,0 +1,146 @@
package org.dromara.cw.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
import java.util.Date;
/**
* 项目月度运行监控对象 cw_project_monthly_monitoring
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("cw_project_monthly_monitoring")
public class CwProjectMonthlyMonitoring extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 绩效自评ID
*/
private Long evaluationId;
/**
* 项目申报ID
*/
private Long projectApplicationId;
/**
* 监控年度
*/
private Integer monitoringYear;
/**
* 监控月份(1-12)
*/
private Integer monitoringMonth;
/**
* 计划进度(%)
*/
private BigDecimal plannedProgress;
/**
* 实际进度(%)
*/
private BigDecimal actualProgress;
/**
* 进度偏差(%)
*/
private BigDecimal progressVariance;
/**
* 本月预算(万元)
*/
private BigDecimal monthlyBudget;
/**
* 本月实际支出(万元)
*/
private BigDecimal monthlyExpenditure;
/**
* 累计预算(万元)
*/
private BigDecimal cumulativeBudget;
/**
* 累计实际支出(万元)
*/
private BigDecimal cumulativeExpenditure;
/**
* 资金执行率(%)
*/
private BigDecimal fundExecutionRate;
/**
* 本月完成的关键节点
*/
private String keyMilestones;
/**
* 节点完成情况(completed-已完成/delayed-延期/notStarted-未开始)
*/
private String milestoneCompletion;
/**
* 本月遇到的问题
*/
private String problemsEncountered;
/**
* 风险描述
*/
private String riskDescription;
/**
* 应对措施
*/
private String countermeasures;
/**
* 下月工作计划
*/
private String nextMonthPlan;
/**
* 填报状态(not_filled-未填报/filled-已填报)
*/
private String fillStatus;
/**
* 填报时间
*/
private Date fillTime;
/**
* 填报人ID
*/
private Long fillBy;
/**
* 填报人姓名
*/
private String fillByName;
/**
* 逻辑删除标志(0-正常 1-删除)
*/
@TableLogic
private Integer deleted;
}
@@ -0,0 +1,591 @@
package org.dromara.cw.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
import java.util.Date;
/**
* 项目绩效自评对象 cw_project_performance_self_evaluation
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("cw_project_performance_self_evaluation")
public class CwProjectPerformanceSelfEvaluation extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 项目申报ID
*/
private Long projectApplicationId;
/**
* 申报编号
*/
private String applicationNo;
/**
* 项目名称
*/
private String projectName;
/**
* 主管部门
*/
private String supervisoryDept;
/**
* 实施单位
*/
private String implementUnit;
/**
* 自评年度
*/
private Integer evaluationYear;
/**
* 年初预算数(万元)
*/
private BigDecimal initialBudget;
/**
* 项目负责人
*/
private String principal;
/**
* 联系电话
*/
private String contactPhone;
/**
* 项目实施开始时间
*/
private Date startDate;
/**
* 项目实施结束时间
*/
private Date endDate;
/**
* 项目总投资(万元)
*/
private BigDecimal totalInvestment;
/**
* 本年度预算(万元)
*/
private BigDecimal annualBudget;
/**
* 本年度实际支出(万元)
*/
private BigDecimal actualExpenditure;
/**
* 预算执行率(%)
*/
private BigDecimal budgetExecutionRate;
/**
* 资金到位分值
*/
private BigDecimal fundValue;
/**
* 资金执行率(%)
*/
private BigDecimal fundExecutionRate;
/**
* 资金到位得分
*/
private BigDecimal fundScore;
/**
* 当年财政拨款-年初预算数(万元)
*/
private BigDecimal currentYearBudgetInitial;
/**
* 当年财政拨款-全年预算数(万元)
*/
private BigDecimal currentYearBudgetAnnual;
/**
* 当年财政拨款-全年执行数(万元)
*/
private BigDecimal currentYearBudgetActual;
/**
* 当年财政拨款-分值
*/
private BigDecimal currentYearBudgetValue;
/**
* 当年财政拨款-执行率(%)
*/
private BigDecimal currentYearBudgetExecutionRate;
/**
* 当年财政拨款-得分
*/
private BigDecimal currentYearBudgetScore;
/**
* 上年结转资金-年初预算数(万元)
*/
private BigDecimal lastYearCarryoverInitial;
/**
* 上年结转资金-全年预算数(万元)
*/
private BigDecimal lastYearCarryoverAnnual;
/**
* 上年结转资金-全年执行数(万元)
*/
private BigDecimal lastYearCarryoverActual;
/**
* 上年结转资金-分值
*/
private BigDecimal lastYearCarryoverValue;
/**
* 上年结转资金-执行率(%)
*/
private BigDecimal lastYearCarryoverExecutionRate;
/**
* 上年结转资金-得分
*/
private BigDecimal lastYearCarryoverScore;
/**
* 其他资金-年初预算数(万元)
*/
private BigDecimal otherFundsInitial;
/**
* 其他资金-全年预算数(万元)
*/
private BigDecimal otherFundsAnnual;
/**
* 其他资金-全年执行数(万元)
*/
private BigDecimal otherFundsActual;
/**
* 其他资金-分值
*/
private BigDecimal otherFundsValue;
/**
* 其他资金-执行率(%)
*/
private BigDecimal otherFundsExecutionRate;
/**
* 其他资金-得分
*/
private BigDecimal otherFundsScore;
/**
* 年度绩效目标
*/
private String performanceGoal;
/**
* 目标完成情况说明
*/
private String targetCompletion;
/**
* 数量指标名称
*/
private String quantityIndicator;
/**
* 数量指标-指标值
*/
private String quantityTarget;
/**
* 数量指标-实际完成值
*/
private String quantityActual;
/**
* 数量指标-分值
*/
private BigDecimal quantityValue;
/**
* 数量指标-得分
*/
private BigDecimal quantityScore;
/**
* 数量指标-偏差原因分析及改进措施
*/
private String quantityDeviation;
/**
* 质量指标名称
*/
private String qualityIndicator;
/**
* 质量指标-指标值
*/
private String qualityTarget;
/**
* 质量指标-实际完成值
*/
private String qualityActual;
/**
* 质量指标-分值
*/
private BigDecimal qualityValue;
/**
* 质量指标-得分
*/
private BigDecimal qualityScore;
/**
* 质量指标-偏差原因分析及改进措施
*/
private String qualityDeviation;
/**
* 时效指标名称
*/
private String timelinessIndicator;
/**
* 时效指标-指标值
*/
private String timelinessTarget;
/**
* 时效指标-实际完成值
*/
private String timelinessActual;
/**
* 时效指标-分值
*/
private BigDecimal timelinessValue;
/**
* 时效指标-得分
*/
private BigDecimal timelinessScore;
/**
* 时效指标-偏差原因分析及改进措施
*/
private String timelinessDeviation;
/**
* 成本指标名称
*/
private String costIndicator;
/**
* 成本指标-指标值
*/
private String costTarget;
/**
* 成本指标-实际完成值
*/
private String costActual;
/**
* 成本指标-分值
*/
private BigDecimal costValue;
/**
* 成本指标-得分
*/
private BigDecimal costScore;
/**
* 成本指标-偏差原因分析及改进措施
*/
private String costDeviation;
/**
* 经济效益指标名称
*/
private String economicIndicator;
/**
* 经济效益指标-指标值
*/
private String economicTarget;
/**
* 经济效益指标-实际完成值
*/
private String economicActual;
/**
* 经济效益指标-分值
*/
private BigDecimal economicValue;
/**
* 经济效益指标-得分
*/
private BigDecimal economicScore;
/**
* 经济效益指标-偏差原因分析及改进措施
*/
private String economicDeviation;
/**
* 社会效益指标名称
*/
private String socialIndicator;
/**
* 社会效益指标-指标值
*/
private String socialTarget;
/**
* 社会效益指标-实际完成值
*/
private String socialActual;
/**
* 社会效益指标-分值
*/
private BigDecimal socialValue;
/**
* 社会效益指标-得分
*/
private BigDecimal socialScore;
/**
* 社会效益指标-偏差原因分析及改进措施
*/
private String socialDeviation;
/**
* 生态效益指标名称
*/
private String ecologicalIndicator;
/**
* 生态效益指标-指标值
*/
private String ecologicalTarget;
/**
* 生态效益指标-实际完成值
*/
private String ecologicalActual;
/**
* 生态效益指标-分值
*/
private BigDecimal ecologicalValue;
/**
* 生态效益指标-得分
*/
private BigDecimal ecologicalScore;
/**
* 生态效益指标-偏差原因分析及改进措施
*/
private String ecologicalDeviation;
/**
* 满意度指标名称
*/
private String satisfactionIndicator;
/**
* 满意度指标-指标值
*/
private String satisfactionTarget;
/**
* 满意度指标-实际完成值
*/
private String satisfactionActual;
/**
* 满意度指标-分值
*/
private BigDecimal satisfactionValue;
/**
* 满意度指标-得分
*/
private BigDecimal satisfactionScore;
/**
* 满意度指标-偏差原因分析及改进措施
*/
private String satisfactionDeviation;
/**
* 项目决策得分
*/
private BigDecimal decisionScore;
/**
* 项目决策自评内容
*/
private String decisionContent;
/**
* 项目过程得分
*/
private BigDecimal processScore;
/**
* 项目过程自评内容
*/
private String processContent;
/**
* 项目产出得分
*/
private BigDecimal outputScore;
/**
* 项目产出自评内容
*/
private String outputContent;
/**
* 项目效益得分
*/
private BigDecimal benefitScore;
/**
* 项目效益自评内容
*/
private String benefitContent;
/**
* 自评总分
*/
private BigDecimal totalScore;
/**
* 自评等级(优秀/良好/合格/不合格)
*/
private String selfEvaluationLevel;
/**
* 存在的主要问题
*/
private String existingProblems;
/**
* 改进措施和建议
*/
private String improvementMeasures;
/**
* 自评状态(draft-草稿 submitted-已提交 approved-已审核)
*/
private String evaluationStatus;
/**
* 允许已提交后编辑(0-不允许 1-允许)
*/
private Integer allowEditAfterSubmit;
/**
* 提交时间
*/
private Date submitTime;
/**
* 提交人ID
*/
private Long submitBy;
/**
* 提交人姓名
*/
private String submitByName;
/**
* 审核时间
*/
private Date approveTime;
/**
* 审核人ID
*/
private Long approveBy;
/**
* 审核人姓名
*/
private String approveByName;
/**
* 审核意见
*/
private String approveOpinion;
/**
* 是否锁定(0-未锁定 1-已锁定),锁定后不可修改
*/
private Integer isLocked;
/**
* 锁定时间
*/
private Date lockTime;
/**
* 锁定人ID
*/
private Long lockBy;
/**
* 锁定人姓名
*/
private String lockByName;
/**
* 逻辑删除标志(0-正常 1-删除)
*/
@TableLogic
private Integer deleted;
}
@@ -0,0 +1,88 @@
package org.dromara.cw.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.cw.domain.CwPerformanceMonitoring;
/**
* 绩效运行监控业务对象 cw_performance_monitoring
*
* @author ruoyi
* @date 2025-11-04
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = CwPerformanceMonitoring.class, reverseConvertGenerate = false)
public class CwPerformanceMonitoringBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 绩效自评ID
*/
@NotNull(message = "绩效自评ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long evaluationId;
/**
* 项目申报ID
*/
private Long projectApplicationId;
/**
* 监控周期
*/
@NotBlank(message = "监控周期不能为空", groups = { AddGroup.class, EditGroup.class })
private String monitoringPeriod;
/**
* 资金使用情况
*/
@NotBlank(message = "资金使用情况不能为空", groups = { AddGroup.class, EditGroup.class })
private String fundUsage;
/**
* 绩效目标完成情况
*/
@NotBlank(message = "绩效目标完成情况不能为空", groups = { AddGroup.class, EditGroup.class })
private String performanceTargetCompletion;
/**
* 绩效指标完成情况(JSON数组)
*/
private String indicators;
/**
* 存在问题
*/
private String existingProblems;
/**
* 指标差异率分析和说明
*/
private String indicatorVarianceAnalysis;
/**
* 下一步工作建议和打算
*/
private String nextStepSuggestions;
/**
* 填报人ID
*/
private Long fillBy;
/**
* 填报人姓名
*/
private String fillByName;
}
@@ -0,0 +1,91 @@
package org.dromara.cw.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.cw.domain.CwProjectEvaluationIndicator;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
/**
* 项目绩效自评指标明细业务对象 cw_project_evaluation_indicator
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = CwProjectEvaluationIndicator.class, reverseConvertGenerate = false)
public class CwProjectEvaluationIndicatorBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 绩效自评ID
*/
@NotNull(message = "绩效自评ID不能为空", groups = {AddGroup.class})
private Long evaluationId;
/**
* 指标类别(decision-决策/process-过程/output-产出/benefit-效益)
*/
@NotBlank(message = "指标类别不能为空", groups = {AddGroup.class})
private String indicatorCategory;
/**
* 指标名称
*/
@NotBlank(message = "指标名称不能为空", groups = {AddGroup.class})
private String indicatorName;
/**
* 指标类型(quantity-数量/quality-质量/time-时效/cost-成本)
*/
private String indicatorType;
/**
* 目标值
*/
private String targetValue;
/**
* 实际完成值
*/
private String actualValue;
/**
* 完成率(%)
*/
@DecimalMin(value = "0", message = "完成率不能小于0")
@DecimalMax(value = "200", message = "完成率不能大于200")
private BigDecimal completionRate;
/**
* 指标分值
*/
private BigDecimal standardScore;
/**
* 实际得分
*/
private BigDecimal actualScore;
/**
* 完成情况说明
*/
private String completionDescription;
/**
* 排序号
*/
private Integer sortOrder;
}
@@ -0,0 +1,152 @@
package org.dromara.cw.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.cw.domain.CwProjectMonthlyMonitoring;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.util.Date;
/**
* 项目月度运行监控业务对象 cw_project_monthly_monitoring
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = CwProjectMonthlyMonitoring.class, reverseConvertGenerate = false)
public class CwProjectMonthlyMonitoringBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 绩效自评ID
*/
@NotNull(message = "绩效自评ID不能为空", groups = {AddGroup.class})
private Long evaluationId;
/**
* 项目申报ID
*/
@NotNull(message = "项目申报ID不能为空", groups = {AddGroup.class})
private Long projectApplicationId;
/**
* 监控年度
*/
@NotNull(message = "监控年度不能为空", groups = {AddGroup.class})
private Integer monitoringYear;
/**
* 监控月份(1-12)
*/
@NotNull(message = "监控月份不能为空", groups = {AddGroup.class})
@Min(value = 1, message = "监控月份最小为1")
@Max(value = 12, message = "监控月份最大为12")
private Integer monitoringMonth;
/**
* 计划进度(%)
*/
@DecimalMin(value = "0", message = "计划进度不能小于0")
@DecimalMax(value = "100", message = "计划进度不能大于100")
private BigDecimal plannedProgress;
/**
* 实际进度(%)
*/
@DecimalMin(value = "0", message = "实际进度不能小于0")
@DecimalMax(value = "100", message = "实际进度不能大于100")
private BigDecimal actualProgress;
/**
* 进度偏差(%)
*/
private BigDecimal progressVariance;
/**
* 本月预算(万元)
*/
private BigDecimal monthlyBudget;
/**
* 本月实际支出(万元)
*/
private BigDecimal monthlyExpenditure;
/**
* 累计预算(万元)
*/
private BigDecimal cumulativeBudget;
/**
* 累计实际支出(万元)
*/
private BigDecimal cumulativeExpenditure;
/**
* 资金执行率(%)
*/
private BigDecimal fundExecutionRate;
/**
* 本月完成的关键节点
*/
private String keyMilestones;
/**
* 节点完成情况(completed-已完成/delayed-延期/notStarted-未开始)
*/
private String milestoneCompletion;
/**
* 本月遇到的问题
*/
private String problemsEncountered;
/**
* 风险描述
*/
private String riskDescription;
/**
* 应对措施
*/
private String countermeasures;
/**
* 下月工作计划
*/
private String nextMonthPlan;
/**
* 填报状态(not_filled-未填报/filled-已填报)
*/
private String fillStatus;
/**
* 填报时间
*/
private Date fillTime;
/**
* 填报人ID
*/
private Long fillBy;
/**
* 填报人姓名
*/
private String fillByName;
}
@@ -0,0 +1,583 @@
package org.dromara.cw.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.cw.domain.CwProjectPerformanceSelfEvaluation;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.util.Date;
/**
* 项目绩效自评业务对象 cw_project_performance_self_evaluation
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = CwProjectPerformanceSelfEvaluation.class, reverseConvertGenerate = false)
public class CwProjectPerformanceSelfEvaluationBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 项目申报ID
*/
@NotNull(message = "项目申报ID不能为空", groups = {AddGroup.class})
private Long projectApplicationId;
/**
* 申报编号
*/
private String applicationNo;
/**
* 项目名称
*/
@NotBlank(message = "项目名称不能为空", groups = {AddGroup.class})
private String projectName;
/**
* 主管部门
*/
private String supervisoryDept;
/**
* 实施单位
*/
private String implementUnit;
/**
* 自评年度
*/
@NotNull(message = "自评年度不能为空", groups = {AddGroup.class})
private Integer evaluationYear;
/**
* 年初预算数(万元)
*/
private BigDecimal initialBudget;
/**
* 项目负责人
*/
private String principal;
/**
* 联系电话
*/
private String contactPhone;
/**
* 项目实施开始时间
*/
private Date startDate;
/**
* 项目实施结束时间
*/
private Date endDate;
/**
* 项目总投资(万元)
*/
private BigDecimal totalInvestment;
/**
* 本年度预算(万元)
*/
private BigDecimal annualBudget;
/**
* 本年度实际支出(万元)
*/
private BigDecimal actualExpenditure;
/**
* 预算执行率(%)
*/
private BigDecimal budgetExecutionRate;
/**
* 资金到位分值
*/
private BigDecimal fundValue;
/**
* 资金执行率(%)
*/
private BigDecimal fundExecutionRate;
/**
* 资金到位得分
*/
private BigDecimal fundScore;
/**
* 当年财政拨款-年初预算数(万元)
*/
private BigDecimal currentYearBudgetInitial;
/**
* 当年财政拨款-全年预算数(万元)
*/
private BigDecimal currentYearBudgetAnnual;
/**
* 当年财政拨款-全年执行数(万元)
*/
private BigDecimal currentYearBudgetActual;
/**
* 当年财政拨款-分值
*/
private BigDecimal currentYearBudgetValue;
/**
* 当年财政拨款-执行率(%)
*/
private BigDecimal currentYearBudgetExecutionRate;
/**
* 当年财政拨款-得分
*/
private BigDecimal currentYearBudgetScore;
/**
* 上年结转资金-年初预算数(万元)
*/
private BigDecimal lastYearCarryoverInitial;
/**
* 上年结转资金-全年预算数(万元)
*/
private BigDecimal lastYearCarryoverAnnual;
/**
* 上年结转资金-全年执行数(万元)
*/
private BigDecimal lastYearCarryoverActual;
/**
* 上年结转资金-分值
*/
private BigDecimal lastYearCarryoverValue;
/**
* 上年结转资金-执行率(%)
*/
private BigDecimal lastYearCarryoverExecutionRate;
/**
* 上年结转资金-得分
*/
private BigDecimal lastYearCarryoverScore;
/**
* 其他资金-年初预算数(万元)
*/
private BigDecimal otherFundsInitial;
/**
* 其他资金-全年预算数(万元)
*/
private BigDecimal otherFundsAnnual;
/**
* 其他资金-全年执行数(万元)
*/
private BigDecimal otherFundsActual;
/**
* 其他资金-分值
*/
private BigDecimal otherFundsValue;
/**
* 其他资金-执行率(%)
*/
private BigDecimal otherFundsExecutionRate;
/**
* 其他资金-得分
*/
private BigDecimal otherFundsScore;
/**
* 年度绩效目标
*/
private String performanceGoal;
/**
* 目标完成情况说明
*/
private String targetCompletion;
/**
* 数量指标名称
*/
private String quantityIndicator;
/**
* 数量指标-指标值
*/
private String quantityTarget;
/**
* 数量指标-实际完成值
*/
private String quantityActual;
/**
* 数量指标-分值
*/
private BigDecimal quantityValue;
/**
* 数量指标-得分
*/
private BigDecimal quantityScore;
/**
* 数量指标-偏差原因分析及改进措施
*/
private String quantityDeviation;
/**
* 质量指标名称
*/
private String qualityIndicator;
/**
* 质量指标-指标值
*/
private String qualityTarget;
/**
* 质量指标-实际完成值
*/
private String qualityActual;
/**
* 质量指标-分值
*/
private BigDecimal qualityValue;
/**
* 质量指标-得分
*/
private BigDecimal qualityScore;
/**
* 质量指标-偏差原因分析及改进措施
*/
private String qualityDeviation;
/**
* 时效指标名称
*/
private String timelinessIndicator;
/**
* 时效指标-指标值
*/
private String timelinessTarget;
/**
* 时效指标-实际完成值
*/
private String timelinessActual;
/**
* 时效指标-分值
*/
private BigDecimal timelinessValue;
/**
* 时效指标-得分
*/
private BigDecimal timelinessScore;
/**
* 时效指标-偏差原因分析及改进措施
*/
private String timelinessDeviation;
/**
* 成本指标名称
*/
private String costIndicator;
/**
* 成本指标-指标值
*/
private String costTarget;
/**
* 成本指标-实际完成值
*/
private String costActual;
/**
* 成本指标-分值
*/
private BigDecimal costValue;
/**
* 成本指标-得分
*/
private BigDecimal costScore;
/**
* 成本指标-偏差原因分析及改进措施
*/
private String costDeviation;
/**
* 经济效益指标名称
*/
private String economicIndicator;
/**
* 经济效益指标-指标值
*/
private String economicTarget;
/**
* 经济效益指标-实际完成值
*/
private String economicActual;
/**
* 经济效益指标-分值
*/
private BigDecimal economicValue;
/**
* 经济效益指标-得分
*/
private BigDecimal economicScore;
/**
* 经济效益指标-偏差原因分析及改进措施
*/
private String economicDeviation;
/**
* 社会效益指标名称
*/
private String socialIndicator;
/**
* 社会效益指标-指标值
*/
private String socialTarget;
/**
* 社会效益指标-实际完成值
*/
private String socialActual;
/**
* 社会效益指标-分值
*/
private BigDecimal socialValue;
/**
* 社会效益指标-得分
*/
private BigDecimal socialScore;
/**
* 社会效益指标-偏差原因分析及改进措施
*/
private String socialDeviation;
/**
* 生态效益指标名称
*/
private String ecologicalIndicator;
/**
* 生态效益指标-指标值
*/
private String ecologicalTarget;
/**
* 生态效益指标-实际完成值
*/
private String ecologicalActual;
/**
* 生态效益指标-分值
*/
private BigDecimal ecologicalValue;
/**
* 生态效益指标-得分
*/
private BigDecimal ecologicalScore;
/**
* 生态效益指标-偏差原因分析及改进措施
*/
private String ecologicalDeviation;
/**
* 满意度指标名称
*/
private String satisfactionIndicator;
/**
* 满意度指标-指标值
*/
private String satisfactionTarget;
/**
* 满意度指标-实际完成值
*/
private String satisfactionActual;
/**
* 满意度指标-分值
*/
private BigDecimal satisfactionValue;
/**
* 满意度指标-得分
*/
private BigDecimal satisfactionScore;
/**
* 满意度指标-偏差原因分析及改进措施
*/
private String satisfactionDeviation;
/**
* 项目决策得分
*/
@DecimalMin(value = "0", message = "项目决策得分不能小于0")
@DecimalMax(value = "20", message = "项目决策得分不能大于20")
private BigDecimal decisionScore;
/**
* 项目决策自评内容
*/
private String decisionContent;
/**
* 项目过程得分
*/
@DecimalMin(value = "0", message = "项目过程得分不能小于0")
@DecimalMax(value = "20", message = "项目过程得分不能大于20")
private BigDecimal processScore;
/**
* 项目过程自评内容
*/
private String processContent;
/**
* 项目产出得分
*/
@DecimalMin(value = "0", message = "项目产出得分不能小于0")
@DecimalMax(value = "30", message = "项目产出得分不能大于30")
private BigDecimal outputScore;
/**
* 项目产出自评内容
*/
private String outputContent;
/**
* 项目效益得分
*/
@DecimalMin(value = "0", message = "项目效益得分不能小于0")
@DecimalMax(value = "30", message = "项目效益得分不能大于30")
private BigDecimal benefitScore;
/**
* 项目效益自评内容
*/
private String benefitContent;
/**
* 自评总分
*/
private BigDecimal totalScore;
/**
* 自评等级(优秀/良好/合格/不合格)
*/
private String selfEvaluationLevel;
/**
* 存在的主要问题
*/
private String existingProblems;
/**
* 改进措施和建议
*/
private String improvementMeasures;
/**
* 自评状态(draft-草稿 submitted-已提交 approved-已审核)
*/
private String evaluationStatus;
/**
* 允许已提交后编辑(0-不允许 1-允许)
*/
private Integer allowEditAfterSubmit;
/**
* 提交时间
*/
private Date submitTime;
/**
* 提交人ID
*/
private Long submitBy;
/**
* 提交人姓名
*/
private String submitByName;
/**
* 审核时间
*/
private Date approveTime;
/**
* 审核人ID
*/
private Long approveBy;
/**
* 审核人姓名
*/
private String approveByName;
/**
* 审核意见
*/
private String approveOpinion;
/**
* 是否锁定(0-未锁定 1-已锁定),锁定后不可修改
*/
private Integer isLocked;
}
@@ -0,0 +1,128 @@
package org.dromara.cw.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.cw.domain.CwPerformanceMonitoring;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 绩效运行监控视图对象 cw_performance_monitoring
*
* @author ruoyi
* @date 2025-11-04
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = CwPerformanceMonitoring.class)
public class CwPerformanceMonitoringVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 绩效自评ID
*/
@ExcelProperty(value = "绩效自评ID")
private Long evaluationId;
/**
* 项目申报ID
*/
@ExcelProperty(value = "项目申报ID")
private Long projectApplicationId;
/**
* 监控周期
*/
@ExcelProperty(value = "监控周期")
private String monitoringPeriod;
/**
* 资金使用情况
*/
@ExcelProperty(value = "资金使用情况")
private String fundUsage;
/**
* 绩效目标完成情况
*/
@ExcelProperty(value = "绩效目标完成情况")
private String performanceTargetCompletion;
/**
* 绩效指标完成情况(JSON数组)
*/
@ExcelProperty(value = "绩效指标完成情况")
private String indicators;
/**
* 存在问题
*/
@ExcelProperty(value = "存在问题")
private String existingProblems;
/**
* 指标差异率分析和说明
*/
@ExcelProperty(value = "指标差异率分析和说明")
private String indicatorVarianceAnalysis;
/**
* 下一步工作建议和打算
*/
@ExcelProperty(value = "下一步工作建议和打算")
private String nextStepSuggestions;
/**
* 填报人ID
*/
private Long fillBy;
/**
* 填报人姓名
*/
@ExcelProperty(value = "填报人")
private String fillByName;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,118 @@
package org.dromara.cw.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.cw.domain.CwProjectEvaluationIndicator;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 项目绩效自评指标明细视图对象 cw_project_evaluation_indicator
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = CwProjectEvaluationIndicator.class)
public class CwProjectEvaluationIndicatorVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 绩效自评ID
*/
@ExcelProperty(value = "绩效自评ID")
private Long evaluationId;
/**
* 指标类别(decision-决策/process-过程/output-产出/benefit-效益)
*/
@ExcelProperty(value = "指标类别")
private String indicatorCategory;
/**
* 指标名称
*/
@ExcelProperty(value = "指标名称")
private String indicatorName;
/**
* 指标类型(quantity-数量/quality-质量/time-时效/cost-成本)
*/
@ExcelProperty(value = "指标类型")
private String indicatorType;
/**
* 目标值
*/
@ExcelProperty(value = "目标值")
private String targetValue;
/**
* 实际完成值
*/
@ExcelProperty(value = "实际完成值")
private String actualValue;
/**
* 完成率(%)
*/
@ExcelProperty(value = "完成率(%)")
private BigDecimal completionRate;
/**
* 指标分值
*/
@ExcelProperty(value = "指标分值")
private BigDecimal standardScore;
/**
* 实际得分
*/
@ExcelProperty(value = "实际得分")
private BigDecimal actualScore;
/**
* 完成情况说明
*/
@ExcelProperty(value = "完成情况说明")
private String completionDescription;
/**
* 排序号
*/
@ExcelProperty(value = "排序号")
private Integer sortOrder;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,183 @@
package org.dromara.cw.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.cw.domain.CwProjectMonthlyMonitoring;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 项目月度运行监控视图对象 cw_project_monthly_monitoring
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = CwProjectMonthlyMonitoring.class)
public class CwProjectMonthlyMonitoringVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 绩效自评ID
*/
@ExcelProperty(value = "绩效自评ID")
private Long evaluationId;
/**
* 项目申报ID
*/
@ExcelProperty(value = "项目申报ID")
private Long projectApplicationId;
/**
* 监控年度
*/
@ExcelProperty(value = "监控年度")
private Integer monitoringYear;
/**
* 监控月份(1-12)
*/
@ExcelProperty(value = "监控月份")
private Integer monitoringMonth;
/**
* 计划进度(%)
*/
@ExcelProperty(value = "计划进度(%)")
private BigDecimal plannedProgress;
/**
* 实际进度(%)
*/
@ExcelProperty(value = "实际进度(%)")
private BigDecimal actualProgress;
/**
* 进度偏差(%)
*/
@ExcelProperty(value = "进度偏差(%)")
private BigDecimal progressVariance;
/**
* 本月预算(万元)
*/
@ExcelProperty(value = "本月预算(万元)")
private BigDecimal monthlyBudget;
/**
* 本月实际支出(万元)
*/
@ExcelProperty(value = "本月实际支出(万元)")
private BigDecimal monthlyExpenditure;
/**
* 累计预算(万元)
*/
@ExcelProperty(value = "累计预算(万元)")
private BigDecimal cumulativeBudget;
/**
* 累计实际支出(万元)
*/
@ExcelProperty(value = "累计实际支出(万元)")
private BigDecimal cumulativeExpenditure;
/**
* 资金执行率(%)
*/
@ExcelProperty(value = "资金执行率(%)")
private BigDecimal fundExecutionRate;
/**
* 本月完成的关键节点
*/
@ExcelProperty(value = "关键节点")
private String keyMilestones;
/**
* 节点完成情况(completed-已完成/delayed-延期/notStarted-未开始)
*/
@ExcelProperty(value = "节点完成情况")
private String milestoneCompletion;
/**
* 本月遇到的问题
*/
@ExcelProperty(value = "遇到的问题")
private String problemsEncountered;
/**
* 风险描述
*/
@ExcelProperty(value = "风险描述")
private String riskDescription;
/**
* 应对措施
*/
@ExcelProperty(value = "应对措施")
private String countermeasures;
/**
* 下月工作计划
*/
@ExcelProperty(value = "下月工作计划")
private String nextMonthPlan;
/**
* 填报状态(not_filled-未填报/filled-已填报)
*/
@ExcelProperty(value = "填报状态")
private String fillStatus;
/**
* 填报时间
*/
@ExcelProperty(value = "填报时间")
private Date fillTime;
/**
* 填报人ID
*/
private Long fillBy;
/**
* 填报人姓名
*/
@ExcelProperty(value = "填报人姓名")
private String fillByName;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,666 @@
package org.dromara.cw.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.cw.domain.CwProjectPerformanceSelfEvaluation;
import org.dromara.cw.domain.vo.CwPerformanceMonitoringVo;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 项目绩效自评视图对象 cw_project_performance_self_evaluation
*
* @author ruoyi
* @date 2025-11-03
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = CwProjectPerformanceSelfEvaluation.class)
public class CwProjectPerformanceSelfEvaluationVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 项目申报ID
*/
@ExcelProperty(value = "项目申报ID")
private Long projectApplicationId;
/**
* 申报编号
*/
@ExcelProperty(value = "申报编号")
private String applicationNo;
/**
* 项目名称
*/
@ExcelProperty(value = "项目名称")
private String projectName;
/**
* 主管部门
*/
@ExcelProperty(value = "主管部门")
private String supervisoryDept;
/**
* 实施单位
*/
@ExcelProperty(value = "实施单位")
private String implementUnit;
/**
* 自评年度
*/
@ExcelProperty(value = "自评年度")
private Integer evaluationYear;
/**
* 年初预算数(万元)
*/
@ExcelProperty(value = "年初预算数(万元)")
private BigDecimal initialBudget;
/**
* 项目负责人
*/
@ExcelProperty(value = "项目负责人")
private String principal;
/**
* 联系电话
*/
@ExcelProperty(value = "联系电话")
private String contactPhone;
/**
* 项目实施开始时间
*/
@ExcelProperty(value = "项目实施开始时间")
private Date startDate;
/**
* 项目实施结束时间
*/
@ExcelProperty(value = "项目实施结束时间")
private Date endDate;
/**
* 项目总投资(万元)
*/
@ExcelProperty(value = "项目总投资(万元)")
private BigDecimal totalInvestment;
/**
* 本年度预算(万元)
*/
@ExcelProperty(value = "本年度预算(万元)")
private BigDecimal annualBudget;
/**
* 本年度实际支出(万元)
*/
@ExcelProperty(value = "本年度实际支出(万元)")
private BigDecimal actualExpenditure;
/**
* 预算执行率(%)
*/
@ExcelProperty(value = "预算执行率(%)")
private BigDecimal budgetExecutionRate;
/**
* 资金到位分值
*/
private BigDecimal fundValue;
/**
* 资金执行率(%)
*/
private BigDecimal fundExecutionRate;
/**
* 资金到位得分
*/
private BigDecimal fundScore;
/**
* 当年财政拨款-年初预算数(万元)
*/
private BigDecimal currentYearBudgetInitial;
/**
* 当年财政拨款-全年预算数(万元)
*/
private BigDecimal currentYearBudgetAnnual;
/**
* 当年财政拨款-全年执行数(万元)
*/
private BigDecimal currentYearBudgetActual;
/**
* 当年财政拨款-分值
*/
private BigDecimal currentYearBudgetValue;
/**
* 当年财政拨款-执行率(%)
*/
private BigDecimal currentYearBudgetExecutionRate;
/**
* 当年财政拨款-得分
*/
private BigDecimal currentYearBudgetScore;
/**
* 上年结转资金-年初预算数(万元)
*/
private BigDecimal lastYearCarryoverInitial;
/**
* 上年结转资金-全年预算数(万元)
*/
private BigDecimal lastYearCarryoverAnnual;
/**
* 上年结转资金-全年执行数(万元)
*/
private BigDecimal lastYearCarryoverActual;
/**
* 上年结转资金-分值
*/
private BigDecimal lastYearCarryoverValue;
/**
* 上年结转资金-执行率(%)
*/
private BigDecimal lastYearCarryoverExecutionRate;
/**
* 上年结转资金-得分
*/
private BigDecimal lastYearCarryoverScore;
/**
* 其他资金-年初预算数(万元)
*/
private BigDecimal otherFundsInitial;
/**
* 其他资金-全年预算数(万元)
*/
private BigDecimal otherFundsAnnual;
/**
* 其他资金-全年执行数(万元)
*/
private BigDecimal otherFundsActual;
/**
* 其他资金-分值
*/
private BigDecimal otherFundsValue;
/**
* 其他资金-执行率(%)
*/
private BigDecimal otherFundsExecutionRate;
/**
* 其他资金-得分
*/
private BigDecimal otherFundsScore;
/**
* 年度绩效目标
*/
@ExcelProperty(value = "年度绩效目标")
private String performanceGoal;
/**
* 目标完成情况说明
*/
@ExcelProperty(value = "目标完成情况说明")
private String targetCompletion;
/**
* 数量指标名称
*/
private String quantityIndicator;
/**
* 数量指标-指标值
*/
private String quantityTarget;
/**
* 数量指标-实际完成值
*/
private String quantityActual;
/**
* 数量指标-分值
*/
private BigDecimal quantityValue;
/**
* 数量指标-得分
*/
private BigDecimal quantityScore;
/**
* 数量指标-偏差原因分析及改进措施
*/
private String quantityDeviation;
/**
* 质量指标名称
*/
private String qualityIndicator;
/**
* 质量指标-指标值
*/
private String qualityTarget;
/**
* 质量指标-实际完成值
*/
private String qualityActual;
/**
* 质量指标-分值
*/
private BigDecimal qualityValue;
/**
* 质量指标-得分
*/
private BigDecimal qualityScore;
/**
* 质量指标-偏差原因分析及改进措施
*/
private String qualityDeviation;
/**
* 时效指标名称
*/
private String timelinessIndicator;
/**
* 时效指标-指标值
*/
private String timelinessTarget;
/**
* 时效指标-实际完成值
*/
private String timelinessActual;
/**
* 时效指标-分值
*/
private BigDecimal timelinessValue;
/**
* 时效指标-得分
*/
private BigDecimal timelinessScore;
/**
* 时效指标-偏差原因分析及改进措施
*/
private String timelinessDeviation;
/**
* 成本指标名称
*/
private String costIndicator;
/**
* 成本指标-指标值
*/
private String costTarget;
/**
* 成本指标-实际完成值
*/
private String costActual;
/**
* 成本指标-分值
*/
private BigDecimal costValue;
/**
* 成本指标-得分
*/
private BigDecimal costScore;
/**
* 成本指标-偏差原因分析及改进措施
*/
private String costDeviation;
/**
* 经济效益指标名称
*/
private String economicIndicator;
/**
* 经济效益指标-指标值
*/
private String economicTarget;
/**
* 经济效益指标-实际完成值
*/
private String economicActual;
/**
* 经济效益指标-分值
*/
private BigDecimal economicValue;
/**
* 经济效益指标-得分
*/
private BigDecimal economicScore;
/**
* 经济效益指标-偏差原因分析及改进措施
*/
private String economicDeviation;
/**
* 社会效益指标名称
*/
private String socialIndicator;
/**
* 社会效益指标-指标值
*/
private String socialTarget;
/**
* 社会效益指标-实际完成值
*/
private String socialActual;
/**
* 社会效益指标-分值
*/
private BigDecimal socialValue;
/**
* 社会效益指标-得分
*/
private BigDecimal socialScore;
/**
* 社会效益指标-偏差原因分析及改进措施
*/
private String socialDeviation;
/**
* 生态效益指标名称
*/
private String ecologicalIndicator;
/**
* 生态效益指标-指标值
*/
private String ecologicalTarget;
/**
* 生态效益指标-实际完成值
*/
private String ecologicalActual;
/**
* 生态效益指标-分值
*/
private BigDecimal ecologicalValue;
/**
* 生态效益指标-得分
*/
private BigDecimal ecologicalScore;
/**
* 生态效益指标-偏差原因分析及改进措施
*/
private String ecologicalDeviation;
/**
* 满意度指标名称
*/
private String satisfactionIndicator;
/**
* 满意度指标-指标值
*/
private String satisfactionTarget;
/**
* 满意度指标-实际完成值
*/
private String satisfactionActual;
/**
* 满意度指标-分值
*/
private BigDecimal satisfactionValue;
/**
* 满意度指标-得分
*/
private BigDecimal satisfactionScore;
/**
* 满意度指标-偏差原因分析及改进措施
*/
private String satisfactionDeviation;
/**
* 项目决策得分
*/
@ExcelProperty(value = "项目决策得分")
private BigDecimal decisionScore;
/**
* 项目决策自评内容
*/
@ExcelProperty(value = "项目决策自评内容")
private String decisionContent;
/**
* 项目过程得分
*/
@ExcelProperty(value = "项目过程得分")
private BigDecimal processScore;
/**
* 项目过程自评内容
*/
@ExcelProperty(value = "项目过程自评内容")
private String processContent;
/**
* 项目产出得分
*/
@ExcelProperty(value = "项目产出得分")
private BigDecimal outputScore;
/**
* 项目产出自评内容
*/
@ExcelProperty(value = "项目产出自评内容")
private String outputContent;
/**
* 项目效益得分
*/
@ExcelProperty(value = "项目效益得分")
private BigDecimal benefitScore;
/**
* 项目效益自评内容
*/
@ExcelProperty(value = "项目效益自评内容")
private String benefitContent;
/**
* 自评总分
*/
@ExcelProperty(value = "自评总分")
private BigDecimal totalScore;
/**
* 自评等级(优秀/良好/合格/不合格)
*/
@ExcelProperty(value = "自评等级")
private String selfEvaluationLevel;
/**
* 存在的主要问题
*/
@ExcelProperty(value = "存在的主要问题")
private String existingProblems;
/**
* 改进措施和建议
*/
@ExcelProperty(value = "改进措施和建议")
private String improvementMeasures;
/**
* 自评状态(draft-草稿 submitted-已提交 approved-已审核)
*/
@ExcelProperty(value = "自评状态")
private String evaluationStatus;
/**
* 允许已提交后编辑(0-不允许 1-允许)
*/
@ExcelProperty(value = "允许已提交后编辑")
private Integer allowEditAfterSubmit;
/**
* 提交时间
*/
@ExcelProperty(value = "提交时间")
private Date submitTime;
/**
* 提交人ID
*/
private Long submitBy;
/**
* 提交人姓名
*/
@ExcelProperty(value = "提交人姓名")
private String submitByName;
/**
* 审核时间
*/
@ExcelProperty(value = "审核时间")
private Date approveTime;
/**
* 审核人ID
*/
private Long approveBy;
/**
* 审核人姓名
*/
@ExcelProperty(value = "审核人姓名")
private String approveByName;
/**
* 审核意见
*/
@ExcelProperty(value = "审核意见")
private String approveOpinion;
/**
* 是否锁定(0-未锁定 1-已锁定),锁定后不可修改
*/
@ExcelProperty(value = "锁定状态")
private Integer isLocked;
/**
* 锁定时间
*/
@ExcelProperty(value = "锁定时间")
private Date lockTime;
/**
* 锁定人ID
*/
private Long lockBy;
/**
* 锁定人姓名
*/
@ExcelProperty(value = "锁定人")
private String lockByName;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "更新时间")
private Date updateTime;
/**
* 运行监控数据列表(查看详情时返回)
*/
private List<CwPerformanceMonitoringVo> monitoringList;
}
@@ -0,0 +1,16 @@
package org.dromara.cw.mapper;
import org.dromara.cw.domain.CwPerformanceMonitoring;
import org.dromara.cw.domain.vo.CwPerformanceMonitoringVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 绩效运行监控Mapper接口
*
* @author ruoyi
* @date 2025-11-04
*/
public interface CwPerformanceMonitoringMapper extends BaseMapperPlus<CwPerformanceMonitoring, CwPerformanceMonitoringVo> {
}
@@ -0,0 +1,38 @@
package org.dromara.cw.mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.cw.domain.CwProjectEvaluationIndicator;
import org.dromara.cw.domain.vo.CwProjectEvaluationIndicatorVo;
import java.util.List;
/**
* 项目绩效自评指标明细Mapper接口
*
* @author ruoyi
* @date 2025-11-03
*/
public interface CwProjectEvaluationIndicatorMapper extends BaseMapperPlus<CwProjectEvaluationIndicator, CwProjectEvaluationIndicatorVo> {
/**
* 根据自评ID查询指标列表
*/
List<CwProjectEvaluationIndicatorVo> selectByEvaluationId(@Param("evaluationId") Long evaluationId);
/**
* 根据自评ID和指标类别查询指标列表
*/
List<CwProjectEvaluationIndicatorVo> selectByEvaluationIdAndCategory(@Param("evaluationId") Long evaluationId, @Param("indicatorCategory") String indicatorCategory);
/**
* 批量插入指标
*/
int batchInsert(@Param("list") List<CwProjectEvaluationIndicator> list);
/**
* 根据自评ID删除指标
*/
int deleteByEvaluationId(@Param("evaluationId") Long evaluationId);
}
@@ -0,0 +1,54 @@
package org.dromara.cw.mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.cw.domain.CwProjectMonthlyMonitoring;
import org.dromara.cw.domain.vo.CwProjectMonthlyMonitoringVo;
import java.math.BigDecimal;
import java.util.List;
/**
* 项目月度运行监控Mapper接口
*
* @author ruoyi
* @date 2025-11-03
*/
public interface CwProjectMonthlyMonitoringMapper extends BaseMapperPlus<CwProjectMonthlyMonitoring, CwProjectMonthlyMonitoringVo> {
/**
* 根据自评ID查询月度监控列表
*/
List<CwProjectMonthlyMonitoringVo> selectByEvaluationId(@Param("evaluationId") Long evaluationId);
/**
* 根据自评ID和年度查询月度监控列表
*/
List<CwProjectMonthlyMonitoringVo> selectByEvaluationIdAndYear(@Param("evaluationId") Long evaluationId, @Param("monitoringYear") Integer monitoringYear);
/**
* 根据自评ID、年度和月份查询月度监控
*/
CwProjectMonthlyMonitoringVo selectByEvaluationIdAndMonth(@Param("evaluationId") Long evaluationId, @Param("monitoringYear") Integer monitoringYear, @Param("monitoringMonth") Integer monitoringMonth);
/**
* 批量插入月度监控数据
*/
int batchInsert(@Param("list") List<CwProjectMonthlyMonitoring> list);
/**
* 根据自评ID删除月度监控数据
*/
int deleteByEvaluationId(@Param("evaluationId") Long evaluationId);
/**
* 计算年度累计进度
*/
BigDecimal calculateYearlyProgress(@Param("evaluationId") Long evaluationId, @Param("monitoringYear") Integer monitoringYear);
/**
* 计算年度累计支出
*/
BigDecimal calculateYearlyExpenditure(@Param("evaluationId") Long evaluationId, @Param("monitoringYear") Integer monitoringYear);
}
@@ -0,0 +1,55 @@
package org.dromara.cw.mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.cw.domain.CwProjectPerformanceSelfEvaluation;
import org.dromara.cw.domain.vo.CwProjectPerformanceSelfEvaluationVo;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
* 项目绩效自评Mapper接口
*
* @author ruoyi
* @date 2025-11-03
*/
public interface CwProjectPerformanceSelfEvaluationMapper extends BaseMapperPlus<CwProjectPerformanceSelfEvaluation, CwProjectPerformanceSelfEvaluationVo> {
/**
* 根据项目申报ID和年度查询自评
*/
CwProjectPerformanceSelfEvaluationVo selectByProjectAndYear(@Param("projectApplicationId") Long projectApplicationId, @Param("evaluationYear") Integer evaluationYear);
/**
* 查询我的自评列表
*/
List<CwProjectPerformanceSelfEvaluationVo> selectMyEvaluations(@Param("userId") Long userId);
/**
* 查询待审核的自评列表
*/
List<CwProjectPerformanceSelfEvaluationVo> selectPendingApprovals();
/**
* 统计自评数量
*/
Long countEvaluations(@Param("evaluationYear") Integer evaluationYear, @Param("evaluationStatus") String evaluationStatus);
/**
* 计算平均分
*/
BigDecimal calculateAverageScore(@Param("evaluationYear") Integer evaluationYear);
/**
* 按部门统计自评情况
*/
List<Map<String, Object>> statisticsByDept(@Param("evaluationYear") Integer evaluationYear);
/**
* 按等级统计自评数量
*/
List<Map<String, Object>> statisticsByLevel(@Param("evaluationYear") Integer evaluationYear);
}
@@ -0,0 +1,56 @@
package org.dromara.cw.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cw.domain.bo.CwProjectPerformanceSelfEvaluationBo;
import org.dromara.cw.domain.vo.CwProjectPerformanceSelfEvaluationVo;
import java.util.List;
/**
* 运行监控管理Service接口
* 财务管理员和学校领导使用,用于查看所有人提交的绩效自评,并进行锁定/解锁操作
*
* @author ruoyi
* @date 2025-01-03
*/
public interface ICwMonitoringManagementService {
/**
* 查询绩效自评(运行监控管理)
* 财务管理员和学校领导可以看到所有人的数据
*/
CwProjectPerformanceSelfEvaluationVo queryById(Long id);
/**
* 查询绩效自评列表(运行监控管理)
* 财务管理员和学校领导可以看到所有人的数据
*/
TableDataInfo<CwProjectPerformanceSelfEvaluationVo> queryPageList(CwProjectPerformanceSelfEvaluationBo bo, PageQuery pageQuery);
/**
* 查询绩效自评列表(运行监控管理)
*/
List<CwProjectPerformanceSelfEvaluationVo> queryList(CwProjectPerformanceSelfEvaluationBo bo);
/**
* 锁定绩效自评(单个)
*/
Boolean lockEvaluation(Long id);
/**
* 解锁绩效自评(单个)
*/
Boolean unlockEvaluation(Long id);
/**
* 批量锁定绩效自评
*/
Boolean lockBatch(List<Long> ids);
/**
* 批量解锁绩效自评
*/
Boolean unlockBatch(List<Long> ids);
}
@@ -0,0 +1,66 @@
package org.dromara.cw.service;
import org.dromara.cw.domain.vo.CwPerformanceMonitoringVo;
import org.dromara.cw.domain.bo.CwPerformanceMonitoringBo;
import java.util.Collection;
import java.util.List;
/**
* 绩效运行监控Service接口
*
* @author ruoyi
* @date 2025-11-04
*/
public interface ICwPerformanceMonitoringService {
/**
* 查询绩效运行监控
*
* @param id 主键
* @return 绩效运行监控
*/
CwPerformanceMonitoringVo queryById(Long id);
/**
* 根据自评ID查询运行监控列表
*
* @param evaluationId 自评ID
* @return 运行监控列表
*/
List<CwPerformanceMonitoringVo> queryByEvaluationId(Long evaluationId);
/**
* 新增绩效运行监控
*
* @param bo 绩效运行监控
* @return 是否新增成功
*/
Boolean insertByBo(CwPerformanceMonitoringBo bo);
/**
* 修改绩效运行监控
*
* @param bo 绩效运行监控
* @return 是否修改成功
*/
Boolean updateByBo(CwPerformanceMonitoringBo bo);
/**
* 校验并批量删除绩效运行监控信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据自评ID删除所有运行监控数据
*
* @param evaluationId 自评ID
* @return 是否删除成功
*/
Boolean deleteByEvaluationId(Long evaluationId);
}
@@ -0,0 +1,103 @@
package org.dromara.cw.service;
import org.dromara.cw.domain.vo.CwProjectEvaluationIndicatorVo;
import org.dromara.cw.domain.bo.CwProjectEvaluationIndicatorBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 项目绩效自评指标明细Service接口
*
* @author ruoyi
* @date 2025-11-03
*/
public interface ICwProjectEvaluationIndicatorService {
/**
* 查询项目绩效自评指标明细
*
* @param id 主键
* @return 项目绩效自评指标明细
*/
CwProjectEvaluationIndicatorVo queryById(Long id);
/**
* 查询项目绩效自评指标明细列表
*
* @param bo 查询条件
* @return 项目绩效自评指标明细列表
*/
List<CwProjectEvaluationIndicatorVo> queryList(CwProjectEvaluationIndicatorBo bo);
/**
* 分页查询项目绩效自评指标明细列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 项目绩效自评指标明细分页列表
*/
TableDataInfo<CwProjectEvaluationIndicatorVo> queryPageList(CwProjectEvaluationIndicatorBo bo, PageQuery pageQuery);
/**
* 根据自评ID查询指标明细列表
*
* @param evaluationId 自评ID
* @return 指标明细列表
*/
List<CwProjectEvaluationIndicatorVo> queryByEvaluationId(Long evaluationId);
/**
* 根据自评ID和指标类型查询指标明细列表
*
* @param evaluationId 自评ID
* @param indicatorType 指标类型
* @return 指标明细列表
*/
List<CwProjectEvaluationIndicatorVo> queryByEvaluationIdAndType(Long evaluationId, String indicatorType);
/**
* 新增项目绩效自评指标明细
*
* @param bo 项目绩效自评指标明细
* @return 是否新增成功
*/
Boolean insertByBo(CwProjectEvaluationIndicatorBo bo);
/**
* 修改项目绩效自评指标明细
*
* @param bo 项目绩效自评指标明细
* @return 是否修改成功
*/
Boolean updateByBo(CwProjectEvaluationIndicatorBo bo);
/**
* 批量保存指标明细
*
* @param evaluationId 自评ID
* @param boList 指标明细列表
* @return 是否保存成功
*/
Boolean batchSave(Long evaluationId, List<CwProjectEvaluationIndicatorBo> boList);
/**
* 校验并批量删除项目绩效自评指标明细信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据自评ID删除所有指标明细
*
* @param evaluationId 自评ID
* @return 是否删除成功
*/
Boolean deleteByEvaluationId(Long evaluationId);
}
@@ -0,0 +1,112 @@
package org.dromara.cw.service;
import org.dromara.cw.domain.vo.CwProjectMonthlyMonitoringVo;
import org.dromara.cw.domain.bo.CwProjectMonthlyMonitoringBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 项目月度运行监控Service接口
*
* @author ruoyi
* @date 2025-11-03
*/
public interface ICwProjectMonthlyMonitoringService {
/**
* 查询项目月度运行监控
*
* @param id 主键
* @return 项目月度运行监控
*/
CwProjectMonthlyMonitoringVo queryById(Long id);
/**
* 查询项目月度运行监控列表
*
* @param bo 查询条件
* @return 项目月度运行监控列表
*/
List<CwProjectMonthlyMonitoringVo> queryList(CwProjectMonthlyMonitoringBo bo);
/**
* 分页查询项目月度运行监控列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 项目月度运行监控分页列表
*/
TableDataInfo<CwProjectMonthlyMonitoringVo> queryPageList(CwProjectMonthlyMonitoringBo bo, PageQuery pageQuery);
/**
* 根据自评ID查询月度监控列表
*
* @param evaluationId 自评ID
* @return 月度监控列表
*/
List<CwProjectMonthlyMonitoringVo> queryByEvaluationId(Long evaluationId);
/**
* 查询某个自评的全年监控数据
*
* @param evaluationId 自评ID
* @param year 年份
* @return 月度数据Map,key为月份(1-12),value为监控数据
*/
Map<Integer, CwProjectMonthlyMonitoringVo> queryYearlyMonitoring(Long evaluationId, Integer year);
/**
* 新增项目月度运行监控
*
* @param bo 项目月度运行监控
* @return 是否新增成功
*/
Boolean insertByBo(CwProjectMonthlyMonitoringBo bo);
/**
* 修改项目月度运行监控
*
* @param bo 项目月度运行监控
* @return 是否修改成功
*/
Boolean updateByBo(CwProjectMonthlyMonitoringBo bo);
/**
* 填报月度数据
*
* @param bo 月度监控数据
* @return 是否填报成功
*/
Boolean fillMonthlyData(CwProjectMonthlyMonitoringBo bo);
/**
* 批量保存月度监控数据
*
* @param evaluationId 自评ID
* @param boList 月度监控数据列表
* @return 是否保存成功
*/
Boolean batchSave(Long evaluationId, List<CwProjectMonthlyMonitoringBo> boList);
/**
* 校验并批量删除项目月度运行监控信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据自评ID删除所有月度监控数据
*
* @param evaluationId 自评ID
* @return 是否删除成功
*/
Boolean deleteByEvaluationId(Long evaluationId);
}
@@ -0,0 +1,106 @@
package org.dromara.cw.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cw.domain.bo.CwProjectPerformanceSelfEvaluationBo;
import org.dromara.cw.domain.vo.CwProjectPerformanceSelfEvaluationVo;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 项目绩效自评Service接口
*
* @author ruoyi
* @date 2025-11-03
*/
public interface ICwProjectPerformanceSelfEvaluationService {
/**
* 查询项目绩效自评
*/
CwProjectPerformanceSelfEvaluationVo queryById(Long id);
/**
* 查询项目绩效自评列表
*/
TableDataInfo<CwProjectPerformanceSelfEvaluationVo> queryPageList(CwProjectPerformanceSelfEvaluationBo bo, PageQuery pageQuery);
/**
* 查询项目绩效自评列表
*/
List<CwProjectPerformanceSelfEvaluationVo> queryList(CwProjectPerformanceSelfEvaluationBo bo);
/**
* 根据项目申报ID和年度查询自评
*/
CwProjectPerformanceSelfEvaluationVo queryByProjectAndYear(Long projectApplicationId, Integer evaluationYear);
/**
* 查询我的自评列表
*/
List<CwProjectPerformanceSelfEvaluationVo> queryMyEvaluations(Long userId);
/**
* 查询待审核的自评列表
*/
List<CwProjectPerformanceSelfEvaluationVo> queryPendingApprovals();
/**
* 新增项目绩效自评
*/
Boolean insertByBo(CwProjectPerformanceSelfEvaluationBo bo);
/**
* 修改项目绩效自评
*/
Boolean updateByBo(CwProjectPerformanceSelfEvaluationBo bo);
/**
* 保存草稿
*/
Boolean saveDraft(CwProjectPerformanceSelfEvaluationBo bo);
/**
* 提交自评
*/
Boolean submitEvaluation(Long id);
/**
* 审核自评
*/
Boolean approveEvaluation(Long id, String opinion, Boolean approved);
/**
* 校验并批量删除项目绩效自评信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 统计自评数量
*/
Long countEvaluations(Integer evaluationYear, String evaluationStatus);
/**
* 计算平均分
*/
BigDecimal calculateAverageScore(Integer evaluationYear);
/**
* 按部门统计自评情况
*/
List<Map<String, Object>> statisticsByDept(Integer evaluationYear);
/**
* 按等级统计自评数量
*/
List<Map<String, Object>> statisticsByLevel(Integer evaluationYear);
/**
* 导出项目绩效自评
*/
void export(CwProjectPerformanceSelfEvaluationBo bo);
}
@@ -0,0 +1,306 @@
package org.dromara.cw.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.cw.domain.CwProjectPerformanceSelfEvaluation;
import org.dromara.cw.domain.bo.CwProjectPerformanceSelfEvaluationBo;
import org.dromara.cw.domain.vo.CwProjectPerformanceSelfEvaluationVo;
import org.dromara.cw.mapper.CwProjectPerformanceSelfEvaluationMapper;
import org.dromara.cw.service.ICwMonitoringManagementService;
import org.dromara.cw.service.ICwPerformanceMonitoringService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
/**
* 运行监控管理Service业务层处理
* 财务管理员和学校领导使用,用于查看所有人提交的绩效自评,并进行锁定/解锁操作
*
* @author ruoyi
* @date 2025-01-03
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class CwMonitoringManagementServiceImpl implements ICwMonitoringManagementService {
private final CwProjectPerformanceSelfEvaluationMapper baseMapper;
private final ICwPerformanceMonitoringService performanceMonitoringService;
/**
* 查询绩效自评(运行监控管理)
* 财务管理员和学校领导可以看到所有人的数据,不进行数据权限过滤
*/
@Override
public CwProjectPerformanceSelfEvaluationVo queryById(Long id) {
CwProjectPerformanceSelfEvaluationVo vo = baseMapper.selectVoById(id);
if (vo != null) {
// 加载运行监控数据
vo.setMonitoringList(performanceMonitoringService.queryByEvaluationId(id));
}
return vo;
}
/**
* 查询绩效自评列表(运行监控管理)
* 财务管理员和学校领导可以看到所有人的数据,不进行数据权限过滤
*/
@Override
public TableDataInfo<CwProjectPerformanceSelfEvaluationVo> queryPageList(CwProjectPerformanceSelfEvaluationBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> lqw = buildQueryWrapper(bo);
Page<CwProjectPerformanceSelfEvaluationVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询绩效自评列表(运行监控管理)
*/
@Override
public List<CwProjectPerformanceSelfEvaluationVo> queryList(CwProjectPerformanceSelfEvaluationBo bo) {
LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 构建查询条件
* 注意:运行监控管理不进行数据权限过滤,财务管理员和学校领导可以看到所有人的数据
*/
private LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> buildQueryWrapper(CwProjectPerformanceSelfEvaluationBo bo) {
LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> lqw = Wrappers.lambdaQuery();
// 🔧 修复:只有当projectApplicationId > 0时才作为查询条件
lqw.eq(bo.getProjectApplicationId() != null && bo.getProjectApplicationId() > 0,
CwProjectPerformanceSelfEvaluation::getProjectApplicationId, bo.getProjectApplicationId());
lqw.like(StringUtils.isNotBlank(bo.getApplicationNo()), CwProjectPerformanceSelfEvaluation::getApplicationNo, bo.getApplicationNo());
lqw.like(StringUtils.isNotBlank(bo.getProjectName()), CwProjectPerformanceSelfEvaluation::getProjectName, bo.getProjectName());
lqw.eq(bo.getEvaluationYear() != null, CwProjectPerformanceSelfEvaluation::getEvaluationYear, bo.getEvaluationYear());
lqw.eq(StringUtils.isNotBlank(bo.getEvaluationStatus()), CwProjectPerformanceSelfEvaluation::getEvaluationStatus, bo.getEvaluationStatus());
lqw.eq(StringUtils.isNotBlank(bo.getSelfEvaluationLevel()), CwProjectPerformanceSelfEvaluation::getSelfEvaluationLevel, bo.getSelfEvaluationLevel());
lqw.eq(bo.getIsLocked() != null, CwProjectPerformanceSelfEvaluation::getIsLocked, bo.getIsLocked());
// 🔒 运行监控管理:不进行数据权限过滤,财务管理员和学校领导可以看到所有人的数据
lqw.orderByDesc(CwProjectPerformanceSelfEvaluation::getCreateTime);
return lqw;
}
/**
* 锁定绩效自评(单个)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean lockEvaluation(Long id) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
log.error("无法获取登录用户信息,锁定失败");
return false;
}
Long userId = loginUser.getUserId();
String userName = loginUser.getUsername();
// 检查记录是否存在
CwProjectPerformanceSelfEvaluation evaluation = baseMapper.selectById(id);
if (evaluation == null) {
log.error("绩效自评记录不存在,ID: {}", id);
return false;
}
// 检查是否已经锁定
if (evaluation.getIsLocked() != null && evaluation.getIsLocked() == 1) {
log.warn("绩效自评记录已锁定,ID: {}", id);
return false;
}
// 执行锁定
LambdaUpdateWrapper<CwProjectPerformanceSelfEvaluation> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(CwProjectPerformanceSelfEvaluation::getId, id)
.set(CwProjectPerformanceSelfEvaluation::getIsLocked, 1)
.set(CwProjectPerformanceSelfEvaluation::getLockTime, new Date())
.set(CwProjectPerformanceSelfEvaluation::getLockBy, userId)
.set(CwProjectPerformanceSelfEvaluation::getLockByName, userName);
int result = baseMapper.update(null, updateWrapper);
log.info("锁定绩效自评成功,ID: {}, 操作人: {}", id, userName);
return result > 0;
} catch (Exception e) {
log.error("锁定绩效自评异常,ID: {}", id, e);
throw new RuntimeException("锁定绩效自评失败: " + e.getMessage(), e);
}
}
/**
* 解锁绩效自评(单个)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean unlockEvaluation(Long id) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
log.error("无法获取登录用户信息,解锁失败");
return false;
}
String userName = loginUser.getUsername();
// 检查记录是否存在
CwProjectPerformanceSelfEvaluation evaluation = baseMapper.selectById(id);
if (evaluation == null) {
log.error("绩效自评记录不存在,ID: {}", id);
return false;
}
// 检查是否已经解锁
if (evaluation.getIsLocked() == null || evaluation.getIsLocked() == 0) {
log.warn("绩效自评记录未锁定,ID: {}", id);
return false;
}
// 执行解锁
LambdaUpdateWrapper<CwProjectPerformanceSelfEvaluation> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(CwProjectPerformanceSelfEvaluation::getId, id)
.set(CwProjectPerformanceSelfEvaluation::getIsLocked, 0)
.set(CwProjectPerformanceSelfEvaluation::getLockTime, null)
.set(CwProjectPerformanceSelfEvaluation::getLockBy, null)
.set(CwProjectPerformanceSelfEvaluation::getLockByName, null);
int result = baseMapper.update(null, updateWrapper);
log.info("解锁绩效自评成功,ID: {}, 操作人: {}", id, userName);
return result > 0;
} catch (Exception e) {
log.error("解锁绩效自评异常,ID: {}", id, e);
throw new RuntimeException("解锁绩效自评失败: " + e.getMessage(), e);
}
}
/**
* 批量锁定绩效自评
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean lockBatch(List<Long> ids) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
log.error("无法获取登录用户信息,批量锁定失败");
return false;
}
Long userId = loginUser.getUserId();
String userName = loginUser.getUsername();
// 批量锁定
int successCount = 0;
for (Long id : ids) {
try {
// 检查记录是否存在
CwProjectPerformanceSelfEvaluation evaluation = baseMapper.selectById(id);
if (evaluation == null) {
log.warn("绩效自评记录不存在,跳过,ID: {}", id);
continue;
}
// 检查是否已经锁定
if (evaluation.getIsLocked() != null && evaluation.getIsLocked() == 1) {
log.warn("绩效自评记录已锁定,跳过,ID: {}", id);
continue;
}
// 执行锁定
LambdaUpdateWrapper<CwProjectPerformanceSelfEvaluation> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(CwProjectPerformanceSelfEvaluation::getId, id)
.set(CwProjectPerformanceSelfEvaluation::getIsLocked, 1)
.set(CwProjectPerformanceSelfEvaluation::getLockTime, new Date())
.set(CwProjectPerformanceSelfEvaluation::getLockBy, userId)
.set(CwProjectPerformanceSelfEvaluation::getLockByName, userName);
int result = baseMapper.update(null, updateWrapper);
if (result > 0) {
successCount++;
}
} catch (Exception e) {
log.error("锁定绩效自评失败,ID: {}", id, e);
}
}
log.info("批量锁定绩效自评完成,总数: {}, 成功: {}, 操作人: {}", ids.size(), successCount, userName);
return successCount > 0;
} catch (Exception e) {
log.error("批量锁定绩效自评异常", e);
throw new RuntimeException("批量锁定绩效自评失败: " + e.getMessage(), e);
}
}
/**
* 批量解锁绩效自评
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean unlockBatch(List<Long> ids) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
log.error("无法获取登录用户信息,批量解锁失败");
return false;
}
String userName = loginUser.getUsername();
// 批量解锁
int successCount = 0;
for (Long id : ids) {
try {
// 检查记录是否存在
CwProjectPerformanceSelfEvaluation evaluation = baseMapper.selectById(id);
if (evaluation == null) {
log.warn("绩效自评记录不存在,跳过,ID: {}", id);
continue;
}
// 检查是否已经解锁
if (evaluation.getIsLocked() == null || evaluation.getIsLocked() == 0) {
log.warn("绩效自评记录未锁定,跳过,ID: {}", id);
continue;
}
// 执行解锁
LambdaUpdateWrapper<CwProjectPerformanceSelfEvaluation> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(CwProjectPerformanceSelfEvaluation::getId, id)
.set(CwProjectPerformanceSelfEvaluation::getIsLocked, 0)
.set(CwProjectPerformanceSelfEvaluation::getLockTime, null)
.set(CwProjectPerformanceSelfEvaluation::getLockBy, null)
.set(CwProjectPerformanceSelfEvaluation::getLockByName, null);
int result = baseMapper.update(null, updateWrapper);
if (result > 0) {
successCount++;
}
} catch (Exception e) {
log.error("解锁绩效自评失败,ID: {}", id, e);
}
}
log.info("批量解锁绩效自评完成,总数: {}, 成功: {}, 操作人: {}", ids.size(), successCount, userName);
return successCount > 0;
} catch (Exception e) {
log.error("批量解锁绩效自评异常", e);
throw new RuntimeException("批量解锁绩效自评失败: " + e.getMessage(), e);
}
}
}
@@ -0,0 +1,145 @@
package org.dromara.cw.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.cw.domain.CwPerformanceMonitoring;
import org.dromara.cw.domain.bo.CwPerformanceMonitoringBo;
import org.dromara.cw.domain.vo.CwPerformanceMonitoringVo;
import org.dromara.cw.mapper.CwPerformanceMonitoringMapper;
import org.dromara.cw.service.ICwPerformanceMonitoringService;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
/**
* 绩效运行监控Service业务层处理
*
* @author ruoyi
* @date 2025-11-04
*/
@RequiredArgsConstructor
@Service
public class CwPerformanceMonitoringServiceImpl implements ICwPerformanceMonitoringService {
private final CwPerformanceMonitoringMapper baseMapper;
/**
* 查询绩效运行监控
*/
@Override
public CwPerformanceMonitoringVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 根据自评ID查询运行监控列表
*/
@Override
public List<CwPerformanceMonitoringVo> queryByEvaluationId(Long evaluationId) {
LambdaQueryWrapper<CwPerformanceMonitoring> lqw = Wrappers.lambdaQuery();
lqw.eq(CwPerformanceMonitoring::getEvaluationId, evaluationId);
lqw.orderByDesc(CwPerformanceMonitoring::getCreateTime);
return baseMapper.selectVoList(lqw);
}
/**
* 新增绩效运行监控
*/
@Override
public Boolean insertByBo(CwPerformanceMonitoringBo bo) {
CwPerformanceMonitoring add = MapstructUtils.convert(bo, CwPerformanceMonitoring.class);
validEntityBeforeSave(add);
// 设置填报人信息
Long userId = getUserIdSafely();
String username = LoginHelper.getUsername();
String nickname = LoginHelper.getNickname();
add.setFillBy(userId);
// 优先使用昵称,如果昵称为空则使用用户名
add.setFillByName(StringUtils.isNotBlank(nickname) ? nickname : username);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 安全获取用户ID,处理可能的字符串格式(如 "sys_user:1")
*/
private Long getUserIdSafely() {
try {
Long userId = LoginHelper.getUserId();
if (userId != null) {
return userId;
}
} catch (Exception e) {
// 如果getUserId()失败,尝试从getUserIdStr()解析
}
// 尝试从字符串格式解析
try {
String userIdStr = LoginHelper.getUserIdStr();
if (StringUtils.isNotBlank(userIdStr)) {
// 处理 "sys_user:1" 这种格式,提取数字部分
if (userIdStr.contains(":")) {
String[] parts = userIdStr.split(":");
if (parts.length > 1) {
return Long.parseLong(parts[1]);
}
} else {
return Long.parseLong(userIdStr);
}
}
} catch (Exception e) {
// 解析失败,返回null或抛出异常
}
return null;
}
/**
* 修改绩效运行监控
*/
@Override
public Boolean updateByBo(CwPerformanceMonitoringBo bo) {
CwPerformanceMonitoring update = MapstructUtils.convert(bo, CwPerformanceMonitoring.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(CwPerformanceMonitoring entity) {
// TODO 做一些数据校验,如唯一约束
}
/**
* 批量删除绩效运行监控
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
* 根据自评ID删除所有运行监控数据
*/
@Override
public Boolean deleteByEvaluationId(Long evaluationId) {
LambdaQueryWrapper<CwPerformanceMonitoring> lqw = Wrappers.lambdaQuery();
lqw.eq(CwPerformanceMonitoring::getEvaluationId, evaluationId);
return baseMapper.delete(lqw) > 0;
}
}
@@ -0,0 +1,203 @@
package org.dromara.cw.service.impl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.dromara.cw.domain.bo.CwProjectEvaluationIndicatorBo;
import org.dromara.cw.domain.vo.CwProjectEvaluationIndicatorVo;
import org.dromara.cw.domain.CwProjectEvaluationIndicator;
import org.dromara.cw.mapper.CwProjectEvaluationIndicatorMapper;
import org.dromara.cw.service.ICwProjectEvaluationIndicatorService;
import java.util.List;
import java.util.Collection;
/**
* 项目绩效自评指标明细Service业务层处理
*
* @author ruoyi
* @date 2025-11-03
*/
@RequiredArgsConstructor
@Service
public class CwProjectEvaluationIndicatorServiceImpl implements ICwProjectEvaluationIndicatorService {
private final CwProjectEvaluationIndicatorMapper baseMapper;
/**
* 查询项目绩效自评指标明细
*
* @param id 主键
* @return 项目绩效自评指标明细
*/
@Override
public CwProjectEvaluationIndicatorVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询项目绩效自评指标明细列表
*
* @param bo 查询条件
* @return 项目绩效自评指标明细列表
*/
@Override
public List<CwProjectEvaluationIndicatorVo> queryList(CwProjectEvaluationIndicatorBo bo) {
LambdaQueryWrapper<CwProjectEvaluationIndicator> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 分页查询项目绩效自评指标明细列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 项目绩效自评指标明细分页列表
*/
@Override
public TableDataInfo<CwProjectEvaluationIndicatorVo> queryPageList(CwProjectEvaluationIndicatorBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<CwProjectEvaluationIndicator> lqw = buildQueryWrapper(bo);
Page<CwProjectEvaluationIndicatorVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 根据自评ID查询指标明细列表
*
* @param evaluationId 自评ID
* @return 指标明细列表
*/
@Override
public List<CwProjectEvaluationIndicatorVo> queryByEvaluationId(Long evaluationId) {
LambdaQueryWrapper<CwProjectEvaluationIndicator> lqw = Wrappers.lambdaQuery();
lqw.eq(CwProjectEvaluationIndicator::getEvaluationId, evaluationId);
lqw.orderByAsc(CwProjectEvaluationIndicator::getIndicatorType);
lqw.orderByAsc(CwProjectEvaluationIndicator::getSortOrder);
return baseMapper.selectVoList(lqw);
}
/**
* 根据自评ID和指标类型查询指标明细列表
*
* @param evaluationId 自评ID
* @param indicatorType 指标类型
* @return 指标明细列表
*/
@Override
public List<CwProjectEvaluationIndicatorVo> queryByEvaluationIdAndType(Long evaluationId, String indicatorType) {
LambdaQueryWrapper<CwProjectEvaluationIndicator> lqw = Wrappers.lambdaQuery();
lqw.eq(CwProjectEvaluationIndicator::getEvaluationId, evaluationId);
lqw.eq(StringUtils.isNotBlank(indicatorType), CwProjectEvaluationIndicator::getIndicatorType, indicatorType);
lqw.orderByAsc(CwProjectEvaluationIndicator::getSortOrder);
return baseMapper.selectVoList(lqw);
}
/**
* 构建查询条件
*/
private LambdaQueryWrapper<CwProjectEvaluationIndicator> buildQueryWrapper(CwProjectEvaluationIndicatorBo bo) {
LambdaQueryWrapper<CwProjectEvaluationIndicator> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getEvaluationId() != null, CwProjectEvaluationIndicator::getEvaluationId, bo.getEvaluationId());
lqw.eq(StringUtils.isNotBlank(bo.getIndicatorType()), CwProjectEvaluationIndicator::getIndicatorType, bo.getIndicatorType());
lqw.like(StringUtils.isNotBlank(bo.getIndicatorName()), CwProjectEvaluationIndicator::getIndicatorName, bo.getIndicatorName());
lqw.orderByAsc(CwProjectEvaluationIndicator::getIndicatorType);
lqw.orderByAsc(CwProjectEvaluationIndicator::getSortOrder);
return lqw;
}
/**
* 新增项目绩效自评指标明细
*
* @param bo 项目绩效自评指标明细
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(CwProjectEvaluationIndicatorBo bo) {
CwProjectEvaluationIndicator add = MapstructUtils.convert(bo, CwProjectEvaluationIndicator.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改项目绩效自评指标明细
*
* @param bo 项目绩效自评指标明细
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(CwProjectEvaluationIndicatorBo bo) {
CwProjectEvaluationIndicator update = MapstructUtils.convert(bo, CwProjectEvaluationIndicator.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 批量保存指标明细
*
* @param evaluationId 自评ID
* @param boList 指标明细列表
* @return 是否保存成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean batchSave(Long evaluationId, List<CwProjectEvaluationIndicatorBo> boList) {
// 先删除该自评下的所有指标
deleteByEvaluationId(evaluationId);
// 批量插入新的指标
if (boList != null && !boList.isEmpty()) {
for (CwProjectEvaluationIndicatorBo bo : boList) {
bo.setEvaluationId(evaluationId);
insertByBo(bo);
}
}
return true;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(CwProjectEvaluationIndicator entity) {
// 可以在此添加业务校验逻辑
}
/**
* 批量删除项目绩效自评指标明细
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 可以在此添加删除前的校验逻辑
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 根据自评ID删除所有指标明细
*
* @param evaluationId 自评ID
* @return 是否删除成功
*/
@Override
public Boolean deleteByEvaluationId(Long evaluationId) {
LambdaQueryWrapper<CwProjectEvaluationIndicator> lqw = Wrappers.lambdaQuery();
lqw.eq(CwProjectEvaluationIndicator::getEvaluationId, evaluationId);
return baseMapper.delete(lqw) >= 0;
}
}
@@ -0,0 +1,319 @@
package org.dromara.cw.service.impl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.dromara.cw.domain.bo.CwProjectMonthlyMonitoringBo;
import org.dromara.cw.domain.vo.CwProjectMonthlyMonitoringVo;
import org.dromara.cw.domain.CwProjectMonthlyMonitoring;
import org.dromara.cw.mapper.CwProjectMonthlyMonitoringMapper;
import org.dromara.cw.service.ICwProjectMonthlyMonitoringService;
import org.dromara.common.satoken.utils.LoginHelper;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 项目月度运行监控Service业务层处理
*
* @author ruoyi
* @date 2025-11-03
*/
@RequiredArgsConstructor
@Service
public class CwProjectMonthlyMonitoringServiceImpl implements ICwProjectMonthlyMonitoringService {
private final CwProjectMonthlyMonitoringMapper baseMapper;
/**
* 查询项目月度运行监控
*
* @param id 主键
* @return 项目月度运行监控
*/
@Override
public CwProjectMonthlyMonitoringVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询项目月度运行监控列表
*
* @param bo 查询条件
* @return 项目月度运行监控列表
*/
@Override
public List<CwProjectMonthlyMonitoringVo> queryList(CwProjectMonthlyMonitoringBo bo) {
LambdaQueryWrapper<CwProjectMonthlyMonitoring> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 分页查询项目月度运行监控列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 项目月度运行监控分页列表
*/
@Override
public TableDataInfo<CwProjectMonthlyMonitoringVo> queryPageList(CwProjectMonthlyMonitoringBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<CwProjectMonthlyMonitoring> lqw = buildQueryWrapper(bo);
Page<CwProjectMonthlyMonitoringVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 根据自评ID查询月度监控列表
*
* @param evaluationId 自评ID
* @return 月度监控列表
*/
@Override
public List<CwProjectMonthlyMonitoringVo> queryByEvaluationId(Long evaluationId) {
LambdaQueryWrapper<CwProjectMonthlyMonitoring> lqw = Wrappers.lambdaQuery();
lqw.eq(CwProjectMonthlyMonitoring::getEvaluationId, evaluationId);
lqw.orderByAsc(CwProjectMonthlyMonitoring::getMonitoringYear);
lqw.orderByAsc(CwProjectMonthlyMonitoring::getMonitoringMonth);
return baseMapper.selectVoList(lqw);
}
/**
* 查询某个自评的全年监控数据
*
* @param evaluationId 自评ID
* @param year 年份
* @return 月度数据Map,key为月份(1-12),value为监控数据
*/
@Override
public Map<Integer, CwProjectMonthlyMonitoringVo> queryYearlyMonitoring(Long evaluationId, Integer year) {
LambdaQueryWrapper<CwProjectMonthlyMonitoring> lqw = Wrappers.lambdaQuery();
lqw.eq(CwProjectMonthlyMonitoring::getEvaluationId, evaluationId);
lqw.eq(CwProjectMonthlyMonitoring::getMonitoringYear, year);
lqw.orderByAsc(CwProjectMonthlyMonitoring::getMonitoringMonth);
List<CwProjectMonthlyMonitoringVo> list = baseMapper.selectVoList(lqw);
return list.stream().collect(
Collectors.toMap(CwProjectMonthlyMonitoringVo::getMonitoringMonth, v -> v)
);
}
/**
* 构建查询条件
*/
private LambdaQueryWrapper<CwProjectMonthlyMonitoring> buildQueryWrapper(CwProjectMonthlyMonitoringBo bo) {
LambdaQueryWrapper<CwProjectMonthlyMonitoring> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getEvaluationId() != null, CwProjectMonthlyMonitoring::getEvaluationId, bo.getEvaluationId());
lqw.eq(bo.getProjectApplicationId() != null, CwProjectMonthlyMonitoring::getProjectApplicationId, bo.getProjectApplicationId());
lqw.eq(bo.getMonitoringYear() != null, CwProjectMonthlyMonitoring::getMonitoringYear, bo.getMonitoringYear());
lqw.eq(bo.getMonitoringMonth() != null, CwProjectMonthlyMonitoring::getMonitoringMonth, bo.getMonitoringMonth());
lqw.eq(StringUtils.isNotBlank(bo.getFillStatus()), CwProjectMonthlyMonitoring::getFillStatus, bo.getFillStatus());
lqw.orderByDesc(CwProjectMonthlyMonitoring::getMonitoringYear);
lqw.orderByAsc(CwProjectMonthlyMonitoring::getMonitoringMonth);
return lqw;
}
/**
* 新增项目月度运行监控
*
* @param bo 项目月度运行监控
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(CwProjectMonthlyMonitoringBo bo) {
CwProjectMonthlyMonitoring add = MapstructUtils.convert(bo, CwProjectMonthlyMonitoring.class);
validEntityBeforeSave(add);
// 自动计算
calculateFields(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改项目月度运行监控
*
* @param bo 项目月度运行监控
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(CwProjectMonthlyMonitoringBo bo) {
CwProjectMonthlyMonitoring update = MapstructUtils.convert(bo, CwProjectMonthlyMonitoring.class);
validEntityBeforeSave(update);
// 自动计算
calculateFields(update);
return baseMapper.updateById(update) > 0;
}
/**
* 填报月度数据
*
* @param bo 月度监控数据
* @return 是否填报成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean fillMonthlyData(CwProjectMonthlyMonitoringBo bo) {
// 检查是否已存在该月数据
LambdaQueryWrapper<CwProjectMonthlyMonitoring> lqw = Wrappers.lambdaQuery();
lqw.eq(CwProjectMonthlyMonitoring::getEvaluationId, bo.getEvaluationId());
lqw.eq(CwProjectMonthlyMonitoring::getMonitoringYear, bo.getMonitoringYear());
lqw.eq(CwProjectMonthlyMonitoring::getMonitoringMonth, bo.getMonitoringMonth());
CwProjectMonthlyMonitoring existing = baseMapper.selectOne(lqw);
if (existing != null) {
// 如果已存在,则更新
bo.setId(existing.getId());
return updateByBo(bo);
} else {
// 如果不存在,则新增
bo.setFillStatus("filled");
bo.setFillTime(new Date());
bo.setFillBy(getUserIdSafely());
return insertByBo(bo);
}
}
/**
* 批量保存月度监控数据
*
* @param evaluationId 自评ID
* @param boList 月度监控数据列表
* @return 是否保存成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean batchSave(Long evaluationId, List<CwProjectMonthlyMonitoringBo> boList) {
if (boList == null || boList.isEmpty()) {
return true;
}
for (CwProjectMonthlyMonitoringBo bo : boList) {
bo.setEvaluationId(evaluationId);
fillMonthlyData(bo);
}
return true;
}
/**
* 安全获取用户ID,处理可能的字符串格式(如 "sys_user:1")
*/
private Long getUserIdSafely() {
try {
Long userId = LoginHelper.getUserId();
if (userId != null) {
return userId;
}
} catch (Exception e) {
// 如果getUserId()失败,尝试从getUserIdStr()解析
}
// 尝试从字符串格式解析
try {
String userIdStr = LoginHelper.getUserIdStr();
if (StringUtils.isNotBlank(userIdStr)) {
// 处理 "sys_user:1" 这种格式,提取数字部分
if (userIdStr.contains(":")) {
String[] parts = userIdStr.split(":");
if (parts.length > 1) {
return Long.parseLong(parts[1]);
}
} else {
return Long.parseLong(userIdStr);
}
}
} catch (Exception e) {
// 解析失败,返回null或抛出异常
}
return null;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(CwProjectMonthlyMonitoring entity) {
// 校验月份范围
if (entity.getMonitoringMonth() != null) {
if (entity.getMonitoringMonth() < 1 || entity.getMonitoringMonth() > 12) {
throw new RuntimeException("月份必须在1-12之间");
}
}
// 校验进度范围
if (entity.getPlannedProgress() != null) {
if (entity.getPlannedProgress().compareTo(BigDecimal.ZERO) < 0
|| entity.getPlannedProgress().compareTo(new BigDecimal("100")) > 0) {
throw new RuntimeException("计划进度必须在0-100之间");
}
}
if (entity.getActualProgress() != null) {
if (entity.getActualProgress().compareTo(BigDecimal.ZERO) < 0
|| entity.getActualProgress().compareTo(new BigDecimal("100")) > 0) {
throw new RuntimeException("实际进度必须在0-100之间");
}
}
}
/**
* 自动计算字段
*/
private void calculateFields(CwProjectMonthlyMonitoring entity) {
// 计算进度偏差 = 实际进度 - 计划进度
if (entity.getPlannedProgress() != null && entity.getActualProgress() != null) {
BigDecimal variance = entity.getActualProgress().subtract(entity.getPlannedProgress());
entity.setProgressVariance(variance);
}
// 计算资金执行率 = (累计实际支出 / 累计预算) × 100
if (entity.getCumulativeBudget() != null && entity.getCumulativeExpenditure() != null
&& entity.getCumulativeBudget().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal rate = entity.getCumulativeExpenditure()
.divide(entity.getCumulativeBudget(), 4, BigDecimal.ROUND_HALF_UP)
.multiply(new BigDecimal("100"));
entity.setFundExecutionRate(rate);
}
}
/**
* 批量删除项目月度运行监控
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 可以在此添加删除前的校验逻辑
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 根据自评ID删除所有月度监控数据
*
* @param evaluationId 自评ID
* @return 是否删除成功
*/
@Override
public Boolean deleteByEvaluationId(Long evaluationId) {
LambdaQueryWrapper<CwProjectMonthlyMonitoring> lqw = Wrappers.lambdaQuery();
lqw.eq(CwProjectMonthlyMonitoring::getEvaluationId, evaluationId);
return baseMapper.delete(lqw) >= 0;
}
}
@@ -0,0 +1,581 @@
package org.dromara.cw.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.cw.domain.CwProjectPerformanceSelfEvaluation;
import org.dromara.cw.domain.bo.CwProjectPerformanceSelfEvaluationBo;
import org.dromara.cw.domain.vo.CwProjectPerformanceSelfEvaluationVo;
import org.dromara.cw.mapper.CwProjectPerformanceSelfEvaluationMapper;
import org.dromara.cw.service.ICwProjectPerformanceSelfEvaluationService;
import org.dromara.cw.service.ICwPerformanceMonitoringService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 项目绩效自评Service业务层处理
*
* @author ruoyi
* @date 2025-11-03
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class CwProjectPerformanceSelfEvaluationServiceImpl implements ICwProjectPerformanceSelfEvaluationService {
private final CwProjectPerformanceSelfEvaluationMapper baseMapper;
private final ICwPerformanceMonitoringService performanceMonitoringService;
/**
* 查询项目绩效自评
*/
@Override
public CwProjectPerformanceSelfEvaluationVo queryById(Long id) {
CwProjectPerformanceSelfEvaluationVo vo = baseMapper.selectVoById(id);
if (vo != null) {
// 🔒 数据权限验证:普通用户只能查看自己创建的数据
if (!hasDataPermission(vo)) {
log.warn("用户尝试访问无权限的绩效自评数据,ID: {}", id);
throw new RuntimeException("无权访问该绩效自评数据");
}
// 查询并设置运行监控数据
vo.setMonitoringList(performanceMonitoringService.queryByEvaluationId(id));
}
return vo;
}
/**
* 验证用户是否有查看指定绩效自评的权限
* 只有超级管理员可以看到所有数据,其他用户(包括财务管理员)只能查看自己创建的数据
*
* @param vo 绩效自评数据
* @return 是否有权限
*/
private boolean hasDataPermission(CwProjectPerformanceSelfEvaluationVo vo) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
log.warn("无法获取登录用户信息,拒绝数据访问");
return false;
}
Long currentUserId = loginUser.getUserId();
// 超级管理员拥有所有权限
if (LoginHelper.isSuperAdmin(currentUserId)) {
return true;
}
// 所有其他用户(包括财务管理员):只能查看自己创建的数据
return currentUserId.equals(vo.getCreateBy());
} catch (Exception e) {
log.error("数据权限验证异常: {}", e.getMessage(), e);
return false;
}
}
/**
* 查询项目绩效自评列表
*/
@Override
public TableDataInfo<CwProjectPerformanceSelfEvaluationVo> queryPageList(CwProjectPerformanceSelfEvaluationBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> lqw = buildQueryWrapper(bo);
Page<CwProjectPerformanceSelfEvaluationVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询项目绩效自评列表
*/
@Override
public List<CwProjectPerformanceSelfEvaluationVo> queryList(CwProjectPerformanceSelfEvaluationBo bo) {
LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> buildQueryWrapper(CwProjectPerformanceSelfEvaluationBo bo) {
LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> lqw = Wrappers.lambdaQuery();
// 🔧 修复:只有当projectApplicationId > 0时才作为查询条件
lqw.eq(bo.getProjectApplicationId() != null && bo.getProjectApplicationId() > 0,
CwProjectPerformanceSelfEvaluation::getProjectApplicationId, bo.getProjectApplicationId());
lqw.like(StringUtils.isNotBlank(bo.getApplicationNo()), CwProjectPerformanceSelfEvaluation::getApplicationNo, bo.getApplicationNo());
lqw.like(StringUtils.isNotBlank(bo.getProjectName()), CwProjectPerformanceSelfEvaluation::getProjectName, bo.getProjectName());
lqw.eq(bo.getEvaluationYear() != null, CwProjectPerformanceSelfEvaluation::getEvaluationYear, bo.getEvaluationYear());
lqw.eq(StringUtils.isNotBlank(bo.getEvaluationStatus()), CwProjectPerformanceSelfEvaluation::getEvaluationStatus, bo.getEvaluationStatus());
lqw.eq(StringUtils.isNotBlank(bo.getSelfEvaluationLevel()), CwProjectPerformanceSelfEvaluation::getSelfEvaluationLevel, bo.getSelfEvaluationLevel());
// 🔒 数据权限过滤:普通用户只能看到自己提交的数据
addDataPermissionFilter(lqw);
lqw.orderByDesc(CwProjectPerformanceSelfEvaluation::getCreateTime);
return lqw;
}
/**
* 添加数据权限过滤
* 只有超级管理员可以看到所有数据,其他用户(包括财务管理员)只能看到自己创建的数据
*/
private void addDataPermissionFilter(LambdaQueryWrapper<CwProjectPerformanceSelfEvaluation> lqw) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
// 如果无法获取用户信息,默认只能看到自己的数据(通过设置一个不存在的用户ID)
lqw.eq(CwProjectPerformanceSelfEvaluation::getCreateBy, -1L);
log.warn("无法获取登录用户信息,应用最严格的数据权限过滤");
return;
}
Long userId = loginUser.getUserId();
// 超级管理员不过滤数据
if (LoginHelper.isSuperAdmin(userId)) {
log.info("✅ [超级管理员权限] 用户ID: {}, 可以查看所有绩效自评数据", userId);
return;
}
// 所有其他用户(包括财务管理员):只能看到自己创建的数据
lqw.eq(CwProjectPerformanceSelfEvaluation::getCreateBy, userId);
log.info("🔒 [个人权限过滤] 用户ID: {}, 只能查看自己提交的绩效自评数据", userId);
} catch (Exception e) {
log.error("❌ 数据权限过滤异常: {}", e.getMessage(), e);
// 异常情况下,应用最严格的数据权限过滤
try {
Long userId = LoginHelper.getUserId();
if (userId != null) {
lqw.eq(CwProjectPerformanceSelfEvaluation::getCreateBy, userId);
} else {
// 完全无法获取用户ID时,返回空结果
lqw.eq(CwProjectPerformanceSelfEvaluation::getCreateBy, -1L);
}
} catch (Exception ex) {
log.error("❌ 获取当前用户ID失败: {}", ex.getMessage(), ex);
// 完全失败时返回空结果
lqw.eq(CwProjectPerformanceSelfEvaluation::getCreateBy, -1L);
}
}
}
/**
* 根据项目申报ID和年度查询自评
*/
@Override
public CwProjectPerformanceSelfEvaluationVo queryByProjectAndYear(Long projectApplicationId, Integer evaluationYear) {
return baseMapper.selectByProjectAndYear(projectApplicationId, evaluationYear);
}
/**
* 查询我的自评列表
*/
@Override
public List<CwProjectPerformanceSelfEvaluationVo> queryMyEvaluations(Long userId) {
return baseMapper.selectMyEvaluations(userId);
}
/**
* 查询待审核的自评列表
*/
@Override
public List<CwProjectPerformanceSelfEvaluationVo> queryPendingApprovals() {
return baseMapper.selectPendingApprovals();
}
/**
* 新增项目绩效自评
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(CwProjectPerformanceSelfEvaluationBo bo) {
// 🔍 调试:打印接收到的Bo对象
log.info("========== 新增绩效自评 - 接收到的Bo对象 ==========");
log.info("项目名称: {}", bo.getProjectName());
log.info("主管部门: {}", bo.getSupervisoryDept());
log.info("实施单位: {}", bo.getImplementUnit());
log.info("绩效目标: {}", bo.getPerformanceGoal());
log.info("目标完成情况: {}", bo.getTargetCompletion());
log.info("数量指标: {}", bo.getQuantityIndicator());
log.info("质量指标: {}", bo.getQualityIndicator());
log.info("时效指标: {}", bo.getTimelinessIndicator());
log.info("成本指标: {}", bo.getCostIndicator());
log.info("经济指标: {}", bo.getEconomicIndicator());
log.info("社会指标: {}", bo.getSocialIndicator());
log.info("生态指标: {}", bo.getEcologicalIndicator());
log.info("满意度指标: {}", bo.getSatisfactionIndicator());
log.info("总分: {}", bo.getTotalScore());
log.info("======================================================");
CwProjectPerformanceSelfEvaluation add = MapstructUtils.convert(bo, CwProjectPerformanceSelfEvaluation.class);
// 🔍 调试:打印转换后的Entity对象
log.info("========== 转换后的Entity对象 ==========");
log.info("主管部门: {}", add.getSupervisoryDept());
log.info("实施单位: {}", add.getImplementUnit());
log.info("数量指标: {}", add.getQuantityIndicator());
log.info("质量指标: {}", add.getQualityIndicator());
log.info("=========================================");
validEntityBeforeSave(add);
// 如果申报编号为空,设置默认值
if (StringUtils.isBlank(add.getApplicationNo())) {
// 如果有项目申报ID且不为0,使用项目申报ID
if (add.getProjectApplicationId() != null && add.getProjectApplicationId() > 0) {
add.setApplicationNo("APP-" + add.getProjectApplicationId());
} else {
// 否则使用时间戳生成唯一编号
add.setApplicationNo("TEMP-" + System.currentTimeMillis());
}
}
// 设置默认状态为草稿
if (StringUtils.isBlank(add.getEvaluationStatus())) {
add.setEvaluationStatus("draft");
}
// 计算预算执行率
if (add.getAnnualBudget() != null && add.getActualExpenditure() != null
&& add.getAnnualBudget().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal rate = add.getActualExpenditure()
.divide(add.getAnnualBudget(), 4, BigDecimal.ROUND_HALF_UP)
.multiply(new BigDecimal("100"));
add.setBudgetExecutionRate(rate);
}
// 🔧 新增:保存时自动计算总分和等级
calculateTotalScoreAndLevel(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改项目绩效自评
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(CwProjectPerformanceSelfEvaluationBo bo) {
// 先查询原始记录,检查状态
CwProjectPerformanceSelfEvaluation existing = baseMapper.selectById(bo.getId());
if (existing == null) {
throw new RuntimeException("自评记录不存在");
}
// 🔒 检查是否已锁定:锁定状态的绩效自评不允许修改
if (existing.getIsLocked() != null && existing.getIsLocked() == 1) {
throw new RuntimeException("该绩效自评已锁定,不允许修改。如需修改,请联系财务管理员或学校领导解锁");
}
// 🔧 已移除编辑限制:允许所有状态的自评都可以编辑
// 原限制逻辑:检查是否允许修改:草稿状态 OR (已提交状态 AND 允许已提交后编辑)
// if (!"draft".equals(existing.getEvaluationStatus())) {
// // 如果不是草稿状态,检查是否允许已提交后编辑
// if (existing.getAllowEditAfterSubmit() == null || existing.getAllowEditAfterSubmit() == 0) {
// throw new RuntimeException("该自评已提交,不允许修改。如需修改,请先设置允许已提交后编辑");
// }
// }
CwProjectPerformanceSelfEvaluation update = MapstructUtils.convert(bo, CwProjectPerformanceSelfEvaluation.class);
validEntityBeforeSave(update);
// 计算预算执行率
if (update.getAnnualBudget() != null && update.getActualExpenditure() != null
&& update.getAnnualBudget().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal rate = update.getActualExpenditure()
.divide(update.getAnnualBudget(), 4, BigDecimal.ROUND_HALF_UP)
.multiply(new BigDecimal("100"));
update.setBudgetExecutionRate(rate);
}
// 🔧 新增:更新时自动计算总分和等级
calculateTotalScoreAndLevel(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存草稿
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveDraft(CwProjectPerformanceSelfEvaluationBo bo) {
bo.setEvaluationStatus("draft");
if (bo.getId() == null) {
return insertByBo(bo);
} else {
return updateByBo(bo);
}
}
/**
* 提交自评
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean submitEvaluation(Long id) {
CwProjectPerformanceSelfEvaluation evaluation = baseMapper.selectById(id);
if (evaluation == null) {
throw new RuntimeException("自评记录不存在");
}
// 🔒 检查是否已锁定:锁定状态的绩效自评不允许提交
if (evaluation.getIsLocked() != null && evaluation.getIsLocked() == 1) {
throw new RuntimeException("该绩效自评已锁定,不允许提交。如需提交,请联系财务管理员或学校领导解锁");
}
if (!"draft".equals(evaluation.getEvaluationStatus())) {
throw new RuntimeException("只能提交草稿状态的自评");
}
// 验证必填字段
validateForSubmit(evaluation);
// 🔧 修复:使用统一的计算方法(支持新的9个得分字段)
calculateTotalScoreAndLevel(evaluation);
// 更新状态
evaluation.setEvaluationStatus("submitted");
evaluation.setSubmitTime(new Date());
evaluation.setSubmitBy(getUserIdSafely());
evaluation.setSubmitByName(LoginHelper.getUsername());
return baseMapper.updateById(evaluation) > 0;
}
/**
* 审核自评
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean approveEvaluation(Long id, String opinion, Boolean approved) {
CwProjectPerformanceSelfEvaluation evaluation = baseMapper.selectById(id);
if (evaluation == null) {
throw new RuntimeException("自评记录不存在");
}
// 🔒 检查是否已锁定:锁定状态的绩效自评不允许审核
if (evaluation.getIsLocked() != null && evaluation.getIsLocked() == 1) {
throw new RuntimeException("该绩效自评已锁定,不允许审核。如需审核,请联系财务管理员或学校领导解锁");
}
if (!"submitted".equals(evaluation.getEvaluationStatus())) {
throw new RuntimeException("只能审核已提交状态的自评");
}
// 更新审核信息
evaluation.setEvaluationStatus("approved");
evaluation.setApproveTime(new Date());
evaluation.setApproveBy(getUserIdSafely());
evaluation.setApproveByName(LoginHelper.getUsername());
evaluation.setApproveOpinion(opinion);
return baseMapper.updateById(evaluation) > 0;
}
/**
* 批量删除项目绩效自评
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 校验是否可以删除(例如:已审核的不能删除)
List<CwProjectPerformanceSelfEvaluation> list = baseMapper.selectBatchIds(ids);
for (CwProjectPerformanceSelfEvaluation evaluation : list) {
if ("approved".equals(evaluation.getEvaluationStatus())) {
throw new RuntimeException("已审核的自评不能删除");
}
}
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 统计自评数量
*/
@Override
public Long countEvaluations(Integer evaluationYear, String evaluationStatus) {
return baseMapper.countEvaluations(evaluationYear, evaluationStatus);
}
/**
* 计算平均分
*/
@Override
public BigDecimal calculateAverageScore(Integer evaluationYear) {
return baseMapper.calculateAverageScore(evaluationYear);
}
/**
* 按部门统计自评情况
*/
@Override
public List<Map<String, Object>> statisticsByDept(Integer evaluationYear) {
return baseMapper.statisticsByDept(evaluationYear);
}
/**
* 按等级统计自评数量
*/
@Override
public List<Map<String, Object>> statisticsByLevel(Integer evaluationYear) {
return baseMapper.statisticsByLevel(evaluationYear);
}
/**
* 导出项目绩效自评
*/
@Override
public void export(CwProjectPerformanceSelfEvaluationBo bo) {
// TODO: 实现导出功能
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(CwProjectPerformanceSelfEvaluation entity) {
// 校验项目名称不能为空
if (StringUtils.isBlank(entity.getProjectName())) {
throw new RuntimeException("项目名称不能为空");
}
// 校验自评年度不能为空
if (entity.getEvaluationYear() == null) {
throw new RuntimeException("自评年度不能为空");
}
}
/**
* 提交前验证必填字段
*/
private void validateForSubmit(CwProjectPerformanceSelfEvaluation entity) {
if (StringUtils.isBlank(entity.getProjectName())) {
throw new RuntimeException("项目名称不能为空");
}
if (entity.getEvaluationYear() == null) {
throw new RuntimeException("自评年度不能为空");
}
if (entity.getTotalInvestment() == null) {
throw new RuntimeException("项目总投资不能为空");
}
// ... 可以添加更多必填字段验证
}
/**
* 根据总分计算等级
*/
private String calculateLevel(BigDecimal totalScore) {
if (totalScore.compareTo(new BigDecimal("90")) >= 0) {
return "优秀";
} else if (totalScore.compareTo(new BigDecimal("80")) >= 0) {
return "良好";
} else if (totalScore.compareTo(new BigDecimal("60")) >= 0) {
return "合格";
} else {
return "不合格";
}
}
/**
* 计算总分和等级(保存时自动计算)
*/
private void calculateTotalScoreAndLevel(CwProjectPerformanceSelfEvaluation entity) {
// 🔧 修复:使用新的9个得分字段计算总分(而非旧的4个字段)
BigDecimal totalScore = BigDecimal.ZERO;
// 资金到位得分
if (entity.getFundScore() != null) {
totalScore = totalScore.add(entity.getFundScore());
}
// 产出指标得分(4项)
if (entity.getQuantityScore() != null) {
totalScore = totalScore.add(entity.getQuantityScore());
}
if (entity.getQualityScore() != null) {
totalScore = totalScore.add(entity.getQualityScore());
}
if (entity.getTimelinessScore() != null) {
totalScore = totalScore.add(entity.getTimelinessScore());
}
if (entity.getCostScore() != null) {
totalScore = totalScore.add(entity.getCostScore());
}
// 效益指标得分(4项)
if (entity.getEconomicScore() != null) {
totalScore = totalScore.add(entity.getEconomicScore());
}
if (entity.getSocialScore() != null) {
totalScore = totalScore.add(entity.getSocialScore());
}
if (entity.getEcologicalScore() != null) {
totalScore = totalScore.add(entity.getEcologicalScore());
}
if (entity.getSatisfactionScore() != null) {
totalScore = totalScore.add(entity.getSatisfactionScore());
}
// 只有当总分大于0时才设置
if (totalScore.compareTo(BigDecimal.ZERO) > 0) {
entity.setTotalScore(totalScore);
// 根据总分自动判定等级
String level = calculateLevel(totalScore);
entity.setSelfEvaluationLevel(level);
}
}
/**
* 安全获取用户ID,处理可能的字符串格式(如 "sys_user:1")
*/
private Long getUserIdSafely() {
try {
Long userId = LoginHelper.getUserId();
if (userId != null) {
return userId;
}
} catch (Exception e) {
// 如果getUserId()失败,尝试从getUserIdStr()解析
}
// 尝试从字符串格式解析
try {
String userIdStr = LoginHelper.getUserIdStr();
if (StringUtils.isNotBlank(userIdStr)) {
// 处理 "sys_user:1" 这种格式,提取数字部分
if (userIdStr.contains(":")) {
String[] parts = userIdStr.split(":");
if (parts.length > 1) {
return Long.parseLong(parts[1]);
}
} else {
return Long.parseLong(userIdStr);
}
}
} catch (Exception e) {
// 解析失败,返回null或抛出异常
}
return null;
}
}
@@ -0,0 +1,87 @@
-- =============================================
-- 检查绩效自评数据完整性
-- 功能:查看指定记录的所有字段值
-- 创建时间:2025-11-04
-- =============================================
USE `hzdyjs_xg`;
-- 查询最新的一条记录,显示所有字段
SELECT
-- 基本信息
id,
project_name,
supervisory_dept,
implement_unit,
evaluation_year,
-- 资金信息
initial_budget,
annual_budget,
actual_expenditure,
fund_score,
-- 绩效目标
performance_goal,
target_completion,
-- 数量指标
quantity_indicator,
quantity_target,
quantity_actual,
quantity_value,
quantity_score,
quantity_deviation,
-- 质量指标
quality_indicator,
quality_target,
quality_actual,
quality_value,
quality_score,
quality_deviation,
-- 时效指标
timeliness_indicator,
timeliness_target,
timeliness_actual,
timeliness_value,
timeliness_score,
timeliness_deviation,
-- 成本指标
cost_indicator,
cost_target,
cost_actual,
cost_value,
cost_score,
cost_deviation,
-- 效益指标
economic_indicator,
social_indicator,
ecological_indicator,
satisfaction_indicator,
-- 总分和等级
total_score,
self_evaluation_level,
-- 状态
evaluation_status,
create_time
FROM `cw_project_performance_self_evaluation`
WHERE deleted = 0
ORDER BY create_time DESC
LIMIT 1;
-- 统计各字段的NULL值数量
SELECT
'项目名称' as field_name, COUNT(*) - COUNT(project_name) as null_count FROM cw_project_performance_self_evaluation WHERE deleted = 0
UNION ALL SELECT '主管部门', COUNT(*) - COUNT(supervisory_dept) FROM cw_project_performance_self_evaluation WHERE deleted = 0
UNION ALL SELECT '实施单位', COUNT(*) - COUNT(implement_unit) FROM cw_project_performance_self_evaluation WHERE deleted = 0
UNION ALL SELECT '绩效目标', COUNT(*) - COUNT(performance_goal) FROM cw_project_performance_self_evaluation WHERE deleted = 0
UNION ALL SELECT '目标完成情况', COUNT(*) - COUNT(target_completion) FROM cw_project_performance_self_evaluation WHERE deleted = 0
UNION ALL SELECT '数量指标名称', COUNT(*) - COUNT(quantity_indicator) FROM cw_project_performance_self_evaluation WHERE deleted = 0
UNION ALL SELECT '质量指标名称', COUNT(*) - COUNT(quality_indicator) FROM cw_project_performance_self_evaluation WHERE deleted = 0;
@@ -0,0 +1,27 @@
-- 项目绩效自评状态字典
-- 删除旧数据(如果存在)
DELETE FROM sys_dict_data WHERE dict_type = 'cw_evaluation_status';
DELETE FROM sys_dict_type WHERE dict_type = 'cw_evaluation_status';
-- 插入字典类型
INSERT INTO sys_dict_type (dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark)
VALUES (
NEXT VALUE FOR sys_dict_type_seq, -- dict_id 使用序列生成
'000000', -- tenant_id
'绩效自评状态', -- dict_name
'cw_evaluation_status', -- dict_type
103, -- create_dept
1, -- create_by
NOW(), -- create_time
1, -- update_by
NOW(), -- update_time
'项目绩效自评的状态' -- remark
);
-- 插入字典数据项
INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark)
VALUES
(NEXT VALUE FOR sys_dict_data_seq, '000000', 1, '草稿', 'draft', 'cw_evaluation_status', '', 'info', 'N', 103, 1, NOW(), 1, NOW(), '草稿状态'),
(NEXT VALUE FOR sys_dict_data_seq, '000000', 2, '已提交', 'submitted', 'cw_evaluation_status', '', 'primary', 'N', 103, 1, NOW(), 1, NOW(), '已提交待审核'),
(NEXT VALUE FOR sys_dict_data_seq, '000000', 3, '已审核', 'approved', 'cw_evaluation_status', '', 'success', 'N', 103, 1, NOW(), 1, NOW(), '已审核通过');
@@ -0,0 +1,22 @@
-- ========================================
-- 修复 application_no 字段问题
-- 日期: 2025-01-03
-- 描述: 允许 application_no 字段为空并设置默认值
-- ========================================
-- 修改 application_no 字段:允许为空,设置默认空字符串
ALTER TABLE cw_project_performance_self_evaluation
MODIFY COLUMN application_no varchar(50) NULL DEFAULT '' COMMENT '项目申报编号';
-- 更新现有的空值或无效值记录
UPDATE cw_project_performance_self_evaluation
SET application_no = CONCAT('TEMP-', id)
WHERE application_no IS NULL
OR application_no = ''
OR application_no = 'APP-0';
-- 验证修改结果
SELECT COUNT(*) as total_count,
SUM(CASE WHEN application_no IS NULL OR application_no = '' THEN 1 ELSE 0 END) as empty_count
FROM cw_project_performance_self_evaluation;
@@ -0,0 +1,66 @@
-- =============================================
-- 修复绩效自评总分计算脚本
-- 功能:重新计算所有记录的total_score(使用新的9个得分字段)
-- 创建时间:2025-11-04
-- =============================================
USE `hzdyjs_xg`;
-- 更新所有记录的总分(使用新的9个得分字段)
UPDATE `cw_project_performance_self_evaluation`
SET
total_score = COALESCE(fund_score, 0)
+ COALESCE(quantity_score, 0)
+ COALESCE(quality_score, 0)
+ COALESCE(timeliness_score, 0)
+ COALESCE(cost_score, 0)
+ COALESCE(economic_score, 0)
+ COALESCE(social_score, 0)
+ COALESCE(ecological_score, 0)
+ COALESCE(satisfaction_score, 0),
-- 根据总分自动判定等级
self_evaluation_level = CASE
WHEN (COALESCE(fund_score, 0) + COALESCE(quantity_score, 0) + COALESCE(quality_score, 0)
+ COALESCE(timeliness_score, 0) + COALESCE(cost_score, 0) + COALESCE(economic_score, 0)
+ COALESCE(social_score, 0) + COALESCE(ecological_score, 0) + COALESCE(satisfaction_score, 0)) >= 90 THEN '优秀'
WHEN (COALESCE(fund_score, 0) + COALESCE(quantity_score, 0) + COALESCE(quality_score, 0)
+ COALESCE(timeliness_score, 0) + COALESCE(cost_score, 0) + COALESCE(economic_score, 0)
+ COALESCE(social_score, 0) + COALESCE(ecological_score, 0) + COALESCE(satisfaction_score, 0)) >= 80 THEN '良好'
WHEN (COALESCE(fund_score, 0) + COALESCE(quantity_score, 0) + COALESCE(quality_score, 0)
+ COALESCE(timeliness_score, 0) + COALESCE(cost_score, 0) + COALESCE(economic_score, 0)
+ COALESCE(social_score, 0) + COALESCE(ecological_score, 0) + COALESCE(satisfaction_score, 0)) >= 60 THEN '合格'
ELSE '不合格'
END
WHERE deleted = 0;
-- 查看更新结果
SELECT
id,
project_name,
fund_score,
quantity_score,
quality_score,
timeliness_score,
cost_score,
economic_score,
social_score,
ecological_score,
satisfaction_score,
total_score,
self_evaluation_level,
evaluation_status
FROM `cw_project_performance_self_evaluation`
WHERE deleted = 0
ORDER BY create_time DESC
LIMIT 10;
-- =============================================
-- 说明
-- =============================================
-- 1. 本脚本重新计算所有记录的总分(基于新的9个得分字段)
-- 2. 同时更新自评等级(优秀≥90分,良好≥80分,合格≥60分)
-- 3. 只更新未删除的记录(deleted = 0)
-- 4. 执行后可以查看最近10条记录验证结果
-- =============================================
@@ -0,0 +1,237 @@
-- =============================================
-- 绩效自评表结构升级脚本 (方案一:完整版)
-- 功能:补全52个缺失字段,支持完整的杭州市绩效自评标准
-- 创建时间:2025-11-03
-- 执行前请备份数据库!
-- =============================================
USE `hzdyjs_xg`;
-- =============================================
-- 第一部分:基础信息字段(2个)
-- =============================================
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `supervisory_dept` varchar(100) DEFAULT NULL COMMENT '主管部门' AFTER `project_name`,
ADD COLUMN `implement_unit` varchar(100) DEFAULT NULL COMMENT '实施单位' AFTER `supervisory_dept`;
-- =============================================
-- 第二部分:项目资金详细字段(18个)
-- =============================================
-- 2.1 年度资金总额(4个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `initial_budget` decimal(15,2) DEFAULT NULL COMMENT '年初预算数(万元)' AFTER `evaluation_year`,
ADD COLUMN `fund_value` decimal(5,2) DEFAULT NULL COMMENT '资金到位分值' AFTER `actual_expenditure`,
ADD COLUMN `fund_execution_rate` decimal(5,2) DEFAULT NULL COMMENT '资金执行率(%)' AFTER `fund_value`,
ADD COLUMN `fund_score` decimal(5,2) DEFAULT NULL COMMENT '资金到位得分' AFTER `fund_execution_rate`;
-- 2.2 当年财政拨款(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `current_year_budget_initial` decimal(15,2) DEFAULT NULL COMMENT '当年财政拨款-年初预算数(万元)' AFTER `fund_score`,
ADD COLUMN `current_year_budget_annual` decimal(15,2) DEFAULT NULL COMMENT '当年财政拨款-全年预算数(万元)' AFTER `current_year_budget_initial`,
ADD COLUMN `current_year_budget_actual` decimal(15,2) DEFAULT NULL COMMENT '当年财政拨款-全年执行数(万元)' AFTER `current_year_budget_annual`,
ADD COLUMN `current_year_budget_value` decimal(5,2) DEFAULT NULL COMMENT '当年财政拨款-分值' AFTER `current_year_budget_actual`,
ADD COLUMN `current_year_budget_execution_rate` decimal(5,2) DEFAULT NULL COMMENT '当年财政拨款-执行率(%)' AFTER `current_year_budget_value`,
ADD COLUMN `current_year_budget_score` decimal(5,2) DEFAULT NULL COMMENT '当年财政拨款-得分' AFTER `current_year_budget_execution_rate`;
-- 2.3 上年结转资金(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `last_year_carryover_initial` decimal(15,2) DEFAULT NULL COMMENT '上年结转资金-年初预算数(万元)' AFTER `current_year_budget_score`,
ADD COLUMN `last_year_carryover_annual` decimal(15,2) DEFAULT NULL COMMENT '上年结转资金-全年预算数(万元)' AFTER `last_year_carryover_initial`,
ADD COLUMN `last_year_carryover_actual` decimal(15,2) DEFAULT NULL COMMENT '上年结转资金-全年执行数(万元)' AFTER `last_year_carryover_annual`,
ADD COLUMN `last_year_carryover_value` decimal(5,2) DEFAULT NULL COMMENT '上年结转资金-分值' AFTER `last_year_carryover_actual`,
ADD COLUMN `last_year_carryover_execution_rate` decimal(5,2) DEFAULT NULL COMMENT '上年结转资金-执行率(%)' AFTER `last_year_carryover_value`,
ADD COLUMN `last_year_carryover_score` decimal(5,2) DEFAULT NULL COMMENT '上年结转资金-得分' AFTER `last_year_carryover_execution_rate`;
-- 2.4 其他资金(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `other_funds_initial` decimal(15,2) DEFAULT NULL COMMENT '其他资金-年初预算数(万元)' AFTER `last_year_carryover_score`,
ADD COLUMN `other_funds_annual` decimal(15,2) DEFAULT NULL COMMENT '其他资金-全年预算数(万元)' AFTER `other_funds_initial`,
ADD COLUMN `other_funds_actual` decimal(15,2) DEFAULT NULL COMMENT '其他资金-全年执行数(万元)' AFTER `other_funds_annual`,
ADD COLUMN `other_funds_value` decimal(5,2) DEFAULT NULL COMMENT '其他资金-分值' AFTER `other_funds_actual`,
ADD COLUMN `other_funds_execution_rate` decimal(5,2) DEFAULT NULL COMMENT '其他资金-执行率(%)' AFTER `other_funds_value`,
ADD COLUMN `other_funds_score` decimal(5,2) DEFAULT NULL COMMENT '其他资金-得分' AFTER `other_funds_execution_rate`;
-- =============================================
-- 第三部分:产出指标字段(24个)
-- =============================================
-- 3.1 数量指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `quantity_indicator` varchar(200) DEFAULT NULL COMMENT '数量指标名称' AFTER `target_completion`,
ADD COLUMN `quantity_target` varchar(100) DEFAULT NULL COMMENT '数量指标-指标值' AFTER `quantity_indicator`,
ADD COLUMN `quantity_actual` varchar(100) DEFAULT NULL COMMENT '数量指标-实际完成值' AFTER `quantity_target`,
ADD COLUMN `quantity_value` decimal(5,2) DEFAULT NULL COMMENT '数量指标-分值' AFTER `quantity_actual`,
ADD COLUMN `quantity_score` decimal(5,2) DEFAULT NULL COMMENT '数量指标-得分' AFTER `quantity_value`,
ADD COLUMN `quantity_deviation` text DEFAULT NULL COMMENT '数量指标-偏差原因分析及改进措施' AFTER `quantity_score`;
-- 3.2 质量指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `quality_indicator` varchar(200) DEFAULT NULL COMMENT '质量指标名称' AFTER `quantity_deviation`,
ADD COLUMN `quality_target` varchar(100) DEFAULT NULL COMMENT '质量指标-指标值' AFTER `quality_indicator`,
ADD COLUMN `quality_actual` varchar(100) DEFAULT NULL COMMENT '质量指标-实际完成值' AFTER `quality_target`,
ADD COLUMN `quality_value` decimal(5,2) DEFAULT NULL COMMENT '质量指标-分值' AFTER `quality_actual`,
ADD COLUMN `quality_score` decimal(5,2) DEFAULT NULL COMMENT '质量指标-得分' AFTER `quality_value`,
ADD COLUMN `quality_deviation` text DEFAULT NULL COMMENT '质量指标-偏差原因分析及改进措施' AFTER `quality_score`;
-- 3.3 时效指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `timeliness_indicator` varchar(200) DEFAULT NULL COMMENT '时效指标名称' AFTER `quality_deviation`,
ADD COLUMN `timeliness_target` varchar(100) DEFAULT NULL COMMENT '时效指标-指标值' AFTER `timeliness_indicator`,
ADD COLUMN `timeliness_actual` varchar(100) DEFAULT NULL COMMENT '时效指标-实际完成值' AFTER `timeliness_target`,
ADD COLUMN `timeliness_value` decimal(5,2) DEFAULT NULL COMMENT '时效指标-分值' AFTER `timeliness_actual`,
ADD COLUMN `timeliness_score` decimal(5,2) DEFAULT NULL COMMENT '时效指标-得分' AFTER `timeliness_value`,
ADD COLUMN `timeliness_deviation` text DEFAULT NULL COMMENT '时效指标-偏差原因分析及改进措施' AFTER `timeliness_score`;
-- 3.4 成本指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `cost_indicator` varchar(200) DEFAULT NULL COMMENT '成本指标名称' AFTER `timeliness_deviation`,
ADD COLUMN `cost_target` varchar(100) DEFAULT NULL COMMENT '成本指标-指标值' AFTER `cost_indicator`,
ADD COLUMN `cost_actual` varchar(100) DEFAULT NULL COMMENT '成本指标-实际完成值' AFTER `cost_target`,
ADD COLUMN `cost_value` decimal(5,2) DEFAULT NULL COMMENT '成本指标-分值' AFTER `cost_actual`,
ADD COLUMN `cost_score` decimal(5,2) DEFAULT NULL COMMENT '成本指标-得分' AFTER `cost_value`,
ADD COLUMN `cost_deviation` text DEFAULT NULL COMMENT '成本指标-偏差原因分析及改进措施' AFTER `cost_score`;
-- =============================================
-- 第四部分:效益指标字段(24个)
-- =============================================
-- 4.1 经济效益指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `economic_indicator` varchar(200) DEFAULT NULL COMMENT '经济效益指标名称' AFTER `cost_deviation`,
ADD COLUMN `economic_target` varchar(100) DEFAULT NULL COMMENT '经济效益指标-指标值' AFTER `economic_indicator`,
ADD COLUMN `economic_actual` varchar(100) DEFAULT NULL COMMENT '经济效益指标-实际完成值' AFTER `economic_target`,
ADD COLUMN `economic_value` decimal(5,2) DEFAULT NULL COMMENT '经济效益指标-分值' AFTER `economic_actual`,
ADD COLUMN `economic_score` decimal(5,2) DEFAULT NULL COMMENT '经济效益指标-得分' AFTER `economic_value`,
ADD COLUMN `economic_deviation` text DEFAULT NULL COMMENT '经济效益指标-偏差原因分析及改进措施' AFTER `economic_score`;
-- 4.2 社会效益指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `social_indicator` varchar(200) DEFAULT NULL COMMENT '社会效益指标名称' AFTER `economic_deviation`,
ADD COLUMN `social_target` varchar(100) DEFAULT NULL COMMENT '社会效益指标-指标值' AFTER `social_indicator`,
ADD COLUMN `social_actual` varchar(100) DEFAULT NULL COMMENT '社会效益指标-实际完成值' AFTER `social_target`,
ADD COLUMN `social_value` decimal(5,2) DEFAULT NULL COMMENT '社会效益指标-分值' AFTER `social_actual`,
ADD COLUMN `social_score` decimal(5,2) DEFAULT NULL COMMENT '社会效益指标-得分' AFTER `social_value`,
ADD COLUMN `social_deviation` text DEFAULT NULL COMMENT '社会效益指标-偏差原因分析及改进措施' AFTER `social_score`;
-- 4.3 生态效益指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `ecological_indicator` varchar(200) DEFAULT NULL COMMENT '生态效益指标名称' AFTER `social_deviation`,
ADD COLUMN `ecological_target` varchar(100) DEFAULT NULL COMMENT '生态效益指标-指标值' AFTER `ecological_indicator`,
ADD COLUMN `ecological_actual` varchar(100) DEFAULT NULL COMMENT '生态效益指标-实际完成值' AFTER `ecological_target`,
ADD COLUMN `ecological_value` decimal(5,2) DEFAULT NULL COMMENT '生态效益指标-分值' AFTER `ecological_actual`,
ADD COLUMN `ecological_score` decimal(5,2) DEFAULT NULL COMMENT '生态效益指标-得分' AFTER `ecological_value`,
ADD COLUMN `ecological_deviation` text DEFAULT NULL COMMENT '生态效益指标-偏差原因分析及改进措施' AFTER `ecological_score`;
-- 4.4 满意度指标(6个字段)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `satisfaction_indicator` varchar(200) DEFAULT NULL COMMENT '满意度指标名称' AFTER `ecological_deviation`,
ADD COLUMN `satisfaction_target` varchar(100) DEFAULT NULL COMMENT '满意度指标-指标值' AFTER `satisfaction_indicator`,
ADD COLUMN `satisfaction_actual` varchar(100) DEFAULT NULL COMMENT '满意度指标-实际完成值' AFTER `satisfaction_target`,
ADD COLUMN `satisfaction_value` decimal(5,2) DEFAULT NULL COMMENT '满意度指标-分值' AFTER `satisfaction_actual`,
ADD COLUMN `satisfaction_score` decimal(5,2) DEFAULT NULL COMMENT '满意度指标-得分' AFTER `satisfaction_value`,
ADD COLUMN `satisfaction_deviation` text DEFAULT NULL COMMENT '满意度指标-偏差原因分析及改进措施' AFTER `satisfaction_score`;
-- =============================================
-- 升级完成验证
-- =============================================
-- 查看表结构
DESC `cw_project_performance_self_evaluation`;
-- 统计字段数量(应该是89个字段:原37个 + 新增52个)
SELECT COUNT(*) AS total_columns
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'hzdyjs_xg'
AND TABLE_NAME = 'cw_project_performance_self_evaluation';
-- =============================================
-- 回滚脚本(如果升级失败需要回滚)
-- =============================================
/*
-- 删除所有新增字段
ALTER TABLE `cw_project_performance_self_evaluation`
DROP COLUMN `supervisory_dept`,
DROP COLUMN `implement_unit`,
DROP COLUMN `initial_budget`,
DROP COLUMN `fund_value`,
DROP COLUMN `fund_execution_rate`,
DROP COLUMN `fund_score`,
DROP COLUMN `current_year_budget_initial`,
DROP COLUMN `current_year_budget_annual`,
DROP COLUMN `current_year_budget_actual`,
DROP COLUMN `current_year_budget_value`,
DROP COLUMN `current_year_budget_execution_rate`,
DROP COLUMN `current_year_budget_score`,
DROP COLUMN `last_year_carryover_initial`,
DROP COLUMN `last_year_carryover_annual`,
DROP COLUMN `last_year_carryover_actual`,
DROP COLUMN `last_year_carryover_value`,
DROP COLUMN `last_year_carryover_execution_rate`,
DROP COLUMN `last_year_carryover_score`,
DROP COLUMN `other_funds_initial`,
DROP COLUMN `other_funds_annual`,
DROP COLUMN `other_funds_actual`,
DROP COLUMN `other_funds_value`,
DROP COLUMN `other_funds_execution_rate`,
DROP COLUMN `other_funds_score`,
DROP COLUMN `quantity_indicator`,
DROP COLUMN `quantity_target`,
DROP COLUMN `quantity_actual`,
DROP COLUMN `quantity_value`,
DROP COLUMN `quantity_score`,
DROP COLUMN `quantity_deviation`,
DROP COLUMN `quality_indicator`,
DROP COLUMN `quality_target`,
DROP COLUMN `quality_actual`,
DROP COLUMN `quality_value`,
DROP COLUMN `quality_score`,
DROP COLUMN `quality_deviation`,
DROP COLUMN `timeliness_indicator`,
DROP COLUMN `timeliness_target`,
DROP COLUMN `timeliness_actual`,
DROP COLUMN `timeliness_value`,
DROP COLUMN `timeliness_score`,
DROP COLUMN `timeliness_deviation`,
DROP COLUMN `cost_indicator`,
DROP COLUMN `cost_target`,
DROP COLUMN `cost_actual`,
DROP COLUMN `cost_value`,
DROP COLUMN `cost_score`,
DROP COLUMN `cost_deviation`,
DROP COLUMN `economic_indicator`,
DROP COLUMN `economic_target`,
DROP COLUMN `economic_actual`,
DROP COLUMN `economic_value`,
DROP COLUMN `economic_score`,
DROP COLUMN `economic_deviation`,
DROP COLUMN `social_indicator`,
DROP COLUMN `social_target`,
DROP COLUMN `social_actual`,
DROP COLUMN `social_value`,
DROP COLUMN `social_score`,
DROP COLUMN `social_deviation`,
DROP COLUMN `ecological_indicator`,
DROP COLUMN `ecological_target`,
DROP COLUMN `ecological_actual`,
DROP COLUMN `ecological_value`,
DROP COLUMN `ecological_score`,
DROP COLUMN `ecological_deviation`,
DROP COLUMN `satisfaction_indicator`,
DROP COLUMN `satisfaction_target`,
DROP COLUMN `satisfaction_actual`,
DROP COLUMN `satisfaction_value`,
DROP COLUMN `satisfaction_score`,
DROP COLUMN `satisfaction_deviation`;
*/
-- =============================================
-- 升级说明
-- =============================================
-- 1. 执行前请备份数据库
-- 2. 本脚本增加了52个新字段,完整支持杭州市绩效自评表标准格式
-- 3. 所有新字段均允许为NULL,不影响现有数据
-- 4. 升级后前端表单所有字段都能正常保存和显示
-- 5. 如需回滚,请执行上面注释中的回滚脚本
-- =============================================
@@ -0,0 +1,29 @@
-- =============================================
-- 修复唯一索引冲突问题
-- 问题:原唯一索引导致同一项目同一年度无法创建多条自评记录
-- 解决:删除旧索引,添加新的合理索引
-- 创建时间:2025-11-03
-- =============================================
USE `hzdyjs_xg`;
-- 1. 删除原有的唯一索引
ALTER TABLE `cw_project_performance_self_evaluation`
DROP INDEX `uk_application_evaluation`;
-- 2. 添加普通索引(允许重复,但便于查询)
ALTER TABLE `cw_project_performance_self_evaluation`
ADD INDEX `idx_application_year` (`project_application_id`, `evaluation_year`, `tenant_id`)
COMMENT '项目-年度-租户联合索引';
-- 3. 验证索引
SHOW INDEX FROM `cw_project_performance_self_evaluation`;
-- =============================================
-- 说明:
-- 1. 删除了唯一约束,允许同一项目同一年度创建多条自评记录
-- 2. 保留普通索引以提升查询性能
-- 3. 用户可以创建多个草稿,最终只提交一个
-- 4. 如需控制重复提交,应在业务层面控制(如:状态为已提交时禁止再次提交)
-- =============================================
@@ -0,0 +1,257 @@
-- =============================================
-- 修复菜单乱码数据脚本
-- 创建时间: 2025-01-03
-- 说明: 修复因INSERT语句字段顺序错误导致的菜单数据乱码问题
-- =============================================
-- 修复绩效自评管理菜单(menu_id = 4050)
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e7aea1e79086') USING utf8mb4),
`perms` = 'cw:evaluation:list',
`icon` = 'chart',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = CONVERT(UNHEX('e9a1b9e79baee7bba9e69588e887aae8af84e7aea1e79086e88f9ce58d95') USING utf8mb4)
WHERE `menu_id` = 4050;
-- 修复绩效自评管理按钮权限
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e69fa5e8afa2') USING utf8mb4),
`perms` = 'cw:evaluation:query',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405001;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e696b0e5a29e') USING utf8mb4),
`perms` = 'cw:evaluation:add',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405002;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e4bfaee694b9') USING utf8mb4),
`perms` = 'cw:evaluation:edit',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405003;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e588a0e999a4') USING utf8mb4),
`perms` = 'cw:evaluation:remove',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405004;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e68f90e4baa4') USING utf8mb4),
`perms` = 'cw:evaluation:submit',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405005;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e5aea1e6a0b8') USING utf8mb4),
`perms` = 'cw:evaluation:approve',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405006;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e7bba9e69588e887aae8af84e5afbce587ba') USING utf8mb4),
`perms` = 'cw:evaluation:export',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405007;
-- 修复运行监控管理菜单(menu_id = 4052)
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e7aea1e79086') USING utf8mb4),
`perms` = 'cw:monitoringManagement:list',
`icon` = 'monitor',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = CONVERT(UNHEX('e9a1b9e79baee8bf90e8a18ce79b91e68ea7e7aea1e79086e88f9ce58d95') USING utf8mb4)
WHERE `menu_id` = 4052;
-- 修复运行监控管理按钮权限
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e69fa5e8afa2') USING utf8mb4),
`perms` = 'cw:monitoring:query',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405101;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e5a1abe68aa5') USING utf8mb4),
`perms` = 'cw:monitoring:fill',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405102;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e4bfaee694b9') USING utf8mb4),
`perms` = 'cw:monitoring:edit',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405103;
-- 修复绩效运行监控按钮权限
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e69fa5e79c8b') USING utf8mb4),
`perms` = 'cw:performanceMonitoring:query',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405008;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e696b0e5a29e') USING utf8mb4),
`perms` = 'cw:performanceMonitoring:add',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405009;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e7bc96e8be91') USING utf8mb4),
`perms` = 'cw:performanceMonitoring:edit',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405010;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e588a0e999a4') USING utf8mb4),
`perms` = 'cw:performanceMonitoring:remove',
`icon` = '#',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405011;
-- 修复运行监控管理按钮权限(menu_id = 405201, 405202, 405203)
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e69fa5e8afa2') USING utf8mb4),
`perms` = 'cw:monitoringManagement:query',
`icon` = '',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405201;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e99481e5ae9a') USING utf8mb4),
`perms` = 'cw:monitoringManagement:lock',
`icon` = '',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405202;
UPDATE `sys_menu` SET
`menu_name` = CONVERT(UNHEX('e8bf90e8a18ce79b91e68ea7e8a7a3e99481') USING utf8mb4),
`perms` = 'cw:monitoringManagement:unlock',
`icon` = '',
`create_dept` = NULL,
`create_by` = NULL,
`create_time` = NOW(),
`update_by` = NULL,
`update_time` = NULL,
`remark` = ''
WHERE `menu_id` = 405203;
-- =============================================
-- 验证修复结果
-- =============================================
-- 检查修复后的菜单数据
SELECT
menu_id,
menu_name,
perms,
icon,
remark
FROM `sys_menu`
WHERE menu_id IN (4050, 405001, 405002, 405003, 405004, 405005, 405006, 405007,
4051, 405101, 405102, 405103,
405008, 405009, 405010, 405011,
4052, 405201, 405202, 405203)
ORDER BY menu_id;
@@ -0,0 +1,15 @@
-- =============================================
-- 添加允许已提交后编辑字段
-- 创建时间: 2025-01-03
-- 说明: 允许已提交的项目在特定条件下可以修改
-- =============================================
-- 添加允许已提交后编辑字段
ALTER TABLE `cw_project_performance_self_evaluation`
ADD COLUMN `allow_edit_after_submit` tinyint(1) DEFAULT '0' COMMENT '允许已提交后编辑(0-不允许 1-允许)'
AFTER `evaluation_status`;
-- 添加索引(可选,如果需要根据此字段查询)
-- ALTER TABLE `cw_project_performance_self_evaluation`
-- ADD INDEX `idx_allow_edit` (`allow_edit_after_submit`) USING BTREE;
@@ -0,0 +1,63 @@
-- =============================================
-- 绩效运行监控表建表脚本
-- 创建时间: 2025-11-04
-- 说明: 基于PDF模板的绩效运行监控表
-- =============================================
-- =============================================
-- 表: 绩效运行监控表
-- =============================================
CREATE TABLE `cw_performance_monitoring` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '000000' COMMENT '租户ID',
-- 关联信息
`evaluation_id` bigint(20) NOT NULL COMMENT '绩效自评ID',
`project_application_id` bigint(20) DEFAULT NULL COMMENT '项目申报ID',
-- 监控周期
`monitoring_period` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '监控周期(例如:2024年度1-6月)',
-- 项目概况
`fund_usage` text COLLATE utf8mb4_unicode_ci COMMENT '资金使用情况',
`performance_target_completion` text COLLATE utf8mb4_unicode_ci COMMENT '绩效目标完成情况',
-- 绩效指标完成情况(JSON格式存储)
`indicators` text COLLATE utf8mb4_unicode_ci COMMENT '绩效指标完成情况(JSON数组)',
-- 绩效运行分析
`existing_problems` text COLLATE utf8mb4_unicode_ci COMMENT '存在问题',
`indicator_variance_analysis` text COLLATE utf8mb4_unicode_ci COMMENT '指标差异率分析和说明',
`next_step_suggestions` text COLLATE utf8mb4_unicode_ci COMMENT '下一步工作建议和打算',
-- 填报信息
`fill_by` bigint(20) DEFAULT NULL COMMENT '填报人ID',
`fill_by_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '填报人姓名',
-- 系统字段
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '删除标志',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_evaluation_id` (`evaluation_id`) USING BTREE,
KEY `idx_project_application` (`project_application_id`) USING BTREE,
KEY `idx_tenant` (`tenant_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='绩效运行监控表';
-- =============================================
-- 菜单权限配置(如果需要独立菜单)
-- =============================================
-- 注:这里只添加按钮权限,因为运行监控作为绩效自评的子功能
-- 绩效运行监控按钮权限
INSERT INTO `sys_menu` VALUES
(405008, '运行监控查看', 4050, 8, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:query', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405009, '运行监控新增', 4050, 9, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:add', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405010, '运行监控编辑', 4050, 10, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:edit', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405011, '运行监控删除', 4050, 11, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:remove', '#', NULL, NULL, NOW(), NULL, NULL, '');
@@ -0,0 +1,35 @@
-- =============================================
-- 绩效运行监控菜单权限脚本
-- 创建时间: 2025-11-04
-- =============================================
-- 绩效运行监控按钮权限
-- 注:这里只添加按钮权限,因为运行监控作为绩效自评的子功能
INSERT INTO `sys_menu` (
`menu_id`,
`menu_name`,
`parent_id`,
`order_num`,
`path`,
`component`,
`query_param`,
`is_frame`,
`is_cache`,
`menu_type`,
`visible`,
`status`,
`perms`,
`icon`,
`create_dept`,
`create_by`,
`create_time`,
`update_by`,
`update_time`,
`remark`
) VALUES
(405008, '运行监控查看', 4050, 8, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:query', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405009, '运行监控新增', 4050, 9, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:add', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405010, '运行监控编辑', 4050, 10, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:edit', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405011, '运行监控删除', 4050, 11, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:performanceMonitoring:remove', '#', NULL, NULL, NOW(), NULL, NULL, '');
@@ -0,0 +1,88 @@
-- =============================================
-- 运行监控管理功能 - 添加锁定字段
-- 创建时间: 2025-01-03
-- 说明: 为绩效自评表添加锁定字段,用于运行监控管理功能
-- =============================================
-- 添加锁定字段(如果字段已存在则跳过)
-- 检查并添加 is_locked 字段
SET @exist := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cw_project_performance_self_evaluation' AND COLUMN_NAME = 'is_locked');
SET @sqlstmt := IF(@exist = 0, 'ALTER TABLE `cw_project_performance_self_evaluation` ADD COLUMN `is_locked` tinyint(1) DEFAULT ''0'' COMMENT ''是否锁定(0-未锁定 1-已锁定),锁定后不可修改'' AFTER `approve_opinion`', 'SELECT ''字段 is_locked 已存在,跳过添加''');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 lock_time 字段
SET @exist := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cw_project_performance_self_evaluation' AND COLUMN_NAME = 'lock_time');
SET @sqlstmt := IF(@exist = 0, 'ALTER TABLE `cw_project_performance_self_evaluation` ADD COLUMN `lock_time` datetime DEFAULT NULL COMMENT ''锁定时间'' AFTER `is_locked`', 'SELECT ''字段 lock_time 已存在,跳过添加''');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 lock_by 字段
SET @exist := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cw_project_performance_self_evaluation' AND COLUMN_NAME = 'lock_by');
SET @sqlstmt := IF(@exist = 0, 'ALTER TABLE `cw_project_performance_self_evaluation` ADD COLUMN `lock_by` bigint(20) DEFAULT NULL COMMENT ''锁定人ID'' AFTER `lock_time`', 'SELECT ''字段 lock_by 已存在,跳过添加''');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 lock_by_name 字段
SET @exist := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cw_project_performance_self_evaluation' AND COLUMN_NAME = 'lock_by_name');
SET @sqlstmt := IF(@exist = 0, 'ALTER TABLE `cw_project_performance_self_evaluation` ADD COLUMN `lock_by_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ''锁定人姓名'' AFTER `lock_by`', 'SELECT ''字段 lock_by_name 已存在,跳过添加''');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 添加锁定状态索引(如果索引已存在则跳过)
SET @exist := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cw_project_performance_self_evaluation' AND INDEX_NAME = 'idx_is_locked');
SET @sqlstmt := IF(@exist = 0, 'ALTER TABLE `cw_project_performance_self_evaluation` ADD KEY `idx_is_locked` (`is_locked`) USING BTREE', 'SELECT ''索引 idx_is_locked 已存在,跳过添加''');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- =============================================
-- 菜单权限配置 - 运行监控管理
-- =============================================
-- 注意:运行监控管理作为绩效自评的同级菜单,父菜单ID为6000(与绩效自评4050同级)
-- 如果菜单ID 4052 已被使用,请修改为其他未使用的ID
-- 二级菜单:运行监控管理(如果已存在则跳过,请根据实际情况调整菜单ID)
INSERT INTO `sys_menu` (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES
(4052, '运行监控管理', 6000, 8, 'monitoringManagement', 'cw/monitoringManagement/index', NULL, 1, 0, 'C', '0', '0', 'cw:monitoringManagement:list', 'monitor', NULL, NULL, NOW(), NULL, NULL, '项目运行监控管理菜单(财务管理员和学校领导使用)')
ON DUPLICATE KEY UPDATE menu_name = '运行监控管理', path = 'monitoringManagement', component = 'cw/monitoringManagement/index', perms = 'cw:monitoringManagement:list';
-- 运行监控管理按钮权限
INSERT INTO `sys_menu` (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES
(405201, '运行监控查询', 4052, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:monitoringManagement:query', '', NULL, NULL, NOW(), NULL, NULL, '')
ON DUPLICATE KEY UPDATE
menu_name = '运行监控查询',
perms = 'cw:monitoringManagement:query',
icon = '',
create_time = NOW();
INSERT INTO `sys_menu` (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES
(405202, '运行监控锁定', 4052, 2, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:monitoringManagement:lock', '', NULL, NULL, NOW(), NULL, NULL, '')
ON DUPLICATE KEY UPDATE
menu_name = '运行监控锁定',
perms = 'cw:monitoringManagement:lock',
icon = '',
create_time = NOW();
INSERT INTO `sys_menu` (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES
(405203, '运行监控解锁', 4052, 3, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:monitoringManagement:unlock', '', NULL, NULL, NOW(), NULL, NULL, '')
ON DUPLICATE KEY UPDATE
menu_name = '运行监控解锁',
perms = 'cw:monitoringManagement:unlock',
icon = '',
create_time = NOW();
-- =============================================
-- 数据字典配置 - 锁定状态
-- =============================================
-- 锁定状态字典(如果已存在则跳过)
-- INSERT INTO `sys_dict_type` VALUES (NULL, '000000', '绩效自评锁定状态', 'cw_evaluation_locked', '0', NULL, NOW(), NULL, NULL, '项目绩效自评锁定状态字典');
-- INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 1, '未锁定', '0', 'cw_evaluation_locked', NULL, 'success', 'N', '0', NULL, NOW(), NULL, NULL, '');
-- INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 2, '已锁定', '1', 'cw_evaluation_locked', NULL, 'danger', 'N', '0', NULL, NOW(), NULL, NULL, '');
@@ -0,0 +1,253 @@
-- =============================================
-- 项目绩效自评模块建表脚本
-- 创建时间: 2025-11-03
-- 说明: 项目支出绩效自评和运行监控管理
-- 版本: v1.0
-- =============================================
-- =============================================
-- 表1: 项目绩效自评表 (主表)
-- =============================================
CREATE TABLE `cw_project_performance_self_evaluation` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '000000' COMMENT '租户ID',
-- 关联项目申报
`project_application_id` bigint(20) NOT NULL COMMENT '项目申报ID(关联cw_project_application表)',
`application_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '申报编号',
`project_name` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '项目名称',
-- 基本信息
`evaluation_year` int(11) NOT NULL COMMENT '自评年度',
`principal` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '项目负责人',
`contact_phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '联系电话',
`start_date` date DEFAULT NULL COMMENT '项目实施开始时间',
`end_date` date DEFAULT NULL COMMENT '项目实施结束时间',
-- 资金情况 (单位:万元)
`total_investment` decimal(15,2) DEFAULT NULL COMMENT '项目总投资',
`annual_budget` decimal(15,2) DEFAULT NULL COMMENT '本年度预算',
`actual_expenditure` decimal(15,2) DEFAULT NULL COMMENT '本年度实际支出',
`budget_execution_rate` decimal(5,2) DEFAULT NULL COMMENT '预算执行率(%)',
-- 绩效目标
`performance_goal` text COLLATE utf8mb4_unicode_ci COMMENT '年度绩效目标',
`target_completion` text COLLATE utf8mb4_unicode_ci COMMENT '目标完成情况说明',
-- 绩效自评 (决策、过程、产出、效益四个维度)
`decision_score` decimal(5,2) DEFAULT NULL COMMENT '项目决策得分',
`decision_content` text COLLATE utf8mb4_unicode_ci COMMENT '项目决策自评内容',
`process_score` decimal(5,2) DEFAULT NULL COMMENT '项目过程得分',
`process_content` text COLLATE utf8mb4_unicode_ci COMMENT '项目过程自评内容',
`output_score` decimal(5,2) DEFAULT NULL COMMENT '项目产出得分',
`output_content` text COLLATE utf8mb4_unicode_ci COMMENT '项目产出自评内容',
`benefit_score` decimal(5,2) DEFAULT NULL COMMENT '项目效益得分',
`benefit_content` text COLLATE utf8mb4_unicode_ci COMMENT '项目效益自评内容',
`total_score` decimal(5,2) DEFAULT NULL COMMENT '自评总分',
`self_evaluation_level` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '自评等级(优秀/良好/合格/不合格)',
-- 存在的问题和改进措施
`existing_problems` text COLLATE utf8mb4_unicode_ci COMMENT '存在的主要问题',
`improvement_measures` text COLLATE utf8mb4_unicode_ci COMMENT '改进措施和建议',
-- 状态管理
`evaluation_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'draft' COMMENT '自评状态(draft-草稿 submitted-已提交 approved-已审核)',
`submit_time` datetime DEFAULT NULL COMMENT '提交时间',
`submit_by` bigint(20) DEFAULT NULL COMMENT '提交人ID',
`submit_by_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '提交人姓名',
-- 审核信息
`approve_time` datetime DEFAULT NULL COMMENT '审核时间',
`approve_by` bigint(20) DEFAULT NULL COMMENT '审核人ID',
`approve_by_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核人姓名',
`approve_opinion` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核意见',
-- 系统字段
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '删除标志(0-正常 1-删除)',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_application_year` (`project_application_id`,`evaluation_year`,`tenant_id`) USING BTREE COMMENT '项目-年度-租户联合索引',
KEY `idx_application_no` (`application_no`) USING BTREE,
KEY `idx_evaluation_year` (`evaluation_year`) USING BTREE,
KEY `idx_status` (`evaluation_status`) USING BTREE,
KEY `idx_tenant` (`tenant_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目绩效自评表(允许同一项目同一年度创建多条自评)';
-- =============================================
-- 表2: 项目绩效自评指标明细表
-- =============================================
CREATE TABLE `cw_project_evaluation_indicator` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '000000' COMMENT '租户ID',
-- 关联自评表
`evaluation_id` bigint(20) NOT NULL COMMENT '绩效自评ID',
-- 指标分类
`indicator_category` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '指标类别(decision-决策/process-过程/output-产出/benefit-效益)',
`indicator_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '指标名称',
`indicator_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '指标类型(quantity-数量/quality-质量/time-时效/cost-成本)',
-- 指标内容
`target_value` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目标值',
`actual_value` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '实际完成值',
`completion_rate` decimal(5,2) DEFAULT NULL COMMENT '完成率(%)',
-- 评分
`standard_score` decimal(5,2) DEFAULT NULL COMMENT '指标分值',
`actual_score` decimal(5,2) DEFAULT NULL COMMENT '实际得分',
-- 说明
`completion_description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '完成情况说明',
`sort_order` int(11) DEFAULT '0' COMMENT '排序号',
-- 系统字段
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '删除标志',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_evaluation_id` (`evaluation_id`) USING BTREE,
KEY `idx_category` (`indicator_category`) USING BTREE,
KEY `idx_tenant` (`tenant_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目绩效自评指标明细表';
-- =============================================
-- 表3: 项目月度运行监控表
-- =============================================
CREATE TABLE `cw_project_monthly_monitoring` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '000000' COMMENT '租户ID',
-- 关联自评表
`evaluation_id` bigint(20) NOT NULL COMMENT '绩效自评ID',
`project_application_id` bigint(20) NOT NULL COMMENT '项目申报ID',
-- 月度信息
`monitoring_year` int(11) NOT NULL COMMENT '监控年度',
`monitoring_month` int(11) NOT NULL COMMENT '监控月份(1-12)',
-- 执行情况
`planned_progress` decimal(5,2) DEFAULT NULL COMMENT '计划进度(%)',
`actual_progress` decimal(5,2) DEFAULT NULL COMMENT '实际进度(%)',
`progress_variance` decimal(5,2) DEFAULT NULL COMMENT '进度偏差(%) = 实际进度 - 计划进度',
-- 资金情况 (单位:万元)
`monthly_budget` decimal(15,2) DEFAULT NULL COMMENT '本月预算',
`monthly_expenditure` decimal(15,2) DEFAULT NULL COMMENT '本月实际支出',
`cumulative_budget` decimal(15,2) DEFAULT NULL COMMENT '累计预算',
`cumulative_expenditure` decimal(15,2) DEFAULT NULL COMMENT '累计实际支出',
`fund_execution_rate` decimal(5,2) DEFAULT NULL COMMENT '资金执行率(%)',
-- 关键节点
`key_milestones` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本月完成的关键节点',
`milestone_completion` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '节点完成情况(completed-已完成/delayed-延期/notStarted-未开始)',
-- 问题与风险
`problems_encountered` text COLLATE utf8mb4_unicode_ci COMMENT '本月遇到的问题',
`risk_description` text COLLATE utf8mb4_unicode_ci COMMENT '风险描述',
`countermeasures` text COLLATE utf8mb4_unicode_ci COMMENT '应对措施',
-- 下月计划
`next_month_plan` text COLLATE utf8mb4_unicode_ci COMMENT '下月工作计划',
-- 填报信息
`fill_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'not_filled' COMMENT '填报状态(not_filled-未填报/filled-已填报)',
`fill_time` datetime DEFAULT NULL COMMENT '填报时间',
`fill_by` bigint(20) DEFAULT NULL COMMENT '填报人ID',
`fill_by_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '填报人姓名',
-- 系统字段
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '删除标志',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_evaluation_month` (`evaluation_id`,`monitoring_year`,`monitoring_month`,`tenant_id`,`deleted`) USING BTREE COMMENT '同一项目同一年同一月只能有一条记录',
KEY `idx_project_application` (`project_application_id`) USING BTREE,
KEY `idx_year_month` (`monitoring_year`,`monitoring_month`) USING BTREE,
KEY `idx_fill_status` (`fill_status`) USING BTREE,
KEY `idx_tenant` (`tenant_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目月度运行监控表';
-- =============================================
-- 菜单权限配置
-- =============================================
-- 注意:请根据实际的项目申报管理菜单ID调整父菜单ID
-- 假设项目申报管理的菜单ID为 4040(请根据实际情况调整)
-- 二级菜单:绩效自评管理
INSERT INTO `sys_menu` VALUES
(4050, '绩效自评管理', 4040, 5, 'evaluation', 'cw/evaluation/index', NULL, 1, 0, 'C', '0', '0', 'cw:evaluation:list', 'chart', NULL, NULL, NOW(), NULL, NULL, '项目绩效自评管理菜单');
-- 绩效自评管理按钮权限
INSERT INTO `sys_menu` VALUES
(405001, '绩效自评查询', 4050, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:evaluation:query', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405002, '绩效自评新增', 4050, 2, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:evaluation:add', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405003, '绩效自评修改', 4050, 3, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:evaluation:edit', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405004, '绩效自评删除', 4050, 4, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:evaluation:remove', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405005, '绩效自评提交', 4050, 5, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:evaluation:submit', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405006, '绩效自评审核', 4050, 6, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:evaluation:approve', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405007, '绩效自评导出', 4050, 7, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:evaluation:export', '#', NULL, NULL, NOW(), NULL, NULL, '');
-- 二级菜单:运行监控管理
INSERT INTO `sys_menu` VALUES
(4051, '运行监控管理', 4040, 6, 'monitoring', 'cw/monitoring/index', NULL, 1, 0, 'C', '0', '0', 'cw:monitoring:list', 'monitor', NULL, NULL, NOW(), NULL, NULL, '项目运行监控管理菜单');
-- 运行监控管理按钮权限
INSERT INTO `sys_menu` VALUES
(405101, '运行监控查询', 4051, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:monitoring:query', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405102, '运行监控填报', 4051, 2, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:monitoring:fill', '#', NULL, NULL, NOW(), NULL, NULL, ''),
(405103, '运行监控修改', 4051, 3, '', NULL, NULL, 1, 0, 'F', '0', '0', 'cw:monitoring:edit', '#', NULL, NULL, NOW(), NULL, NULL, '');
-- =============================================
-- 数据字典配置
-- =============================================
-- 自评状态
INSERT INTO `sys_dict_type` VALUES (NULL, '000000', '绩效自评状态', 'cw_evaluation_status', '0', NULL, NOW(), NULL, NULL, '项目绩效自评状态字典');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 1, '草稿', 'draft', 'cw_evaluation_status', NULL, 'info', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 2, '已提交', 'submitted', 'cw_evaluation_status', NULL, 'primary', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 3, '已审核', 'approved', 'cw_evaluation_status', NULL, 'success', 'N', '0', NULL, NOW(), NULL, NULL, '');
-- 自评等级
INSERT INTO `sys_dict_type` VALUES (NULL, '000000', '绩效自评等级', 'cw_evaluation_level', '0', NULL, NOW(), NULL, NULL, '项目绩效自评等级字典');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 1, '优秀', '优秀', 'cw_evaluation_level', NULL, 'success', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 2, '良好', '良好', 'cw_evaluation_level', NULL, 'primary', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 3, '合格', '合格', 'cw_evaluation_level', NULL, 'warning', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 4, '不合格', '不合格', 'cw_evaluation_level', NULL, 'danger', 'N', '0', NULL, NOW(), NULL, NULL, '');
-- 节点完成情况
INSERT INTO `sys_dict_type` VALUES (NULL, '000000', '节点完成情况', 'cw_milestone_completion', '0', NULL, NOW(), NULL, NULL, '项目节点完成情况字典');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 1, '已完成', 'completed', 'cw_milestone_completion', NULL, 'success', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 2, '延期', 'delayed', 'cw_milestone_completion', NULL, 'warning', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 3, '未开始', 'notStarted', 'cw_milestone_completion', NULL, 'info', 'N', '0', NULL, NOW(), NULL, NULL, '');
-- 月度填报状态
INSERT INTO `sys_dict_type` VALUES (NULL, '000000', '月度填报状态', 'cw_monthly_fill_status', '0', NULL, NOW(), NULL, NULL, '月度监控填报状态字典');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 1, '未填报', 'not_filled', 'cw_monthly_fill_status', NULL, 'info', 'N', '0', NULL, NOW(), NULL, NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, '000000', 2, '已填报', 'filled', 'cw_monthly_fill_status', NULL, 'success', 'N', '0', NULL, NOW(), NULL, NULL, '');
@@ -275,7 +275,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND u.status = '0'
AND u.del_flag = '0'
AND r.status = '0'
ORDER BY u.user_name
ORDER BY u.user_name ASC, u.create_time DESC
</select>
</mapper>
@@ -129,7 +129,7 @@
<foreach item="userId" collection="userIds" open="(" separator="," close=")">
#{userId}
</foreach>
order by u.dept_id, u.user_name
order by u.user_name asc, u.dept_id
</select>
</mapper>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.cw.mapper.CwPerformanceMonitoringMapper">
<resultMap type="org.dromara.cw.domain.CwPerformanceMonitoring" id="CwPerformanceMonitoringResult">
<result property="id" column="id"/>
<result property="evaluationId" column="evaluation_id"/>
<result property="projectApplicationId" column="project_application_id"/>
<result property="monitoringPeriod" column="monitoring_period"/>
<result property="fundUsage" column="fund_usage"/>
<result property="performanceTargetCompletion" column="performance_target_completion"/>
<result property="indicators" column="indicators"/>
<result property="existingProblems" column="existing_problems"/>
<result property="indicatorVarianceAnalysis" column="indicator_variance_analysis"/>
<result property="nextStepSuggestions" column="next_step_suggestions"/>
<result property="fillBy" column="fill_by"/>
<result property="fillByName" column="fill_by_name"/>
<result property="remark" column="remark"/>
<result property="createDept" column="create_dept"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
</mapper>
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.cw.mapper.CwProjectEvaluationIndicatorMapper">
<resultMap type="org.dromara.cw.domain.CwProjectEvaluationIndicator" id="CwProjectEvaluationIndicatorResult">
<result property="id" column="id"/>
<result property="evaluationId" column="evaluation_id"/>
<result property="indicatorCategory" column="indicator_category"/>
<result property="indicatorName" column="indicator_name"/>
<result property="indicatorType" column="indicator_type"/>
<result property="targetValue" column="target_value"/>
<result property="actualValue" column="actual_value"/>
<result property="completionRate" column="completion_rate"/>
<result property="standardScore" column="standard_score"/>
<result property="actualScore" column="actual_score"/>
<result property="completionDescription" column="completion_description"/>
<result property="sortOrder" column="sort_order"/>
<result property="remark" column="remark"/>
<result property="createDept" column="create_dept"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="deleted" column="deleted"/>
</resultMap>
<!-- 根据自评ID查询指标列表 -->
<select id="selectByEvaluationId" resultMap="CwProjectEvaluationIndicatorResult">
SELECT *
FROM cw_project_evaluation_indicator
WHERE evaluation_id = #{evaluationId}
AND deleted = 0
ORDER BY indicator_category, sort_order, create_time DESC
</select>
<!-- 根据自评ID和指标类别查询指标列表 -->
<select id="selectByEvaluationIdAndCategory" resultMap="CwProjectEvaluationIndicatorResult">
SELECT *
FROM cw_project_evaluation_indicator
WHERE evaluation_id = #{evaluationId}
AND indicator_category = #{indicatorCategory}
AND deleted = 0
ORDER BY sort_order, create_time DESC
</select>
<!-- 批量插入指标 -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO cw_project_evaluation_indicator
(id, tenant_id, evaluation_id, indicator_category, indicator_name, indicator_type,
target_value, actual_value, completion_rate, standard_score, actual_score,
completion_description, sort_order, remark, create_dept, create_by, create_time)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.id}, #{item.tenantId}, #{item.evaluationId}, #{item.indicatorCategory},
#{item.indicatorName}, #{item.indicatorType}, #{item.targetValue}, #{item.actualValue},
#{item.completionRate}, #{item.standardScore}, #{item.actualScore},
#{item.completionDescription}, #{item.sortOrder}, #{item.remark},
#{item.createDept}, #{item.createBy}, #{item.createTime})
</foreach>
</insert>
<!-- 根据自评ID删除指标 -->
<delete id="deleteByEvaluationId">
UPDATE cw_project_evaluation_indicator
SET deleted = 1
WHERE evaluation_id = #{evaluationId}
</delete>
</mapper>
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.cw.mapper.CwProjectMonthlyMonitoringMapper">
<resultMap type="org.dromara.cw.domain.CwProjectMonthlyMonitoring" id="CwProjectMonthlyMonitoringResult">
<result property="id" column="id"/>
<result property="evaluationId" column="evaluation_id"/>
<result property="projectApplicationId" column="project_application_id"/>
<result property="monitoringYear" column="monitoring_year"/>
<result property="monitoringMonth" column="monitoring_month"/>
<result property="plannedProgress" column="planned_progress"/>
<result property="actualProgress" column="actual_progress"/>
<result property="progressVariance" column="progress_variance"/>
<result property="monthlyBudget" column="monthly_budget"/>
<result property="monthlyExpenditure" column="monthly_expenditure"/>
<result property="cumulativeBudget" column="cumulative_budget"/>
<result property="cumulativeExpenditure" column="cumulative_expenditure"/>
<result property="fundExecutionRate" column="fund_execution_rate"/>
<result property="keyMilestones" column="key_milestones"/>
<result property="milestoneCompletion" column="milestone_completion"/>
<result property="problemsEncountered" column="problems_encountered"/>
<result property="riskDescription" column="risk_description"/>
<result property="countermeasures" column="countermeasures"/>
<result property="nextMonthPlan" column="next_month_plan"/>
<result property="fillStatus" column="fill_status"/>
<result property="fillTime" column="fill_time"/>
<result property="fillBy" column="fill_by"/>
<result property="fillByName" column="fill_by_name"/>
<result property="remark" column="remark"/>
<result property="createDept" column="create_dept"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="deleted" column="deleted"/>
</resultMap>
<!-- 根据自评ID查询月度监控列表 -->
<select id="selectByEvaluationId" resultMap="CwProjectMonthlyMonitoringResult">
SELECT *
FROM cw_project_monthly_monitoring
WHERE evaluation_id = #{evaluationId}
AND deleted = 0
ORDER BY monitoring_year, monitoring_month, create_time DESC
</select>
<!-- 根据自评ID和年度查询月度监控列表 -->
<select id="selectByEvaluationIdAndYear" resultMap="CwProjectMonthlyMonitoringResult">
SELECT *
FROM cw_project_monthly_monitoring
WHERE evaluation_id = #{evaluationId}
AND monitoring_year = #{monitoringYear}
AND deleted = 0
ORDER BY monitoring_month, create_time DESC
</select>
<!-- 根据自评ID、年度和月份查询月度监控 -->
<select id="selectByEvaluationIdAndMonth" resultMap="CwProjectMonthlyMonitoringResult">
SELECT *
FROM cw_project_monthly_monitoring
WHERE evaluation_id = #{evaluationId}
AND monitoring_year = #{monitoringYear}
AND monitoring_month = #{monitoringMonth}
AND deleted = 0
LIMIT 1
</select>
<!-- 批量插入月度监控数据 -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO cw_project_monthly_monitoring
(id, tenant_id, evaluation_id, project_application_id, monitoring_year, monitoring_month,
planned_progress, actual_progress, progress_variance, monthly_budget, monthly_expenditure,
cumulative_budget, cumulative_expenditure, fund_execution_rate, key_milestones,
milestone_completion, problems_encountered, risk_description, countermeasures,
next_month_plan, fill_status, fill_time, fill_by, fill_by_name,
remark, create_dept, create_by, create_time)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.id}, #{item.tenantId}, #{item.evaluationId}, #{item.projectApplicationId},
#{item.monitoringYear}, #{item.monitoringMonth}, #{item.plannedProgress},
#{item.actualProgress}, #{item.progressVariance}, #{item.monthlyBudget},
#{item.monthlyExpenditure}, #{item.cumulativeBudget}, #{item.cumulativeExpenditure},
#{item.fundExecutionRate}, #{item.keyMilestones}, #{item.milestoneCompletion},
#{item.problemsEncountered}, #{item.riskDescription}, #{item.countermeasures},
#{item.nextMonthPlan}, #{item.fillStatus}, #{item.fillTime}, #{item.fillBy},
#{item.fillByName}, #{item.remark}, #{item.createDept}, #{item.createBy},
#{item.createTime})
</foreach>
</insert>
<!-- 根据自评ID删除月度监控数据 -->
<delete id="deleteByEvaluationId">
UPDATE cw_project_monthly_monitoring
SET deleted = 1
WHERE evaluation_id = #{evaluationId}
</delete>
<!-- 计算年度累计进度 -->
<select id="calculateYearlyProgress" resultType="java.math.BigDecimal">
SELECT MAX(actual_progress)
FROM cw_project_monthly_monitoring
WHERE evaluation_id = #{evaluationId}
AND monitoring_year = #{monitoringYear}
AND deleted = 0
</select>
<!-- 计算年度累计支出 -->
<select id="calculateYearlyExpenditure" resultType="java.math.BigDecimal">
SELECT MAX(cumulative_expenditure)
FROM cw_project_monthly_monitoring
WHERE evaluation_id = #{evaluationId}
AND monitoring_year = #{monitoringYear}
AND deleted = 0
</select>
</mapper>
@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.cw.mapper.CwProjectPerformanceSelfEvaluationMapper">
<resultMap type="org.dromara.cw.domain.CwProjectPerformanceSelfEvaluation" id="CwProjectPerformanceSelfEvaluationResult">
<result property="id" column="id"/>
<result property="projectApplicationId" column="project_application_id"/>
<result property="applicationNo" column="application_no"/>
<result property="projectName" column="project_name"/>
<result property="supervisoryDept" column="supervisory_dept"/>
<result property="implementUnit" column="implement_unit"/>
<result property="evaluationYear" column="evaluation_year"/>
<result property="initialBudget" column="initial_budget"/>
<result property="principal" column="principal"/>
<result property="contactPhone" column="contact_phone"/>
<result property="startDate" column="start_date"/>
<result property="endDate" column="end_date"/>
<result property="totalInvestment" column="total_investment"/>
<result property="annualBudget" column="annual_budget"/>
<result property="actualExpenditure" column="actual_expenditure"/>
<result property="budgetExecutionRate" column="budget_execution_rate"/>
<result property="fundValue" column="fund_value"/>
<result property="fundExecutionRate" column="fund_execution_rate"/>
<result property="fundScore" column="fund_score"/>
<result property="currentYearBudgetInitial" column="current_year_budget_initial"/>
<result property="currentYearBudgetAnnual" column="current_year_budget_annual"/>
<result property="currentYearBudgetActual" column="current_year_budget_actual"/>
<result property="currentYearBudgetValue" column="current_year_budget_value"/>
<result property="currentYearBudgetExecutionRate" column="current_year_budget_execution_rate"/>
<result property="currentYearBudgetScore" column="current_year_budget_score"/>
<result property="lastYearCarryoverInitial" column="last_year_carryover_initial"/>
<result property="lastYearCarryoverAnnual" column="last_year_carryover_annual"/>
<result property="lastYearCarryoverActual" column="last_year_carryover_actual"/>
<result property="lastYearCarryoverValue" column="last_year_carryover_value"/>
<result property="lastYearCarryoverExecutionRate" column="last_year_carryover_execution_rate"/>
<result property="lastYearCarryoverScore" column="last_year_carryover_score"/>
<result property="otherFundsInitial" column="other_funds_initial"/>
<result property="otherFundsAnnual" column="other_funds_annual"/>
<result property="otherFundsActual" column="other_funds_actual"/>
<result property="otherFundsValue" column="other_funds_value"/>
<result property="otherFundsExecutionRate" column="other_funds_execution_rate"/>
<result property="otherFundsScore" column="other_funds_score"/>
<result property="performanceGoal" column="performance_goal"/>
<result property="targetCompletion" column="target_completion"/>
<result property="quantityIndicator" column="quantity_indicator"/>
<result property="quantityTarget" column="quantity_target"/>
<result property="quantityActual" column="quantity_actual"/>
<result property="quantityValue" column="quantity_value"/>
<result property="quantityScore" column="quantity_score"/>
<result property="quantityDeviation" column="quantity_deviation"/>
<result property="qualityIndicator" column="quality_indicator"/>
<result property="qualityTarget" column="quality_target"/>
<result property="qualityActual" column="quality_actual"/>
<result property="qualityValue" column="quality_value"/>
<result property="qualityScore" column="quality_score"/>
<result property="qualityDeviation" column="quality_deviation"/>
<result property="timelinessIndicator" column="timeliness_indicator"/>
<result property="timelinessTarget" column="timeliness_target"/>
<result property="timelinessActual" column="timeliness_actual"/>
<result property="timelinessValue" column="timeliness_value"/>
<result property="timelinessScore" column="timeliness_score"/>
<result property="timelinessDeviation" column="timeliness_deviation"/>
<result property="costIndicator" column="cost_indicator"/>
<result property="costTarget" column="cost_target"/>
<result property="costActual" column="cost_actual"/>
<result property="costValue" column="cost_value"/>
<result property="costScore" column="cost_score"/>
<result property="costDeviation" column="cost_deviation"/>
<result property="economicIndicator" column="economic_indicator"/>
<result property="economicTarget" column="economic_target"/>
<result property="economicActual" column="economic_actual"/>
<result property="economicValue" column="economic_value"/>
<result property="economicScore" column="economic_score"/>
<result property="economicDeviation" column="economic_deviation"/>
<result property="socialIndicator" column="social_indicator"/>
<result property="socialTarget" column="social_target"/>
<result property="socialActual" column="social_actual"/>
<result property="socialValue" column="social_value"/>
<result property="socialScore" column="social_score"/>
<result property="socialDeviation" column="social_deviation"/>
<result property="ecologicalIndicator" column="ecological_indicator"/>
<result property="ecologicalTarget" column="ecological_target"/>
<result property="ecologicalActual" column="ecological_actual"/>
<result property="ecologicalValue" column="ecological_value"/>
<result property="ecologicalScore" column="ecological_score"/>
<result property="ecologicalDeviation" column="ecological_deviation"/>
<result property="satisfactionIndicator" column="satisfaction_indicator"/>
<result property="satisfactionTarget" column="satisfaction_target"/>
<result property="satisfactionActual" column="satisfaction_actual"/>
<result property="satisfactionValue" column="satisfaction_value"/>
<result property="satisfactionScore" column="satisfaction_score"/>
<result property="satisfactionDeviation" column="satisfaction_deviation"/>
<result property="decisionScore" column="decision_score"/>
<result property="decisionContent" column="decision_content"/>
<result property="processScore" column="process_score"/>
<result property="processContent" column="process_content"/>
<result property="outputScore" column="output_score"/>
<result property="outputContent" column="output_content"/>
<result property="benefitScore" column="benefit_score"/>
<result property="benefitContent" column="benefit_content"/>
<result property="totalScore" column="total_score"/>
<result property="selfEvaluationLevel" column="self_evaluation_level"/>
<result property="existingProblems" column="existing_problems"/>
<result property="improvementMeasures" column="improvement_measures"/>
<result property="evaluationStatus" column="evaluation_status"/>
<result property="allowEditAfterSubmit" column="allow_edit_after_submit"/>
<result property="submitTime" column="submit_time"/>
<result property="submitBy" column="submit_by"/>
<result property="submitByName" column="submit_by_name"/>
<result property="approveTime" column="approve_time"/>
<result property="approveBy" column="approve_by"/>
<result property="approveByName" column="approve_by_name"/>
<result property="approveOpinion" column="approve_opinion"/>
<result property="remark" column="remark"/>
<result property="createDept" column="create_dept"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="deleted" column="deleted"/>
</resultMap>
<!-- 根据项目申报ID和年度查询自评 -->
<select id="selectByProjectAndYear" resultMap="CwProjectPerformanceSelfEvaluationResult">
SELECT *
FROM cw_project_performance_self_evaluation
WHERE project_application_id = #{projectApplicationId}
AND evaluation_year = #{evaluationYear}
AND deleted = 0
LIMIT 1
</select>
<!-- 查询我的自评列表 -->
<select id="selectMyEvaluations" resultMap="CwProjectPerformanceSelfEvaluationResult">
SELECT *
FROM cw_project_performance_self_evaluation
WHERE create_by = #{userId}
AND deleted = 0
ORDER BY create_time DESC
</select>
<!-- 查询待审核的自评列表 -->
<select id="selectPendingApprovals" resultMap="CwProjectPerformanceSelfEvaluationResult">
SELECT *
FROM cw_project_performance_self_evaluation
WHERE evaluation_status = 'submitted'
AND deleted = 0
ORDER BY submit_time ASC, create_time DESC
</select>
<!-- 统计自评数量 -->
<select id="countEvaluations" resultType="java.lang.Long">
SELECT COUNT(*)
FROM cw_project_performance_self_evaluation
WHERE deleted = 0
<if test="evaluationYear != null">
AND evaluation_year = #{evaluationYear}
</if>
<if test="evaluationStatus != null and evaluationStatus != ''">
AND evaluation_status = #{evaluationStatus}
</if>
</select>
<!-- 计算平均分 -->
<select id="calculateAverageScore" resultType="java.math.BigDecimal">
SELECT AVG(total_score)
FROM cw_project_performance_self_evaluation
WHERE deleted = 0
AND total_score IS NOT NULL
<if test="evaluationYear != null">
AND evaluation_year = #{evaluationYear}
</if>
</select>
<!-- 按部门统计自评情况 -->
<select id="statisticsByDept" resultType="java.util.HashMap">
SELECT
create_dept AS deptId,
COUNT(*) AS count,
AVG(total_score) AS avgScore
FROM cw_project_performance_self_evaluation
WHERE deleted = 0
<if test="evaluationYear != null">
AND evaluation_year = #{evaluationYear}
</if>
GROUP BY create_dept
</select>
<!-- 按等级统计自评数量 -->
<select id="statisticsByLevel" resultType="java.util.HashMap">
SELECT
self_evaluation_level AS level,
COUNT(*) AS count
FROM cw_project_performance_self_evaluation
WHERE deleted = 0
AND self_evaluation_level IS NOT NULL
<if test="evaluationYear != null">
AND evaluation_year = #{evaluationYear}
</if>
GROUP BY self_evaluation_level
</select>
</mapper>
@@ -75,6 +75,7 @@ public class HqSatQuestionServiceImpl implements IHqSatQuestionService {
LambdaQueryWrapper<HqSatQuestion> lqw = Wrappers.lambdaQuery();
// 处理排序参数
boolean sortedByCreateTime = false;
if (params != null && params.containsKey("orderByColumn") && params.containsKey("isAsc")) {
String orderByColumn = (String) params.get("orderByColumn");
String isAsc = (String) params.get("isAsc");
@@ -85,6 +86,7 @@ public class HqSatQuestionServiceImpl implements IHqSatQuestionService {
} else {
lqw.orderByAsc(HqSatQuestion::getCreateTime);
}
sortedByCreateTime = true;
} else if ("updateTime".equals(orderByColumn)) {
if ("desc".equals(isAsc)) {
lqw.orderByDesc(HqSatQuestion::getUpdateTime);
@@ -100,10 +102,15 @@ public class HqSatQuestionServiceImpl implements IHqSatQuestionService {
} else {
// 默认按创建时间倒序
lqw.orderByDesc(HqSatQuestion::getCreateTime);
sortedByCreateTime = true;
}
} else {
// 默认按创建时间倒序
lqw.orderByDesc(HqSatQuestion::getCreateTime);
sortedByCreateTime = true;
}
if (!sortedByCreateTime) {
lqw.orderByDesc(HqSatQuestion::getCreateTime);
}
// 添加ID查询条件
@@ -1097,7 +1097,7 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
}
sql.append("GROUP BY u.user_id, rec.id ");
sql.append("ORDER BY CASE WHEN rec.submit_time IS NOT NULL THEN 0 ELSE 1 END, rec.submit_time DESC, u.user_id ");
sql.append("ORDER BY CASE WHEN rec.submit_time IS NOT NULL THEN 0 ELSE 1 END, rec.submit_time DESC, u.user_name ASC, u.user_id ");
// 查询总数
StringBuilder countSql = new StringBuilder();
@@ -18,7 +18,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
WHERE r.role_key = 'maintenance_worker'
AND u.status = '0'
AND u.del_flag = '0'
ORDER BY u.user_id
ORDER BY u.user_name ASC, u.create_time DESC
</select>
</mapper>
@@ -527,7 +527,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp
WHERE cm.class_teacher_id = ?
AND cm.deleted = 0
AND cm.class_status = '1'
ORDER BY cm.grade DESC, cm.class_name ASC
ORDER BY cm.grade DESC, cm.class_name ASC, cm.create_time DESC
""";
// 执行查询
@@ -713,7 +713,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp
AND sm.deleted = 0
AND sm.student_status = '1'
AND (cm.deleted IS NULL OR cm.deleted = 0)
ORDER BY sm.student_no ASC
ORDER BY sm.student_no DESC, sm.create_time DESC
""";
List<Map<String, Object>> studentList = jdbcTemplate.queryForList(sql, classId);
@@ -246,7 +246,7 @@
and s.deleted = 0
and s.student_status = '1'
and (c.deleted is null or c.deleted = 0)
order by s.student_no asc
order by s.student_no desc
</select>
</mapper>
+7
View File
@@ -116,6 +116,13 @@
<artifactId>itextpdf</artifactId>
<version>5.5.13.3</version>
</dependency>
<!-- iText亚洲字体支持包 - 支持中文 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,116 @@
package org.dromara.rs.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.rs.domain.bo.RsOverseasApplicationAttachmentBo;
import org.dromara.rs.domain.vo.RsOverseasApplicationAttachmentVo;
import org.dromara.rs.service.IRsOverseasApplicationAttachmentService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* 出国申请附件Controller
*
* @author weizzzz
* @date 2025-10-10
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/rs/overseas/attachment")
public class RsOverseasApplicationAttachmentController extends BaseController {
private final IRsOverseasApplicationAttachmentService attachmentService;
/**
* 查询出国申请的附件列表
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/list/{applicationId}")
public R<List<RsOverseasApplicationAttachmentVo>> list(
@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) {
List<RsOverseasApplicationAttachmentVo> list = attachmentService.selectAttachmentList(applicationId);
return R.ok(list);
}
/**
* 查询附件详情
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/{attachmentId}")
public R<RsOverseasApplicationAttachmentVo> getInfo(
@NotNull(message = "附件ID不能为空") @PathVariable Long attachmentId) {
RsOverseasApplicationAttachmentVo vo = attachmentService.selectAttachmentById(attachmentId);
return R.ok(vo);
}
/**
* 上传附件
*
* @param applicationId 申请ID
* @param title 附件标题
* @param content 附件内容说明
* @param file 文件
*/
@SaCheckPermission("rs:overseas:edit")
@Log(title = "上传出国申请附件", businessType = BusinessType.INSERT)
@PostMapping("/upload")
public R<RsOverseasApplicationAttachmentVo> upload(
@NotNull(message = "申请ID不能为空") @RequestParam Long applicationId,
@RequestParam String title,
@RequestParam(required = false) String content,
@RequestParam("file") MultipartFile file) {
try {
RsOverseasApplicationAttachmentVo vo = attachmentService.uploadAttachment(
applicationId, title, content, file);
return R.ok("上传成功", vo);
} catch (Exception e) {
log.error("上传附件失败", e);
return R.fail("上传失败:" + e.getMessage());
}
}
/**
* 更新附件信息(标题和内容)
*/
@SaCheckPermission("rs:overseas:edit")
@Log(title = "更新出国申请附件", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> update(@Validated @RequestBody RsOverseasApplicationAttachmentBo bo) {
boolean success = attachmentService.updateAttachment(bo);
return toAjax(success);
}
/**
* 删除附件
*/
@SaCheckPermission("rs:overseas:remove")
@Log(title = "删除出国申请附件", businessType = BusinessType.DELETE)
@DeleteMapping("/{attachmentId}")
public R<Void> remove(@NotNull(message = "附件ID不能为空") @PathVariable Long attachmentId) {
boolean success = attachmentService.deleteAttachment(attachmentId);
return toAjax(success);
}
/**
* 批量删除附件
*/
@SaCheckPermission("rs:overseas:remove")
@Log(title = "批量删除出国申请附件", businessType = BusinessType.DELETE)
@DeleteMapping("/batch")
public R<Void> removeBatch(@RequestBody Long[] attachmentIds) {
boolean success = attachmentService.deleteAttachments(attachmentIds);
return toAjax(success);
}
}
@@ -26,15 +26,28 @@ import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.satoken.utils.LoginHelper;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import lombok.extern.slf4j.Slf4j;
import org.dromara.rs.domain.bo.RsOverseasApplicationBo;
import org.dromara.rs.domain.bo.GenerateRegistrationFormBo;
import org.dromara.rs.domain.vo.RsOverseasApplicationVo;
import org.dromara.rs.domain.vo.RsOverseasApprovalVo;
import org.dromara.rs.domain.vo.RegistrationFormVO;
import org.dromara.rs.domain.query.RsOverseasApplicationQuery;
import org.dromara.rs.service.IRsOverseasApplicationService;
import org.dromara.rs.service.IRsOverseasApprovalService;
import org.dromara.rs.service.IRegistrationFormService;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import org.springframework.web.multipart.MultipartFile;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.oss.entity.UploadResult;
import java.io.IOException;
import java.text.SimpleDateFormat;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.domain.vo.SysOssVo;
/**
* 出国申请Controller
@@ -42,6 +55,7 @@ import java.util.HashMap;
* @author weizzzz
* @date 2025-07-28
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/rs/overseas/application")
@@ -53,6 +67,12 @@ public class RsOverseasApplicationController extends BaseController {
@Autowired
private IRsOverseasApprovalService overseasApprovalService;
@Autowired
private IRegistrationFormService registrationFormService;
@Autowired
private ISysOssService ossService;
/**
* 查询出国申请列表
*/
@@ -76,47 +96,27 @@ public class RsOverseasApplicationController extends BaseController {
}
/**
* 查询待审批的申请列表(有审批权限的用户可以看到所有待审批申请)
* 查询当前用户的待审批申请列表(五级审批模式)
*/
@SaCheckPermission("rs:overseas:approve")
@SaCheckPermission("rs:overseasapproval:list")
@GetMapping("/pending/list")
public TableDataInfo<RsOverseasApplicationVo> pendingList(RsOverseasApplicationQuery query) {
// 不限制currentApprover,让有审批权限的用户看到所有待审批申请
// String currentUser = LoginHelper.getUsername();
// query.setCurrentApprover(currentUser);
query.setStatus("0"); // 只查询待审核状态
query.setTenantId(LoginHelper.getTenantId()); // 设置租户ID
return applicationService.queryPageList(query);
public R<List<RsOverseasApplicationVo>> pendingList() {
List<RsOverseasApplicationVo> list = applicationService.queryPendingByCurrentUser();
return R.ok(list);
}
/**
* 临时测试API - 查询待审批的申请列表(调试用)
*/
@SaCheckPermission("rs:overseas:approve")
@GetMapping("/pending/debug")
public R<Object> pendingDebug(RsOverseasApplicationQuery query) {
// 获取当前用户信息
String currentUser = LoginHelper.getUsername();
Long userId = LoginHelper.getUserId();
String tenantId = LoginHelper.getTenantId();
// 设置查询条件
query.setStatus("0"); // 只查询待审核状态
query.setTenantId(tenantId); // 设置租户ID
// 执行查询
TableDataInfo<RsOverseasApplicationVo> result = applicationService.queryPageList(query);
// 返回调试信息
Map<String, Object> debugInfo = new HashMap<>();
debugInfo.put("currentUser", currentUser);
debugInfo.put("userId", userId);
debugInfo.put("tenantId", tenantId);
debugInfo.put("queryParams", query);
debugInfo.put("result", result);
return R.ok(debugInfo);
}
// ========================================
// 五级审批模式说明
//
// 当前实现:自定义五级审批流程
// 1. 部门负责人审批(根据申请人部门动态分配)
// 2. 人事处审批(角色:hr_dept_overseas_approver)
// 3. 纪委审批(角色:discipline_overseas_approver)
// 4. 学院分管领导审批(角色:college_leader_overseas_approver)
// 5. 学院党委书记审批(角色:party_secretary_overseas_approver)
//
// 审批权限控制:每个审批人只能看到分配给自己的待审批申请
// ========================================
/**
* 根据状态查询申请列表
@@ -139,40 +139,85 @@ public class RsOverseasApplicationController extends BaseController {
ExcelUtil.exportExcel(list, "出国申请数据", RsOverseasApplicationVo.class, response);
}
/**
* 下载登记备案表模板
*/
@GetMapping("/downloadRegistrationTemplate")
public void downloadRegistrationTemplate(HttpServletResponse response) {
registrationFormService.downloadRegistrationTemplate(response);
}
/**
* 获取出国申请详细信息
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/{applicationId}")
@GetMapping("/detail/{applicationId}")
public R<RsOverseasApplicationVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long applicationId) {
return R.ok(applicationService.queryById(applicationId));
}
/**
* 获取申请人的部门负责人ID
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/dept-approver/{applicationId}")
public R<Long> getDepartmentApproverId(@PathVariable Long applicationId) {
RsOverseasApplicationVo application = applicationService.queryById(applicationId);
if (application == null) {
return R.fail("申请不存在");
}
Long deptLeaderId = applicationService.getDepartmentApproverId(application.getApplicantId());
if (deptLeaderId == null) {
return R.fail("未找到部门负责人,请联系管理员配置");
}
return R.ok(deptLeaderId);
}
/**
* 新增出国申请
*/
@SaCheckPermission("rs:overseas:add")
@Log(title = "出国申请", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated(AddGroup.class) @RequestBody RsOverseasApplicationBo bo) {
public R<Map<String, Object>> add(@Validated(AddGroup.class) @RequestBody RsOverseasApplicationBo bo) {
// 🔍 调试日志:检查接收到的关键字段
System.out.println("========== Controller 接收到的数据 ==========");
System.out.println("emergencyContact: " + bo.getEmergencyContact());
System.out.println("destination: " + bo.getDestination());
System.out.println("idNumber: " + bo.getIdNumber());
// unitContact 字段已删除
// System.out.println("unitContact: " + bo.getUnitContact());
System.out.println("===============================================");
// 自动设置申请人信息
bo.setApplicantId(LoginHelper.getUserId());
bo.setApplicantName(LoginHelper.getUsername());
// ✅ 不覆盖用户填写的姓名,保留用户在表单中填写的申请证件姓名
// 自动设置部门信息
bo.setDepartment(LoginHelper.getDeptName());
// 🔴 关键修复:设置租户ID,否则列表查询时会因租户ID过滤而看不到数据
bo.setTenantId(LoginHelper.getTenantId());
// 自动计算出国天数
if (bo.getStartDate() != null && bo.getEndDate() != null) {
long diffTime = Math.abs(bo.getEndDate().getTime() - bo.getStartDate().getTime());
int diffDays = (int) Math.ceil(diffTime / (1000.0 * 60 * 60 * 24)) + 1;
bo.setDuration(diffDays);
}
// 状态由Service层控制,新增时为草稿状态
bo.setApplyTime(new Date());
return toAjax(applicationService.insertByBo(bo));
boolean result = applicationService.insertByBo(bo);
if (result) {
// 🔴 返回新创建的applicationId,供前端上传文件时使用
Map<String, Object> data = new HashMap<>();
data.put("applicationId", bo.getApplicationId());
return R.ok("操作成功", data);
} else {
return R.fail("新增失败");
}
}
/**
@@ -192,6 +237,28 @@ public class RsOverseasApplicationController extends BaseController {
@Log(title = "提交出国申请", businessType = BusinessType.UPDATE)
@PutMapping("/submit/{applicationId}")
public R<Void> submit(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) {
// 获取申请详情进行校验
RsOverseasApplicationVo application = applicationService.queryById(applicationId);
if (application == null) {
return R.fail("申请不存在");
}
// 1. 校验出国登记前置条件(已禁用 - 允许直接提交申请)
// java.util.Map<String, Object> registrationValidation = applicationService
// .validateRegistrationPrerequisite(application.getApplicantId());
// Boolean registrationValid = (Boolean) registrationValidation.get("isValid");
// if (!registrationValid) {
// String message = (String) registrationValidation.get("message");
// return R.fail(message);
// }
// 2. 校验是否上传了盖章文件(可选 - 根据业务需要决定是否保留)
// if (application.getStampedFileUploadStatus() == null ||
// !"1".equals(application.getStampedFileUploadStatus())) {
// return R.fail("请先下载登记备案附件填写盖章后上传,才能提交申请!");
// }
// 所有校验通过,执行提交操作
return toAjax(applicationService.submitApplication(applicationId));
}
@@ -202,10 +269,31 @@ public class RsOverseasApplicationController extends BaseController {
@Log(title = "重新提交出国申请", businessType = BusinessType.UPDATE)
@PutMapping("/resubmit/{applicationId}")
public R<Void> resubmit(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId,
@Validated @RequestBody RsOverseasApplicationBo bo) {
@Validated @RequestBody RsOverseasApplicationBo bo) {
return toAjax(applicationService.resubmitApplication(applicationId, bo));
}
/**
* 校验出国申请前置条件
* 用于前端在填写表单时实时提示用户
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/validatePrerequisites")
public R<java.util.Map<String, Object>> validatePrerequisites(
@RequestParam(required = false) String politicalStatus,
@RequestParam(required = false) Integer duration) {
Long applicantId = LoginHelper.getUserId();
java.util.Map<String, Object> result = new java.util.HashMap<>();
// 1. 校验出国登记前置条件
java.util.Map<String, Object> registrationValidation = applicationService
.validateRegistrationPrerequisite(applicantId);
result.put("registrationValidation", registrationValidation);
return R.ok(result);
}
/**
* 取消出国申请
*/
@@ -213,22 +301,36 @@ public class RsOverseasApplicationController extends BaseController {
@Log(title = "取消出国申请", businessType = BusinessType.UPDATE)
@PutMapping("/cancel/{applicationId}")
public R<Void> cancel(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId,
@RequestParam(required = false) String reason) {
@RequestParam(required = false) String reason) {
return toAjax(applicationService.cancelApplication(applicationId, reason));
}
/**
* 审批出国申请
* 审批出国申请(支持标准工作流模式)
* 🔴 支持五级审批权限:满足任一级别权限即可访问接口,具体级别在Service层验证
*/
@SaCheckPermission("rs:overseas:approve")
@SaCheckPermission(value = {
"rs:overseas:approve", // 通用审批权限(超级管理员/高级审批者)
"rs:overseas:approve1", // 第1级:部门负责人
"rs:overseas:approve2", // 第2级:人事处
"rs:overseas:approve3", // 第3级:纪委
"rs:overseas:approve4", // 第4级:学院分管领导
"rs:overseas:approve5" // 第5级:学院党委书记
}, mode = SaMode.OR) // 🔴 关键:OR模式,满足任一权限即可通过
@Log(title = "审批出国申请", businessType = BusinessType.UPDATE)
@PostMapping("/approve")
public R<Void> approve(@RequestBody ApprovalRequest request) {
Long approverId = LoginHelper.getUserId();
String approverName = LoginHelper.getUsername();
// 🔴 标准工作流模式:支持任务ID参数
// 如果传入了taskId,将通过工作流引擎进行审批
// 如果没有taskId,则使用原有的业务审批逻辑(向后兼容)
return toAjax(applicationService.approveApplication(
request.getApplicationId(), approverId, approverName,
request.getApprovalStatus(), request.getApprovalOpinion()));
request.getApplicationId(), approverId, approverName,
request.getApprovalStatus(), request.getApprovalOpinion(),
request.getApprovalSignature(), request.getTaskId()));
}
/**
@@ -247,7 +349,8 @@ public class RsOverseasApplicationController extends BaseController {
@SaCheckPermission("rs:overseas:query")
@GetMapping("/approval/history/{applicationId}")
public R<List<RsOverseasApprovalVo>> getApprovalHistory(@PathVariable("applicationId") Long applicationId) {
List<RsOverseasApprovalVo> approvalList = overseasApprovalService.selectApprovalHistoryByApplicationId(applicationId);
List<RsOverseasApprovalVo> approvalList = overseasApprovalService
.selectApprovalHistoryByApplicationId(applicationId);
return R.ok(approvalList);
}
@@ -267,12 +370,14 @@ public class RsOverseasApplicationController extends BaseController {
}
/**
* 审批请求对象
* 审批请求对象(支持标准工作流模式)
*/
public static class ApprovalRequest {
private Long applicationId;
private String approvalStatus;
private String approvalOpinion;
private String approvalSignature; // 审批签字(base64图片)
private String taskId; // 🔴 新增:工作流任务ID,支持标准工作流模式
public Long getApplicationId() {
return applicationId;
@@ -297,5 +402,247 @@ public class RsOverseasApplicationController extends BaseController {
public void setApprovalOpinion(String approvalOpinion) {
this.approvalOpinion = approvalOpinion;
}
public String getApprovalSignature() {
return approvalSignature;
}
public void setApprovalSignature(String approvalSignature) {
this.approvalSignature = approvalSignature;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
}
}
// ==================== PDF生成相关接口 ====================
/**
* 生成出国申请登记表PDF
*/
@SaCheckPermission("rs:overseas:generate")
@PostMapping("/generateRegistrationForm")
@Log(title = "生成出国申请登记表", businessType = BusinessType.OTHER)
public R<RegistrationFormVO> generateRegistrationForm(@RequestBody @Validated GenerateRegistrationFormBo bo) {
RegistrationFormVO result = registrationFormService.generateRegistrationFormPdf(bo);
return R.ok(result);
}
/**
* 下载生成的PDF文档
*/
@SaCheckPermission("rs:overseas:download")
@GetMapping("/downloadPdf/{documentId}")
public void downloadPdf(@PathVariable String documentId, HttpServletResponse response) {
registrationFormService.downloadPdf(documentId, response);
}
/**
* 预览生成的PDF文档
*/
@SaCheckPermission("rs:overseas:preview")
@GetMapping("/previewPdf/{documentId}")
public void previewPdf(@PathVariable String documentId, HttpServletResponse response) {
registrationFormService.downloadPdf(documentId, response);
}
// ==================== 动态审批人获取API ====================
/**
* 根据申请人ID获取部门审批人ID
*
* @param applicantId 申请人ID
*/
@SaCheckPermission("rs:overseas:list")
@GetMapping("/getDepartmentApproverByApplicant/{applicantId}")
public R<Long> getDepartmentApproverByApplicant(@PathVariable Long applicantId) {
Long departmentApproverId = applicationService.getDepartmentApproverId(applicantId);
return R.ok(departmentApproverId);
}
/**
* 根据申请人ID获取人事处审批人列表
*
* @param applicantId 申请人ID
*/
@SaCheckPermission("rs:overseas:list")
@GetMapping("/getHrApprovers/{applicantId}")
public R<List<Long>> getHrApprovers(@PathVariable Long applicantId) {
List<Long> hrApprovers = applicationService.getHrApprovers(applicantId);
return R.ok(hrApprovers);
}
// ==================== 盖章文件上传接口 ====================
/**
* 上传登记备案表盖章文件
*/
@SaCheckPermission("rs:overseas:add")
@PostMapping("/uploadStampedFile")
@Log(title = "上传盖章文件", businessType = BusinessType.OTHER)
public R<Map<String, Object>> uploadStampedFile(@RequestParam("file") MultipartFile file,
@RequestParam("applicationId") Long applicationId) {
try {
// 验证文件格式
String contentType = file.getContentType();
if (!isValidFileType(contentType)) {
return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件");
}
// 验证文件大小(10MB)
long maxSize = 10 * 1024 * 1024; // 10MB
if (file.getSize() > maxSize) {
return R.fail("文件大小超过10MB限制");
}
// 使用系统OSS服务上传文件(自动保存到sys_oss表)
SysOssVo ossResult = ossService.upload(file);
// 更新申请记录 - 存储OSS ID和文件路径
applicationService.updateStampedFileInfo(
applicationId,
ossResult.getUrl(), // 文件URL
ossResult.getOriginalName(), // 原始文件名
new Date(), // 上传时间
"1", // 上传状态:已上传
ossResult.getOssId() // OSS文件ID
);
// 返回结果
Map<String, Object> result = new HashMap<>();
result.put("filePath", ossResult.getUrl());
result.put("fileName", ossResult.getOriginalName());
result.put("uploadTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
result.put("ossId", ossResult.getOssId());
return R.ok("盖章文件上传成功", result);
} catch (Exception e) {
e.printStackTrace();
return R.fail("系统异常:" + e.getMessage());
}
}
/**
* 下载盖章文件(安全下载)
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/downloadStampedFile/{applicationId}")
public void downloadStampedFile(@PathVariable Long applicationId, HttpServletResponse response) {
try {
RsOverseasApplicationVo application = applicationService.queryById(applicationId);
if (application == null || application.getStampedFileOssId() == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":404,\"msg\":\"文件不存在\"}");
return;
}
// 使用系统OSS服务进行安全下载
ossService.secureDownload(application.getStampedFileOssId(), response);
} catch (Exception e) {
e.printStackTrace();
}
}
// ==================== 党籍保留附件上传接口 ====================
/**
* 上传党籍保留附件
*/
@SaCheckPermission("rs:overseas:add")
@PostMapping("/uploadPartyRetentionFile")
@Log(title = "上传党籍保留附件", businessType = BusinessType.OTHER)
public R<Map<String, Object>> uploadPartyRetentionFile(@RequestParam("file") MultipartFile file,
@RequestParam("applicationId") Long applicationId) {
try {
// 验证文件格式
String contentType = file.getContentType();
if (!isValidFileType(contentType)) {
return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件");
}
// 验证文件大小(10MB)
long maxSize = 10 * 1024 * 1024; // 10MB
if (file.getSize() > maxSize) {
return R.fail("文件大小超过10MB限制");
}
// 使用系统OSS服务上传文件(自动保存到sys_oss表)
SysOssVo ossResult = ossService.upload(file);
// 更新申请记录 - 存储OSS ID和文件路径
applicationService.updatePartyRetentionFileInfo(
applicationId,
ossResult.getUrl(), // 文件URL
ossResult.getOriginalName(), // 原始文件名
new Date(), // 上传时间
"1", // 上传状态:已上传
ossResult.getOssId() // OSS文件ID
);
// 返回结果
Map<String, Object> result = new HashMap<>();
result.put("filePath", ossResult.getUrl());
result.put("fileName", ossResult.getOriginalName());
result.put("uploadTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
result.put("ossId", ossResult.getOssId());
return R.ok("党籍保留附件上传成功", result);
} catch (Exception e) {
e.printStackTrace();
return R.fail("系统异常:" + e.getMessage());
}
}
/**
* 下载党籍保留附件(安全下载)
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/downloadPartyRetentionFile/{applicationId}")
public void downloadPartyRetentionFile(@PathVariable Long applicationId, HttpServletResponse response) {
try {
RsOverseasApplicationVo application = applicationService.queryById(applicationId);
if (application == null || application.getPartyRetentionFileOssId() == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":404,\"msg\":\"文件不存在\"}");
return;
}
// 使用系统OSS服务进行安全下载
ossService.secureDownload(application.getPartyRetentionFileOssId(), response);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 验证文件类型
*/
private boolean isValidFileType(String contentType) {
if (contentType == null)
return false;
return contentType.equals("application/pdf") ||
contentType.equals("image/jpeg") ||
contentType.equals("image/jpg") ||
contentType.equals("image/png");
}
/**
* 获取文件扩展名
*/
private String getFileExtension(String filename) {
if (filename == null || !filename.contains(".")) {
return ".pdf";
}
return filename.substring(filename.lastIndexOf("."));
}
}
@@ -25,11 +25,14 @@ import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.satoken.utils.LoginHelper;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import org.dromara.rs.domain.bo.RsOverseasApprovalBo;
import org.dromara.rs.domain.vo.RsOverseasApprovalVo;
import org.dromara.rs.domain.query.RsOverseasApprovalQuery;
import org.dromara.rs.service.IRsOverseasApprovalService;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.HashMap;
/**
* 出国审批记录Controller
@@ -75,6 +78,40 @@ public class RsOverseasApprovalController extends BaseController {
return approvalService.queryCombinedPageList(query);
}
/**
* 调试接口:查看当前用户的权限过滤情况
*/
@SaCheckPermission("rs:overseas:list")
@GetMapping("/debug/permission")
public R<Map<String, Object>> debugPermission(RsOverseasApprovalQuery query) {
Map<String, Object> debugInfo = new HashMap<>();
// 当前用户信息
debugInfo.put("currentUserId", LoginHelper.getUserId());
debugInfo.put("currentUsername", LoginHelper.getUsername());
debugInfo.put("currentDeptId", LoginHelper.getDeptId());
debugInfo.put("isSuperAdmin", LoginHelper.isSuperAdmin());
// 角色信息
debugInfo.put("hasRsApplicant", StpUtil.hasRole("rs-Applicant"));
debugInfo.put("hasRsAdmin", StpUtil.hasRole("rs-admin"));
debugInfo.put("hasApprove1Permission", StpUtil.hasPermission("rs:overseas:approve1"));
debugInfo.put("hasDeptHeadRole", StpUtil.hasRole("dept_head_role"));
debugInfo.put("hasOldApprovePermission", StpUtil.hasPermission("overseas:application:dept_head:approve"));
// 查询参数(应用权限过滤前)
debugInfo.put("originalQuery", query.toString());
// 应用权限过滤
TableDataInfo<RsOverseasApprovalVo> result = approvalService.queryCombinedPageList(query);
// 查询参数(应用权限过滤后)
debugInfo.put("filteredQuery", query.toString());
debugInfo.put("resultCount", result.getTotal());
return R.ok(debugInfo);
}
/**
* 根据申请ID查询审批记录列表
*/
@@ -153,4 +190,4 @@ public class RsOverseasApprovalController extends BaseController {
return R.fail("修复失败:" + e.getMessage());
}
}
}
}
@@ -0,0 +1,129 @@
package org.dromara.rs.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.rs.domain.vo.RsOverseasApprovalMonitorVo;
import org.dromara.rs.domain.query.RsOverseasApprovalMonitorQuery;
import org.dromara.rs.service.IRsOverseasApprovalMonitorService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 出国申请审批监控Controller
* <p>
* 功能说明:
* 1. 实时查看出国申请的审批流程(当前在哪个审批环节、审批人是谁)
* 2. 查看已完成审批的出国申请
*
* @author Cursor AI
* @date 2025-10-21
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/rs/overseas/monitor")
public class RsOverseasApprovalMonitorController extends BaseController {
private final IRsOverseasApprovalMonitorService monitorService;
/**
* 查询待审批的申请列表(出国申请进度查询)
* <p>
* 🔴 功能说明:查询所有待审批申请(不限制申请人)
* - 拥有权限的用户可以查看所有待审批申请
* - 显示当前审批环节和审批人
* - 可以查看审批历史记录
*
* @param query 查询条件
* @return 所有待审批申请列表
*/
@SaCheckPermission("rs:overseas:list")
@GetMapping("/pending/list")
public TableDataInfo<RsOverseasApprovalMonitorVo> pendingList(RsOverseasApprovalMonitorQuery query) {
log.info("查询所有待审批申请列表");
// 只查询待审核状态(status = 0)
query.setStatus("0");
return monitorService.queryPendingList(query);
}
/**
* 查询已完成审批的申请列表
* <p>
* 🔴 功能说明:查询所有已完成审批申请(不限制申请人)
* - 拥有权限的用户可以查看所有已完成审批申请
* - 显示最终审批结果和完成时间
* - 可以查看所有审批环节的历史记录
*
* @param query 查询条件
* @return 所有已完成审批的申请列表
*/
@SaCheckPermission("rs:overseas:list")
@GetMapping("/completed/list")
public TableDataInfo<RsOverseasApprovalMonitorVo> completedList(RsOverseasApprovalMonitorQuery query) {
log.info("查询所有已完成审批的申请列表,查询条件:{}", query);
// 只查询已通过状态(status = 1)
query.setStatus("1");
return monitorService.queryCompletedList(query);
}
/**
* 查询申请详情(含审批流程信息)
* <p>
* 获取申请的完整信息,包括:
* - 申请基本信息
* - 当前审批状态
* - 审批历史记录
*
* @param applicationId 申请ID
* @return 申请详情
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/detail/{applicationId}")
public R<RsOverseasApprovalMonitorVo> getDetail(@PathVariable @NotNull(message = "申请ID不能为空") Long applicationId) {
log.info("查询申请详情,申请ID:{}", applicationId);
RsOverseasApprovalMonitorVo detail = monitorService.queryDetailWithApprovalInfo(applicationId);
return R.ok(detail);
}
/**
* 统计审批进度概况
* <p>
* 返回审批监控的统计数据:
* - 待审批总数
* - 各审批级别的待审批数量
* - 已完成审批总数
* - 今日新增申请数
*
* @return 统计数据
*/
@SaCheckPermission("rs:overseas:list")
@GetMapping("/statistics")
public R<Object> statistics() {
log.info("查询审批进度统计");
return R.ok(monitorService.getApprovalStatistics());
}
/**
* 下载出国申请审批表PDF
* <p>
* 生成并下载"杭州第一技师学院登记报备人员因私事出国(境)审批表"PDF文件
*
* @param applicationId 申请ID
* @param response HTTP响应对象
*/
@SaCheckPermission("rs:overseas:query")
@GetMapping("/downloadApprovalPdf/{applicationId}")
public void downloadApprovalPdf(
@PathVariable @NotNull(message = "申请ID不能为空") Long applicationId,
HttpServletResponse response) {
log.info("下载出国申请审批表PDF,申请ID:{}", applicationId);
monitorService.downloadApprovalPdf(applicationId, response);
}
}
@@ -0,0 +1,187 @@
package org.dromara.rs.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.rs.domain.bo.RsOverseasItineraryChangeBo;
import org.dromara.rs.domain.query.RsOverseasItineraryChangeQuery;
import org.dromara.rs.domain.vo.RsOverseasItineraryChangeVo;
import org.dromara.rs.domain.vo.RsOverseasRecordVo;
import org.dromara.rs.service.IRsOverseasItineraryChangeService;
import org.dromara.rs.service.IRsOverseasRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* 出国行程变更申请Controller
*
* @author weizzzz
* @date 2025-10-13
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/rs/itinerary/change")
public class RsOverseasItineraryChangeController extends BaseController {
private final IRsOverseasItineraryChangeService changeService;
private final IRsOverseasRecordService recordService;
/**
* 查询行程变更申请列表
*/
@SaCheckPermission("rs:itineraryChange:list")
@GetMapping("/list")
public TableDataInfo<RsOverseasItineraryChangeVo> list(RsOverseasItineraryChangeQuery query) {
query.setTenantId(LoginHelper.getTenantId());
return changeService.queryPageList(query);
}
/**
* 查询我的变更记录列表(只显示已通过的变更申请)
* 说明:变更记录Tab只显示已通过审批的变更申请,这些是真正生效的变更记录
*/
@SaCheckPermission("rs:itineraryChange:list")
@GetMapping("/my/list")
public TableDataInfo<RsOverseasItineraryChangeVo> myList(RsOverseasItineraryChangeQuery query) {
query.setTenantId(LoginHelper.getTenantId());
return changeService.queryMyChangeList(query);
}
/**
* 🔴 标准化改造:删除自定义待审批查询接口
*
* 原因:出国行程变更工作流应使用标准工作流模式
* 用户应通过工作流API pageByTaskWait() 获取待办任务
* 而不是调用自定义的业务API
*/
@SaCheckPermission("rs:itineraryChange:approve")
@GetMapping("/pending/list")
public R<String> pendingList(RsOverseasItineraryChangeQuery query) {
// 提示用户使用标准工作流API
return R.fail("请使用标准工作流待办页面查看审批任务。访问:工作流 > 我的任务 > 待办任务");
}
/**
* 查询行程变更申请详情
*/
@SaCheckPermission("rs:itineraryChange:query")
@GetMapping("/{changeId}")
public R<RsOverseasItineraryChangeVo> getInfo(@PathVariable Long changeId) {
return R.ok(changeService.queryById(changeId));
}
/**
* 获取可用的出国记录列表(用于选择原出国申请)
* 说明:选择的是出国记录选项卡中的记录
*/
@SaCheckPermission("rs:itineraryChange:list")
@GetMapping("/available-records")
public R<List<RsOverseasRecordVo>> getAvailableRecords() {
// 查询当前用户的所有出国记录
org.dromara.rs.domain.query.RsOverseasRecordQuery query = new org.dromara.rs.domain.query.RsOverseasRecordQuery();
query.setApplicantId(LoginHelper.getUserId());
query.setTenantId(LoginHelper.getTenantId());
List<RsOverseasRecordVo> records = recordService.queryList(query);
return R.ok(records);
}
/**
* 根据出国记录ID查询记录详情(用于显示原申请信息)
*/
@SaCheckPermission("rs:itineraryChange:query")
@GetMapping("/record/{recordId}")
public R<RsOverseasRecordVo> getRecordInfo(@PathVariable Long recordId) {
return R.ok(recordService.queryById(recordId));
}
/**
* 新增行程变更申请
*/
@SaCheckPermission("rs:itineraryChange:add")
@Log(title = "行程变更申请", businessType = BusinessType.INSERT)
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody RsOverseasItineraryChangeBo bo) {
bo.setTenantId(LoginHelper.getTenantId());
return toAjax(changeService.insertByBo(bo));
}
/**
* 修改行程变更申请(重新提交)
*/
@SaCheckPermission("rs:itineraryChange:edit")
@Log(title = "行程变更申请", businessType = BusinessType.UPDATE)
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody RsOverseasItineraryChangeBo bo) {
return toAjax(changeService.updateByBo(bo));
}
/**
* 删除行程变更申请
*/
@SaCheckPermission("rs:itineraryChange:remove")
@Log(title = "行程变更申请", businessType = BusinessType.DELETE)
@DeleteMapping("/{changeIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] changeIds) {
return toAjax(changeService.deleteByIds(List.of(changeIds)));
}
/**
* 撤回行程变更申请
*/
@SaCheckPermission("rs:itineraryChange:cancel")
@Log(title = "行程变更申请", businessType = BusinessType.UPDATE)
@PutMapping("/cancel/{changeId}")
public R<Void> cancel(@PathVariable Long changeId) {
return toAjax(changeService.cancelChange(changeId));
}
/**
* 审批行程变更申请
*/
@SaCheckPermission("rs:itineraryChange:approve")
@Log(title = "行程变更申请", businessType = BusinessType.UPDATE)
@PutMapping("/approve")
public R<Void> approve(@RequestBody Map<String, Object> params) {
Long changeId = Long.valueOf(params.get("changeId").toString());
Boolean approved = Boolean.valueOf(params.get("approved").toString());
String opinion = params.get("opinion") != null ? params.get("opinion").toString() : "";
return toAjax(changeService.approveChange(changeId, approved, opinion));
}
/**
* 导出行程变更申请列表
*/
@SaCheckPermission("rs:itineraryChange:export")
@Log(title = "行程变更申请", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(RsOverseasItineraryChangeQuery query, HttpServletResponse response) {
query.setTenantId(LoginHelper.getTenantId());
List<RsOverseasItineraryChangeVo> list = changeService.queryList(query);
ExcelUtil.exportExcel(list, "行程变更申请", RsOverseasItineraryChangeVo.class, response);
}
/**
* 查询某个出国记录的所有变更历史
*/
@SaCheckPermission("rs:itineraryChange:query")
@GetMapping("/record/{recordId}/history")
public R<List<RsOverseasItineraryChangeVo>> getChangeHistory(@PathVariable Long recordId) {
return R.ok(changeService.queryByRecordId(recordId));
}
}
@@ -30,6 +30,12 @@ import org.dromara.rs.domain.bo.RsOverseasRecordBo;
import org.dromara.rs.domain.vo.RsOverseasRecordVo;
import org.dromara.rs.domain.query.RsOverseasRecordQuery;
import org.dromara.rs.service.IRsOverseasRecordService;
import org.dromara.rs.service.IRegistrationFormService;
import org.dromara.rs.util.OverseasReceiptPdfUtil;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* 出国记录Controller
@@ -45,6 +51,9 @@ public class RsOverseasRecordController extends BaseController {
@Autowired
private IRsOverseasRecordService recordService;
@Autowired
private IRegistrationFormService registrationFormService;
/**
* 查询出国记录列表
*/
@@ -61,8 +70,17 @@ public class RsOverseasRecordController extends BaseController {
@GetMapping("/my/list")
public TableDataInfo<RsOverseasRecordVo> myList(RsOverseasRecordQuery query) {
Long userId = LoginHelper.getUserId();
System.out.println("========== 出国记录Controller: 接收到查询请求 ==========");
System.out.println("当前用户ID: " + userId);
System.out.println("原始查询参数: " + query);
query.setApplicantId(userId);
return recordService.queryMyRecordPageList(query);
System.out.println("设置申请人ID后: " + query);
TableDataInfo<RsOverseasRecordVo> result = recordService.queryMyRecordPageList(query);
System.out.println("Controller返回结果: total=" + result.getTotal() + ", rows="
+ (result.getRows() != null ? result.getRows().size() : 0));
System.out.println("========== 出国记录Controller: 查询完成 ==========");
return result;
}
/**
@@ -86,6 +104,17 @@ public class RsOverseasRecordController extends BaseController {
ExcelUtil.exportExcel(list, "出国记录数据", RsOverseasRecordVo.class, response);
}
/**
* 下载党籍保留申请PDF模板
* 注意:此接口必须放在 /{recordId} 之前,避免路由冲突
*/
@SaCheckPermission("rs:overseas:query")
@Log(title = "下载党籍保留申请模板", businessType = BusinessType.EXPORT)
@GetMapping("/downloadPartyRetention")
public void downloadPartyRetention(HttpServletResponse response) {
registrationFormService.downloadPartyMembershipTemplate(response);
}
/**
* 获取出国记录详细信息
*/
@@ -133,8 +162,8 @@ public class RsOverseasRecordController extends BaseController {
@PutMapping("/complete")
public R<Void> updateCompletion(@RequestBody CompletionRequest request) {
return toAjax(recordService.updateCompletionStatus(
request.getRecordId(), request.getCompletionStatus(),
request.getAchievement(), request.getReportContent()));
request.getRecordId(), request.getCompletionStatus(),
request.getAchievement(), request.getReportContent()));
}
/**
@@ -147,6 +176,53 @@ public class RsOverseasRecordController extends BaseController {
return toAjax(recordService.deleteWithValidByIds(recordIds, true));
}
/**
* 下载因私出国(境)情况回执PDF
*/
@SaCheckPermission("rs:overseas:query")
@Log(title = "下载出国情况回执", businessType = BusinessType.EXPORT)
@GetMapping("/downloadReceipt/{recordId}")
public void downloadReceipt(@NotNull(message = "记录ID不能为空") @PathVariable Long recordId,
HttpServletResponse response) {
try {
// 查询出国记录详情
RsOverseasRecordVo record = recordService.queryById(recordId);
if (record == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":404,\"msg\":\"出国记录不存在\"}");
return;
}
// 生成PDF
byte[] pdfBytes = OverseasReceiptPdfUtil.generateReceiptPdf(record);
// 设置响应头
String fileName = String.format("因私出国情况回执_%s_%s.pdf",
record.getApplicantName(),
new java.text.SimpleDateFormat("yyyyMMdd").format(new java.util.Date()));
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20");
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
response.setContentLength(pdfBytes.length);
// 输出PDF
response.getOutputStream().write(pdfBytes);
response.getOutputStream().flush();
} catch (Exception e) {
try {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":500,\"msg\":\"PDF生成失败: " + e.getMessage() + "\"}");
} catch (IOException ioException) {
// 记录日志
System.err.println("PDF生成失败: " + e.getMessage());
}
}
}
/**
* 初始化测试数据(临时接口,用于解决出国记录列表显示问题)
*/
@@ -156,7 +232,7 @@ public class RsOverseasRecordController extends BaseController {
try {
Long currentUserId = LoginHelper.getUserId();
String currentUserName = LoginHelper.getUsername();
// 创建一个测试出国记录
RsOverseasRecordBo testRecord = new RsOverseasRecordBo();
testRecord.setApplicationId(1L);
@@ -173,9 +249,9 @@ public class RsOverseasRecordController extends BaseController {
testRecord.setAchievement("参加国际智能控制学术会议,发表论文,与德国同行建立合作关系");
testRecord.setReportContent("此次德国学术交流收获丰富,重点学习了最新的智能控制理论和技术");
testRecord.setCertificatePath("/uploads/overseas/records/2025/08/conference_certificate.pdf");
recordService.insertByBo(testRecord);
return R.ok("测试数据初始化成功,请刷新页面查看");
} catch (Exception e) {
return R.fail("测试数据初始化失败: " + e.getMessage());
@@ -223,4 +299,4 @@ public class RsOverseasRecordController extends BaseController {
this.reportContent = reportContent;
}
}
}
}
@@ -0,0 +1,206 @@
package org.dromara.rs.controller;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.satoken.utils.LoginHelper;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.rs.domain.bo.RsOverseasRegistrationBo;
import org.dromara.rs.domain.vo.RsOverseasRegistrationVo;
import org.dromara.rs.domain.query.RsOverseasRegistrationQuery;
import org.dromara.rs.service.IRsOverseasRegistrationService;
/**
* 出国登记Controller
*
* @author weizzzz
* @date 2025-09-30
*/
@Validated
@RestController
@RequestMapping("/rs/overseas/registration")
public class RsOverseasRegistrationController extends BaseController {
@Autowired
private IRsOverseasRegistrationService registrationService;
/**
* 查询出国登记列表
*/
@SaCheckPermission("rs:overseas:registration:list")
@GetMapping("/list")
public TableDataInfo<RsOverseasRegistrationVo> list(RsOverseasRegistrationQuery query) {
return registrationService.queryPageList(query);
}
/**
* 查询我的出国登记列表
*/
@SaCheckPermission("rs:overseas:registration:list")
@GetMapping("/my/list")
public TableDataInfo<RsOverseasRegistrationVo> myList(RsOverseasRegistrationQuery query) {
return registrationService.queryMyRegistrationList(query);
}
/**
* 导出出国登记列表
*/
@SaCheckPermission("rs:overseas:registration:export")
@Log(title = "出国登记", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(RsOverseasRegistrationQuery query, HttpServletResponse response) {
List<RsOverseasRegistrationVo> list = registrationService.queryList(query);
ExcelUtil.exportExcel(list, "出国登记", RsOverseasRegistrationVo.class, response);
}
/**
* 获取出国登记详细信息
*/
@SaCheckPermission("rs:overseas:registration:query")
@GetMapping("/{registrationId}")
public R<RsOverseasRegistrationVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long registrationId) {
return R.ok(registrationService.queryById(registrationId));
}
/**
* 根据申请ID获取出国登记信息
*/
@SaCheckPermission("rs:overseas:registration:query")
@GetMapping("/application/{applicationId}")
public R<RsOverseasRegistrationVo> getByApplicationId(@NotNull(message = "申请ID不能为空")
@PathVariable Long applicationId) {
return R.ok(registrationService.queryByApplicationId(applicationId));
}
/**
* 新增出国登记
*/
@SaCheckPermission("rs:overseas:registration:add")
@Log(title = "出国登记", businessType = BusinessType.INSERT)
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
return toAjax(registrationService.insertByBo(bo));
}
/**
* 新增并提交出国登记
*/
@SaCheckPermission("rs:overseas:registration:add")
@Log(title = "出国登记", businessType = BusinessType.INSERT)
@PostMapping("/addAndSubmit")
public R<Void> addAndSubmit(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
return toAjax(registrationService.addAndSubmit(bo));
}
/**
* 修改出国登记
*/
@SaCheckPermission("rs:overseas:registration:edit")
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
return toAjax(registrationService.updateByBo(bo));
}
/**
* 删除出国登记
*/
@SaCheckPermission("rs:overseas:registration:remove")
@Log(title = "出国登记", businessType = BusinessType.DELETE)
@DeleteMapping("/{registrationIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] registrationIds) {
return toAjax(registrationService.deleteWithValidByIds(List.of(registrationIds), true));
}
/**
* 提交出国登记
*/
@SaCheckPermission("rs:overseas:registration:submit")
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
@PutMapping("/submit/{registrationId}")
public R<Void> submit(@NotNull(message = "主键不能为空")
@PathVariable Long registrationId) {
return toAjax(registrationService.submitRegistration(registrationId));
}
/**
* 确认出国登记
*/
@SaCheckPermission("rs:overseas:registration:confirm")
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
@PutMapping("/confirm/{registrationId}")
public R<Void> confirm(@NotNull(message = "主键不能为空")
@PathVariable Long registrationId) {
return toAjax(registrationService.confirmRegistration(registrationId));
}
/**
* 撤回出国登记
*/
@SaCheckPermission("rs:overseas:registration:withdraw")
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
@PutMapping("/withdraw/{registrationId}")
public R<Void> withdraw(@NotNull(message = "主键不能为空")
@PathVariable Long registrationId) {
return toAjax(registrationService.withdrawRegistration(registrationId));
}
/**
* 基于出国申请创建登记记录
*/
@SaCheckPermission("rs:overseas:registration:create")
@Log(title = "出国登记", businessType = BusinessType.INSERT)
@PostMapping("/create/{applicationId}")
public R<Void> createFromApplication(@NotNull(message = "申请ID不能为空")
@PathVariable Long applicationId) {
return toAjax(registrationService.createFromApplication(applicationId));
}
/**
* 批准出国登记
*/
@SaCheckPermission("rs:registration:approve")
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
@PutMapping("/approve/{registrationId}")
public R<Void> approve(@NotNull(message = "主键不能为空")
@PathVariable Long registrationId,
@RequestBody(required = false) RsOverseasRegistrationBo bo) {
String approveNotes = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "同意";
return toAjax(registrationService.approveRegistration(registrationId, approveNotes));
}
/**
* 拒绝出国登记
*/
@SaCheckPermission("rs:registration:reject")
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
@PutMapping("/reject/{registrationId}")
public R<Void> reject(@NotNull(message = "主键不能为空")
@PathVariable Long registrationId,
@RequestBody(required = false) RsOverseasRegistrationBo bo) {
String rejectReason = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "不同意";
return toAjax(registrationService.rejectRegistration(registrationId, rejectReason));
}
}
@@ -19,7 +19,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletResponse;
@@ -28,6 +27,10 @@ import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.rs.domain.vo.SysUserBasicVo;
import cn.hutool.core.collection.CollUtil;
import java.util.ArrayList;
/**
* 培训申请控制器
@@ -38,12 +41,12 @@ import org.dromara.common.satoken.utils.LoginHelper;
@RestController
@RequestMapping("/rs/training/application")
public class RsTrainingApplicationController extends BaseController {
private static final Logger log = LoggerFactory.getLogger(RsTrainingApplicationController.class);
@Autowired
private IRsTrainingApplicationService trainingApplicationService;
// 新增用户服务注入
@Autowired
private ISysUserService sysUserService;
@@ -87,7 +90,7 @@ public class RsTrainingApplicationController extends BaseController {
// 获取用户ID,处理可能的字符串格式
Object loginId = StpUtil.getLoginId();
Long userId = null;
if (loginId != null) {
if (loginId instanceof Long) {
userId = (Long) loginId;
@@ -114,18 +117,18 @@ public class RsTrainingApplicationController extends BaseController {
userId = ((Number) loginId).longValue();
}
}
if (userId != null) {
log.info("获取当前登录用户信息: userId={}", userId);
// 从系统用户表获取用户真实信息
SysUserVo userVo = sysUserService.selectUserById(userId);
if (userVo != null) {
String nickName = userVo.getNickName();
String userName = userVo.getUserName();
log.info("成功获取用户信息: userId={}, nickName={}, userName={}", userId, nickName, userName);
// 使用用户名(根据前端要求)
String displayName = StringUtils.isNotBlank(userName) ? userName : "用户" + userId;
return new UserInfo(userId, displayName);
@@ -138,7 +141,7 @@ public class RsTrainingApplicationController extends BaseController {
} catch (Exception e) {
log.error("获取用户信息异常: loginId={}, error={}", StpUtil.getLoginId(), e.getMessage(), e);
}
// 如果获取失败,尝试从Sa-Token获取基本信息
try {
Object fallbackLoginId = StpUtil.getLoginId();
@@ -163,26 +166,31 @@ public class RsTrainingApplicationController extends BaseController {
} catch (Exception e) {
log.error("备用方案也失败: {}", e.getMessage(), e);
}
// 最后的备用方案
log.error("所有用户信息获取方案都失败,返回默认值");
return new UserInfo(1L, "系统用户");
}
/**
* 用户信息内部类
*/
private static class UserInfo {
private final Long userId;
private final String userName;
public UserInfo(Long userId, String userName) {
this.userId = userId;
this.userName = StringUtils.isNotBlank(userName) ? userName : "用户" + userId;
}
public Long getUserId() { return userId; }
public String getUserName() { return userName; }
public Long getUserId() {
return userId;
}
public String getUserName() {
return userName;
}
}
/**
@@ -198,17 +206,17 @@ public class RsTrainingApplicationController extends BaseController {
UserInfo userInfo = getCurrentUserInfo();
application.setUserId(userInfo.getUserId());
application.setUserName(userInfo.getUserName());
// 确保courseId的类型正确
if (application.getCourseId() == null) {
application.setCourseId(0L);
}
// 确保必需字段不为空
if (application.getDepartment() == null) {
application.setDepartment("未指定部门");
}
int result = trainingApplicationService.insertRsTrainingApplication(application);
return toAjax(result);
} catch (Exception e) {
@@ -238,7 +246,7 @@ public class RsTrainingApplicationController extends BaseController {
public R<Void> remove(@PathVariable Long[] applicationIds) {
return toAjax(trainingApplicationService.deleteRsTrainingApplicationByIds(applicationIds));
}
/**
* 取消培训申请
*/
@@ -248,27 +256,19 @@ public class RsTrainingApplicationController extends BaseController {
public R<Void> cancel(@PathVariable("applicationId") Long applicationId) {
return toAjax(trainingApplicationService.cancelApplication(applicationId));
}
/**
* 查询待审批的培训申请列表(供审批人使用)
* 🔴 标准化改造:删除自定义待审批查询接口
*
* 原因:培训申请工作流应使用标准工作流模式
* 用户应通过工作流API pageByTaskWait() 获取待办任务
* 而不是调用自定义的业务API
*/
@SaCheckPermission("rs:trainingapproval:process")
@GetMapping("/pending/list")
public TableDataInfo<RsTrainingApplication> pendingList(RsTrainingApplication application, PageQuery pageQuery) {
// 设置租户ID
if (application.getTenantId() == null || application.getTenantId().isEmpty()) {
application.setTenantId(LoginHelper.getTenantId());
}
// 只查询待审核状态的申请(排除已通过状态)
application.setExcludeStatus("1"); // 排除已通过状态
log.info("查询待审批培训申请列表 - 用户: {}, 角色: {}, 查询条件: {}",
LoginHelper.getUsername(),
StpUtil.getRoleList(),
application);
return trainingApplicationService.selectPageRsTrainingApplicationList(application, pageQuery);
public R<String> pendingList(RsTrainingApplication application, PageQuery pageQuery) {
// 提示用户使用标准工作流API
return R.fail("请使用标准工作流待办页面查看审批任务。访问:工作流 > 我的任务 > 待办任务");
}
/**
@@ -276,16 +276,20 @@ public class RsTrainingApplicationController extends BaseController {
*/
@SaCheckPermission("rs:trainingapplication:query")
@GetMapping("/my")
public TableDataInfo<RsTrainingApplication> listMyApplications(RsTrainingApplication application, PageQuery pageQuery) {
return trainingApplicationService.selectPageMyApplications(StpUtil.getLoginIdAsLong(), application, pageQuery);
public TableDataInfo<RsTrainingApplication> listMyApplications(RsTrainingApplication application,
PageQuery pageQuery) {
// 安全获取用户ID,兼容 "sys_user:2091" 格式
Long userId = getCurrentUserInfo().getUserId();
return trainingApplicationService.selectPageMyApplications(userId, application, pageQuery);
}
/**
* 查询指定课程的申请列表
*/
@SaCheckPermission("rs:trainingapplication:list")
@GetMapping("/list/{courseId}")
public TableDataInfo<RsTrainingApplication> listByCourse(@PathVariable("courseId") Long courseId, RsTrainingApplication application, PageQuery pageQuery) {
public TableDataInfo<RsTrainingApplication> listByCourse(@PathVariable("courseId") Long courseId,
RsTrainingApplication application, PageQuery pageQuery) {
return trainingApplicationService.selectPageApplicationsByCourse(courseId, application, pageQuery);
}
@@ -302,17 +306,17 @@ public class RsTrainingApplicationController extends BaseController {
UserInfo userInfo = getCurrentUserInfo();
application.setUserId(userInfo.getUserId());
application.setUserName(userInfo.getUserName());
// 确保courseId的类型正确
if (application.getCourseId() == null) {
application.setCourseId(0L);
}
// 确保必需字段不为空
if (application.getDepartment() == null) {
application.setDepartment("未指定部门");
}
int result = trainingApplicationService.insertRsTrainingApplication(application);
return toAjax(result);
} catch (Exception e) {
@@ -334,7 +338,7 @@ public class RsTrainingApplicationController extends BaseController {
log.info("更新培训申请状态: applicationId={}, status={}", applicationId, status);
return toAjax(trainingApplicationService.updateApplicationStatus(applicationId, status));
}
/**
* 重新提交被拒绝的培训申请
*/
@@ -348,4 +352,177 @@ public class RsTrainingApplicationController extends BaseController {
log.info("重新提交被拒绝的培训申请: applicationId={}", applicationId);
return toAjax(trainingApplicationService.resubmitApplication(applicationId, application));
}
}
// ========================================
// 7级审批接口(OR模式权限)
// ========================================
/**
* 审批培训申请(7级审批制度)
* 🔴 支持7级审批权限:满足任一级别权限即可访问接口,具体级别在Service层验证
*
* 审批级别:
* - 第1级:部门负责人 (rs:training:approve1)
* - 第2A级:教务处 (rs:training:approve2a) - 培训类型=教学
* - 第2B级:学生处 (rs:training:approve2b) - 培训类型=德育
* - 第3级:计划财务处 (rs:training:approve3)
* - 第4级:人事处 (rs:training:approve4)
* - 第5级:业务分管领导 (rs:training:approve5)
* - 第6级:人事分管院领导 (rs:training:approve6)
* - 第7级:院长 (rs:training:approve7) - 最终审批
*/
@SaCheckPermission(value = {
"rs:training:approve", // 通用审批权限(超级管理员)
"rs:training:approve1", // 第1级:部门负责人
"rs:training:approve2a", // 第2A级:教务处
"rs:training:approve2b", // 第2B级:学生处
"rs:training:approve3", // 第3级:计划财务处
"rs:training:approve4", // 第4级:人事处
"rs:training:approve5", // 第5级:业务分管领导
"rs:training:approve6", // 第6级:人事分管院领导
"rs:training:approve7" // 第7级:院长
}, mode = cn.dev33.satoken.annotation.SaMode.OR) // 🔴 关键:OR模式,满足任一权限即可通过
@Log(title = "审批培训申请", businessType = BusinessType.UPDATE)
@PostMapping("/approve")
public R<Void> approve(@RequestBody java.util.Map<String, Object> request) {
try {
// 解析请求参数
Long applicationId = Long.parseLong(request.get("applicationId").toString());
String approvalStatus = request.get("approvalStatus").toString();
String approvalOpinion = request.get("approvalOpinion") != null ? request.get("approvalOpinion").toString()
: "";
String approvalSignature = request.get("approvalSignature") != null
? request.get("approvalSignature").toString()
: "";
// 获取当前审批人信息
Long approverId = LoginHelper.getUserId();
String approverName = LoginHelper.getUsername();
log.info("审批培训申请 - 申请ID: {}, 审批人: {} ({}), 审批状态: {}",
applicationId, approverName, approverId, approvalStatus);
// 调用Service层审批方法
Boolean result = trainingApplicationService.approveApplication(
applicationId, approverId, approverName,
approvalStatus, approvalOpinion, approvalSignature);
return result ? R.ok() : R.fail("审批失败");
} catch (Exception e) {
log.error("审批培训申请失败: {}", e.getMessage(), e);
return R.fail("审批失败: " + e.getMessage());
}
}
/**
* 查询当前用户的待审批申请列表(动态权限方案 + 部门过滤)
*
* 权限说明:
* - 第1级审批人:只能看到本部门员工的待审批申请
* - 第2-7级审批人:可以看到全校的待审批申请
*/
@SaCheckPermission(value = {
"rs:training:approve",
"rs:training:approve1",
"rs:training:approve2a",
"rs:training:approve2b",
"rs:training:approve3",
"rs:training:approve4",
"rs:training:approve5",
"rs:training:approve6",
"rs:training:approve7"
}, mode = cn.dev33.satoken.annotation.SaMode.OR)
@GetMapping("/pending")
public R<List<RsTrainingApplication>> queryPending() {
try {
List<RsTrainingApplication> result = trainingApplicationService.queryPendingByCurrentUser();
log.info("查询待审批申请 - 用户: {}, 待审批数量: {}",
LoginHelper.getUsername(), result.size());
return R.ok(result);
} catch (Exception e) {
log.error("查询待审批申请失败: {}", e.getMessage(), e);
return R.fail("查询失败: " + e.getMessage());
}
}
/**
* 批量查询同行人基本信息
* 用于培训申请/记录详情展示,不限制account_type
*
* 权限说明:
* - 只需要培训申请的查询权限即可
* - 不需要system:teacher:list权限
* - 只返回用户的基本信息(姓名、部门等),不暴露敏感数据
*
* @param userIds 用户ID列表(逗号分隔)
* @return 用户基本信息列表
*/
@SaCheckPermission("rs:trainingapplication:query")
@GetMapping("/companions/batch")
public R<List<SysUserBasicVo>> batchQueryCompanions(@RequestParam String userIds) {
log.info("批量查询同行人信息,用户ID列表: {}", userIds);
// 参数验证
if (StringUtils.isBlank(userIds)) {
log.warn("同行人ID列表为空");
return R.ok(new ArrayList<>());
}
// 解析用户ID列表(兼容 Redis 缓存键格式 "sys_user:2091")
List<Long> userIdList = new ArrayList<>();
try {
String[] idArray = userIds.split(",");
for (String idStr : idArray) {
if (StringUtils.isNotBlank(idStr)) {
String trimmedId = idStr.trim();
// 处理 Redis 缓存键格式 "sys_user:2091" -> "2091"
if (trimmedId.contains(":")) {
String[] parts = trimmedId.split(":");
if (parts.length > 1) {
trimmedId = parts[1]; // 提取冒号后面的数字部分
}
}
userIdList.add(Long.parseLong(trimmedId));
}
}
} catch (NumberFormatException e) {
log.error("解析用户ID失败,原始字符串: {}, 错误: {}", userIds, e.getMessage());
return R.fail("用户ID格式错误: " + e.getMessage());
}
if (CollUtil.isEmpty(userIdList)) {
log.warn("解析后的用户ID列表为空");
return R.ok(new ArrayList<>());
}
// 批量查询用户信息
List<SysUserBasicVo> userList = new ArrayList<>();
for (Long userId : userIdList) {
try {
// 调用用户服务查询
SysUserVo sysUser = sysUserService.selectUserById(userId);
if (sysUser != null) {
SysUserBasicVo vo = new SysUserBasicVo();
vo.setUserId(sysUser.getUserId());
vo.setUserName(sysUser.getUserName());
vo.setNickName(sysUser.getNickName());
vo.setAccountType(sysUser.getAccountType());
vo.setStatus(sysUser.getStatus());
vo.setDeptId(sysUser.getDeptId());
// 设置部门名称(SysUserVo已经包含deptName字段)
vo.setDeptName(sysUser.getDeptName());
userList.add(vo);
log.debug("查询到用户: userId={}, nickName={}", userId, sysUser.getNickName());
} else {
log.warn("用户不存在: userId={}", userId);
}
} catch (Exception e) {
log.error("查询用户{}失败: {}", userId, e.getMessage());
// 继续查询下一个用户,不中断整个流程
}
}
log.info("批量查询同行人完成,请求{}个,查询到{}个", userIdList.size(), userList.size());
return R.ok(userList);
}
}
@@ -21,6 +21,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
/**
* 培训审批控制器
@@ -28,6 +29,7 @@ import jakarta.servlet.http.HttpServletResponse;
* @author weizzzz
* @date 2025-07-16
*/
@Slf4j
@RestController
@RequestMapping("/rs/training/approval")
public class RsTrainingApprovalController extends BaseController {
@@ -102,7 +104,38 @@ public class RsTrainingApprovalController extends BaseController {
@SaCheckPermission("rs:trainingapproval:process")
@GetMapping("/pending")
public TableDataInfo<RsTrainingApproval> listPendingApprovals(RsTrainingApproval approval, PageQuery pageQuery) {
return trainingApprovalService.selectPagePendingApprovals(StpUtil.getLoginIdAsLong(), approval, pageQuery);
// 安全获取用户ID,兼容 "sys_user:2091" 格式
Long userId = getSafeUserId();
return trainingApprovalService.selectPagePendingApprovals(userId, approval, pageQuery);
}
/**
* 安全获取当前登录用户ID,兼容多种格式
*/
private Long getSafeUserId() {
try {
Object loginId = StpUtil.getLoginId();
if (loginId instanceof Long) {
return (Long) loginId;
} else if (loginId instanceof String) {
String loginIdStr = (String) loginId;
// 处理 "sys_user:2091" 格式
if (loginIdStr.contains(":")) {
String[] parts = loginIdStr.split(":");
if (parts.length > 1) {
return Long.parseLong(parts[1]);
}
} else {
return Long.parseLong(loginIdStr);
}
} else if (loginId instanceof Number) {
return ((Number) loginId).longValue();
}
} catch (Exception e) {
log.error("❌ 获取用户ID失败: {}", e.getMessage());
throw new RuntimeException("无法获取当前用户ID,请重新登录");
}
throw new RuntimeException("无法获取当前用户ID,请重新登录");
}
/**
@@ -150,7 +150,38 @@ public class RsTrainingRecordController extends BaseController {
@SaCheckPermission("rs:trainingrecord:query")
@GetMapping("/my")
public TableDataInfo<RsTrainingRecord> listMyRecords(RsTrainingRecord record, PageQuery pageQuery) {
return trainingRecordService.selectPageMyRecords(StpUtil.getLoginIdAsLong(), record, pageQuery);
// 安全获取用户ID,兼容 "sys_user:2091" 格式
Long userId = getSafeUserId();
return trainingRecordService.selectPageMyRecords(userId, record, pageQuery);
}
/**
* 安全获取当前登录用户ID,兼容多种格式
*/
private Long getSafeUserId() {
try {
Object loginId = StpUtil.getLoginId();
if (loginId instanceof Long) {
return (Long) loginId;
} else if (loginId instanceof String) {
String loginIdStr = (String) loginId;
// 处理 "sys_user:2091" 格式
if (loginIdStr.contains(":")) {
String[] parts = loginIdStr.split(":");
if (parts.length > 1) {
return Long.parseLong(parts[1]);
}
} else {
return Long.parseLong(loginIdStr);
}
} else if (loginId instanceof Number) {
return ((Number) loginId).longValue();
}
} catch (Exception e) {
log.error("❌ 获取用户ID失败: {}", e.getMessage());
throw new RuntimeException("无法获取当前用户ID,请重新登录");
}
throw new RuntimeException("无法获取当前用户ID,请重新登录");
}
/**
@@ -0,0 +1,49 @@
package org.dromara.rs.domain;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 文档信息存储
*
* @author weizzzz
* @date 2025-01-03
*/
@Data
public class DocumentInfo {
/**
* 文档ID
*/
private String documentId;
/**
* 文件路径
*/
private String filePath;
/**
* 文件名
*/
private String fileName;
/**
* 申请ID
*/
private Long applicationId;
/**
* 文档编号
*/
private String documentNumber;
/**
* 文件大小
*/
private Long fileSize;
/**
* 创建时间
*/
private LocalDateTime createTime;
}
@@ -44,12 +44,6 @@ public class RsJob extends BaseEntity {
@ExcelProperty(value = "部门")
private String department;
/**
* 薪资范围
*/
@ExcelProperty(value = "薪资范围")
private String salaryRange;
/**
* 工作地点
*/
@@ -1,5 +1,6 @@
package org.dromara.rs.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -11,14 +12,12 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.ExcelIgnore;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
/**
* 出国申请对象 rs_overseas_application
*
* @author weizzzz
* @author weizzzz
* @date 2025-07-28
*/
@Data
@@ -35,6 +34,12 @@ public class RsOverseasApplication extends BaseEntity {
@ExcelProperty(value = "申请ID")
private Long applicationId;
/**
* 编号(自动生成的登记编号)
*/
@ExcelProperty(value = "编号")
private String serialNumber;
/**
* 申请人ID
*/
@@ -48,13 +53,134 @@ public class RsOverseasApplication extends BaseEntity {
private String applicantName;
/**
* 所属部门
* 性别:0-男,1-女
*/
@ExcelProperty(value = "性别")
private String gender;
/**
* 出生日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@ExcelProperty(value = "出生日期")
private Date birthDate;
/**
* 照片地址(2寸免冠白色或淡蓝色底彩照)
*/
@ExcelProperty(value = "照片")
private String photo;
/**
* 公民身份号码(18位身份证号)
*/
@ExcelProperty(value = "公民身份号码")
private String idNumber;
/**
* 政治面貌
*/
@ExcelProperty(value = "政治面貌")
private String politicalStatus;
/**
* 户籍所在地(填写至县市区)
*/
@ExcelProperty(value = "户籍所在地")
private String registeredAddress;
/**
* 所属部门(工作部门/教学系部)
*/
@ExcelProperty(value = "所属部门")
private String department;
/**
* 联系电话
* 职务职称
*/
@ExcelProperty(value = "职务职称")
private String position;
/**
* 职称
*/
@ExcelProperty(value = "职称")
private String professionalTitle;
/**
* 行政职级
*/
@ExcelProperty(value = "行政职级")
private String administrativeLevel;
/**
* 人员类别代码(根据报备人员范围中的情形项填写)
*/
@ExcelProperty(value = "人员类别代码")
private String personnelCategory;
/**
* 登记备案责任单位
*/
@ExcelProperty(value = "登记备案责任单位")
private String registrationUnit;
/**
* 登记备案责任单位负责人签名
*/
@ExcelProperty(value = "登记备案责任单位负责人签名")
private String unitLeaderSignature;
/**
* 办公电话
*/
@ExcelProperty(value = "办公电话")
private String officePhone;
/**
* 申领证件名称
*/
@ExcelProperty(value = "申领证件名称")
private String documentName;
/**
* 证件号码
*/
@ExcelProperty(value = "证件号码")
private String documentNumber;
/**
* 前往国家
*/
@ExcelProperty(value = "前往国家")
private String destinationCountry;
/**
* 出境事由
*/
@ExcelProperty(value = "出境事由")
private String exitReason;
/**
* 探望对象姓名及与申请者关系
*/
@ExcelProperty(value = "探望对象关系")
private String visitRelationship;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remarks;
/**
* 移动电话
*/
@ExcelProperty(value = "移动电话")
private String mobilePhone;
/**
* 联系电话(保留原字段兼容性)
*/
@ExcelProperty(value = "联系电话")
private String contactPhone;
@@ -103,12 +229,6 @@ public class RsOverseasApplication extends BaseEntity {
@ExcelProperty(value = "出国天数")
private Integer duration;
/**
* 预算金额
*/
@ExcelProperty(value = "预算金额")
private BigDecimal budget;
/**
* 邀请方信息
*/
@@ -139,6 +259,12 @@ public class RsOverseasApplication extends BaseEntity {
@ExcelProperty(value = "申请状态")
private String status;
/**
* 审批状态(工作流状态):cancel-已撤销,draft-草稿,waiting-待审核,finish-已完成,invalid-已作废,back-已退回,termination-已终止
*/
@ExcelProperty(value = "审批状态")
private String approvalStatus;
/**
* 当前审批人
*/
@@ -146,7 +272,13 @@ public class RsOverseasApplication extends BaseEntity {
private String currentApprover;
/**
* 审批级别:1-部门主管,2-分管领导,3-校领导
* 当前审批人ID
*/
@ExcelProperty(value = "当前审批人ID")
private Long currentApproverId;
/**
* 审批级别:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
*/
@ExcelProperty(value = "审批级别")
private String approvalLevel;
@@ -177,6 +309,93 @@ public class RsOverseasApplication extends BaseEntity {
@ExcelProperty(value = "申请人备注")
private String applicantRemark;
/**
* 应急联系人及电话
*/
@ExcelProperty(value = "应急联系人及电话")
private String emergencyContact;
/**
* 附件OSS ID列表(逗号分隔)
*/
@ExcelProperty(value = "附件ID列表")
private String attachmentIds;
/**
* 手写签字(base64格式)
*/
@ExcelProperty(value = "手写签字")
private String signature;
/**
* 登记备案表盖章文件路径
*/
@ExcelProperty(value = "登记备案表盖章文件路径")
private String stampedRegistrationFilePath;
/**
* 登记备案表盖章文件名
*/
@ExcelProperty(value = "登记备案表盖章文件名")
private String stampedRegistrationFileName;
/**
* 盖章文件上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "盖章文件上传时间")
private Date stampedFileUploadTime;
/**
* 盖章文件上传状态:0-未上传,1-已上传
*/
@ExcelProperty(value = "盖章文件上传状态")
private String stampedFileUploadStatus;
/**
* 盖章文件OSS ID(用于安全下载)
*/
@ExcelIgnore
private Long stampedFileOssId;
/**
* 党籍保留附件文件路径
*/
@ExcelProperty(value = "党籍保留附件文件路径")
private String partyRetentionFilePath;
/**
* 党籍保留附件文件名
*/
@ExcelProperty(value = "党籍保留附件文件名")
private String partyRetentionFileName;
/**
* 党籍保留附件上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "党籍保留附件上传时间")
private Date partyRetentionUploadTime;
/**
* 党籍保留附件上传状态:0-未上传,1-已上传
*/
@ExcelProperty(value = "党籍保留附件上传状态")
private String partyRetentionUploadStatus;
/**
* 党籍保留附件OSS ID(用于安全下载)
*/
@ExcelIgnore
private Long partyRetentionFileOssId;
/**
* 租户编号
*/
@TableField(fill = FieldFill.INSERT)
@ExcelIgnore
private String tenantId;
/**
* 基础字段:创建者
*/
@@ -203,5 +422,4 @@ public class RsOverseasApplication extends BaseEntity {
@ExcelIgnore
private Date updateTime;
}
}
@@ -0,0 +1,93 @@
package org.dromara.rs.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
/**
* 出国申请附件对象 rs_overseas_application_attachment
*
* @author weizzzz
* @date 2025-10-10
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("rs_overseas_application_attachment")
public class RsOverseasApplicationAttachment extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 附件ID(主键)
*/
@TableId(value = "attachment_id", type = IdType.ASSIGN_ID)
private Long attachmentId;
/**
* 出国申请ID
*/
private Long applicationId;
/**
* 附件标题
*/
private String attachmentTitle;
/**
* 附件内容说明
*/
private String attachmentContent;
/**
* 文件名称
*/
private String fileName;
/**
* 文件原始名称
*/
private String fileOriginalName;
/**
* 文件存储路径/OSS URL
*/
private String filePath;
/**
* 文件大小(字节)
*/
private Long fileSize;
/**
* 文件类型(如pdf、jpg、png、doc等)
*/
private String fileType;
/**
* MIME类型
*/
private String mimeType;
/**
* 上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date uploadTime;
/**
* 上传人ID
*/
private Long uploadUserId;
/**
* 上传人姓名
*/
private String uploadUserName;
}
@@ -54,7 +54,7 @@ public class RsOverseasApproval extends BaseEntity {
private String approverName;
/**
* 审批级别:1-部门主管,2-分管领导,3-校领导
* 审批级别:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
*/
@ExcelProperty(value = "审批级别")
private String approvalLevel;
@@ -71,6 +71,12 @@ public class RsOverseasApproval extends BaseEntity {
@ExcelProperty(value = "审批意见")
private String approvalOpinion;
/**
* 审批签字(base64图片数据)
*/
@ExcelIgnore
private String approvalSignature;
/**
* 审批时间
*/
@@ -118,5 +124,4 @@ public class RsOverseasApproval extends BaseEntity {
@ExcelIgnore
private Date updateTime;
}
}
@@ -0,0 +1,230 @@
package org.dromara.rs.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 出国行程变更对象 rs_overseas_itinerary_change
*
* @author zlwu
* @date 2025-01-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("rs_overseas_itinerary_change")
public class RsOverseasItineraryChange extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 变更ID(主键,雪花ID)
*/
@TableId(value = "change_id")
private Long changeId;
/**
* 关联备案记录ID(外键,关联rs_overseas_entry_exit_record.record_id)
*/
private Long recordId;
/**
* 关联出国申请ID(外键,关联rs_overseas_application.application_id,可为空)
*/
private Long applicationId;
/**
* 申请人ID(外键,关联sys_user.user_id)
*/
private Long applicantId;
/**
* 申请人姓名
*/
private String applicantName;
/**
* 性别:0-男,1-女
*/
private String gender;
/**
* 出生日期
*/
private Date birthDate;
/**
* 政治面貌
*/
private String politicalStatus;
/**
* 职称
*/
private String professionalTitle;
/**
* 职务
*/
private String position;
/**
* 申领证件名称
*/
private String documentName;
/**
* 证件号码
*/
private String documentNumber;
/**
* 部门
*/
private String department;
/**
* 原目的地国家
*/
private String originalDestination;
/**
* 原目的地城市
*/
private String originalDestinationCity;
/**
* 原出境事由
*/
private String originalExitReason;
/**
* 原计划开始日期
*/
private Date originalStartDate;
/**
* 原计划结束日期
*/
private Date originalEndDate;
/**
* 原计划天数
*/
private Integer originalDuration;
/**
* 原行程安排(详细)
*/
private String originalItinerary;
/**
* 原住宿计划
*/
private String originalAccommodationPlan;
/**
* 原交通计划
*/
private String originalTransportationPlan;
/**
* 变更类型(字典:rs_itinerary_change_type)
*/
private String changeType;
/**
* 新目的地国家
*/
private String newDestination;
/**
* 新目的地城市
*/
private String newDestinationCity;
/**
* 新出境事由
*/
private String newExitReason;
/**
* 新计划开始日期
*/
private Date newStartDate;
/**
* 新计划结束日期
*/
private Date newEndDate;
/**
* 新计划天数
*/
private Integer newDuration;
/**
* 新行程安排(详细)
*/
private String newItinerary;
/**
* 新住宿计划
*/
private String newAccommodationPlan;
/**
* 新交通计划
*/
private String newTransportationPlan;
/**
* 变更原因(必填)
*/
private String changeReason;
/**
* 备注
*/
private String remark;
/**
* 审批状态(字典:rs_itinerary_change_status,0:待审核 1:已通过 2:已拒绝 3:已撤回)
*/
private String status;
/**
* 审批意见
*/
private String approvalOpinion;
/**
* 审批时间
*/
private Date approvalTime;
/**
* 审批人ID
*/
private Long approverId;
/**
* 审批人姓名
*/
private String approverName;
/**
* 租户编号
*/
private String tenantId;
/**
* 删除标志(0代表存在 1代表删除)
*/
@TableLogic
private Integer deleted;
}
@@ -59,12 +59,24 @@ public class RsOverseasRecord extends BaseEntity {
@ExcelProperty(value = "所属部门")
private String department;
/**
* 职务
*/
@ExcelProperty(value = "职务")
private String position;
/**
* 联系电话
*/
@ExcelProperty(value = "联系电话")
private String contactPhone;
/**
* 联系邮箱
*/
@ExcelProperty(value = "联系邮箱")
private String contactEmail;
/**
* 出国目的
*/
@@ -115,6 +127,12 @@ public class RsOverseasRecord extends BaseEntity {
@ExcelProperty(value = "实际花费金额")
private BigDecimal actualBudget;
/**
* 经费来源
*/
@ExcelProperty(value = "经费来源")
private String budgetSource;
/**
* 邀请方信息
*/
@@ -0,0 +1,212 @@
package org.dromara.rs.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.ExcelIgnore;
import java.util.Date;
/**
* 出国登记对象 rs_overseas_registration
*
* @author weizzzz
* @date 2025-09-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("rs_overseas_registration")
public class RsOverseasRegistration extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 登记ID
*/
@TableId(value = "registration_id", type = IdType.AUTO)
@ExcelProperty(value = "登记ID")
private Long registrationId;
/**
* 关联申请ID
*/
@ExcelProperty(value = "关联申请ID")
private Long applicationId;
/**
* 姓名
*/
@ExcelProperty(value = "姓名")
private String name;
/**
* 性别:0-男,1-女
*/
@ExcelProperty(value = "性别")
private String gender;
/**
* 出生日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@ExcelProperty(value = "出生日期")
private Date birthDate;
/**
* 政治面貌
*/
@ExcelProperty(value = "政治面貌")
private String politicalStatus;
/**
* 身份证号(数据库字段:id_number)
*/
@TableField("id_number")
@ExcelProperty(value = "身份证号")
private String idNumber;
/**
* 户籍地址(数据库字段:registered_address)
*/
@TableField("registered_address")
@ExcelProperty(value = "户籍地址")
private String registeredAddress;
/**
* 单位部门(数据库字段:department)
*/
@ExcelProperty(value = "单位部门")
private String department;
/**
* 职务(数据库字段:position)
*/
@ExcelProperty(value = "职务")
private String position;
/**
* 行政级别(数据库字段:administrative_level)
*/
@TableField("administrative_level")
@ExcelProperty(value = "行政级别")
private String administrativeLevel;
/**
* 人员类别(数据库字段:personnel_category)
*/
@TableField("personnel_category")
@ExcelProperty(value = "人员类别")
private String personnelCategory;
// ========== 以下字段仅用于前端展示,数据库表中不存在 ==========
/**
* 工作单位及职务(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "工作单位及职务")
private String workUnitPosition;
/**
* 配偶姓名(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "配偶姓名")
private String spouse;
/**
* 联系电话(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "联系电话")
private String contactPhone;
/**
* 出国日期(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@JsonFormat(pattern = "yyyy-MM-dd")
@ExcelProperty(value = "出国日期")
private Date departureDate;
/**
* 预定返回日期(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@JsonFormat(pattern = "yyyy-MM-dd")
@ExcelProperty(value = "预定返回日期")
private Date returnDate;
/**
* 停留天数(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "停留天数")
private Integer stayDays;
/**
* 前往国家(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "前往国家")
private String destinationCountry;
/**
* 前往国家详细信息(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "前往国家详细信息")
private String destinationDetail;
/**
* 事由(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "事由")
private String reason;
/**
* 登记单位(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "登记单位")
private String registrationUnit;
/**
* 出国类型:1-因公出国,2-因私出国(前端字段,数据库无此字段)
*/
@TableField(exist = false)
@ExcelProperty(value = "出国类型")
private String travelType;
/**
* 状态:0-草稿,1-待审核,2-已通过,3-已拒绝
*/
@ExcelProperty(value = "状态")
private String status;
/**
* 提交时间(数据库字段:registration_time)
*/
@TableField("registration_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "提交时间")
private Date submitTime;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remarks;
// 注意:createBy、createTime、updateBy、updateTime 等基础字段已在父类 BaseEntity 中定义
// 父类中的字段带有 @TableField(fill = FieldFill.INSERT) 或 @TableField(fill = FieldFill.INSERT_UPDATE) 注解
// 可以自动填充创建者、创建时间、更新者、更新时间等信息
// 请勿在子类中重复定义这些字段,否则会覆盖父类的自动填充功能!
}
@@ -9,6 +9,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -50,6 +51,12 @@ public class RsTrainingApplication extends BaseEntity {
*/
private String userName;
/**
* 申请人昵称(用户真实姓名,非数据库字段)
*/
@TableField(exist = false)
private String nickName;
/**
* 所属部门
*/
@@ -60,6 +67,52 @@ public class RsTrainingApplication extends BaseEntity {
*/
private String position;
/**
* 现职称
*/
private String currentTitle;
/**
* 现岗位
*/
private String currentPosition;
/**
* 参加工作时间
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date workStartTime;
/**
* 联系电话
*/
private String phone;
/**
* 主办单位
*/
private String organizerUnit;
/**
* 培训类型
*/
private String trainingType;
/**
* 培训费用
*/
private BigDecimal trainingFee;
/**
* 同行人员(JSON格式)
*/
private String companions;
/**
* 培训目的
*/
private String trainingPurpose;
/**
* 申请理由
*/
@@ -80,23 +133,23 @@ public class RsTrainingApplication extends BaseEntity {
* 当前审批人
*/
private String currentApprover;
/**
* 培训地点
*/
private String location;
/**
* 学时
*/
private Integer duration;
/**
* 开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
/**
* 结束时间
*/
@@ -107,21 +160,51 @@ public class RsTrainingApplication extends BaseEntity {
* 租户ID
*/
private String tenantId;
/**
* 创建部门
*/
private Long createDept;
/**
* 拒绝时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date rejectTime;
/**
* 附件ID列表(逗号分隔)
*/
private String attachmentIds;
/**
* 当前审批级别(1-7,2a-教务处,2b-学生处)
*/
private String approvalLevel;
/**
* 当前审批人ID
*/
private Long currentApproverId;
/**
* 审批完成时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date approvalTime;
/**
* 排除状态(查询时排除指定状态,不存储到数据库)
*/
@TableField(exist = false)
private String excludeStatus;
}
/**
* 审批角色列表(查询条件,不存储到数据库)
* 用于根据用户角色过滤可见的审批申请
*/
@TableField(exist = false)
private java.util.List<String> approvalRoles;
/**
* 当前用户部门ID(查询条件,不存储到数据库)
* 用于第一阶段部门负责人权限过滤
*/
@TableField(exist = false)
private Long currentDeptId;
}
@@ -9,6 +9,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
import java.util.List;
/**
* 培训审批对象 rs_training_approval
@@ -69,11 +70,81 @@ public class RsTrainingApproval extends BaseEntity {
* 租户ID
*/
private String tenantId;
/** 非数据库字段 **/
/** 非数据库字段 - 审批记录列表显示 **/
@TableField(exist = false)
private String applicationUserName;
@TableField(exist = false)
private String courseTitle;
}
/** 非数据库字段 - 审批记录详情显示 **/
@TableField(exist = false)
private String userName;
@TableField(exist = false)
private String department;
@TableField(exist = false)
private String currentTitle;
@TableField(exist = false)
private String currentPosition;
@TableField(exist = false)
@JsonFormat(pattern = "yyyy-MM-dd")
private Date workStartTime;
@TableField(exist = false)
private String phone;
@TableField(exist = false)
private String organizerUnit;
@TableField(exist = false)
private String courseName;
@TableField(exist = false)
private String location;
@TableField(exist = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
@TableField(exist = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
@TableField(exist = false)
private String trainingType;
@TableField(exist = false)
private Double trainingFee;
@TableField(exist = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date applyTime;
@TableField(exist = false)
private String companions;
@TableField(exist = false)
private String trainingPurpose;
@TableField(exist = false)
private String reason;
@TableField(exist = false)
private String attachmentIds;
/** 非数据库字段 - 权限过滤辅助字段 **/
@TableField(exist = false)
private Long applicantId; // 申请人ID(用于权限过滤)
@TableField(exist = false)
private Long deptId; // 部门ID(用于部门权限过滤)
/** 🆕 新增:审批级别列表(用于多级审批过滤) **/
@TableField(exist = false)
private List<String> approvalLevels; // 审批级别列表,例如:["1", "4"]
}
@@ -116,7 +116,6 @@ public class RsTrainingRecord extends BaseEntity {
private String feedback;
private String tenantId;
private Long createDept;
/**
* 部门ID
@@ -127,4 +126,14 @@ public class RsTrainingRecord extends BaseEntity {
private String courseName;
private Integer duration;
private String location;
// 关联字段 - 从申请表关联查询(不存储在记录表中)
@TableField(exist = false)
private String organizerUnit; // 主办单位
@TableField(exist = false)
private String trainingType; // 培训类型
@TableField(exist = false)
private String companions; // 同行人员
}
@@ -0,0 +1,30 @@
package org.dromara.rs.domain.bo;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
/**
* 生成登记表请求BO
*
* @author weizzzz
* @date 2025-01-03
*/
@Data
public class GenerateRegistrationFormBo {
/**
* 申请ID
*/
@NotNull(message = "申请ID不能为空")
private Long applicationId;
/**
* 文档编号(可选,不传则自动生成)
*/
private String documentNumber;
/**
* 文档格式(固定为PDF)
*/
private String format = "pdf";
}
@@ -0,0 +1,85 @@
package org.dromara.rs.domain.bo;
import lombok.Data;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.util.Date;
/**
* 出国申请附件BO
*
* @author weizzzz
* @date 2025-10-10
*/
@Data
public class RsOverseasApplicationAttachmentBo extends BaseEntity {
/**
* 附件ID
*/
private Long attachmentId;
/**
* 出国申请ID
*/
@NotNull(message = "申请ID不能为空")
private Long applicationId;
/**
* 附件标题
*/
@NotBlank(message = "附件标题不能为空")
private String attachmentTitle;
/**
* 附件内容说明
*/
private String attachmentContent;
/**
* 文件名称
*/
private String fileName;
/**
* 文件原始名称
*/
private String fileOriginalName;
/**
* 文件存储路径/OSS URL
*/
private String filePath;
/**
* 文件大小(字节)
*/
private Long fileSize;
/**
* 文件类型
*/
private String fileType;
/**
* MIME类型
*/
private String mimeType;
/**
* 上传时间
*/
private Date uploadTime;
/**
* 上传人ID
*/
private Long uploadUserId;
/**
* 上传人姓名
*/
private String uploadUserName;
}
@@ -7,13 +7,12 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -36,36 +35,131 @@ public class RsOverseasApplicationBo extends BaseEntity {
private Long applicationId;
/**
* 申请人ID
* 编号(自动生成的登记编号)
*/
private String serialNumber;
/**
* 申请人ID(系统自动设置,关联当前登录用户)
*/
@NotNull(message = "申请人ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long applicantId;
/**
* 申请人姓名
*/
@NotBlank(message = "申请人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
private String applicantName;
/**
* 性别:0-男,1-女
*/
@NotBlank(message = "性别不能为空", groups = { AddGroup.class, EditGroup.class })
private String gender;
/**
* 出生日期
*/
@NotNull(message = "出生日期不能为空", groups = { AddGroup.class, EditGroup.class })
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthDate;
/**
* 照片地址
*/
private String photo;
/**
* 公民身份号码(18位身份证号)
* 注意:此字段为可选字段,不进行格式校验(因为可能使用护照等其他证件)
*/
private String idNumber;
/**
* 政治面貌
*/
@NotBlank(message = "政治面貌不能为空", groups = { AddGroup.class, EditGroup.class })
private String politicalStatus;
/**
* 户籍所在地(填写至县市区)
*/
@NotBlank(message = "户籍所在地不能为空", groups = { AddGroup.class, EditGroup.class })
private String registeredAddress;
/**
* 所属部门
*/
@NotBlank(message = "所属部门不能为空", groups = { EditGroup.class })
private String department;
/**
* 联系电话
* 职务职称
*/
@NotBlank(message = "职务职称不能为空", groups = { AddGroup.class, EditGroup.class })
private String position;
/**
* 职称
*/
private String professionalTitle;
/**
* 行政职级
*/
private String administrativeLevel;
/**
* 人员类别代码(根据报备人员范围中的情形项填写)
*/
@NotBlank(message = "人员类别代码不能为空", groups = { AddGroup.class, EditGroup.class })
private String personnelCategory;
/**
* 登记备案责任单位
*/
private String registrationUnit;
/**
* 登记备案责任单位负责人签名
*/
private String unitLeaderSignature;
/**
* 办公电话
*/
@Pattern(regexp = "^(\\d{3,4}-)?\\d{7,8}$", message = "办公电话格式不正确", groups = { AddGroup.class, EditGroup.class })
private String officePhone;
/**
* 申领证件名称
*/
private String documentName;
/**
* 证件号码
*/
private String documentNumber;
/**
* 移动电话
*/
@NotBlank(message = "移动电话不能为空", groups = { AddGroup.class, EditGroup.class })
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "移动电话格式不正确", groups = { AddGroup.class, EditGroup.class })
private String mobilePhone;
/**
* 联系电话(保留兼容性)
*/
private String contactPhone;
/**
* 出国目的
*/
@NotBlank(message = "出国目的不能为空", groups = { AddGroup.class, EditGroup.class })
private String purpose;
/**
* 目的详细说明
*/
@NotBlank(message = "目的详细说明不能为空", groups = { AddGroup.class, EditGroup.class })
private String purposeDetail;
/**
@@ -74,6 +168,21 @@ public class RsOverseasApplicationBo extends BaseEntity {
@NotBlank(message = "目的地国家不能为空", groups = { AddGroup.class, EditGroup.class })
private String destination;
/**
* 前往国家
*/
private String destinationCountry;
/**
* 出境事由
*/
private String exitReason;
/**
* 探望对象姓名及与申请者关系
*/
private String visitRelationship;
/**
* 开始日期
*/
@@ -94,13 +203,6 @@ public class RsOverseasApplicationBo extends BaseEntity {
@Min(value = 1, message = "出国天数必须大于0", groups = { AddGroup.class, EditGroup.class })
private Integer duration;
/**
* 预算金额
*/
@NotNull(message = "预算金额不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0.01", message = "预算金额必须大于0", groups = { AddGroup.class, EditGroup.class })
private BigDecimal budget;
/**
* 行程安排
*/
@@ -119,8 +221,6 @@ public class RsOverseasApplicationBo extends BaseEntity {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date approvalTime;
/**
* 目的地
*/
@@ -146,6 +246,26 @@ public class RsOverseasApplicationBo extends BaseEntity {
*/
private String applicantRemark;
/**
* 应急联系人及电话
*/
private String emergencyContact;
/**
* 附件OSS ID列表(逗号分隔)
*/
private String attachmentIds;
/**
* 手写签字(base64格式)
*/
private String signature;
/**
* 备注
*/
private String remarks;
/**
* 管理员备注
*/
@@ -157,7 +277,12 @@ public class RsOverseasApplicationBo extends BaseEntity {
private String currentApprover;
/**
* 审批级别
* 当前审批人ID
*/
private Long currentApproverId;
/**
* 审批级别:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
*/
private String approvalLevel;
@@ -165,4 +290,35 @@ public class RsOverseasApplicationBo extends BaseEntity {
* 申请状态
*/
private String status;
}
/**
* 审批状态(工作流状态):cancel-已撤销,draft-草稿,waiting-待审核,finish-已完成,invalid-已作废,back-已退回,termination-已终止
*/
private String approvalStatus;
/**
* 登记备案表盖章文件路径
*/
private String stampedRegistrationFilePath;
/**
* 登记备案表盖章文件名
*/
private String stampedRegistrationFileName;
/**
* 盖章文件上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date stampedFileUploadTime;
/**
* 盖章文件上传状态:0-未上传,1-已上传
*/
private String stampedFileUploadStatus;
/**
* 租户ID
*/
private String tenantId;
}
@@ -72,4 +72,9 @@ public class RsOverseasApprovalBo extends BaseEntity {
* 审批意见
*/
private String approvalOpinion;
}
/**
* 审批签字(base64图片数据)
*/
private String approvalSignature;
}
@@ -0,0 +1,238 @@
package org.dromara.rs.domain.bo;
import org.dromara.rs.domain.RsOverseasItineraryChange;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
/**
* 出国行程变更业务对象 rs_overseas_itinerary_change
*
* @author zlwu
* @date 2025-01-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = RsOverseasItineraryChange.class, reverseConvertGenerate = false)
public class RsOverseasItineraryChangeBo extends BaseEntity {
/**
* 变更ID(主键,雪花ID)
*/
@NotNull(message = "变更ID不能为空", groups = { EditGroup.class })
private Long changeId;
/**
* 关联备案记录ID(外键,关联rs_overseas_entry_exit_record.record_id)
* 注:该字段已废弃,现在使用applicationId
*/
private Long recordId;
/**
* 关联出国申请ID(外键,关联rs_overseas_application.application_id)
*/
@NotNull(message = "关联出国申请ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long applicationId;
/**
* 申请人ID(外键,关联sys_user.user_id)
*/
@NotNull(message = "申请人ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long applicantId;
/**
* 申请人姓名
*/
@NotBlank(message = "申请人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
private String applicantName;
/**
* 性别:0-男,1-女
*/
private String gender;
/**
* 出生日期
*/
private Date birthDate;
/**
* 政治面貌
*/
private String politicalStatus;
/**
* 职称
*/
private String professionalTitle;
/**
* 职务
*/
private String position;
/**
* 申领证件名称
*/
private String documentName;
/**
* 证件号码
*/
private String documentNumber;
/**
* 部门
*/
private String department;
/**
* 原目的地国家
*/
private String originalDestination;
/**
* 原目的地城市
*/
private String originalDestinationCity;
/**
* 原出境事由
*/
private String originalExitReason;
/**
* 原计划开始日期
*/
private Date originalStartDate;
/**
* 原计划结束日期
*/
private Date originalEndDate;
/**
* 原计划天数
*/
private Integer originalDuration;
/**
* 原行程安排(详细)
*/
private String originalItinerary;
/**
* 原住宿计划
*/
private String originalAccommodationPlan;
/**
* 原交通计划
*/
private String originalTransportationPlan;
/**
* 变更类型(字典:rs_itinerary_change_type)
*/
@NotBlank(message = "变更类型不能为空", groups = { AddGroup.class, EditGroup.class })
private String changeType;
/**
* 新目的地国家
*/
private String newDestination;
/**
* 新目的地城市
*/
private String newDestinationCity;
/**
* 新出境事由
*/
private String newExitReason;
/**
* 新计划开始日期
*/
private Date newStartDate;
/**
* 新计划结束日期
*/
private Date newEndDate;
/**
* 新计划天数
*/
private Integer newDuration;
/**
* 新行程安排(详细)
*/
private String newItinerary;
/**
* 新住宿计划
*/
private String newAccommodationPlan;
/**
* 新交通计划
*/
private String newTransportationPlan;
/**
* 变更原因(必填)
*/
@NotBlank(message = "变更原因不能为空", groups = { AddGroup.class, EditGroup.class })
private String changeReason;
/**
* 备注
*/
private String remark;
/**
* 审批状态(字典:rs_itinerary_change_status,0:待审核 1:已通过 2:已拒绝 3:已撤回)
*/
private String status;
/**
* 审批意见
*/
private String approvalOpinion;
/**
* 审批时间
*/
private Date approvalTime;
/**
* 审批人ID
*/
private Long approverId;
/**
* 审批人姓名
*/
private String approverName;
/**
* 租户编号
*/
private String tenantId;
/**
* 删除标志(0代表存在 1代表删除)
*/
private Integer deleted;
}
@@ -0,0 +1,175 @@
package org.dromara.rs.domain.bo;
import org.dromara.rs.domain.RsOverseasRegistration;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
/**
* 出国登记业务对象 rs_overseas_registration
*
* @author weizzzz
* @date 2025-09-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = RsOverseasRegistration.class, reverseConvertGenerate = false)
public class RsOverseasRegistrationBo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 登记ID
*/
@NotNull(message = "登记ID不能为空", groups = { EditGroup.class })
private Long registrationId;
/**
* 关联申请ID
*/
private Long applicationId;
/**
* 姓名
*/
@NotBlank(message = "姓名不能为空", groups = { AddGroup.class, EditGroup.class })
private String name;
/**
* 性别:0-男,1-女
*/
@NotBlank(message = "性别不能为空", groups = { AddGroup.class, EditGroup.class })
private String gender;
/**
* 出生日期
*/
@NotNull(message = "出生日期不能为空", groups = { AddGroup.class, EditGroup.class })
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthDate;
/**
* 政治面貌
*/
@NotBlank(message = "政治面貌不能为空", groups = { AddGroup.class, EditGroup.class })
private String politicalStatus;
/**
* 身份证号(数据库字段:id_number)
*/
@NotBlank(message = "身份证号不能为空", groups = { AddGroup.class, EditGroup.class })
private String idNumber;
/**
* 户籍地址(数据库字段:registered_address)
*/
@NotBlank(message = "户籍地址不能为空", groups = { AddGroup.class, EditGroup.class })
private String registeredAddress;
/**
* 单位部门(数据库字段:department)
*/
@NotBlank(message = "单位部门不能为空", groups = { AddGroup.class, EditGroup.class })
private String department;
/**
* 职务(数据库字段:position)
*/
@NotBlank(message = "职务不能为空", groups = { AddGroup.class, EditGroup.class })
private String position;
/**
* 行政级别(数据库字段:administrative_level)
*/
private String administrativeLevel;
/**
* 人员类别(数据库字段:personnel_category)
*/
private String personnelCategory;
// ========== 以下字段数据库表中不存在,仅用于前端展示,不做强制验证 ==========
/**
* 工作单位及职务(前端字段,数据库无此字段)
*/
private String workUnitPosition;
/**
* 配偶姓名(前端字段,数据库无此字段)
*/
private String spouse;
/**
* 联系电话(前端字段,数据库无此字段)
*/
private String contactPhone;
/**
* 出国日期(前端字段,数据库无此字段)
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date departureDate;
/**
* 预定返回日期(前端字段,数据库无此字段)
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date returnDate;
/**
* 停留天数(前端字段,数据库无此字段)
*/
private Integer stayDays;
/**
* 前往国家(前端字段,数据库无此字段)
*/
private String destinationCountry;
/**
* 前往国家详细信息(前端字段,数据库无此字段)
*/
private String destinationDetail;
/**
* 事由(前端字段,数据库无此字段)
*/
private String reason;
/**
* 登记单位(前端字段,数据库无此字段)
*/
private String registrationUnit;
/**
* 出国类型(前端字段,数据库无此字段)
*/
private String travelType;
/**
* 状态:0-草稿,1-待审核,2-已通过,3-已拒绝
*/
private String status;
/**
* 提交时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date submitTime;
/**
* 备注
*/
private String remarks;
}
@@ -33,6 +33,11 @@ public class RsOverseasApplicationQuery extends PageQuery {
*/
private String purpose;
/**
* 出境事由
*/
private String exitReason;
/**
* 目的地国家
*/
@@ -58,16 +63,6 @@ public class RsOverseasApplicationQuery extends PageQuery {
*/
private Long applicantId;
/**
* 预算金额范围 - 最小值
*/
private BigDecimal budgetMin;
/**
* 预算金额范围 - 最大值
*/
private BigDecimal budgetMax;
/**
* 申请时间范围 - 开始时间
*/
@@ -96,4 +91,10 @@ public class RsOverseasApplicationQuery extends PageQuery {
* 租户ID
*/
private String tenantId;
}
/**
* 🔴 申请人部门ID(用于部门负责人权限过滤)
* 部门负责人只能看到本部门员工提交的申请
*/
private Long applicantDeptId;
}
@@ -0,0 +1,73 @@
package org.dromara.rs.domain.query;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Date;
/**
* 出国申请审批监控查询对象
*
* @author Cursor AI
* @date 2025-10-21
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RsOverseasApprovalMonitorQuery extends PageQuery {
private static final long serialVersionUID = 1L;
/**
* 申请人姓名(模糊查询)
*/
private String applicantName;
/**
* 所属部门(模糊查询)
*/
private String department;
/**
* 目的地国家(模糊查询)
*/
private String destination;
/**
* 申请状态:0-待审核,1-已通过,2-已拒绝,3-已取消,4-已完成
*/
private String status;
/**
* 审批级别:1-部门主管,2-分管领导,3-校领导
*/
private String approvalLevel;
/**
* 当前审批人(模糊查询)
*/
private String currentApprover;
/**
* 申请时间范围 - 开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date applyTimeStart;
/**
* 申请时间范围 - 结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date applyTimeEnd;
/**
* 是否超时
*/
private Boolean isOverdue;
/**
* 租户ID
*/
private String tenantId;
}
@@ -63,4 +63,33 @@ public class RsOverseasApprovalQuery extends PageQuery {
* 租户ID
*/
private String tenantId;
}
/**
* 申请人ID(用于权限过滤)
*/
private Long applicantId;
/**
* 部门ID(用于部门权限过滤)
*/
private Long deptId;
@Override
public String toString() {
return "RsOverseasApprovalQuery{" +
"applicationId=" + applicationId +
", approverId=" + approverId +
", approverName='" + approverName + '\'' +
", approvalLevel='" + approvalLevel + '\'' +
", approvalStatus='" + approvalStatus + '\'' +
", approvalTimeBegin=" + approvalTimeBegin +
", approvalTimeEnd=" + approvalTimeEnd +
", applicantName='" + applicantName + '\'' +
", tenantId='" + tenantId + '\'' +
", applicantId=" + applicantId +
", deptId=" + deptId +
", pageNum=" + getPageNum() +
", pageSize=" + getPageSize() +
'}';
}
}
@@ -0,0 +1,83 @@
package org.dromara.rs.domain.query;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.io.Serial;
import java.util.Date;
/**
* 出国行程变更申请查询对象 rs_overseas_itinerary_change
*
* @author weizzzz
* @date 2025-10-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RsOverseasItineraryChangeQuery extends PageQuery {
@Serial
private static final long serialVersionUID = 1L;
/**
* 关联出国记录ID
*/
private Long recordId;
/**
* 原出国申请ID
*/
private Long applicationId;
/**
* 申请人ID
*/
private Long applicantId;
/**
* 申请人姓名(模糊查询)
*/
private String applicantName;
/**
* 部门(模糊查询)
*/
private String department;
/**
* 变更状态
*/
private String status;
/**
* 变更类型
*/
private String changeType;
/**
* 创建时间开始
*/
private Date createTimeStart;
/**
* 创建时间结束
*/
private Date createTimeEnd;
/**
* 审批时间开始
*/
private Date approvalTimeStart;
/**
* 审批时间结束
*/
private Date approvalTimeEnd;
/**
* 租户ID
*/
private String tenantId;
}
@@ -0,0 +1,132 @@
package org.dromara.rs.domain.query;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
/**
* 出国登记查询对象 rs_overseas_registration
*
* @author weizzzz
* @date 2025-09-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RsOverseasRegistrationQuery extends PageQuery {
private static final long serialVersionUID = 1L;
/**
* 登记ID
*/
private Long registrationId;
/**
* 关联申请ID
*/
private Long applicationId;
/**
* 姓名(模糊查询)
*/
private String name;
/**
* 性别
*/
private String gender;
/**
* 政治面貌
*/
private String politicalStatus;
/**
* 身份证号(模糊查询)
*/
private String idNumber;
/**
* 工作部门(模糊查询)
*/
private String department;
/**
* 职务职称(模糊查询)
*/
private String position;
/**
* 行政级别
*/
private String administrativeLevel;
/**
* 人员类别代码
*/
private String personnelCategory;
/**
* 前往国家(模糊查询)
*/
private String destinationCountry;
/**
* 登记单位(模糊查询)
*/
private String registrationUnit;
/**
* 出国类型
*/
private String travelType;
/**
* 状态
*/
private String status;
/**
* 出国日期-开始
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date departureDateStart;
/**
* 出国日期-结束
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date departureDateEnd;
/**
* 登记时间-开始(前端传参)
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date registrationTimeStart;
/**
* 登记时间-结束(前端传参)
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date registrationTimeEnd;
/**
* 提交时间-开始
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date submitTimeStart;
/**
* 提交时间-结束
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date submitTimeEnd;
/**
* 创建者(用于查询我的登记)
*/
private Long createBy;
}
@@ -0,0 +1,55 @@
package org.dromara.rs.domain.vo;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 登记表PDF生成响应VO
*
* @author weizzzz
* @date 2025-01-03
*/
@Data
public class RegistrationFormVO {
/**
* 文档唯一ID
*/
private String documentId;
/**
* 文件名
*/
private String fileName;
/**
* 下载链接
*/
private String downloadUrl;
/**
* 预览链接
*/
private String previewUrl;
/**
* 文件大小(字节)
*/
private Long fileSize;
/**
* 生成时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String generateTime;
/**
* 文档编号
*/
private String documentNumber;
/**
* 申请ID
*/
private Long applicationId;
}
@@ -17,7 +17,7 @@ import java.util.List;
public class RsApplicationVo extends RsApplication {
private static final long serialVersionUID = 1L;
/**
* 创建者
*/
@@ -37,64 +37,64 @@ public class RsApplicationVo extends RsApplication {
* 更新时间
*/
private Date updateTime;
/**
* 部门
*/
private String department;
/**
* 应聘者手机(兼容旧代码,实际使用contactPhone)
*/
private String applicantPhone;
/**
* 应聘者邮箱(兼容旧代码,实际使用contactEmail)
*/
private String applicantEmail;
/**
* 教育经历列表
*/
private List<RsEducationVo> educationList;
/**
* 工作经历列表
*/
private List<RsWorkExperienceVo> experienceList;
/**
* 状态名称
*/
private String statusName;
/**
* 获取应聘者手机号
*/
public String getApplicantPhone() {
return this.getContactPhone();
return this.applicantPhone != null ? this.applicantPhone : super.getContactPhone();
}
/**
* 设置应聘者手机号
*/
public void setApplicantPhone(String applicantPhone) {
this.setContactPhone(applicantPhone);
super.setContactPhone(applicantPhone);
this.applicantPhone = applicantPhone;
}
/**
* 获取应聘者邮箱
*/
public String getApplicantEmail() {
return this.getContactEmail();
return this.applicantEmail != null ? this.applicantEmail : super.getContactEmail();
}
/**
* 设置应聘者邮箱
*/
public void setApplicantEmail(String applicantEmail) {
this.setContactEmail(applicantEmail);
super.setContactEmail(applicantEmail);
this.applicantEmail = applicantEmail;
}
}
}
@@ -0,0 +1,112 @@
package org.dromara.rs.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 出国申请附件VO
*
* @author weizzzz
* @date 2025-10-10
*/
@Data
public class RsOverseasApplicationAttachmentVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 附件ID
*/
private Long attachmentId;
/**
* 出国申请ID
*/
private Long applicationId;
/**
* 附件标题
*/
private String attachmentTitle;
/**
* 附件内容说明
*/
private String attachmentContent;
/**
* 文件名称
*/
private String fileName;
/**
* 文件原始名称
*/
private String fileOriginalName;
/**
* 文件存储路径/OSS URL
*/
private String filePath;
/**
* 文件大小(字节)
*/
private Long fileSize;
/**
* 文件大小(格式化)
*/
private String fileSizeFormatted;
/**
* 文件类型
*/
private String fileType;
/**
* MIME类型
*/
private String mimeType;
/**
* 上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date uploadTime;
/**
* 上传人ID
*/
private Long uploadUserId;
/**
* 上传人姓名
*/
private String uploadUserName;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -7,7 +7,6 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -28,6 +27,11 @@ public class RsOverseasApplicationVo implements Serializable {
*/
private Long applicationId;
/**
* 编号(自动生成的登记编号)
*/
private String serialNumber;
/**
* 申请人ID
*/
@@ -38,11 +42,112 @@ public class RsOverseasApplicationVo implements Serializable {
*/
private String applicantName;
/**
* 性别
*/
private String gender;
/**
* 性别文本
*/
private String genderText;
/**
* 出生日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthDate;
/**
* 照片地址
*/
private String photo;
/**
* 公民身份号码
*/
private String idNumber;
/**
* 政治面貌
*/
private String politicalStatus;
/**
* 政治面貌文本
*/
private String politicalStatusText;
/**
* 户籍所在地
*/
private String registeredAddress;
/**
* 所属部门
*/
private String department;
/**
* 职务职称
*/
private String position;
/**
* 职称
*/
private String professionalTitle;
/**
* 行政职级
*/
private String administrativeLevel;
/**
* 行政职级文本
*/
private String administrativeLevelText;
/**
* 人员类别代码
*/
private String personnelCategory;
/**
* 人员类别代码文本
*/
private String personnelCategoryText;
/**
* 登记备案责任单位
*/
private String registrationUnit;
/**
* 登记备案责任单位负责人签名
*/
private String unitLeaderSignature;
/**
* 办公电话
*/
private String officePhone;
/**
* 申领证件名称
*/
private String documentName;
/**
* 证件号码
*/
private String documentNumber;
/**
* 移动电话
*/
private String mobilePhone;
/**
* 联系电话
*/
@@ -68,6 +173,21 @@ public class RsOverseasApplicationVo implements Serializable {
*/
private String destination;
/**
* 前往国家
*/
private String destinationCountry;
/**
* 出境事由
*/
private String exitReason;
/**
* 探望对象姓名及与申请者关系
*/
private String visitRelationship;
/**
* 目的地
*/
@@ -90,11 +210,6 @@ public class RsOverseasApplicationVo implements Serializable {
*/
private Integer duration;
/**
* 预算金额
*/
private BigDecimal budget;
/**
* 邀请函信息
*/
@@ -125,13 +240,33 @@ public class RsOverseasApplicationVo implements Serializable {
*/
private String statusText;
/**
* 审批状态(工作流状态):cancel-已撤销,draft-草稿,waiting-待审核,finish-已完成,invalid-已作废,back-已退回,termination-已终止
*/
private String approvalStatus;
/**
* 审批状态文本
*/
private String approvalStatusText;
/**
* 工作流任务ID(用于前端判断是否显示审批按钮)
*/
private Long taskId;
/**
* 当前审批人
*/
private String currentApprover;
/**
* 审批级别
* 当前审批人ID
*/
private Long currentApproverId;
/**
* 审批级别:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
*/
private String approvalLevel;
@@ -157,11 +292,83 @@ public class RsOverseasApplicationVo implements Serializable {
*/
private String applicantRemark;
/**
* 应急联系人及电话
*/
private String emergencyContact;
/**
* 附件OSS ID列表(逗号分隔)
*/
private String attachmentIds;
/**
* 手写签字(base64格式)
*/
private String signature;
/**
* 备注
*/
private String remarks;
/**
* 管理员备注
*/
private String adminRemark;
/**
* 登记备案表盖章文件路径
*/
private String stampedRegistrationFilePath;
/**
* 登记备案表盖章文件名
*/
private String stampedRegistrationFileName;
/**
* 盖章文件上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date stampedFileUploadTime;
/**
* 盖章文件上传状态:0-未上传,1-已上传
*/
private String stampedFileUploadStatus;
/**
* 盖章文件OSS ID(用于安全下载)
*/
private Long stampedFileOssId;
/**
* 党籍保留附件文件路径
*/
private String partyRetentionFilePath;
/**
* 党籍保留附件文件名
*/
private String partyRetentionFileName;
/**
* 党籍保留附件上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date partyRetentionUploadTime;
/**
* 党籍保留附件上传状态:0-未上传,1-已上传
*/
private String partyRetentionUploadStatus;
/**
* 党籍保留附件OSS ID(用于安全下载)
*/
private Long partyRetentionFileOssId;
/**
* 租户编号
*/
@@ -198,4 +405,4 @@ public class RsOverseasApplicationVo implements Serializable {
* 获取经费来源文本
*/
}
}
@@ -0,0 +1,339 @@
package org.dromara.rs.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 出国申请审批监控视图对象
* <p>
* 用于审批监控页面展示,包含申请信息和审批流程信息
*
* @author Cursor AI
* @date 2025-10-21
*/
@Data
public class RsOverseasApprovalMonitorVo implements Serializable {
private static final long serialVersionUID = 1L;
// ==================== 申请基本信息 ====================
/**
* 申请ID
*/
@ExcelProperty(value = "申请ID")
private Long applicationId;
/**
* 出国记录ID(已完成的申请会有对应的记录ID)
*/
private Long recordId;
/**
* 编号(自动生成的登记编号)
*/
@ExcelProperty(value = "编号")
private String serialNumber;
/**
* 申请人ID
*/
private Long applicantId;
/**
* 申请人姓名
*/
@ExcelProperty(value = "申请人姓名")
private String applicantName;
/**
* 性别:0-男,1-女
*/
@ExcelProperty(value = "性别")
private String gender;
/**
* 性别文本(用于前端显示)
*/
private String genderText;
/**
* 出生日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@ExcelProperty(value = "出生日期")
private Date birthDate;
/**
* 政治面貌
*/
@ExcelProperty(value = "政治面貌")
private String politicalStatus;
/**
* 政治面貌文本(用于前端显示)
*/
private String politicalStatusText;
/**
* 所属部门
*/
@ExcelProperty(value = "所属部门")
private String department;
/**
* 职称
*/
@ExcelProperty(value = "职称")
private String professionalTitle;
/**
* 职务职称
*/
@ExcelProperty(value = "职务职称")
private String position;
// ==================== 证件信息 ====================
/**
* 申领证件名称
*/
@ExcelProperty(value = "申领证件名称")
private String documentName;
/**
* 证件号码
*/
@ExcelProperty(value = "证件号码")
private String documentNumber;
// ==================== 出国信息 ====================
/**
* 出国目的
*/
@ExcelProperty(value = "出国目的")
private String purpose;
/**
* 目的地国家(兼容字段)
*/
@ExcelProperty(value = "目的地国家")
private String destination;
/**
* 前往国家
*/
@ExcelProperty(value = "前往国家")
private String destinationCountry;
/**
* 目的地城市
*/
@ExcelProperty(value = "目的地城市")
private String destinationCity;
/**
* 出境事由
*/
@ExcelProperty(value = "出境事由")
private String exitReason;
/**
* 探望对象姓名及与申请者关系(仅探亲时)
*/
@ExcelProperty(value = "探望对象关系")
private String visitRelationship;
/**
* 行程安排
*/
@ExcelProperty(value = "行程安排")
private String itinerary;
/**
* 开始日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@ExcelProperty(value = "开始日期")
private Date startDate;
/**
* 结束日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@ExcelProperty(value = "结束日期")
private Date endDate;
/**
* 出国天数
*/
@ExcelProperty(value = "出国天数")
private Integer duration;
// ==================== 联系信息 ====================
/**
* 移动电话
*/
@ExcelProperty(value = "移动电话")
private String mobilePhone;
/**
* 联系电话(兼容字段)
*/
@ExcelProperty(value = "联系电话")
private String contactPhone;
/**
* 应急联系人及电话
*/
@ExcelProperty(value = "应急联系人及电话")
private String emergencyContact;
// ==================== 审批状态信息 ====================
/**
* 申请状态:0-待审核,1-已通过,2-已拒绝,3-已取消,4-已完成
*/
@ExcelProperty(value = "申请状态")
private String status;
/**
* 申请状态文本(用于前端显示)
*/
@ExcelProperty(value = "申请状态")
private String statusText;
/**
* 当前审批人
*/
@ExcelProperty(value = "当前审批人")
private String currentApprover;
/**
* 当前审批人ID
*/
private Long currentApproverId;
/**
* 审批级别:1-部门主管,2-分管领导,3-校领导
*/
@ExcelProperty(value = "审批级别")
private String approvalLevel;
/**
* 审批级别文本(用于前端显示)
*/
@ExcelProperty(value = "当前审批环节")
private String approvalLevelText;
/**
* 申请时间(提交时间)
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "申请时间")
private Date applyTime;
/**
* 审批时间(最后一次审批时间)
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "审批时间")
private Date approvalTime;
// ==================== 审批进度信息 ====================
/**
* 审批进度:例如 "2/3"(已完成2级,共3级)
*/
@ExcelProperty(value = "审批进度")
private String approvalProgress;
/**
* 已审批级别数
*/
private Integer approvedLevels;
/**
* 总审批级别数
*/
private Integer totalLevels;
/**
* 审批耗时(天)
*/
@ExcelProperty(value = "审批耗时(天)")
private Integer approvalDays;
/**
* 是否超时(根据业务规则判断)
*/
private Boolean isOverdue;
// ==================== 审批历史记录 ====================
/**
* 审批历史记录列表
*/
private List<RsOverseasApprovalVo> approvalHistory;
/**
* 最近一次审批意见
*/
@ExcelProperty(value = "最近审批意见")
private String latestApprovalOpinion;
/**
* 最近一次审批人
*/
@ExcelProperty(value = "最近审批人")
private String latestApproverName;
/**
* 最近一次审批时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "最近审批时间")
private Date latestApprovalTime;
// ==================== 其他信息 ====================
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remarks;
/**
* 管理员备注
*/
private String adminRemark;
/**
* 申请人备注
*/
private String applicantRemark;
/**
* 租户编号
*/
private String tenantId;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
Loaded 100 of 392 files, more files were not shown because too many files have changed in this diff. Show more