提交代码
This commit is contained in:
1 parent
4e34c7eb36
commit
375a2c8df7
54 files changed
+4631
-4352
No files matched your search
@@ -1,2 +0,0 @@
|
|||||||
# 人事管理模块 - 控制器层
|
|
||||||
# 此目录用于存放REST API控制器类
|
|
||||||
+41
-27
@@ -24,6 +24,7 @@ import org.dromara.common.core.domain.R;
|
|||||||
import org.dromara.common.core.validate.AddGroup;
|
import org.dromara.common.core.validate.AddGroup;
|
||||||
import org.dromara.common.core.validate.EditGroup;
|
import org.dromara.common.core.validate.EditGroup;
|
||||||
import org.dromara.common.excel.utils.ExcelUtil;
|
import org.dromara.common.excel.utils.ExcelUtil;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.dev33.satoken.annotation.SaMode;
|
import cn.dev33.satoken.annotation.SaMode;
|
||||||
@@ -97,6 +98,8 @@ public class RsOverseasApplicationController extends BaseController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询当前用户的待审批申请列表(五级审批模式)
|
* 查询当前用户的待审批申请列表(五级审批模式)
|
||||||
|
*
|
||||||
|
* @deprecated 已废弃,请使用 pendingPageList 方法(支持分页和搜索)
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseasapproval:list")
|
@SaCheckPermission("rs:overseasapproval:list")
|
||||||
@GetMapping("/pending/list")
|
@GetMapping("/pending/list")
|
||||||
@@ -105,6 +108,30 @@ public class RsOverseasApplicationController extends BaseController {
|
|||||||
return R.ok(list);
|
return R.ok(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前用户的待审批申请分页列表(根据角色过滤,支持分页和搜索)
|
||||||
|
*
|
||||||
|
* 🔴 核心逻辑:
|
||||||
|
* 1. 根据用户角色确定审批级别列表
|
||||||
|
* 2. 部门负责人(rs_bmfzr)只能看到本部门的申请
|
||||||
|
* 3. 其他角色可以看到全校的申请
|
||||||
|
* 4. 支持分页和搜索(前往国家、出境事由)
|
||||||
|
* 5. 只返回status='0'(待审核)的申请
|
||||||
|
* 6. 按创建时间倒序排列
|
||||||
|
*
|
||||||
|
* 角色映射:
|
||||||
|
* - rs_bmfzr → approval_level='1' + 部门过滤
|
||||||
|
* - hr_dept_role → approval_level='2'
|
||||||
|
* - discipline_committee_role → approval_level='3'
|
||||||
|
* - college_leader_role → approval_level='4'
|
||||||
|
* - party_secretary_role → approval_level='5'
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("rs:overseasapproval:list")
|
||||||
|
@GetMapping("/pending/page")
|
||||||
|
public TableDataInfo<RsOverseasApplicationVo> pendingPageList(RsOverseasApplicationQuery query) {
|
||||||
|
return applicationService.queryPendingPageListByRole(query);
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 五级审批模式说明
|
// 五级审批模式说明
|
||||||
//
|
//
|
||||||
@@ -321,7 +348,7 @@ public class RsOverseasApplicationController extends BaseController {
|
|||||||
@PostMapping("/approve")
|
@PostMapping("/approve")
|
||||||
public R<Void> approve(@RequestBody ApprovalRequest request) {
|
public R<Void> approve(@RequestBody ApprovalRequest request) {
|
||||||
Long approverId = LoginHelper.getUserId();
|
Long approverId = LoginHelper.getUserId();
|
||||||
String approverName = LoginHelper.getUsername();
|
String approverName = StringUtils.defaultIfBlank(LoginHelper.getNickname(), LoginHelper.getUsername());
|
||||||
|
|
||||||
// 🔴 标准工作流模式:支持任务ID参数
|
// 🔴 标准工作流模式:支持任务ID参数
|
||||||
// 如果传入了taskId,将通过工作流引擎进行审批
|
// 如果传入了taskId,将通过工作流引擎进行审批
|
||||||
@@ -488,12 +515,6 @@ public class RsOverseasApplicationController extends BaseController {
|
|||||||
public R<Map<String, Object>> uploadStampedFile(@RequestParam("file") MultipartFile file,
|
public R<Map<String, Object>> uploadStampedFile(@RequestParam("file") MultipartFile file,
|
||||||
@RequestParam("applicationId") Long applicationId) {
|
@RequestParam("applicationId") Long applicationId) {
|
||||||
try {
|
try {
|
||||||
// 验证文件格式
|
|
||||||
String contentType = file.getContentType();
|
|
||||||
if (!isValidFileType(contentType)) {
|
|
||||||
return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证文件大小(10MB)
|
// 验证文件大小(10MB)
|
||||||
long maxSize = 10 * 1024 * 1024; // 10MB
|
long maxSize = 10 * 1024 * 1024; // 10MB
|
||||||
if (file.getSize() > maxSize) {
|
if (file.getSize() > maxSize) {
|
||||||
@@ -561,12 +582,6 @@ public class RsOverseasApplicationController extends BaseController {
|
|||||||
public R<Map<String, Object>> uploadPartyRetentionFile(@RequestParam("file") MultipartFile file,
|
public R<Map<String, Object>> uploadPartyRetentionFile(@RequestParam("file") MultipartFile file,
|
||||||
@RequestParam("applicationId") Long applicationId) {
|
@RequestParam("applicationId") Long applicationId) {
|
||||||
try {
|
try {
|
||||||
// 验证文件格式
|
|
||||||
String contentType = file.getContentType();
|
|
||||||
if (!isValidFileType(contentType)) {
|
|
||||||
return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证文件大小(10MB)
|
// 验证文件大小(10MB)
|
||||||
long maxSize = 10 * 1024 * 1024; // 10MB
|
long maxSize = 10 * 1024 * 1024; // 10MB
|
||||||
if (file.getSize() > maxSize) {
|
if (file.getSize() > maxSize) {
|
||||||
@@ -583,7 +598,7 @@ public class RsOverseasApplicationController extends BaseController {
|
|||||||
ossResult.getOriginalName(), // 原始文件名
|
ossResult.getOriginalName(), // 原始文件名
|
||||||
new Date(), // 上传时间
|
new Date(), // 上传时间
|
||||||
"1", // 上传状态:已上传
|
"1", // 上传状态:已上传
|
||||||
ossResult.getOssId() // OSS文件ID
|
String.valueOf(ossResult.getOssId()) // OSS文件ID - 转换为字符串
|
||||||
);
|
);
|
||||||
|
|
||||||
// 返回结果
|
// 返回结果
|
||||||
@@ -616,25 +631,24 @@ public class RsOverseasApplicationController extends BaseController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理多个OSS ID的情况(逗号分隔),取第一个ID进行下载
|
||||||
|
String ossIdStr = application.getPartyRetentionFileOssId();
|
||||||
|
Long ossId;
|
||||||
|
if (ossIdStr.contains(",")) {
|
||||||
|
// 如果包含逗号,取第一个ID
|
||||||
|
ossId = Long.parseLong(ossIdStr.split(",")[0].trim());
|
||||||
|
} else {
|
||||||
|
// 单个ID直接解析
|
||||||
|
ossId = Long.parseLong(ossIdStr);
|
||||||
|
}
|
||||||
|
|
||||||
// 使用系统OSS服务进行安全下载
|
// 使用系统OSS服务进行安全下载
|
||||||
ossService.secureDownload(application.getPartyRetentionFileOssId(), response);
|
ossService.secureDownload(ossId, response);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件扩展名
|
* 获取文件扩展名
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
-10
@@ -62,17 +62,13 @@ public class RsOverseasItineraryChangeController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔴 标准化改造:删除自定义待审批查询接口
|
* 查询待审批的行程变更申请列表(status=0)
|
||||||
*
|
|
||||||
* 原因:出国行程变更工作流应使用标准工作流模式
|
|
||||||
* 用户应通过工作流API pageByTaskWait() 获取待办任务
|
|
||||||
* 而不是调用自定义的业务API
|
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:itineraryChange:approve")
|
@SaCheckPermission("rs:itineraryChange:approve")
|
||||||
@GetMapping("/pending/list")
|
@GetMapping("/pending/list")
|
||||||
public R<String> pendingList(RsOverseasItineraryChangeQuery query) {
|
public TableDataInfo<RsOverseasItineraryChangeVo> pendingList(RsOverseasItineraryChangeQuery query) {
|
||||||
// 提示用户使用标准工作流API
|
query.setTenantId(LoginHelper.getTenantId());
|
||||||
return R.fail("请使用标准工作流待办页面查看审批任务。访问:工作流 > 我的任务 > 待办任务");
|
return changeService.queryPendingList(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,7 +163,7 @@ public class RsOverseasItineraryChangeController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 导出行程变更申请列表
|
* 导出行程变更申请列表
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:itineraryChange:export")
|
// @SaCheckPermission("rs:itineraryChange:export")
|
||||||
@Log(title = "行程变更申请", businessType = BusinessType.EXPORT)
|
@Log(title = "行程变更申请", businessType = BusinessType.EXPORT)
|
||||||
@PostMapping("/export")
|
@PostMapping("/export")
|
||||||
public void export(RsOverseasItineraryChangeQuery query, HttpServletResponse response) {
|
public void export(RsOverseasItineraryChangeQuery query, HttpServletResponse response) {
|
||||||
@@ -179,7 +175,7 @@ public class RsOverseasItineraryChangeController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 查询某个出国记录的所有变更历史
|
* 查询某个出国记录的所有变更历史
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:itineraryChange:query")
|
// @SaCheckPermission("rs:itineraryChange:query")
|
||||||
@GetMapping("/record/{recordId}/history")
|
@GetMapping("/record/{recordId}/history")
|
||||||
public R<List<RsOverseasItineraryChangeVo>> getChangeHistory(@PathVariable Long recordId) {
|
public R<List<RsOverseasItineraryChangeVo>> getChangeHistory(@PathVariable Long recordId) {
|
||||||
return R.ok(changeService.queryByRecordId(recordId));
|
return R.ok(changeService.queryByRecordId(recordId));
|
||||||
|
|||||||
+29
-37
@@ -32,7 +32,7 @@ import org.dromara.rs.service.IRsOverseasRegistrationService;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 出国登记Controller
|
* 出国登记Controller
|
||||||
*
|
*
|
||||||
* @author weizzzz
|
* @author weizzzz
|
||||||
* @date 2025-09-30
|
* @date 2025-09-30
|
||||||
*/
|
*/
|
||||||
@@ -47,7 +47,7 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 查询出国登记列表
|
* 查询出国登记列表
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:list")
|
// @SaCheckPermission("rs:overseasregistration:list")
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public TableDataInfo<RsOverseasRegistrationVo> list(RsOverseasRegistrationQuery query) {
|
public TableDataInfo<RsOverseasRegistrationVo> list(RsOverseasRegistrationQuery query) {
|
||||||
return registrationService.queryPageList(query);
|
return registrationService.queryPageList(query);
|
||||||
@@ -56,7 +56,7 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 查询我的出国登记列表
|
* 查询我的出国登记列表
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:list")
|
// @SaCheckPermission("rs:overseasregistration:list")
|
||||||
@GetMapping("/my/list")
|
@GetMapping("/my/list")
|
||||||
public TableDataInfo<RsOverseasRegistrationVo> myList(RsOverseasRegistrationQuery query) {
|
public TableDataInfo<RsOverseasRegistrationVo> myList(RsOverseasRegistrationQuery query) {
|
||||||
return registrationService.queryMyRegistrationList(query);
|
return registrationService.queryMyRegistrationList(query);
|
||||||
@@ -65,7 +65,7 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 导出出国登记列表
|
* 导出出国登记列表
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:export")
|
// @SaCheckPermission("rs:overseasregistration:export")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.EXPORT)
|
@Log(title = "出国登记", businessType = BusinessType.EXPORT)
|
||||||
@PostMapping("/export")
|
@PostMapping("/export")
|
||||||
public void export(RsOverseasRegistrationQuery query, HttpServletResponse response) {
|
public void export(RsOverseasRegistrationQuery query, HttpServletResponse response) {
|
||||||
@@ -76,27 +76,26 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 获取出国登记详细信息
|
* 获取出国登记详细信息
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:query")
|
// @SaCheckPermission("rs:overseasregistration:query")
|
||||||
@GetMapping("/{registrationId}")
|
@GetMapping("/{registrationId}")
|
||||||
public R<RsOverseasRegistrationVo> getInfo(@NotNull(message = "主键不能为空")
|
public R<RsOverseasRegistrationVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) {
|
||||||
@PathVariable Long registrationId) {
|
|
||||||
return R.ok(registrationService.queryById(registrationId));
|
return R.ok(registrationService.queryById(registrationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据申请ID获取出国登记信息
|
* 根据申请ID获取出国登记信息
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:query")
|
// @SaCheckPermission("rs:overseasregistration:query")
|
||||||
@GetMapping("/application/{applicationId}")
|
@GetMapping("/application/{applicationId}")
|
||||||
public R<RsOverseasRegistrationVo> getByApplicationId(@NotNull(message = "申请ID不能为空")
|
public R<RsOverseasRegistrationVo> getByApplicationId(
|
||||||
@PathVariable Long applicationId) {
|
@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) {
|
||||||
return R.ok(registrationService.queryByApplicationId(applicationId));
|
return R.ok(registrationService.queryByApplicationId(applicationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增出国登记
|
* 新增出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:add")
|
// @SaCheckPermission("rs:overseasregistration:add")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.INSERT)
|
@Log(title = "出国登记", businessType = BusinessType.INSERT)
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
|
public R<Void> add(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
|
||||||
@@ -106,7 +105,7 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 新增并提交出国登记
|
* 新增并提交出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:add")
|
// @SaCheckPermission("rs:overseasregistration:add")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.INSERT)
|
@Log(title = "出国登记", businessType = BusinessType.INSERT)
|
||||||
@PostMapping("/addAndSubmit")
|
@PostMapping("/addAndSubmit")
|
||||||
public R<Void> addAndSubmit(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
|
public R<Void> addAndSubmit(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
|
||||||
@@ -116,7 +115,7 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 修改出国登记
|
* 修改出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:edit")
|
// @SaCheckPermission("rs:overseasregistration:edit")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
|
public R<Void> edit(@Validated(EditGroup.class) @RequestBody RsOverseasRegistrationBo bo) {
|
||||||
@@ -126,67 +125,61 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 删除出国登记
|
* 删除出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:remove")
|
// @SaCheckPermission("rs:overseasregistration:remove")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.DELETE)
|
@Log(title = "出国登记", businessType = BusinessType.DELETE)
|
||||||
@DeleteMapping("/{registrationIds}")
|
@DeleteMapping("/{registrationIds}")
|
||||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] registrationIds) {
|
||||||
@PathVariable Long[] registrationIds) {
|
|
||||||
return toAjax(registrationService.deleteWithValidByIds(List.of(registrationIds), true));
|
return toAjax(registrationService.deleteWithValidByIds(List.of(registrationIds), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交出国登记
|
* 提交出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:submit")
|
// @SaCheckPermission("rs:overseasregistration:submit")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping("/submit/{registrationId}")
|
@PutMapping("/submit/{registrationId}")
|
||||||
public R<Void> submit(@NotNull(message = "主键不能为空")
|
public R<Void> submit(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) {
|
||||||
@PathVariable Long registrationId) {
|
|
||||||
return toAjax(registrationService.submitRegistration(registrationId));
|
return toAjax(registrationService.submitRegistration(registrationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 确认出国登记
|
* 确认出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:confirm")
|
// @SaCheckPermission("rs:overseasregistration:confirm")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping("/confirm/{registrationId}")
|
@PutMapping("/confirm/{registrationId}")
|
||||||
public R<Void> confirm(@NotNull(message = "主键不能为空")
|
public R<Void> confirm(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) {
|
||||||
@PathVariable Long registrationId) {
|
|
||||||
return toAjax(registrationService.confirmRegistration(registrationId));
|
return toAjax(registrationService.confirmRegistration(registrationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 撤回出国登记
|
* 撤回出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:withdraw")
|
// @SaCheckPermission("rs:overseasregistration:withdraw")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping("/withdraw/{registrationId}")
|
@PutMapping("/withdraw/{registrationId}")
|
||||||
public R<Void> withdraw(@NotNull(message = "主键不能为空")
|
public R<Void> withdraw(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) {
|
||||||
@PathVariable Long registrationId) {
|
|
||||||
return toAjax(registrationService.withdrawRegistration(registrationId));
|
return toAjax(registrationService.withdrawRegistration(registrationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于出国申请创建登记记录
|
* 基于出国申请创建登记记录
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:overseas:registration:create")
|
// @SaCheckPermission("rs:overseasregistration:create")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.INSERT)
|
@Log(title = "出国登记", businessType = BusinessType.INSERT)
|
||||||
@PostMapping("/create/{applicationId}")
|
@PostMapping("/create/{applicationId}")
|
||||||
public R<Void> createFromApplication(@NotNull(message = "申请ID不能为空")
|
public R<Void> createFromApplication(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) {
|
||||||
@PathVariable Long applicationId) {
|
|
||||||
return toAjax(registrationService.createFromApplication(applicationId));
|
return toAjax(registrationService.createFromApplication(applicationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批准出国登记
|
* 批准出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:registration:approve")
|
// @SaCheckPermission("rs:registration:approve")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping("/approve/{registrationId}")
|
@PutMapping("/approve/{registrationId}")
|
||||||
public R<Void> approve(@NotNull(message = "主键不能为空")
|
public R<Void> approve(@NotNull(message = "主键不能为空") @PathVariable Long registrationId,
|
||||||
@PathVariable Long registrationId,
|
@RequestBody(required = false) RsOverseasRegistrationBo bo) {
|
||||||
@RequestBody(required = false) RsOverseasRegistrationBo bo) {
|
|
||||||
String approveNotes = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "同意";
|
String approveNotes = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "同意";
|
||||||
return toAjax(registrationService.approveRegistration(registrationId, approveNotes));
|
return toAjax(registrationService.approveRegistration(registrationId, approveNotes));
|
||||||
}
|
}
|
||||||
@@ -194,13 +187,12 @@ public class RsOverseasRegistrationController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 拒绝出国登记
|
* 拒绝出国登记
|
||||||
*/
|
*/
|
||||||
@SaCheckPermission("rs:registration:reject")
|
// @SaCheckPermission("rs:registration:reject")
|
||||||
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
@Log(title = "出国登记", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping("/reject/{registrationId}")
|
@PutMapping("/reject/{registrationId}")
|
||||||
public R<Void> reject(@NotNull(message = "主键不能为空")
|
public R<Void> reject(@NotNull(message = "主键不能为空") @PathVariable Long registrationId,
|
||||||
@PathVariable Long registrationId,
|
@RequestBody(required = false) RsOverseasRegistrationBo bo) {
|
||||||
@RequestBody(required = false) RsOverseasRegistrationBo bo) {
|
|
||||||
String rejectReason = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "不同意";
|
String rejectReason = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "不同意";
|
||||||
return toAjax(registrationService.rejectRegistration(registrationId, rejectReason));
|
return toAjax(registrationService.rejectRegistration(registrationId, rejectReason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -384,10 +384,10 @@ public class RsOverseasApplication extends BaseEntity {
|
|||||||
private String partyRetentionUploadStatus;
|
private String partyRetentionUploadStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 党籍保留附件OSS ID(用于安全下载)
|
* 党籍保留附件OSS ID(用于安全下载,支持多个ID用逗号分隔)
|
||||||
*/
|
*/
|
||||||
@ExcelIgnore
|
@ExcelIgnore
|
||||||
private Long partyRetentionFileOssId;
|
private String partyRetentionFileOssId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户编号
|
* 租户编号
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ public class RsOverseasRegistration extends BaseEntity {
|
|||||||
/**
|
/**
|
||||||
* 登记ID
|
* 登记ID
|
||||||
*/
|
*/
|
||||||
@TableId(value = "registration_id", type = IdType.AUTO)
|
@TableId(value = "registration_id", type = IdType.ASSIGN_ID)
|
||||||
@ExcelProperty(value = "登记ID")
|
@ExcelProperty(value = "登记ID")
|
||||||
private Long registrationId;
|
private Long registrationId;
|
||||||
|
|
||||||
|
|||||||
+26
@@ -317,6 +317,32 @@ public class RsOverseasApplicationBo extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String stampedFileUploadStatus;
|
private String stampedFileUploadStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 党籍保留附件文件路径
|
||||||
|
*/
|
||||||
|
private String partyRetentionFilePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 党籍保留附件文件名
|
||||||
|
*/
|
||||||
|
private String partyRetentionFileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 党籍保留附件上传时间
|
||||||
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date partyRetentionUploadTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 党籍保留附件上传状态:0-未上传,1-已上传
|
||||||
|
*/
|
||||||
|
private String partyRetentionUploadStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 党籍保留附件OSS ID(支持多个ID用逗号分隔)
|
||||||
|
*/
|
||||||
|
private String partyRetentionFileOssId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户ID
|
* 租户ID
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
-2
@@ -365,9 +365,9 @@ public class RsOverseasApplicationVo implements Serializable {
|
|||||||
private String partyRetentionUploadStatus;
|
private String partyRetentionUploadStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 党籍保留附件OSS ID(用于安全下载)
|
* 党籍保留附件OSS ID(用于安全下载,支持多个ID用逗号分隔)
|
||||||
*/
|
*/
|
||||||
private Long partyRetentionFileOssId;
|
private String partyRetentionFileOssId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户编号
|
* 租户编号
|
||||||
|
|||||||
+5
@@ -196,6 +196,11 @@ public class RsOverseasApprovalMonitorVo implements Serializable {
|
|||||||
@ExcelProperty(value = "应急联系人及电话")
|
@ExcelProperty(value = "应急联系人及电话")
|
||||||
private String emergencyContact;
|
private String emergencyContact;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请人手写签名(base64)
|
||||||
|
*/
|
||||||
|
private String signature;
|
||||||
|
|
||||||
// ==================== 审批状态信息 ====================
|
// ==================== 审批状态信息 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+19
@@ -144,4 +144,23 @@ public interface RsOverseasApplicationMapper extends BaseMapperPlus<RsOverseasAp
|
|||||||
* @return 待审批申请列表
|
* @return 待审批申请列表
|
||||||
*/
|
*/
|
||||||
List<RsOverseasApplication> selectPendingApplications(List<String> approvalLevels, Long deptId);
|
List<RsOverseasApplication> selectPendingApplications(List<String> approvalLevels, Long deptId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 🔴 新增:根据角色查询待审批申请列表(支持分页和搜索)
|
||||||
|
*
|
||||||
|
* 查询逻辑:
|
||||||
|
* 1. 根据用户角色确定审批级别列表
|
||||||
|
* 2. 关联 sys_user 表获取申请人的部门信息
|
||||||
|
* 3. 按审批级别过滤(approvalLevel IN (...))
|
||||||
|
* 4. 按状态过滤(status = '0' 待审核)
|
||||||
|
* 5. 按部门过滤(部门负责人:u.dept_id = 当前用户部门ID)
|
||||||
|
* 6. 按搜索条件过滤(前往国家、出境事由)
|
||||||
|
* 7. 按创建时间倒序排列
|
||||||
|
*
|
||||||
|
* @param query 查询条件(包含分页、搜索条件)
|
||||||
|
* @param approvalLevels 审批级别列表,例如:["1", "2"]
|
||||||
|
* @param deptId 部门ID(部门负责人需要),为null则不过滤部门
|
||||||
|
* @return 待审批申请分页列表
|
||||||
|
*/
|
||||||
|
List<RsOverseasApplicationVo> selectPendingApplicationsByRole(RsOverseasApplicationQuery query, List<String> approvalLevels, Long deptId);
|
||||||
}
|
}
|
||||||
+9
-1
@@ -48,6 +48,14 @@ public interface IRsOverseasApplicationService {
|
|||||||
*/
|
*/
|
||||||
List<RsOverseasApplicationVo> queryPendingByCurrentUser();
|
List<RsOverseasApplicationVo> queryPendingByCurrentUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前用户的待审批申请分页列表(根据角色过滤)
|
||||||
|
*
|
||||||
|
* @param query 查询条件(包含分页、搜索条件)
|
||||||
|
* @return 待审批申请分页集合
|
||||||
|
*/
|
||||||
|
TableDataInfo<RsOverseasApplicationVo> queryPendingPageListByRole(RsOverseasApplicationQuery query);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询出国申请详细信息
|
* 查询出国申请详细信息
|
||||||
*
|
*
|
||||||
@@ -207,7 +215,7 @@ public interface IRsOverseasApplicationService {
|
|||||||
* @return 是否成功
|
* @return 是否成功
|
||||||
*/
|
*/
|
||||||
Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName,
|
Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName,
|
||||||
java.util.Date uploadTime, String uploadStatus, Long ossId);
|
java.util.Date uploadTime, String uploadStatus, String ossId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验出国登记前置条件
|
* 校验出国登记前置条件
|
||||||
|
|||||||
+195
-19
@@ -51,7 +51,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 出国申请Service业务层处理
|
* 出国申请Service业务层处理
|
||||||
*
|
*
|
||||||
* @author weizzzz
|
* @author weizzzz
|
||||||
* @date 2025-07-28
|
* @date 2025-07-28
|
||||||
*/
|
*/
|
||||||
@@ -232,14 +232,14 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询当前用户的待审批申请列表(动态权限方案 + 部门过滤)
|
* 查询当前用户的待审批申请列表(动态权限方案 + 部门过滤)
|
||||||
*
|
*
|
||||||
* 🆕 核心改进:
|
* 🆕 核心改进:
|
||||||
* 1. 不再依赖 current_approver_id 字段(该字段仅作为冗余展示用)
|
* 1. 不再依赖 current_approver_id 字段(该字段仅作为冗余展示用)
|
||||||
* 2. 动态查询用户的审批权限,转换为审批级别
|
* 2. 动态查询用户的审批权限,转换为审批级别
|
||||||
* 3. 根据审批级别和状态查询待审批申请
|
* 3. 根据审批级别和状态查询待审批申请
|
||||||
* 4. 🔴 部门负责人只能看到本部门的申请
|
* 4. 🔴 部门负责人只能看到本部门的申请
|
||||||
* 5. 支持人员岗位变动:权限调整后立即生效
|
* 5. 支持人员岗位变动:权限调整后立即生效
|
||||||
*
|
*
|
||||||
* 查询逻辑:
|
* 查询逻辑:
|
||||||
* - 用户有 rs:overseas:approve1 权限(部门负责人)→ 查询本部门员工的、approval_level='1' 且
|
* - 用户有 rs:overseas:approve1 权限(部门负责人)→ 查询本部门员工的、approval_level='1' 且
|
||||||
* status='0' 的申请
|
* status='0' 的申请
|
||||||
@@ -307,6 +307,156 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前用户的待审批申请分页列表(根据角色过滤)
|
||||||
|
*
|
||||||
|
* 🔴 核心逻辑:
|
||||||
|
* 1. 根据用户角色确定审批级别列表
|
||||||
|
* 2. 部门负责人(rs_bmfzr)只能看到本部门的申请
|
||||||
|
* 3. 其他角色可以看到全校的申请
|
||||||
|
* 4. 支持分页和搜索(前往国家、出境事由)
|
||||||
|
* 5. 按创建时间倒序排列
|
||||||
|
* 6. 🔴 修复:当用户同时拥有多个角色时,分别查询后合并去重
|
||||||
|
*
|
||||||
|
* 角色映射:
|
||||||
|
* - rs_bmfzr → approval_level='1' + 部门过滤
|
||||||
|
* - hr_dept_role → approval_level='2'
|
||||||
|
* - discipline_committee_role → approval_level='3'
|
||||||
|
* - college_leader_role → approval_level='4'
|
||||||
|
* - party_secretary_role → approval_level='5'
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<RsOverseasApplicationVo> queryPendingPageListByRole(RsOverseasApplicationQuery query) {
|
||||||
|
Long currentUserId = LoginHelper.getUserId();
|
||||||
|
String currentUserName = LoginHelper.getUsername();
|
||||||
|
String tenantId = LoginHelper.getTenantId();
|
||||||
|
|
||||||
|
log.info("🔍 查询用户待审批申请分页列表 - 用户ID: {}, 用户名: {}", currentUserId, currentUserName);
|
||||||
|
|
||||||
|
// 1. 获取当前用户的角色列表
|
||||||
|
List<SysRoleVo> userRoles = roleService.selectRolesByUserId(currentUserId);
|
||||||
|
if (userRoles == null || userRoles.isEmpty()) {
|
||||||
|
log.warn("❌ 用户 {} 没有任何角色,返回空列表", currentUserName);
|
||||||
|
return TableDataInfo.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 根据角色编码确定审批级别列表
|
||||||
|
List<String> deptHeadLevels = new ArrayList<>(); // 部门负责人审批级别
|
||||||
|
List<String> otherLevels = new ArrayList<>(); // 其他审批级别
|
||||||
|
Long deptIdForFilter = null;
|
||||||
|
boolean hasDeptHeadRole = false;
|
||||||
|
boolean hasOtherRole = false;
|
||||||
|
|
||||||
|
for (SysRoleVo role : userRoles) {
|
||||||
|
String roleKey = role.getRoleKey();
|
||||||
|
if (roleKey == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 角色编码映射到审批级别
|
||||||
|
if ("rs_bmfzr".equals(roleKey)) {
|
||||||
|
deptHeadLevels.add("1");
|
||||||
|
hasDeptHeadRole = true;
|
||||||
|
} else if ("hr_dept_role".equals(roleKey)) {
|
||||||
|
otherLevels.add("2");
|
||||||
|
hasOtherRole = true;
|
||||||
|
} else if ("discipline_committee_role".equals(roleKey)) {
|
||||||
|
otherLevels.add("3");
|
||||||
|
hasOtherRole = true;
|
||||||
|
} else if ("college_leader_role".equals(roleKey)) {
|
||||||
|
otherLevels.add("4");
|
||||||
|
hasOtherRole = true;
|
||||||
|
} else if ("party_secretary_role".equals(roleKey)) {
|
||||||
|
otherLevels.add("5");
|
||||||
|
hasOtherRole = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasDeptHeadRole && !hasOtherRole) {
|
||||||
|
log.warn("❌ 用户 {} 没有任何出国审批角色,返回空列表", currentUserName);
|
||||||
|
return TableDataInfo.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("✅ 用户 {} 拥有审批级别 - 部门负责人: {}, 其他角色: {}",
|
||||||
|
currentUserName, deptHeadLevels, otherLevels);
|
||||||
|
|
||||||
|
// 3. 如果是部门负责人,获取部门ID进行过滤
|
||||||
|
if (hasDeptHeadRole) {
|
||||||
|
SysUserVo currentUser = userService.selectUserById(currentUserId);
|
||||||
|
if (currentUser != null && currentUser.getDeptId() != null) {
|
||||||
|
deptIdForFilter = currentUser.getDeptId();
|
||||||
|
log.info("🔒 [部门权限过滤] 部门负责人,用户: {}, 部门ID: {}, 只查看本部门待审批",
|
||||||
|
currentUserName, deptIdForFilter);
|
||||||
|
} else {
|
||||||
|
log.warn("⚠️ 用户 {} 是部门负责人但没有部门信息,无法过滤", currentUserName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 设置租户ID
|
||||||
|
if (StringUtils.isNotBlank(tenantId)) {
|
||||||
|
query.setTenantId(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 🔴 修复:分别查询不同角色的数据,然后合并去重
|
||||||
|
List<RsOverseasApplicationVo> allList = new ArrayList<>();
|
||||||
|
Map<Long, RsOverseasApplicationVo> applicationMap = new HashMap<>(); // 用于去重,key=applicationId
|
||||||
|
|
||||||
|
// 5.1 查询部门负责人的数据(本部门,审批级别='1')
|
||||||
|
if (hasDeptHeadRole && !deptHeadLevels.isEmpty() && deptIdForFilter != null) {
|
||||||
|
List<RsOverseasApplicationVo> deptList = baseMapper.selectPendingApplicationsByRole(
|
||||||
|
query, deptHeadLevels, deptIdForFilter);
|
||||||
|
log.info("📋 [部门负责人] 查询到 {} 条本部门待审批申请", deptList.size());
|
||||||
|
for (RsOverseasApplicationVo vo : deptList) {
|
||||||
|
applicationMap.put(vo.getApplicationId(), vo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.2 查询其他角色的数据(全校,审批级别为其他级别)
|
||||||
|
if (hasOtherRole && !otherLevels.isEmpty()) {
|
||||||
|
List<RsOverseasApplicationVo> otherList = baseMapper.selectPendingApplicationsByRole(
|
||||||
|
query, otherLevels, null); // deptIdForFilter=null,不限制部门
|
||||||
|
log.info("📋 [其他角色] 查询到 {} 条全校待审批申请", otherList.size());
|
||||||
|
for (RsOverseasApplicationVo vo : otherList) {
|
||||||
|
applicationMap.put(vo.getApplicationId(), vo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.3 合并去重后的结果,按创建时间倒序排列
|
||||||
|
allList = new ArrayList<>(applicationMap.values());
|
||||||
|
allList.sort((a, b) -> {
|
||||||
|
if (a.getCreateTime() != null && b.getCreateTime() != null) {
|
||||||
|
return b.getCreateTime().compareTo(a.getCreateTime());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("✅ [合并结果] 去重后共 {} 条待审批申请", allList.size());
|
||||||
|
|
||||||
|
// 6. 手动分页处理
|
||||||
|
int pageNum = query.getPageNum();
|
||||||
|
int pageSize = query.getPageSize();
|
||||||
|
int total = allList.size();
|
||||||
|
int start = (pageNum - 1) * pageSize;
|
||||||
|
int end = Math.min(start + pageSize, total);
|
||||||
|
|
||||||
|
List<RsOverseasApplicationVo> pageList;
|
||||||
|
if (start >= total) {
|
||||||
|
pageList = new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
pageList = allList.subList(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 构建分页对象
|
||||||
|
Page<RsOverseasApplicationVo> page = new Page<>(pageNum, pageSize);
|
||||||
|
page.setRecords(pageList);
|
||||||
|
page.setTotal(total);
|
||||||
|
|
||||||
|
log.info("📋 用户 {} 查询到 {} 条待审批申请,分页显示第 {} 页,共 {} 条",
|
||||||
|
currentUserName, total, pageNum, pageList.size());
|
||||||
|
|
||||||
|
return TableDataInfo.build(page);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换实体为VO对象
|
* 转换实体为VO对象
|
||||||
* 🔴 修复:使用Mapper查询完整的VO对象,包含字典翻译等扩展信息
|
* 🔴 修复:使用Mapper查询完整的VO对象,包含字典翻译等扩展信息
|
||||||
@@ -514,7 +664,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交出国申请(动态审批人方案)
|
* 提交出国申请(动态审批人方案)
|
||||||
*
|
*
|
||||||
* 🆕 核心改进:
|
* 🆕 核心改进:
|
||||||
* 1. 不再设置固定的 current_approver_id(该字段改为冗余展示字段)
|
* 1. 不再设置固定的 current_approver_id(该字段改为冗余展示字段)
|
||||||
* 2. 仅设置 approval_level='1',审批人根据权限动态查询
|
* 2. 仅设置 approval_level='1',审批人根据权限动态查询
|
||||||
@@ -599,7 +749,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
RsOverseasApproval approval = new RsOverseasApproval();
|
RsOverseasApproval approval = new RsOverseasApproval();
|
||||||
approval.setApplicationId(applicationId);
|
approval.setApplicationId(applicationId);
|
||||||
approval.setApproverId(approverId);
|
approval.setApproverId(approverId);
|
||||||
approval.setApproverName(approverName);
|
approval.setApproverName(resolveApproverDisplayName(approverId, approverName));
|
||||||
approval.setApprovalLevel(application.getApprovalLevel());
|
approval.setApprovalLevel(application.getApprovalLevel());
|
||||||
approval.setApprovalStatus(approvalStatus);
|
approval.setApprovalStatus(approvalStatus);
|
||||||
approval.setApprovalOpinion(approvalOpinion);
|
approval.setApprovalOpinion(approvalOpinion);
|
||||||
@@ -721,7 +871,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 修复已审批通过但未创建记录的申请
|
* 修复已审批通过但未创建记录的申请
|
||||||
*
|
*
|
||||||
* @return 修复的记录数量
|
* @return 修复的记录数量
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -950,6 +1100,16 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
entity.setStampedFileUploadTime(bo.getStampedFileUploadTime());
|
entity.setStampedFileUploadTime(bo.getStampedFileUploadTime());
|
||||||
entity.setStampedFileUploadStatus(bo.getStampedFileUploadStatus());
|
entity.setStampedFileUploadStatus(bo.getStampedFileUploadStatus());
|
||||||
|
|
||||||
|
// ==================== 党籍保留附件相关字段 ====================
|
||||||
|
entity.setPartyRetentionFilePath(bo.getPartyRetentionFilePath());
|
||||||
|
entity.setPartyRetentionFileName(bo.getPartyRetentionFileName());
|
||||||
|
entity.setPartyRetentionUploadTime(bo.getPartyRetentionUploadTime());
|
||||||
|
entity.setPartyRetentionUploadStatus(bo.getPartyRetentionUploadStatus());
|
||||||
|
entity.setPartyRetentionFileOssId(bo.getPartyRetentionFileOssId());
|
||||||
|
|
||||||
|
// ==================== 附件ID列表 ====================
|
||||||
|
entity.setAttachmentIds(bo.getAttachmentIds());
|
||||||
|
|
||||||
// ==================== 租户信息 ====================
|
// ==================== 租户信息 ====================
|
||||||
// 🔴 关键修复:设置租户ID(如果Bo中有值则使用Bo的值,否则依赖MyBatis-Plus自动填充)
|
// 🔴 关键修复:设置租户ID(如果Bo中有值则使用Bo的值,否则依赖MyBatis-Plus自动填充)
|
||||||
if (bo.getTenantId() != null) {
|
if (bo.getTenantId() != null) {
|
||||||
@@ -975,6 +1135,22 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析审批人的展示名称:优先使用用户昵称,缺省时回退账号
|
||||||
|
*/
|
||||||
|
private String resolveApproverDisplayName(Long approverId, String fallbackName) {
|
||||||
|
String displayName = fallbackName;
|
||||||
|
try {
|
||||||
|
SysUserVo approver = userService.selectUserById(approverId);
|
||||||
|
if (approver != null && StringUtils.isNotBlank(approver.getNickName())) {
|
||||||
|
displayName = approver.getNickName();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("⚠️ 获取审批人昵称失败, 使用账号作为展示名称. approverId={}, error={}", approverId, e.getMessage());
|
||||||
|
}
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除出国申请
|
* 批量删除出国申请
|
||||||
*/
|
*/
|
||||||
@@ -1008,7 +1184,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName,
|
public Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName,
|
||||||
java.util.Date uploadTime, String uploadStatus, Long ossId) {
|
java.util.Date uploadTime, String uploadStatus, String ossId) {
|
||||||
return baseMapper.update(null,
|
return baseMapper.update(null,
|
||||||
new LambdaUpdateWrapper<RsOverseasApplication>()
|
new LambdaUpdateWrapper<RsOverseasApplication>()
|
||||||
.eq(RsOverseasApplication::getApplicationId, applicationId)
|
.eq(RsOverseasApplication::getApplicationId, applicationId)
|
||||||
@@ -1087,7 +1263,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据申请人ID获取部门审批人ID
|
* 根据申请人ID获取部门审批人ID
|
||||||
*
|
*
|
||||||
* @param applicantId 申请人ID
|
* @param applicantId 申请人ID
|
||||||
* @return 部门审批人ID
|
* @return 部门审批人ID
|
||||||
*/
|
*/
|
||||||
@@ -1134,7 +1310,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据申请人ID获取人事处审批人列表
|
* 根据申请人ID获取人事处审批人列表
|
||||||
*
|
*
|
||||||
* @param applicantId 申请人ID
|
* @param applicantId 申请人ID
|
||||||
* @return 人事处审批人ID列表
|
* @return 人事处审批人ID列表
|
||||||
*/
|
*/
|
||||||
@@ -1176,7 +1352,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据申请人查找对应的部门负责人审批人ID
|
* 根据申请人查找对应的部门负责人审批人ID
|
||||||
*
|
*
|
||||||
* @param applicantId 申请人ID
|
* @param applicantId 申请人ID
|
||||||
* @return 部门负责人审批人ID
|
* @return 部门负责人审批人ID
|
||||||
*/
|
*/
|
||||||
@@ -1203,7 +1379,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据部门ID查找对应的部门负责人审批人
|
* 根据部门ID查找对应的部门负责人审批人
|
||||||
*
|
*
|
||||||
* @param deptId 部门ID
|
* @param deptId 部门ID
|
||||||
* @return 部门负责人审批人ID
|
* @return 部门负责人审批人ID
|
||||||
*/
|
*/
|
||||||
@@ -1251,7 +1427,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找父部门的负责人审批人
|
* 查找父部门的负责人审批人
|
||||||
*
|
*
|
||||||
* @param deptId 当前部门ID
|
* @param deptId 当前部门ID
|
||||||
* @param approvers 所有部门负责人审批人列表
|
* @param approvers 所有部门负责人审批人列表
|
||||||
* @return 父部门负责人审批人ID
|
* @return 父部门负责人审批人ID
|
||||||
@@ -1288,7 +1464,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据审批级别获取下一级审批人ID
|
* 根据审批级别获取下一级审批人ID
|
||||||
*
|
*
|
||||||
* @param approvalLevel 审批级别
|
* @param approvalLevel 审批级别
|
||||||
* @return 下一级审批人ID
|
* @return 下一级审批人ID
|
||||||
*/
|
*/
|
||||||
@@ -1298,7 +1474,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据审批级别获取审批环节描述
|
* 根据审批级别获取审批环节描述
|
||||||
*
|
*
|
||||||
* @param approvalLevel 审批级别
|
* @param approvalLevel 审批级别
|
||||||
* @return 审批环节描述
|
* @return 审批环节描述
|
||||||
*/
|
*/
|
||||||
@@ -1321,14 +1497,14 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 根据当前登录用户的审批权限,获取其可审批的级别列表
|
* 🆕 根据当前登录用户的审批权限,获取其可审批的级别列表
|
||||||
*
|
*
|
||||||
* 权限到级别的映射:
|
* 权限到级别的映射:
|
||||||
* - rs:overseas:approve1 → 审批级别 "1" (部门负责人)
|
* - rs:overseas:approve1 → 审批级别 "1" (部门负责人)
|
||||||
* - rs:overseas:approve2 → 审批级别 "2" (人事处)
|
* - rs:overseas:approve2 → 审批级别 "2" (人事处)
|
||||||
* - rs:overseas:approve3 → 审批级别 "3" (纪委)
|
* - rs:overseas:approve3 → 审批级别 "3" (纪委)
|
||||||
* - rs:overseas:approve4 → 审批级别 "4" (学院分管领导)
|
* - rs:overseas:approve4 → 审批级别 "4" (学院分管领导)
|
||||||
* - rs:overseas:approve5 → 审批级别 "5" (学院党委书记)
|
* - rs:overseas:approve5 → 审批级别 "5" (学院党委书记)
|
||||||
*
|
*
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
* @return 可审批的级别列表,例如:["2", "3"]
|
* @return 可审批的级别列表,例如:["2", "3"]
|
||||||
*/
|
*/
|
||||||
@@ -1371,7 +1547,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 根据审批级别获取对应的权限标识
|
* 🆕 根据审批级别获取对应的权限标识
|
||||||
*
|
*
|
||||||
* @param approvalLevel 审批级别(2-5)
|
* @param approvalLevel 审批级别(2-5)
|
||||||
* @return 权限标识,例如:"rs:overseas:approve2"
|
* @return 权限标识,例如:"rs:overseas:approve2"
|
||||||
*/
|
*/
|
||||||
@@ -1396,7 +1572,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔴 新增方法:验证审批人是否有指定级别的审批权限
|
* 🔴 新增方法:验证审批人是否有指定级别的审批权限
|
||||||
*
|
*
|
||||||
* @param approverId 审批人ID
|
* @param approverId 审批人ID
|
||||||
* @param approvalLevel 需要的审批级别(1-5)
|
* @param approvalLevel 需要的审批级别(1-5)
|
||||||
* @return true-有权限,false-无权限
|
* @return true-有权限,false-无权限
|
||||||
@@ -1423,4 +1599,4 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl<RsOverseasAppl
|
|||||||
return hasPermission;
|
return hasPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
@@ -311,6 +311,7 @@ public class RsOverseasApprovalMonitorServiceImpl implements IRsOverseasApproval
|
|||||||
vo.setMobilePhone(application.getMobilePhone());
|
vo.setMobilePhone(application.getMobilePhone());
|
||||||
vo.setContactPhone(application.getContactPhone());
|
vo.setContactPhone(application.getContactPhone());
|
||||||
vo.setEmergencyContact(application.getEmergencyContact());
|
vo.setEmergencyContact(application.getEmergencyContact());
|
||||||
|
vo.setSignature(application.getSignature());
|
||||||
|
|
||||||
// 审批状态信息
|
// 审批状态信息
|
||||||
vo.setStatus(application.getStatus());
|
vo.setStatus(application.getStatus());
|
||||||
|
|||||||
+190
-18
@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
import org.dromara.rs.domain.RsOverseasItineraryChange;
|
import org.dromara.rs.domain.RsOverseasItineraryChange;
|
||||||
@@ -13,14 +14,23 @@ import org.dromara.rs.domain.bo.RsOverseasItineraryChangeBo;
|
|||||||
import org.dromara.rs.domain.query.RsOverseasItineraryChangeQuery;
|
import org.dromara.rs.domain.query.RsOverseasItineraryChangeQuery;
|
||||||
import org.dromara.rs.domain.vo.RsOverseasItineraryChangeVo;
|
import org.dromara.rs.domain.vo.RsOverseasItineraryChangeVo;
|
||||||
import org.dromara.rs.mapper.RsOverseasItineraryChangeMapper;
|
import org.dromara.rs.mapper.RsOverseasItineraryChangeMapper;
|
||||||
|
import org.dromara.rs.mapper.RsOverseasApplicationMapper;
|
||||||
|
import org.dromara.rs.mapper.RsOverseasRecordMapper;
|
||||||
|
import org.dromara.rs.domain.RsOverseasApplication;
|
||||||
|
import org.dromara.rs.domain.RsOverseasRecord;
|
||||||
|
import org.dromara.rs.domain.vo.RsOverseasRecordVo;
|
||||||
import org.dromara.rs.service.IRsOverseasItineraryChangeService;
|
import org.dromara.rs.service.IRsOverseasItineraryChangeService;
|
||||||
import org.dromara.rs.service.IRsOverseasApplicationService;
|
import org.dromara.rs.service.IRsOverseasApplicationService;
|
||||||
|
import org.dromara.rs.service.IRsOverseasRecordService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 出国行程变更申请Service业务层处理
|
* 出国行程变更申请Service业务层处理
|
||||||
@@ -35,14 +45,38 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
|
|
||||||
private final RsOverseasItineraryChangeMapper changeMapper;
|
private final RsOverseasItineraryChangeMapper changeMapper;
|
||||||
private final IRsOverseasApplicationService applicationService;
|
private final IRsOverseasApplicationService applicationService;
|
||||||
|
private final RsOverseasApplicationMapper applicationMapper;
|
||||||
|
private final IRsOverseasRecordService recordService;
|
||||||
|
private final RsOverseasRecordMapper recordMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询出国行程变更申请分页列表
|
* 查询出国行程变更申请分页列表
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo<RsOverseasItineraryChangeVo> queryPageList(RsOverseasItineraryChangeQuery query) {
|
public TableDataInfo<RsOverseasItineraryChangeVo> queryPageList(RsOverseasItineraryChangeQuery query) {
|
||||||
|
// 查询所有符合条件的数据(不分页)
|
||||||
List<RsOverseasItineraryChangeVo> list = changeMapper.selectChangeList(query);
|
List<RsOverseasItineraryChangeVo> list = changeMapper.selectChangeList(query);
|
||||||
return TableDataInfo.build(list);
|
|
||||||
|
// 手动分页处理
|
||||||
|
int pageNum = query.getPageNum();
|
||||||
|
int pageSize = query.getPageSize();
|
||||||
|
int total = list.size();
|
||||||
|
int start = (pageNum - 1) * pageSize;
|
||||||
|
int end = Math.min(start + pageSize, total);
|
||||||
|
|
||||||
|
List<RsOverseasItineraryChangeVo> pageList;
|
||||||
|
if (start >= total) {
|
||||||
|
pageList = new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
pageList = list.subList(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建分页对象
|
||||||
|
Page<RsOverseasItineraryChangeVo> page = new Page<>(pageNum, pageSize);
|
||||||
|
page.setRecords(pageList);
|
||||||
|
page.setTotal(total);
|
||||||
|
|
||||||
|
return TableDataInfo.build(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,11 +123,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean insertByBo(RsOverseasItineraryChangeBo bo) {
|
public Boolean insertByBo(RsOverseasItineraryChangeBo bo) {
|
||||||
// 查询出国申请信息
|
// 查询出国申请信息
|
||||||
org.dromara.rs.domain.vo.RsOverseasApplicationVo application = applicationService.queryById(bo.getApplicationId());
|
org.dromara.rs.domain.vo.RsOverseasApplicationVo application = applicationService
|
||||||
|
.queryById(bo.getApplicationId());
|
||||||
if (application == null) {
|
if (application == null) {
|
||||||
throw new ServiceException("出国申请不存在");
|
throw new ServiceException("出国申请不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查申请状态是否为已通过
|
// 检查申请状态是否为已通过
|
||||||
if (!"1".equals(application.getStatus())) {
|
if (!"1".equals(application.getStatus())) {
|
||||||
throw new ServiceException("只能为已通过审批的出国申请提交变更申请");
|
throw new ServiceException("只能为已通过审批的出国申请提交变更申请");
|
||||||
@@ -102,8 +137,8 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
// 检查是否有未完成的变更申请
|
// 检查是否有未完成的变更申请
|
||||||
LambdaQueryWrapper<RsOverseasItineraryChange> wrapper = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<RsOverseasItineraryChange> wrapper = Wrappers.lambdaQuery();
|
||||||
wrapper.eq(RsOverseasItineraryChange::getApplicationId, bo.getApplicationId())
|
wrapper.eq(RsOverseasItineraryChange::getApplicationId, bo.getApplicationId())
|
||||||
.in(RsOverseasItineraryChange::getStatus, Arrays.asList("0")) // 待审核
|
.in(RsOverseasItineraryChange::getStatus, Arrays.asList("0")) // 待审核
|
||||||
.eq(RsOverseasItineraryChange::getDeleted, 0);
|
.eq(RsOverseasItineraryChange::getDeleted, 0);
|
||||||
Long count = changeMapper.selectCount(wrapper);
|
Long count = changeMapper.selectCount(wrapper);
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
throw new ServiceException("该出国申请已有待审核的变更申请,请先处理");
|
throw new ServiceException("该出国申请已有待审核的变更申请,请先处理");
|
||||||
@@ -117,11 +152,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
entity.setApplicantId(userId);
|
entity.setApplicantId(userId);
|
||||||
entity.setApplicantName(LoginHelper.getUsername());
|
entity.setApplicantName(LoginHelper.getUsername());
|
||||||
entity.setApplicationId(bo.getApplicationId());
|
entity.setApplicationId(bo.getApplicationId());
|
||||||
|
|
||||||
// 从出国申请中复制原始信息
|
// 从出国申请中复制原始信息
|
||||||
entity.setOriginalDestination(application.getDestination());
|
entity.setOriginalDestination(application.getDestination());
|
||||||
entity.setOriginalDestinationCity(application.getDestinationCity());
|
entity.setOriginalDestinationCity(application.getDestinationCity());
|
||||||
entity.setOriginalExitReason(application.getExitReason() != null ? application.getExitReason() : application.getPurposeDetail());
|
entity.setOriginalExitReason(
|
||||||
|
application.getExitReason() != null ? application.getExitReason() : application.getPurposeDetail());
|
||||||
entity.setOriginalStartDate(application.getStartDate());
|
entity.setOriginalStartDate(application.getStartDate());
|
||||||
entity.setOriginalEndDate(application.getEndDate());
|
entity.setOriginalEndDate(application.getEndDate());
|
||||||
entity.setOriginalDuration(application.getDuration());
|
entity.setOriginalDuration(application.getDuration());
|
||||||
@@ -148,9 +184,10 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
throw new ServiceException("变更申请不存在");
|
throw new ServiceException("变更申请不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只有待审核或已拒绝的申请可以修改
|
// 只有待审核、已拒绝或已撤回的申请可以修改
|
||||||
if (!"0".equals(original.getStatus()) && !"2".equals(original.getStatus())) {
|
if (!"0".equals(original.getStatus()) && !"2".equals(original.getStatus())
|
||||||
throw new ServiceException("只有待审核或已拒绝的变更申请可以修改");
|
&& !"3".equals(original.getStatus())) {
|
||||||
|
throw new ServiceException("只有待审核、已拒绝或已撤回的变更申请可以修改");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查权限:只能修改自己的申请
|
// 检查权限:只能修改自己的申请
|
||||||
@@ -161,7 +198,7 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
|
|
||||||
// 转换BO到Entity
|
// 转换BO到Entity
|
||||||
RsOverseasItineraryChange entity = BeanUtil.toBean(bo, RsOverseasItineraryChange.class);
|
RsOverseasItineraryChange entity = BeanUtil.toBean(bo, RsOverseasItineraryChange.class);
|
||||||
|
|
||||||
// 重新提交时,状态改为待审核
|
// 重新提交时,状态改为待审核
|
||||||
entity.setStatus("0");
|
entity.setStatus("0");
|
||||||
entity.setApprovalOpinion(null);
|
entity.setApprovalOpinion(null);
|
||||||
@@ -179,24 +216,24 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean deleteByIds(List<Long> changeIds) {
|
public Boolean deleteByIds(List<Long> changeIds) {
|
||||||
Long userId = LoginHelper.getUserId();
|
Long userId = LoginHelper.getUserId();
|
||||||
|
|
||||||
for (Long changeId : changeIds) {
|
for (Long changeId : changeIds) {
|
||||||
RsOverseasItineraryChange change = changeMapper.selectById(changeId);
|
RsOverseasItineraryChange change = changeMapper.selectById(changeId);
|
||||||
if (change == null) {
|
if (change == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查权限:只能删除自己的申请
|
// 检查权限:只能删除自己的申请
|
||||||
if (!userId.equals(change.getApplicantId())) {
|
if (!userId.equals(change.getApplicantId())) {
|
||||||
throw new ServiceException("只能删除自己的变更申请");
|
throw new ServiceException("只能删除自己的变更申请");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只有待审核或已拒绝的申请可以删除
|
// 只有待审核或已拒绝的申请可以删除
|
||||||
if (!"0".equals(change.getStatus()) && !"2".equals(change.getStatus())) {
|
if (!"0".equals(change.getStatus()) && !"2".equals(change.getStatus())) {
|
||||||
throw new ServiceException("只有待审核或已拒绝的变更申请可以删除");
|
throw new ServiceException("只有待审核或已拒绝的变更申请可以删除");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return changeMapper.deleteBatchIds(changeIds) > 0;
|
return changeMapper.deleteBatchIds(changeIds) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +266,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批变更申请
|
* 审批变更申请
|
||||||
|
*
|
||||||
|
* 🔴 审批通过后,需要更新原申请单和记录的数据:
|
||||||
|
* 1. 前往国家 → 更新到 rs_overseas_application 和 rs_overseas_record
|
||||||
|
* 2. 实际出行开始时间、结束时间 → 更新到 rs_overseas_application 和 rs_overseas_record
|
||||||
|
* 3. 行程安排 → 更新到 rs_overseas_application 和 rs_overseas_record
|
||||||
|
* 4. 出境事由 → 追加到 rs_overseas_application 的备注字段
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@@ -249,9 +292,139 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
change.setApprovalOpinion(opinion);
|
change.setApprovalOpinion(opinion);
|
||||||
change.setApprovalTime(new Date());
|
change.setApprovalTime(new Date());
|
||||||
change.setApproverId(userId);
|
change.setApproverId(userId);
|
||||||
change.setApproverName(LoginHelper.getUsername());
|
String displayName = StringUtils.defaultIfBlank(LoginHelper.getNickname(), LoginHelper.getUsername());
|
||||||
|
change.setApproverName(displayName);
|
||||||
|
|
||||||
return changeMapper.updateById(change) > 0;
|
// 更新变更申请状态
|
||||||
|
boolean updateChangeResult = changeMapper.updateById(change) > 0;
|
||||||
|
if (!updateChangeResult) {
|
||||||
|
throw new ServiceException("更新变更申请状态失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔴 如果审批通过,需要更新原申请单和记录的数据
|
||||||
|
if (approved && change.getApplicationId() != null) {
|
||||||
|
log.info("🔧 [审批通过] 开始更新原申请单和记录数据,变更ID: {}, 申请ID: {}", changeId, change.getApplicationId());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 更新 rs_overseas_application 表
|
||||||
|
RsOverseasApplication application = applicationMapper.selectById(change.getApplicationId());
|
||||||
|
if (application != null) {
|
||||||
|
LambdaUpdateWrapper<RsOverseasApplication> appUpdateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
appUpdateWrapper.eq(RsOverseasApplication::getApplicationId, change.getApplicationId());
|
||||||
|
|
||||||
|
// 更新前往国家(destination 和 destinationCountry 同步更新)
|
||||||
|
if (change.getNewDestination() != null && !change.getNewDestination().trim().isEmpty()) {
|
||||||
|
appUpdateWrapper.set(RsOverseasApplication::getDestination, change.getNewDestination());
|
||||||
|
appUpdateWrapper.set(RsOverseasApplication::getDestinationCountry, change.getNewDestination());
|
||||||
|
log.info("✅ [更新申请单] 前往国家: {} (同步更新 destination 和 destinationCountry)",
|
||||||
|
change.getNewDestination());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新出发日期和返回日期
|
||||||
|
if (change.getNewStartDate() != null) {
|
||||||
|
appUpdateWrapper.set(RsOverseasApplication::getStartDate, change.getNewStartDate());
|
||||||
|
log.info("✅ [更新申请单] 出发日期: {}", change.getNewStartDate());
|
||||||
|
}
|
||||||
|
if (change.getNewEndDate() != null) {
|
||||||
|
appUpdateWrapper.set(RsOverseasApplication::getEndDate, change.getNewEndDate());
|
||||||
|
log.info("✅ [更新申请单] 返回日期: {}", change.getNewEndDate());
|
||||||
|
|
||||||
|
// 重新计算出国天数
|
||||||
|
if (change.getNewStartDate() != null && change.getNewEndDate() != null) {
|
||||||
|
long diffInMillies = change.getNewEndDate().getTime() - change.getNewStartDate().getTime();
|
||||||
|
int days = (int) (diffInMillies / (1000 * 60 * 60 * 24)) + 1;
|
||||||
|
appUpdateWrapper.set(RsOverseasApplication::getDuration, days);
|
||||||
|
log.info("✅ [更新申请单] 出国天数: {} 天", days);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新行程安排
|
||||||
|
if (change.getNewItinerary() != null && !change.getNewItinerary().trim().isEmpty()) {
|
||||||
|
appUpdateWrapper.set(RsOverseasApplication::getItinerary, change.getNewItinerary());
|
||||||
|
log.info("✅ [更新申请单] 行程安排已更新");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加出境事由到备注字段
|
||||||
|
if (change.getNewExitReason() != null && !change.getNewExitReason().trim().isEmpty()) {
|
||||||
|
String currentRemarks = application.getRemarks() != null ? application.getRemarks() : "";
|
||||||
|
String newRemarks = currentRemarks.isEmpty()
|
||||||
|
? "行程变更-出境事由: " + change.getNewExitReason()
|
||||||
|
: currentRemarks + "\n行程变更-出境事由: " + change.getNewExitReason();
|
||||||
|
appUpdateWrapper.set(RsOverseasApplication::getRemarks, newRemarks);
|
||||||
|
log.info("✅ [更新申请单] 备注已追加出境事由: {}", change.getNewExitReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean appUpdateResult = applicationMapper.update(null, appUpdateWrapper) > 0;
|
||||||
|
if (!appUpdateResult) {
|
||||||
|
log.warn("⚠️ [更新申请单] 更新失败,申请ID: {}", change.getApplicationId());
|
||||||
|
} else {
|
||||||
|
log.info("✅ [更新申请单] 更新成功,申请ID: {}", change.getApplicationId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("⚠️ [更新申请单] 申请单不存在,申请ID: {}", change.getApplicationId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 更新 rs_overseas_record 表
|
||||||
|
RsOverseasRecordVo recordVo = recordMapper
|
||||||
|
.selectOverseasRecordByApplicationId(change.getApplicationId());
|
||||||
|
if (recordVo != null) {
|
||||||
|
RsOverseasRecord record = recordMapper.selectById(recordVo.getRecordId());
|
||||||
|
if (record != null) {
|
||||||
|
LambdaUpdateWrapper<RsOverseasRecord> recordUpdateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
recordUpdateWrapper.eq(RsOverseasRecord::getRecordId, record.getRecordId());
|
||||||
|
|
||||||
|
// 更新前往国家
|
||||||
|
if (change.getNewDestination() != null && !change.getNewDestination().trim().isEmpty()) {
|
||||||
|
recordUpdateWrapper.set(RsOverseasRecord::getDestination, change.getNewDestination());
|
||||||
|
log.info("✅ [更新记录] 前往国家: {}", change.getNewDestination());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新实际开始日期和结束日期
|
||||||
|
if (change.getNewStartDate() != null) {
|
||||||
|
recordUpdateWrapper.set(RsOverseasRecord::getActualStartDate, change.getNewStartDate());
|
||||||
|
log.info("✅ [更新记录] 实际开始日期: {}", change.getNewStartDate());
|
||||||
|
}
|
||||||
|
if (change.getNewEndDate() != null) {
|
||||||
|
recordUpdateWrapper.set(RsOverseasRecord::getActualEndDate, change.getNewEndDate());
|
||||||
|
log.info("✅ [更新记录] 实际结束日期: {}", change.getNewEndDate());
|
||||||
|
|
||||||
|
// 重新计算实际出国天数
|
||||||
|
if (change.getNewStartDate() != null && change.getNewEndDate() != null) {
|
||||||
|
long diffInMillies = change.getNewEndDate().getTime()
|
||||||
|
- change.getNewStartDate().getTime();
|
||||||
|
int days = (int) (diffInMillies / (1000 * 60 * 60 * 24)) + 1;
|
||||||
|
recordUpdateWrapper.set(RsOverseasRecord::getActualDuration, days);
|
||||||
|
log.info("✅ [更新记录] 实际出国天数: {} 天", days);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新行程安排
|
||||||
|
if (change.getNewItinerary() != null && !change.getNewItinerary().trim().isEmpty()) {
|
||||||
|
recordUpdateWrapper.set(RsOverseasRecord::getItinerary, change.getNewItinerary());
|
||||||
|
log.info("✅ [更新记录] 行程安排已更新");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean recordUpdateResult = recordMapper.update(null, recordUpdateWrapper) > 0;
|
||||||
|
if (!recordUpdateResult) {
|
||||||
|
log.warn("⚠️ [更新记录] 更新失败,记录ID: {}", record.getRecordId());
|
||||||
|
} else {
|
||||||
|
log.info("✅ [更新记录] 更新成功,记录ID: {}", record.getRecordId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("⚠️ [更新记录] 记录不存在,记录ID: {}", recordVo.getRecordId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("⚠️ [更新记录] 未找到关联记录,申请ID: {}", change.getApplicationId());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("✅ [审批通过] 数据更新完成,变更ID: {}", changeId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ [审批通过] 更新原申请单和记录数据失败,变更ID: {}, 错误: {}", changeId, e.getMessage(), e);
|
||||||
|
throw new ServiceException("审批通过后更新原申请单和记录数据失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,4 +435,3 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar
|
|||||||
return changeMapper.selectChangeListByRecordId(recordId);
|
return changeMapper.selectChangeListByRecordId(recordId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+37
@@ -617,5 +617,42 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</if>
|
</if>
|
||||||
ORDER BY a.apply_time DESC, a.create_time DESC
|
ORDER BY a.apply_time DESC, a.create_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 🔴 新增:根据角色查询待审批申请列表(支持分页和搜索) -->
|
||||||
|
<select id="selectPendingApplicationsByRole" resultMap="RsOverseasApplicationVoResult">
|
||||||
|
<include refid="selectOverseasApplicationVo"/>
|
||||||
|
<where>
|
||||||
|
oa.deleted = '0'
|
||||||
|
AND oa.status = '0'
|
||||||
|
<!-- 审批级别过滤 -->
|
||||||
|
<if test="approvalLevels != null and approvalLevels.size() > 0">
|
||||||
|
AND oa.approval_level IN
|
||||||
|
<foreach collection="approvalLevels" item="level" open="(" separator="," close=")">
|
||||||
|
#{level}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<!-- 部门过滤(仅对部门负责人生效) -->
|
||||||
|
<if test="deptId != null">
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1 FROM sys_user u
|
||||||
|
WHERE u.user_id = oa.applicant_id
|
||||||
|
AND u.dept_id = #{deptId}
|
||||||
|
)
|
||||||
|
</if>
|
||||||
|
<!-- 搜索条件:前往国家 -->
|
||||||
|
<if test="query != null and query.destination != null and query.destination != ''">
|
||||||
|
AND oa.destination LIKE CONCAT('%', #{query.destination}, '%')
|
||||||
|
</if>
|
||||||
|
<!-- 搜索条件:出境事由 -->
|
||||||
|
<if test="query != null and query.exitReason != null and query.exitReason != ''">
|
||||||
|
AND oa.exit_reason = #{query.exitReason}
|
||||||
|
</if>
|
||||||
|
<!-- 租户过滤 -->
|
||||||
|
<if test="query != null and query.tenantId != null and query.tenantId != ''">
|
||||||
|
AND oa.tenant_id = #{query.tenantId}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY oa.create_time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
+5
-8
@@ -74,8 +74,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<sql id="selectOverseasRecordDetailVo">
|
<sql id="selectOverseasRecordDetailVo">
|
||||||
select
|
select
|
||||||
ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department,
|
ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department,
|
||||||
ors.position,
|
oa.position,
|
||||||
COALESCE(sp.post_name, ors.position) as position_text,
|
oa.position as position_text,
|
||||||
ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city,
|
ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city,
|
||||||
ors.actual_start_date, ors.actual_end_date, ors.actual_duration, ors.actual_budget,
|
ors.actual_start_date, ors.actual_end_date, ors.actual_duration, ors.actual_budget,
|
||||||
ors.invitation, ors.itinerary, ors.accommodation_plan, ors.transportation_plan,
|
ors.invitation, ors.itinerary, ors.accommodation_plan, ors.transportation_plan,
|
||||||
@@ -134,7 +134,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
'record' as data_source
|
'record' as data_source
|
||||||
from rs_overseas_record ors
|
from rs_overseas_record ors
|
||||||
left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0'
|
left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0'
|
||||||
left join sys_post sp on (ors.position = CAST(sp.post_id AS CHAR) or ors.position = sp.post_code) and sp.status = '0'
|
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<!-- 联合查询:出国记录 + 已完成审批的申请 -->
|
<!-- 联合查询:出国记录 + 已完成审批的申请 -->
|
||||||
@@ -142,8 +141,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
-- 查询已创建的出国记录(关联申请表获取扩展字段)
|
-- 查询已创建的出国记录(关联申请表获取扩展字段)
|
||||||
select
|
select
|
||||||
ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department,
|
ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department,
|
||||||
ors.position,
|
oa.position,
|
||||||
COALESCE(sp1.post_name, ors.position) as position_text,
|
oa.position as position_text,
|
||||||
ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city,
|
ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city,
|
||||||
ors.actual_start_date as actual_start_date, ors.actual_end_date as actual_end_date, ors.actual_duration as actual_duration,
|
ors.actual_start_date as actual_start_date, ors.actual_end_date as actual_end_date, ors.actual_duration as actual_duration,
|
||||||
ors.actual_budget as actual_budget,
|
ors.actual_budget as actual_budget,
|
||||||
@@ -212,7 +211,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
'record' as data_source
|
'record' as data_source
|
||||||
from rs_overseas_record ors
|
from rs_overseas_record ors
|
||||||
left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0'
|
left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0'
|
||||||
left join sys_post sp1 on (ors.position = CAST(sp1.post_id AS CHAR) or ors.position = sp1.post_code) and sp1.status = '0'
|
|
||||||
where ors.deleted = '0'
|
where ors.deleted = '0'
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
@@ -222,7 +220,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
NULL as record_id,
|
NULL as record_id,
|
||||||
oa.application_id, oa.applicant_id, oa.applicant_name, oa.department,
|
oa.application_id, oa.applicant_id, oa.applicant_name, oa.department,
|
||||||
oa.position,
|
oa.position,
|
||||||
COALESCE(sp2.post_name, oa.position) as position_text,
|
oa.position as position_text,
|
||||||
oa.contact_phone, oa.contact_email, oa.purpose, oa.purpose_detail, oa.destination, oa.destination_city,
|
oa.contact_phone, oa.contact_email, oa.purpose, oa.purpose_detail, oa.destination, oa.destination_city,
|
||||||
oa.start_date as actual_start_date, oa.end_date as actual_end_date, oa.duration as actual_duration,
|
oa.start_date as actual_start_date, oa.end_date as actual_end_date, oa.duration as actual_duration,
|
||||||
NULL as actual_budget,
|
NULL as actual_budget,
|
||||||
@@ -284,7 +282,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
'进行中' as completion_status_text,
|
'进行中' as completion_status_text,
|
||||||
'approved_application' as data_source
|
'approved_application' as data_source
|
||||||
from rs_overseas_application oa
|
from rs_overseas_application oa
|
||||||
left join sys_post sp2 on (oa.position = CAST(sp2.post_id AS CHAR) or oa.position = sp2.post_code) and sp2.status = '0'
|
|
||||||
where oa.deleted = '0'
|
where oa.deleted = '0'
|
||||||
and oa.status = '1'
|
and oa.status = '1'
|
||||||
and not exists (
|
and not exists (
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
-- 招聘模块表结构
|
|
||||||
-- 创建于 2025-07-02
|
|
||||||
|
|
||||||
-- 职位表
|
|
||||||
DROP TABLE IF EXISTS `rs_job`;
|
|
||||||
CREATE TABLE `rs_job` (
|
|
||||||
`job_id` bigint(20) NOT NULL COMMENT '职位ID',
|
|
||||||
`title` varchar(100) NOT NULL COMMENT '职位名称',
|
|
||||||
`department` varchar(50) NOT NULL COMMENT '部门',
|
|
||||||
`salary_range` varchar(50) DEFAULT NULL COMMENT '薪资范围',
|
|
||||||
`location` varchar(100) DEFAULT NULL COMMENT '工作地点',
|
|
||||||
`description` text COMMENT '职位描述',
|
|
||||||
`requirements` text COMMENT '任职要求',
|
|
||||||
`is_active` char(1) DEFAULT '0' COMMENT '是否在招(0是 1否)',
|
|
||||||
`publish_time` datetime DEFAULT NULL COMMENT '发布时间',
|
|
||||||
`view_count` int(11) DEFAULT '0' COMMENT '浏览次数',
|
|
||||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
|
|
||||||
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
|
|
||||||
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
|
||||||
`deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
|
||||||
PRIMARY KEY (`job_id`),
|
|
||||||
KEY `idx_department` (`department`),
|
|
||||||
KEY `idx_is_active` (`is_active`)
|
|
||||||
) ENGINE=InnoDB COMMENT='招聘职位表';
|
|
||||||
|
|
||||||
-- 应聘者表
|
|
||||||
DROP TABLE IF EXISTS `rs_applicant`;
|
|
||||||
CREATE TABLE `rs_applicant` (
|
|
||||||
`applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID',
|
|
||||||
`user_id` bigint(20) DEFAULT NULL COMMENT '关联用户ID',
|
|
||||||
`name` varchar(50) NOT NULL COMMENT '姓名',
|
|
||||||
`phone` varchar(20) NOT NULL COMMENT '手机号码',
|
|
||||||
`email` varchar(100) NOT NULL COMMENT '电子邮箱',
|
|
||||||
`resume_file_path` varchar(255) DEFAULT NULL COMMENT '简历文件路径',
|
|
||||||
`self_intro` text COMMENT '自我介绍',
|
|
||||||
`current_status` varchar(20) DEFAULT 'active' COMMENT '当前状态(active-可申请 hired-已录用 rejected-已拒绝)',
|
|
||||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
|
|
||||||
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
|
||||||
`deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
|
||||||
PRIMARY KEY (`applicant_id`),
|
|
||||||
KEY `idx_user_id` (`user_id`),
|
|
||||||
KEY `idx_phone` (`phone`)
|
|
||||||
) ENGINE=InnoDB COMMENT='应聘者信息表';
|
|
||||||
|
|
||||||
-- 教育经历表
|
|
||||||
DROP TABLE IF EXISTS `rs_education`;
|
|
||||||
CREATE TABLE `rs_education` (
|
|
||||||
`edu_id` bigint(20) NOT NULL COMMENT '教育经历ID',
|
|
||||||
`applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID',
|
|
||||||
`school_name` varchar(100) NOT NULL COMMENT '学校名称',
|
|
||||||
`major` varchar(100) DEFAULT NULL COMMENT '专业',
|
|
||||||
`degree` varchar(50) DEFAULT NULL COMMENT '学位',
|
|
||||||
`start_date` date DEFAULT NULL COMMENT '开始日期',
|
|
||||||
`end_date` date DEFAULT NULL COMMENT '结束日期',
|
|
||||||
`gpa` varchar(20) DEFAULT NULL COMMENT '绩点',
|
|
||||||
`description` text COMMENT '描述',
|
|
||||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
|
|
||||||
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
|
||||||
PRIMARY KEY (`edu_id`),
|
|
||||||
KEY `idx_applicant_id` (`applicant_id`)
|
|
||||||
) ENGINE=InnoDB COMMENT='教育经历表';
|
|
||||||
|
|
||||||
-- 工作经历表
|
|
||||||
DROP TABLE IF EXISTS `rs_work_experience`;
|
|
||||||
CREATE TABLE `rs_work_experience` (
|
|
||||||
`exp_id` bigint(20) NOT NULL COMMENT '工作经历ID',
|
|
||||||
`applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID',
|
|
||||||
`company_name` varchar(100) NOT NULL COMMENT '公司名称',
|
|
||||||
`position` varchar(100) NOT NULL COMMENT '职位',
|
|
||||||
`start_date` date DEFAULT NULL COMMENT '开始日期',
|
|
||||||
`end_date` date DEFAULT NULL COMMENT '结束日期',
|
|
||||||
`is_current` char(1) DEFAULT '0' COMMENT '是否当前工作(0是 1否)',
|
|
||||||
`description` text COMMENT '工作描述',
|
|
||||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
|
|
||||||
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
|
||||||
PRIMARY KEY (`exp_id`),
|
|
||||||
KEY `idx_applicant_id` (`applicant_id`)
|
|
||||||
) ENGINE=InnoDB COMMENT='工作经历表';
|
|
||||||
|
|
||||||
-- 申请记录表
|
|
||||||
DROP TABLE IF EXISTS `rs_application`;
|
|
||||||
CREATE TABLE `rs_application` (
|
|
||||||
`application_id` bigint(20) NOT NULL COMMENT '申请ID',
|
|
||||||
`job_id` bigint(20) NOT NULL COMMENT '职位ID',
|
|
||||||
`applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID',
|
|
||||||
`apply_time` datetime NOT NULL COMMENT '申请时间',
|
|
||||||
`status` varchar(20) DEFAULT 'pending' COMMENT '状态(pending-待处理 interviewed-已面试 offered-已录用 rejected-已拒绝)',
|
|
||||||
`hr_notes` text COMMENT 'HR备注',
|
|
||||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
|
|
||||||
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
|
||||||
`deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
|
||||||
PRIMARY KEY (`application_id`),
|
|
||||||
KEY `idx_job_id` (`job_id`),
|
|
||||||
KEY `idx_applicant_id` (`applicant_id`),
|
|
||||||
KEY `idx_status` (`status`)
|
|
||||||
) ENGINE=InnoDB COMMENT='申请记录表';
|
|
||||||
|
|
||||||
-- 附件表
|
|
||||||
DROP TABLE IF EXISTS `rs_attachment`;
|
|
||||||
CREATE TABLE `rs_attachment` (
|
|
||||||
`attachment_id` bigint(20) NOT NULL COMMENT '附件ID',
|
|
||||||
`application_id` bigint(20) NOT NULL COMMENT '申请ID',
|
|
||||||
`file_name` varchar(100) NOT NULL COMMENT '文件名',
|
|
||||||
`file_path` varchar(255) NOT NULL COMMENT '文件路径',
|
|
||||||
`file_type` varchar(50) DEFAULT NULL COMMENT '文件类型',
|
|
||||||
`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)',
|
|
||||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
|
|
||||||
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
|
||||||
PRIMARY KEY (`attachment_id`),
|
|
||||||
KEY `idx_application_id` (`application_id`)
|
|
||||||
) ENGINE=InnoDB COMMENT='申请附件表';
|
|
||||||
|
|
||||||
-- 插入初始测试数据
|
|
||||||
INSERT INTO `rs_job` (`job_id`, `title`, `department`, `salary_range`, `location`, `description`, `requirements`, `is_active`, `create_time`, `create_by`, `update_time`, `update_by`, `remark`, `deleted`, `tenant_id`)
|
|
||||||
VALUES
|
|
||||||
(1, '高级Java开发工程师', '教学', '15k-25k', '杭州', '负责校园教学管理系统的开发和维护,参与核心模块设计与实现,优化系统性能,解决技术难题。\n作为团队核心成员,参与技术方案评审,指导初级开发人员,确保项目按时高质量交付。', '计算机相关专业本科及以上学历,3年以上Java开发经验\n精通Java核心技术,熟悉Spring Boot、MyBatis等主流框架\n熟练掌握MySQL数据库设计与优化\n有教育行业相关系统开发经验者优先\n良好的沟通能力和团队协作精神', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'),
|
|
||||||
(2, '前端工程师', '教学', '12k-18k', '杭州', '负责校园教学管理系统前端开发,使用Vue3+TypeScript技术栈,参与UI设计与实现', '计算机相关专业本科及以上学历,2年以上前端开发经验\n精通HTML5/CSS3/JavaScript,熟悉Vue3/React等主流框架\n熟练使用TypeScript进行开发\n有教育行业相关系统开发经验者优先', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'),
|
|
||||||
(3, '行政助理', '行政', '5k-8k', '杭州', '协助部门主管处理日常行政事务,包括文件整理、会议安排、接待来访等工作', '大专及以上学历,行政管理相关专业优先\n熟悉办公软件,有较强的文字处理能力\n工作认真负责,有较强的组织协调能力\n有相关工作经验者优先', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000');
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
-- -----------------------------
|
|
||||||
-- 删除培训申请表中的提交次数字段
|
|
||||||
-- -----------------------------
|
|
||||||
|
|
||||||
-- 删除 rs_training_application 表中的 submit_count 字段
|
|
||||||
-- 注意:MySQL 5.7及以下版本不支持 DROP COLUMN IF EXISTS 语法
|
|
||||||
-- 需要先检查字段是否存在,然后删除
|
|
||||||
ALTER TABLE rs_training_application DROP COLUMN submit_count;
|
|
||||||
|
|
||||||
-- 验证字段已删除
|
|
||||||
DESCRIBE rs_training_application;
|
|
||||||
-62
@@ -1,62 +0,0 @@
|
|||||||
-- -----------------------------
|
|
||||||
-- 培训审批数据一致性检查和修复
|
|
||||||
-- -----------------------------
|
|
||||||
|
|
||||||
-- 检查审批记录表是否为空
|
|
||||||
SELECT COUNT(*) AS approval_count FROM rs_training_approval WHERE deleted = '0';
|
|
||||||
|
|
||||||
-- 检查审批记录与申请记录是否一致
|
|
||||||
SELECT
|
|
||||||
COUNT(*) AS inconsistent_count
|
|
||||||
FROM
|
|
||||||
rs_training_approval a
|
|
||||||
LEFT JOIN
|
|
||||||
rs_training_application app ON a.application_id = app.application_id
|
|
||||||
WHERE
|
|
||||||
a.deleted = '0'
|
|
||||||
AND (app.application_id IS NULL OR app.deleted = '1');
|
|
||||||
|
|
||||||
-- 插入测试数据(如果没有审批记录)
|
|
||||||
INSERT INTO rs_training_approval (
|
|
||||||
approval_id, application_id, approver_id, approver_name,
|
|
||||||
approval_level, status, opinion, approval_time, tenant_id,
|
|
||||||
create_by, create_time, deleted
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
FLOOR(RAND() * 1000000) + 100000, -- 随机审批ID
|
|
||||||
app.application_id, -- 使用真实申请ID
|
|
||||||
app.create_by, -- 使用申请的创建者ID作为审批人ID
|
|
||||||
(SELECT user_name FROM sys_user WHERE user_id = app.create_by LIMIT 1), -- 获取用户名
|
|
||||||
1, -- 审批级别
|
|
||||||
app.status, -- 使用申请状态
|
|
||||||
'系统自动生成的测试审批记录', -- 审批意见
|
|
||||||
app.update_time, -- 使用申请更新时间作为审批时间
|
|
||||||
app.tenant_id, -- 租户ID
|
|
||||||
app.create_by, -- 创建者
|
|
||||||
NOW(), -- 创建时间
|
|
||||||
'0' -- 未删除
|
|
||||||
FROM
|
|
||||||
rs_training_application app
|
|
||||||
WHERE
|
|
||||||
app.deleted = '0'
|
|
||||||
AND app.status IN ('1', '2') -- 已通过或已拒绝
|
|
||||||
AND NOT EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM rs_training_approval a
|
|
||||||
WHERE a.application_id = app.application_id AND a.deleted = '0'
|
|
||||||
)
|
|
||||||
LIMIT 5; -- 限制生成5条测试数据
|
|
||||||
|
|
||||||
-- 查询生成的测试数据
|
|
||||||
SELECT
|
|
||||||
a.approval_id, a.application_id, a.approver_name, a.status, a.opinion, a.approval_time,
|
|
||||||
app.user_name AS applicant_name, app.reason
|
|
||||||
FROM
|
|
||||||
rs_training_approval a
|
|
||||||
JOIN
|
|
||||||
rs_training_application app ON a.application_id = app.application_id
|
|
||||||
WHERE
|
|
||||||
a.deleted = '0'
|
|
||||||
AND a.create_time > DATE_SUB(NOW(), INTERVAL 1 HOUR) -- 最近一小时创建的记录
|
|
||||||
ORDER BY
|
|
||||||
a.create_time DESC;
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
-- ----------------------------
|
|
||||||
-- 培训管理模块测试数据
|
|
||||||
-- ----------------------------
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训课程表测试数据
|
|
||||||
-- ----------------------------
|
|
||||||
INSERT INTO `rs_training_course` (`course_id`, `title`, `description`, `instructor`, `duration`, `location`, `capacity`, `status`, `start_time`, `end_time`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
|
|
||||||
VALUES
|
|
||||||
(10001, '新入职教师培训', '针对新入职教师的岗前培训,包括教学理念、师德师风、教学方法等内容', '张教授', 16, '学术会议厅', 50, '1', '2025-08-01 09:00:00', '2025-08-03 17:00:00', '000000', 1, NOW(), 1, NOW(), '重要培训', '0'),
|
|
||||||
(10002, '教育技术与信息化', '介绍现代教育技术在教学中的应用,包括多媒体教学、在线教育平台等', '李博士', 12, '多媒体教室', 30, '1', '2025-08-10 09:00:00', '2025-08-12 17:00:00', '000000', 1, NOW(), 1, NOW(), '技术培训', '0'),
|
|
||||||
(10003, '教学方法与技能培训', '提升教师教学技能,包括课堂管理、学生互动、教学评估等', '王老师', 8, '实训楼', 40, '1', '2025-08-05 14:00:00', '2025-08-05 18:00:00', '000000', 1, NOW(), 1, NOW(), '实用培训', '0'),
|
|
||||||
(10004, '学生管理与心理健康', '帮助教师了解学生心理,掌握学生管理技巧和心理疏导方法', '陈心理师', 6, '学术报告厅', 60, '1', '2025-08-15 14:00:00', '2025-08-15 18:00:00', '000000', 1, NOW(), 1, NOW(), '心理培训', '0'),
|
|
||||||
(10005, '科研方法与学术写作', '指导教师进行科学研究,提升学术写作和论文发表能力', '赵教授', 20, '图书馆报告厅', 25, '1', '2025-08-25 09:00:00', '2025-08-30 17:00:00', '000000', 1, NOW(), 1, NOW(), '科研培训', '0');
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训申请表测试数据
|
|
||||||
-- ----------------------------
|
|
||||||
INSERT INTO `rs_training_application` (`application_id`, `course_id`, `user_id`, `user_name`, `department`, `position`, `reason`, `status`, `apply_time`, `current_approver`, `process_instance_id`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
|
|
||||||
VALUES
|
|
||||||
(20001, 10001, 2, '张老师', '教务处', '教师', '新入职需要培训', '1', '2025-07-25 12:03:49', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), NULL, '0'),
|
|
||||||
(20002, 10002, 3, '王老师', '信息技术中心', '工程师', '学习新技术', '1', '2025-08-01 12:37:17', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'),
|
|
||||||
(20003, 10003, 4, '刘老师', '计算机学院', '讲师', '提升教学技能', '1', '2025-07-25 12:04:06', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '培训尚未结束', '0'),
|
|
||||||
(20004, 10004, 5, '李老师', '学生处', '辅导员', '提升学生管理能力', '1', '2025-08-06 14:24:37', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'),
|
|
||||||
(20005, 10005, 6, '陈老师', '科研处', '副教授', '提升科研能力', '1', '2025-08-22 15:25:51', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0');
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训审批表测试数据
|
|
||||||
-- ----------------------------
|
|
||||||
INSERT INTO `rs_training_approval` (`approval_id`, `application_id`, `approver_id`, `approver_name`, `approval_level`, `status`, `opinion`, `approval_time`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
|
|
||||||
VALUES
|
|
||||||
(30001, 20001, 101, '王主任', 1, '1', '同意参加,安排好工作交接', '2025-07-21 09:30:00', '000000', 101, NOW(), 101, NOW(), NULL, '0'),
|
|
||||||
(30002, 20003, 102, '孙院长', 1, '1', '批准参加,对提升教学质量有帮助', '2025-07-23 10:15:00', '000000', 102, NOW(), 102, NOW(), NULL, '0'),
|
|
||||||
(30003, 20004, 103, '周主任', 1, '2', '近期有重要工作交接,建议下次再参加', '2025-07-24 14:20:00', '000000', 103, NOW(), 103, NOW(), NULL, '0');
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训记录表测试数据已删除,请通过申请界面新增
|
|
||||||
-- ----------------------------
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
-- -----------------------------
|
|
||||||
-- 培训审批数据修复脚本
|
|
||||||
-- -----------------------------
|
|
||||||
|
|
||||||
-- 检查comment列是否存在,如果存在则迁移数据到opinion列
|
|
||||||
SET @columnExists = 0;
|
|
||||||
SELECT COUNT(*) INTO @columnExists
|
|
||||||
FROM INFORMATION_SCHEMA.COLUMNS
|
|
||||||
WHERE TABLE_SCHEMA = DATABASE()
|
|
||||||
AND TABLE_NAME = 'rs_training_approval'
|
|
||||||
AND COLUMN_NAME = 'comment';
|
|
||||||
|
|
||||||
-- 如果comment列存在,将数据从comment迁移到opinion
|
|
||||||
SET @sqlStmt = IF(@columnExists > 0,
|
|
||||||
'UPDATE rs_training_approval SET opinion = comment WHERE opinion IS NULL AND comment IS NOT NULL',
|
|
||||||
'SELECT "comment列不存在,无需迁移" AS message');
|
|
||||||
|
|
||||||
PREPARE stmt FROM @sqlStmt;
|
|
||||||
EXECUTE stmt;
|
|
||||||
DEALLOCATE PREPARE stmt;
|
|
||||||
|
|
||||||
-- 添加测试数据(如果表为空)
|
|
||||||
INSERT INTO rs_training_approval (
|
|
||||||
approval_id, application_id, approver_id, approver_name,
|
|
||||||
approval_level, status, opinion, approval_time, tenant_id,
|
|
||||||
create_by, create_time, deleted
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
FLOOR(RAND() * 1000000) + 100000, -- 随机ID
|
|
||||||
app.application_id, -- 关联到真实的申请ID
|
|
||||||
1, -- 管理员ID
|
|
||||||
'系统管理员', -- 管理员名称
|
|
||||||
1, -- 第一级审批
|
|
||||||
'1', -- 已通过状态
|
|
||||||
'系统自动生成的测试审批记录', -- 审批意见
|
|
||||||
NOW(), -- 当前时间
|
|
||||||
app.tenant_id, -- 使用申请的租户ID
|
|
||||||
app.create_by, -- 使用申请的创建者
|
|
||||||
NOW(), -- 当前时间
|
|
||||||
'0' -- 未删除
|
|
||||||
FROM rs_training_application app
|
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM rs_training_approval)
|
|
||||||
LIMIT 1;
|
|
||||||
-58
@@ -1,58 +0,0 @@
|
|||||||
-- -----------------------------
|
|
||||||
-- 培训审批历史记录完整性更新脚本
|
|
||||||
-- -----------------------------
|
|
||||||
|
|
||||||
-- 1. 在培训申请表中添加提交次数字段
|
|
||||||
ALTER TABLE rs_training_application ADD COLUMN IF NOT EXISTS submit_count int DEFAULT 1 COMMENT '提交次数';
|
|
||||||
|
|
||||||
-- 2. 检查并移除审批表可能存在的唯一约束(允许一个申请有多条审批记录)
|
|
||||||
ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_approver;
|
|
||||||
ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_level;
|
|
||||||
|
|
||||||
-- 3. 确保审批表中有足够的索引来高效查询
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_training_approval_application ON rs_training_approval(application_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_training_approval_status ON rs_training_approval(status);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_training_approval_time ON rs_training_approval(approval_time);
|
|
||||||
|
|
||||||
-- 4. 添加审批记录表注释说明
|
|
||||||
ALTER TABLE rs_training_approval COMMENT = '培训审批表(记录所有审批动作,包括通过和拒绝记录)';
|
|
||||||
|
|
||||||
-- 5. 更新已有数据的提交次数为1(如果有)
|
|
||||||
UPDATE rs_training_application SET submit_count = 1 WHERE submit_count IS NULL;
|
|
||||||
|
|
||||||
-- 6. 检查并修复可能的数据不一致问题
|
|
||||||
-- 查找有申请状态为已拒绝但没有对应拒绝记录的数据
|
|
||||||
SELECT
|
|
||||||
app.application_id,
|
|
||||||
app.course_id,
|
|
||||||
app.user_name,
|
|
||||||
app.status,
|
|
||||||
COUNT(appr.approval_id) as approval_count
|
|
||||||
FROM
|
|
||||||
rs_training_application app
|
|
||||||
LEFT JOIN
|
|
||||||
rs_training_approval appr ON app.application_id = appr.application_id
|
|
||||||
WHERE
|
|
||||||
app.status = '2' AND app.deleted = '0'
|
|
||||||
GROUP BY
|
|
||||||
app.application_id
|
|
||||||
HAVING
|
|
||||||
approval_count = 0;
|
|
||||||
|
|
||||||
-- 查找有申请状态为已通过但没有对应通过记录的数据
|
|
||||||
SELECT
|
|
||||||
app.application_id,
|
|
||||||
app.course_id,
|
|
||||||
app.user_name,
|
|
||||||
app.status,
|
|
||||||
COUNT(appr.approval_id) as approval_count
|
|
||||||
FROM
|
|
||||||
rs_training_application app
|
|
||||||
LEFT JOIN
|
|
||||||
rs_training_approval appr ON app.application_id = appr.application_id
|
|
||||||
WHERE
|
|
||||||
app.status = '1' AND app.deleted = '0'
|
|
||||||
GROUP BY
|
|
||||||
app.application_id
|
|
||||||
HAVING
|
|
||||||
approval_count = 0;
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
-- -----------------------------
|
|
||||||
-- 培训审批历史记录更新
|
|
||||||
-- -----------------------------
|
|
||||||
|
|
||||||
-- 确保审批表中没有唯一约束,允许一个申请有多条审批记录
|
|
||||||
ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_approver;
|
|
||||||
|
|
||||||
-- 添加审批意见列(如果不存在)
|
|
||||||
ALTER TABLE rs_training_approval ADD COLUMN IF NOT EXISTS opinion varchar(500) COMMENT '审批意见' AFTER status;
|
|
||||||
|
|
||||||
-- 添加comment列的别名以保持向后兼容(如果存在)
|
|
||||||
ALTER TABLE rs_training_approval CHANGE COLUMN comment opinion varchar(500) COMMENT '审批意见';
|
|
||||||
|
|
||||||
-- 更新审批记录表注释
|
|
||||||
ALTER TABLE rs_training_approval COMMENT = '培训审批表(记录所有审批动作,包括拒绝记录)';
|
|
||||||
|
|
||||||
-- 添加相关索引以提高查询效率
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_training_approval_application ON rs_training_approval(application_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_training_approval_status ON rs_training_approval(status);
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
-- 更新培训课程表,添加create_dept字段
|
|
||||||
ALTER TABLE rs_training_course ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id;
|
|
||||||
|
|
||||||
-- 更新培训申请表,添加create_dept字段
|
|
||||||
ALTER TABLE rs_training_application ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id;
|
|
||||||
|
|
||||||
-- 更新培训申请表,添加location、duration、startTime和endTime字段
|
|
||||||
ALTER TABLE rs_training_application ADD COLUMN location varchar(200) COMMENT '培训地点' AFTER current_approver;
|
|
||||||
ALTER TABLE rs_training_application ADD COLUMN duration int(11) COMMENT '学时' AFTER location;
|
|
||||||
ALTER TABLE rs_training_application ADD COLUMN start_time datetime COMMENT '开始时间' AFTER duration;
|
|
||||||
ALTER TABLE rs_training_application ADD COLUMN end_time datetime COMMENT '结束时间' AFTER start_time;
|
|
||||||
|
|
||||||
-- 更新培训审批表,添加create_dept字段
|
|
||||||
ALTER TABLE rs_training_approval ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id;
|
|
||||||
|
|
||||||
-- 更新培训记录表,添加create_dept字段
|
|
||||||
ALTER TABLE rs_training_record ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id;
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
-- ----------------------------
|
|
||||||
-- 培训管理模块数据库表
|
|
||||||
-- ----------------------------
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训课程表
|
|
||||||
-- ----------------------------
|
|
||||||
drop table if exists rs_training_course;
|
|
||||||
create table rs_training_course (
|
|
||||||
course_id bigint(20) not null comment '课程ID',
|
|
||||||
title varchar(100) not null comment '课程名称',
|
|
||||||
category varchar(50) not null comment '课程类别',
|
|
||||||
description text comment '课程描述',
|
|
||||||
start_time datetime not null comment '开始时间',
|
|
||||||
end_time datetime not null comment '结束时间',
|
|
||||||
duration int(11) not null comment '学时数',
|
|
||||||
location varchar(200) comment '培训地点',
|
|
||||||
max_participants int(11) default 0 comment '最大参与人数',
|
|
||||||
current_participants int(11) default 0 comment '当前报名人数',
|
|
||||||
status char(1) default '0' comment '状态(0正常 1停用)',
|
|
||||||
tenant_id varchar(20) default '000000' comment '租户编号',
|
|
||||||
create_by bigint(20) comment '创建者',
|
|
||||||
create_time datetime comment '创建时间',
|
|
||||||
update_by bigint(20) comment '更新者',
|
|
||||||
update_time datetime comment '更新时间',
|
|
||||||
remark varchar(500) comment '备注',
|
|
||||||
deleted char(1) default '0' comment '删除标志',
|
|
||||||
primary key (course_id)
|
|
||||||
) engine=innodb comment = '培训课程表';
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训申请表
|
|
||||||
-- ----------------------------
|
|
||||||
drop table if exists rs_training_application;
|
|
||||||
create table rs_training_application (
|
|
||||||
application_id bigint(20) not null comment '申请ID',
|
|
||||||
course_name varchar(100) not null comment '培训名称',
|
|
||||||
user_id bigint(20) not null comment '申请人ID',
|
|
||||||
user_name varchar(50) not null comment '申请人姓名',
|
|
||||||
department varchar(50) not null comment '所属部门',
|
|
||||||
position varchar(50) comment '职位',
|
|
||||||
reason text comment '申请理由',
|
|
||||||
status char(1) default '0' comment '状态(0待审核 1已通过 2已拒绝 3进行中 4已完成)',
|
|
||||||
apply_time datetime comment '申请时间',
|
|
||||||
current_approver varchar(50) comment '当前审批人',
|
|
||||||
location varchar(200) comment '培训地点',
|
|
||||||
duration int(11) comment '学时',
|
|
||||||
start_time datetime comment '开始时间',
|
|
||||||
end_time datetime comment '结束时间',
|
|
||||||
tenant_id varchar(20) default '000000' comment '租户编号',
|
|
||||||
create_dept bigint(20) comment '创建部门',
|
|
||||||
reject_time datetime comment '拒绝时间',
|
|
||||||
create_by bigint(20) comment '创建者',
|
|
||||||
create_time datetime comment '创建时间',
|
|
||||||
update_by bigint(20) comment '更新者',
|
|
||||||
update_time datetime comment '更新时间',
|
|
||||||
remark varchar(500) comment '备注',
|
|
||||||
deleted char(1) default '0' comment '删除标志',
|
|
||||||
primary key (application_id)
|
|
||||||
) engine=innodb comment = '培训申请表';
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训审批表
|
|
||||||
-- ----------------------------
|
|
||||||
drop table if exists rs_training_approval;
|
|
||||||
create table rs_training_approval (
|
|
||||||
approval_id bigint(20) not null comment '审批ID',
|
|
||||||
application_id bigint(20) not null comment '申请ID',
|
|
||||||
approver_id bigint(20) not null comment '审批人ID',
|
|
||||||
approver_name varchar(50) not null comment '审批人姓名',
|
|
||||||
approval_level int(11) not null comment '审批级别(1部门主管 2分管领导 3校领导)',
|
|
||||||
status char(1) default '0' comment '状态(0待审核 1已通过 2已拒绝)',
|
|
||||||
opinion varchar(500) comment '审批意见',
|
|
||||||
approval_time datetime comment '审批时间',
|
|
||||||
tenant_id varchar(20) default '000000' comment '租户编号',
|
|
||||||
create_by bigint(20) comment '创建者',
|
|
||||||
create_time datetime comment '创建时间',
|
|
||||||
update_by bigint(20) comment '更新者',
|
|
||||||
update_time datetime comment '更新时间',
|
|
||||||
remark varchar(500) comment '备注',
|
|
||||||
deleted char(1) default '0' comment '删除标志',
|
|
||||||
primary key (approval_id)
|
|
||||||
) engine=innodb comment = '培训审批表';
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- 培训记录表
|
|
||||||
-- ----------------------------
|
|
||||||
drop table if exists rs_training_record;
|
|
||||||
create table rs_training_record (
|
|
||||||
record_id bigint(20) not null comment '记录ID',
|
|
||||||
application_id bigint(20) not null comment '申请ID',
|
|
||||||
course_id bigint(20) not null comment '课程ID',
|
|
||||||
user_id bigint(20) not null comment '用户ID',
|
|
||||||
user_name varchar(50) not null comment '用户姓名',
|
|
||||||
completion_status char(1) default '0' comment '完成状态(0进行中 1已完成 2未完成)',
|
|
||||||
completion_time datetime comment '完成时间',
|
|
||||||
score decimal(5,2) comment '成绩',
|
|
||||||
certificate_path varchar(255) comment '证书路径',
|
|
||||||
certificate_number varchar(50) comment '证书编号',
|
|
||||||
attachment_ids varchar(255) comment '证书附件ID(多个用逗号分隔)',
|
|
||||||
feedback text comment '培训反馈',
|
|
||||||
location varchar(255) comment '培训地点',
|
|
||||||
duration int(11) comment '学时',
|
|
||||||
apply_time datetime comment '申请时间',
|
|
||||||
status varchar(10) comment '申请状态',
|
|
||||||
course_name varchar(255) comment '课程名称',
|
|
||||||
department varchar(255) comment '所属部门',
|
|
||||||
position varchar(255) comment '职位',
|
|
||||||
reason varchar(255) comment '申请理由',
|
|
||||||
current_approver varchar(255) comment '当前审批人',
|
|
||||||
start_time datetime comment '开始时间',
|
|
||||||
end_time datetime comment '结束时间',
|
|
||||||
tenant_id varchar(20) default '000000' comment '租户编号',
|
|
||||||
create_dept bigint(20) comment '创建部门',
|
|
||||||
create_by bigint(20) comment '创建者',
|
|
||||||
create_time datetime comment '创建时间',
|
|
||||||
update_by bigint(20) comment '更新者',
|
|
||||||
update_time datetime comment '更新时间',
|
|
||||||
remark varchar(500) comment '备注',
|
|
||||||
deleted char(1) default '0' comment '删除标志',
|
|
||||||
primary key (record_id)
|
|
||||||
) engine=innodb comment = '培训记录表';
|
|
||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
-- 添加自定义课程名称字段
|
|
||||||
-- 创建于 2025-07-24
|
|
||||||
|
|
||||||
-- 在rs_training_application表中添加course_name列,用于存储自定义培训名称
|
|
||||||
ALTER TABLE `rs_training_application` ADD COLUMN `course_name` varchar(100) DEFAULT NULL COMMENT '自定义课程名称' AFTER `course_id`;
|
|
||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
-- 为rs_application表添加附件相关字段
|
|
||||||
ALTER TABLE `rs_application`
|
|
||||||
ADD COLUMN `attachment_path` varchar(255) DEFAULT NULL COMMENT '附件路径' AFTER `self_description`,
|
|
||||||
ADD COLUMN `attachment_name` varchar(255) DEFAULT NULL COMMENT '附件名称' AFTER `attachment_path`;
|
|
||||||
|
|
||||||
-- 更新表注释
|
|
||||||
ALTER TABLE `rs_application` COMMENT '应聘申请记录表';
|
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
-- =============================================
|
||||||
|
-- 修改 rs_overseas_application 表结构
|
||||||
|
-- 将 party_retention_file_oss_id 字段从 bigint 改为 varchar(500)
|
||||||
|
-- 以支持存储多个附件OSS ID(逗号分隔)
|
||||||
|
-- 创建时间: 2025-11-27
|
||||||
|
-- =============================================
|
||||||
|
|
||||||
|
-- 修改字段类型(MySQL/MariaDB)
|
||||||
|
ALTER TABLE rs_overseas_application
|
||||||
|
MODIFY COLUMN party_retention_file_oss_id VARCHAR(500) COMMENT '党籍保留附件OSS ID(多个ID用逗号分隔)';
|
||||||
|
|
||||||
|
-- 注意:
|
||||||
|
-- 1. 此脚本会保留现有数据,bigint值会自动转换为字符串
|
||||||
|
-- 2. 建议在执行前备份数据库
|
||||||
|
-- 3. 如果字段当前有NOT NULL约束,会被保留
|
||||||
@@ -316,7 +316,7 @@ interface OverseasApprovalVO {
|
|||||||
approvalId: number | string; // 审批ID
|
approvalId: number | string; // 审批ID
|
||||||
applicationId: number; // 关联申请ID
|
applicationId: number; // 关联申请ID
|
||||||
approverId: number; // 审批人ID
|
approverId: number; // 审批人ID
|
||||||
approverName: string; // 审批人姓名
|
approverName: string; // 审批人姓名(优先返回昵称,缺省时回退账号)
|
||||||
approvalLevel: string; // 审批级别编码
|
approvalLevel: string; // 审批级别编码
|
||||||
approvalStatus: string; // 审批状态编码
|
approvalStatus: string; // 审批状态编码
|
||||||
approvalOpinion: string; // 审批意见
|
approvalOpinion: string; // 审批意见
|
||||||
@@ -329,6 +329,8 @@ interface OverseasApprovalVO {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 🆕 **2025-11-28 更新**:`approverName` 字段统一返回审批人的昵称,若用户未设置昵称则自动回退到账号,确保审批记录界面展示真实姓名。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔧 核心接口详细说明
|
## 🔧 核心接口详细说明
|
||||||
@@ -1048,6 +1050,7 @@ PDF模板包含以下空白填写区域(完全手工填写):
|
|||||||
- 申请表单:党员且出国天数>30天时自动显示
|
- 申请表单:党员且出国天数>30天时自动显示
|
||||||
- 申请详情:符合条件时在弹窗底部显示绿色按钮
|
- 申请详情:符合条件时在弹窗底部显示绿色按钮
|
||||||
- **前端实现**:必须使用 `request` 工具而不是 `fetch` API,以确保包含所有必要的认证请求头
|
- **前端实现**:必须使用 `request` 工具而不是 `fetch` API,以确保包含所有必要的认证请求头
|
||||||
|
- **附件格式**:自 2025-11-27 起,党籍保留附件支持任意文件类型上传,仅保留 10MB 单文件大小限制
|
||||||
|
|
||||||
#### 错误处理
|
#### 错误处理
|
||||||
|
|
||||||
@@ -1242,4 +1245,20 @@ import { getCurrentApproversForTask } from '@/api/workflow/common';
|
|||||||
|
|
||||||
**更新时间**:2025-10-28
|
**更新时间**:2025-10-28
|
||||||
**维护人员**:开发团队
|
**维护人员**:开发团队
|
||||||
**变更记录**:新增工作流动态审批人方案说明
|
**变更记录**:新增工作流动态审批人方案说明
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2025-11-28 更新:出国记录回执下载
|
||||||
|
|
||||||
|
- **前端**:`web/src/views/rs/overseas/index.vue` 中的 “下载因私出国(境)情况回执” 按钮改为直接基于 Word 模板生成文件,摆脱后端 PDF 下载接口依赖。
|
||||||
|
- **模板**:`web/public/chuguoqingkuanghuizhi.docx`,占位符 `{{applicantName}}`、`{{startYear}}`、`{{startMonth}}`、`{{startDay}}`、`{{endYear}}`、`{{endMonth}}`、`{{endDay}}`、`{{destinationText}}`、`{{exitReasonText}}`、`{{durationDays}}`、`{{expenseType}}`。
|
||||||
|
- **数据映射**:优先使用记录中的实际出行信息(`actualStartDate`、`actualEndDate`、`destinationCountry`、`exitReason` 等),缺省时回落到申请单数据,行程天数自动根据起止日期计算,默认费用说明为“费用自理”。
|
||||||
|
- **交互提示**:增加起止日期缺失时的前端校验,模板生成失败会提示用户检查模板或稍后再试。
|
||||||
|
|
||||||
|
### 2025-11-28 更新:审批监控页面审批表导出
|
||||||
|
|
||||||
|
- **前端**:`web/src/views/rs/overseas/monitor/index.vue` 的 “下载审批表” 按钮从 PDF 调整为 Word 导出,保持统一的 docxtemplater + image module 生成逻辑。
|
||||||
|
- **模板**:`web/public/chuguojingshenpibiao.docx`,覆盖个人信息、行程信息、申请人签名及五级审批节点(部门负责人、人事处、纪委、学院分管领导、学院党委书记)的签名、意见、日期占位符,支持 `{{%signature}}` 形式的图片变量。
|
||||||
|
- **数据来源**:优先取 `currentDetail` 详情接口返回值;审批节点数据由 `approvalHistory` 根据 `approvalLevel` 映射,若无签名/日期则留空。详情接口新增 `signature` 字段(申请人手写签名 base64),供模板直接使用。
|
||||||
|
- **交互提示**:仅在审批完成(状态为 `1`)时显示导出按钮,生成失败时给出友好提示并引导检查模板。
|
||||||
@@ -144,6 +144,10 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 字段补充说明(2025-11-28)
|
||||||
|
|
||||||
|
- `approverName`:后台统一返回审批人的 **昵称**,若用户未设置昵称则自动回退到账号;前端无需再做用户名到昵称的手动映射。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 3. 查询待审批列表
|
### 3. 查询待审批列表
|
||||||
|
|||||||
+5
-1
@@ -34,9 +34,12 @@
|
|||||||
"await-to-js": "3.0.0",
|
"await-to-js": "3.0.0",
|
||||||
"axios": "1.8.4",
|
"axios": "1.8.4",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
|
"docx": "^9.5.1",
|
||||||
|
"docxtemplater": "^3.67.5",
|
||||||
|
"docxtemplater-image-module-free": "^1.1.1",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"element-plus": "2.9.8",
|
"element-plus": "2.9.8",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"highlight.js": "11.9.0",
|
"highlight.js": "11.9.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"image-conversion": "2.1.1",
|
"image-conversion": "2.1.1",
|
||||||
@@ -46,6 +49,7 @@
|
|||||||
"jspdf-font": "^1.0.7",
|
"jspdf-font": "^1.0.7",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"pinia": "3.0.2",
|
"pinia": "3.0.2",
|
||||||
|
"pizzip": "^3.2.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"screenfull": "6.0.2",
|
"screenfull": "6.0.2",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -70,6 +70,7 @@ export interface OverseasRegistrationForm {
|
|||||||
officePhone?: string; // 办公电话
|
officePhone?: string; // 办公电话
|
||||||
mobilePhone?: string; // 移动电话
|
mobilePhone?: string; // 移动电话
|
||||||
remarks?: string; // 备注
|
remarks?: string; // 备注
|
||||||
|
status?: string; // 登记状态:0-草稿,1-待审核,2-已通过,3-已拒绝
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登记历史查询参数类型
|
// 登记历史查询参数类型
|
||||||
@@ -142,6 +143,17 @@ export function getOverseasRegistrationList(query: OverseasRegistrationQuery) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询我的出国登记列表(只查询当前登录人创建的记录)
|
||||||
|
*/
|
||||||
|
export function getMyOverseasRegistrationList(query: OverseasRegistrationQuery) {
|
||||||
|
return request({
|
||||||
|
url: '/rs/overseas/registration/my/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询出国登记详细
|
* 查询出国登记详细
|
||||||
*/
|
*/
|
||||||
@@ -433,4 +445,4 @@ export function batchRejectOverseasRegistration(registrationIds: Array<number |
|
|||||||
...data
|
...data
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ export interface OverseasApplicationVO {
|
|||||||
partyRetentionFileName?: string; // 党籍保留附件文件名
|
partyRetentionFileName?: string; // 党籍保留附件文件名
|
||||||
partyRetentionUploadTime?: string; // 党籍保留附件上传时间
|
partyRetentionUploadTime?: string; // 党籍保留附件上传时间
|
||||||
partyRetentionUploadStatus?: string; // 党籍保留附件上传状态
|
partyRetentionUploadStatus?: string; // 党籍保留附件上传状态
|
||||||
partyRetentionFileOssId?: number; // 党籍保留附件OSS ID
|
partyRetentionFileOssId?: string; // 党籍保留附件OSS ID(支持多个ID用逗号分隔)
|
||||||
purpose: string;
|
purpose: string;
|
||||||
purposeDetail: string;
|
purposeDetail: string;
|
||||||
destination: string;
|
destination: string;
|
||||||
@@ -137,6 +137,7 @@ export interface OverseasApplicationForm {
|
|||||||
partyRetentionFileName?: string; // 党籍保留附件文件名
|
partyRetentionFileName?: string; // 党籍保留附件文件名
|
||||||
partyRetentionUploadTime?: string; // 党籍保留附件上传时间
|
partyRetentionUploadTime?: string; // 党籍保留附件上传时间
|
||||||
partyRetentionUploadStatus?: string; // 党籍保留附件上传状态:0-未上传,1-已上传
|
partyRetentionUploadStatus?: string; // 党籍保留附件上传状态:0-未上传,1-已上传
|
||||||
|
partyRetentionFileOssId?: string; // 党籍保留附件OSS ID(支持多个ID用逗号分隔)
|
||||||
purpose: string;
|
purpose: string;
|
||||||
purposeDetail: string;
|
purposeDetail: string;
|
||||||
destination: string;
|
destination: string;
|
||||||
@@ -341,6 +342,15 @@ export function listPendingOverseasApplication() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询当前用户的待审批申请分页列表(根据角色过滤,支持分页和搜索)
|
||||||
|
export function listPendingOverseasApplicationPage(query: OverseasApplicationQuery) {
|
||||||
|
return request({
|
||||||
|
url: '/rs/overseas/application/pending/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 🚨 标准化改造:删除自定义待审批查询API
|
// 🚨 标准化改造:删除自定义待审批查询API
|
||||||
//
|
//
|
||||||
@@ -805,6 +815,7 @@ export interface ItineraryChangeQuery {
|
|||||||
applicantName?: string;
|
applicantName?: string;
|
||||||
destination?: string;
|
destination?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
|
applicantId?: number;
|
||||||
params?: Record<string, any>;
|
params?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -842,8 +853,8 @@ export interface ItineraryChangeVO {
|
|||||||
status: string;
|
status: string;
|
||||||
statusText: string;
|
statusText: string;
|
||||||
approvalOpinion?: string;
|
approvalOpinion?: string;
|
||||||
approvalBy?: number;
|
approverId?: number;
|
||||||
approvalByName?: string;
|
approverName?: string;
|
||||||
approvalTime?: string;
|
approvalTime?: string;
|
||||||
createTime: string;
|
createTime: string;
|
||||||
updateTime?: string;
|
updateTime?: string;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
const [err] = await tos(useUserStore().getInfo());
|
const [err] = await tos(useUserStore().getInfo());
|
||||||
if (err) {
|
if (err) {
|
||||||
await useUserStore().logout();
|
await useUserStore().logout();
|
||||||
ElMessage.error(err);
|
// ElMessage.error(err.message || '获取用户信息失败');
|
||||||
next({ path: '/' });
|
next({ path: '/' });
|
||||||
} else {
|
} else {
|
||||||
isRelogin.show = false;
|
isRelogin.show = false;
|
||||||
|
|||||||
@@ -204,8 +204,14 @@ service.interceptors.response.use(
|
|||||||
err.response = res;
|
err.response = res;
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
} else if (code !== HttpStatus.SUCCESS) {
|
} else if (code !== HttpStatus.SUCCESS) {
|
||||||
ElNotification.error({ title: msg });
|
// 确保错误消息不为空,避免只显示图标没有文字
|
||||||
const err = new Error(msg) as any;
|
const errorMsg = msg || errorCode['default'] || '操作失败,请稍后重试';
|
||||||
|
ElNotification.error({
|
||||||
|
title: '错误提示',
|
||||||
|
message: errorMsg,
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
const err = new Error(errorMsg) as any;
|
||||||
err.response = res;
|
err.response = res;
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+22
-6
@@ -192,13 +192,29 @@ const getLoginData = () => {
|
|||||||
* 获取租户列表
|
* 获取租户列表
|
||||||
*/
|
*/
|
||||||
const initTenantList = async () => {
|
const initTenantList = async () => {
|
||||||
const { data } = await getTenantList(false);
|
try {
|
||||||
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
|
const [err, res] = await to(getTenantList(false));
|
||||||
if (tenantEnabled.value) {
|
if (err) {
|
||||||
tenantList.value = data.voList;
|
// 静默处理租户列表获取失败,不影响登录功能
|
||||||
if (tenantList.value != null && tenantList.value.length !== 0) {
|
console.warn('获取租户列表失败:', err);
|
||||||
loginForm.value.tenantId = tenantList.value[0].tenantId;
|
// 设置默认值,确保登录功能正常
|
||||||
|
tenantEnabled.value = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (res && res.data) {
|
||||||
|
const { data } = res;
|
||||||
|
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
|
||||||
|
if (tenantEnabled.value) {
|
||||||
|
tenantList.value = data.voList;
|
||||||
|
if (tenantList.value != null && tenantList.value.length !== 0) {
|
||||||
|
loginForm.value.tenantId = tenantList.value[0].tenantId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 捕获其他可能的错误,静默处理
|
||||||
|
console.warn('初始化租户列表时发生错误:', error);
|
||||||
|
tenantEnabled.value = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1142
-573
File diff suppressed because it is too large.
Load diff
@@ -1,30 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-card class="box-card">
|
<el-card class="box-card">
|
||||||
<!-- 🔴 标准化改造:提示用户使用标准工作流页面 -->
|
|
||||||
<el-alert
|
|
||||||
title="功能迁移通知"
|
|
||||||
type="info"
|
|
||||||
description="出国行程变更审批功能已标准化,请使用系统统一的工作流页面查看和处理任务"
|
|
||||||
show-icon
|
|
||||||
:closable="false"
|
|
||||||
class="mb-4"
|
|
||||||
>
|
|
||||||
<template #default>
|
|
||||||
<div class="mt-2">
|
|
||||||
<p><strong>推荐操作:</strong></p>
|
|
||||||
<ul class="ml-4">
|
|
||||||
<li>访问 <strong>工作流 > 我的任务 > 待办任务</strong> 页面</li>
|
|
||||||
<li>在统一的待办列表中查看"出国行程变更审批"任务</li>
|
|
||||||
<li>使用工作流的筛选功能按业务类型查看</li>
|
|
||||||
</ul>
|
|
||||||
<div class="mt-3">
|
|
||||||
<el-button type="primary" @click="navigateToTaskWaiting"> 前往待办任务页面 </el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-alert>
|
|
||||||
|
|
||||||
<!-- 审批管理选项卡 -->
|
<!-- 审批管理选项卡 -->
|
||||||
<el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
|
<el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
|
||||||
<!-- 待审批 -->
|
<!-- 待审批 -->
|
||||||
@@ -69,13 +45,8 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 🔴 标准化改造:显示迁移提示 -->
|
<!-- 待审批变更列表 -->
|
||||||
<el-empty description="请使用标准工作流待办页面查看审批任务" :image-size="120">
|
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleView">
|
||||||
<el-button type="primary" @click="navigateToTaskWaiting">前往待办页面</el-button>
|
|
||||||
</el-empty>
|
|
||||||
|
|
||||||
<!-- 保留原表格结构用于兼容(隐藏显示) -->
|
|
||||||
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleView" style="display: none">
|
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="申请人" align="center" prop="applicantName" min-width="100" />
|
<el-table-column label="申请人" align="center" prop="applicantName" min-width="100" />
|
||||||
<el-table-column label="部门" align="center" prop="department" min-width="120" />
|
<el-table-column label="部门" align="center" prop="department" min-width="120" />
|
||||||
@@ -190,7 +161,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="审批时间" align="center" prop="approvalTime" min-width="160" />
|
<el-table-column label="审批时间" align="center" prop="approvalTime" min-width="160" />
|
||||||
<el-table-column label="审批人" align="center" prop="approvalByName" min-width="100" />
|
<el-table-column label="审批人" align="center" prop="approverName" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="操作" align="center" width="80" fixed="right">
|
<el-table-column label="操作" align="center" width="80" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip content="查看详情" placement="top">
|
<el-tooltip content="查看详情" placement="top">
|
||||||
@@ -243,7 +214,7 @@
|
|||||||
<el-tag :type="getStatusTagType(viewData.status)">{{ viewData.statusText }}</el-tag>
|
<el-tag :type="getStatusTagType(viewData.status)">{{ viewData.statusText }}</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="申请时间">{{ viewData.createTime || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="申请时间">{{ viewData.createTime || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="审批人" v-if="viewData.approvalByName">{{ viewData.approvalByName }}</el-descriptions-item>
|
<el-descriptions-item label="审批人" v-if="viewData.approverName">{{ viewData.approverName }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="审批时间" v-if="viewData.approvalTime">{{ viewData.approvalTime }}</el-descriptions-item>
|
<el-descriptions-item label="审批时间" v-if="viewData.approvalTime">{{ viewData.approvalTime }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="审批意见" :span="2" v-if="viewData.approvalOpinion">{{ viewData.approvalOpinion }}</el-descriptions-item>
|
<el-descriptions-item label="审批意见" :span="2" v-if="viewData.approvalOpinion">{{ viewData.approvalOpinion }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
@@ -267,16 +238,10 @@
|
|||||||
<el-form-item label="审批意见" prop="opinion">
|
<el-form-item label="审批意见" prop="opinion">
|
||||||
<el-input v-model="approvalForm.opinion" type="textarea" :rows="4" placeholder="请输入审批意见" maxlength="500" show-word-limit />
|
<el-input v-model="approvalForm.opinion" type="textarea" :rows="4" placeholder="请输入审批意见" maxlength="500" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 手写签字 -->
|
<!-- 手写签字 -->
|
||||||
<el-form-item label="审批签字">
|
<el-form-item label="审批签字">
|
||||||
<SignaturePad
|
<SignaturePad v-model="approvalForm.signature" :width="450" :height="120" :auto-save="true" :show-save-button="false" />
|
||||||
v-model="approvalForm.signature"
|
|
||||||
:width="450"
|
|
||||||
:height="120"
|
|
||||||
:auto-save="true"
|
|
||||||
:show-save-button="false"
|
|
||||||
/>
|
|
||||||
<div class="signature-tip">请在上方签字板中手写签名</div>
|
<div class="signature-tip">请在上方签字板中手写签名</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -413,17 +378,24 @@ const handleTabClick = (tab: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 🔴 标准化改造:简化待审批列表获取(仅用于兼容模式)
|
// 获取待审批变更列表(status=0)
|
||||||
const getPendingList = async () => {
|
const getPendingList = async () => {
|
||||||
state.pendingLoading = true;
|
state.pendingLoading = true;
|
||||||
|
try {
|
||||||
// 提示用户使用标准工作流页面
|
// 查询待审核状态的变更申请(status='0')
|
||||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
const params = { ...state.pendingQueryParams };
|
||||||
|
params.status = '0'; // 只查询待审核的
|
||||||
// 清空数据,不再调用自定义API
|
const response = await listPendingItineraryChange(params);
|
||||||
state.pendingList = [];
|
state.pendingList = response.rows || [];
|
||||||
state.pendingTotal = 0;
|
state.pendingTotal = response.total || 0;
|
||||||
state.pendingLoading = false;
|
} catch (error) {
|
||||||
|
console.error('获取待审批列表失败:', error);
|
||||||
|
ElMessage.error('获取待审批列表失败');
|
||||||
|
state.pendingList = [];
|
||||||
|
state.pendingTotal = 0;
|
||||||
|
} finally {
|
||||||
|
state.pendingLoading = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 查询审批记录列表
|
// 查询审批记录列表
|
||||||
@@ -446,20 +418,22 @@ const getRecordList = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 🔴 标准化改造:导航到标准工作流待办页面
|
|
||||||
const navigateToTaskWaiting = () => {
|
|
||||||
// 跳转到标准工作流待办页面
|
|
||||||
router.push('/workflow/task/taskWaiting');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 搜索查询 - 待审批列表
|
// 搜索查询 - 待审批列表
|
||||||
const handlePendingQuery = () => {
|
const handlePendingQuery = () => {
|
||||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
state.pendingQueryParams.pageNum = 1;
|
||||||
|
getPendingList();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置搜索 - 待审批列表
|
// 重置搜索 - 待审批列表
|
||||||
const resetPendingQuery = () => {
|
const resetPendingQuery = () => {
|
||||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
pendingQueryFormRef.value?.resetFields();
|
||||||
|
Object.assign(state.pendingQueryParams, {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
applicantName: '',
|
||||||
|
destination: ''
|
||||||
|
});
|
||||||
|
getPendingList();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 搜索查询 - 审批记录
|
// 搜索查询 - 审批记录
|
||||||
@@ -574,4 +548,7 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
|||||||
color: #909399;
|
color: #909399;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
:global(.app-container) {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
<el-card class="box-card">
|
<el-card class="box-card">
|
||||||
<!-- Tab切换:变更申请 / 变更记录 -->
|
<!-- Tab切换:变更申请 / 变更记录 -->
|
||||||
<el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
|
<el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
|
||||||
|
|
||||||
<!-- Tab 1: 变更申请(待审核) -->
|
<!-- Tab 1: 变更申请(待审核) -->
|
||||||
<el-tab-pane label="变更申请(待审核)" name="all">
|
<el-tab-pane label="变更申请(待审核)" name="all">
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
@@ -23,15 +22,6 @@
|
|||||||
<el-input v-model="queryParams.destination" placeholder="请输入目的地国家" clearable />
|
<el-input v-model="queryParams.destination" placeholder="请输入目的地国家" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
|
||||||
<el-form-item label="变更状态" prop="status">
|
|
||||||
<el-select v-model="queryParams.status" placeholder="请选择变更状态" clearable style="width: 100%">
|
|
||||||
<el-option label="待审核" value="0" />
|
|
||||||
<el-option label="已通过" value="1" />
|
|
||||||
<el-option label="已拒绝" value="2" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
@@ -52,9 +42,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<el-row :gutter="10" class="mb8">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['rs:itineraryChange:add']">
|
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['rs:itineraryChange:add']"> 新增变更申请 </el-button>
|
||||||
新增变更申请
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -62,45 +50,65 @@
|
|||||||
|
|
||||||
<!-- 变更行程列表 -->
|
<!-- 变更行程列表 -->
|
||||||
<el-table v-loading="loading" border :data="changeList" @row-dblclick="handleView">
|
<el-table v-loading="loading" border :data="changeList" @row-dblclick="handleView">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="申请人" align="center" prop="applicantName" min-width="100" />
|
<el-table-column label="申请人" align="center" prop="applicantName" min-width="100" />
|
||||||
<el-table-column label="原目的地" align="center" prop="originalDestination" min-width="120" />
|
<el-table-column label="原目的地" align="center" prop="originalDestination" min-width="120" />
|
||||||
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
||||||
<el-table-column label="原出国时间" align="center" min-width="200">
|
<el-table-column label="原出国时间" align="center" min-width="200">
|
||||||
<template #default="scope">
|
<template #default="scope"> {{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }} </template>
|
||||||
{{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }}
|
</el-table-column>
|
||||||
</template>
|
<el-table-column label="新出国时间" align="center" min-width="200">
|
||||||
</el-table-column>
|
<template #default="scope"> {{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }} </template>
|
||||||
<el-table-column label="新出国时间" align="center" min-width="200">
|
</el-table-column>
|
||||||
<template #default="scope">
|
<el-table-column label="变更状态" align="center" prop="statusText" min-width="100">
|
||||||
{{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }}
|
<template #default="scope">
|
||||||
</template>
|
<el-tag :type="getStatusTagType(scope.row.status)">{{ scope.row.statusText }}</el-tag>
|
||||||
</el-table-column>
|
</template>
|
||||||
<el-table-column label="变更状态" align="center" prop="statusText" min-width="100">
|
</el-table-column>
|
||||||
<template #default="scope">
|
<el-table-column label="申请时间" align="center" prop="createTime" min-width="160" />
|
||||||
<el-tag :type="getStatusTagType(scope.row.status)">{{ scope.row.statusText }}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="申请时间" align="center" prop="createTime" min-width="160" />
|
|
||||||
<el-table-column label="操作" align="center" width="250" fixed="right">
|
<el-table-column label="操作" align="center" width="250" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip content="查看详情" placement="top">
|
<el-tooltip content="查看详情" placement="top">
|
||||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="变更历史" placement="top">
|
<el-tooltip content="变更历史" placement="top">
|
||||||
<el-button link type="info" icon="List" @click="handleViewHistory(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="info"
|
||||||
|
icon="List"
|
||||||
|
@click="handleViewHistory(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:query']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<!-- 待审核状态:显示撤回按钮 -->
|
<!-- 待审核状态:显示撤回按钮 -->
|
||||||
<el-tooltip content="撤回申请" placement="top" v-if="scope.row.status === '0'">
|
<el-tooltip content="撤回申请" placement="top" v-if="scope.row.status === '0'">
|
||||||
<el-button link type="warning" icon="RefreshLeft" @click="handleCancel(scope.row)" v-hasPermi="['rs:itineraryChange:cancel']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
icon="RefreshLeft"
|
||||||
|
@click="handleCancel(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:cancel']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<!-- 已拒绝状态:显示重新提交按钮 -->
|
<!-- 已拒绝或已撤回状态:显示重新提交按钮 -->
|
||||||
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2'">
|
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2' || scope.row.status === '3'">
|
||||||
<el-button link type="success" icon="RefreshRight" @click="handleResubmit(scope.row)" v-hasPermi="['rs:itineraryChange:edit']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
icon="RefreshRight"
|
||||||
|
@click="handleResubmit(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:edit']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<!-- 删除按钮 -->
|
<!-- 删除按钮 -->
|
||||||
<el-tooltip content="删除" placement="top" v-if="scope.row.status !== '1'">
|
<el-tooltip content="删除" placement="top" v-if="scope.row.status !== '1'">
|
||||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['rs:itineraryChange:remove']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
icon="Delete"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:remove']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -131,15 +139,6 @@
|
|||||||
<el-input v-model="myQueryParams.destination" placeholder="请输入目的地国家" clearable />
|
<el-input v-model="myQueryParams.destination" placeholder="请输入目的地国家" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
|
||||||
<el-form-item label="变更状态" prop="status">
|
|
||||||
<el-select v-model="myQueryParams.status" placeholder="请选择变更状态" clearable style="width: 100%">
|
|
||||||
<el-option label="待审核" value="0" />
|
|
||||||
<el-option label="已通过" value="1" />
|
|
||||||
<el-option label="已拒绝" value="2" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
@@ -169,14 +168,10 @@
|
|||||||
<el-table-column label="原目的地" align="center" prop="originalDestination" min-width="120" />
|
<el-table-column label="原目的地" align="center" prop="originalDestination" min-width="120" />
|
||||||
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
<el-table-column label="新目的地" align="center" prop="newDestination" min-width="120" />
|
||||||
<el-table-column label="原出国时间" align="center" min-width="200">
|
<el-table-column label="原出国时间" align="center" min-width="200">
|
||||||
<template #default="scope">
|
<template #default="scope"> {{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }} </template>
|
||||||
{{ formatDate(scope.row.originalStartDate) }} 至 {{ formatDate(scope.row.originalEndDate) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="新出国时间" align="center" min-width="200">
|
<el-table-column label="新出国时间" align="center" min-width="200">
|
||||||
<template #default="scope">
|
<template #default="scope"> {{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }} </template>
|
||||||
{{ formatDate(scope.row.newStartDate) }} 至 {{ formatDate(scope.row.newEndDate) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="变更状态" align="center" prop="statusText" min-width="100">
|
<el-table-column label="变更状态" align="center" prop="statusText" min-width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -190,19 +185,43 @@
|
|||||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="变更历史" placement="top">
|
<el-tooltip content="变更历史" placement="top">
|
||||||
<el-button link type="info" icon="List" @click="handleViewHistory(scope.row)" v-hasPermi="['rs:itineraryChange:query']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="info"
|
||||||
|
icon="List"
|
||||||
|
@click="handleViewHistory(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:query']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<!-- 待审核状态:显示撤回按钮 -->
|
<!-- 待审核状态:显示撤回按钮 -->
|
||||||
<el-tooltip content="撤回申请" placement="top" v-if="scope.row.status === '0'">
|
<el-tooltip content="撤回申请" placement="top" v-if="scope.row.status === '0'">
|
||||||
<el-button link type="warning" icon="RefreshLeft" @click="handleCancel(scope.row)" v-hasPermi="['rs:itineraryChange:cancel']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
icon="RefreshLeft"
|
||||||
|
@click="handleCancel(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:cancel']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<!-- 已拒绝状态:显示重新提交按钮 -->
|
<!-- 已拒绝或已撤回状态:显示重新提交按钮 -->
|
||||||
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2'">
|
<el-tooltip content="重新提交" placement="top" v-if="scope.row.status === '2' || scope.row.status === '3'">
|
||||||
<el-button link type="success" icon="RefreshRight" @click="handleResubmit(scope.row)" v-hasPermi="['rs:itineraryChange:edit']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
icon="RefreshRight"
|
||||||
|
@click="handleResubmit(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:edit']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<!-- 删除按钮 -->
|
<!-- 删除按钮 -->
|
||||||
<el-tooltip content="删除" placement="top" v-if="scope.row.status !== '1'">
|
<el-tooltip content="删除" placement="top" v-if="scope.row.status !== '1'">
|
||||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['rs:itineraryChange:remove']"></el-button>
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
icon="Delete"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
v-hasPermi="['rs:itineraryChange:remove']"
|
||||||
|
></el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -223,24 +242,23 @@
|
|||||||
|
|
||||||
<!-- 新增/编辑对话框 -->
|
<!-- 新增/编辑对话框 -->
|
||||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body :close-on-click-modal="false" destroy-on-close>
|
<el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body :close-on-click-modal="false" destroy-on-close>
|
||||||
<div style="max-height: 70vh; overflow-y: auto; padding-right: 10px;">
|
<div style="max-height: 70vh; overflow-y: auto; padding-right: 10px">
|
||||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
|
||||||
|
|
||||||
<!-- 选择原出国申请 -->
|
<!-- 选择原出国申请 -->
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="选择原出国申请" prop="applicationId">
|
<el-form-item label="选择原出国申请" prop="applicationId">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.applicationId"
|
v-model="form.applicationId"
|
||||||
placeholder="请选择已审批通过的出国申请"
|
placeholder="请选择已审批通过的出国申请"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@change="handleApplicationChange"
|
@change="handleApplicationChange"
|
||||||
:disabled="!!form.changeId"
|
:disabled="!!form.changeId"
|
||||||
filterable
|
filterable
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="application in availableApplicationList"
|
v-for="application in availableApplicationList"
|
||||||
:key="application.applicationId"
|
:key="application.applicationId"
|
||||||
:label="`${application.applicantName} - ${application.destination} (${formatDate(application.startDate)}至${formatDate(application.endDate)})`"
|
:label="`${application.applicantName} - ${application.destination} (${formatDate(application.startDate)}至${formatDate(application.endDate)})`"
|
||||||
:value="application.applicationId"
|
:value="application.applicationId"
|
||||||
/>
|
/>
|
||||||
@@ -336,13 +354,7 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="行程安排">
|
<el-form-item label="行程安排">
|
||||||
<el-input
|
<el-input v-model="form.originalItinerary" type="textarea" :rows="3" disabled placeholder="原行程安排" />
|
||||||
v-model="form.originalItinerary"
|
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
disabled
|
|
||||||
placeholder="原行程安排"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -384,14 +396,7 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="行程安排" prop="newItinerary">
|
<el-form-item label="行程安排" prop="newItinerary">
|
||||||
<el-input
|
<el-input v-model="form.newItinerary" type="textarea" :rows="3" placeholder="请输入实际行程安排" maxlength="1000" show-word-limit />
|
||||||
v-model="form.newItinerary"
|
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
placeholder="请输入实际行程安排"
|
|
||||||
maxlength="1000"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -401,10 +406,10 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item prop="changeReason">
|
<el-form-item prop="changeReason">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.changeReason"
|
v-model="form.changeReason"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="4"
|
:rows="4"
|
||||||
placeholder="请详细说明行程变更的原因(相关证明材料可附后)"
|
placeholder="请详细说明行程变更的原因(相关证明材料可附后)"
|
||||||
maxlength="500"
|
maxlength="500"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
@@ -418,21 +423,13 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-input
|
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请填写有关认识和态度" maxlength="200" show-word-limit />
|
||||||
v-model="form.remark"
|
<div style="margin-top: 12px; color: #303133; font-size: 14px; font-weight: bold">
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
placeholder="请填写有关认识和态度"
|
|
||||||
maxlength="200"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
<div style="margin-top: 12px; color: #303133; font-size: 14px; font-weight: bold;">
|
|
||||||
我承诺,以上情况说明完全属实。如有不实行为,愿意接受组织处理。
|
我承诺,以上情况说明完全属实。如有不实行为,愿意接受组织处理。
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -450,14 +447,14 @@
|
|||||||
<!-- 基本信息 -->
|
<!-- 基本信息 -->
|
||||||
<el-descriptions-item label="申请人">{{ viewData.applicantName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="申请人">{{ viewData.applicantName || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="部门">{{ viewData.department || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="部门">{{ viewData.department || '-' }}</el-descriptions-item>
|
||||||
|
|
||||||
<!-- 原申请信息 -->
|
<!-- 原申请信息 -->
|
||||||
<el-descriptions-item label="原目的地">{{ viewData.originalDestination || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="原目的地">{{ viewData.originalDestination || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="原出境事由">{{ viewData.originalExitReason || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="原出境事由">{{ viewData.originalExitReason || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="原出发日期">{{ formatDate(viewData.originalStartDate) }}</el-descriptions-item>
|
<el-descriptions-item label="原出发日期">{{ formatDate(viewData.originalStartDate) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="原返回日期">{{ formatDate(viewData.originalEndDate) }}</el-descriptions-item>
|
<el-descriptions-item label="原返回日期">{{ formatDate(viewData.originalEndDate) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="原出国天数" :span="2">{{ viewData.originalDuration || 0 }}天</el-descriptions-item>
|
<el-descriptions-item label="原出国天数" :span="2">{{ viewData.originalDuration || 0 }}天</el-descriptions-item>
|
||||||
|
|
||||||
<!-- 变更信息 -->
|
<!-- 变更信息 -->
|
||||||
<el-descriptions-item label="变更类型" :span="2">{{ viewData.changeType || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="变更类型" :span="2">{{ viewData.changeType || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="新目的地" v-if="viewData.newDestination">{{ viewData.newDestination }}</el-descriptions-item>
|
<el-descriptions-item label="新目的地" v-if="viewData.newDestination">{{ viewData.newDestination }}</el-descriptions-item>
|
||||||
@@ -468,7 +465,7 @@
|
|||||||
<el-descriptions-item label="新行程安排" :span="2" v-if="viewData.newItinerary">{{ viewData.newItinerary }}</el-descriptions-item>
|
<el-descriptions-item label="新行程安排" :span="2" v-if="viewData.newItinerary">{{ viewData.newItinerary }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="变更原因" :span="2">{{ viewData.changeReason || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="变更原因" :span="2">{{ viewData.changeReason || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="备注" :span="2" v-if="viewData.remark">{{ viewData.remark }}</el-descriptions-item>
|
<el-descriptions-item label="备注" :span="2" v-if="viewData.remark">{{ viewData.remark }}</el-descriptions-item>
|
||||||
|
|
||||||
<!-- 审批信息 -->
|
<!-- 审批信息 -->
|
||||||
<el-descriptions-item label="变更状态">
|
<el-descriptions-item label="变更状态">
|
||||||
<el-tag :type="getStatusTagType(viewData.status)">{{ viewData.statusText }}</el-tag>
|
<el-tag :type="getStatusTagType(viewData.status)">{{ viewData.statusText }}</el-tag>
|
||||||
@@ -513,7 +510,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -522,7 +518,8 @@ import { ref, reactive, toRefs, onMounted, computed, watch } from 'vue';
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import animate from '@/animate';
|
import animate from '@/animate';
|
||||||
import { parseTime } from '@/utils/ruoyi';
|
import { parseTime } from '@/utils/ruoyi';
|
||||||
import {
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
import {
|
||||||
listItineraryChange,
|
listItineraryChange,
|
||||||
listMyItineraryChange,
|
listMyItineraryChange,
|
||||||
getItineraryChange,
|
getItineraryChange,
|
||||||
@@ -535,6 +532,7 @@ import {
|
|||||||
getOverseasApplication,
|
getOverseasApplication,
|
||||||
getItineraryChangeHistory,
|
getItineraryChangeHistory,
|
||||||
getOverseasRecordByApplication,
|
getOverseasRecordByApplication,
|
||||||
|
listOverseasApplication,
|
||||||
type ItineraryChangeQuery,
|
type ItineraryChangeQuery,
|
||||||
type ItineraryChangeVO,
|
type ItineraryChangeVO,
|
||||||
type ItineraryChangeForm,
|
type ItineraryChangeForm,
|
||||||
@@ -553,7 +551,7 @@ const formRef = ref();
|
|||||||
const state = reactive({
|
const state = reactive({
|
||||||
// Tab切换
|
// Tab切换
|
||||||
activeTab: 'all',
|
activeTab: 'all',
|
||||||
|
|
||||||
// 全部变更列表
|
// 全部变更列表
|
||||||
loading: false,
|
loading: false,
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
@@ -565,9 +563,10 @@ const state = reactive({
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
applicantName: '',
|
applicantName: '',
|
||||||
destination: '',
|
destination: '',
|
||||||
status: ''
|
status: '',
|
||||||
|
applicantId: undefined as number | undefined
|
||||||
} as ItineraryChangeQuery,
|
} as ItineraryChangeQuery,
|
||||||
|
|
||||||
// 我的变更列表
|
// 我的变更列表
|
||||||
myLoading: false,
|
myLoading: false,
|
||||||
showMySearch: true,
|
showMySearch: true,
|
||||||
@@ -577,9 +576,10 @@ const state = reactive({
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
destination: '',
|
destination: '',
|
||||||
status: ''
|
status: '',
|
||||||
|
applicantId: undefined as number | undefined
|
||||||
} as ItineraryChangeQuery,
|
} as ItineraryChangeQuery,
|
||||||
|
|
||||||
// 对话框
|
// 对话框
|
||||||
dialog: {
|
dialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -591,21 +591,21 @@ const state = reactive({
|
|||||||
historyDialog: {
|
historyDialog: {
|
||||||
visible: false
|
visible: false
|
||||||
},
|
},
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
form: {} as ItineraryChangeForm,
|
form: {} as ItineraryChangeForm,
|
||||||
viewData: null as ItineraryChangeVO | null,
|
viewData: null as ItineraryChangeVO | null,
|
||||||
|
|
||||||
// 变更历史
|
// 变更历史
|
||||||
historyLoading: false,
|
historyLoading: false,
|
||||||
historyList: [] as ItineraryChangeVO[],
|
historyList: [] as ItineraryChangeVO[],
|
||||||
|
|
||||||
// 可用的出国申请列表(已审批通过)
|
// 可用的出国申请列表(已审批通过)
|
||||||
availableApplicationList: [] as OverseasApplicationVO[],
|
availableApplicationList: [] as OverseasApplicationVO[],
|
||||||
|
|
||||||
// 变更类型(多选)
|
// 变更类型(多选)
|
||||||
changeTypes: [] as string[],
|
changeTypes: [] as string[],
|
||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
rules: {
|
rules: {
|
||||||
applicationId: [{ required: true, message: '请选择原出国申请', trigger: 'change' }],
|
applicationId: [{ required: true, message: '请选择原出国申请', trigger: 'change' }],
|
||||||
@@ -690,7 +690,7 @@ onMounted(() => {
|
|||||||
const handleTabClick = (tab: any) => {
|
const handleTabClick = (tab: any) => {
|
||||||
const tabName = tab.props?.name || tab.paneName;
|
const tabName = tab.props?.name || tab.paneName;
|
||||||
state.activeTab = tabName;
|
state.activeTab = tabName;
|
||||||
|
|
||||||
if (tabName === 'all') {
|
if (tabName === 'all') {
|
||||||
getList();
|
getList();
|
||||||
} else if (tabName === 'my') {
|
} else if (tabName === 'my') {
|
||||||
@@ -698,18 +698,36 @@ const handleTabClick = (tab: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 查询变更申请列表(只显示待审核的数据)
|
// 查询变更申请列表(只显示当前登录人的待审核和已撤回的数据)
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
try {
|
try {
|
||||||
// 如果用户没有手动选择状态,默认只显示待审核的申请
|
const userStore = useUserStore();
|
||||||
const params = { ...state.queryParams };
|
// 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页
|
||||||
if (!params.status) {
|
const params = {
|
||||||
params.status = '0'; // 默认只显示待审核状态
|
pageNum: 1,
|
||||||
}
|
pageSize: 10000, // 设置一个较大的值,获取所有数据
|
||||||
|
applicantId: Number(userStore.userId),
|
||||||
|
applicantName: state.queryParams.applicantName,
|
||||||
|
destination: state.queryParams.destination
|
||||||
|
};
|
||||||
|
|
||||||
const response = await listItineraryChange(params);
|
const response = await listItineraryChange(params);
|
||||||
state.changeList = response.rows;
|
|
||||||
state.total = response.total;
|
// 前端过滤:只显示状态为 0(待审核)和 3(已撤回)的数据
|
||||||
|
const filteredRows = response.rows.filter((item: ItineraryChangeVO) => {
|
||||||
|
return item.status === '0' || item.status === '3';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 前端分页
|
||||||
|
const pageNum = state.queryParams.pageNum;
|
||||||
|
const pageSize = state.queryParams.pageSize;
|
||||||
|
const total = filteredRows.length;
|
||||||
|
const start = (pageNum - 1) * pageSize;
|
||||||
|
const end = Math.min(start + pageSize, total);
|
||||||
|
|
||||||
|
state.changeList = filteredRows.slice(start, end);
|
||||||
|
state.total = total;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取变更行程列表失败:', error);
|
console.error('获取变更行程列表失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -717,13 +735,35 @@ const getList = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 查询我的变更记录列表(只显示已通过的变更申请)
|
// 查询我的变更记录列表(只显示当前登录人的已通过和已拒绝的数据)
|
||||||
const getMyList = async () => {
|
const getMyList = async () => {
|
||||||
state.myLoading = true;
|
state.myLoading = true;
|
||||||
try {
|
try {
|
||||||
const response = await listMyItineraryChange(state.myQueryParams);
|
const userStore = useUserStore();
|
||||||
state.myChangeList = response.rows;
|
// 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页
|
||||||
state.myTotal = response.total;
|
const params = {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10000, // 设置一个较大的值,获取所有数据
|
||||||
|
applicantId: Number(userStore.userId),
|
||||||
|
destination: state.myQueryParams.destination
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await listItineraryChange(params);
|
||||||
|
|
||||||
|
// 前端过滤:只显示状态为 1(已通过)和 2(已拒绝)的数据
|
||||||
|
const filteredRows = response.rows.filter((item: ItineraryChangeVO) => {
|
||||||
|
return item.status === '1' || item.status === '2';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 前端分页
|
||||||
|
const pageNum = state.myQueryParams.pageNum;
|
||||||
|
const pageSize = state.myQueryParams.pageSize;
|
||||||
|
const total = filteredRows.length;
|
||||||
|
const start = (pageNum - 1) * pageSize;
|
||||||
|
const end = Math.min(start + pageSize, total);
|
||||||
|
|
||||||
|
state.myChangeList = filteredRows.slice(start, end);
|
||||||
|
state.myTotal = total;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取我的变更记录失败:', error);
|
console.error('获取我的变更记录失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -731,13 +771,25 @@ const getMyList = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取已审批通过的出国申请列表
|
// 获取已审批通过的出国申请列表(只显示当前登录人的)
|
||||||
const getApprovedApplicationList = async () => {
|
const getApprovedApplicationList = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await getApprovedOverseasApplications();
|
const userStore = useUserStore();
|
||||||
state.availableApplicationList = response.data;
|
// 使用listOverseasApplication查询当前登录人的已通过申请
|
||||||
|
const response = await listOverseasApplication({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 1000, // 设置一个较大的值,获取所有数据
|
||||||
|
applicantId: Number(userStore.userId), // 只查询当前登录人的申请
|
||||||
|
status: '1' // 只查询已通过的状态
|
||||||
|
});
|
||||||
|
|
||||||
|
// 确保只返回已通过审批的申请
|
||||||
|
state.availableApplicationList = response.rows.filter((item: OverseasApplicationVO) => {
|
||||||
|
return item.status === '1';
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取已审批通过的出国申请列表失败:', error);
|
console.error('获取已审批通过的出国申请列表失败:', error);
|
||||||
|
ElMessage.error('获取已审批通过的出国申请列表失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -798,30 +850,30 @@ const handleApplicationChange = async (applicationId: number) => {
|
|||||||
// 1. 获取出国申请详情
|
// 1. 获取出国申请详情
|
||||||
const response = await getOverseasApplication(applicationId);
|
const response = await getOverseasApplication(applicationId);
|
||||||
const selectedApplication = response.data;
|
const selectedApplication = response.data;
|
||||||
|
|
||||||
// 2. 根据申请ID获取出国记录(包含recordId)
|
// 2. 根据申请ID获取出国记录(包含recordId)
|
||||||
const recordResponse = await getOverseasRecordByApplication(applicationId);
|
const recordResponse = await getOverseasRecordByApplication(applicationId);
|
||||||
const recordData = recordResponse.data;
|
const recordData = recordResponse.data;
|
||||||
|
|
||||||
if (selectedApplication && recordData) {
|
if (selectedApplication && recordData) {
|
||||||
// 设置recordId(关键字段,必填)
|
// 设置recordId(关键字段,必填)
|
||||||
state.form.recordId = recordData.recordId;
|
state.form.recordId = recordData.recordId;
|
||||||
|
|
||||||
// 设置申请人ID(关键字段,必填)
|
// 设置申请人ID(关键字段,必填)
|
||||||
state.form.applicantId = selectedApplication.applicantId;
|
state.form.applicantId = selectedApplication.applicantId;
|
||||||
|
|
||||||
// 填充本人基本情况
|
// 填充本人基本情况
|
||||||
state.form.applicantName = selectedApplication.applicantName;
|
state.form.applicantName = selectedApplication.applicantName;
|
||||||
state.form.gender = selectedApplication.gender;
|
state.form.gender = selectedApplication.gender;
|
||||||
state.form.birthDate = selectedApplication.birthDate;
|
state.form.birthDate = selectedApplication.birthDate;
|
||||||
// 政治面貌:将字典值转换为文本标签
|
// 政治面貌:将字典值转换为文本标签
|
||||||
state.form.politicalStatus = getPoliticalStatusLabel(selectedApplication.politicalStatus);
|
state.form.politicalStatus = selectedApplication.politicalStatus ? getPoliticalStatusLabel(selectedApplication.politicalStatus) : '';
|
||||||
state.form.professionalTitle = selectedApplication.professionalTitle;
|
state.form.professionalTitle = selectedApplication.professionalTitle;
|
||||||
state.form.position = selectedApplication.position;
|
state.form.position = selectedApplication.position;
|
||||||
state.form.documentName = selectedApplication.documentName;
|
state.form.documentName = selectedApplication.documentName;
|
||||||
state.form.documentNumber = selectedApplication.documentNumber;
|
state.form.documentNumber = selectedApplication.documentNumber;
|
||||||
state.form.department = selectedApplication.department;
|
state.form.department = selectedApplication.department;
|
||||||
|
|
||||||
// 填充申请行程情况
|
// 填充申请行程情况
|
||||||
// 前往国家:优先使用destinationCountry,其次使用destination
|
// 前往国家:优先使用destinationCountry,其次使用destination
|
||||||
state.form.originalDestination = selectedApplication.destinationCountry || selectedApplication.destination;
|
state.form.originalDestination = selectedApplication.destinationCountry || selectedApplication.destination;
|
||||||
@@ -833,7 +885,7 @@ const handleApplicationChange = async (applicationId: number) => {
|
|||||||
state.form.originalStartDate = selectedApplication.startDate;
|
state.form.originalStartDate = selectedApplication.startDate;
|
||||||
state.form.originalEndDate = selectedApplication.endDate;
|
state.form.originalEndDate = selectedApplication.endDate;
|
||||||
state.form.originalDuration = selectedApplication.duration;
|
state.form.originalDuration = selectedApplication.duration;
|
||||||
|
|
||||||
// 默认将原信息复制到新信息
|
// 默认将原信息复制到新信息
|
||||||
state.form.newDestination = selectedApplication.destinationCountry || selectedApplication.destination;
|
state.form.newDestination = selectedApplication.destinationCountry || selectedApplication.destination;
|
||||||
state.form.newDestinationCity = selectedApplication.destinationCity;
|
state.form.newDestinationCity = selectedApplication.destinationCity;
|
||||||
@@ -867,22 +919,24 @@ const handleCancel = (row: ItineraryChangeVO) => {
|
|||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
inputPlaceholder: '请输入撤回原因'
|
inputPlaceholder: '请输入撤回原因'
|
||||||
}).then(async ({ value }) => {
|
})
|
||||||
try {
|
.then(async ({ value }) => {
|
||||||
await cancelItineraryChange(row.changeId, value);
|
try {
|
||||||
ElMessage.success('撤回成功');
|
await cancelItineraryChange(row.changeId, value);
|
||||||
|
ElMessage.success('撤回成功');
|
||||||
// 根据当前Tab刷新列表
|
|
||||||
if (state.activeTab === 'all') {
|
// 根据当前Tab刷新列表
|
||||||
getList();
|
if (state.activeTab === 'all') {
|
||||||
} else if (state.activeTab === 'my') {
|
getList();
|
||||||
getMyList();
|
} else if (state.activeTab === 'my') {
|
||||||
|
getMyList();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('撤回失败:', error);
|
||||||
|
ElMessage.error('撤回失败');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})
|
||||||
console.error('撤回失败:', error);
|
.catch(() => {});
|
||||||
ElMessage.error('撤回失败');
|
|
||||||
}
|
|
||||||
}).catch(() => {});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重新提交
|
// 重新提交
|
||||||
@@ -890,12 +944,12 @@ const handleResubmit = async (row: ItineraryChangeVO) => {
|
|||||||
try {
|
try {
|
||||||
const response = await getItineraryChange(row.changeId);
|
const response = await getItineraryChange(row.changeId);
|
||||||
Object.assign(state.form, response.data);
|
Object.assign(state.form, response.data);
|
||||||
|
|
||||||
// 解析变更类型
|
// 解析变更类型
|
||||||
if (response.data.changeType) {
|
if (response.data.changeType) {
|
||||||
state.changeTypes = response.data.changeType.split(',');
|
state.changeTypes = response.data.changeType.split(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
state.dialog.title = '重新提交行程变更申请';
|
state.dialog.title = '重新提交行程变更申请';
|
||||||
state.dialog.visible = true;
|
state.dialog.visible = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -910,30 +964,32 @@ const handleDelete = (row: ItineraryChangeVO) => {
|
|||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(async () => {
|
})
|
||||||
try {
|
.then(async () => {
|
||||||
await delItineraryChange(row.changeId);
|
try {
|
||||||
ElMessage.success('删除成功');
|
await delItineraryChange(row.changeId);
|
||||||
|
ElMessage.success('删除成功');
|
||||||
// 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页)
|
|
||||||
if (state.activeTab === 'all') {
|
// 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页)
|
||||||
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
if (state.activeTab === 'all') {
|
||||||
if (state.changeList.length === 1 && state.queryParams.pageNum > 1) {
|
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
||||||
state.queryParams.pageNum--;
|
if (state.changeList.length === 1 && state.queryParams.pageNum > 1) {
|
||||||
|
state.queryParams.pageNum--;
|
||||||
|
}
|
||||||
|
getList();
|
||||||
|
} else if (state.activeTab === 'my') {
|
||||||
|
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
||||||
|
if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) {
|
||||||
|
state.myQueryParams.pageNum--;
|
||||||
|
}
|
||||||
|
getMyList();
|
||||||
}
|
}
|
||||||
getList();
|
} catch (error) {
|
||||||
} else if (state.activeTab === 'my') {
|
console.error('删除失败:', error);
|
||||||
// 如果删除的是当前页最后一条且不是第1页,返回上一页
|
ElMessage.error('删除失败');
|
||||||
if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) {
|
|
||||||
state.myQueryParams.pageNum--;
|
|
||||||
}
|
|
||||||
getMyList();
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})
|
||||||
console.error('删除失败:', error);
|
.catch(() => {});
|
||||||
ElMessage.error('删除失败');
|
|
||||||
}
|
|
||||||
}).catch(() => {});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 查看变更历史
|
// 查看变更历史
|
||||||
@@ -963,15 +1019,14 @@ const submitForm = () => {
|
|||||||
if (state.form.newExitReason !== state.form.originalExitReason) {
|
if (state.form.newExitReason !== state.form.originalExitReason) {
|
||||||
changeTypeList.push('出境事由变更');
|
changeTypeList.push('出境事由变更');
|
||||||
}
|
}
|
||||||
if (state.form.newStartDate !== state.form.originalStartDate ||
|
if (state.form.newStartDate !== state.form.originalStartDate || state.form.newEndDate !== state.form.originalEndDate) {
|
||||||
state.form.newEndDate !== state.form.originalEndDate) {
|
|
||||||
changeTypeList.push('时间变更');
|
changeTypeList.push('时间变更');
|
||||||
}
|
}
|
||||||
if (state.form.newItinerary && state.form.newItinerary !== state.form.originalItinerary) {
|
if (state.form.newItinerary && state.form.newItinerary !== state.form.originalItinerary) {
|
||||||
changeTypeList.push('行程变更');
|
changeTypeList.push('行程变更');
|
||||||
}
|
}
|
||||||
state.form.changeType = changeTypeList.length > 0 ? changeTypeList.join(',') : '其他变更';
|
state.form.changeType = changeTypeList.length > 0 ? changeTypeList.join(',') : '其他变更';
|
||||||
|
|
||||||
state.submitLoading = true;
|
state.submitLoading = true;
|
||||||
try {
|
try {
|
||||||
if (state.form.changeId) {
|
if (state.form.changeId) {
|
||||||
@@ -982,7 +1037,7 @@ const submitForm = () => {
|
|||||||
ElMessage.success('提交成功!变更申请已进入审批流程。');
|
ElMessage.success('提交成功!变更申请已进入审批流程。');
|
||||||
}
|
}
|
||||||
state.dialog.visible = false;
|
state.dialog.visible = false;
|
||||||
|
|
||||||
// 根据当前Tab刷新列表(重置到第1页)
|
// 根据当前Tab刷新列表(重置到第1页)
|
||||||
if (state.activeTab === 'all') {
|
if (state.activeTab === 'all') {
|
||||||
state.queryParams.pageNum = 1;
|
state.queryParams.pageNum = 1;
|
||||||
@@ -1056,11 +1111,11 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
|||||||
const typeMap: Record<string, 'primary' | 'success' | 'info' | 'warning' | 'danger'> = {
|
const typeMap: Record<string, 'primary' | 'success' | 'info' | 'warning' | 'danger'> = {
|
||||||
'0': 'warning', // 待审核
|
'0': 'warning', // 待审核
|
||||||
'1': 'success', // 已通过
|
'1': 'success', // 已通过
|
||||||
'2': 'danger' // 已拒绝
|
'2': 'danger', // 已拒绝
|
||||||
|
'3': 'info' // 已撤回
|
||||||
};
|
};
|
||||||
return typeMap[status] || 'info';
|
return typeMap[status] || 'info';
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -1081,6 +1136,8 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
|||||||
.table-card {
|
.table-card {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.app-container) {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -49,24 +49,24 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮区域 -->
|
<!-- 操作按钮区域 -->
|
||||||
<el-card shadow="never" class="button-card table-card">
|
<el-card shadow="never" class="button-card table-card">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-row :gutter="10" class="mb8">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="primary" plain icon="Refresh" @click="handleRefreshPending">刷新</el-button>
|
<el-button type="primary" plain icon="Refresh" @click="handleRefreshPending">刷新</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<div class="info-text">
|
<div class="info-text">
|
||||||
<el-icon><InfoFilled /></el-icon>
|
<el-icon><InfoFilled /></el-icon>
|
||||||
<span>这里显示所有待审批申请</span>
|
<span>这里显示所有待审批申请</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table v-loading="pendingLoading" :data="pendingList" border>
|
<el-table v-loading="pendingLoading" :data="pendingList" border>
|
||||||
<el-table-column label="申请人" prop="applicantName" min-width="100" align="center" :show-overflow-tooltip="true" />
|
<el-table-column label="申请人" prop="applicantName" min-width="100" align="center" :show-overflow-tooltip="true" />
|
||||||
<el-table-column label="所属部门" prop="department" min-width="140" align="center" :show-overflow-tooltip="true" />
|
<el-table-column label="所属部门" prop="department" min-width="140" align="center" :show-overflow-tooltip="true" />
|
||||||
<el-table-column label="前往国家" prop="destinationCountry" min-width="110" align="center" :show-overflow-tooltip="true" />
|
<el-table-column label="前往国家" prop="destinationCountry" min-width="110" align="center" :show-overflow-tooltip="true" />
|
||||||
@@ -350,8 +350,16 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div style="display: flex; justify-content: space-between; width: 100%">
|
<div style="display: flex; justify-content: space-between; width: 100%">
|
||||||
<div>
|
<div>
|
||||||
<!-- 只有审批完成的申请才显示下载PDF按钮 -->
|
<!-- 只有审批完成的申请才显示下载Word按钮 -->
|
||||||
<el-button v-if="currentDetail && currentDetail.status === '1'" type="primary" icon="Download" @click="handleDownloadPdf" v-hasPermi="['rs:overseas:query']">下载审批表PDF</el-button>
|
<el-button
|
||||||
|
v-if="currentDetail && currentDetail.status === '1'"
|
||||||
|
type="primary"
|
||||||
|
icon="Download"
|
||||||
|
@click="handleDownloadApprovalWord"
|
||||||
|
v-hasPermi="['rs:overseas:query']"
|
||||||
|
>
|
||||||
|
下载审批表Word
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||||
@@ -362,14 +370,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="OverseasApprovalMonitor">
|
<script setup lang="ts" name="OverseasApprovalMonitor">
|
||||||
import { ref, reactive, onMounted, watch } from 'vue';
|
import { ref, reactive, onMounted, watch } from 'vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { Clock, Search, Refresh, View, EditPen, InfoFilled, Download } from '@element-plus/icons-vue';
|
import { Clock, Search, Refresh, View, EditPen, InfoFilled, Download } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
// API请求
|
// API请求
|
||||||
import { getPendingList as apiGetPendingList, getCompletedList as apiGetCompletedList, getDetail, downloadApprovalPdf } from '@/api/rs/overseas';
|
import { getPendingList as apiGetPendingList, getCompletedList as apiGetCompletedList, getDetail } from '@/api/rs/overseas';
|
||||||
|
|
||||||
// 导入变更历史API接口
|
// 导入变更历史API接口
|
||||||
import { getItineraryChangeHistory } from '@/api/rs/overseas';
|
import { getItineraryChangeHistory } from '@/api/rs/overseas';
|
||||||
@@ -405,6 +414,8 @@ const completedQueryParams = reactive({
|
|||||||
department: undefined,
|
department: undefined,
|
||||||
destination: undefined,
|
destination: undefined,
|
||||||
applyTimeRange: undefined,
|
applyTimeRange: undefined,
|
||||||
|
applyTimeStart: undefined as string | undefined,
|
||||||
|
applyTimeEnd: undefined as string | undefined,
|
||||||
status: '1'
|
status: '1'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -691,48 +702,180 @@ const getChangeStatusTagType = (status) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载审批表PDF
|
* 下载审批表Word
|
||||||
*/
|
*/
|
||||||
const handleDownloadPdf = async () => {
|
const handleDownloadApprovalWord = async () => {
|
||||||
if (!currentDetail.value || !currentDetail.value.applicationId) {
|
const detail = currentDetail.value;
|
||||||
|
if (!detail || !detail.applicationId) {
|
||||||
ElMessage.error('无法获取申请ID');
|
ElMessage.error('无法获取申请ID');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ElMessage.info('正在生成PDF,请稍候...');
|
ElMessage.info('正在生成审批表,请稍候...');
|
||||||
|
|
||||||
// 调用API获取blob数据
|
const templateData = buildApprovalTemplateData(detail);
|
||||||
const response = await downloadApprovalPdf(currentDetail.value.applicationId);
|
const [Docxtemplater, PizZip, ImageModule] = await Promise.all([
|
||||||
|
import('docxtemplater'),
|
||||||
// 创建带MIME类型的Blob对象
|
import('pizzip'),
|
||||||
const blob = response instanceof Blob
|
import('docxtemplater-image-module-free')
|
||||||
? new Blob([response], { type: 'application/pdf' })
|
]);
|
||||||
: new Blob([response.data], { type: 'application/pdf' });
|
const templateResponse = await fetch('/chuguojingshenpibiao.docx', { cache: 'no-store' });
|
||||||
|
|
||||||
// 创建下载链接
|
if (!templateResponse.ok) {
|
||||||
|
throw new Error('模板文件加载失败,请联系管理员');
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrayBuffer = await templateResponse.arrayBuffer();
|
||||||
|
const zip = new PizZip.default(arrayBuffer);
|
||||||
|
const imageOpts = {
|
||||||
|
centered: false,
|
||||||
|
getImage: (base64: string) => {
|
||||||
|
if (!base64) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const pure = base64.split(',')[1] || base64;
|
||||||
|
const bytes = Uint8Array.from(atob(pure), (char) => char.charCodeAt(0));
|
||||||
|
return bytes.buffer;
|
||||||
|
},
|
||||||
|
getSize: () => [60, 20]
|
||||||
|
};
|
||||||
|
|
||||||
|
const doc = new Docxtemplater.default(zip, {
|
||||||
|
modules: [new ImageModule.default(imageOpts)],
|
||||||
|
paragraphLoop: true,
|
||||||
|
linebreaks: true,
|
||||||
|
delimiters: { start: '{{', end: '}}' },
|
||||||
|
parser: (tag: string) => {
|
||||||
|
return {
|
||||||
|
get: (scope: Record<string, any>) => {
|
||||||
|
const cleanTag = tag.trim().replace(/^%+/, '');
|
||||||
|
return scope[cleanTag];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.render(templateData);
|
||||||
|
|
||||||
|
const blob = doc.getZip().generate({
|
||||||
|
type: 'blob',
|
||||||
|
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileName = `出国申请审批表-${detail.applicantName || dayjs().format('YYYYMMDDHHmmss')}.docx`;
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = url;
|
link.href = url;
|
||||||
|
|
||||||
// 设置下载文件名(使用申请人姓名和时间戳)
|
|
||||||
const fileName = `出国申请审批表_${currentDetail.value.applicantName}_${currentDetail.value.applicationId}.pdf`;
|
|
||||||
link.download = fileName;
|
link.download = fileName;
|
||||||
|
|
||||||
// 触发下载
|
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
|
|
||||||
// 清理资源
|
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
|
|
||||||
ElMessage.success('PDF下载成功');
|
ElMessage.success('审批表下载成功');
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('下载PDF失败', error);
|
console.error('下载审批表失败', error);
|
||||||
ElMessage.error('下载PDF失败,请稍后重试');
|
ElMessage.error(error?.message || '下载审批表失败,请稍后重试');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建审批表模板数据
|
||||||
|
*/
|
||||||
|
const buildApprovalTemplateData = (detail) => {
|
||||||
|
const startParts = getDateParts(detail.startDate);
|
||||||
|
const endParts = getDateParts(detail.endDate);
|
||||||
|
const applyParts = getDateParts(detail.applyTime || detail.createTime);
|
||||||
|
const durationDays = detail.duration || calculateDuration(detail.startDate, detail.endDate);
|
||||||
|
const templateData: Record<string, any> = {
|
||||||
|
applicantName: detail.applicantName || '',
|
||||||
|
genderText: detail.genderText || '',
|
||||||
|
birthYear: getDateParts(detail.birthDate).year,
|
||||||
|
birthMonth: getDateParts(detail.birthDate).month,
|
||||||
|
birthDay: getDateParts(detail.birthDate).day,
|
||||||
|
politicalStatusText: detail.politicalStatusText || '-',
|
||||||
|
department: detail.department || '-',
|
||||||
|
professionalTitle: detail.professionalTitle || '-',
|
||||||
|
position: detail.position || '-',
|
||||||
|
documentName: detail.documentName || '-',
|
||||||
|
documentNumber: detail.documentNumber || '-',
|
||||||
|
destinationCountry: detail.destinationCountry || detail.destination || '-',
|
||||||
|
isTourismChecked: detail.exitReason === '旅游' ? '√' : '',
|
||||||
|
isVisitChecked: detail.exitReason === '探亲' ? '√' : '',
|
||||||
|
visitRelationship: detail.visitRelationship || '',
|
||||||
|
remarks: detail.remarks || '',
|
||||||
|
startYear: startParts.year,
|
||||||
|
startMonth: startParts.month,
|
||||||
|
startDay: startParts.day,
|
||||||
|
endYear: endParts.year,
|
||||||
|
endMonth: endParts.month,
|
||||||
|
endDay: endParts.day,
|
||||||
|
durationDays: durationDays || '',
|
||||||
|
applicantSignature: detail.signature || '',
|
||||||
|
mobilePhone: detail.mobilePhone || detail.contactPhone || '',
|
||||||
|
emergencyContact: detail.emergencyContact || '',
|
||||||
|
applicantSignYear: applyParts.year,
|
||||||
|
applicantSignMonth: applyParts.month,
|
||||||
|
applicantSignDay: applyParts.day
|
||||||
|
};
|
||||||
|
|
||||||
|
fillApprovalNodeData(templateData, detail.approvalHistory || []);
|
||||||
|
return templateData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算出国天数
|
||||||
|
*/
|
||||||
|
const calculateDuration = (startDate, endDate) => {
|
||||||
|
if (!startDate || !endDate) return '';
|
||||||
|
const start = dayjs(startDate);
|
||||||
|
const end = dayjs(endDate);
|
||||||
|
if (!start.isValid() || !end.isValid()) return '';
|
||||||
|
return Math.max(end.diff(start, 'day') + 1, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日期拆分
|
||||||
|
*/
|
||||||
|
const getDateParts = (date) => {
|
||||||
|
if (!date) {
|
||||||
|
return { year: '', month: '', day: '' };
|
||||||
|
}
|
||||||
|
const parsed = dayjs(date);
|
||||||
|
if (!parsed.isValid()) {
|
||||||
|
return { year: '', month: '', day: '' };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
year: parsed.format('YYYY'),
|
||||||
|
month: parsed.format('MM'),
|
||||||
|
day: parsed.format('DD')
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充审批节点数据
|
||||||
|
*/
|
||||||
|
const fillApprovalNodeData = (templateData, approvalHistory) => {
|
||||||
|
const approvalNodes = [
|
||||||
|
{ level: '1', prefix: 'deptLeader' },
|
||||||
|
{ level: '2', prefix: 'hr' },
|
||||||
|
{ level: '3', prefix: 'discipline' },
|
||||||
|
{ level: '4', prefix: 'viceDean' },
|
||||||
|
{ level: '5', prefix: 'secretary' }
|
||||||
|
];
|
||||||
|
|
||||||
|
approvalNodes.forEach((node) => {
|
||||||
|
const record = approvalHistory.find((item) => item.approvalLevel === node.level);
|
||||||
|
const dateParts = getDateParts(record?.approvalTime);
|
||||||
|
templateData[`${node.prefix}Signature`] = record?.approvalSignature || record?.signature || '';
|
||||||
|
templateData[`${node.prefix}Opinion`] = record?.approvalOpinion || '';
|
||||||
|
templateData[`${node.prefix}Year`] = dateParts.year;
|
||||||
|
templateData[`${node.prefix}Month`] = dateParts.month;
|
||||||
|
templateData[`${node.prefix}Day`] = dateParts.day;
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
# 出国申请进度查询功能修改总结 - 快速参考
|
|
||||||
|
|
||||||
## ✅ 修改完成
|
|
||||||
|
|
||||||
**修改时间**:2025-10-29
|
|
||||||
**修改范围**:仅修改monitor相关文件,未影响其他模块
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 核心修改
|
|
||||||
|
|
||||||
### 1. 功能变化
|
|
||||||
|
|
||||||
#### 修改前
|
|
||||||
- ❌ 显示工作流迁移提示
|
|
||||||
- ❌ 无实际查询功能
|
|
||||||
- ❌ 表格被隐藏
|
|
||||||
|
|
||||||
#### 修改后
|
|
||||||
- ✅ 显示当前用户提交的待审批申请
|
|
||||||
- ✅ 显示当前用户提交的已完成审批申请(抄送)
|
|
||||||
- ✅ 支持搜索和查看审批历史
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 核心逻辑
|
|
||||||
|
|
||||||
### 数据过滤
|
|
||||||
```java
|
|
||||||
// 后端Service层添加过滤条件
|
|
||||||
Long currentUserId = LoginHelper.getUserId();
|
|
||||||
wrapper.eq("applicant_id", currentUserId); // 🔴 只查询当前用户提交的申请
|
|
||||||
```
|
|
||||||
|
|
||||||
### 功能说明
|
|
||||||
- **待审批中**:用户提交申请后,在这里查看审批进度
|
|
||||||
- **审批完成**:审批流程完成后,抄送一份到这里
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📄 修改文件
|
|
||||||
|
|
||||||
| 文件 | 类型 | 修改内容 |
|
|
||||||
|------|------|---------|
|
|
||||||
| `RsOverseasApprovalMonitorServiceImpl.java` | 后端 | 添加当前用户过滤 |
|
|
||||||
| `RsOverseasApprovalMonitorController.java` | 后端 | 恢复pending接口 |
|
|
||||||
| `monitor/index.vue` | 前端 | 删除提示,恢复表格 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 快速测试
|
|
||||||
|
|
||||||
### 测试步骤
|
|
||||||
1. 登录系统
|
|
||||||
2. 访问"出国申请进度查询"
|
|
||||||
3. 查看"待审批中"和"审批完成"选项卡
|
|
||||||
|
|
||||||
### 预期结果
|
|
||||||
- ✅ 只显示当前用户提交的申请
|
|
||||||
- ✅ 不显示其他用户的申请
|
|
||||||
- ✅ 可以查看审批历史
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 部署命令
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 后端
|
|
||||||
cd backend
|
|
||||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
|
||||||
./stop-main.sh && ./start-main.sh
|
|
||||||
|
|
||||||
# 前端(如需要)
|
|
||||||
cd web
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 详细文档
|
|
||||||
|
|
||||||
完整修改说明请参考:`出国申请进度查询功能修改说明.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**状态**:✅ 已完成
|
|
||||||
**版本**:v1.0
|
|
||||||
|
|
||||||
@@ -1,365 +0,0 @@
|
|||||||
# 出国申请进度查询功能优化说明
|
|
||||||
|
|
||||||
**日期**: 2025-01-29
|
|
||||||
**模块**: 出国申请进度查询 (`/rs/overseas/monitor`)
|
|
||||||
|
|
||||||
## 📋 功能需求
|
|
||||||
|
|
||||||
### 1. 变更历史记录条件显示
|
|
||||||
- **需求**: 变更历史记录字段只在有变更记录时显示,没有变更记录时不显示该区域
|
|
||||||
- **实现位置**: `web/src/views/rs/overseas/monitor/index.vue`
|
|
||||||
|
|
||||||
### 2. PDF下载功能
|
|
||||||
- **需求**: 在审批完成选项卡的详情弹窗中新增PDF下载按钮
|
|
||||||
- **PDF模板**: 杭州第一技师学院登记报备人员因私事出国(境)审批表
|
|
||||||
- **显示条件**: 仅当申请状态为"已通过"(status='1')时显示下载按钮
|
|
||||||
|
|
||||||
## 🎯 实现内容
|
|
||||||
|
|
||||||
### 一、前端修改
|
|
||||||
|
|
||||||
#### 1. 变更历史记录条件显示
|
|
||||||
**文件**: `web/src/views/rs/overseas/monitor/index.vue`
|
|
||||||
|
|
||||||
**修改内容**:
|
|
||||||
- 原条件: `v-if="currentDetail.recordId"`
|
|
||||||
- 新条件: `v-if="currentDetail.recordId && changeHistoryList.length > 0"`
|
|
||||||
- 移除空状态提示组件 `<el-empty>`
|
|
||||||
|
|
||||||
**代码位置**: 第287行
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<!-- 变更历史记录(仅已完成且有变更记录时显示) -->
|
|
||||||
<div v-if="currentDetail.recordId && changeHistoryList.length > 0" style="margin-top: 20px">
|
|
||||||
<el-divider content-position="left">
|
|
||||||
<span style="font-size: 16px; font-weight: bold; color: #409eff">
|
|
||||||
<el-icon style="vertical-align: middle; margin-right: 5px"><EditPen /></el-icon>
|
|
||||||
变更历史记录
|
|
||||||
</span>
|
|
||||||
</el-divider>
|
|
||||||
|
|
||||||
<!-- 变更历史列表 -->
|
|
||||||
<div v-loading="changeHistoryLoading">
|
|
||||||
<el-table :data="changeHistoryList" border style="width: 100%" max-height="400px">
|
|
||||||
<!-- 表格列定义 -->
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. PDF下载按钮
|
|
||||||
**文件**: `web/src/views/rs/overseas/monitor/index.vue`
|
|
||||||
|
|
||||||
**新增内容**:
|
|
||||||
1. **导入图标**: 添加 `Download` 图标
|
|
||||||
2. **导入API**: 添加 `downloadApprovalPdf` 方法
|
|
||||||
3. **弹窗footer布局**: 改为左右布局,左侧显示下载按钮,右侧显示关闭按钮
|
|
||||||
4. **下载方法**: 添加 `handleDownloadPdf` 方法
|
|
||||||
|
|
||||||
**代码位置**: 第350-360行
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template #footer>
|
|
||||||
<div style="display: flex; justify-content: space-between; width: 100%">
|
|
||||||
<div>
|
|
||||||
<!-- 只有审批完成的申请才显示下载PDF按钮 -->
|
|
||||||
<el-button
|
|
||||||
v-if="currentDetail && currentDetail.status === '1'"
|
|
||||||
type="primary"
|
|
||||||
icon="Download"
|
|
||||||
@click="handleDownloadPdf"
|
|
||||||
v-hasPermi="['rs:overseas:query']">
|
|
||||||
下载审批表PDF
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**JavaScript方法** (第693-710行):
|
|
||||||
```javascript
|
|
||||||
/**
|
|
||||||
* 下载审批表PDF
|
|
||||||
*/
|
|
||||||
const handleDownloadPdf = async () => {
|
|
||||||
if (!currentDetail.value || !currentDetail.value.applicationId) {
|
|
||||||
ElMessage.error('无法获取申请ID');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ElMessage.info('正在生成PDF,请稍候...');
|
|
||||||
await downloadApprovalPdf(currentDetail.value.applicationId);
|
|
||||||
ElMessage.success('PDF下载成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('下载PDF失败', error);
|
|
||||||
ElMessage.error('下载PDF失败,请稍后重试');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. API接口定义
|
|
||||||
**文件**: `web/src/api/rs/overseas.ts`
|
|
||||||
|
|
||||||
**新增内容**: Monitor相关API接口(第1036-1085行)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// ==================== 出国申请进度查询(监控)相关API ====================
|
|
||||||
|
|
||||||
// 审批监控查询参数类型
|
|
||||||
export interface OverseasMonitorQuery {
|
|
||||||
pageNum: number;
|
|
||||||
pageSize: number;
|
|
||||||
applicantName?: string;
|
|
||||||
department?: string;
|
|
||||||
destination?: string;
|
|
||||||
approvalLevel?: string;
|
|
||||||
currentApprover?: string;
|
|
||||||
status?: string;
|
|
||||||
applyTimeStart?: string;
|
|
||||||
applyTimeEnd?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询待审批列表(监控)
|
|
||||||
export function getPendingList(query: OverseasMonitorQuery) {
|
|
||||||
return request({
|
|
||||||
url: '/rs/overseas/monitor/pending/list',
|
|
||||||
method: 'get',
|
|
||||||
params: query
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询已完成审批列表(监控)
|
|
||||||
export function getCompletedList(query: OverseasMonitorQuery) {
|
|
||||||
return request({
|
|
||||||
url: '/rs/overseas/monitor/completed/list',
|
|
||||||
method: 'get',
|
|
||||||
params: query
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询申请详情(监控)
|
|
||||||
export function getDetail(applicationId: string | number) {
|
|
||||||
return request({
|
|
||||||
url: `/rs/overseas/monitor/detail/${applicationId}`,
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载审批表PDF
|
|
||||||
export function downloadApprovalPdf(applicationId: string | number) {
|
|
||||||
return request({
|
|
||||||
url: `/rs/overseas/monitor/downloadApprovalPdf/${applicationId}`,
|
|
||||||
method: 'get',
|
|
||||||
responseType: 'blob'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 二、后端修改
|
|
||||||
|
|
||||||
#### 1. Service接口
|
|
||||||
**文件**: `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApprovalMonitorService.java`
|
|
||||||
|
|
||||||
**新增方法**:
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 下载出国申请审批表PDF
|
|
||||||
*
|
|
||||||
* @param applicationId 申请ID
|
|
||||||
* @param response HTTP响应对象
|
|
||||||
*/
|
|
||||||
void downloadApprovalPdf(Long applicationId, HttpServletResponse response);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Controller接口
|
|
||||||
**文件**: `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApprovalMonitorController.java`
|
|
||||||
|
|
||||||
**新增接口** (第119-135行):
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 下载出国申请审批表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);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. Service实现
|
|
||||||
**文件**: `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalMonitorServiceImpl.java`
|
|
||||||
|
|
||||||
**新增依赖导入**:
|
|
||||||
```java
|
|
||||||
import com.itextpdf.text.*;
|
|
||||||
import com.itextpdf.text.pdf.*;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
```
|
|
||||||
|
|
||||||
**新增方法** (第521-808行):
|
|
||||||
|
|
||||||
1. **`downloadApprovalPdf`**: 主方法,负责查询数据和设置响应头
|
|
||||||
2. **`generateApprovalPdf`**: PDF生成核心方法
|
|
||||||
3. **`addCell`**: 添加表格单元格辅助方法
|
|
||||||
4. **`addApprovalRow`**: 添加审批意见行辅助方法
|
|
||||||
5. **`getApprovalOpinion`**: 获取指定级别审批意见
|
|
||||||
6. **`formatDate`**: 格式化日期(yyyy-MM-dd)
|
|
||||||
7. **`formatBirthDate`**: 格式化出生日期(yyyy年MM月)
|
|
||||||
|
|
||||||
**PDF文档结构**:
|
|
||||||
1. 标题:
|
|
||||||
- "杭州第一技师学院"
|
|
||||||
- "登记报备人员因私事出国(境)审批表"
|
|
||||||
|
|
||||||
2. 个人信息表格(8列):
|
|
||||||
- 姓名、性别、出生年月、政治面貌
|
|
||||||
- 现工作部门(教学系)、职称、职务
|
|
||||||
- 申领证件名称、证件号码
|
|
||||||
- 前往国家或地区、出境事由(旅游/探亲)
|
|
||||||
- 探望对象姓名及与申请者关系
|
|
||||||
- 出国时间、备注
|
|
||||||
- 承诺声明、联系电话、应急联系人
|
|
||||||
|
|
||||||
3. 审批意见表格(2列):
|
|
||||||
- 部门(教学系)意见
|
|
||||||
- 人事(组织)处审核意见
|
|
||||||
- 纪委意见
|
|
||||||
- 学院分管领导意见
|
|
||||||
- 学院党委书记意见
|
|
||||||
|
|
||||||
4. 备注说明:
|
|
||||||
- 因私出境时间说明
|
|
||||||
- 证件号码填写说明
|
|
||||||
|
|
||||||
## 🔧 技术实现细节
|
|
||||||
|
|
||||||
### PDF生成技术
|
|
||||||
- **库**: iText 5.5.13.3
|
|
||||||
- **中文字体**: STSong-Light (UniGB-UCS2-H)
|
|
||||||
- **页面尺寸**: A4
|
|
||||||
- **页边距**: 40pt (上下左右)
|
|
||||||
|
|
||||||
### 字体大小
|
|
||||||
- **标题字体**: 16pt, 加粗
|
|
||||||
- **副标题字体**: 14pt, 加粗
|
|
||||||
- **正文字体**: 10pt, 常规
|
|
||||||
- **小字体**: 9pt, 常规
|
|
||||||
|
|
||||||
### 表格设计
|
|
||||||
- **个人信息表**: 8列,列宽比例 [8:12:6:10:10:12:16:6]
|
|
||||||
- **审批意见表**: 2列,列宽比例 [20:80]
|
|
||||||
- **单元格最小高度**: 25-50pt
|
|
||||||
- **表头背景**: 浅灰色 (BaseColor.LIGHT_GRAY)
|
|
||||||
|
|
||||||
### 数据处理
|
|
||||||
- **性别翻译**: 0→男, 1→女
|
|
||||||
- **政治面貌翻译**: 支持多种编码格式(01-13, 1-13, 英文代码)
|
|
||||||
- **出境事由**: 旅游/探亲,用√标记
|
|
||||||
- **日期格式化**: yyyy-MM-dd / yyyy年MM月
|
|
||||||
- **审批意见**: 优先显示审批意见,无意见时通过状态显示"同意"
|
|
||||||
|
|
||||||
## 📝 权限要求
|
|
||||||
|
|
||||||
- **查看权限**: `rs:overseas:query`
|
|
||||||
- **列表权限**: `rs:overseas:list`
|
|
||||||
|
|
||||||
## 🎨 UI/UX优化
|
|
||||||
|
|
||||||
1. **按钮位置**: 弹窗footer左右布局,下载按钮在左侧,关闭按钮在右侧
|
|
||||||
2. **按钮样式**: 主色调按钮(type="primary")+ 下载图标
|
|
||||||
3. **显示条件**: 仅审批完成状态显示下载按钮
|
|
||||||
4. **用户提示**:
|
|
||||||
- 开始下载:"正在生成PDF,请稍候..."
|
|
||||||
- 下载成功:"PDF下载成功"
|
|
||||||
- 下载失败:"下载PDF失败,请稍后重试"
|
|
||||||
|
|
||||||
## ✅ 测试要点
|
|
||||||
|
|
||||||
### 前端测试
|
|
||||||
1. ✅ 有变更记录时,变更历史区域正常显示
|
|
||||||
2. ✅ 无变更记录时,变更历史区域不显示
|
|
||||||
3. ✅ 审批完成状态显示下载PDF按钮
|
|
||||||
4. ✅ 待审批状态不显示下载PDF按钮
|
|
||||||
5. ✅ 点击下载按钮能触发PDF下载
|
|
||||||
6. ✅ 下载过程中显示加载提示
|
|
||||||
|
|
||||||
### 后端测试
|
|
||||||
1. ✅ PDF文件名包含申请人姓名和时间戳
|
|
||||||
2. ✅ PDF包含完整的个人信息
|
|
||||||
3. ✅ PDF包含所有审批历史记录
|
|
||||||
4. ✅ 中文字体正确显示
|
|
||||||
5. ✅ 表格格式符合模板要求
|
|
||||||
6. ✅ 审批意见正确填充
|
|
||||||
7. ✅ 日期格式化正确
|
|
||||||
|
|
||||||
## 📊 文件清单
|
|
||||||
|
|
||||||
### 前端文件
|
|
||||||
- ✅ `web/src/views/rs/overseas/monitor/index.vue` (修改)
|
|
||||||
- ✅ `web/src/api/rs/overseas.ts` (修改,新增monitor相关API)
|
|
||||||
|
|
||||||
### 后端文件
|
|
||||||
- ✅ `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApprovalMonitorService.java` (修改)
|
|
||||||
- ✅ `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApprovalMonitorController.java` (修改)
|
|
||||||
- ✅ `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalMonitorServiceImpl.java` (修改)
|
|
||||||
|
|
||||||
### 文档文件
|
|
||||||
- ✅ `web/src/views/rs/overseas/monitor/出国申请进度查询功能优化说明.md` (新增)
|
|
||||||
|
|
||||||
## 🚀 部署说明
|
|
||||||
|
|
||||||
### 前端部署
|
|
||||||
```bash
|
|
||||||
cd web
|
|
||||||
npm run build
|
|
||||||
# 部署dist目录到Nginx
|
|
||||||
```
|
|
||||||
|
|
||||||
### 后端部署
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
mvn clean package -DskipTests
|
|
||||||
# 重启ruoyi-admin服务
|
|
||||||
```
|
|
||||||
|
|
||||||
### 依赖检查
|
|
||||||
后端已包含iText PDF生成库依赖:
|
|
||||||
- `com.itextpdf:itextpdf:5.5.13.3`
|
|
||||||
- `com.itextpdf:itext-asian:5.2.0` (中文字体支持)
|
|
||||||
|
|
||||||
## 📖 相关文档
|
|
||||||
|
|
||||||
- **项目规范**: `.cursor/cursorrules.md`
|
|
||||||
- **开发文档**: `docs/开发文档/API/`
|
|
||||||
- **出国申请API**: `docs/开发文档/API/出国申请管理API.md`
|
|
||||||
|
|
||||||
## 🔗 相关模块
|
|
||||||
|
|
||||||
- **出国申请管理**: `/rs/overseas/application`
|
|
||||||
- **出国记录管理**: `/rs/overseas/record`
|
|
||||||
- **行程变更管理**: `/rs/itinerary/change`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**实施人**: Cursor AI
|
|
||||||
**审核状态**: ✅ 已完成
|
|
||||||
**更新日期**: 2025-01-29
|
|
||||||
|
|
||||||
@@ -1,394 +0,0 @@
|
|||||||
# 出国申请进度查询功能修改说明
|
|
||||||
|
|
||||||
## 📋 修改概述
|
|
||||||
|
|
||||||
**修改时间**:2025-10-29
|
|
||||||
**修改目的**:按照审批流程要求,修改出国申请进度查询功能,实现申请人查看自己提交的申请进度
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 功能需求
|
|
||||||
|
|
||||||
### 1. 待审批选项卡
|
|
||||||
- **功能**:用户提交申请后,在这里查看自己的待审批记录
|
|
||||||
- **显示内容**:
|
|
||||||
- 当前用户提交的所有待审批申请
|
|
||||||
- 当前审批环节和审批人
|
|
||||||
- 审批历史记录
|
|
||||||
|
|
||||||
### 2. 审批完成选项卡
|
|
||||||
- **功能**:审批完成后,抄送一份到这里
|
|
||||||
- **显示内容**:
|
|
||||||
- 当前用户提交的所有已完成审批申请
|
|
||||||
- 完整的审批历史记录
|
|
||||||
- 关联的出国记录信息
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 修改内容
|
|
||||||
|
|
||||||
### 1. 后端Service修改
|
|
||||||
|
|
||||||
**文件**:`backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalMonitorServiceImpl.java`
|
|
||||||
|
|
||||||
#### 修改点1:待审批列表查询
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 查询审批中的申请列表(待审核状态)
|
|
||||||
* 🔴 只查询当前用户提交的待审批申请
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public TableDataInfo<RsOverseasApprovalMonitorVo> queryPendingList(RsOverseasApprovalMonitorQuery query) {
|
|
||||||
// 🔴 获取当前登录用户ID
|
|
||||||
Long currentUserId = LoginHelper.getUserId();
|
|
||||||
|
|
||||||
// 构建查询条件
|
|
||||||
QueryWrapper<RsOverseasApplication> wrapper = buildQueryWrapper(query);
|
|
||||||
wrapper.eq("status", "0"); // 只查询待审核状态
|
|
||||||
wrapper.eq("applicant_id", currentUserId); // 🔴 只查询当前用户提交的申请
|
|
||||||
|
|
||||||
// ... 分页查询和数据处理
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ✅ 添加当前用户ID过滤条件
|
|
||||||
- ✅ 只查询申请人ID = 当前用户ID的记录
|
|
||||||
- ✅ 添加日志记录便于排查问题
|
|
||||||
|
|
||||||
#### 修改点2:已完成列表查询
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 查询已完成审批的申请列表(已通过状态)
|
|
||||||
* 🔴 只查询当前用户提交的已完成审批申请(抄送功能)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public TableDataInfo<RsOverseasApprovalMonitorVo> queryCompletedList(RsOverseasApprovalMonitorQuery query) {
|
|
||||||
// 🔴 获取当前登录用户ID
|
|
||||||
Long currentUserId = LoginHelper.getUserId();
|
|
||||||
|
|
||||||
// 构建查询条件
|
|
||||||
QueryWrapper<RsOverseasApplication> wrapper = buildQueryWrapper(query);
|
|
||||||
wrapper.eq("status", "1"); // 只查询已通过状态
|
|
||||||
wrapper.eq("applicant_id", currentUserId); // 🔴 只查询当前用户提交的申请
|
|
||||||
|
|
||||||
// ... 分页查询和数据处理
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ✅ 实现抄送功能:审批完成后,申请人可以在这里查看
|
|
||||||
- ✅ 添加当前用户ID过滤条件
|
|
||||||
- ✅ 只显示当前用户提交且已完成审批的申请
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 后端Controller修改
|
|
||||||
|
|
||||||
**文件**:`backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApprovalMonitorController.java`
|
|
||||||
|
|
||||||
#### 修改点:恢复pending接口功能
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 查询待审批的申请列表(出国申请进度查询)
|
|
||||||
* 🔴 功能说明:查询当前用户提交的待审批申请
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("rs:overseas:list")
|
|
||||||
@GetMapping("/pending/list")
|
|
||||||
public TableDataInfo<RsOverseasApprovalMonitorVo> pendingList(RsOverseasApprovalMonitorQuery query) {
|
|
||||||
log.info("查询当前用户的待审批申请列表");
|
|
||||||
query.setStatus("0");
|
|
||||||
return monitorService.queryPendingList(query);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ❌ 删除了工作流迁移提示
|
|
||||||
- ✅ 恢复正常的查询功能
|
|
||||||
- ✅ 返回实际数据而不是错误提示
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 前端页面修改
|
|
||||||
|
|
||||||
**文件**:`web/src/views/rs/overseas/monitor/index.vue`
|
|
||||||
|
|
||||||
#### 修改点1:删除工作流迁移提示
|
|
||||||
```vue
|
|
||||||
<!-- 🔴 删除的内容 -->
|
|
||||||
<el-alert
|
|
||||||
title="功能迁移通知"
|
|
||||||
description="出国申请监控功能已标准化..."
|
|
||||||
...
|
|
||||||
>
|
|
||||||
<el-button @click="navigateToTaskWaiting">前往待办任务页面</el-button>
|
|
||||||
</el-alert>
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ❌ 删除整个Alert提示组件
|
|
||||||
- ❌ 删除跳转到工作流页面的按钮
|
|
||||||
|
|
||||||
#### 修改点2:恢复待审批表格显示
|
|
||||||
```vue
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table v-loading="pendingLoading" :data="pendingList" border>
|
|
||||||
<!-- 之前是 style="display: none" 隐藏的,现在恢复显示 -->
|
|
||||||
<el-table-column label="申请人" prop="applicantName" ... />
|
|
||||||
<el-table-column label="所属部门" prop="department" ... />
|
|
||||||
<!-- ... 其他列 -->
|
|
||||||
</el-table>
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ✅ 恢复表格正常显示
|
|
||||||
- ✅ 删除 `style="display: none"`
|
|
||||||
- ✅ 显示待审批申请数据
|
|
||||||
|
|
||||||
#### 修改点3:恢复查询功能
|
|
||||||
```javascript
|
|
||||||
// 🔴 修改前:显示提示信息
|
|
||||||
const getPendingList = async () => {
|
|
||||||
ElMessage.info('请使用标准工作流待办页面查看审批任务');
|
|
||||||
pendingList.value = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ 修改后:正常查询数据
|
|
||||||
const getPendingList = async () => {
|
|
||||||
pendingLoading.value = true;
|
|
||||||
try {
|
|
||||||
const response = await apiGetPendingList(pendingQueryParams);
|
|
||||||
if (response.code === 200) {
|
|
||||||
pendingList.value = response.rows || [];
|
|
||||||
pendingTotal.value = response.total || 0;
|
|
||||||
} else {
|
|
||||||
ElMessage.error(response.msg || '获取数据失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取待审批列表失败', error);
|
|
||||||
ElMessage.error('获取数据失败,请稍后重试');
|
|
||||||
} finally {
|
|
||||||
pendingLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ✅ 调用真实API接口
|
|
||||||
- ✅ 处理loading状态
|
|
||||||
- ✅ 显示查询结果或错误提示
|
|
||||||
|
|
||||||
#### 修改点4:恢复查询和重置按钮功能
|
|
||||||
```javascript
|
|
||||||
// 查询按钮
|
|
||||||
const handlePendingQuery = () => {
|
|
||||||
pendingQueryParams.pageNum = 1;
|
|
||||||
getPendingList();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 重置按钮
|
|
||||||
const resetPendingQuery = () => {
|
|
||||||
pendingQueryParams.pageNum = 1;
|
|
||||||
pendingQueryParams.applicantName = undefined;
|
|
||||||
pendingQueryParams.department = undefined;
|
|
||||||
pendingQueryParams.destination = undefined;
|
|
||||||
pendingQueryParams.approvalLevel = undefined;
|
|
||||||
getPendingList();
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ✅ 恢复正常的查询功能
|
|
||||||
- ✅ 恢复正常的重置功能
|
|
||||||
- ❌ 删除工作流跳转提示
|
|
||||||
|
|
||||||
#### 修改点5:添加功能说明
|
|
||||||
```vue
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<div class="info-text">
|
|
||||||
<el-icon><InfoFilled /></el-icon>
|
|
||||||
<span>这里显示您提交的待审批申请</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改说明**:
|
|
||||||
- ✅ 在操作按钮区域添加功能说明
|
|
||||||
- ✅ 明确告知用户这里只显示自己提交的申请
|
|
||||||
- ✅ 添加对应的CSS样式
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 UI变化
|
|
||||||
|
|
||||||
### 修改前
|
|
||||||
```
|
|
||||||
[Alert提示框:功能迁移通知]
|
|
||||||
[前往待办任务页面按钮]
|
|
||||||
[Empty: 请使用标准工作流待办页面]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 修改后
|
|
||||||
```
|
|
||||||
[搜索条件]
|
|
||||||
[刷新按钮] [ℹ️ 这里显示您提交的待审批申请]
|
|
||||||
[数据表格:显示待审批申请列表]
|
|
||||||
[分页组件]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔍 数据过滤逻辑
|
|
||||||
|
|
||||||
### 待审批选项卡
|
|
||||||
```sql
|
|
||||||
SELECT * FROM rs_overseas_application
|
|
||||||
WHERE status = '0' -- 待审核状态
|
|
||||||
AND applicant_id = {当前用户ID} -- 🔴 只查询当前用户提交的
|
|
||||||
AND deleted = '0'
|
|
||||||
AND tenant_id = {租户ID}
|
|
||||||
ORDER BY apply_time DESC
|
|
||||||
```
|
|
||||||
|
|
||||||
### 审批完成选项卡
|
|
||||||
```sql
|
|
||||||
SELECT * FROM rs_overseas_application
|
|
||||||
WHERE status = '1' -- 已通过状态
|
|
||||||
AND applicant_id = {当前用户ID} -- 🔴 只查询当前用户提交的
|
|
||||||
AND deleted = '0'
|
|
||||||
AND tenant_id = {租户ID}
|
|
||||||
ORDER BY apply_time DESC
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 测试场景1:待审批选项卡
|
|
||||||
|
|
||||||
**测试步骤**:
|
|
||||||
1. 使用用户A登录系统
|
|
||||||
2. 提交一个出国申请
|
|
||||||
3. 访问"出国申请进度查询"菜单
|
|
||||||
4. 切换到"待审批中"选项卡
|
|
||||||
|
|
||||||
**预期结果**:
|
|
||||||
- ✅ 能看到刚才提交的申请记录
|
|
||||||
- ✅ 显示当前审批环节和审批人
|
|
||||||
- ✅ 点击"查看详情"能查看审批历史
|
|
||||||
|
|
||||||
**测试步骤2**:
|
|
||||||
1. 使用用户B登录系统
|
|
||||||
2. 访问"出国申请进度查询"菜单
|
|
||||||
3. 切换到"待审批中"选项卡
|
|
||||||
|
|
||||||
**预期结果**:
|
|
||||||
- ✅ 只能看到用户B提交的申请
|
|
||||||
- ❌ 看不到用户A提交的申请
|
|
||||||
|
|
||||||
### 测试场景2:审批完成选项卡
|
|
||||||
|
|
||||||
**测试步骤**:
|
|
||||||
1. 使用用户A登录系统
|
|
||||||
2. 提交一个出国申请
|
|
||||||
3. 完成全部审批流程
|
|
||||||
4. 访问"出国申请进度查询"菜单
|
|
||||||
5. 切换到"审批完成"选项卡
|
|
||||||
|
|
||||||
**预期结果**:
|
|
||||||
- ✅ 能看到刚才审批完成的申请(抄送)
|
|
||||||
- ✅ 显示完整的审批历史记录
|
|
||||||
- ✅ 能查看关联的出国记录信息
|
|
||||||
|
|
||||||
### 测试场景3:搜索功能
|
|
||||||
|
|
||||||
**测试步骤**:
|
|
||||||
1. 在"待审批中"选项卡输入搜索条件
|
|
||||||
2. 点击"搜索"按钮
|
|
||||||
|
|
||||||
**预期结果**:
|
|
||||||
- ✅ 能正常搜索并返回结果
|
|
||||||
- ✅ 结果仍然只包含当前用户提交的申请
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 修改文件清单
|
|
||||||
|
|
||||||
| 序号 | 文件类型 | 文件路径 | 修改内容 | 状态 |
|
|
||||||
|------|---------|---------|---------|------|
|
|
||||||
| 1 | 后端Service | `RsOverseasApprovalMonitorServiceImpl.java` | 添加当前用户过滤条件 | ✅ |
|
|
||||||
| 2 | 后端Controller | `RsOverseasApprovalMonitorController.java` | 恢复pending接口功能 | ✅ |
|
|
||||||
| 3 | 前端页面 | `monitor/index.vue` | 删除工作流提示,恢复表格显示 | ✅ |
|
|
||||||
| 4 | 前端脚本 | `monitor/index.vue` | 恢复查询、重置功能 | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚠️ 注意事项
|
|
||||||
|
|
||||||
### 1. 权限要求
|
|
||||||
- 用户需要有 `rs:overseas:list` 权限才能访问此菜单
|
|
||||||
- 用户需要有 `rs:overseas:query` 权限才能查看详情
|
|
||||||
|
|
||||||
### 2. 数据范围
|
|
||||||
- **待审批选项卡**:只显示当前用户提交的待审核申请
|
|
||||||
- **审批完成选项卡**:只显示当前用户提交的已完成申请
|
|
||||||
- 不会显示其他用户提交的申请
|
|
||||||
|
|
||||||
### 3. 与其他模块的关系
|
|
||||||
- **出国申请**:用户在这里提交申请
|
|
||||||
- **出国审批**:审批人在这里审批申请
|
|
||||||
- **出国记录**:审批完成后,申请人在这里查看出国记录
|
|
||||||
- **出国申请进度查询**:申请人在这里查看申请进度(当前模块)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 部署说明
|
|
||||||
|
|
||||||
### 后端部署
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
|
||||||
./stop-main.sh
|
|
||||||
./start-main.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### 前端部署
|
|
||||||
```bash
|
|
||||||
cd web
|
|
||||||
npm run build
|
|
||||||
# 将dist目录部署到nginx
|
|
||||||
```
|
|
||||||
|
|
||||||
### 验证步骤
|
|
||||||
1. 清除浏览器缓存
|
|
||||||
2. 重新登录系统
|
|
||||||
3. 访问"出国申请进度查询"菜单
|
|
||||||
4. 确认功能正常
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 验收标准
|
|
||||||
|
|
||||||
- [ ] 待审批选项卡只显示当前用户提交的待审批申请
|
|
||||||
- [ ] 审批完成选项卡只显示当前用户提交的已完成审批申请
|
|
||||||
- [ ] 搜索功能正常工作
|
|
||||||
- [ ] 重置功能正常工作
|
|
||||||
- [ ] 查看详情能显示审批历史
|
|
||||||
- [ ] 不会显示其他用户的申请
|
|
||||||
- [ ] UI显示正常,无工作流迁移提示
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 问题反馈
|
|
||||||
|
|
||||||
如有问题,请检查:
|
|
||||||
1. 后端日志:`backend/logs/sys-info.log`
|
|
||||||
2. 浏览器控制台:F12查看网络请求和错误
|
|
||||||
3. 确认用户权限配置正确
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**文档版本**:v1.0
|
|
||||||
**最后更新**:2025-10-29
|
|
||||||
**修改人员**:Cursor AI
|
|
||||||
|
|
||||||
@@ -1,411 +0,0 @@
|
|||||||
# 出国申请审批环节命名标准分析报告
|
|
||||||
|
|
||||||
## 📋 问题发现
|
|
||||||
|
|
||||||
在分析代码时,发现系统中**存在两套不同的审批级别命名标准**:
|
|
||||||
|
|
||||||
1. **出国申请进度查询模块**:使用**3级审批**命名
|
|
||||||
2. **出国申请审批模块**:使用**5级审批**命名
|
|
||||||
|
|
||||||
这导致**命名不一致**的问题。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔍 详细分析
|
|
||||||
|
|
||||||
### 一、出国申请进度查询模块(monitor)
|
|
||||||
|
|
||||||
**文件位置**:`RsOverseasApprovalMonitorServiceImpl.java`
|
|
||||||
|
|
||||||
**当前定义**(第390-404行):
|
|
||||||
```java
|
|
||||||
private String getApprovalLevelText(String level) {
|
|
||||||
if (level == null) {
|
|
||||||
return "未开始";
|
|
||||||
}
|
|
||||||
switch (level) {
|
|
||||||
case "1":
|
|
||||||
return "部门主管审批";
|
|
||||||
case "2":
|
|
||||||
return "分管领导审批";
|
|
||||||
case "3":
|
|
||||||
return "校领导审批";
|
|
||||||
default:
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**问题**:
|
|
||||||
- ❌ 只定义了3个级别
|
|
||||||
- ❌ Level 4 和 Level 5 会显示为"未知"
|
|
||||||
- ❌ 命名与实际审批流程不符
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 二、出国申请审批模块(application)
|
|
||||||
|
|
||||||
**文件位置**:`RsOverseasApplicationServiceImpl.java`
|
|
||||||
|
|
||||||
**当前定义**(第1269-1283行):
|
|
||||||
```java
|
|
||||||
private String getApprovalLevelDescription(String approvalLevel) {
|
|
||||||
switch (approvalLevel) {
|
|
||||||
case "1":
|
|
||||||
return "待部门负责人审批";
|
|
||||||
case "2":
|
|
||||||
return "待人事处审批";
|
|
||||||
case "3":
|
|
||||||
return "待纪委审批";
|
|
||||||
case "4":
|
|
||||||
return "待学院分管领导审批";
|
|
||||||
case "5":
|
|
||||||
return "待学院党委书记审批";
|
|
||||||
default:
|
|
||||||
return "待审批";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**流转逻辑**(第683-698行):
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 获取下一级审批级别
|
|
||||||
* 五级审批制度:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
|
|
||||||
*/
|
|
||||||
private String getNextApprovalLevel(String currentLevel) {
|
|
||||||
switch (currentLevel) {
|
|
||||||
case "1":
|
|
||||||
return "2"; // 部门负责人 → 人事处
|
|
||||||
case "2":
|
|
||||||
return "3"; // 人事处 → 纪委
|
|
||||||
case "3":
|
|
||||||
return "4"; // 纪委 → 学院分管领导
|
|
||||||
case "4":
|
|
||||||
return "5"; // 学院分管领导 → 学院党委书记
|
|
||||||
case "5":
|
|
||||||
return null; // 学院党委书记 → 审批完成
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 标准审批级别定义
|
|
||||||
|
|
||||||
### 完整的五级审批制度
|
|
||||||
|
|
||||||
| 审批级别 | 数据库值 | 权限标识 | 审批环节名称 | 当前审批人显示 | 说明 |
|
|
||||||
|---------|---------|---------|------------|--------------|------|
|
|
||||||
| **Level 1** | `"1"` | `rs:overseas:approve1` | **部门负责人审批** | 待部门负责人审批 | 只能看到本部门的申请 |
|
|
||||||
| **Level 2** | `"2"` | `rs:overseas:approve2` | **人事处审批** | 待人事处审批 | 可以看到所有部门的申请 |
|
|
||||||
| **Level 3** | `"3"` | `rs:overseas:approve3` | **纪委审批** | 待纪委审批 | 可以看到所有部门的申请 |
|
|
||||||
| **Level 4** | `"4"` | `rs:overseas:approve4` | **学院分管领导审批** | 待学院分管领导审批 | 可以看到所有部门的申请 |
|
|
||||||
| **Level 5** | `"5"` | `rs:overseas:approve5` | **学院党委书记审批** | 待学院党委书记审批 | 可以看到所有部门的申请 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 审批流程完整流转
|
|
||||||
|
|
||||||
### 流程图
|
|
||||||
```
|
|
||||||
申请提交
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────┐
|
|
||||||
│ Level 1: 部门负责人审批 │
|
|
||||||
│ - 审批人:各部门负责人 │
|
|
||||||
│ - 权限:rs:overseas:approve1 │
|
|
||||||
│ - 范围:仅本部门申请 │
|
|
||||||
└─────────────────────────────────┘
|
|
||||||
↓ (审批通过)
|
|
||||||
┌─────────────────────────────────┐
|
|
||||||
│ Level 2: 人事处审批 │
|
|
||||||
│ - 审批人:人事处工作人员 │
|
|
||||||
│ - 权限:rs:overseas:approve2 │
|
|
||||||
│ - 范围:所有部门申请 │
|
|
||||||
└─────────────────────────────────┘
|
|
||||||
↓ (审批通过)
|
|
||||||
┌─────────────────────────────────┐
|
|
||||||
│ Level 3: 纪委审批 │
|
|
||||||
│ - 审批人:纪委工作人员 │
|
|
||||||
│ - 权限:rs:overseas:approve3 │
|
|
||||||
│ - 范围:所有部门申请 │
|
|
||||||
└─────────────────────────────────┘
|
|
||||||
↓ (审批通过)
|
|
||||||
┌─────────────────────────────────┐
|
|
||||||
│ Level 4: 学院分管领导审批 │
|
|
||||||
│ - 审批人:学院分管领导 │
|
|
||||||
│ - 权限:rs:overseas:approve4 │
|
|
||||||
│ - 范围:所有部门申请 │
|
|
||||||
└─────────────────────────────────┘
|
|
||||||
↓ (审批通过)
|
|
||||||
┌─────────────────────────────────┐
|
|
||||||
│ Level 5: 学院党委书记审批 │
|
|
||||||
│ - 审批人:学院党委书记 │
|
|
||||||
│ - 权限:rs:overseas:approve5 │
|
|
||||||
│ - 范围:所有部门申请 │
|
|
||||||
└─────────────────────────────────┘
|
|
||||||
↓ (审批通过)
|
|
||||||
申请完成 → 生成出国记录
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 各字段的具体命名标准
|
|
||||||
|
|
||||||
### 1. **当前审批环节**(approval_level字段)
|
|
||||||
|
|
||||||
**数据库字段**:`approval_level VARCHAR(10)`
|
|
||||||
|
|
||||||
**标准命名**(用于显示):
|
|
||||||
```java
|
|
||||||
switch (approvalLevel) {
|
|
||||||
case "1": return "部门负责人审批";
|
|
||||||
case "2": return "人事处审批";
|
|
||||||
case "3": return "纪委审批";
|
|
||||||
case "4": return "学院分管领导审批";
|
|
||||||
case "5": return "学院党委书记审批";
|
|
||||||
default: return "未知";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. **当前审批人**(current_approver字段)
|
|
||||||
|
|
||||||
**数据库字段**:`current_approver VARCHAR(100)`
|
|
||||||
|
|
||||||
**标准命名格式**:
|
|
||||||
```java
|
|
||||||
switch (approvalLevel) {
|
|
||||||
case "1": return "待部门负责人审批";
|
|
||||||
case "2": return "待人事处审批";
|
|
||||||
case "3": return "待纪委审批";
|
|
||||||
case "4": return "待学院分管领导审批";
|
|
||||||
case "5": return "待学院党委书记审批";
|
|
||||||
default: return "待审批";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**特殊情况**:
|
|
||||||
- 如果是部门负责人级别,可能显示:`"待指定部门负责人"` 或 `"待{具体审批人姓名}审批"`
|
|
||||||
- 其他级别通常显示:`"待{审批环节}审批"`
|
|
||||||
|
|
||||||
### 3. **审批进度**(用于进度查询)
|
|
||||||
|
|
||||||
**显示格式**:`"已审批级别数/总级别数"`
|
|
||||||
|
|
||||||
**示例**:
|
|
||||||
- `"0/5"` - 未开始审批
|
|
||||||
- `"1/5"` - 部门负责人已审批
|
|
||||||
- `"2/5"` - 人事处已审批
|
|
||||||
- `"3/5"` - 纪委已审批
|
|
||||||
- `"4/5"` - 学院分管领导已审批
|
|
||||||
- `"5/5"` - 全部审批完成
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 数据库定义
|
|
||||||
|
|
||||||
### rs_overseas_application表字段
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- 审批级别字段
|
|
||||||
`approval_level` varchar(10) DEFAULT '1'
|
|
||||||
COMMENT '当前审批级别:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记'
|
|
||||||
|
|
||||||
-- 当前审批人字段
|
|
||||||
`current_approver` varchar(100) DEFAULT NULL
|
|
||||||
COMMENT '当前审批人姓名或环节描述'
|
|
||||||
|
|
||||||
-- 当前审批人ID字段(冗余字段,仅供参考)
|
|
||||||
`current_approver_id` bigint DEFAULT NULL
|
|
||||||
COMMENT '当前审批人ID(冗余字段)'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 前端显示示例
|
|
||||||
|
|
||||||
### 待审批列表显示
|
|
||||||
|
|
||||||
| 申请人 | 所属部门 | 当前审批环节 | 当前审批人 | 审批进度 |
|
|
||||||
|--------|---------|-------------|-----------|---------|
|
|
||||||
| 张三 | 教务处 | 部门负责人审批 | 待部门负责人审批 | 0% (0/5) |
|
|
||||||
| 李四 | 人事处 | 人事处审批 | 待人事处审批 | 20% (1/5) |
|
|
||||||
| 王五 | 学工处 | 纪委审批 | 待纪委审批 | 40% (2/5) |
|
|
||||||
| 赵六 | 后勤处 | 学院分管领导审批 | 待学院分管领导审批 | 60% (3/5) |
|
|
||||||
| 钱七 | 财务处 | 学院党委书记审批 | 待学院党委书记审批 | 80% (4/5) |
|
|
||||||
|
|
||||||
### 审批历史记录显示
|
|
||||||
|
|
||||||
```
|
|
||||||
📋 审批历史
|
|
||||||
|
|
||||||
✅ 第1级:部门负责人审批
|
|
||||||
审批人:张主任
|
|
||||||
审批时间:2025-10-27 10:30:25
|
|
||||||
审批结果:通过
|
|
||||||
审批意见:同意出国交流
|
|
||||||
|
|
||||||
✅ 第2级:人事处审批
|
|
||||||
审批人:李处长
|
|
||||||
审批时间:2025-10-27 14:20:15
|
|
||||||
审批结果:通过
|
|
||||||
审批意见:人事档案无问题,同意
|
|
||||||
|
|
||||||
⏳ 第3级:纪委审批
|
|
||||||
状态:待审批
|
|
||||||
|
|
||||||
⏸️ 第4级:学院分管领导审批
|
|
||||||
状态:未到审批阶段
|
|
||||||
|
|
||||||
⏸️ 第5级:学院党委书记审批
|
|
||||||
状态:未到审批阶段
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚠️ 当前存在的问题
|
|
||||||
|
|
||||||
### 问题1:命名不统一
|
|
||||||
|
|
||||||
**问题描述**:
|
|
||||||
- `RsOverseasApprovalMonitorServiceImpl.java` 中只定义了3个级别
|
|
||||||
- 与实际的5级审批流程不符
|
|
||||||
|
|
||||||
**影响范围**:
|
|
||||||
- 出国申请进度查询菜单的"当前审批环节"显示
|
|
||||||
- Level 4 和 Level 5 会显示为"未知"
|
|
||||||
|
|
||||||
**解决方案**:
|
|
||||||
需要修改 `getApprovalLevelText()` 方法,添加完整的5级定义
|
|
||||||
|
|
||||||
### 问题2:总审批级别数设置错误
|
|
||||||
|
|
||||||
**问题位置**:`RsOverseasApprovalMonitorServiceImpl.java` 第345-346行
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 总审批级别数(假设最多3级:部门主管、分管领导、校领导)
|
|
||||||
vo.setTotalLevels(3);
|
|
||||||
```
|
|
||||||
|
|
||||||
**问题**:
|
|
||||||
- 实际是5级审批,但这里设置为3级
|
|
||||||
- 导致审批进度显示错误(如:2/3 而不是 2/5)
|
|
||||||
|
|
||||||
**影响**:
|
|
||||||
- 审批进度百分比计算错误
|
|
||||||
- 进度条显示不准确
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 建议的标准命名
|
|
||||||
|
|
||||||
### 统一的命名标准
|
|
||||||
|
|
||||||
| 级别 | 简称 | 完整名称 | 待审批显示 | 已审批显示 |
|
|
||||||
|------|------|---------|-----------|-----------|
|
|
||||||
| 1 | 部门负责人 | 部门负责人审批 | 待部门负责人审批 | 部门负责人已审批 |
|
|
||||||
| 2 | 人事处 | 人事处审批 | 待人事处审批 | 人事处已审批 |
|
|
||||||
| 3 | 纪委 | 纪委审批 | 待纪委审批 | 纪委已审批 |
|
|
||||||
| 4 | 学院分管领导 | 学院分管领导审批 | 待学院分管领导审批 | 学院分管领导已审批 |
|
|
||||||
| 5 | 学院党委书记 | 学院党委书记审批 | 待学院党委书记审批 | 学院党委书记已审批 |
|
|
||||||
|
|
||||||
### 代码实现建议
|
|
||||||
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 获取审批级别完整名称(用于"当前审批环节"列)
|
|
||||||
*/
|
|
||||||
private String getApprovalLevelText(String level) {
|
|
||||||
if (level == null) {
|
|
||||||
return "未开始";
|
|
||||||
}
|
|
||||||
switch (level) {
|
|
||||||
case "1":
|
|
||||||
return "部门负责人审批";
|
|
||||||
case "2":
|
|
||||||
return "人事处审批";
|
|
||||||
case "3":
|
|
||||||
return "纪委审批";
|
|
||||||
case "4":
|
|
||||||
return "学院分管领导审批";
|
|
||||||
case "5":
|
|
||||||
return "学院党委书记审批";
|
|
||||||
default:
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前审批人显示文本(用于"当前审批人"列)
|
|
||||||
*/
|
|
||||||
private String getCurrentApproverText(String level) {
|
|
||||||
if (level == null) {
|
|
||||||
return "未指定";
|
|
||||||
}
|
|
||||||
switch (level) {
|
|
||||||
case "1":
|
|
||||||
return "待部门负责人审批";
|
|
||||||
case "2":
|
|
||||||
return "待人事处审批";
|
|
||||||
case "3":
|
|
||||||
return "待纪委审批";
|
|
||||||
case "4":
|
|
||||||
return "待学院分管领导审批";
|
|
||||||
case "5":
|
|
||||||
return "待学院党委书记审批";
|
|
||||||
default:
|
|
||||||
return "待审批";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 总结
|
|
||||||
|
|
||||||
### 当前审批级别定义
|
|
||||||
|
|
||||||
**完整的五级审批体系**:
|
|
||||||
|
|
||||||
1. **Level 1 - 部门负责人审批**
|
|
||||||
- 权限:`rs:overseas:approve1`
|
|
||||||
- 范围:仅本部门申请
|
|
||||||
- 显示:部门负责人审批
|
|
||||||
|
|
||||||
2. **Level 2 - 人事处审批**
|
|
||||||
- 权限:`rs:overseas:approve2`
|
|
||||||
- 范围:所有部门申请
|
|
||||||
- 显示:人事处审批
|
|
||||||
|
|
||||||
3. **Level 3 - 纪委审批**
|
|
||||||
- 权限:`rs:overseas:approve3`
|
|
||||||
- 范围:所有部门申请
|
|
||||||
- 显示:纪委审批
|
|
||||||
|
|
||||||
4. **Level 4 - 学院分管领导审批**
|
|
||||||
- 权限:`rs:overseas:approve4`
|
|
||||||
- 范围:所有部门申请
|
|
||||||
- 显示:学院分管领导审批
|
|
||||||
|
|
||||||
5. **Level 5 - 学院党委书记审批**
|
|
||||||
- 权限:`rs:overseas:approve5`
|
|
||||||
- 范围:所有部门申请
|
|
||||||
- 显示:学院党委书记审批
|
|
||||||
|
|
||||||
### 关键注意事项
|
|
||||||
|
|
||||||
- ✅ 系统使用**5级审批制度**
|
|
||||||
- ✅ 只有**部门负责人(Level 1)**需要部门过滤
|
|
||||||
- ✅ **Level 2-5** 可以看到所有部门的申请
|
|
||||||
- ⚠️ 出国申请进度查询模块存在命名不一致问题
|
|
||||||
- ⚠️ 总审批级别数应该是5而不是3
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**分析完成时间**:2025-10-29
|
|
||||||
**分析人员**:Cursor AI
|
|
||||||
**文档版本**:v1.0
|
|
||||||
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
# 出国申请进度查询 - 审批级别修复说明
|
|
||||||
|
|
||||||
## 🔧 修复时间
|
|
||||||
2025-10-29
|
|
||||||
|
|
||||||
## 🎯 修复目标
|
|
||||||
将出国申请进度查询模块从3级审批改为完整的5级审批,与实际审批流程保持一致。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 修复内容
|
|
||||||
|
|
||||||
### 修复1:更新审批级别文本定义
|
|
||||||
|
|
||||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
|
||||||
**方法**:`getApprovalLevelText(String level)`
|
|
||||||
|
|
||||||
#### 修复前
|
|
||||||
```java
|
|
||||||
switch (level) {
|
|
||||||
case "1":
|
|
||||||
return "部门主管审批";
|
|
||||||
case "2":
|
|
||||||
return "分管领导审批";
|
|
||||||
case "3":
|
|
||||||
return "校领导审批";
|
|
||||||
default:
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**问题**:
|
|
||||||
- ❌ Level 4 和 Level 5 显示为"未知"
|
|
||||||
- ❌ 命名与实际流程不符
|
|
||||||
|
|
||||||
#### 修复后
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 获取审批级别文本
|
|
||||||
* 五级审批制度:1-部门负责人,2-人事处,3-纪委,4-学院分管领导,5-学院党委书记
|
|
||||||
*/
|
|
||||||
switch (level) {
|
|
||||||
case "1":
|
|
||||||
return "部门负责人审批";
|
|
||||||
case "2":
|
|
||||||
return "人事处审批";
|
|
||||||
case "3":
|
|
||||||
return "纪委审批";
|
|
||||||
case "4":
|
|
||||||
return "学院分管领导审批";
|
|
||||||
case "5":
|
|
||||||
return "学院党委书记审批";
|
|
||||||
default:
|
|
||||||
return "未知";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- ✅ 支持完整的5级审批
|
|
||||||
- ✅ 命名与实际流程一致
|
|
||||||
- ✅ 添加了详细的注释说明
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 修复2:更新总审批级别数
|
|
||||||
|
|
||||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
|
||||||
**方法**:`fillApprovalProgress()`
|
|
||||||
|
|
||||||
#### 修复前
|
|
||||||
```java
|
|
||||||
// 总审批级别数(假设最多3级:部门主管、分管领导、校领导)
|
|
||||||
vo.setTotalLevels(3);
|
|
||||||
|
|
||||||
// 未开始审批
|
|
||||||
vo.setApprovedLevels(0);
|
|
||||||
vo.setTotalLevels(3);
|
|
||||||
vo.setApprovalProgress("0/3");
|
|
||||||
```
|
|
||||||
|
|
||||||
**问题**:
|
|
||||||
- ❌ 总级别数设置为3
|
|
||||||
- ❌ 审批进度显示错误(如:2/3 而不是 2/5)
|
|
||||||
- ❌ 进度百分比计算错误
|
|
||||||
|
|
||||||
#### 修复后
|
|
||||||
```java
|
|
||||||
// 总审批级别数(五级审批制度:部门负责人、人事处、纪委、学院分管领导、学院党委书记)
|
|
||||||
vo.setTotalLevels(5);
|
|
||||||
|
|
||||||
// 未开始审批
|
|
||||||
vo.setApprovedLevels(0);
|
|
||||||
vo.setTotalLevels(5);
|
|
||||||
vo.setApprovalProgress("0/5");
|
|
||||||
```
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- ✅ 总级别数正确设置为5
|
|
||||||
- ✅ 审批进度显示正确
|
|
||||||
- ✅ 进度百分比计算准确
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 修复3:更新统计信息定义
|
|
||||||
|
|
||||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
|
||||||
**方法**:`getApprovalStatistics()`
|
|
||||||
|
|
||||||
#### 修复前
|
|
||||||
```java
|
|
||||||
// 2. 各审批级别的待审批数量
|
|
||||||
Map<String, Long> pendingByLevel = new HashMap<>();
|
|
||||||
pendingByLevel.put("level1", countByStatusAndLevel("0", "1", tenantId)); // 部门主管
|
|
||||||
pendingByLevel.put("level2", countByStatusAndLevel("0", "2", tenantId)); // 分管领导
|
|
||||||
pendingByLevel.put("level3", countByStatusAndLevel("0", "3", tenantId)); // 校领导
|
|
||||||
statistics.put("pendingByLevel", pendingByLevel);
|
|
||||||
```
|
|
||||||
|
|
||||||
**问题**:
|
|
||||||
- ❌ 只统计3个级别
|
|
||||||
- ❌ Level 4 和 Level 5 的统计缺失
|
|
||||||
|
|
||||||
#### 修复后
|
|
||||||
```java
|
|
||||||
// 2. 各审批级别的待审批数量(五级审批制度)
|
|
||||||
Map<String, Long> pendingByLevel = new HashMap<>();
|
|
||||||
pendingByLevel.put("level1", countByStatusAndLevel("0", "1", tenantId)); // 部门负责人
|
|
||||||
pendingByLevel.put("level2", countByStatusAndLevel("0", "2", tenantId)); // 人事处
|
|
||||||
pendingByLevel.put("level3", countByStatusAndLevel("0", "3", tenantId)); // 纪委
|
|
||||||
pendingByLevel.put("level4", countByStatusAndLevel("0", "4", tenantId)); // 学院分管领导
|
|
||||||
pendingByLevel.put("level5", countByStatusAndLevel("0", "5", tenantId)); // 学院党委书记
|
|
||||||
statistics.put("pendingByLevel", pendingByLevel);
|
|
||||||
```
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- ✅ 统计所有5个级别
|
|
||||||
- ✅ 统计数据完整准确
|
|
||||||
- ✅ 命名规范一致
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 修复效果对比
|
|
||||||
|
|
||||||
### 1. 当前审批环节显示
|
|
||||||
|
|
||||||
| 审批级别 | 修复前 | 修复后 | 状态 |
|
|
||||||
|---------|--------|--------|------|
|
|
||||||
| Level 1 | 部门主管审批 | 部门负责人审批 | ✅ 已修正 |
|
|
||||||
| Level 2 | 分管领导审批 | 人事处审批 | ✅ 已修正 |
|
|
||||||
| Level 3 | 校领导审批 | 纪委审批 | ✅ 已修正 |
|
|
||||||
| Level 4 | **未知** ❌ | 学院分管领导审批 | ✅ 已修复 |
|
|
||||||
| Level 5 | **未知** ❌ | 学院党委书记审批 | ✅ 已修复 |
|
|
||||||
|
|
||||||
### 2. 审批进度显示
|
|
||||||
|
|
||||||
| 已审批级别 | 修复前 | 修复后 | 进度百分比(修复前) | 进度百分比(修复后) |
|
|
||||||
|-----------|--------|--------|-------------------|-------------------|
|
|
||||||
| 0级 | 0/3 | 0/5 | 0% | 0% |
|
|
||||||
| 1级 | 1/3 | 1/5 | 33% ❌ | 20% ✅ |
|
|
||||||
| 2级 | 2/3 | 2/5 | 67% ❌ | 40% ✅ |
|
|
||||||
| 3级 | 3/3 | 3/5 | 100% ❌ | 60% ✅ |
|
|
||||||
| 4级 | - | 4/5 | - | 80% ✅ |
|
|
||||||
| 5级 | - | 5/5 | - | 100% ✅ |
|
|
||||||
|
|
||||||
### 3. 统计信息
|
|
||||||
|
|
||||||
| 统计项 | 修复前 | 修复后 |
|
|
||||||
|--------|--------|--------|
|
|
||||||
| 统计级别数 | 3个 | 5个 ✅ |
|
|
||||||
| Level 4统计 | ❌ 缺失 | ✅ 包含 |
|
|
||||||
| Level 5统计 | ❌ 缺失 | ✅ 包含 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 前端显示效果
|
|
||||||
|
|
||||||
### 待审批列表示例
|
|
||||||
|
|
||||||
#### 修复前
|
|
||||||
| 申请人 | 当前审批环节 | 审批进度 |
|
|
||||||
|--------|-------------|---------|
|
|
||||||
| 张三 | 部门主管审批 | 0/3 (0%) |
|
|
||||||
| 李四 | 分管领导审批 | 1/3 (33%) |
|
|
||||||
| 王五 | 校领导审批 | 2/3 (67%) |
|
|
||||||
| 赵六 | **未知** ❌ | 3/3 (100%) ❌ |
|
|
||||||
| 钱七 | **未知** ❌ | - |
|
|
||||||
|
|
||||||
#### 修复后
|
|
||||||
| 申请人 | 当前审批环节 | 审批进度 |
|
|
||||||
|--------|-------------|---------|
|
|
||||||
| 张三 | 部门负责人审批 | 0/5 (0%) |
|
|
||||||
| 李四 | 人事处审批 | 1/5 (20%) |
|
|
||||||
| 王五 | 纪委审批 | 2/5 (40%) |
|
|
||||||
| 赵六 | 学院分管领导审批 ✅ | 3/5 (60%) ✅ |
|
|
||||||
| 钱七 | 学院党委书记审批 ✅ | 4/5 (80%) ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 修复验证
|
|
||||||
|
|
||||||
### 验证步骤
|
|
||||||
|
|
||||||
1. **重新编译后端**
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **重启后端服务**
|
|
||||||
```bash
|
|
||||||
./stop-main.sh
|
|
||||||
./start-main.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **登录系统测试**
|
|
||||||
- 访问"出国申请进度查询"菜单
|
|
||||||
- 查看待审批中和审批完成选项卡
|
|
||||||
- 验证以下内容:
|
|
||||||
|
|
||||||
#### 验证项1:当前审批环节显示
|
|
||||||
- [ ] Level 1 显示为"部门负责人审批"
|
|
||||||
- [ ] Level 2 显示为"人事处审批"
|
|
||||||
- [ ] Level 3 显示为"纪委审批"
|
|
||||||
- [ ] Level 4 显示为"学院分管领导审批"(不再是"未知")
|
|
||||||
- [ ] Level 5 显示为"学院党委书记审批"(不再是"未知")
|
|
||||||
|
|
||||||
#### 验证项2:审批进度显示
|
|
||||||
- [ ] 未审批显示:0/5 (0%)
|
|
||||||
- [ ] 部门负责人已审批:1/5 (20%)
|
|
||||||
- [ ] 人事处已审批:2/5 (40%)
|
|
||||||
- [ ] 纪委已审批:3/5 (60%)
|
|
||||||
- [ ] 学院分管领导已审批:4/5 (80%)
|
|
||||||
- [ ] 学院党委书记已审批:5/5 (100%)
|
|
||||||
|
|
||||||
#### 验证项3:统计信息
|
|
||||||
- [ ] 统计信息包含所有5个级别
|
|
||||||
- [ ] Level 4 和 Level 5 的统计数据正常显示
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 修改文件清单
|
|
||||||
|
|
||||||
| 序号 | 文件 | 修改内容 | 行数 | 状态 |
|
|
||||||
|------|------|---------|------|------|
|
|
||||||
| 1 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新审批级别文本定义 | 387-404 | ✅ |
|
|
||||||
| 2 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新总审批级别数(已审批) | 345-346 | ✅ |
|
|
||||||
| 3 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新总审批级别数(未审批) | 358-360 | ✅ |
|
|
||||||
| 4 | `RsOverseasApprovalMonitorServiceImpl.java` | 更新统计信息定义 | 152-157 | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 修复总结
|
|
||||||
|
|
||||||
### 修复前的问题
|
|
||||||
- ❌ 只支持3级审批,Level 4和5显示"未知"
|
|
||||||
- ❌ 总审批级别数错误(3而不是5)
|
|
||||||
- ❌ 审批进度百分比计算错误
|
|
||||||
- ❌ 统计信息不完整
|
|
||||||
|
|
||||||
### 修复后的改进
|
|
||||||
- ✅ 完整支持5级审批流程
|
|
||||||
- ✅ 所有审批级别正确显示
|
|
||||||
- ✅ 审批进度计算准确
|
|
||||||
- ✅ 统计信息完整
|
|
||||||
- ✅ 与实际业务流程一致
|
|
||||||
|
|
||||||
### 影响范围
|
|
||||||
- ✅ 仅修改出国申请进度查询模块
|
|
||||||
- ✅ 不影响其他模块
|
|
||||||
- ✅ 向后兼容
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 相关文档
|
|
||||||
|
|
||||||
- 详细分析报告:`审批环节命名标准分析报告.md`
|
|
||||||
- 功能修改说明:`出国申请进度查询功能修改说明.md`
|
|
||||||
- 权限修改说明:`权限修改说明.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**修复状态**:✅ 已完成
|
|
||||||
**需要操作**:重新编译和重启后端服务
|
|
||||||
**版本**:v1.2
|
|
||||||
**最后更新**:2025-10-29
|
|
||||||
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
# 出国申请进度查询权限修改说明
|
|
||||||
|
|
||||||
## 📋 修改时间
|
|
||||||
2025-10-29
|
|
||||||
|
|
||||||
## 🎯 修改目的
|
|
||||||
将"出国申请进度查询"从"只能查看自己提交的申请"改为"拥有权限的用户可以查看所有申请"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 修改内容
|
|
||||||
|
|
||||||
### 1. 后端Service层修改
|
|
||||||
|
|
||||||
**文件**:`RsOverseasApprovalMonitorServiceImpl.java`
|
|
||||||
|
|
||||||
#### 修改前
|
|
||||||
```java
|
|
||||||
// 只查询当前用户提交的申请
|
|
||||||
Long currentUserId = LoginHelper.getUserId();
|
|
||||||
wrapper.eq("applicant_id", currentUserId);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 修改后
|
|
||||||
```java
|
|
||||||
// 不再限制申请人,拥有权限的用户可以看到所有数据
|
|
||||||
// 已删除 applicant_id 过滤条件
|
|
||||||
```
|
|
||||||
|
|
||||||
**影响方法**:
|
|
||||||
- ✅ `queryPendingList()` - 待审批列表
|
|
||||||
- ✅ `queryCompletedList()` - 已完成列表
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 后端Controller层修改
|
|
||||||
|
|
||||||
**文件**:`RsOverseasApprovalMonitorController.java`
|
|
||||||
|
|
||||||
#### 修改点
|
|
||||||
- 更新注释说明:从"当前用户提交的"改为"所有"
|
|
||||||
- 更新日志输出:从"当前用户的"改为"所有"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 前端页面修改
|
|
||||||
|
|
||||||
**文件**:`monitor/index.vue`
|
|
||||||
|
|
||||||
#### 修改点
|
|
||||||
- 提示文字:从"这里显示您提交的"改为"这里显示所有"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔍 权限控制
|
|
||||||
|
|
||||||
### 访问控制
|
|
||||||
- **权限标识**:`rs:overseas:list`
|
|
||||||
- **控制级别**:拥有此权限的用户可以查看所有数据
|
|
||||||
- **数据范围**:不再限制申请人,显示所有待审批和已完成的申请
|
|
||||||
|
|
||||||
### 查询逻辑
|
|
||||||
```sql
|
|
||||||
-- 待审批列表
|
|
||||||
SELECT * FROM rs_overseas_application
|
|
||||||
WHERE status = '0' -- 待审核状态
|
|
||||||
AND deleted = '0'
|
|
||||||
AND tenant_id = {租户ID}
|
|
||||||
ORDER BY apply_time DESC
|
|
||||||
|
|
||||||
-- 已完成列表
|
|
||||||
SELECT * FROM rs_overseas_application
|
|
||||||
WHERE status = '1' -- 已通过状态
|
|
||||||
AND deleted = '0'
|
|
||||||
AND tenant_id = {租户ID}
|
|
||||||
ORDER BY apply_time DESC
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 修改对比
|
|
||||||
|
|
||||||
| 项目 | 修改前 | 修改后 |
|
|
||||||
|------|--------|--------|
|
|
||||||
| 数据范围 | 当前用户提交的申请 | 所有申请 |
|
|
||||||
| 权限要求 | rs:overseas:list | rs:overseas:list(不变) |
|
|
||||||
| 查询条件 | applicant_id = 当前用户ID | 无申请人限制 |
|
|
||||||
| 用户体验 | 只能看到自己的 | 可以看到所有 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 测试场景1:查看待审批列表
|
|
||||||
|
|
||||||
**测试步骤**:
|
|
||||||
1. 用户A登录系统(拥有 rs:overseas:list 权限)
|
|
||||||
2. 访问"出国申请进度查询"菜单
|
|
||||||
3. 切换到"待审批中"选项卡
|
|
||||||
|
|
||||||
**预期结果**:
|
|
||||||
- ✅ 能看到所有用户提交的待审批申请
|
|
||||||
- ✅ 包括用户A自己提交的和其他用户提交的
|
|
||||||
|
|
||||||
### 测试场景2:查看已完成列表
|
|
||||||
|
|
||||||
**测试步骤**:
|
|
||||||
1. 用户A登录系统(拥有 rs:overseas:list 权限)
|
|
||||||
2. 访问"出国申请进度查询"菜单
|
|
||||||
3. 切换到"审批完成"选项卡
|
|
||||||
|
|
||||||
**预期结果**:
|
|
||||||
- ✅ 能看到所有已完成审批的申请
|
|
||||||
- ✅ 不限制申请人
|
|
||||||
|
|
||||||
### 测试场景3:无权限用户
|
|
||||||
|
|
||||||
**测试步骤**:
|
|
||||||
1. 用户B登录系统(没有 rs:overseas:list 权限)
|
|
||||||
2. 尝试访问"出国申请进度查询"菜单
|
|
||||||
|
|
||||||
**预期结果**:
|
|
||||||
- ❌ 无法访问该菜单(权限控制在菜单级别)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 修改文件清单
|
|
||||||
|
|
||||||
| 序号 | 文件类型 | 文件路径 | 修改内容 | 状态 |
|
|
||||||
|------|---------|---------|---------|------|
|
|
||||||
| 1 | 后端Service | `RsOverseasApprovalMonitorServiceImpl.java` | 删除申请人ID过滤 | ✅ |
|
|
||||||
| 2 | 后端Controller | `RsOverseasApprovalMonitorController.java` | 更新注释和日志 | ✅ |
|
|
||||||
| 3 | 前端页面 | `monitor/index.vue` | 更新提示文字 | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚠️ 注意事项
|
|
||||||
|
|
||||||
### 1. 权限配置
|
|
||||||
- 确保只有需要查看所有数据的用户才有 `rs:overseas:list` 权限
|
|
||||||
- 如果需要区分不同权限级别,建议创建新的权限标识
|
|
||||||
|
|
||||||
### 2. 数据安全
|
|
||||||
- 所有查询仍然受租户ID限制
|
|
||||||
- 不会跨租户显示数据
|
|
||||||
|
|
||||||
### 3. 性能考虑
|
|
||||||
- 查询所有数据时,建议使用搜索条件过滤
|
|
||||||
- 分页显示可以减轻服务器压力
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 部署说明
|
|
||||||
|
|
||||||
### 后端部署
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
mvn clean package -pl ruoyi-admin -am -DskipTests
|
|
||||||
./stop-main.sh
|
|
||||||
./start-main.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### 前端(如已运行无需重启)
|
|
||||||
```bash
|
|
||||||
# 前端代码修改后会自动热更新
|
|
||||||
# 如需打包部署:
|
|
||||||
cd web
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 相关文档
|
|
||||||
|
|
||||||
- 详细修改说明:`出国申请进度查询功能修改说明.md`
|
|
||||||
- 快速参考:`修改总结-快速参考.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**修改状态**:✅ 已完成
|
|
||||||
**生效方式**:需要重启后端服务
|
|
||||||
**版本**:v1.1
|
|
||||||
|
|
||||||
Reference in new issue
Block a user