From e772cd14356f41f3ac66e39d9a9f4d828bb81ff5 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Fri, 9 Jan 2026 18:04:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CwApprovalRecordController.java | 77 +- .../rs/controller/RsStaffInfoController.java | 265 ++++ .../org/dromara/rs/domain/RsStaffInfo.java | 93 ++ .../dromara/rs/domain/bo/RsStaffInfoBo.java | 92 ++ .../rs/domain/vo/RsStaffInfoImportVo.java | 76 ++ .../dromara/rs/domain/vo/RsStaffInfoVo.java | 110 ++ .../listener/RsStaffInfoImportListener.java | 214 ++++ .../dromara/rs/mapper/RsStaffInfoMapper.java | 35 + .../rs/service/IRsStaffInfoService.java | 60 + .../service/impl/RsStaffInfoServiceImpl.java | 285 +++++ .../resources/mapper/rs/RsStaffInfoMapper.xml | 70 ++ .../org/dromara/system/domain/SysUser.java | 5 + .../dromara/system/domain/bo/SysUserBo.java | 6 + .../controller/XgConductScoreController.java | 215 +++- .../xg/domain/bo/XgConductScoreBo.java | 3 + .../xg/domain/vo/XgConductScoreExportVo.java | 153 +++ .../xg/domain/vo/XgConductScoreImportVo.java | 52 + ...XgConductScoreSemesterAverageExportVo.java | 142 +++ .../XgConductScoreImportListener.java | 200 +++ .../xg/mapper/IXgConductScoreMapper.java | 16 + .../xg/service/IXgConductScoreService.java | 11 + .../impl/XgConductScoreServiceImpl.java | 6 + .../conductScore/XgConductScoreMapper.xml | 24 + web/src/api/cw/budgetExecutionImport.ts | 3 - web/src/api/cw/monitoringManagement.ts | 21 +- web/src/api/cw/performanceEvaluation.ts | 643 +++++----- web/src/api/cw/projectAuditor.ts | 2 +- web/src/api/rs/overseas.ts | 22 +- web/src/api/rs/overseas/monitor.js | 73 +- web/src/api/rs/staffInfo/index.ts | 131 ++ web/src/api/rs/staffInfo/types.ts | 92 ++ web/src/api/rs/training.ts | 58 +- web/src/api/stgl/activity/audit.ts | 1 - web/src/api/stgl/auditor/index.ts | 50 +- web/src/api/stgl/auditor/types.ts | 15 +- web/src/api/stgl/club/advisor-history.ts | 10 +- web/src/api/stgl/club/annualReview/index.ts | 11 +- web/src/api/stgl/club/annualReview/type.ts | 2 +- .../api/stgl/club/annualReviewAudit/index.ts | 10 +- .../api/stgl/club/annualReviewAudit/type.ts | 2 +- web/src/api/stgl/clubPermission.ts | 38 +- web/src/api/xg/conductScore.ts | 30 +- web/src/api/xg/ejxyfgld/index.ts | 1 - web/src/api/xg/studentDynamics.ts | 1 - web/src/api/xg/studentStatistics.ts | 66 +- .../xgss/holidayStay/holidayStay/index-sg.ts | 8 +- web/src/api/xg/xsjcxx/xssssq/types.ts | 4 +- web/src/api/xljk/consultation/index.ts | 44 +- web/src/api/xljk/consultation/types.ts | 13 +- web/src/views/rs/staff-info/README.md | 178 +++ web/src/views/rs/staff-info/index.vue | 1090 +++++++++++++++++ .../xg/xsjcxx/ConductScoreInput/index.vue | 215 +++- .../xg/xsjcxx/ConductScoreQuery/index.vue | 37 +- .../xg/xsjcxx/SemesterStatistics/index.vue | 33 +- .../xg/xsjcxx/studentMsg/studentMsg/index.vue | 4 +- 55 files changed, 4466 insertions(+), 652 deletions(-) create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsStaffInfoController.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsStaffInfo.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsStaffInfoBo.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoImportVo.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoVo.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/listener/RsStaffInfoImportListener.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsStaffInfoMapper.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsStaffInfoService.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsStaffInfoServiceImpl.java create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsStaffInfoMapper.xml create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreExportVo.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreImportVo.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreSemesterAverageExportVo.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/listener/XgConductScoreImportListener.java create mode 100644 web/src/api/rs/staffInfo/index.ts create mode 100644 web/src/api/rs/staffInfo/types.ts create mode 100644 web/src/views/rs/staff-info/README.md create mode 100644 web/src/views/rs/staff-info/index.vue diff --git a/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwApprovalRecordController.java b/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwApprovalRecordController.java index 69d2de56..484eb25f 100644 --- a/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwApprovalRecordController.java +++ b/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwApprovalRecordController.java @@ -44,7 +44,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询审批记录列表 */ - //@SaCheckPermission("cw:approval:list") + // @SaCheckPermission("cw:approval:list") @GetMapping("/list") public TableDataInfo list(CwApprovalRecordBo bo, PageQuery pageQuery) { return approvalRecordService.queryPageList(bo, pageQuery); @@ -53,7 +53,7 @@ public class CwApprovalRecordController extends BaseController { /** * 导出审批记录列表 */ - //@SaCheckPermission("cw:approval:export") + // @SaCheckPermission("cw:approval:export") @Log(title = "审批记录", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(CwApprovalRecordBo bo, HttpServletResponse response) { @@ -64,17 +64,16 @@ public class CwApprovalRecordController extends BaseController { /** * 获取审批记录详细信息 */ - //@SaCheckPermission("cw:approval:query") + // @SaCheckPermission("cw:approval:query") @GetMapping("/{id}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable Long id) { + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { return R.ok(approvalRecordService.queryById(id)); } /** * 根据申报ID查询审批记录列表 */ - //@SaCheckPermission("cw:approval:query") + // @SaCheckPermission("cw:approval:query") @GetMapping("/byApplication/{applicationId}") public R> getByApplicationId(@PathVariable Long applicationId) { List list = approvalRecordService.queryByApplicationId(applicationId); @@ -84,7 +83,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询当前用户待审批记录列表 */ - //@SaCheckPermission("cw:approval:list") + // @SaCheckPermission("cw:approval:list") @GetMapping("/pending") public R> getPendingList() { List list = approvalRecordService.queryPendingByCurrentUser(); @@ -94,7 +93,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询当前用户已审批记录列表 */ - //@SaCheckPermission("cw:approval:list") + // @SaCheckPermission("cw:approval:list") @GetMapping("/my") public R> getMyApprovedList() { List list = approvalRecordService.queryApprovedByCurrentUser(); @@ -104,7 +103,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询部门待审批申报列表 */ - //@SaCheckPermission("cw:approval:dept") + // @SaCheckPermission("cw:approval:dept") @GetMapping("/dept/pending") public R> getDeptPendingList() { List list = approvalRecordService.queryDeptPendingApprovals(); @@ -114,7 +113,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询财务待审批申报列表 */ - //@SaCheckPermission("cw:approval:finance") + // @SaCheckPermission("cw:approval:finance") @GetMapping("/finance/pending") public R> getFinancePendingList() { List list = approvalRecordService.queryFinancePendingApprovals(); @@ -124,7 +123,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询财务已审批申报列表 */ - //@SaCheckPermission("cw:approval:finance") + // @SaCheckPermission("cw:approval:finance") @GetMapping("/finance/approved") public R> getFinanceApprovedList() { List list = approvalRecordService.queryFinanceApprovedRecords(); @@ -134,9 +133,8 @@ public class CwApprovalRecordController extends BaseController { /** * 查询学院领导待审批申报列表 */ - //@SaCheckPermission("cw:approval:leader") - @GetMapping("/leader/pending") -backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwApprovalRecordController.java + // @SaCheckPermission("cw:approval:leader") + @GetMapping("/leader/pending") public R> getLeaderPendingList() { List list = approvalRecordService.queryLeaderPendingApprovals(); return R.ok(list); @@ -145,7 +143,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询学院领导已审批申报列表 */ - //@SaCheckPermission("cw:approval:leader") + // @SaCheckPermission("cw:approval:leader") @GetMapping("/leader/approved") public R> getLeaderApprovedList() { List list = approvalRecordService.queryLeaderApprovedApprovals(); @@ -155,7 +153,7 @@ public class CwApprovalRecordController extends BaseController { /** * 查询所有待审批申报列表(超级管理员专用) */ - //@SaCheckPermission("cw:approval:list") + // @SaCheckPermission("cw:approval:list") @GetMapping("/all/pending") public R> getAllPendingList() { List list = approvalRecordService.queryAllPendingApprovals(); @@ -165,7 +163,7 @@ public class CwApprovalRecordController extends BaseController { /** * 新增审批记录 */ - //@SaCheckPermission("cw:approval:add") + // @SaCheckPermission("cw:approval:add") @Log(title = "审批记录", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() @@ -176,7 +174,7 @@ public class CwApprovalRecordController extends BaseController { /** * 修改审批记录 */ - //@SaCheckPermission("cw:approval:edit") + // @SaCheckPermission("cw:approval:edit") @Log(title = "审批记录", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() @@ -187,18 +185,17 @@ public class CwApprovalRecordController extends BaseController { /** * 删除审批记录 */ - //@SaCheckPermission("cw:approval:remove") + // @SaCheckPermission("cw:approval:remove") @Log(title = "审批记录", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ids) { + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { return toAjax(approvalRecordService.deleteWithValidByIds(List.of(ids), true)); } /** * 创建审批流程 */ - //@SaCheckPermission("cw:approval:create") + // @SaCheckPermission("cw:approval:create") @Log(title = "创建审批流程", businessType = BusinessType.INSERT) @PostMapping("/create/{applicationId}") public R createProcess(@PathVariable Long applicationId, @RequestParam String applicationNo) { @@ -216,34 +213,34 @@ public class CwApprovalRecordController extends BaseController { /** * 审批操作 */ - //@SaCheckPermission("cw:approval:process") + // @SaCheckPermission("cw:approval:process") @Log(title = "审批操作", businessType = BusinessType.UPDATE) @RepeatSubmit() @PostMapping("/process") public R processApproval(@RequestParam Long applicationId, - @RequestParam String nodeCode, - @RequestParam String approveAction, - @RequestParam(required = false) String approveOpinion) { + @RequestParam String nodeCode, + @RequestParam String approveAction, + @RequestParam(required = false) String approveOpinion) { return toAjax(approvalRecordService.processApproval(applicationId, nodeCode, approveAction, approveOpinion)); } /** * 部门审批 */ - //@SaCheckPermission("cw:approval:dept") + // @SaCheckPermission("cw:approval:dept") @Log(title = "部门审批", businessType = BusinessType.UPDATE) @RepeatSubmit() @PostMapping("/dept") public R deptApprove(@RequestParam Long applicationId, - @RequestParam String approveAction, - @RequestParam(required = false) String approveOpinion) { + @RequestParam String approveAction, + @RequestParam(required = false) String approveOpinion) { return toAjax(approvalRecordService.deptApprove(applicationId, approveAction, approveOpinion)); } /** * 查询当前审批节点 */ - //@SaCheckPermission("cw:approval:query") + // @SaCheckPermission("cw:approval:query") @GetMapping("/current/{applicationId}") public R getCurrentNode(@PathVariable Long applicationId) { CwApprovalRecordVo record = approvalRecordService.queryCurrentApprovalNode(applicationId); @@ -253,7 +250,7 @@ public class CwApprovalRecordController extends BaseController { /** * 检查审批权限 */ - //@SaCheckPermission("cw:approval:check") + // @SaCheckPermission("cw:approval:check") @GetMapping("/check/{applicationId}") public R checkPermission(@PathVariable Long applicationId, @RequestParam String nodeCode) { Boolean hasPermission = approvalRecordService.checkApprovalPermission(applicationId, nodeCode); @@ -263,7 +260,7 @@ public class CwApprovalRecordController extends BaseController { /** * 检查部门审批权限 */ - //@SaCheckPermission("cw:approval:dept") + // @SaCheckPermission("cw:approval:dept") @GetMapping("/check/dept/{applicationId}") public R checkDeptPermission(@PathVariable Long applicationId) { Boolean hasPermission = approvalRecordService.checkDeptApprovalPermission(applicationId); @@ -273,7 +270,7 @@ public class CwApprovalRecordController extends BaseController { /** * 获取可选审批人列表(院领导) */ - //@SaCheckPermission("cw:approval:finance") + // @SaCheckPermission("cw:approval:finance") @GetMapping("/approvers/{applicationId}") public R>> getAvailableApprovers(@PathVariable Long applicationId) { List> approvers = approvalRecordService.getAvailableApprovers(applicationId); @@ -283,11 +280,11 @@ public class CwApprovalRecordController extends BaseController { /** * 分管领导审批 */ - //@SaCheckPermission("cw:approval:leader") + // @SaCheckPermission("cw:approval:leader") @PostMapping("/leader") public R leaderApproval(@RequestParam Long applicationId, - @RequestParam String approveAction, - @RequestParam String approveOpinion) { + @RequestParam String approveAction, + @RequestParam String approveOpinion) { log.info("🔍 分管领导审批:applicationId={}, approveAction={}, approveOpinion={}", applicationId, approveAction, approveOpinion); @@ -308,12 +305,12 @@ public class CwApprovalRecordController extends BaseController { /** * 财务审批 */ - //@SaCheckPermission("cw:approval:finance") + // @SaCheckPermission("cw:approval:finance") @PostMapping("/finance") public R financeApproval(@RequestParam Long applicationId, - @RequestParam String approveAction, - @RequestParam String approveOpinion, - @RequestParam(required = false) Long selectedApproverId) { + @RequestParam String approveAction, + @RequestParam String approveOpinion, + @RequestParam(required = false) Long selectedApproverId) { log.info("🔍 财务审批:applicationId={}, approveAction={}, approveOpinion={}, selectedApproverId={}", applicationId, approveAction, approveOpinion, selectedApproverId); diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsStaffInfoController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsStaffInfoController.java new file mode 100644 index 00000000..84f8b0be --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsStaffInfoController.java @@ -0,0 +1,265 @@ +package org.dromara.rs.controller; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.http.MediaType; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.core.ExcelResult; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.rs.domain.vo.RsStaffInfoVo; +import org.dromara.rs.domain.bo.RsStaffInfoBo; +import org.dromara.rs.domain.vo.RsStaffInfoImportVo; +import org.dromara.rs.listener.RsStaffInfoImportListener; +import org.dromara.rs.service.IRsStaffInfoService; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.service.ISysDeptService; +import org.dromara.system.domain.vo.SysDeptVo; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +/** + * 人员信息 + * + * @author zlwu + * @date 2025-01-09 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/staff/staffInfo") +@Slf4j +public class RsStaffInfoController extends BaseController { + + private final IRsStaffInfoService staffInfoService; + private final ISysDeptService deptService; + + /** + * 查询人员信息列表 + */ + // @SaCheckPermission("rs:staffInfo:list") + @GetMapping("/list") + public TableDataInfo list(RsStaffInfoBo bo, PageQuery pageQuery) { + return staffInfoService.queryPageList(bo, pageQuery); + } + + /** + * 根据部门ID查询人员信息列表 + */ + // @SaCheckPermission("rs:staffInfo:list") + @PostMapping("/listByDeptId") + public TableDataInfo listByDeptId(@RequestBody Map requestBody, + PageQuery pageQuery) { + // 从请求体中解析查询条件和部门ID + RsStaffInfoBo bo = new RsStaffInfoBo(); + + // 设置基础查询条件 + if (requestBody.containsKey("name")) { + bo.setName((String) requestBody.get("name")); + } + if (requestBody.containsKey("staffNo")) { + bo.setStaffNo((String) requestBody.get("staffNo")); + } + if (requestBody.containsKey("phone")) { + bo.setPhone((String) requestBody.get("phone")); + } + if (requestBody.containsKey("staffType")) { + bo.setStaffType((String) requestBody.get("staffType")); + } + if (requestBody.containsKey("status")) { + bo.setStatus((String) requestBody.get("status")); + } + + // 设置时间范围查询条件 + Map params = new HashMap<>(); + if (requestBody.containsKey("beginTime")) { + params.put("beginTime", requestBody.get("beginTime")); + } + if (requestBody.containsKey("endTime")) { + params.put("endTime", requestBody.get("endTime")); + } + bo.setParams(params); + + // 提取部门ID参数 + Object deptIdValue = null; + if (requestBody.containsKey("deptId") && requestBody.get("deptId") != null) { + deptIdValue = requestBody.get("deptId"); + } else if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") != null) { + deptIdValue = requestBody.get("dept_id"); + } + + if (deptIdValue != null) { + bo.setDeptId(Long.valueOf(deptIdValue.toString())); + } + + // 处理排序参数 + if (requestBody.containsKey("orderBy")) { + String orderBy = (String) requestBody.get("orderBy"); + // 解析 orderBy 字符串,格式如 "staffType ASC, createTime DESC" + if (StringUtils.isNotBlank(orderBy)) { + String[] orderParts = orderBy.split(","); + StringBuilder columns = new StringBuilder(); + StringBuilder directions = new StringBuilder(); + + for (String part : orderParts) { + part = part.trim(); + String[] columnAndDirection = part.split("\\s+"); + if (columnAndDirection.length == 2) { + if (columns.length() > 0) { + columns.append(","); + directions.append(","); + } + columns.append(columnAndDirection[0].trim()); + String direction = columnAndDirection[1].trim().toLowerCase(); + directions.append("asc".equals(direction) ? "asc" : "desc"); + } + } + + if (columns.length() > 0) { + pageQuery.setOrderByColumn(columns.toString()); + pageQuery.setIsAsc(directions.toString()); + } + } + } + + return staffInfoService.queryPageListByDeptId(bo, pageQuery); + } + + /** + * 获取人员信息详细信息 + */ + // @SaCheckPermission("rs:staffInfo:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(staffInfoService.queryById(id)); + } + + /** + * 新增人员信息 + */ + // @SaCheckPermission("rs:staffInfo:add") + @Log(title = "人员信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody RsStaffInfoBo bo) { + return toAjax(staffInfoService.insertByBo(bo)); + } + + /** + * 修改人员信息 + */ + // @SaCheckPermission("rs:staffInfo:edit") + @Log(title = "人员信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody RsStaffInfoBo bo) { + return toAjax(staffInfoService.updateByBo(bo)); + } + + /** + * 删除人员信息 + */ + // @SaCheckPermission("rs:staffInfo:remove") + @Log(title = "人员信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(staffInfoService.deleteWithRelatedData(java.util.Arrays.asList(ids))); + } + + /** + * 获取部门树形数据 + */ + // @SaCheckPermission("rs:staffInfo:list") + @GetMapping("/deptTree") + public R> deptTree() { + List depts = deptService.selectDeptList( + new org.dromara.system.domain.bo.SysDeptBo()); + return R.ok(depts); + } + + /** + * 导出人员信息 + */ + // @SaCheckPermission("rs:staffInfo:export") + @Log(title = "人员信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, RsStaffInfoBo query) { + try { + List list = staffInfoService.queryList(query); + ExcelUtil.exportExcel(list, "人员信息", RsStaffInfoVo.class, response); + } catch (Exception e) { + log.error("导出人员信息失败", e); + throw new org.dromara.common.core.exception.ServiceException("导出失败: " + e.getMessage()); + } + } + + /** + * 下载人员信息导入模板 + */ + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + try { + // 创建示例数据 + List templateData = new ArrayList<>(); + RsStaffInfoImportVo example = new RsStaffInfoImportVo(); + + // 设置示例数据 + example.setName("张三"); + example.setGender("0"); // Excel中会显示为"男" + example.setPosition("讲师"); + example.setDeptName("计算机学院"); + example.setPhone("13800138000"); + example.setStaffType("0"); // Excel中会显示为"正式工" + + templateData.add(example); + + // 动态生成Excel模板(自动包含字典下拉选项) + ExcelUtil.exportExcel(templateData, "人员信息导入模板", RsStaffInfoImportVo.class, response); + } catch (Exception e) { + log.error("生成导入模板失败", e); + throw new org.dromara.common.core.exception.ServiceException("生成模板失败: " + e.getMessage()); + } + } + + /** + * 导入人员数据 + */ + // @SaCheckPermission("rs:staffInfo:import") + @Log(title = "人员信息", businessType = BusinessType.IMPORT) + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file, String deptId, boolean updateSupport) throws Exception { + log.info("导入人员信息,部门ID: {}, 更新支持: {}", deptId, updateSupport); + log.info("导入文件名: {}, 文件大小: {} bytes", file.getOriginalFilename(), file.getSize()); + + try { + ExcelResult result = ExcelUtil.importExcel( + file.getInputStream(), + RsStaffInfoImportVo.class, + new RsStaffInfoImportListener(updateSupport, deptId) + ); + + log.info("Excel导入完成,结果: {}", result.getAnalysis()); + return R.ok(result.getAnalysis()); + } catch (Exception e) { + log.error("Excel导入失败", e); + throw e; + } + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsStaffInfo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsStaffInfo.java new file mode 100644 index 00000000..3864c35e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsStaffInfo.java @@ -0,0 +1,93 @@ +package org.dromara.rs.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 人员信息对象 rs_staff_info + * + * @author zlwu + * @date 2025-01-09 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("rs_staff_info") +public class RsStaffInfo extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID(雪花ID) + */ + @TableId(value = "id") + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic(value = "0", delval = "1") + private String delFlag; + + /** + * 工号 + */ + private String staffNo; + + /** + * 姓名 + */ + private String name; + + /** + * 性别:0-男,1-女 + */ + private String gender; + + /** + * 职务 + */ + private String position; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 手机号 + */ + private String phone; + + /** + * 人员类型(0正式工 1临时工 2外聘工) + */ + private String staffType; + + /** + * 关联的用户ID + */ + private Long userId; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsStaffInfoBo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsStaffInfoBo.java new file mode 100644 index 00000000..4fb28936 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsStaffInfoBo.java @@ -0,0 +1,92 @@ +package org.dromara.rs.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.rs.domain.RsStaffInfo; + +/** + * 人员信息业务对象 rs_staff_info + * + * @author zlwu + * @date 2025-01-09 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = RsStaffInfo.class, reverseConvertGenerate = false) +public class RsStaffInfoBo extends BaseEntity { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 工号 + */ + private String staffNo; + + /** + * 姓名 + */ + @NotBlank(message = "姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String name; + + /** + * 性别 + */ + @NotBlank(message = "性别不能为空", groups = { AddGroup.class, EditGroup.class }) + @Pattern(regexp = "^[01]$", message = "性别只能是0(男)或1(女)", groups = { AddGroup.class, EditGroup.class }) + private String gender; + + /** + * 职务 + */ + private String position; + + /** + * 部门ID + */ + @NotNull(message = "部门ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long deptId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空", groups = { AddGroup.class, EditGroup.class }) + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确", groups = { AddGroup.class, EditGroup.class }) + private String phone; + + /** + * 人员类型 + */ + @NotBlank(message = "人员类型不能为空", groups = { AddGroup.class, EditGroup.class }) + @Pattern(regexp = "^[0-2]$", message = "人员类型只能是0(正式工)、1(临时工)或2(外聘工)", groups = { AddGroup.class, EditGroup.class }) + private String staffType; + + /** + * 关联的用户ID + */ + private Long userId; + + /** + * 状态 + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoImportVo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoImportVo.java new file mode 100644 index 00000000..11bdc709 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoImportVo.java @@ -0,0 +1,76 @@ +package org.dromara.rs.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictPreloadConvert; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 人员信息导入VO rs_staff_info + * + * @author zlwu + * @date 2025-01-09 + */ +@Data +@cn.idev.excel.annotation.ExcelIgnoreUnannotated +public class RsStaffInfoImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 工号 + */ + @ExcelProperty("工号") + private String staffNo; + + /** + * 姓名 + */ + @ExcelProperty("姓名") + @NotBlank(message = "姓名不能为空") + private String name; + + /** + * 性别 + */ + @ExcelProperty(value = "性别", converter = ExcelDictPreloadConvert.class) + @org.dromara.common.excel.annotation.ExcelDictFormat(dictType = "sys_user_sex") + @Pattern(regexp = "^[01]$", message = "性别只能选择:0-男,1-女") + private String gender; + + /** + * 职务 + */ + @ExcelProperty("职务") + private String position; + + /** + * 部门名称 + */ + @ExcelProperty("部门名称") + @NotBlank(message = "部门名称不能为空") + private String deptName; + + /** + * 手机号 + */ + @ExcelProperty("手机号") + @NotBlank(message = "手机号不能为空") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String phone; + + /** + * 人员类型 + */ + @ExcelProperty(value = "人员类型", converter = ExcelDictPreloadConvert.class) + @org.dromara.common.excel.annotation.ExcelDictFormat(readConverterExp = "0=正式工,1=临时工,2=外聘工") + @Pattern(regexp = "^[0-2]$", message = "人员类型只能选择:0-正式工,1-临时工,2-外聘工") + private String staffType; + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoVo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoVo.java new file mode 100644 index 00000000..1a75c8cd --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsStaffInfoVo.java @@ -0,0 +1,110 @@ +package org.dromara.rs.domain.vo; + +import org.dromara.rs.domain.RsStaffInfo; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictPreloadConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 人员信息视图对象 rs_staff_info + * + * @author zlwu + * @date 2025-01-09 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = RsStaffInfo.class) +public class RsStaffInfoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 工号 + */ + @ExcelProperty(value = "工号") + private String staffNo; + + /** + * 姓名 + */ + @ExcelProperty(value = "姓名") + private String name; + + /** + * 性别 + */ + @ExcelProperty(value = "性别", converter = ExcelDictPreloadConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String gender; + + /** + * 职务 + */ + @ExcelProperty(value = "职务") + private String position; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号") + private String phone; + + /** + * 人员类型 + */ + @ExcelProperty(value = "人员类型", converter = ExcelDictPreloadConvert.class) + @ExcelDictFormat(readConverterExp = "0=正式工,1=临时工,2=外聘工") + private String staffType; + + /** + * 关联的用户ID + */ + private Long userId; + + /** + * 状态 + */ + @ExcelProperty(value = "状态", converter = ExcelDictPreloadConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + +} diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/listener/RsStaffInfoImportListener.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/listener/RsStaffInfoImportListener.java new file mode 100644 index 00000000..059960b6 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/listener/RsStaffInfoImportListener.java @@ -0,0 +1,214 @@ +package org.dromara.rs.listener; + +import cn.hutool.core.bean.BeanUtil; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import cn.idev.excel.util.ListUtils; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.excel.core.ExcelListener; +import org.dromara.common.excel.core.ExcelResult; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.rs.domain.bo.RsStaffInfoBo; +import org.dromara.rs.domain.vo.RsStaffInfoImportVo; +import org.dromara.rs.service.IRsStaffInfoService; +import org.dromara.system.service.ISysDeptService; +import org.dromara.system.service.ISysUserService; +import org.dromara.system.domain.vo.SysDeptVo; +import org.dromara.common.core.utils.StringUtils; +import cn.hutool.http.HtmlUtil; + +import java.util.List; + +/** + * 人员信息自定义导入 + * + * @author zlwu + * @date 2025-01-09 + */ +@Slf4j +public class RsStaffInfoImportListener extends AnalysisEventListener + implements ExcelListener { + + private final IRsStaffInfoService staffInfoService; + private final ISysDeptService deptService; + private final ISysUserService userService; + private final Boolean isUpdateSupport; + private final Long operUserId; + private final String deptId; + + private int successNum = 0; + private int failureNum = 0; + private final StringBuilder successMsg = new StringBuilder(); + private final StringBuilder failureMsg = new StringBuilder(); + + public RsStaffInfoImportListener(Boolean isUpdateSupport, String deptId) { + this.staffInfoService = SpringUtils.getBean(IRsStaffInfoService.class); + this.deptService = SpringUtils.getBean(ISysDeptService.class); + this.userService = SpringUtils.getBean(ISysUserService.class); + this.isUpdateSupport = isUpdateSupport; + this.deptId = deptId; + this.operUserId = LoginHelper.getUserId(); + } + + @Override + public void invoke(RsStaffInfoImportVo staffVo, AnalysisContext context) { + try { + // 1. 验证数据完整性 + validateImportData(staffVo); + + // 2. 验证唯一性(工号、手机号) + if (!checkUniqueness(staffVo)) { + return; + } + + // 3. 根据部门名称查找部门ID + Long deptId = findDeptByName(staffVo.getDeptName()); + if (deptId == null) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、部门 ").append(staffVo.getDeptName()).append(" 不存在"); + return; + } + + // 4. 转换并保存数据 + RsStaffInfoBo staffBo = convertToBo(staffVo, deptId); + staffInfoService.insertByBo(staffBo); + + successNum++; + successMsg.append("
").append(successNum).append("、工号 ").append(staffBo.getStaffNo()).append(" 导入成功"); + + } catch (Exception e) { + handleImportError(staffVo, e); + } + } + + /** + * 验证导入数据 + */ + private void validateImportData(RsStaffInfoImportVo staffVo) { + if (staffVo == null) { + throw new ServiceException("导入数据为空"); + } + + // 验证必填字段 + if (StringUtils.isBlank(staffVo.getName())) { + throw new ServiceException("姓名不能为空"); + } + if (StringUtils.isBlank(staffVo.getGender())) { + throw new ServiceException("性别不能为空"); + } + if (StringUtils.isBlank(staffVo.getDeptName())) { + throw new ServiceException("部门名称不能为空"); + } + if (StringUtils.isBlank(staffVo.getPhone())) { + throw new ServiceException("手机号不能为空"); + } + if (StringUtils.isBlank(staffVo.getStaffType())) { + throw new ServiceException("人员类型不能为空"); + } + } + + /** + * 验证唯一性 + */ + private boolean checkUniqueness(RsStaffInfoImportVo staffVo) { + // 验证工号唯一性(如果提供了工号) + if (StringUtils.isNotBlank(staffVo.getStaffNo())) { + // 这里可以添加工号唯一性验证逻辑 + // 暂时跳过,因为工号是自动生成的 + } + + // 验证手机号唯一性 + if (StringUtils.isNotBlank(staffVo.getPhone())) { + var existingUser = userService.selectUserByPhonenumber(staffVo.getPhone()); + if (existingUser != null) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、手机号 ").append(staffVo.getPhone()).append(" 已存在"); + return false; + } + } + + return true; + } + + /** + * 根据部门名称查找部门ID + */ + private Long findDeptByName(String deptName) { + if (StringUtils.isBlank(deptName)) { + return null; + } + + // 查询所有部门 + List allDepts = deptService.selectDeptList(new org.dromara.system.domain.bo.SysDeptBo()); + for (SysDeptVo dept : allDepts) { + if (deptName.equals(dept.getDeptName())) { + return dept.getDeptId(); + } + } + + return null; + } + + /** + * 转换VO到BO + */ + private RsStaffInfoBo convertToBo(RsStaffInfoImportVo staffVo, Long deptId) { + RsStaffInfoBo staffBo = BeanUtil.toBean(staffVo, RsStaffInfoBo.class); + staffBo.setDeptId(deptId); + staffBo.setCreateBy(operUserId); + staffBo.setUpdateBy(operUserId); + return staffBo; + } + + /** + * 处理导入错误 + */ + private void handleImportError(RsStaffInfoImportVo staffVo, Exception e) { + failureNum++; + String msg = "
" + failureNum + "、姓名 " + HtmlUtil.cleanHtmlTag(staffVo.getName()) + " 导入失败:"; + String message = e.getMessage(); + if (e instanceof ConstraintViolationException cvException) { + message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage, + ", "); + } + failureMsg.append(msg).append(message); + log.error(msg, e); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // 处理完成后的逻辑 + } + + @Override + public ExcelResult getExcelResult() { + return new ExcelResult<>() { + @Override + public String getAnalysis() { + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsStaffInfoMapper.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsStaffInfoMapper.java new file mode 100644 index 00000000..c0a1ee58 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsStaffInfoMapper.java @@ -0,0 +1,35 @@ +package org.dromara.rs.mapper; + +import org.dromara.rs.domain.RsStaffInfo; +import org.dromara.rs.domain.vo.RsStaffInfoVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 人员信息Mapper接口 + * + * @author zlwu + * @date 2025-01-09 + */ +public interface RsStaffInfoMapper extends BaseMapperPlus { + + /** + * 根据人员类型获取该类别最大的工号 + * + * @param staffType 人员类型(0正式工 1临时工 2外聘工) + * @param prefix 工号前缀 + * @return 该类别最大的工号 + */ + String selectMaxStaffNoByType(@Param("staffType") String staffType, @Param("prefix") String prefix); + + /** + * 根据部门ID列表查询人员信息 + * + * @param deptIds 部门ID列表 + * @return 人员信息列表 + */ + List selectListByDeptIds(@Param("deptIds") List deptIds); + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsStaffInfoService.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsStaffInfoService.java new file mode 100644 index 00000000..fe18c3ba --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsStaffInfoService.java @@ -0,0 +1,60 @@ +package org.dromara.rs.service; + +import org.dromara.rs.domain.RsStaffInfo; +import org.dromara.rs.domain.bo.RsStaffInfoBo; +import org.dromara.rs.domain.vo.RsStaffInfoVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 人员信息Service接口 + * + * @author zlwu + * @date 2025-01-09 + */ +public interface IRsStaffInfoService { + + /** + * 查询人员信息 + */ + RsStaffInfoVo queryById(Long id); + + /** + * 查询人员信息列表 + */ + TableDataInfo queryPageList(RsStaffInfoBo bo, PageQuery pageQuery); + + /** + * 根据部门ID查询人员信息列表 + */ + TableDataInfo queryPageListByDeptId(RsStaffInfoBo bo, PageQuery pageQuery); + + /** + * 查询人员信息列表 + */ + List queryList(RsStaffInfoBo bo); + + /** + * 新增人员信息 + */ + Boolean insertByBo(RsStaffInfoBo bo); + + /** + * 修改人员信息 + */ + Boolean updateByBo(RsStaffInfoBo bo); + + /** + * 批量删除人员信息 + */ + Boolean deleteWithRelatedData(Collection ids); + + /** + * 生成工号 + */ + String generateStaffNo(String staffType); + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsStaffInfoServiceImpl.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsStaffInfoServiceImpl.java new file mode 100644 index 00000000..82dfae02 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsStaffInfoServiceImpl.java @@ -0,0 +1,285 @@ +package org.dromara.rs.service.impl; + +import cn.hutool.crypto.digest.BCrypt; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.satoken.utils.LoginHelper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.dromara.rs.domain.bo.RsStaffInfoBo; +import org.dromara.rs.domain.vo.RsStaffInfoVo; +import org.dromara.rs.domain.RsStaffInfo; +import org.dromara.rs.mapper.RsStaffInfoMapper; +import org.dromara.rs.service.IRsStaffInfoService; +import org.dromara.system.service.ISysUserService; +import org.dromara.system.service.ISysDeptService; +import org.dromara.system.domain.bo.SysUserBo; +import org.dromara.system.domain.vo.SysDeptVo; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; + +/** + * 人员信息Service业务层处理 + * + * @author zlwu + * @date 2025-01-09 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class RsStaffInfoServiceImpl implements IRsStaffInfoService { + + private final RsStaffInfoMapper baseMapper; + private final ISysUserService sysUserService; + private final ISysDeptService sysDeptService; + + /** + * 查询人员信息 + * + * @param id 主键 + * @return 人员信息 + */ + @Override + public RsStaffInfoVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 查询人员信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 人员信息分页列表 + */ + @Override + public TableDataInfo queryPageList(RsStaffInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 根据部门ID查询人员信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 人员信息分页列表 + */ + @Override + public TableDataInfo queryPageListByDeptId(RsStaffInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + private LambdaQueryWrapper buildQueryWrapper(RsStaffInfoBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getDeptId() != null, RsStaffInfo::getDeptId, bo.getDeptId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), RsStaffInfo::getName, bo.getName()); + lqw.like(StringUtils.isNotBlank(bo.getStaffNo()), RsStaffInfo::getStaffNo, bo.getStaffNo()); + lqw.like(StringUtils.isNotBlank(bo.getPhone()), RsStaffInfo::getPhone, bo.getPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getStaffType()), RsStaffInfo::getStaffType, bo.getStaffType()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), RsStaffInfo::getStatus, bo.getStatus()); + return lqw; + } + + /** + * 查询人员信息列表 + * + * @param bo 查询条件 + * @return 人员信息列表 + */ + @Override + public List queryList(RsStaffInfoBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 新增人员信息 + * + * @param bo 人员信息 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(RsStaffInfoBo bo) { + // 1. 生成工号 + String staffNo = generateStaffNo(bo.getStaffType()); + bo.setStaffNo(staffNo); + + // 2. 插入人员主表 + RsStaffInfo entity = MapstructUtils.convert(bo, RsStaffInfo.class); + entity.setCreateBy(LoginHelper.getUserId()); + entity.setCreateDept(LoginHelper.getDeptId()); + entity.setStatus("0"); // 默认正常状态 + boolean flag = baseMapper.insert(entity) > 0; + + if (flag) { + try { + // 3. 获取部门名称 + if (bo.getDeptId() != null) { + SysDeptVo dept = sysDeptService.selectDeptById(bo.getDeptId()); + if (dept != null) { + entity.setDeptName(dept.getDeptName()); + baseMapper.updateById(entity); + } + } + + // 4. 创建sys_user记录 + SysUserBo sysUserBo = new SysUserBo(); + sysUserBo.setUserId(entity.getId()); + sysUserBo.setUserName(bo.getPhone()); + sysUserBo.setNickName(bo.getName()); + sysUserBo.setUserType("sys_user"); + sysUserBo.setEmail(""); + sysUserBo.setPhonenumber(bo.getPhone()); + sysUserBo.setSex(bo.getGender()); + sysUserBo.setDeptId(bo.getDeptId()); + sysUserBo.setPassword(BCrypt.hashpw("Hzdyjs123456!@#", BCrypt.gensalt())); + sysUserBo.setStatus("0"); + sysUserBo.setAccountType("1"); // 账号类型:教职工 + sysUserBo.setUserNo(bo.getStaffNo()); + + int userResult = sysUserService.insertUser(sysUserBo); + if (userResult > 0) { + // 5. 绑定默认角色(4和1938120323613728770) + Long[] roleIds = { 4L, 1938120323613728770L }; + sysUserService.insertUserAuth(entity.getId(), roleIds); + + // 6. 更新人员表的user_id关联 + entity.setUserId(entity.getId()); + baseMapper.updateById(entity); + } + } catch (Exception e) { + log.error("创建人员系统用户失败: {}", e.getMessage(), e); + throw new RuntimeException("创建人员系统用户失败: " + e.getMessage(), e); + } + } + return flag; + } + + /** + * 修改人员信息 + * + * @param bo 人员信息 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(RsStaffInfoBo bo) { + RsStaffInfo update = MapstructUtils.convert(bo, RsStaffInfo.class); + update.setUpdateBy(LoginHelper.getUserId()); + boolean flag = baseMapper.updateById(update) > 0; + + if (flag) { + try { + // 查询现有的sys_user记录 + var existingUser = sysUserService.selectUserById(bo.getId()); + if (existingUser != null) { + // 更新sys_user表信息 + SysUserBo sysUserBo = new SysUserBo(); + sysUserBo.setUserId(bo.getId()); + sysUserBo.setUserName(bo.getPhone()); + sysUserBo.setNickName(bo.getName()); + sysUserBo.setPhonenumber(bo.getPhone()); + sysUserBo.setSex(bo.getGender()); + sysUserBo.setDeptId(bo.getDeptId()); + sysUserBo.setUserNo(bo.getStaffNo()); + sysUserBo.setAccountType("1"); + + sysUserService.updateUser(sysUserBo); + log.info("成功更新人员 {} 的系统用户信息", bo.getName()); + } + } catch (Exception e) { + log.error("更新人员系统用户信息失败: {}", e.getMessage(), e); + throw new RuntimeException("更新人员系统用户信息失败: " + e.getMessage(), e); + } + } + return flag; + } + + /** + * 批量删除人员信息 + * + * @param ids 人员ID集合 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithRelatedData(Collection ids) { + // 1. 删除人员主表记录(逻辑删除) + boolean staffDeleteResult = baseMapper.deleteByIds(ids) > 0; + + if (staffDeleteResult) { + try { + // 2. 同步删除sys_user表中的对应数据(逻辑删除) + Long[] userIds = ids.toArray(new Long[0]); + int userDeleteResult = sysUserService.deleteUserByIds(userIds); + + if (userDeleteResult > 0) { + log.info("成功删除 {} 个人员对应的系统用户", userDeleteResult); + } else { + log.warn("删除人员对应的系统用户失败,删除数量: {}", userDeleteResult); + } + } catch (Exception e) { + log.error("删除人员信息时同步删除系统用户失败: {}", e.getMessage(), e); + throw new RuntimeException("删除人员信息时同步删除系统用户失败: " + e.getMessage(), e); + } + } + + return staffDeleteResult; + } + + /** + * 生成工号 + * + * @param staffType 人员类型(0正式工 1临时工 2外聘工) + * @return 新生成的工号 + */ + @Override + public String generateStaffNo(String staffType) { + // 根据人员类型确定前缀 + String prefix = switch (staffType) { + case "0" -> "0"; // 正式工 + case "1" -> "L"; // 临时工 + case "2" -> "W"; // 外聘工 + default -> throw new ServiceException("无效的人员类型: " + staffType); + }; + + // 从rs_staff_info表中获取该类别最大工号 + String maxStaffNo = baseMapper.selectMaxStaffNoByType(staffType, prefix); + + int nextNumber = 1; + if (StringUtils.isNotBlank(maxStaffNo)) { + String numberPart = maxStaffNo.substring(1); + try { + nextNumber = Integer.parseInt(numberPart) + 1; + } catch (NumberFormatException e) { + log.warn("工号格式错误: {}, 使用默认值1", maxStaffNo); + nextNumber = 1; + } + } + + // 生成新的工号,确保是5位数字 + String numberStr = String.format("%05d", nextNumber); + + // 检查是否超过最大值 + if (nextNumber > 99999) { + throw new ServiceException("工号已达到最大值,无法生成新的工号"); + } + + return prefix + numberStr; + } + +} diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsStaffInfoMapper.xml b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsStaffInfoMapper.xml new file mode 100644 index 00000000..5dbffc3c --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsStaffInfoMapper.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java index 9e061c85..23bd5671 100644 --- a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java @@ -37,6 +37,11 @@ public class SysUser extends TenantEntity { */ private String userName; + /** + * 用户编号 + */ + private String userNo; + /** * 用户昵称 */ diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java index 5516a825..7f4bd77e 100644 --- a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java @@ -42,6 +42,12 @@ public class SysUserBo extends BaseEntity { @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") private String userName; + /** + * 用户编号 + */ + @Size(min = 0, max = 30, message = "用户编号长度不能超过{max}个字符") + private String userNo; + /** * 用户昵称 */ diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgConductScoreController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgConductScoreController.java index b55fc0d3..2bd45556 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgConductScoreController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgConductScoreController.java @@ -1,6 +1,7 @@ package org.dromara.xg.controller; import java.util.List; +import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.math.BigDecimal; @@ -9,6 +10,8 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.log.annotation.Log; @@ -17,7 +20,12 @@ import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.core.domain.R; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.core.ExcelResult; +import org.dromara.xg.listener.XgConductScoreImportListener; import org.dromara.xg.domain.vo.XgConductScoreVo; +import org.dromara.xg.domain.vo.XgConductScoreExportVo; +import org.dromara.xg.domain.vo.XgConductScoreImportVo; +import org.dromara.xg.domain.vo.XgConductScoreSemesterAverageExportVo; import org.dromara.xg.domain.bo.XgConductScoreBo; import org.dromara.xg.domain.bo.XgConductScoreQueryBo; import org.dromara.xg.domain.bo.XgConductScoreBatchBo; @@ -282,7 +290,62 @@ public class XgConductScoreController extends BaseController { } } - ExcelUtil.exportExcel(list, "学生操行分", XgConductScoreVo.class, response); + // 如果需要友好格式的学年显示,使用导出专用VO类 + if (requestBody.containsKey("academicYearFormat") && + "friendly".equals(requestBody.get("academicYearFormat").toString())) { + // 创建导出专用的VO列表,将学年转换为友好格式 + java.util.List exportList = new java.util.ArrayList<>(); + for (XgConductScoreVo vo : list) { + XgConductScoreExportVo exportVo = new XgConductScoreExportVo(); + + // 复制所有基础字段 + exportVo.setId(vo.getId()); + exportVo.setTenantId(vo.getTenantId()); + exportVo.setStudentId(vo.getStudentId()); + exportVo.setStudentName(vo.getStudentName()); + exportVo.setStudentNo(vo.getStudentNo()); + exportVo.setClassId(vo.getClassId()); + exportVo.setClassName(vo.getClassName()); + exportVo.setCollegeName(vo.getCollegeName()); + exportVo.setMajorName(vo.getMajorName()); + exportVo.setClassTeacherId(vo.getClassTeacherId()); + exportVo.setClassTeacherName(vo.getClassTeacherName()); + exportVo.setScore(vo.getScore()); + exportVo.setRemark(vo.getRemark()); + exportVo.setEvaluationResult(vo.getEvaluationResult()); + exportVo.setCreateTime(vo.getCreateTime()); + exportVo.setCreateBy(vo.getCreateBy()); + exportVo.setCreateByName(vo.getCreateByName()); + exportVo.setUpdateBy(vo.getUpdateBy()); + exportVo.setUpdateTime(vo.getUpdateTime()); + + // 学年:转换为友好格式,如 2026 -> "2025-2026学年" + String academicYearStr = vo.getAcademicYear() != null + ? (vo.getAcademicYear() - 1) + "-" + vo.getAcademicYear() + "学年" + : ""; + exportVo.setAcademicYear(academicYearStr); + + // 学期:转换为中文,如 1 -> "第一学期" + String semesterStr = vo.getSemester() != null + ? (vo.getSemester() == 1 ? "第一学期" : "第二学期") + : ""; + exportVo.setSemester(semesterStr); + + // 月份:转换为带月字,如 1 -> "1月" + String monthStr = vo.getMonth() != null + ? vo.getMonth() + "月" + : ""; + exportVo.setMonth(monthStr); + + exportList.add(exportVo); + } + + // 使用导出专用VO类进行导出 + ExcelUtil.exportExcel(exportList, "学生操行分", XgConductScoreExportVo.class, response); + } else { + // 正常导出,使用原始VO类 + ExcelUtil.exportExcel(list, "学生操行分", XgConductScoreVo.class, response); + } } /** @@ -518,16 +581,92 @@ public class XgConductScoreController extends BaseController { List list = xgConductScoreService .getSemesterAverageByDeptId(academicYear, semester, bo, deptParams, pageQuery).getRows(); - // 为每条数据计算评判结果 - for (XgConductScoreSemesterAverageVo vo : list) { - if (vo.getAvgScore() != null && vo.getRecordCount() != null && vo.getRecordCount() > 0) { - vo.setEvaluationResult(calculateEvaluationResult(vo.getAvgScore())); - } else { - vo.setEvaluationResult(""); - } - } + // 如果需要友好格式的学年显示,使用导出专用VO类 + if (requestBody.containsKey("academicYearFormat") && + "friendly".equals(requestBody.get("academicYearFormat").toString())) { + // 创建导出专用的VO列表,将学年转换为友好格式 + java.util.List exportList = new java.util.ArrayList<>(); + for (XgConductScoreSemesterAverageVo vo : list) { + XgConductScoreSemesterAverageExportVo exportVo = new XgConductScoreSemesterAverageExportVo(); - ExcelUtil.exportExcel(list, "学期平均分统计", XgConductScoreSemesterAverageVo.class, response); + // 复制所有基础字段 + exportVo.setStudentId(vo.getStudentId()); + exportVo.setStudentNo(vo.getStudentNo()); + exportVo.setStudentName(vo.getStudentName()); + exportVo.setGender(vo.getGender()); + exportVo.setClassId(vo.getClassId()); + exportVo.setCollegeName(vo.getCollegeName()); + exportVo.setMajorName(vo.getMajorName()); + exportVo.setClassName(vo.getClassName()); + exportVo.setAvgScore(vo.getAvgScore()); + exportVo.setRecordCount(vo.getRecordCount()); + exportVo.setMaxScore(vo.getMaxScore()); + exportVo.setMinScore(vo.getMinScore()); + exportVo.setScoreRange(vo.getScoreRange()); + exportVo.setLastUpdateTime(vo.getLastUpdateTime()); + exportVo.setCreateBy(vo.getCreateBy()); + exportVo.setCreateTime(vo.getCreateTime()); + exportVo.setUpdateBy(vo.getUpdateBy()); + exportVo.setUpdateTime(vo.getUpdateTime()); + + // 计算评判结果 + if (vo.getAvgScore() != null && vo.getRecordCount() != null && vo.getRecordCount() > 0) { + exportVo.setEvaluationResult(calculateEvaluationResult(vo.getAvgScore())); + } else { + exportVo.setEvaluationResult(""); + } + + // 学年:转换为友好格式,如 2026 -> "2025-2026学年" + String academicYearStr = vo.getAcademicYear() != null + ? (vo.getAcademicYear() - 1) + "-" + vo.getAcademicYear() + "学年" + : ""; + exportVo.setAcademicYear(academicYearStr); + + // 学期:转换为中文,如 1 -> "第一学期" + String semesterStr = vo.getSemester() != null + ? (vo.getSemester() == 1 ? "第一学期" : "第二学期") + : ""; + exportVo.setSemester(semesterStr); + + exportList.add(exportVo); + } + + // 按学院、专业、班级、学号排序 + exportList.sort((a, b) -> { + int result = java.util.Comparator.nullsLast(String::compareTo) + .compare(a.getCollegeName(), b.getCollegeName()); + if (result != 0) + return result; + + result = java.util.Comparator.nullsLast(String::compareTo) + .compare(a.getMajorName(), b.getMajorName()); + if (result != 0) + return result; + + result = java.util.Comparator.nullsLast(String::compareTo) + .compare(a.getClassName(), b.getClassName()); + if (result != 0) + return result; + + return java.util.Comparator.nullsLast(String::compareTo) + .compare(a.getStudentNo(), b.getStudentNo()); + }); + + // 使用导出专用VO类进行导出 + ExcelUtil.exportExcel(exportList, "学期平均分统计", XgConductScoreSemesterAverageExportVo.class, response); + } else { + // 为每条数据计算评判结果 + for (XgConductScoreSemesterAverageVo vo : list) { + if (vo.getAvgScore() != null && vo.getRecordCount() != null && vo.getRecordCount() > 0) { + vo.setEvaluationResult(calculateEvaluationResult(vo.getAvgScore())); + } else { + vo.setEvaluationResult(""); + } + } + + // 正常导出,使用原始VO类 + ExcelUtil.exportExcel(list, "学期平均分统计", XgConductScoreSemesterAverageVo.class, response); + } } /** @@ -584,4 +723,60 @@ public class XgConductScoreController extends BaseController { return R.ok(xgConductScoreService.checkInputPermission()); } + /** + * 下载操行分导入模板 + */ + // @SaCheckPermission("xg:conductScore:importTemplate") + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + try { + // 创建示例数据 + List templateData = new ArrayList<>(); + XgConductScoreImportVo example = new XgConductScoreImportVo(); + + // 设置示例数据 + example.setStudentNo("2024001"); + example.setStudentName("张三"); + example.setScore(85); + example.setRemark("表现良好"); + + templateData.add(example); + + // 动态生成Excel模板 + ExcelUtil.exportExcel(templateData, "操行分导入模板", XgConductScoreImportVo.class, response); + } catch (Exception e) { + log.error("生成导入模板失败", e); + throw new org.dromara.common.core.exception.ServiceException("生成模板失败: " + e.getMessage()); + } + } + + /** + * 导入操行分数据 + */ + // @SaCheckPermission("xg:conductScore:importData") + @Log(title = "学生操行分", businessType = BusinessType.IMPORT) + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file, + @NotNull(message = "学年不能为空") Integer academicYear, + @NotNull(message = "学期不能为空") Integer semester, + @NotNull(message = "月份不能为空") Integer month, + String classId) throws Exception { + log.info("导入操行分数据,学年: {}, 学期: {}, 月份: {}, 班级ID: {}", academicYear, semester, month, classId); + log.info("导入文件名: {}, 文件大小: {} bytes", file.getOriginalFilename(), file.getSize()); + + try { + ExcelResult result = ExcelUtil.importExcel( + file.getInputStream(), + XgConductScoreImportVo.class, + new XgConductScoreImportListener(academicYear, semester, month, classId) + ); + + log.info("Excel导入完成,结果: {}", result.getAnalysis()); + return R.ok(result.getAnalysis()); + } catch (Exception e) { + log.error("Excel导入失败", e); + throw e; + } + } + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgConductScoreBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgConductScoreBo.java index bad667a8..f2329878 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgConductScoreBo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgConductScoreBo.java @@ -5,6 +5,8 @@ import lombok.EqualsAndHashCode; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.xg.domain.XgConductScore; +import io.github.linpeilie.annotations.AutoMapper; import jakarta.validation.constraints.*; import java.math.BigDecimal; @@ -16,6 +18,7 @@ import java.math.BigDecimal; */ @Data @EqualsAndHashCode(callSuper = true) +@AutoMapper(target = XgConductScore.class, reverseConvertGenerate = false) public class XgConductScoreBo extends BaseEntity { private static final long serialVersionUID = 1L; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreExportVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreExportVo.java new file mode 100644 index 00000000..a4831c72 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreExportVo.java @@ -0,0 +1,153 @@ +package org.dromara.xg.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; +import java.util.Date; +import java.io.Serial; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import org.dromara.xg.domain.XgConductScore; +import io.github.linpeilie.annotations.AutoMapper; + +/** + * 学生操行分导出视图对象 + * + * @author Lion Li + * @date 2025-01-13 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = XgConductScore.class) +public class XgConductScoreExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @cn.idev.excel.annotation.ExcelProperty(value = "记录ID") + private Long id; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 学年(友好格式,如:2025-2026学年) + */ + @cn.idev.excel.annotation.ExcelProperty(value = "学年") + private String academicYear; + + /** + * 学期(中文格式,如:第一学期) + */ + @cn.idev.excel.annotation.ExcelProperty(value = "学期") + private String semester; + + /** + * 月份(带月字,如:1月) + */ + @cn.idev.excel.annotation.ExcelProperty(value = "月份") + private String month; + + /** + * 学生ID + */ + private Long studentId; + + /** + * 学生姓名 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "学号") + private String studentNo; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "班级名称") + private String className; + + /** + * 学院名称 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "学院名称") + private String collegeName; + + /** + * 专业名称 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "专业名称") + private String majorName; + + /** + * 班主任ID + */ + private Long classTeacherId; + + /** + * 班主任姓名 + */ + private String classTeacherName; + + /** + * 分数 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "分数") + private BigDecimal score; + + /** + * 备注 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "备注") + private String remark; + + /** + * 评判结果 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "评判结果") + private String evaluationResult; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @cn.idev.excel.annotation.ExcelProperty(value = "录入时间") + private Date createTime; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 创建者姓名 + */ + @cn.idev.excel.annotation.ExcelProperty(value = "录入人") + private String createByName; + + /** + * 更新者 + */ + private Long updateBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreImportVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreImportVo.java new file mode 100644 index 00000000..31aba675 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreImportVo.java @@ -0,0 +1,52 @@ +package org.dromara.xg.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import jakarta.validation.constraints.*; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 学生操行分导入VO xg_conduct_score + * + * @author Lion Li + * @date 2025-01-13 + */ +@Data +@cn.idev.excel.annotation.ExcelIgnoreUnannotated +public class XgConductScoreImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 学号 + */ + @ExcelProperty("学号") + @NotBlank(message = "学号不能为空") + private String studentNo; + + /** + * 学生姓名 + */ + @ExcelProperty("学生姓名") + @NotBlank(message = "学生姓名不能为空") + private String studentName; + + /** + * 分数 + */ + @ExcelProperty("分数") + @NotNull(message = "分数不能为空") + @Min(value = 0, message = "分数不能小于0") + @Max(value = 100, message = "分数不能大于100") + private Integer score; + + /** + * 备注 + */ + @ExcelProperty("备注") + private String remark; + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreSemesterAverageExportVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreSemesterAverageExportVo.java new file mode 100644 index 00000000..7a7422dd --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgConductScoreSemesterAverageExportVo.java @@ -0,0 +1,142 @@ +package org.dromara.xg.domain.vo; + +import lombok.Data; +import java.math.BigDecimal; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; + +/** + * 操行分学期平均分导出视图对象 + * + * @author Lion Li + * @date 2025-01-13 + */ +@Data +@ExcelIgnoreUnannotated +public class XgConductScoreSemesterAverageExportVo { + + private static final long serialVersionUID = 1L; + + /** + * 学生ID + */ + private Long studentId; + + /** + * 学号 + */ + @ExcelProperty(value = "学号") + private String studentNo; + + /** + * 学生姓名 + */ + @ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 性别 + */ + @ExcelProperty(value = "性别", converter = org.dromara.common.excel.convert.ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=男,1=女") + private String gender; + + /** + * 班级ID + */ + private Long classId; + + /** + * 学院名称 + */ + @ExcelProperty(value = "学院") + private String collegeName; + + /** + * 专业名称 + */ + @ExcelProperty(value = "专业") + private String majorName; + + /** + * 班级名称 + */ + @ExcelProperty(value = "班级") + private String className; + + /** + * 学年(友好格式,如:2025-2026学年) + */ + @ExcelProperty(value = "学年") + private String academicYear; + + /** + * 学期(中文格式,如:第一学期) + */ + @ExcelProperty(value = "学期") + private String semester; + + /** + * 学期平均分 + */ + @ExcelProperty(value = "学期平均分") + private BigDecimal avgScore; + + /** + * 评判结果 + */ + @ExcelProperty(value = "评判结果") + private String evaluationResult; + + /** + * 录入月份数 + */ + @ExcelProperty(value = "录入月份") + private Integer recordCount; + + /** + * 最高分 + */ + @ExcelProperty(value = "最高分") + private BigDecimal maxScore; + + /** + * 最低分 + */ + @ExcelProperty(value = "最低分") + private BigDecimal minScore; + + /** + * 分数范围 + */ + private String scoreRange; + + /** + * 最后更新时间 + */ + private Date lastUpdateTime; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新者 + */ + private Long updateBy; + + /** + * 更新时间 + */ + private Date updateTime; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/listener/XgConductScoreImportListener.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/listener/XgConductScoreImportListener.java new file mode 100644 index 00000000..87fdbd7a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/listener/XgConductScoreImportListener.java @@ -0,0 +1,200 @@ +package org.dromara.xg.listener; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.http.HtmlUtil; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.excel.core.ExcelListener; +import org.dromara.common.excel.core.ExcelResult; +import org.dromara.common.excel.core.ExcelResult; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.xg.domain.bo.XgConductScoreBo; +import org.dromara.xg.domain.bo.XgStudentMsgBo; +import org.dromara.xg.domain.vo.XgConductScoreImportVo; +import org.dromara.xg.domain.vo.XgConductScoreVo; +import org.dromara.xg.domain.vo.XgStudentMsgVo; +import org.dromara.xg.service.IXgConductScoreService; +import org.dromara.xg.service.IXgStudentMsgService; +import org.dromara.common.core.utils.StringUtils; + +import java.util.List; + +/** + * 学生操行分自定义导入 + * + * @author Lion Li + * @date 2025-01-13 + */ +@Slf4j +public class XgConductScoreImportListener extends AnalysisEventListener + implements ExcelListener { + + private final IXgConductScoreService conductScoreService; + private final IXgStudentMsgService studentMsgService; + private final Integer academicYear; + private final Integer semester; + private final Integer month; + private final String classId; // 可选,指定班级ID + private final Long operUserId; + + private int successNum = 0; + private int failureNum = 0; + private final StringBuilder successMsg = new StringBuilder(); + private final StringBuilder failureMsg = new StringBuilder(); + + public XgConductScoreImportListener(Integer academicYear, Integer semester, Integer month, String classId) { + this.conductScoreService = SpringUtils.getBean(IXgConductScoreService.class); + this.studentMsgService = SpringUtils.getBean(IXgStudentMsgService.class); + this.academicYear = academicYear; + this.semester = semester; + this.month = month; + this.classId = classId; + this.operUserId = LoginHelper.getUserId(); + } + + @Override + public void invoke(XgConductScoreImportVo importVo, AnalysisContext context) { + try { + // 1. 验证数据完整性 + validateImportData(importVo); + + // 2. 根据学号查找学生信息 + XgStudentMsgBo queryBo = new XgStudentMsgBo(); + queryBo.setStudentNo(importVo.getStudentNo()); + List students = studentMsgService.queryList(queryBo); + if (students == null || students.isEmpty()) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、学号 ").append(importVo.getStudentNo()).append(" 不存在"); + return; + } + XgStudentMsgVo student = students.get(0); + + // 3. 验证学生姓名匹配 + if (!importVo.getStudentName().equals(student.getName())) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、学号 ").append(importVo.getStudentNo()) + .append(" 姓名不匹配,Excel中:").append(importVo.getStudentName()) + .append(",系统中:").append(student.getName()); + return; + } + + // 4. 验证班级权限(如果指定了班级) + if (StringUtils.isNotBlank(classId) && !classId.equals(student.getClassId())) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、学号 ").append(importVo.getStudentNo()).append(" 不属于指定班级"); + return; + } + + // 5. 检查是否已有记录 + XgConductScoreVo existingRecord = conductScoreService.selectByStudentAndTime( + student.getId(), academicYear, semester, month); + + if (existingRecord != null) { + // 更新现有记录 + XgConductScoreBo updateBo = new XgConductScoreBo(); + updateBo.setId(existingRecord.getId()); + updateBo.setScore(java.math.BigDecimal.valueOf(importVo.getScore())); + updateBo.setRemark(importVo.getRemark()); + updateBo.setUpdateBy(operUserId); + conductScoreService.updateByBo(updateBo); + + successNum++; + successMsg.append("
").append(successNum).append("、学号 ").append(importVo.getStudentNo()) + .append(" 更新成功(覆盖原有分数 ").append(existingRecord.getScore()).append(" → ").append(importVo.getScore()).append(")"); + } else { + // 插入新记录 + XgConductScoreBo insertBo = new XgConductScoreBo(); + insertBo.setAcademicYear(academicYear); + insertBo.setSemester(semester); + insertBo.setMonth(month); + insertBo.setStudentId(student.getId()); + insertBo.setClassId(Long.valueOf(student.getClassId())); + insertBo.setClassTeacherId(student.getClassTeacherId()); + insertBo.setScore(java.math.BigDecimal.valueOf(importVo.getScore())); + insertBo.setRemark(importVo.getRemark()); + insertBo.setCreateBy(operUserId); + insertBo.setUpdateBy(operUserId); + conductScoreService.insertByBo(insertBo); + + successNum++; + successMsg.append("
").append(successNum).append("、学号 ").append(importVo.getStudentNo()).append(" 导入成功"); + } + + } catch (Exception e) { + handleImportError(importVo, e); + } + } + + /** + * 验证导入数据 + */ + private void validateImportData(XgConductScoreImportVo importVo) { + if (importVo == null) { + throw new ServiceException("导入数据为空"); + } + + // 验证必填字段 + if (StringUtils.isBlank(importVo.getStudentNo())) { + throw new ServiceException("学号不能为空"); + } + if (StringUtils.isBlank(importVo.getStudentName())) { + throw new ServiceException("学生姓名不能为空"); + } + if (importVo.getScore() == null) { + throw new ServiceException("分数不能为空"); + } + } + + /** + * 处理导入错误 + */ + private void handleImportError(XgConductScoreImportVo importVo, Exception e) { + failureNum++; + String msg = "
" + failureNum + "、学号 " + HtmlUtil.cleanHtmlTag(importVo.getStudentNo()) + " 导入失败:"; + String message = e.getMessage(); + if (e instanceof ConstraintViolationException cvException) { + message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage, + ", "); + } + failureMsg.append(msg).append(message); + log.error(msg, e); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // 处理完成后的逻辑 + } + + @Override + public ExcelResult getExcelResult() { + return new ExcelResult<>() { + @Override + public String getAnalysis() { + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/IXgConductScoreMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/IXgConductScoreMapper.java index 8519afe0..17fef104 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/IXgConductScoreMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/IXgConductScoreMapper.java @@ -111,6 +111,22 @@ public interface IXgConductScoreMapper extends BaseMapperPlus + + INSERT INTO xg_conduct_score ( id, tenant_id, academic_year, semester, month, diff --git a/web/src/api/cw/budgetExecutionImport.ts b/web/src/api/cw/budgetExecutionImport.ts index 5d74824b..23cfb6e0 100644 --- a/web/src/api/cw/budgetExecutionImport.ts +++ b/web/src/api/cw/budgetExecutionImport.ts @@ -49,6 +49,3 @@ export const listImportItems = (batchId: number, params?: any) => { params: { batchId, ...params } }); }; - - - diff --git a/web/src/api/cw/monitoringManagement.ts b/web/src/api/cw/monitoringManagement.ts index 2bf08f65..e8db719c 100644 --- a/web/src/api/cw/monitoringManagement.ts +++ b/web/src/api/cw/monitoringManagement.ts @@ -1,6 +1,6 @@ -import request from '@/utils/request' -import type { AxiosPromise } from 'axios' -import type { CwProjectPerformanceSelfEvaluationVO, CwProjectPerformanceSelfEvaluationQuery } from './performanceEvaluation' +import request from '@/utils/request'; +import type { AxiosPromise } from 'axios'; +import type { CwProjectPerformanceSelfEvaluationVO, CwProjectPerformanceSelfEvaluationQuery } from './performanceEvaluation'; /** * 运行监控管理API @@ -11,7 +11,7 @@ import type { CwProjectPerformanceSelfEvaluationVO, CwProjectPerformanceSelfEval * 运行监控管理查询对象 */ export interface MonitoringManagementQuery extends CwProjectPerformanceSelfEvaluationQuery { - isLocked?: number + isLocked?: number; } /** @@ -22,7 +22,7 @@ export function listMonitoringManagement(query: MonitoringManagementQuery): Axio url: '/cw/monitoringManagement/list', method: 'get', params: query - }) + }); } /** @@ -32,7 +32,7 @@ export function getMonitoringManagement(id: number): AxiosPromise { return request({ url: `/cw/monitoringManagement/lock/${id}`, method: 'post' - }) + }); } /** @@ -52,7 +52,7 @@ export function unlockEvaluation(id: number): AxiosPromise { return request({ url: `/cw/monitoringManagement/unlock/${id}`, method: 'post' - }) + }); } /** @@ -63,7 +63,7 @@ export function lockBatchEvaluation(ids: number[]): AxiosPromise { url: '/cw/monitoringManagement/lock/batch', method: 'post', data: ids - }) + }); } /** @@ -74,6 +74,5 @@ export function unlockBatchEvaluation(ids: number[]): AxiosPromise { url: '/cw/monitoringManagement/unlock/batch', method: 'post', data: ids - }) + }); } - diff --git a/web/src/api/cw/performanceEvaluation.ts b/web/src/api/cw/performanceEvaluation.ts index 02afe930..c7172732 100644 --- a/web/src/api/cw/performanceEvaluation.ts +++ b/web/src/api/cw/performanceEvaluation.ts @@ -1,5 +1,5 @@ -import request from '@/utils/request' -import type { AxiosPromise } from 'axios' +import request from '@/utils/request'; +import type { AxiosPromise } from 'axios'; // ========== 绩效自评API ========== @@ -7,300 +7,300 @@ import type { AxiosPromise } from 'axios' * 项目绩效自评视图对象 */ export interface CwProjectPerformanceSelfEvaluationVO { - id?: number - tenantId?: string - projectApplicationId: number - applicationNo: string - projectName: string - supervisoryDept?: string - implementUnit?: string - evaluationYear: number - initialBudget?: number - principal?: string - contactPhone?: string - startDate?: string - endDate?: string - totalInvestment?: number - annualBudget?: number - actualExpenditure?: number - budgetExecutionRate?: number + id?: number; + tenantId?: string; + projectApplicationId: number; + applicationNo: string; + projectName: string; + supervisoryDept?: string; + implementUnit?: string; + evaluationYear: number; + initialBudget?: number; + principal?: string; + contactPhone?: string; + startDate?: string; + endDate?: string; + totalInvestment?: number; + annualBudget?: number; + actualExpenditure?: number; + budgetExecutionRate?: number; // 资金到位(年度资金总额) - fundValue?: number - fundExecutionRate?: number - fundScore?: number + fundValue?: number; + fundExecutionRate?: number; + fundScore?: number; // 当年财政拨款 - currentYearBudgetInitial?: number - currentYearBudgetAnnual?: number - currentYearBudgetActual?: number - currentYearBudgetValue?: number - currentYearBudgetExecutionRate?: number - currentYearBudgetScore?: number + currentYearBudgetInitial?: number; + currentYearBudgetAnnual?: number; + currentYearBudgetActual?: number; + currentYearBudgetValue?: number; + currentYearBudgetExecutionRate?: number; + currentYearBudgetScore?: number; // 上年结转资金 - lastYearCarryoverInitial?: number - lastYearCarryoverAnnual?: number - lastYearCarryoverActual?: number - lastYearCarryoverValue?: number - lastYearCarryoverExecutionRate?: number - lastYearCarryoverScore?: number + lastYearCarryoverInitial?: number; + lastYearCarryoverAnnual?: number; + lastYearCarryoverActual?: number; + lastYearCarryoverValue?: number; + lastYearCarryoverExecutionRate?: number; + lastYearCarryoverScore?: number; // 其他资金 - otherFundsInitial?: number - otherFundsAnnual?: number - otherFundsActual?: number - otherFundsValue?: number - otherFundsExecutionRate?: number - otherFundsScore?: number + otherFundsInitial?: number; + otherFundsAnnual?: number; + otherFundsActual?: number; + otherFundsValue?: number; + otherFundsExecutionRate?: number; + otherFundsScore?: number; - performanceGoal?: string - targetCompletion?: string + performanceGoal?: string; + targetCompletion?: string; // 数量指标 - quantityIndicator?: string - quantityTarget?: string - quantityActual?: string - quantityValue?: number - quantityScore?: number - quantityDeviation?: string + quantityIndicator?: string; + quantityTarget?: string; + quantityActual?: string; + quantityValue?: number; + quantityScore?: number; + quantityDeviation?: string; // 质量指标 - qualityIndicator?: string - qualityTarget?: string - qualityActual?: string - qualityValue?: number - qualityScore?: number - qualityDeviation?: string + qualityIndicator?: string; + qualityTarget?: string; + qualityActual?: string; + qualityValue?: number; + qualityScore?: number; + qualityDeviation?: string; // 时效指标 - timelinessIndicator?: string - timelinessTarget?: string - timelinessActual?: string - timelinessValue?: number - timelinessScore?: number - timelinessDeviation?: string + timelinessIndicator?: string; + timelinessTarget?: string; + timelinessActual?: string; + timelinessValue?: number; + timelinessScore?: number; + timelinessDeviation?: string; // 成本指标 - costIndicator?: string - costTarget?: string - costActual?: string - costValue?: number - costScore?: number - costDeviation?: string + costIndicator?: string; + costTarget?: string; + costActual?: string; + costValue?: number; + costScore?: number; + costDeviation?: string; // 经济效益指标 - economicIndicator?: string - economicTarget?: string - economicActual?: string - economicValue?: number - economicScore?: number - economicDeviation?: string + economicIndicator?: string; + economicTarget?: string; + economicActual?: string; + economicValue?: number; + economicScore?: number; + economicDeviation?: string; // 社会效益指标 - socialIndicator?: string - socialTarget?: string - socialActual?: string - socialValue?: number - socialScore?: number - socialDeviation?: string + socialIndicator?: string; + socialTarget?: string; + socialActual?: string; + socialValue?: number; + socialScore?: number; + socialDeviation?: string; // 生态效益指标 - ecologicalIndicator?: string - ecologicalTarget?: string - ecologicalActual?: string - ecologicalValue?: number - ecologicalScore?: number - ecologicalDeviation?: string + ecologicalIndicator?: string; + ecologicalTarget?: string; + ecologicalActual?: string; + ecologicalValue?: number; + ecologicalScore?: number; + ecologicalDeviation?: string; // 满意度指标 - satisfactionIndicator?: string - satisfactionTarget?: string - satisfactionActual?: string - satisfactionValue?: number - satisfactionScore?: number - satisfactionDeviation?: string + satisfactionIndicator?: string; + satisfactionTarget?: string; + satisfactionActual?: string; + satisfactionValue?: number; + satisfactionScore?: number; + satisfactionDeviation?: string; - decisionScore?: number - decisionContent?: string - processScore?: number - processContent?: string - outputScore?: number - outputContent?: string - benefitScore?: number - benefitContent?: string - totalScore?: number - selfEvaluationLevel?: string - existingProblems?: string - improvementMeasures?: string - evaluationStatus: string - allowEditAfterSubmit?: number - submitTime?: string - submitBy?: number - submitByName?: string - approveTime?: string - approveBy?: number - approveByName?: string - approveOpinion?: string - remark?: string + decisionScore?: number; + decisionContent?: string; + processScore?: number; + processContent?: string; + outputScore?: number; + outputContent?: string; + benefitScore?: number; + benefitContent?: string; + totalScore?: number; + selfEvaluationLevel?: string; + existingProblems?: string; + improvementMeasures?: string; + evaluationStatus: string; + allowEditAfterSubmit?: number; + submitTime?: string; + submitBy?: number; + submitByName?: string; + approveTime?: string; + approveBy?: number; + approveByName?: string; + approveOpinion?: string; + remark?: string; // 锁定相关字段 - isLocked?: number - lockTime?: string - lockBy?: number - lockByName?: string - createTime?: string - updateTime?: string + isLocked?: number; + lockTime?: string; + lockBy?: number; + lockByName?: string; + createTime?: string; + updateTime?: string; // 前端使用的字段,用于标记是否有运行监控数据 - hasMonitoringData?: boolean + hasMonitoringData?: boolean; // 运行监控数据列表 - monitoringList?: CwPerformanceMonitoringVO[] + monitoringList?: CwPerformanceMonitoringVO[]; } /** * 项目绩效自评表单对象 */ export interface CwProjectPerformanceSelfEvaluationForm { - id?: number - projectApplicationId: number - applicationNo?: string - projectName: string - supervisoryDept?: string - implementUnit?: string - evaluationYear: number - initialBudget?: number - principal?: string - contactPhone?: string - startDate?: string - endDate?: string - totalInvestment?: number - annualBudget?: number - actualExpenditure?: number - budgetExecutionRate?: number + id?: number; + projectApplicationId: number; + applicationNo?: string; + projectName: string; + supervisoryDept?: string; + implementUnit?: string; + evaluationYear: number; + initialBudget?: number; + principal?: string; + contactPhone?: string; + startDate?: string; + endDate?: string; + totalInvestment?: number; + annualBudget?: number; + actualExpenditure?: number; + budgetExecutionRate?: number; // 资金到位(年度资金总额) - fundValue?: number - fundExecutionRate?: number - fundScore?: number + fundValue?: number; + fundExecutionRate?: number; + fundScore?: number; // 当年财政拨款 - currentYearBudgetInitial?: number - currentYearBudgetAnnual?: number - currentYearBudgetActual?: number - currentYearBudgetValue?: number - currentYearBudgetExecutionRate?: number - currentYearBudgetScore?: number + currentYearBudgetInitial?: number; + currentYearBudgetAnnual?: number; + currentYearBudgetActual?: number; + currentYearBudgetValue?: number; + currentYearBudgetExecutionRate?: number; + currentYearBudgetScore?: number; // 上年结转资金 - lastYearCarryoverInitial?: number - lastYearCarryoverAnnual?: number - lastYearCarryoverActual?: number - lastYearCarryoverValue?: number - lastYearCarryoverExecutionRate?: number - lastYearCarryoverScore?: number + lastYearCarryoverInitial?: number; + lastYearCarryoverAnnual?: number; + lastYearCarryoverActual?: number; + lastYearCarryoverValue?: number; + lastYearCarryoverExecutionRate?: number; + lastYearCarryoverScore?: number; // 其他资金 - otherFundsInitial?: number - otherFundsAnnual?: number - otherFundsActual?: number - otherFundsValue?: number - otherFundsExecutionRate?: number - otherFundsScore?: number + otherFundsInitial?: number; + otherFundsAnnual?: number; + otherFundsActual?: number; + otherFundsValue?: number; + otherFundsExecutionRate?: number; + otherFundsScore?: number; - performanceGoal?: string - targetCompletion?: string + performanceGoal?: string; + targetCompletion?: string; // 数量指标 - quantityIndicator?: string - quantityTarget?: string - quantityActual?: string - quantityValue?: number - quantityScore?: number - quantityDeviation?: string + quantityIndicator?: string; + quantityTarget?: string; + quantityActual?: string; + quantityValue?: number; + quantityScore?: number; + quantityDeviation?: string; // 质量指标 - qualityIndicator?: string - qualityTarget?: string - qualityActual?: string - qualityValue?: number - qualityScore?: number - qualityDeviation?: string + qualityIndicator?: string; + qualityTarget?: string; + qualityActual?: string; + qualityValue?: number; + qualityScore?: number; + qualityDeviation?: string; // 时效指标 - timelinessIndicator?: string - timelinessTarget?: string - timelinessActual?: string - timelinessValue?: number - timelinessScore?: number - timelinessDeviation?: string + timelinessIndicator?: string; + timelinessTarget?: string; + timelinessActual?: string; + timelinessValue?: number; + timelinessScore?: number; + timelinessDeviation?: string; // 成本指标 - costIndicator?: string - costTarget?: string - costActual?: string - costValue?: number - costScore?: number - costDeviation?: string + costIndicator?: string; + costTarget?: string; + costActual?: string; + costValue?: number; + costScore?: number; + costDeviation?: string; // 经济效益指标 - economicIndicator?: string - economicTarget?: string - economicActual?: string - economicValue?: number - economicScore?: number - economicDeviation?: string + economicIndicator?: string; + economicTarget?: string; + economicActual?: string; + economicValue?: number; + economicScore?: number; + economicDeviation?: string; // 社会效益指标 - socialIndicator?: string - socialTarget?: string - socialActual?: string - socialValue?: number - socialScore?: number - socialDeviation?: string + socialIndicator?: string; + socialTarget?: string; + socialActual?: string; + socialValue?: number; + socialScore?: number; + socialDeviation?: string; // 生态效益指标 - ecologicalIndicator?: string - ecologicalTarget?: string - ecologicalActual?: string - ecologicalValue?: number - ecologicalScore?: number - ecologicalDeviation?: string + ecologicalIndicator?: string; + ecologicalTarget?: string; + ecologicalActual?: string; + ecologicalValue?: number; + ecologicalScore?: number; + ecologicalDeviation?: string; // 满意度指标 - satisfactionIndicator?: string - satisfactionTarget?: string - satisfactionActual?: string - satisfactionValue?: number - satisfactionScore?: number - satisfactionDeviation?: string + satisfactionIndicator?: string; + satisfactionTarget?: string; + satisfactionActual?: string; + satisfactionValue?: number; + satisfactionScore?: number; + satisfactionDeviation?: string; - decisionScore?: number - decisionContent?: string - processScore?: number - processContent?: string - outputScore?: number - outputContent?: string - benefitScore?: number - benefitContent?: string - totalScore?: number - selfEvaluationLevel?: string - existingProblems?: string - improvementMeasures?: string - evaluationStatus?: string - remark?: string + decisionScore?: number; + decisionContent?: string; + processScore?: number; + processContent?: string; + outputScore?: number; + outputContent?: string; + benefitScore?: number; + benefitContent?: string; + totalScore?: number; + selfEvaluationLevel?: string; + existingProblems?: string; + improvementMeasures?: string; + evaluationStatus?: string; + remark?: string; } /** * 项目绩效自评查询对象 */ export interface CwProjectPerformanceSelfEvaluationQuery { - pageNum?: number - pageSize?: number - projectApplicationId?: number - applicationNo?: string - projectName?: string - evaluationYear?: number - evaluationStatus?: string - selfEvaluationLevel?: string + pageNum?: number; + pageSize?: number; + projectApplicationId?: number; + applicationNo?: string; + projectName?: string; + evaluationYear?: number; + evaluationStatus?: string; + selfEvaluationLevel?: string; } /** @@ -311,7 +311,7 @@ export function listEvaluation(query: CwProjectPerformanceSelfEvaluationQuery): url: '/cw/evaluation/list', method: 'get', params: query - }) + }); } /** @@ -321,7 +321,7 @@ export function getEvaluation(id: number): AxiosPromise { return request({ url: `/cw/evaluation/submit/${id}`, method: 'post' - }) + }); } /** @@ -375,7 +375,7 @@ export function approveEvaluation(id: number, opinion: string, approved: boolean url: `/cw/evaluation/approve/${id}`, method: 'post', params: { opinion, approved } - }) + }); } /** @@ -385,7 +385,7 @@ export function delEvaluation(ids: number | number[]): AxiosPromise { return request({ url: `/cw/evaluation/${ids}`, method: 'delete' - }) + }); } /** @@ -395,7 +395,7 @@ export function getMyEvaluations(): AxiosPromise { return request({ url: `/cw/performanceMonitoring/${id}`, method: 'delete' - }) + }); } - diff --git a/web/src/api/cw/projectAuditor.ts b/web/src/api/cw/projectAuditor.ts index 9f4a1027..a4feb675 100644 --- a/web/src/api/cw/projectAuditor.ts +++ b/web/src/api/cw/projectAuditor.ts @@ -73,7 +73,7 @@ export interface BatchAddDetailRequest { nickName: string; deptId: string | number; deptName: string; - phoneNumber: string; // 注意:这里使用驼峰命名,确保与数据库字段映射 + phoneNumber: string; // 注意:这里使用驼峰命名,确保与数据库字段映射 email: string; }>; remark?: string; diff --git a/web/src/api/rs/overseas.ts b/web/src/api/rs/overseas.ts index 3a7c0d60..ddb316d0 100644 --- a/web/src/api/rs/overseas.ts +++ b/web/src/api/rs/overseas.ts @@ -93,11 +93,11 @@ export interface OverseasApplicationVO { administrativeLevelText?: string; personnelCategoryText?: string; // 工作流相关字段 - approvalStatus?: string; // 工作流审批状态:draft/waiting/finish/back/cancel - approvalStatusText?: string; // 工作流审批状态文本 - taskId?: string; // 当前待办任务ID(用于审批跳转) + approvalStatus?: string; // 工作流审批状态:draft/waiting/finish/back/cancel + approvalStatusText?: string; // 工作流审批状态文本 + taskId?: string; // 当前待办任务ID(用于审批跳转) // 附件相关字段 - attachmentIds?: string; // 附件OSS ID列表(逗号分隔) + attachmentIds?: string; // 附件OSS ID列表(逗号分隔) } // 出国申请表单类型 @@ -152,9 +152,9 @@ export interface OverseasApplicationForm { transportationPlan?: string; applicantRemark?: string; // 工作流相关字段 - approvalStatus?: string; // 工作流审批状态 + approvalStatus?: string; // 工作流审批状态 // 附件相关字段 - attachmentIds?: string; // 附件OSS ID列表(逗号分隔) + attachmentIds?: string; // 附件OSS ID列表(逗号分隔) } // 审批请求类型(支持标准工作流模式) @@ -353,13 +353,13 @@ export function listPendingOverseasApplicationPage(query: OverseasApplicationQue // ======================================== // 🚨 标准化改造:删除自定义待审批查询API -// +// // 原因:出国申请工作流应使用标准工作流模式 // 用户应通过工作流API pageByTaskWait() 获取待办任务 // 而不是调用自定义的业务API -// +// // 删除的API:listPendingOverseasApplication() -// +// // 标准工作流模式: // 1. 导入工作流API:import { pageByTaskWait } from '@/api/workflow/task' // 2. 调用标准API:pageByTaskWait(queryParams) @@ -865,9 +865,9 @@ export interface ItineraryChangeVO { // 行程变更表单类型 export interface ItineraryChangeForm { changeId?: number | string; - recordId: number; // 关联出国记录ID(必填) + recordId: number; // 关联出国记录ID(必填) applicationId: number; - applicantId: number; // 申请人ID(必填) + applicantId: number; // 申请人ID(必填) applicantName?: string; gender?: string; birthDate?: string; diff --git a/web/src/api/rs/overseas/monitor.js b/web/src/api/rs/overseas/monitor.js index a213ed0b..59deece3 100644 --- a/web/src/api/rs/overseas/monitor.js +++ b/web/src/api/rs/overseas/monitor.js @@ -1,7 +1,7 @@ -import request from '@/utils/request' +import request from '@/utils/request'; // API基础路径 -const baseUrl = '/rs/overseas/monitor' +const baseUrl = '/rs/overseas/monitor'; /** * 查询审批中的申请列表 @@ -9,11 +9,11 @@ const baseUrl = '/rs/overseas/monitor' * @returns {Promise} */ export function getPendingList(query) { - return request({ - url: `${baseUrl}/pending/list`, - method: 'get', - params: query - }) + return request({ + url: `${baseUrl}/pending/list`, + method: 'get', + params: query + }); } /** @@ -22,11 +22,11 @@ export function getPendingList(query) { * @returns {Promise} */ export function getCompletedList(query) { - return request({ - url: `${baseUrl}/completed/list`, - method: 'get', - params: query - }) + return request({ + url: `${baseUrl}/completed/list`, + method: 'get', + params: query + }); } /** @@ -35,10 +35,10 @@ export function getCompletedList(query) { * @returns {Promise} */ export function getApprovalHistory(applicationId) { - return request({ - url: `${baseUrl}/history/${applicationId}`, - method: 'get' - }) + return request({ + url: `${baseUrl}/history/${applicationId}`, + method: 'get' + }); } /** @@ -47,10 +47,10 @@ export function getApprovalHistory(applicationId) { * @returns {Promise} */ export function getDetail(applicationId) { - return request({ - url: `${baseUrl}/detail/${applicationId}`, - method: 'get' - }) + return request({ + url: `${baseUrl}/detail/${applicationId}`, + method: 'get' + }); } /** @@ -59,12 +59,12 @@ export function getDetail(applicationId) { * @returns {Promise} */ export function exportPending(query) { - return request({ - url: `${baseUrl}/pending/export`, - method: 'post', - data: query, - responseType: 'blob' - }) + return request({ + url: `${baseUrl}/pending/export`, + method: 'post', + data: query, + responseType: 'blob' + }); } /** @@ -73,12 +73,12 @@ export function exportPending(query) { * @returns {Promise} */ export function exportCompleted(query) { - return request({ - url: `${baseUrl}/completed/export`, - method: 'post', - data: query, - responseType: 'blob' - }) + return request({ + url: `${baseUrl}/completed/export`, + method: 'post', + data: query, + responseType: 'blob' + }); } /** @@ -86,9 +86,8 @@ export function exportCompleted(query) { * @returns {Promise} */ export function getStatistics() { - return request({ - url: `${baseUrl}/statistics`, - method: 'get' - }) + return request({ + url: `${baseUrl}/statistics`, + method: 'get' + }); } - diff --git a/web/src/api/rs/staffInfo/index.ts b/web/src/api/rs/staffInfo/index.ts new file mode 100644 index 00000000..7fa78591 --- /dev/null +++ b/web/src/api/rs/staffInfo/index.ts @@ -0,0 +1,131 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { StaffInfoQuery, StaffInfoVO, StaffInfoForm, DeptTreeVO, R, TableDataInfo } from './types'; + +/** + * 查询人员信息列表 + * @param query 查询参数 + */ +export function listStaffInfo(query?: StaffInfoQuery): AxiosPromise>> { + return request({ + url: '/staff/staffInfo/list', + method: 'get', + params: query + }); +} + +/** + * 根据部门ID查询人员信息列表 + * @param query 查询参数 + */ +export function listStaffInfoByDeptId(query?: any): AxiosPromise>> { + return request({ + url: '/staff/staffInfo/listByDeptId', + method: 'post', + data: query + }); +} + +/** + * 获取人员信息详细信息 + * @param id 人员ID + */ +export function getStaffInfo(id: number | string): AxiosPromise> { + return request({ + url: '/staff/staffInfo/' + id, + method: 'get' + }); +} + +/** + * 新增人员信息 + * @param data 人员信息 + */ +export function addStaffInfo(data: StaffInfoForm): AxiosPromise> { + return request({ + url: '/staff/staffInfo', + method: 'post', + data: data + }); +} + +/** + * 修改人员信息 + * @param data 人员信息 + */ +export function updateStaffInfo(data: StaffInfoForm): AxiosPromise> { + return request({ + url: '/staff/staffInfo', + method: 'put', + data: data + }); +} + +/** + * 删除人员信息 + * @param ids 人员ID数组 + */ +export function delStaffInfo(ids: (number | string)[]): AxiosPromise> { + return request({ + url: '/staff/staffInfo/' + ids.join(','), + method: 'delete' + }); +} + +/** + * 获取部门树形数据 + */ +export function getDeptTree(): AxiosPromise> { + return request({ + url: '/staff/staffInfo/deptTree', + method: 'get' + }); +} + +/** + * 导出人员信息 + * @param query 查询参数 + */ +export function exportStaffInfo(query?: StaffInfoQuery): AxiosPromise { + return request({ + url: '/staff/staffInfo/export', + method: 'post', + params: query, + responseType: 'blob' + }); +} + +/** + * 下载人员信息导入模板 + */ +export function downloadImportTemplate(): AxiosPromise { + return request({ + url: '/staff/staffInfo/importTemplate', + method: 'post', + responseType: 'blob' + }); +} + +/** + * 导入人员数据 + * @param file 上传的文件 + * @param deptId 部门ID + * @param updateSupport 是否支持更新 + */ +export function importStaffInfo(file: File, deptId?: string, updateSupport: boolean = false): AxiosPromise> { + const formData = new FormData(); + formData.append('file', file); + if (deptId) { + formData.append('deptId', deptId); + } + formData.append('updateSupport', updateSupport.toString()); + + return request({ + url: '/staff/staffInfo/importData', + method: 'post', + data: formData, + headers: { + 'Content-Type': 'multipart/form-data' + } + }); +} diff --git a/web/src/api/rs/staffInfo/types.ts b/web/src/api/rs/staffInfo/types.ts new file mode 100644 index 00000000..14399bf3 --- /dev/null +++ b/web/src/api/rs/staffInfo/types.ts @@ -0,0 +1,92 @@ +/** + * 人员信息查询参数 + */ +export interface StaffInfoQuery { + pageNum: number; + pageSize: number; + name?: string; + staffNo?: string; + phone?: string; + staffType?: string; + status?: string; + deptId?: number; +} + +/** + * 人员信息视图对象 + */ +export interface StaffInfoVO { + id: number | string; + staffNo: string; + name: string; + gender: string; + position: string; + deptId: number; + deptName: string; + phone: string; + staffType: string; + userId?: number; + status: string; + createTime?: string; + updateTime?: string; + remark?: string; +} + +/** + * 人员信息表单对象 + */ +export interface StaffInfoForm { + id?: number | string; + staffNo?: string; + name: string; + gender: string; + position: string; + deptId: number; + deptName?: string; + phone: string; + staffType: string; + status?: string; + remark?: string; +} + +/** + * 人员信息导入VO + */ +export interface StaffInfoImportVo { + staffNo?: string; + name: string; + gender: string; + position?: string; + deptName: string; + phone: string; + staffType: string; +} + +/** + * 部门树结构 + */ +export interface DeptTreeVO { + deptId: number; + parentId: number; + deptName: string; + children?: DeptTreeVO[]; +} + +/** + * 通用响应对象 + */ +export interface R { + code: number; + msg: string; + data: T; +} + +/** + * 分页数据信息 + */ +export interface TableDataInfo { + total: number; + rows: T[]; + code: number; + msg: string; +} diff --git a/web/src/api/rs/training.ts b/web/src/api/rs/training.ts index 296ff07b..d4a79873 100644 --- a/web/src/api/rs/training.ts +++ b/web/src/api/rs/training.ts @@ -70,15 +70,15 @@ export interface ApplicationVO { userName: string; department: string; position?: string; - currentTitle?: string; // 现职称 - currentPosition?: string; // 现岗位 - workStartTime?: string; // 参加工作时间 - phone?: string; // 联系电话 - organizerUnit?: string; // 主办单位 - trainingType?: string; // 培训类型 - trainingFee?: number; // 培训费用 - companions?: string; // 同行人员(JSON字符串) - trainingPurpose?: string; // 培训目的 + currentTitle?: string; // 现职称 + currentPosition?: string; // 现岗位 + workStartTime?: string; // 参加工作时间 + phone?: string; // 联系电话 + organizerUnit?: string; // 主办单位 + trainingType?: string; // 培训类型 + trainingFee?: number; // 培训费用 + companions?: string; // 同行人员(JSON字符串) + trainingPurpose?: string; // 培训目的 reason?: string; status: string; applyTime: string; @@ -87,12 +87,12 @@ export interface ApplicationVO { duration?: number; startTime?: string; endTime?: string; - rejectTime?: string; // 拒绝时间 - attachmentIds?: string; // 附件ID列表 + rejectTime?: string; // 拒绝时间 + attachmentIds?: string; // 附件ID列表 createTime: string; updateTime: string; - createBy: number; // 修正类型:string -> number - updateBy: number; // 修正类型:string -> number + createBy: number; // 修正类型:string -> number + updateBy: number; // 修正类型:string -> number remark: string; } @@ -105,23 +105,23 @@ export interface ApplicationForm { userName?: string; department?: string; position?: string; - currentTitle?: string; // 现职称 - currentPosition?: string; // 现岗位 - workStartTime?: string; // 参加工作时间 - phone?: string; // 联系电话 - organizerUnit?: string; // 主办单位 - trainingType?: string; // 培训类型 - trainingTypeOther?: string; // 其他培训类型描述 - trainingFee?: number; // 培训费用 + currentTitle?: string; // 现职称 + currentPosition?: string; // 现岗位 + workStartTime?: string; // 参加工作时间 + phone?: string; // 联系电话 + organizerUnit?: string; // 主办单位 + trainingType?: string; // 培训类型 + trainingTypeOther?: string; // 其他培训类型描述 + trainingFee?: number; // 培训费用 companions?: (string | number)[]; // 同行人员数组 - trainingPurpose?: string; // 培训目的 + trainingPurpose?: string; // 培训目的 reason?: string; status?: string; location?: string; duration?: number; startTime?: string; endTime?: string; - attachmentIds?: string[]; // 附件ID数组 + attachmentIds?: string[]; // 附件ID数组 remark?: string; } @@ -164,11 +164,11 @@ export interface RecordVO { updateBy: string; remark: string; // 关联字段 - 从申请表获取 - organizerUnit?: string; // 主办单位 - trainingType?: string; // 培训类型 - companions?: string; // 同行人员 - startTime?: string; // 开始时间 - endTime?: string; // 结束时间 + organizerUnit?: string; // 主办单位 + trainingType?: string; // 培训类型 + companions?: string; // 同行人员 + startTime?: string; // 开始时间 + endTime?: string; // 结束时间 } // 培训记录表单类型 @@ -440,7 +440,7 @@ export function updateRecordAttachments(recordId: string | number, data: { attac // 审批培训申请(7级审批制度) export function approveTrainingApplication(data: { applicationId: number | string; - approvalStatus: string; // 1-通过,2-拒绝 + approvalStatus: string; // 1-通过,2-拒绝 approvalOpinion?: string; approvalSignature?: string; // base64格式 }) { diff --git a/web/src/api/stgl/activity/audit.ts b/web/src/api/stgl/activity/audit.ts index 1d2e1bf5..c8e32ece 100644 --- a/web/src/api/stgl/activity/audit.ts +++ b/web/src/api/stgl/activity/audit.ts @@ -128,4 +128,3 @@ export const delActivityAudit = (id: string | number | Array) = method: 'delete' }); }; - diff --git a/web/src/api/stgl/auditor/index.ts b/web/src/api/stgl/auditor/index.ts index e7727e39..9aef3127 100644 --- a/web/src/api/stgl/auditor/index.ts +++ b/web/src/api/stgl/auditor/index.ts @@ -1,6 +1,6 @@ -import request from '@/utils/request' -import { AxiosPromise } from 'axios' -import { StglAuditorVO, StglAuditorForm, StglAuditorQuery } from './types' +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { StglAuditorVO, StglAuditorForm, StglAuditorQuery } from './types'; /** * 查询社团管理审核人配置列表 @@ -13,8 +13,8 @@ export const listStglAuditor = (query?: StglAuditorQuery): AxiosPromise return request({ url: '/stgl/auditor/' + id, method: 'get' - }) -} + }); +}; /** * 新增社团管理审核人配置 @@ -36,8 +36,8 @@ export const addStglAuditor = (data: StglAuditorForm) => { url: '/stgl/auditor', method: 'post', data: data - }) -} + }); +}; /** * 修改社团管理审核人配置 @@ -48,8 +48,8 @@ export const updateStglAuditor = (data: StglAuditorForm) => { url: '/stgl/auditor', method: 'put', data: data - }) -} + }); +}; /** * 删除社团管理审核人配置 @@ -59,8 +59,8 @@ export const delStglAuditor = (id: string | number | Array) => return request({ url: '/stgl/auditor/' + id, method: 'delete' - }) -} + }); +}; /** * 导出社团管理审核人配置 @@ -73,8 +73,8 @@ export const exportStglAuditor = (query?: StglAuditorQuery) => { method: 'post', data: query, responseType: 'blob' - }) -} + }); +}; /** * 批量添加审核人 @@ -89,8 +89,8 @@ export const batchAddAuditors = (userIds: number[], remark?: string) => { userIds, remark } - }) -} + }); +}; /** * 查询我配置的审核人列表 @@ -101,8 +101,8 @@ export const listMyConfigAuditors = (query?: StglAuditorQuery): AxiosPromise { url: '/stgl/auditor/enable', method: 'post', data: ids - }) -} + }); +}; /** * 批量停用审核人 @@ -125,8 +125,8 @@ export const disableAuditors = (ids: number[]) => { url: '/stgl/auditor/disable', method: 'post', data: ids - }) -} + }); +}; /** * 根据部门ID获取用户列表 @@ -136,5 +136,5 @@ export const listUserByDeptId = (deptId: string | number) => { return request({ url: '/stgl/auditor/users/' + deptId, method: 'get' - }) -} \ No newline at end of file + }); +}; diff --git a/web/src/api/stgl/auditor/types.ts b/web/src/api/stgl/auditor/types.ts index b6ef4500..fe6a72f2 100644 --- a/web/src/api/stgl/auditor/types.ts +++ b/web/src/api/stgl/auditor/types.ts @@ -1,4 +1,4 @@ -import { PageQuery } from '@/api/types' +import { PageQuery } from '@/api/types'; export interface StglAuditorVO { /** @@ -145,7 +145,6 @@ export interface StglAuditorForm { } export interface StglAuditorQuery extends PageQuery { - /** * 用户ID */ @@ -176,9 +175,9 @@ export interface StglAuditorQuery extends PageQuery { */ configBy?: number; - /** - * 日期范围参数 - */ - 'params[beginCreateTime]'?: string; - 'params[endCreateTime]'?: string; -} \ No newline at end of file + /** + * 日期范围参数 + */ + 'params[beginCreateTime]'?: string; + 'params[endCreateTime]'?: string; +} diff --git a/web/src/api/stgl/club/advisor-history.ts b/web/src/api/stgl/club/advisor-history.ts index 143cfc0e..dc3d8ff8 100644 --- a/web/src/api/stgl/club/advisor-history.ts +++ b/web/src/api/stgl/club/advisor-history.ts @@ -176,10 +176,10 @@ export interface StglClubAdvisorHistoryQuery extends PageQuery { */ endTime?: string; - /** - * 日期范围参数 - */ - params?: any; + /** + * 日期范围参数 + */ + params?: any; } /** @@ -273,4 +273,4 @@ export const delStglClubAdvisorHistory = (id: string | number | Array { * 审核年审记录 * @param params */ -export const auditStglClubAnnualReview = (params: { - id: string | number; - auditResult: string; - auditOpinion?: string; - auditType: string; -}) => { +export const auditStglClubAnnualReview = (params: { id: string | number; auditResult: string; auditOpinion?: string; auditType: string }) => { return request({ url: '/stgl/club/annualReview/audit', method: 'post', @@ -205,7 +200,7 @@ export const getClubActivitiesByReviewYear = (clubId: string | number, reviewYea isEncrypt: 'true' } }); -}; +}; /** * 获取年审记录的活动数据(从stgl_club_annual_review_activity表中获取) @@ -219,4 +214,4 @@ export const getAnnualReviewActivities = (annualReviewId: string | number) => { isEncrypt: 'true' } }); -}; \ No newline at end of file +}; diff --git a/web/src/api/stgl/club/annualReview/type.ts b/web/src/api/stgl/club/annualReview/type.ts index c70b94d3..27c4f61f 100644 --- a/web/src/api/stgl/club/annualReview/type.ts +++ b/web/src/api/stgl/club/annualReview/type.ts @@ -320,4 +320,4 @@ export const ANNUAL_REVIEW_STATUS_OPTIONS = [ export const YES_NO_OPTIONS = [ { label: '是', value: 'Y' }, { label: '否', value: 'N' } -]; \ No newline at end of file +]; diff --git a/web/src/api/stgl/club/annualReviewAudit/index.ts b/web/src/api/stgl/club/annualReviewAudit/index.ts index 4ddd996d..eb863f86 100644 --- a/web/src/api/stgl/club/annualReviewAudit/index.ts +++ b/web/src/api/stgl/club/annualReviewAudit/index.ts @@ -91,10 +91,10 @@ export const delStglAnnualReviewAudit = (id: string | number | Array + auditId: string | number, + approveResult: string, + approveOpinion: string, + nextAuditors?: Array<{ userId: string | number; userName: string; nickName: string }> ) => { return request({ url: '/stgl/annualReviewAudit/approve', @@ -106,4 +106,4 @@ export const approveAnnualReview = ( nextAuditors: nextAuditors || [] } }); -}; \ No newline at end of file +}; diff --git a/web/src/api/stgl/club/annualReviewAudit/type.ts b/web/src/api/stgl/club/annualReviewAudit/type.ts index 10c90c0b..8d3488d4 100644 --- a/web/src/api/stgl/club/annualReviewAudit/type.ts +++ b/web/src/api/stgl/club/annualReviewAudit/type.ts @@ -179,4 +179,4 @@ export const AUDIT_NODE_STATUS_OPTIONS = [ export const APPROVE_RESULT_OPTIONS = [ { label: '通过', value: '通过' }, { label: '不通过', value: '不通过' } -]; \ No newline at end of file +]; diff --git a/web/src/api/stgl/clubPermission.ts b/web/src/api/stgl/clubPermission.ts index c4d5464a..4c708d68 100644 --- a/web/src/api/stgl/clubPermission.ts +++ b/web/src/api/stgl/clubPermission.ts @@ -1,22 +1,22 @@ -import request from '@/utils/request' +import request from '@/utils/request'; // 社团活动权限检查结果类型定义 export interface ClubActivityPermissionResult { - canCreateActivity: boolean - status: 'NORMAL' | 'TEMPORARILY_LIMITED' | 'PERMANENTLY_LIMITED' - message: string - deadlineTime?: string - remainingHours?: number - remainingDays?: number - annualReviewStartTime?: string + canCreateActivity: boolean; + status: 'NORMAL' | 'TEMPORARILY_LIMITED' | 'PERMANENTLY_LIMITED'; + message: string; + deadlineTime?: string; + remainingHours?: number; + remainingDays?: number; + annualReviewStartTime?: string; } // 权限详情类型定义 export interface ClubPermissionDetails { - permissionResult: ClubActivityPermissionResult - isInAnnualReviewWindow: boolean - daysUntilNextAnnualReview: number - hasSubmittedCurrentYearAnnualReview: boolean + permissionResult: ClubActivityPermissionResult; + isInAnnualReviewWindow: boolean; + daysUntilNextAnnualReview: number; + hasSubmittedCurrentYearAnnualReview: boolean; } /** @@ -28,7 +28,7 @@ export function checkClubActivityPermission(clubId: number | string) { return request({ url: `/stgl/club/permission/checkActivityPermission/${clubId}`, method: 'get' - }) + }); } /** @@ -41,7 +41,7 @@ export function batchCheckClubPermission(clubIds: number[]) { url: '/stgl/club/permission/batchCheckPermission', method: 'post', data: clubIds - }) + }); } /** @@ -53,7 +53,7 @@ export function isInAnnualReviewWindow(clubId: number) { return request({ url: `/stgl/club/permission/isInAnnualReviewWindow/${clubId}`, method: 'get' - }) + }); } /** @@ -65,7 +65,7 @@ export function getDaysUntilNextAnnualReview(clubId: number) { return request({ url: `/stgl/club/permission/getDaysUntilNextAnnualReview/${clubId}`, method: 'get' - }) + }); } /** @@ -77,7 +77,7 @@ export function hasSubmittedCurrentYearAnnualReview(clubId: number) { return request({ url: `/stgl/club/permission/hasSubmittedCurrentYearAnnualReview/${clubId}`, method: 'get' - }) + }); } /** @@ -89,5 +89,5 @@ export function getClubPermissionDetails(clubId: number) { return request({ url: `/stgl/club/permission/getPermissionDetails/${clubId}`, method: 'get' - }) -} \ No newline at end of file + }); +} diff --git a/web/src/api/xg/conductScore.ts b/web/src/api/xg/conductScore.ts index 3d2c5de7..30c11f8f 100644 --- a/web/src/api/xg/conductScore.ts +++ b/web/src/api/xg/conductScore.ts @@ -1,4 +1,4 @@ -import request from '@/utils/request' +import request from '@/utils/request'; // 查询操行分列表 export function conductScoreList(query: any) { @@ -6,7 +6,7 @@ export function conductScoreList(query: any) { url: '/xg/conduct-score/list', method: 'get', params: query - }) + }); } // 根据部门ID查询操行分列表(带权限过滤) @@ -15,7 +15,7 @@ export function conductScoreListByDeptId(query: any) { url: '/xg/conduct-score/listByDeptId', method: 'post', data: query - }) + }); } // 查询操行分详细 @@ -23,7 +23,7 @@ export function conductScoreInfo(id: any) { return request({ url: '/xg/conduct-score/' + id, method: 'get' - }) + }); } // 新增操行分 @@ -32,7 +32,7 @@ export function conductScoreAdd(data: any) { url: '/xg/conduct-score', method: 'post', data: data - }) + }); } // 修改操行分 @@ -41,7 +41,7 @@ export function conductScoreEdit(data: any) { url: '/xg/conduct-score/' + data.id, method: 'put', data: data - }) + }); } // 删除操行分 @@ -49,7 +49,7 @@ export function conductScoreRemove(ids: any) { return request({ url: '/xg/conduct-score/' + ids, method: 'delete' - }) + }); } // 导出操行分 @@ -58,7 +58,7 @@ export function conductScoreExport(query: any) { url: '/xg/conduct-score/export', method: 'post', params: query - }) + }); } // 批量录入操行分 @@ -67,7 +67,7 @@ export function conductScoreBatchAdd(data: any) { url: '/xg/conduct-score/batch', method: 'post', data: data - }) + }); } // 获取班主任负责的班级学生列表 @@ -76,7 +76,7 @@ export function conductScoreClassStudents(query: any) { url: '/xg/conduct-score/class-students', method: 'get', params: query - }) + }); } // 查询学期平均分统计 @@ -85,7 +85,7 @@ export function conductScoreSemesterAverage(query: any) { url: '/xg/conduct-score/semester-average', method: 'get', params: query - }) + }); } // 根据部门ID查询学期平均分统计(带权限过滤) @@ -94,7 +94,7 @@ export function conductScoreSemesterAverageByDeptId(query: any) { url: '/xg/conduct-score/semester-average-by-dept-id', method: 'post', data: query - }) + }); } // 导出学期统计数据 @@ -103,7 +103,7 @@ export function conductScoreExportSemester(query: any) { url: '/xg/conduct-score/export-semester', method: 'get', params: query - }) + }); } // 验证录入权限 @@ -112,7 +112,7 @@ export function conductScoreCheckPermission(query: any) { url: '/xg/conduct-score/check-permission', method: 'get', params: query - }) + }); } // 获取学生详细记录(按月份) @@ -121,5 +121,5 @@ export function conductScoreGetStudentRecords(studentId: any, academicYear: any, url: `/xg/conduct-score/student-records/${studentId}`, method: 'get', params: { academicYear, semester, tenantId } - }) + }); } diff --git a/web/src/api/xg/ejxyfgld/index.ts b/web/src/api/xg/ejxyfgld/index.ts index 01c0dcc8..c0c7e602 100644 --- a/web/src/api/xg/ejxyfgld/index.ts +++ b/web/src/api/xg/ejxyfgld/index.ts @@ -114,4 +114,3 @@ export function exportCollegeLeader(query: any) { responseType: 'blob' }); } - diff --git a/web/src/api/xg/studentDynamics.ts b/web/src/api/xg/studentDynamics.ts index a4b7c059..92765824 100644 --- a/web/src/api/xg/studentDynamics.ts +++ b/web/src/api/xg/studentDynamics.ts @@ -266,4 +266,3 @@ export function getStudentLeave(id: string | number): AxiosPromise { url: '/xljk/consulter', method: 'post', data: data - }) -} + }); +}; /** * 修改心理咨询师配置 @@ -274,8 +274,8 @@ export const updateXljkConsulter = (data: XljkConsulterForm) => { url: '/xljk/consulter', method: 'put', data: data - }) -} + }); +}; /** * 删除心理咨询师配置 @@ -285,8 +285,8 @@ export const delXljkConsulter = (id: string | number | Array) = return request({ url: '/xljk/consulter/' + id, method: 'delete' - }) -} + }); +}; /** * 导出心理咨询师配置 @@ -299,8 +299,8 @@ export const exportXljkConsulter = (query?: XljkConsulterQuery) => { method: 'post', data: query, responseType: 'blob' - }) -} + }); +}; /** * 批量添加咨询师 @@ -315,8 +315,8 @@ export const batchAddConsulters = (userIds: number[], remark?: string) => { userIds, remark } - }) -} + }); +}; /** * 查询我配置的咨询师列表 @@ -327,8 +327,8 @@ export const listMyConfigConsulters = (query?: XljkConsulterQuery): AxiosPromise url: '/xljk/consulter/myConfig', method: 'get', params: query - }) -} + }); +}; /** * 批量启用咨询师 @@ -339,8 +339,8 @@ export const enableConsulters = (ids: number[]) => { url: '/xljk/consulter/enable', method: 'post', data: ids - }) -} + }); +}; /** * 批量停用咨询师 @@ -351,8 +351,8 @@ export const disableConsulters = (ids: number[]) => { url: '/xljk/consulter/disable', method: 'post', data: ids - }) -} + }); +}; /** * 根据部门ID获取用户列表 @@ -362,5 +362,5 @@ export const listUserByDeptId = (deptId: string | number) => { return request({ url: '/xljk/consulter/users/' + deptId, method: 'get' - }) -} + }); +}; diff --git a/web/src/api/xljk/consultation/types.ts b/web/src/api/xljk/consultation/types.ts index 7fae7872..18f41cdb 100644 --- a/web/src/api/xljk/consultation/types.ts +++ b/web/src/api/xljk/consultation/types.ts @@ -1,4 +1,4 @@ -import { PageQuery } from '@/api/types' +import { PageQuery } from '@/api/types'; export interface XljkConsultationScheduleVO { /** @@ -941,7 +941,6 @@ export interface XljkConsulterForm { } export interface XljkConsulterQuery extends PageQuery { - /** * 用户ID */ @@ -977,9 +976,9 @@ export interface XljkConsulterQuery extends PageQuery { */ office?: string; - /** - * 日期范围参数 - */ - 'params[beginCreateTime]'?: string; - 'params[endCreateTime]'?: string; + /** + * 日期范围参数 + */ + 'params[beginCreateTime]'?: string; + 'params[endCreateTime]'?: string; } diff --git a/web/src/views/rs/staff-info/README.md b/web/src/views/rs/staff-info/README.md new file mode 100644 index 00000000..be449c4d --- /dev/null +++ b/web/src/views/rs/staff-info/README.md @@ -0,0 +1,178 @@ +# 人员录入功能说明 + +## 📋 功能概述 + +人员录入功能支持录入正式工、临时工、外聘工三种类型的人员信息,包含完整的增删改查、导入导出功能。 + +## 🔧 功能特性 + +### 核心功能 +- ✅ 人员信息录入(姓名、性别、职务、部门、手机号、工号) +- ✅ 工号自动生成(正式工0XXXXX、临时工LXXXXX、外聘工WXXXXX) +- ✅ 增删改查操作 +- ✅ 数据同步(自动同步sys_user和sys_user_role表) +- ✅ 导入导出Excel功能 +- ✅ 部门树形选择 + +### 工号规则 +| 人员类型 | 工号格式 | 示例 | +|---------|---------|------| +| 正式工 | 0XXXXX | 000001 | +| 临时工 | LXXXXX | L00001 | +| 外聘工 | WXXXXX | W00001 | + +## 📁 文件结构 + +``` +backend/ruoyi-modules/ruoyi-rs/ +├── domain/ +│ ├── RsStaffInfo.java # 实体类 +│ ├── vo/ +│ │ ├── RsStaffInfoVo.java # 视图对象 +│ │ └── RsStaffInfoImportVo.java # 导入VO +│ └── bo/ +│ └── RsStaffInfoBo.java # 业务对象 +├── mapper/ +│ ├── RsStaffInfoMapper.java # 数据访问层接口 +│ └── RsStaffInfoMapper.xml # MyBatis映射文件 +├── service/ +│ ├── IRsStaffInfoService.java # 服务接口 +│ └── impl/ +│ └── RsStaffInfoServiceImpl.java # 服务实现 +├── listener/ +│ └── RsStaffInfoImportListener.java # 导入监听器 +└── controller/ + └── RsStaffInfoController.java # 控制器 + +web/src/views/rs/staff-info/ +├── index.vue # 主页面 +└── README.md # 说明文档 + +web/src/api/rs/staffInfo/ +├── index.ts # API接口 +└── types.ts # 类型定义 +``` + +## 🔌 API接口 + +### 主要接口 +- `GET /staff/staffInfo/list` - 查询人员列表 +- `POST /staff/staffInfo/listByDeptId` - 根据部门查询人员列表 +- `GET /staff/staffInfo/{id}` - 获取人员详情 +- `POST /staff/staffInfo` - 新增人员 +- `PUT /staff/staffInfo` - 修改人员 +- `DELETE /staff/staffInfo/{ids}` - 删除人员 +- `GET /staff/staffInfo/deptTree` - 获取部门树 +- `POST /staff/staffInfo/importData` - 导入人员数据 +- `POST /staff/staffInfo/importTemplate` - 下载导入模板 +- `POST /staff/staffInfo/export` - 导出人员数据 + +## 📊 数据同步逻辑 + +### 新增人员时同步操作 +1. 在`rs_staff_info`表插入人员记录 +2. 自动生成工号(根据人员类型) +3. 在`sys_user`表创建用户记录 +4. 在`sys_user_role`表添加角色(4和1938120323613728770) + +### 修改人员时同步操作 +1. 更新`rs_staff_info`表记录 +2. 同步更新`sys_user`表的对应字段 + +### 删除人员时同步操作 +1. 逻辑删除`rs_staff_info`表记录 +2. 逻辑删除`sys_user`表对应记录 +3. 级联删除`sys_user_role`表记录 + +## 📥 导入导出功能 + +### 导入模板格式 +```excel +| 姓名 | 性别 | 职务 | 部门名称 | 手机号 | 人员类型 | +|------|------|------|----------|--------|----------| +| 张三 | 男 | 讲师 | 计算机学院 | 13800138000 | 正式工 | +``` + +### 模板特性 +- **性别列**:下拉选择(男/女) +- **人员类型列**:下拉选择(正式工/临时工/外聘工) +- **数据验证**:自动校验必填字段和格式 + +### 导入验证规则 +- 姓名:必填 +- 性别:必填,必须是"男"或"女" +- 部门名称:必填,必须存在于系统中 +- 手机号:必填,11位数字格式,且系统中唯一 +- 人员类型:必填,必须是"正式工"、"临时工"或"外聘工" + +## 🎨 前端页面布局 + +### 页面结构 +``` +├── 左侧部门树 +│ ├── 部门搜索框 +│ └── 部门树形结构 +└── 右侧内容区域 + ├── 搜索表单 + │ ├── 姓名、工号、手机号等查询条件 + │ └── 时间范围选择 + ├── 操作按钮栏 + │ ├── 新增、修改、删除按钮 + │ └── 导入、导出按钮 + └── 数据表格 + ├── 工号、姓名、性别等列 + └── 操作列(修改、删除、详情) +``` + +### 交互逻辑 +1. **部门选择**:点击左侧部门树节点,右侧显示该部门人员 +2. **搜索过滤**:支持多条件组合查询 +3. **批量操作**:支持批量删除 +4. **导入导出**:完整的Excel导入导出流程 + +## 🔧 使用说明 + +### 1. 访问页面 +在系统菜单中配置人员录入功能入口,路径为:`/rs/staff-info` + +### 2. 新增人员 +1. 选择左侧部门节点 +2. 点击"新增"按钮 +3. 填写人员信息 +4. 系统自动生成工号 +5. 点击"确定"保存 + +### 3. 导入人员 +1. 选择左侧部门节点 +2. 点击"更多" → "导入数据" +3. 下载导入模板 +4. 按照模板格式填写数据 +5. 上传Excel文件 +6. 查看导入结果 + +### 4. 导出人员 +1. 设置查询条件(可选) +2. 点击"更多" → "导出数据" +3. 系统自动下载Excel文件 + +## ⚠️ 注意事项 + +1. **部门选择**:导入和新增操作前必须先选择部门节点 +2. **数据唯一性**:手机号在整个系统中必须唯一 +3. **工号生成**:工号自动生成,无需手动填写 +4. **角色分配**:新增人员自动分配默认角色 +5. **权限控制**:不同操作需要相应权限 + +## 🔄 数据流转 + +``` +用户操作 → 前端页面 → API接口 → 控制器 → 服务层 → 数据访问层 → 数据库 + ↑ ↓ +导入文件 ← Excel处理 ← 导入监听器 ← 业务验证 ← 数据转换 ← 自动生成工号 + ↑ ↓ +导出文件 ← Excel生成 ← 模板下载 ← 查询结果 ← 条件筛选 ← 用户查询 +``` + +## 📞 技术支持 + +如有问题,请联系开发团队或查看系统日志获取详细错误信息。 \ No newline at end of file diff --git a/web/src/views/rs/staff-info/index.vue b/web/src/views/rs/staff-info/index.vue new file mode 100644 index 00000000..b34c10cb --- /dev/null +++ b/web/src/views/rs/staff-info/index.vue @@ -0,0 +1,1090 @@ + + + + + diff --git a/web/src/views/xg/xsjcxx/ConductScoreInput/index.vue b/web/src/views/xg/xsjcxx/ConductScoreInput/index.vue index ee42ddfb..73aa9a4d 100644 --- a/web/src/views/xg/xsjcxx/ConductScoreInput/index.vue +++ b/web/src/views/xg/xsjcxx/ConductScoreInput/index.vue @@ -73,12 +73,7 @@ collapse-tags-tooltip @change="handleQuery" > - + @@ -108,6 +103,20 @@ 刷新 + + + + 更多 + + + + + @@ -120,8 +129,8 @@ v-model="batchModalForm.score" :min="0" :max="100" - :precision="2" - :step="0.5" + :precision="0" + :step="1" size="small" placeholder="输入分数" style="width: 100%" @@ -146,6 +155,70 @@ + + +
+
+ 当前录入条件: + {{ queryParams.academicYear - 1 }}-{{ queryParams.academicYear }}学年 + {{ queryParams.semester === 1 ? '第一学期' : '第二学期' }} + {{ queryParams.month }}月 + {{ activeTab ? `(${classTabs.find((tab) => tab.classId.toString() === activeTab)?.className})` : '' }} +
+
+ + 如果学生已有该月份的分数记录,将被Excel中的数据覆盖 +
+
+ + + + + +
将文件拖到此处,或点击上传
+ +
+ +
+ @@ -250,9 +323,11 @@