解决合并冲突
This commit is contained in:
commit
067957256d
263 files changed
+34712
-6492
No files matched your search
+5
-1
@@ -326,4 +326,8 @@ docs/变更管理/
|
||||
查找界面中涉及到的权限字符.md
|
||||
docs/部署文档/nginx配置/hzdyjsweb.camasort.com.conf
|
||||
首页完善的提示词.md
|
||||
scripts/
|
||||
scripts/
|
||||
学生宿舍卫生检查、学生违纪记录统计.md
|
||||
学生调宿、退宿功能.md
|
||||
学生动向信息界面(宿管员).md
|
||||
prompt/
|
||||
@@ -85,6 +85,16 @@
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>prod-local</id>
|
||||
<properties>
|
||||
<!-- 生产环境本地配置 -->
|
||||
<profiles.active>prod-local</profiles.active>
|
||||
<logging.level>warn</logging.level>
|
||||
<monitor.username>ruoyi</monitor.username>
|
||||
<monitor.password>123456</monitor.password>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>prod</id>
|
||||
<properties>
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ public class AssessmentApprovalDetailVo implements Serializable {
|
||||
private String superiorOverallComment;
|
||||
|
||||
/**
|
||||
* 月考核得分
|
||||
* 考核得分
|
||||
*/
|
||||
private BigDecimal monthlyScore;
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class AssessmentDetailVo implements Serializable {
|
||||
private List<SpecialWorkIndicatorVo> specialWorkIndicators;
|
||||
|
||||
/**
|
||||
* 月考核得分
|
||||
* 考核得分
|
||||
*/
|
||||
private BigDecimal monthlyScore;
|
||||
|
||||
|
||||
+25
-6
@@ -426,7 +426,7 @@ public class AssessmentApprovalServiceImpl implements IAssessmentApprovalService
|
||||
AssessmentDetailVo detailVo = teacherAssessmentService.getAssessmentSubmitDetail(
|
||||
approval.getPlanId(), approval.getClassId(), approval.getEvaluatedUserId());
|
||||
|
||||
// 计算月考核得分
|
||||
// 计算考核得分
|
||||
Double monthlyScore = calculateMonthlyScore(detailVo, approval);
|
||||
|
||||
ScoreRankingVo ranking = new ScoreRankingVo();
|
||||
@@ -469,7 +469,7 @@ public class AssessmentApprovalServiceImpl implements IAssessmentApprovalService
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算月考核得分
|
||||
* 计算考核得分
|
||||
*/
|
||||
private Double calculateMonthlyScore(AssessmentDetailVo detailVo, AssessmentApprovalVo approval) {
|
||||
// 总系数(K)
|
||||
@@ -523,10 +523,29 @@ public class AssessmentApprovalServiceImpl implements IAssessmentApprovalService
|
||||
}
|
||||
}
|
||||
|
||||
// 月考核得分 = 总系数(K)* (班主任常规工作评分 - 按班级扣分50%计的分值 + 特色工作评分)
|
||||
// 注意:班级扣分是减法,因为它是扣分项
|
||||
BigDecimal monthlyScore = totalCoefficient.multiply(
|
||||
BigDecimal.valueOf(routineWorkTotal - deductionHalf + specialWorkTotal));
|
||||
// 判断是否为自评模式(如果没有上级评分,则为自评模式)
|
||||
boolean isSelfEvaluationMode = scoreDetail.getClassDeductionScore() != null &&
|
||||
scoreDetail.getClassDeductionScore().getSuperiorScore() == null;
|
||||
|
||||
// 检查是否有实际评分数据(避免空白状态显示50分)
|
||||
boolean hasAnyScore = routineWorkTotal > 0 || deductionHalf > 0 || specialWorkTotal > 0;
|
||||
|
||||
BigDecimal monthlyScore;
|
||||
if (!hasAnyScore || totalCoefficient.compareTo(BigDecimal.ZERO) == 0) {
|
||||
// 没有评分数据时返回0
|
||||
monthlyScore = BigDecimal.ZERO;
|
||||
} else if (isSelfEvaluationMode) {
|
||||
// 自评模式:修正后的考核得分公式 = 总系数(K)* ((班主任常规工作评分 + 班级扣分项满分值) - 按班级扣分50%计的分值 + 特色工作评分)
|
||||
// 班级扣分项满分值(固定50分)- 仅在有评分数据时计算
|
||||
double classDeductionMaxScore = 50.0;
|
||||
monthlyScore = totalCoefficient.multiply(
|
||||
BigDecimal.valueOf((routineWorkTotal + classDeductionMaxScore) - deductionHalf + specialWorkTotal));
|
||||
} else {
|
||||
// 审核模式:考核得分 = 总系数(K)* (班主任常规工作评分 - 按班级扣分50%计的分值 + 特色工作评分)
|
||||
// 注意:班级扣分是减法,因为它是扣分项
|
||||
monthlyScore = totalCoefficient.multiply(
|
||||
BigDecimal.valueOf(routineWorkTotal - deductionHalf + specialWorkTotal));
|
||||
}
|
||||
|
||||
return monthlyScore.setScale(2, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
@@ -124,7 +124,19 @@
|
||||
- 计算公式:K = K1 * (K2 + K3)
|
||||
|
||||
### 2. 月考核得分计算
|
||||
- 计算公式:总系数(K)* (班主任常规工作自评分之和 - 按班级扣分50%计的分值 + 特色工作总分值)
|
||||
|
||||
#### 自评模式(修正后公式)
|
||||
- 计算公式:总系数(K)* ((班主任常规工作自评分之和 + 班级扣分项满分值) - 按班级扣分50%计的分值 + 特色工作总分值)
|
||||
- 班级扣分项满分值:固定50分(来自指标设置)
|
||||
- 修正原因:确保基础分值不受扣分影响,提高计算结果的合理性
|
||||
|
||||
#### 审核模式(保持不变)
|
||||
- 计算公式:总系数(K)* (班主任常规工作上级评分之和 + 班级管理实效上级评分 + 特色工作上级评分之和)
|
||||
|
||||
#### 公式变化对比
|
||||
- **修正前**:K × (常规工作自评分 - 班级扣分50% + 特色工作总分)
|
||||
- **修正后**:K × ((常规工作自评分 + 50) - 班级扣分50% + 特色工作总分)
|
||||
- **关键变化**:新增班级扣分项满分值(50分)确保基础分值
|
||||
|
||||
### 3. 数据存储逻辑
|
||||
1. 先逻辑删除该计划下该教师的历史评分数据
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package org.dromara.cw.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.cw.domain.bo.CwCustomApprovalBo;
|
||||
import org.dromara.cw.domain.bo.CwApprovalActionBo;
|
||||
import org.dromara.cw.domain.vo.CwProjectApprovalProcessVo;
|
||||
import org.dromara.cw.domain.vo.CwApprovalRecordVo;
|
||||
import org.dromara.cw.service.ICwProjectApprovalService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
|
||||
/**
|
||||
* 项目审批流程管理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/cw/projectApproval")
|
||||
public class CwProjectApprovalController extends BaseController {
|
||||
|
||||
private final ICwProjectApprovalService projectApprovalService;
|
||||
|
||||
/**
|
||||
* 启动自定义审批流程
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:start")
|
||||
@Log(title = "启动审批流程", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/start")
|
||||
public R<Void> startApprovalProcess(@Validated @RequestBody CwCustomApprovalBo bo) {
|
||||
// 设置提交人信息
|
||||
bo.setSubmitterId(LoginHelper.getUserId());
|
||||
bo.setSubmitterName(LoginHelper.getUsername());
|
||||
return toAjax(projectApprovalService.startCustomApprovalProcess(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行审批操作
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:approve")
|
||||
@Log(title = "执行审批操作", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/approve")
|
||||
public R<Void> executeApproval(@Validated @RequestBody CwApprovalActionBo bo) {
|
||||
return toAjax(projectApprovalService.executeApproval(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的待审批列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:query")
|
||||
@GetMapping("/pending")
|
||||
public TableDataInfo<CwApprovalRecordVo> getMyPendingApprovals(PageQuery pageQuery) {
|
||||
return projectApprovalService.getMyPendingApprovals(pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的已审批列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:query")
|
||||
@GetMapping("/processed")
|
||||
public TableDataInfo<CwApprovalRecordVo> getMyProcessedApprovals(PageQuery pageQuery) {
|
||||
return projectApprovalService.getMyProcessedApprovals(pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询审批流程列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:query")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<CwProjectApprovalProcessVo> list(PageQuery pageQuery) {
|
||||
return projectApprovalService.queryProcessList(pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批流程详情
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<CwProjectApprovalProcessVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(projectApprovalService.getProcessDetail(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批记录列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:query")
|
||||
@GetMapping("/records/{applicationId}")
|
||||
public R<List<CwApprovalRecordVo>> getApprovalRecords(@PathVariable Long applicationId) {
|
||||
return R.ok(projectApprovalService.getApprovalRecords(applicationId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 终止审批流程
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:terminate")
|
||||
@Log(title = "终止审批流程", businessType = BusinessType.UPDATE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> terminate(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(projectApprovalService.terminateProcess(List.of(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批流程验证
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:query")
|
||||
@PostMapping("/validate")
|
||||
public R<String> validateApprovalProcess(@RequestBody CwCustomApprovalBo bo) {
|
||||
// 简单验证逻辑
|
||||
if (bo.getAuditorIds() == null || bo.getAuditorIds().isEmpty()) {
|
||||
return R.fail("请选择审核人");
|
||||
}
|
||||
if (bo.getAuditorIds().size() > 10) {
|
||||
return R.fail("审核人数量不能超过10个");
|
||||
}
|
||||
return R.ok("验证通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批选项查询(用于下拉选择)
|
||||
*/
|
||||
@SaCheckPermission("cw:projectApproval:query")
|
||||
@GetMapping("/options")
|
||||
public R<Object> getApprovalOptions() {
|
||||
// 返回一些选项数据,比如审批动作选项等
|
||||
return R.ok(java.util.Map.of(
|
||||
"actions", java.util.List.of(
|
||||
java.util.Map.of("value", "approve", "label", "通过"),
|
||||
java.util.Map.of("value", "reject", "label", "拒绝")
|
||||
),
|
||||
"statuses", java.util.List.of(
|
||||
java.util.Map.of("value", "waiting", "label", "等待审批"),
|
||||
java.util.Map.of("value", "approved", "label", "已通过"),
|
||||
java.util.Map.of("value", "rejected", "label", "已拒绝")
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
package org.dromara.cw.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.cw.domain.vo.CwProjectAuditorVo;
|
||||
import org.dromara.cw.domain.bo.CwProjectAuditorBo;
|
||||
import org.dromara.cw.service.ICwProjectAuditorService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
|
||||
/**
|
||||
* 项目审核人配置
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/cw/projectAuditor")
|
||||
public class CwProjectAuditorController extends BaseController {
|
||||
|
||||
private final ICwProjectAuditorService cwProjectAuditorService;
|
||||
|
||||
/**
|
||||
* 查询项目审核人配置列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<CwProjectAuditorVo> list(CwProjectAuditorBo bo, PageQuery pageQuery) {
|
||||
return cwProjectAuditorService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出项目审核人配置列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:export")
|
||||
@Log(title = "项目审核人配置", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(CwProjectAuditorBo bo, HttpServletResponse response) {
|
||||
List<CwProjectAuditorVo> list = cwProjectAuditorService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "项目审核人配置", CwProjectAuditorVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目审核人配置详细信息
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<CwProjectAuditorVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(cwProjectAuditorService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增项目审核人配置
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:add")
|
||||
@Log(title = "项目审核人配置", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody CwProjectAuditorBo bo) {
|
||||
// 设置配置人信息
|
||||
bo.setConfigBy(LoginHelper.getUserId());
|
||||
bo.setConfigByName(LoginHelper.getUsername());
|
||||
return toAjax(cwProjectAuditorService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改项目审核人配置
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:edit")
|
||||
@Log(title = "项目审核人配置", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody CwProjectAuditorBo bo) {
|
||||
return toAjax(cwProjectAuditorService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除项目审核人配置
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:remove")
|
||||
@Log(title = "项目审核人配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(cwProjectAuditorService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加审核人
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:add")
|
||||
@Log(title = "批量添加审核人", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/batchAdd")
|
||||
public R<Void> batchAdd(@RequestBody BatchAddRequest request) {
|
||||
return toAjax(cwProjectAuditorService.batchAddAuditors(
|
||||
request.getUserIds(),
|
||||
LoginHelper.getUserId(),
|
||||
LoginHelper.getUsername(),
|
||||
request.getRemark()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用审核人列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:list")
|
||||
@GetMapping("/available")
|
||||
public R<List<CwProjectAuditorVo>> getAvailableAuditors() {
|
||||
return R.ok(cwProjectAuditorService.getAvailableAuditors());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门ID获取用户列表
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:list")
|
||||
@GetMapping("/deptUsers/{deptId}")
|
||||
public R<List<Object>> getDeptUsers(@PathVariable Long deptId) {
|
||||
// 参数验证
|
||||
if (deptId == null || deptId <= 0) {
|
||||
return R.fail("部门ID参数无效");
|
||||
}
|
||||
return R.ok(cwProjectAuditorService.getDeptUsers(deptId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新状态
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:edit")
|
||||
@Log(title = "批量更新审核人状态", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/status")
|
||||
public R<Void> updateStatus(@RequestBody StatusUpdateRequest request) {
|
||||
return toAjax(cwProjectAuditorService.updateStatus(request.getIds(), request.getStatus()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 我配置的审核人
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:list")
|
||||
@GetMapping("/myConfig")
|
||||
public TableDataInfo<CwProjectAuditorVo> getMyConfig(CwProjectAuditorBo bo, PageQuery pageQuery) {
|
||||
bo.setConfigBy(LoginHelper.getUserId());
|
||||
return cwProjectAuditorService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审核人选项(用于下拉选择)
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:list")
|
||||
@GetMapping("/options")
|
||||
public R<List<CwProjectAuditorVo>> getAuditorOptions() {
|
||||
return R.ok(cwProjectAuditorService.getAvailableAuditors());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量启用
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:edit")
|
||||
@Log(title = "批量启用审核人", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/enable/{ids}")
|
||||
public R<Void> enable(@PathVariable Long[] ids) {
|
||||
return toAjax(cwProjectAuditorService.updateStatus(List.of(ids), "1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量停用
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:edit")
|
||||
@Log(title = "批量停用审核人", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/disable/{ids}")
|
||||
public R<Void> disable(@PathVariable Long[] ids) {
|
||||
return toAjax(cwProjectAuditorService.updateStatus(List.of(ids), "0"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审核人统计数据
|
||||
*/
|
||||
@SaCheckPermission("cw:projectAuditor:list")
|
||||
@GetMapping("/statistics")
|
||||
public R<Object> getStatistics() {
|
||||
return R.ok(cwProjectAuditorService.getStatistics());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加请求对象
|
||||
*/
|
||||
public static class BatchAddRequest {
|
||||
private List<Long> userIds;
|
||||
private String remark;
|
||||
|
||||
public List<Long> getUserIds() {
|
||||
return userIds;
|
||||
}
|
||||
|
||||
public void setUserIds(List<Long> userIds) {
|
||||
this.userIds = userIds;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态更新请求对象
|
||||
*/
|
||||
public static class StatusUpdateRequest {
|
||||
private List<Long> ids;
|
||||
private String status;
|
||||
|
||||
public List<Long> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public void setIds(List<Long> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+16
-1
@@ -124,4 +124,19 @@ public class CwApprovalRecord extends TenantEntity implements Serializable {
|
||||
*/
|
||||
private String configSnapshot;
|
||||
|
||||
}
|
||||
/**
|
||||
* 流程ID
|
||||
*/
|
||||
private Long processId;
|
||||
|
||||
/**
|
||||
* 自定义节点名称
|
||||
*/
|
||||
private String customNodeName;
|
||||
|
||||
/**
|
||||
* 是否自定义流程
|
||||
*/
|
||||
private Boolean isCustomProcess;
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package org.dromara.cw.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 项目审批流程对象 cw_project_approval_process
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("cw_project_approval_process")
|
||||
public class CwProjectApprovalProcess extends TenantEntity implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申报ID
|
||||
*/
|
||||
private Long applicationId;
|
||||
|
||||
/**
|
||||
* 申报编号
|
||||
*/
|
||||
private String applicationNo;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 流程状态(waiting-等待开始 running-进行中 completed-已完成 terminated-已终止)
|
||||
*/
|
||||
private String processStatus;
|
||||
|
||||
/**
|
||||
* 总审批节点数
|
||||
*/
|
||||
private Integer totalNodes;
|
||||
|
||||
/**
|
||||
* 当前审批节点
|
||||
*/
|
||||
private Integer currentNode;
|
||||
|
||||
/**
|
||||
* 流程开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 流程结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 提交人ID
|
||||
*/
|
||||
private Long submitterId;
|
||||
|
||||
/**
|
||||
* 提交人姓名
|
||||
*/
|
||||
private String submitterName;
|
||||
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package org.dromara.cw.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 项目审核人配置对象 cw_project_auditor
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("cw_project_auditor")
|
||||
public class CwProjectAuditor extends TenantEntity implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 所属部门ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 所属部门名称
|
||||
*/
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
private String phoneNumber;
|
||||
|
||||
/**
|
||||
* 邮箱地址
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 审核人状态(1正常 0停用)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注说明
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 配置人ID
|
||||
*/
|
||||
private Long configBy;
|
||||
|
||||
/**
|
||||
* 配置人姓名
|
||||
*/
|
||||
private String configByName;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package org.dromara.cw.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户和部门完整信息
|
||||
* 用于批量添加审核人时获取完整的用户数据
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
public class UserWithDeptInfo {
|
||||
|
||||
/** 用户ID */
|
||||
private Long userId;
|
||||
|
||||
/** 用户账号 */
|
||||
private String userName;
|
||||
|
||||
/** 用户昵称 */
|
||||
private String nickName;
|
||||
|
||||
/** 部门ID */
|
||||
private Long deptId;
|
||||
|
||||
/** 部门名称 */
|
||||
private String deptName;
|
||||
|
||||
/** 手机号码 */
|
||||
private String phonenumber;
|
||||
|
||||
/** 用户邮箱 */
|
||||
private String email;
|
||||
|
||||
/** 帐号状态(1正常 0停用) */
|
||||
private String status;
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.dromara.cw.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 审批操作业务对象
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
public class CwApprovalActionBo {
|
||||
|
||||
/**
|
||||
* 审批记录ID
|
||||
*/
|
||||
@NotNull(message = "审批记录ID不能为空")
|
||||
private Long recordId;
|
||||
|
||||
/**
|
||||
* 审批操作(approve-通过 reject-拒绝)
|
||||
*/
|
||||
@NotBlank(message = "审批操作不能为空")
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* 审批意见
|
||||
*/
|
||||
@NotBlank(message = "审批意见不能为空")
|
||||
private String opinion;
|
||||
|
||||
}
|
||||
+7
-2
@@ -132,13 +132,18 @@ public class CwApprovalRecordBo extends BaseEntity {
|
||||
private String configSnapshot;
|
||||
|
||||
// ========== 查询条件字段 ==========
|
||||
|
||||
|
||||
/**
|
||||
* 部门ID查询条件(用于部门审批页面过滤)
|
||||
* 支持具体部门ID或特殊值 'current'(当前用户部门)
|
||||
*/
|
||||
private String deptId;
|
||||
|
||||
/**
|
||||
* 部门ID(Long类型,用于数据库操作)
|
||||
*/
|
||||
private Long deptIdLong;
|
||||
|
||||
/**
|
||||
* 项目名称(查询条件)
|
||||
*/
|
||||
@@ -164,4 +169,4 @@ public class CwApprovalRecordBo extends BaseEntity {
|
||||
*/
|
||||
private String approvalStatus;
|
||||
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.dromara.cw.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义审批流程业务对象
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
public class CwCustomApprovalBo {
|
||||
|
||||
/**
|
||||
* 申报ID
|
||||
*/
|
||||
@NotNull(message = "申报ID不能为空")
|
||||
private Long applicationId;
|
||||
|
||||
/**
|
||||
* 申报编号
|
||||
*/
|
||||
@NotBlank(message = "申报编号不能为空")
|
||||
private String applicationNo;
|
||||
|
||||
/**
|
||||
* 审核人ID列表
|
||||
*/
|
||||
@NotEmpty(message = "审核人列表不能为空")
|
||||
private List<Long> auditorIds;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
@NotBlank(message = "流程名称不能为空")
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 提交人ID
|
||||
*/
|
||||
@NotNull(message = "提交人ID不能为空")
|
||||
private Long submitterId;
|
||||
|
||||
/**
|
||||
* 提交人姓名
|
||||
*/
|
||||
@NotBlank(message = "提交人姓名不能为空")
|
||||
private String submitterName;
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package org.dromara.cw.domain.bo;
|
||||
|
||||
import org.dromara.cw.domain.CwProjectApprovalProcess;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 项目审批流程业务对象 cw_project_approval_process
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = CwProjectApprovalProcess.class, reverseConvertGenerate = false)
|
||||
public class CwProjectApprovalProcessBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申报ID
|
||||
*/
|
||||
@NotNull(message = "申报ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long applicationId;
|
||||
|
||||
/**
|
||||
* 申报编号
|
||||
*/
|
||||
@NotBlank(message = "申报编号不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String applicationNo;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
@NotBlank(message = "流程名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 流程状态(waiting-等待开始 running-进行中 completed-已完成 terminated-已终止)
|
||||
*/
|
||||
@NotBlank(message = "流程状态不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String processStatus;
|
||||
|
||||
/**
|
||||
* 总审批节点数
|
||||
*/
|
||||
@NotNull(message = "总审批节点数不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer totalNodes;
|
||||
|
||||
/**
|
||||
* 当前审批节点
|
||||
*/
|
||||
private Integer currentNode;
|
||||
|
||||
/**
|
||||
* 流程开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 流程结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 提交人ID
|
||||
*/
|
||||
@NotNull(message = "提交人ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long submitterId;
|
||||
|
||||
/**
|
||||
* 提交人姓名
|
||||
*/
|
||||
@NotBlank(message = "提交人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String submitterName;
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package org.dromara.cw.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量添加审核人业务对象
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
public class CwProjectAuditorBatchBo {
|
||||
|
||||
/**
|
||||
* 用户ID列表
|
||||
*/
|
||||
@NotEmpty(message = "用户ID列表不能为空")
|
||||
private List<Long> userIds;
|
||||
|
||||
/**
|
||||
* 配置人ID
|
||||
*/
|
||||
@NotNull(message = "配置人ID不能为空")
|
||||
private Long configBy;
|
||||
|
||||
/**
|
||||
* 配置人姓名
|
||||
*/
|
||||
@NotBlank(message = "配置人姓名不能为空")
|
||||
private String configByName;
|
||||
|
||||
/**
|
||||
* 备注说明
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package org.dromara.cw.domain.bo;
|
||||
|
||||
import org.dromara.cw.domain.CwProjectAuditor;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目审核人配置业务对象 cw_project_auditor
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = CwProjectAuditor.class, reverseConvertGenerate = false)
|
||||
public class CwProjectAuditorBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@NotNull(message = "用户ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
@NotBlank(message = "用户姓名不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 所属部门ID
|
||||
*/
|
||||
@NotNull(message = "所属部门ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 所属部门名称
|
||||
*/
|
||||
@NotBlank(message = "所属部门名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
private String phoneNumber;
|
||||
|
||||
/**
|
||||
* 邮箱地址
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 审核人状态(1正常 0停用)
|
||||
*/
|
||||
@NotBlank(message = "审核人状态不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注说明
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 配置人ID
|
||||
*/
|
||||
private Long configBy;
|
||||
|
||||
/**
|
||||
* 配置人姓名
|
||||
*/
|
||||
private String configByName;
|
||||
|
||||
}
|
||||
+35
-11
@@ -172,37 +172,37 @@ public class CwApprovalRecordVo implements Serializable {
|
||||
private Date updateTime;
|
||||
|
||||
// ================ 项目申报相关字段(通过关联查询获取) ================
|
||||
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
@ExcelProperty(value = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
|
||||
/**
|
||||
* 申报人姓名
|
||||
*/
|
||||
@ExcelProperty(value = "申报人姓名")
|
||||
private String applicantName;
|
||||
|
||||
|
||||
/**
|
||||
* 申报部门名称
|
||||
*/
|
||||
@ExcelProperty(value = "申报部门")
|
||||
private String deptName;
|
||||
|
||||
|
||||
/**
|
||||
* 总投资(万元)
|
||||
*/
|
||||
@ExcelProperty(value = "总投资(万元)")
|
||||
private java.math.BigDecimal totalInvestment;
|
||||
|
||||
|
||||
/**
|
||||
* 项目负责人
|
||||
*/
|
||||
@ExcelProperty(value = "项目负责人")
|
||||
@ExcelProperty(value = "项目负责人")
|
||||
private String principal;
|
||||
|
||||
|
||||
/**
|
||||
* 部门名称(完整)
|
||||
*/
|
||||
@@ -213,23 +213,47 @@ public class CwApprovalRecordVo implements Serializable {
|
||||
*/
|
||||
@ExcelProperty(value = "当前审批人")
|
||||
private String currentApproverName;
|
||||
|
||||
|
||||
/**
|
||||
* 审批状态(前端表格需要)
|
||||
*/
|
||||
@ExcelProperty(value = "审批状态")
|
||||
private String approvalStatus;
|
||||
|
||||
|
||||
/**
|
||||
* 提交时间(前端表格需要)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date submitTime;
|
||||
|
||||
|
||||
/**
|
||||
* 审批时间(前端表格需要)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date approvalTime;
|
||||
|
||||
}
|
||||
/**
|
||||
* 总节点数(从流程表获取)
|
||||
*/
|
||||
@ExcelProperty(value = "总节点数")
|
||||
private Integer totalNodes;
|
||||
|
||||
/**
|
||||
* 当前节点数(从流程表获取)
|
||||
*/
|
||||
@ExcelProperty(value = "当前节点数")
|
||||
private Integer currentNode;
|
||||
|
||||
/**
|
||||
* 流程名称(从流程表获取)
|
||||
*/
|
||||
@ExcelProperty(value = "流程名称")
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 流程状态(从流程表获取)
|
||||
*/
|
||||
@ExcelProperty(value = "流程状态")
|
||||
private String processStatus;
|
||||
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package org.dromara.cw.domain.vo;
|
||||
|
||||
import org.dromara.cw.domain.CwProjectApprovalProcess;
|
||||
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.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 项目审批流程视图对象 cw_project_approval_process
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = CwProjectApprovalProcess.class)
|
||||
public class CwProjectApprovalProcessVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申报ID
|
||||
*/
|
||||
@ExcelProperty(value = "申报ID")
|
||||
private Long applicationId;
|
||||
|
||||
/**
|
||||
* 申报编号
|
||||
*/
|
||||
@ExcelProperty(value = "申报编号")
|
||||
private String applicationNo;
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
@ExcelProperty(value = "流程名称")
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 流程状态(waiting-等待开始 running-进行中 completed-已完成 terminated-已终止)
|
||||
*/
|
||||
@ExcelProperty(value = "流程状态", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "cw_process_status")
|
||||
private String processStatus;
|
||||
|
||||
/**
|
||||
* 总审批节点数
|
||||
*/
|
||||
@ExcelProperty(value = "总审批节点数")
|
||||
private Integer totalNodes;
|
||||
|
||||
/**
|
||||
* 当前审批节点
|
||||
*/
|
||||
@ExcelProperty(value = "当前审批节点")
|
||||
private Integer currentNode;
|
||||
|
||||
/**
|
||||
* 流程开始时间
|
||||
*/
|
||||
@ExcelProperty(value = "流程开始时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 流程结束时间
|
||||
*/
|
||||
@ExcelProperty(value = "流程结束时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 提交人ID
|
||||
*/
|
||||
@ExcelProperty(value = "提交人ID")
|
||||
private Long submitterId;
|
||||
|
||||
/**
|
||||
* 提交人姓名
|
||||
*/
|
||||
@ExcelProperty(value = "提交人姓名")
|
||||
private String submitterName;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package org.dromara.cw.domain.vo;
|
||||
|
||||
import org.dromara.cw.domain.CwProjectAuditor;
|
||||
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.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 项目审核人配置视图对象 cw_project_auditor
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = CwProjectAuditor.class)
|
||||
public class CwProjectAuditorVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@ExcelProperty(value = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
@ExcelProperty(value = "用户姓名")
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
@ExcelProperty(value = "用户昵称")
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 所属部门ID
|
||||
*/
|
||||
@ExcelProperty(value = "所属部门ID")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 所属部门名称
|
||||
*/
|
||||
@ExcelProperty(value = "所属部门名称")
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
@ExcelProperty(value = "联系电话")
|
||||
private String phoneNumber;
|
||||
|
||||
/**
|
||||
* 邮箱地址
|
||||
*/
|
||||
@ExcelProperty(value = "邮箱地址")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 审核人状态(1正常 0停用)
|
||||
*/
|
||||
@ExcelProperty(value = "审核人状态", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "sys_normal_disable")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注说明
|
||||
*/
|
||||
@ExcelProperty(value = "备注说明")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 配置人ID
|
||||
*/
|
||||
@ExcelProperty(value = "配置人ID")
|
||||
private Long configBy;
|
||||
|
||||
/**
|
||||
* 配置人姓名
|
||||
*/
|
||||
@ExcelProperty(value = "配置人姓名")
|
||||
private String configByName;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.dromara.cw.mapper;
|
||||
|
||||
import org.dromara.cw.domain.CwProjectApprovalProcess;
|
||||
import org.dromara.cw.domain.vo.CwProjectApprovalProcessVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 项目审批流程Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface CwProjectApprovalProcessMapper extends BaseMapperPlus<CwProjectApprovalProcess, CwProjectApprovalProcessVo> {
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package org.dromara.cw.mapper;
|
||||
|
||||
import org.dromara.cw.domain.CwProjectAuditor;
|
||||
import org.dromara.cw.domain.UserWithDeptInfo;
|
||||
import org.dromara.cw.domain.vo.CwProjectAuditorVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目审核人配置Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface CwProjectAuditorMapper extends BaseMapperPlus<CwProjectAuditor, CwProjectAuditorVo> {
|
||||
|
||||
/**
|
||||
* 根据部门ID查询可用审核人
|
||||
*/
|
||||
List<CwProjectAuditorVo> selectAuditorsByDeptId(@Param("deptId") Long deptId);
|
||||
|
||||
/**
|
||||
* 查询审核人统计信息
|
||||
*/
|
||||
List<CwProjectAuditorVo> selectAuditorStatistics();
|
||||
|
||||
/**
|
||||
* 查询完整的用户信息(包含部门信息)用于批量添加
|
||||
*/
|
||||
List<UserWithDeptInfo> selectUsersWithDeptInfo(@Param("userIds") List<Long> userIds);
|
||||
|
||||
}
|
||||
+20
@@ -2,6 +2,7 @@ package org.dromara.cw.service;
|
||||
|
||||
import org.dromara.cw.domain.bo.CwApprovalRecordBo;
|
||||
import org.dromara.cw.domain.vo.CwApprovalRecordVo;
|
||||
import org.dromara.cw.domain.CwApprovalRecord;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
@@ -119,6 +120,11 @@ public interface ICwApprovalRecordService {
|
||||
*/
|
||||
Boolean insertByBo(CwApprovalRecordBo bo);
|
||||
|
||||
/**
|
||||
* 批量新增审批记录
|
||||
*/
|
||||
Boolean batchInsert(List<CwApprovalRecord> records);
|
||||
|
||||
/**
|
||||
* 修改审批记录
|
||||
*/
|
||||
@@ -190,4 +196,18 @@ public interface ICwApprovalRecordService {
|
||||
*/
|
||||
List<Map<String, Object>> getAvailableApprovers(Long applicationId);
|
||||
|
||||
/**
|
||||
* 创建自定义审批流程
|
||||
* 根据用户选择的审批人列表创建自定义审批流程
|
||||
*
|
||||
* @param applicationId 申报ID
|
||||
* @param applicationNo 申报编号
|
||||
* @param auditorIds 审批人ID列表(按审批顺序排列)
|
||||
* @param submitterId 提交人ID
|
||||
* @param submitterName 提交人名称
|
||||
* @return 是否创建成功
|
||||
*/
|
||||
Boolean createCustomApprovalProcess(Long applicationId, String applicationNo,
|
||||
List<Long> auditorIds, Long submitterId, String submitterName);
|
||||
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package org.dromara.cw.service;
|
||||
|
||||
import org.dromara.cw.domain.vo.CwProjectApprovalProcessVo;
|
||||
import org.dromara.cw.domain.bo.CwCustomApprovalBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目审批流程Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface ICwProjectApprovalProcessService {
|
||||
|
||||
/**
|
||||
* 查询项目审批流程
|
||||
*/
|
||||
CwProjectApprovalProcessVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询项目审批流程列表
|
||||
*/
|
||||
TableDataInfo<CwProjectApprovalProcessVo> queryPageList(CwProjectApprovalProcessVo vo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询项目审批流程列表
|
||||
*/
|
||||
List<CwProjectApprovalProcessVo> queryList(CwProjectApprovalProcessVo vo);
|
||||
|
||||
/**
|
||||
* 根据申报ID查询审批流程
|
||||
*/
|
||||
CwProjectApprovalProcessVo queryByApplicationId(Long applicationId);
|
||||
|
||||
/**
|
||||
* 根据申报编号查询审批流程
|
||||
*/
|
||||
CwProjectApprovalProcessVo queryByApplicationNo(String applicationNo);
|
||||
|
||||
/**
|
||||
* 查询我提交的审批流程
|
||||
*/
|
||||
List<CwProjectApprovalProcessVo> queryMyProcesses(Long submitterId);
|
||||
|
||||
/**
|
||||
* 查询进行中的审批流程
|
||||
*/
|
||||
List<CwProjectApprovalProcessVo> queryRunningProcesses();
|
||||
|
||||
/**
|
||||
* 查询超时的审批流程
|
||||
*/
|
||||
List<CwProjectApprovalProcessVo> queryOvertimeProcesses(Integer hours);
|
||||
|
||||
/**
|
||||
* 启动自定义审批流程
|
||||
*/
|
||||
Boolean startCustomApprovalProcess(CwCustomApprovalBo bo);
|
||||
|
||||
/**
|
||||
* 终止审批流程
|
||||
*/
|
||||
Boolean terminateProcess(Long id, String reason);
|
||||
|
||||
/**
|
||||
* 校验并批量删除项目审批流程信息
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 推进流程到下一节点
|
||||
*/
|
||||
Boolean advanceToNextNode(Long processId);
|
||||
|
||||
/**
|
||||
* 完成审批流程
|
||||
*/
|
||||
Boolean completeProcess(Long processId);
|
||||
|
||||
/**
|
||||
* 更新流程当前节点
|
||||
*/
|
||||
Boolean updateCurrentNode(Long id, Integer currentNode);
|
||||
|
||||
/**
|
||||
* 更新流程状态
|
||||
*/
|
||||
Boolean updateProcessStatus(Long id, String status);
|
||||
|
||||
/**
|
||||
* 验证审批流程配置
|
||||
*/
|
||||
Boolean validateApprovalProcess(CwCustomApprovalBo bo);
|
||||
|
||||
/**
|
||||
* 统计审批流程状态
|
||||
*/
|
||||
List<Map<String, Object>> queryProcessStatistics(Long submitterId);
|
||||
|
||||
/**
|
||||
* 导出数据
|
||||
*/
|
||||
List<CwProjectApprovalProcessVo> exportList(CwProjectApprovalProcessVo vo);
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package org.dromara.cw.service;
|
||||
|
||||
import org.dromara.cw.domain.bo.CwCustomApprovalBo;
|
||||
import org.dromara.cw.domain.bo.CwApprovalActionBo;
|
||||
import org.dromara.cw.domain.vo.CwProjectApprovalProcessVo;
|
||||
import org.dromara.cw.domain.vo.CwApprovalRecordVo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目审批流程Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface ICwProjectApprovalService {
|
||||
|
||||
/**
|
||||
* 启动自定义审批流程
|
||||
*/
|
||||
Boolean startCustomApprovalProcess(CwCustomApprovalBo bo);
|
||||
|
||||
/**
|
||||
* 执行审批操作
|
||||
*/
|
||||
Boolean executeApproval(CwApprovalActionBo bo);
|
||||
|
||||
/**
|
||||
* 获取我的待审批列表
|
||||
*/
|
||||
TableDataInfo<CwApprovalRecordVo> getMyPendingApprovals(PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 获取我的已审批列表
|
||||
*/
|
||||
TableDataInfo<CwApprovalRecordVo> getMyProcessedApprovals(PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询审批流程列表
|
||||
*/
|
||||
TableDataInfo<CwProjectApprovalProcessVo> queryProcessList(PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 获取审批流程详情
|
||||
*/
|
||||
CwProjectApprovalProcessVo getProcessDetail(Long processId);
|
||||
|
||||
/**
|
||||
* 获取审批记录列表
|
||||
*/
|
||||
List<CwApprovalRecordVo> getApprovalRecords(Long applicationId);
|
||||
|
||||
/**
|
||||
* 终止审批流程
|
||||
*/
|
||||
Boolean terminateProcess(List<Long> processIds);
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.dromara.cw.service;
|
||||
|
||||
import org.dromara.cw.domain.vo.CwProjectAuditorVo;
|
||||
import org.dromara.cw.domain.bo.CwProjectAuditorBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目审核人配置Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface ICwProjectAuditorService {
|
||||
|
||||
/**
|
||||
* 查询项目审核人配置
|
||||
*/
|
||||
CwProjectAuditorVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询项目审核人配置列表
|
||||
*/
|
||||
TableDataInfo<CwProjectAuditorVo> queryPageList(CwProjectAuditorBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询项目审核人配置列表
|
||||
*/
|
||||
List<CwProjectAuditorVo> queryList(CwProjectAuditorBo bo);
|
||||
|
||||
/**
|
||||
* 新增项目审核人配置
|
||||
*/
|
||||
Boolean insertByBo(CwProjectAuditorBo bo);
|
||||
|
||||
/**
|
||||
* 修改项目审核人配置
|
||||
*/
|
||||
Boolean updateByBo(CwProjectAuditorBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除项目审核人配置信息
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 批量添加审核人(从部门用户中选择)
|
||||
*/
|
||||
Boolean batchAddAuditors(List<Long> userIds, Long configBy, String configByName, String remark);
|
||||
|
||||
/**
|
||||
* 获取可用审核人列表
|
||||
*/
|
||||
List<CwProjectAuditorVo> getAvailableAuditors();
|
||||
|
||||
/**
|
||||
* 根据部门ID获取用户列表
|
||||
*/
|
||||
List<Object> getDeptUsers(Long deptId);
|
||||
|
||||
/**
|
||||
* 批量更新状态
|
||||
*/
|
||||
Boolean updateStatus(List<Long> ids, String status);
|
||||
|
||||
/**
|
||||
* 获取审核人统计数据
|
||||
*/
|
||||
Object getStatistics();
|
||||
|
||||
/**
|
||||
* 根据用户ID查询项目审核人配置
|
||||
*/
|
||||
CwProjectAuditorVo queryByUserId(Long userId);
|
||||
|
||||
}
|
||||
+238
-24
@@ -5,13 +5,12 @@ 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.mybatis.helper.DataPermissionHelper;
|
||||
import org.dromara.common.redis.utils.SequenceUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
|
||||
import java.time.Duration;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -25,8 +24,10 @@ import org.dromara.cw.domain.CwApprovalRecord;
|
||||
import org.dromara.cw.mapper.CwApprovalRecordMapper;
|
||||
import org.dromara.cw.service.ICwApprovalRecordService;
|
||||
import org.dromara.cw.service.ICwProjectApplicationService;
|
||||
import org.dromara.cw.service.ICwProjectAuditorService;
|
||||
import org.dromara.cw.mapper.CwProjectApplicationMapper;
|
||||
import org.dromara.cw.domain.CwProjectApplication;
|
||||
import org.dromara.cw.domain.vo.CwProjectAuditorVo;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
import org.dromara.system.service.ISysDeptService;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
@@ -54,6 +55,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService {
|
||||
private final CwProjectApplicationMapper applicationMapper;
|
||||
private final ISysUserService sysUserService;
|
||||
private final ISysDeptService sysDeptService;
|
||||
private final ICwProjectAuditorService projectAuditorService;
|
||||
|
||||
/**
|
||||
* 根据主键查询审批记录
|
||||
@@ -502,6 +504,17 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService {
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量新增审批记录
|
||||
*/
|
||||
@Override
|
||||
public Boolean batchInsert(List<CwApprovalRecord> records) {
|
||||
if (records == null || records.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return baseMapper.batchInsert(records) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改审批记录
|
||||
*/
|
||||
@@ -678,8 +691,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService {
|
||||
Long currentDeptId, String currentTenantId) {
|
||||
CwApprovalRecord record = new CwApprovalRecord();
|
||||
|
||||
// 生成唯一ID(批量插入需要手动生成)
|
||||
record.setId(SequenceUtils.nextId("approval_record", Duration.ofDays(1)));
|
||||
// 移除手动ID设置,使用MyBatis-Plus自动生成雪花ID(符合项目规范)
|
||||
// record.setId() - 让MyBatis-Plus自动生成,避免主键冲突
|
||||
|
||||
// 设置申报相关信息
|
||||
record.setApplicationId(applicationId);
|
||||
@@ -979,27 +992,110 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService {
|
||||
}
|
||||
|
||||
private Boolean updateApplicationStatusAndProcess(Long applicationId, String nodeCode, String approveAction) {
|
||||
if ("approve".equals(approveAction)) {
|
||||
// 审批通过,启动下一个节点或完成审批
|
||||
if ("dept_approve".equals(nodeCode)) {
|
||||
// 部门审批通过,启动财务审批
|
||||
return startNextApprovalNode(applicationId, "finance_approve");
|
||||
} else if ("finance_approve".equals(nodeCode)) {
|
||||
// 财务审批通过,启动领导审批
|
||||
return startNextApprovalNode(applicationId, "leader_approve");
|
||||
} else if ("leader_approve".equals(nodeCode)) {
|
||||
// 领导审批通过,整个审批流程完成
|
||||
return projectApplicationService.updateApprovalStatus(applicationId, "approved");
|
||||
}
|
||||
} else if ("reject".equals(approveAction)) {
|
||||
// 审批拒绝,整个流程结束
|
||||
return projectApplicationService.updateApprovalStatus(applicationId, "rejected");
|
||||
} else if ("return".equals(approveAction)) {
|
||||
// 退回修改,申报状态改为已退回
|
||||
return projectApplicationService.updateApprovalStatus(applicationId, "returned");
|
||||
}
|
||||
try {
|
||||
// 获取当前审批记录
|
||||
LambdaQueryWrapper<CwApprovalRecord> currentQuery = Wrappers.<CwApprovalRecord>lambdaQuery()
|
||||
.eq(CwApprovalRecord::getApplicationId, applicationId)
|
||||
.eq(CwApprovalRecord::getNodeCode, nodeCode);
|
||||
CwApprovalRecord currentRecord = baseMapper.selectOne(currentQuery);
|
||||
|
||||
return true;
|
||||
if (currentRecord == null) {
|
||||
log.error("❌ 当前审批记录不存在:applicationId={}, nodeCode={}", applicationId, nodeCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("approve".equals(approveAction)) {
|
||||
// 🔧 基于node_order的顺序审批逻辑
|
||||
log.info("✅ 审批通过,查找下一级审批:applicationId={}, currentOrder={}",
|
||||
applicationId, currentRecord.getNodeOrder());
|
||||
|
||||
// 查找下一级审批记录(node_order + 1)
|
||||
LambdaQueryWrapper<CwApprovalRecord> nextQuery = Wrappers.<CwApprovalRecord>lambdaQuery()
|
||||
.eq(CwApprovalRecord::getApplicationId, applicationId)
|
||||
.eq(CwApprovalRecord::getNodeOrder, currentRecord.getNodeOrder() + 1);
|
||||
CwApprovalRecord nextRecord = baseMapper.selectOne(nextQuery);
|
||||
|
||||
if (nextRecord != null) {
|
||||
// 存在下一级审批,激活下一级
|
||||
log.info("📋 激活下一级审批:nextRecordId={}, nodeOrder={}, nodeCode={}, currentStatus={}",
|
||||
nextRecord.getId(), nextRecord.getNodeOrder(), nextRecord.getNodeCode(), nextRecord.getNodeStatus());
|
||||
|
||||
// 使用条件更新,确保只有在特定状态下才能激活
|
||||
LambdaUpdateWrapper<CwApprovalRecord> updateWrapper = Wrappers.<CwApprovalRecord>lambdaUpdate()
|
||||
.eq(CwApprovalRecord::getId, nextRecord.getId())
|
||||
.eq(CwApprovalRecord::getApplicationId, applicationId)
|
||||
.ne(CwApprovalRecord::getNodeStatus, "waiting") // 避免重复激活已经是waiting状态的记录
|
||||
.ne(CwApprovalRecord::getNodeStatus, "approved") // 避免激活已完成的记录
|
||||
.ne(CwApprovalRecord::getNodeStatus, "rejected") // 避免激活已拒绝的记录
|
||||
.set(CwApprovalRecord::getNodeStatus, "waiting")
|
||||
.set(CwApprovalRecord::getStartTime, new Date()) // 设置开始时间
|
||||
.set(CwApprovalRecord::getUpdateTime, new Date())
|
||||
.set(CwApprovalRecord::getUpdateBy, LoginHelper.getUserId());
|
||||
|
||||
int updateResult = baseMapper.update(null, updateWrapper);
|
||||
|
||||
if (updateResult <= 0) {
|
||||
log.error("❌ 激活下一级审批失败: applicationId={}, nextRecordId={}, nodeOrder={}, nodeCode={}",
|
||||
applicationId, nextRecord.getId(), nextRecord.getNodeOrder(), nextRecord.getNodeCode());
|
||||
|
||||
// 重新查询记录状态,分析失败原因
|
||||
CwApprovalRecord checkRecord = baseMapper.selectById(nextRecord.getId());
|
||||
if (checkRecord == null) {
|
||||
log.error("❌ 下一级审批记录已不存在: recordId={}", nextRecord.getId());
|
||||
} else {
|
||||
log.error("❌ 下一级审批记录状态不符合激活条件: recordId={}, currentStatus={}, 限制条件=不能是[waiting,approved,rejected]",
|
||||
nextRecord.getId(), checkRecord.getNodeStatus());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("✅ 下一级审批已激活成功: recordId={}, newStatus=waiting", nextRecord.getId());
|
||||
return true;
|
||||
} else {
|
||||
// 没有下一级审批,整个流程完成
|
||||
log.info("🎉 所有审批完成,更新申报状态为approved: applicationId={}, currentOrder={}",
|
||||
applicationId, currentRecord.getNodeOrder());
|
||||
|
||||
boolean statusUpdateResult = projectApplicationService.updateApprovalStatus(applicationId, "approved");
|
||||
if (!statusUpdateResult) {
|
||||
log.error("❌ 更新申报状态为approved失败: applicationId={}", applicationId);
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("✅ 申报状态已成功更新为approved: applicationId={}", applicationId);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if ("reject".equals(approveAction)) {
|
||||
// 审批拒绝,整个流程结束
|
||||
log.info("❌ 审批拒绝,更新申报状态为rejected: applicationId={}", applicationId);
|
||||
boolean rejectResult = projectApplicationService.updateApprovalStatus(applicationId, "rejected");
|
||||
if (!rejectResult) {
|
||||
log.error("❌ 更新申报状态为rejected失败: applicationId={}", applicationId);
|
||||
return false;
|
||||
}
|
||||
log.info("✅ 申报状态已更新为rejected: applicationId={}", applicationId);
|
||||
return true;
|
||||
|
||||
} else if ("return".equals(approveAction)) {
|
||||
// 退回修改,申报状态改为已退回,保留历史审批记录
|
||||
log.info("↩️ 审批退回,更新申报状态为returned: applicationId={}", applicationId);
|
||||
boolean returnResult = projectApplicationService.updateApprovalStatus(applicationId, "returned");
|
||||
if (!returnResult) {
|
||||
log.error("❌ 更新申报状态为returned失败: applicationId={}", applicationId);
|
||||
return false;
|
||||
}
|
||||
log.info("✅ 申报状态已更新为returned: applicationId={}", applicationId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 更新申报状态和流程失败:applicationId={}, nodeCode={}, action={}, error={}",
|
||||
applicationId, nodeCode, approveAction, e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean startNextApprovalNode(Long applicationId, String nextNodeCode) {
|
||||
@@ -1450,4 +1546,122 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService {
|
||||
throw new RuntimeException("指定院领导审批人失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义审批流程
|
||||
* 根据用户选择的审批人列表创建自定义审批流程
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean createCustomApprovalProcess(Long applicationId, String applicationNo,
|
||||
List<Long> auditorIds, Long submitterId, String submitterName) {
|
||||
|
||||
log.info("🚀 创建自定义审批流程:applicationId={}, auditorIds={}", applicationId, auditorIds);
|
||||
|
||||
try {
|
||||
// 1. 验证参数
|
||||
if (applicationId == null) {
|
||||
throw new RuntimeException("申报ID不能为空");
|
||||
}
|
||||
if (auditorIds == null || auditorIds.isEmpty()) {
|
||||
throw new RuntimeException("审批人列表不能为空");
|
||||
}
|
||||
|
||||
// 2. 验证审批人是否有效
|
||||
List<CwProjectAuditorVo> validAuditors = validateAuditors(auditorIds);
|
||||
if (validAuditors.size() != auditorIds.size()) {
|
||||
throw new RuntimeException("包含无效的审批人,请重新选择");
|
||||
}
|
||||
|
||||
// 3. 创建自定义审批记录
|
||||
List<CwApprovalRecord> records = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < auditorIds.size(); i++) {
|
||||
Long auditorId = auditorIds.get(i);
|
||||
CwProjectAuditorVo auditor = validAuditors.get(i);
|
||||
|
||||
CwApprovalRecord record = new CwApprovalRecord();
|
||||
// 移除手动ID设置,使用MyBatis-Plus自动生成雪花ID(符合项目规范)
|
||||
// record.setId() - 让MyBatis-Plus自动生成,避免主键冲突
|
||||
record.setApplicationId(applicationId);
|
||||
record.setApplicationNo(applicationNo);
|
||||
record.setNodeCode("custom_node_" + (i + 1));
|
||||
record.setNodeName("自定义审批节点" + (i + 1));
|
||||
record.setNodeOrder(i + 1);
|
||||
record.setApproverId(auditorId);
|
||||
record.setApproverName(auditor.getUserName());
|
||||
record.setNodeStatus(i == 0 ? "waiting" : "pending"); // 第一个waiting,其他pending
|
||||
|
||||
// 设置基础字段
|
||||
Date now = new Date();
|
||||
record.setCreateBy(submitterId);
|
||||
record.setCreateTime(now);
|
||||
record.setUpdateBy(submitterId);
|
||||
record.setUpdateTime(now);
|
||||
record.setTenantId(LoginHelper.getTenantId());
|
||||
record.setIsDelegate(false);
|
||||
record.setIsOvertime(false);
|
||||
|
||||
// 🔧 修复:设置startTime字段(数据库NOT NULL约束)
|
||||
if (i == 0) {
|
||||
// 第一个节点:waiting状态,立即开始
|
||||
record.setStartTime(now);
|
||||
log.info("设置自定义审批第一个节点开始时间:{} -> {}", record.getNodeCode(), now);
|
||||
} else {
|
||||
// 后续节点:pending状态,也需要设置startTime满足数据库约束
|
||||
record.setStartTime(now);
|
||||
log.info("设置自定义审批后续节点开始时间:{} -> {} (满足数据库约束)", record.getNodeCode(), now);
|
||||
}
|
||||
|
||||
records.add(record);
|
||||
}
|
||||
|
||||
// 4. 批量插入审批记录
|
||||
boolean result = false;
|
||||
if (!records.isEmpty()) {
|
||||
for (CwApprovalRecord record : records) {
|
||||
result = baseMapper.insert(record) > 0;
|
||||
if (!result) {
|
||||
throw new RuntimeException("创建审批记录失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 同步更新项目申报状态为"审批中"
|
||||
// 这是关键步骤:启动审批流程的同时提交申报
|
||||
ICwProjectApplicationService applicationService = SpringUtils.getBean(ICwProjectApplicationService.class);
|
||||
boolean statusUpdateResult = applicationService.updateApprovalStatus(applicationId, "approving");
|
||||
if (!statusUpdateResult) {
|
||||
throw new RuntimeException("更新申报状态失败,审批流程创建失败");
|
||||
}
|
||||
|
||||
log.info("✅ 自定义审批流程创建成功:共创建{}个审批节点", records.size());
|
||||
log.info("✅ 申报状态已更新为审批中:applicationId={}", applicationId);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 创建自定义审批流程失败:{}", e.getMessage(), e);
|
||||
throw new RuntimeException("创建审批流程失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证审批人列表
|
||||
*/
|
||||
private List<CwProjectAuditorVo> validateAuditors(List<Long> auditorIds) {
|
||||
List<CwProjectAuditorVo> validAuditors = new ArrayList<>();
|
||||
|
||||
for (Long auditorId : auditorIds) {
|
||||
CwProjectAuditorVo auditor = projectAuditorService.queryByUserId(auditorId);
|
||||
if (auditor == null) {
|
||||
throw new RuntimeException("审批人不存在:" + auditorId);
|
||||
}
|
||||
if (!"1".equals(auditor.getStatus())) {
|
||||
throw new RuntimeException("审批人已停用:" + auditor.getUserName());
|
||||
}
|
||||
validAuditors.add(auditor);
|
||||
}
|
||||
|
||||
return validAuditors;
|
||||
}
|
||||
}
|
||||
+68
-15
@@ -19,8 +19,8 @@ import org.dromara.cw.domain.CwProjectApplication;
|
||||
import org.dromara.cw.mapper.CwProjectApplicationMapper;
|
||||
import org.dromara.cw.service.ICwProjectApplicationService;
|
||||
import org.dromara.cw.service.ICwPerformanceIndicatorService;
|
||||
import org.dromara.cw.event.ApprovalProcessCreatedEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.dromara.cw.service.ICwApprovalRecordService;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
@@ -41,7 +41,6 @@ public class CwProjectApplicationServiceImpl implements ICwProjectApplicationSer
|
||||
|
||||
private final CwProjectApplicationMapper baseMapper;
|
||||
private final ICwPerformanceIndicatorService performanceIndicatorService;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* 查询项目预算申报
|
||||
@@ -584,30 +583,34 @@ public class CwProjectApplicationServiceImpl implements ICwProjectApplicationSer
|
||||
}
|
||||
|
||||
if (updateResult) {
|
||||
// 异步发布审批流程创建事件(避免阻塞主流程)
|
||||
// 直接同步创建审批流程(确保事务一致性)
|
||||
try {
|
||||
log.info("🚀 准备异步发布审批流程创建事件:applicationId={}, applicationNo={}", id, application.getApplicationNo());
|
||||
log.info("🚀 开始同步创建审批流程:applicationId={}, applicationNo={}", id, application.getApplicationNo());
|
||||
|
||||
// 在主线程中收集用户上下文信息(异步线程中无法获取)
|
||||
// 收集用户上下文信息
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
String currentUserName = LoginHelper.getUsername();
|
||||
Long currentDeptId = LoginHelper.getDeptId();
|
||||
String currentTenantId = LoginHelper.getTenantId();
|
||||
|
||||
log.info("📋 收集用户上下文:userId={}, userName={}, deptId={}, tenantId={}",
|
||||
log.info("📋 用户上下文:userId={}, userName={}, deptId={}, tenantId={}",
|
||||
currentUserId, currentUserName, currentDeptId, currentTenantId);
|
||||
|
||||
ApprovalProcessCreatedEvent event = new ApprovalProcessCreatedEvent(
|
||||
this, id, application.getApplicationNo(),
|
||||
// 使用SpringUtils动态获取服务,避免循环依赖
|
||||
ICwApprovalRecordService approvalRecordService = SpringUtils.getBean(ICwApprovalRecordService.class);
|
||||
Boolean createResult = approvalRecordService.createApprovalProcess(
|
||||
id, application.getApplicationNo(),
|
||||
currentUserId, currentUserName, currentDeptId, currentTenantId);
|
||||
|
||||
log.info("📤 异步发布事件(不阻塞主流程)...");
|
||||
eventPublisher.publishEvent(event);
|
||||
log.info("✅ 审批流程创建事件发布完成(异步处理中):applicationId={}, applicationNo={}",
|
||||
id, application.getApplicationNo());
|
||||
if (createResult) {
|
||||
log.info("✅ 审批流程创建成功:applicationId={}, applicationNo={}", id, application.getApplicationNo());
|
||||
} else {
|
||||
log.error("❌ 审批流程创建失败:applicationId={}, applicationNo={}", id, application.getApplicationNo());
|
||||
throw new RuntimeException("审批流程创建失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("💥 发布审批流程创建事件失败:applicationId={}, error={}", id, e.getMessage(), e);
|
||||
// 事件发布失败不影响提交,审批流程会在后台异步创建
|
||||
log.error("💥 创建审批流程异常:applicationId={}, error={}", id, e.getMessage(), e);
|
||||
throw new RuntimeException("创建审批流程失败:" + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
log.error("❌ 申报提交失败,数据库更新返回false:applicationId={}", id);
|
||||
@@ -667,16 +670,66 @@ public class CwProjectApplicationServiceImpl implements ICwProjectApplicationSer
|
||||
throw new RuntimeException("申报记录不存在");
|
||||
}
|
||||
|
||||
// 验证状态转换的合法性
|
||||
String currentStatus = application.getApprovalStatus();
|
||||
log.info("更新申报状态:applicationId={}, {} -> {}", id, currentStatus, approvalStatus);
|
||||
|
||||
// 当从草稿状态提交到审批中时,进行数据完整性验证和字段设置
|
||||
if ("draft".equals(currentStatus) && "approving".equals(approvalStatus)) {
|
||||
// 验证申报数据完整性
|
||||
validateApplicationForSubmission(application);
|
||||
|
||||
// 设置提交时间(第一次提交)
|
||||
if (application.getSubmitTime() == null) {
|
||||
application.setSubmitTime(new Date());
|
||||
log.info("设置申报提交时间:applicationId={}", id);
|
||||
}
|
||||
|
||||
// 生成申报编号(如果还没有)
|
||||
if (StringUtils.isBlank(application.getApplicationNo())) {
|
||||
String applicationNo = generateApplicationNo();
|
||||
application.setApplicationNo(applicationNo);
|
||||
log.info("生成申报编号:applicationId={}, applicationNo={}", id, applicationNo);
|
||||
}
|
||||
|
||||
log.info("✅ 申报正式提交:从草稿状态提交到审批中");
|
||||
}
|
||||
|
||||
// 更新审批状态
|
||||
application.setApprovalStatus(approvalStatus);
|
||||
|
||||
// 如果是最终通过或拒绝状态,设置审批完成时间
|
||||
if ("approved".equals(approvalStatus) || "rejected".equals(approvalStatus)) {
|
||||
application.setApproveTime(new Date());
|
||||
log.info("设置审批完成时间:applicationId={}, status={}", id, approvalStatus);
|
||||
}
|
||||
|
||||
return baseMapper.updateById(application) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证申报数据完整性(提交时验证)
|
||||
*/
|
||||
private void validateApplicationForSubmission(CwProjectApplication application) {
|
||||
if (StringUtils.isBlank(application.getProjectName())) {
|
||||
throw new RuntimeException("项目名称不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(application.getPrincipal())) {
|
||||
throw new RuntimeException("项目负责人不能为空");
|
||||
}
|
||||
if (application.getTotalInvestment() == null || application.getTotalInvestment().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("项目总投资必须大于0");
|
||||
}
|
||||
if (application.getYear() == null) {
|
||||
throw new RuntimeException("预算年度不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(application.getApplicationReason())) {
|
||||
throw new RuntimeException("项目申请原因不能为空");
|
||||
}
|
||||
|
||||
log.info("✅ 申报数据完整性验证通过:applicationId={}", application.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成申报编号
|
||||
*/
|
||||
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
package org.dromara.cw.service.impl;
|
||||
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.cw.domain.bo.CwProjectApprovalProcessBo;
|
||||
import org.dromara.cw.domain.bo.CwCustomApprovalBo;
|
||||
import org.dromara.cw.domain.vo.CwProjectApprovalProcessVo;
|
||||
import org.dromara.cw.domain.CwProjectApprovalProcess;
|
||||
import org.dromara.cw.mapper.CwProjectApprovalProcessMapper;
|
||||
import org.dromara.cw.service.ICwProjectApprovalProcessService;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 项目审批流程Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class CwProjectApprovalProcessServiceImpl implements ICwProjectApprovalProcessService {
|
||||
|
||||
private final CwProjectApprovalProcessMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询项目审批流程
|
||||
*/
|
||||
@Override
|
||||
public CwProjectApprovalProcessVo queryById(Long id){
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目审批流程列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<CwProjectApprovalProcessVo> queryPageList(CwProjectApprovalProcessVo vo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> lqw = buildQueryWrapper(vo);
|
||||
Page<CwProjectApprovalProcessVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目审批流程列表
|
||||
*/
|
||||
@Override
|
||||
public List<CwProjectApprovalProcessVo> queryList(CwProjectApprovalProcessVo vo) {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> lqw = buildQueryWrapper(vo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<CwProjectApprovalProcess> buildQueryWrapper(CwProjectApprovalProcessVo vo) {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(vo.getApplicationId() != null, CwProjectApprovalProcess::getApplicationId, vo.getApplicationId());
|
||||
lqw.like(StringUtils.isNotBlank(vo.getApplicationNo()), CwProjectApprovalProcess::getApplicationNo, vo.getApplicationNo());
|
||||
lqw.like(StringUtils.isNotBlank(vo.getProcessName()), CwProjectApprovalProcess::getProcessName, vo.getProcessName());
|
||||
lqw.eq(StringUtils.isNotBlank(vo.getProcessStatus()), CwProjectApprovalProcess::getProcessStatus, vo.getProcessStatus());
|
||||
lqw.eq(vo.getTotalNodes() != null, CwProjectApprovalProcess::getTotalNodes, vo.getTotalNodes());
|
||||
lqw.eq(vo.getCurrentNode() != null, CwProjectApprovalProcess::getCurrentNode, vo.getCurrentNode());
|
||||
lqw.eq(vo.getSubmitterId() != null, CwProjectApprovalProcess::getSubmitterId, vo.getSubmitterId());
|
||||
lqw.like(StringUtils.isNotBlank(vo.getSubmitterName()), CwProjectApprovalProcess::getSubmitterName, vo.getSubmitterName());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(CwProjectApprovalProcess entity){
|
||||
// 检查申报ID是否已存在流程
|
||||
if (entity.getApplicationId() != null) {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectApprovalProcess::getApplicationId, entity.getApplicationId());
|
||||
if (entity.getId() != null) {
|
||||
wrapper.ne(CwProjectApprovalProcess::getId, entity.getId());
|
||||
}
|
||||
if (baseMapper.exists(wrapper)) {
|
||||
throw new RuntimeException("该申报已存在审批流程");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除项目审批流程
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据申报ID查询审批流程
|
||||
*/
|
||||
@Override
|
||||
public CwProjectApprovalProcessVo queryByApplicationId(Long applicationId) {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectApprovalProcess::getApplicationId, applicationId);
|
||||
return baseMapper.selectVoOne(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据申报编号查询审批流程
|
||||
*/
|
||||
@Override
|
||||
public CwProjectApprovalProcessVo queryByApplicationNo(String applicationNo) {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectApprovalProcess::getApplicationNo, applicationNo);
|
||||
return baseMapper.selectVoOne(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我提交的流程
|
||||
*/
|
||||
@Override
|
||||
public List<CwProjectApprovalProcessVo> queryMyProcesses(Long submitterId) {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectApprovalProcess::getSubmitterId, submitterId);
|
||||
wrapper.orderByDesc(CwProjectApprovalProcess::getCreateTime);
|
||||
return baseMapper.selectVoList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询运行中的流程
|
||||
*/
|
||||
@Override
|
||||
public List<CwProjectApprovalProcessVo> queryRunningProcesses() {
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectApprovalProcess::getProcessStatus, "running");
|
||||
wrapper.orderByDesc(CwProjectApprovalProcess::getCreateTime);
|
||||
return baseMapper.selectVoList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询超时流程
|
||||
*/
|
||||
@Override
|
||||
public List<CwProjectApprovalProcessVo> queryOvertimeProcesses(Integer hours) {
|
||||
// 这里可以根据需要实现超时查询逻辑
|
||||
LambdaQueryWrapper<CwProjectApprovalProcess> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectApprovalProcess::getProcessStatus, "running");
|
||||
wrapper.orderByDesc(CwProjectApprovalProcess::getCreateTime);
|
||||
return baseMapper.selectVoList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动自定义审批流程
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Boolean startCustomApprovalProcess(CwCustomApprovalBo bo) {
|
||||
try {
|
||||
// 1. 创建审批流程
|
||||
CwProjectApprovalProcess process = new CwProjectApprovalProcess();
|
||||
process.setApplicationId(bo.getApplicationId());
|
||||
process.setApplicationNo(bo.getApplicationNo());
|
||||
process.setProcessName(bo.getProcessName());
|
||||
process.setProcessStatus("running");
|
||||
process.setTotalNodes(bo.getAuditorIds().size());
|
||||
process.setCurrentNode(1);
|
||||
process.setStartTime(new Date());
|
||||
process.setSubmitterId(bo.getSubmitterId());
|
||||
process.setSubmitterName(bo.getSubmitterName());
|
||||
|
||||
boolean result = baseMapper.insert(process) > 0;
|
||||
|
||||
// TODO: 这里可以创建对应的审批记录
|
||||
// 由于CwApprovalRecord相关的实现比较复杂,这里先简化处理
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("启动审批流程失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 终止审批流程
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Boolean terminateProcess(Long id, String reason) {
|
||||
CwProjectApprovalProcess update = new CwProjectApprovalProcess();
|
||||
update.setId(id);
|
||||
update.setProcessStatus("terminated");
|
||||
update.setEndTime(new Date());
|
||||
update.setUpdateBy(LoginHelper.getUserId());
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推进流程到下一节点
|
||||
*/
|
||||
@Override
|
||||
public Boolean advanceToNextNode(Long processId) {
|
||||
CwProjectApprovalProcess process = baseMapper.selectById(processId);
|
||||
if (process != null && process.getCurrentNode() < process.getTotalNodes()) {
|
||||
return updateCurrentNode(processId, process.getCurrentNode() + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成审批流程
|
||||
*/
|
||||
@Override
|
||||
public Boolean completeProcess(Long processId) {
|
||||
return updateProcessStatus(processId, "completed");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新流程当前节点
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateCurrentNode(Long id, Integer currentNode) {
|
||||
CwProjectApprovalProcess update = new CwProjectApprovalProcess();
|
||||
update.setId(id);
|
||||
update.setCurrentNode(currentNode);
|
||||
update.setUpdateBy(LoginHelper.getUserId());
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新流程状态
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateProcessStatus(Long id, String status) {
|
||||
CwProjectApprovalProcess update = new CwProjectApprovalProcess();
|
||||
update.setId(id);
|
||||
update.setProcessStatus(status);
|
||||
update.setUpdateBy(LoginHelper.getUserId());
|
||||
if ("completed".equals(status) || "terminated".equals(status)) {
|
||||
update.setEndTime(new Date());
|
||||
}
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证审批流程配置
|
||||
*/
|
||||
@Override
|
||||
public Boolean validateApprovalProcess(CwCustomApprovalBo bo) {
|
||||
// 验证基本参数
|
||||
if (bo.getApplicationId() == null ||
|
||||
StringUtils.isBlank(bo.getApplicationNo()) ||
|
||||
bo.getAuditorIds() == null || bo.getAuditorIds().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// TODO: 可以添加更多验证逻辑
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计审批流程状态
|
||||
*/
|
||||
@Override
|
||||
public List<Map<String, Object>> queryProcessStatistics(Long submitterId) {
|
||||
// 这里可以根据需要实现统计逻辑
|
||||
return List.of(Map.of(
|
||||
"totalProcesses", 0,
|
||||
"runningProcesses", 0,
|
||||
"completedProcesses", 0,
|
||||
"terminatedProcesses", 0
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出数据
|
||||
*/
|
||||
@Override
|
||||
public List<CwProjectApprovalProcessVo> exportList(CwProjectApprovalProcessVo vo) {
|
||||
return queryList(vo);
|
||||
}
|
||||
}
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
package org.dromara.cw.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.cw.domain.bo.CwCustomApprovalBo;
|
||||
import org.dromara.cw.domain.bo.CwApprovalActionBo;
|
||||
import org.dromara.cw.domain.vo.CwProjectApprovalProcessVo;
|
||||
import org.dromara.cw.domain.vo.CwApprovalRecordVo;
|
||||
import org.dromara.cw.service.ICwProjectApprovalService;
|
||||
import org.dromara.cw.service.ICwProjectApprovalProcessService;
|
||||
import org.dromara.cw.service.ICwApprovalRecordService;
|
||||
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.dromara.cw.domain.CwApprovalRecord;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 项目审批流程Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class CwProjectApprovalServiceImpl implements ICwProjectApprovalService {
|
||||
|
||||
private final ICwProjectApprovalProcessService processService;
|
||||
private final ICwApprovalRecordService recordService;
|
||||
|
||||
/**
|
||||
* 启动自定义审批流程
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean startCustomApprovalProcess(CwCustomApprovalBo bo) {
|
||||
try {
|
||||
log.info("🚀 启动自定义审批流程: applicationId={}, auditorIds={}",
|
||||
bo.getApplicationId(), bo.getAuditorIds());
|
||||
|
||||
// 1. 验证参数
|
||||
if (bo.getApplicationId() == null) {
|
||||
throw new RuntimeException("申报ID不能为空");
|
||||
}
|
||||
if (bo.getAuditorIds() == null || bo.getAuditorIds().isEmpty()) {
|
||||
throw new RuntimeException("审批人列表不能为空");
|
||||
}
|
||||
|
||||
// 2. 调用审批记录服务创建自定义审批流程
|
||||
Boolean result = recordService.createCustomApprovalProcess(
|
||||
bo.getApplicationId(),
|
||||
bo.getApplicationNo(),
|
||||
bo.getAuditorIds(),
|
||||
bo.getSubmitterId(),
|
||||
bo.getSubmitterName()
|
||||
);
|
||||
|
||||
if (result) {
|
||||
log.info("✅ 自定义审批流程启动成功: applicationId={}", bo.getApplicationId());
|
||||
} else {
|
||||
log.error("❌ 自定义审批流程启动失败: applicationId={}", bo.getApplicationId());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 启动自定义审批流程失败: applicationId={}, error={}",
|
||||
bo.getApplicationId(), e.getMessage(), e);
|
||||
throw new RuntimeException("启动审批流程失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行审批操作
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean executeApproval(CwApprovalActionBo bo) {
|
||||
try {
|
||||
log.info("执行审批操作: recordId={}, action={}, opinion={}",
|
||||
bo.getRecordId(), bo.getAction(), bo.getOpinion());
|
||||
|
||||
// 🔧 修复:先通过recordId获取审批记录信息
|
||||
CwApprovalRecordVo record = recordService.queryById(bo.getRecordId());
|
||||
if (record == null) {
|
||||
throw new RuntimeException("审批记录不存在:" + bo.getRecordId());
|
||||
}
|
||||
|
||||
// 🔧 修复:调用现有的审批记录服务执行审批
|
||||
Boolean result = recordService.processApproval(
|
||||
record.getApplicationId(),
|
||||
record.getNodeCode(),
|
||||
bo.getAction(),
|
||||
bo.getOpinion()
|
||||
);
|
||||
|
||||
log.info("✅ 审批操作执行完成:recordId={}, action={}, result={}",
|
||||
bo.getRecordId(), bo.getAction(), result);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("执行审批操作失败: recordId={}, action={}, error={}",
|
||||
bo.getRecordId(), bo.getAction(), e.getMessage(), e);
|
||||
throw new RuntimeException("审批操作失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的待审批列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<CwApprovalRecordVo> getMyPendingApprovals(PageQuery pageQuery) {
|
||||
try {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
log.info("获取用户{}的待审批列表", userId);
|
||||
|
||||
// 🔧 修复:调用现有的审批记录服务查询方法
|
||||
List<CwApprovalRecordVo> list = recordService.queryPendingByApproverId(userId);
|
||||
|
||||
// 应用分页
|
||||
int total = list.size();
|
||||
int start = Math.max(0, (pageQuery.getPageNum() - 1) * pageQuery.getPageSize());
|
||||
int end = Math.min(start + pageQuery.getPageSize(), total);
|
||||
|
||||
List<CwApprovalRecordVo> pagedList = start < total ?
|
||||
list.subList(start, end) : new ArrayList<>();
|
||||
|
||||
log.info("✅ 查询待审批列表成功:总数={}, 当前页={}, 返回数量={}",
|
||||
total, pageQuery.getPageNum(), pagedList.size());
|
||||
|
||||
// 🔧 修复:使用正确的TableDataInfo构造方法
|
||||
TableDataInfo<CwApprovalRecordVo> result = new TableDataInfo<>(pagedList, total);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取待审批列表失败: {}", e.getMessage(), e);
|
||||
return TableDataInfo.build(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的已审批列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<CwApprovalRecordVo> getMyProcessedApprovals(PageQuery pageQuery) {
|
||||
try {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
log.info("获取用户{}的已审批列表", userId);
|
||||
|
||||
// 🔧 修复:直接查询已审批记录,然后手动分页
|
||||
List<CwApprovalRecordVo> allList = recordService.queryApprovedByCurrentUser();
|
||||
|
||||
// 应用分页
|
||||
int total = allList.size();
|
||||
int start = Math.max(0, (pageQuery.getPageNum() - 1) * pageQuery.getPageSize());
|
||||
int end = Math.min(start + pageQuery.getPageSize(), total);
|
||||
|
||||
List<CwApprovalRecordVo> pagedList = start < total ?
|
||||
allList.subList(start, end) : new ArrayList<>();
|
||||
|
||||
log.info("✅ 查询已审批列表成功:总数={}, 当前页={}, 返回数量={}",
|
||||
total, pageQuery.getPageNum(), pagedList.size());
|
||||
|
||||
// 🔧 修复:使用正确的TableDataInfo构造方法
|
||||
TableDataInfo<CwApprovalRecordVo> result = new TableDataInfo<>(pagedList, total);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取已审批列表失败: {}", e.getMessage(), e);
|
||||
return TableDataInfo.build(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询审批流程列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<CwProjectApprovalProcessVo> queryProcessList(PageQuery pageQuery) {
|
||||
try {
|
||||
log.info("查询审批流程列表");
|
||||
|
||||
// 这里应该查询具体的流程数据
|
||||
// 由于缺少具体的业务逻辑,这里返回空列表
|
||||
// 实际项目中需要根据具体业务需求实现
|
||||
|
||||
List<CwProjectApprovalProcessVo> list = new ArrayList<>();
|
||||
return TableDataInfo.build(list);
|
||||
} catch (Exception e) {
|
||||
log.error("查询审批流程列表失败: {}", e.getMessage(), e);
|
||||
return TableDataInfo.build(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批流程详情
|
||||
*/
|
||||
@Override
|
||||
public CwProjectApprovalProcessVo getProcessDetail(Long processId) {
|
||||
try {
|
||||
log.info("获取审批流程详情: {}", processId);
|
||||
|
||||
// 这里应该查询具体的流程详情
|
||||
// 由于缺少具体的业务逻辑,这里返回null
|
||||
// 实际项目中需要根据具体业务需求实现
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("获取审批流程详情失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<CwApprovalRecordVo> getApprovalRecords(Long applicationId) {
|
||||
try {
|
||||
log.info("获取审批记录列表: {}", applicationId);
|
||||
|
||||
// 这里应该查询具体的审批记录
|
||||
// 由于缺少具体的业务逻辑,这里返回空列表
|
||||
// 实际项目中需要根据具体业务需求实现
|
||||
|
||||
return new ArrayList<>();
|
||||
} catch (Exception e) {
|
||||
log.error("获取审批记录列表失败: {}", e.getMessage(), e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 终止审批流程
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean terminateProcess(List<Long> processIds) {
|
||||
try {
|
||||
log.info("终止审批流程: {}", processIds);
|
||||
|
||||
// 这里应该实现具体的流程终止逻辑
|
||||
// 由于缺少具体的业务逻辑,这里返回成功状态
|
||||
// 实际项目中需要根据具体业务需求实现
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("终止审批流程失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
package org.dromara.cw.service.impl;
|
||||
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.cw.domain.bo.CwProjectAuditorBo;
|
||||
import org.dromara.cw.domain.vo.CwProjectAuditorVo;
|
||||
import org.dromara.cw.domain.CwProjectAuditor;
|
||||
import org.dromara.cw.domain.UserWithDeptInfo;
|
||||
import org.dromara.cw.mapper.CwProjectAuditorMapper;
|
||||
import org.dromara.cw.service.ICwProjectAuditorService;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
|
||||
/**
|
||||
* 项目审核人配置Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class CwProjectAuditorServiceImpl implements ICwProjectAuditorService {
|
||||
|
||||
private final CwProjectAuditorMapper baseMapper;
|
||||
private final ISysUserService sysUserService;
|
||||
|
||||
/**
|
||||
* 查询项目审核人配置
|
||||
*/
|
||||
@Override
|
||||
public CwProjectAuditorVo queryById(Long id){
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目审核人配置列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<CwProjectAuditorVo> queryPageList(CwProjectAuditorBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<CwProjectAuditor> lqw = buildQueryWrapper(bo);
|
||||
Page<CwProjectAuditorVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目审核人配置列表
|
||||
*/
|
||||
@Override
|
||||
public List<CwProjectAuditorVo> queryList(CwProjectAuditorBo bo) {
|
||||
LambdaQueryWrapper<CwProjectAuditor> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<CwProjectAuditor> buildQueryWrapper(CwProjectAuditorBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<CwProjectAuditor> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getUserId() != null, CwProjectAuditor::getUserId, bo.getUserId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getUserName()), CwProjectAuditor::getUserName, bo.getUserName());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getNickName()), CwProjectAuditor::getNickName, bo.getNickName());
|
||||
lqw.eq(bo.getDeptId() != null, CwProjectAuditor::getDeptId, bo.getDeptId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getDeptName()), CwProjectAuditor::getDeptName, bo.getDeptName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getPhoneNumber()), CwProjectAuditor::getPhoneNumber, bo.getPhoneNumber());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getEmail()), CwProjectAuditor::getEmail, bo.getEmail());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), CwProjectAuditor::getStatus, bo.getStatus());
|
||||
lqw.eq(bo.getConfigBy() != null, CwProjectAuditor::getConfigBy, bo.getConfigBy());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getConfigByName()), CwProjectAuditor::getConfigByName, bo.getConfigByName());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增项目审核人配置
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(CwProjectAuditorBo bo) {
|
||||
CwProjectAuditor add = MapstructUtils.convert(bo, CwProjectAuditor.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改项目审核人配置
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(CwProjectAuditorBo bo) {
|
||||
CwProjectAuditor update = MapstructUtils.convert(bo, CwProjectAuditor.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(CwProjectAuditor entity){
|
||||
// 检查用户是否已存在
|
||||
if (entity.getUserId() != null) {
|
||||
LambdaQueryWrapper<CwProjectAuditor> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectAuditor::getUserId, entity.getUserId());
|
||||
if (entity.getId() != null) {
|
||||
wrapper.ne(CwProjectAuditor::getId, entity.getId());
|
||||
}
|
||||
if (baseMapper.exists(wrapper)) {
|
||||
throw new RuntimeException("该用户已经是审核人,不能重复添加");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除项目审核人配置
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加审核人
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Boolean batchAddAuditors(List<Long> userIds, Long configBy, String configByName, String remark) {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. 获取完整的用户信息(包含部门信息)
|
||||
List<UserWithDeptInfo> users = baseMapper.selectUsersWithDeptInfo(userIds);
|
||||
if (users.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 过滤已存在的审核人
|
||||
List<Long> existingUserIds = baseMapper.selectList(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.in(CwProjectAuditor::getUserId, userIds))
|
||||
.stream().map(CwProjectAuditor::getUserId).collect(Collectors.toList());
|
||||
|
||||
// 3. 构建新增审核人列表
|
||||
List<CwProjectAuditor> auditors = new ArrayList<>();
|
||||
for (UserWithDeptInfo user : users) {
|
||||
if (!existingUserIds.contains(user.getUserId())) {
|
||||
CwProjectAuditor auditor = new CwProjectAuditor();
|
||||
auditor.setUserId(user.getUserId());
|
||||
auditor.setUserName(user.getUserName());
|
||||
auditor.setNickName(user.getNickName());
|
||||
auditor.setDeptId(user.getDeptId());
|
||||
auditor.setDeptName(user.getDeptName() != null ? user.getDeptName() : "");
|
||||
auditor.setPhoneNumber(user.getPhonenumber());
|
||||
auditor.setEmail(user.getEmail());
|
||||
auditor.setStatus("1"); // 默认正常状态
|
||||
auditor.setConfigBy(configBy);
|
||||
auditor.setConfigByName(configByName);
|
||||
auditor.setRemark(remark);
|
||||
auditors.add(auditor);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 批量保存
|
||||
if (!auditors.isEmpty()) {
|
||||
return baseMapper.insertBatch(auditors);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的审核人列表
|
||||
*/
|
||||
@Override
|
||||
public List<CwProjectAuditorVo> getAvailableAuditors() {
|
||||
LambdaQueryWrapper<CwProjectAuditor> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(CwProjectAuditor::getStatus, "1"); // 只查询正常状态的审核人
|
||||
wrapper.orderByAsc(CwProjectAuditor::getDeptId, CwProjectAuditor::getUserName);
|
||||
return baseMapper.selectVoList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门ID获取用户列表
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getDeptUsers(Long deptId) {
|
||||
try {
|
||||
// 参数验证
|
||||
if (deptId == null || deptId <= 0) {
|
||||
log.warn("无效的部门ID: {}", deptId);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 调用系统用户服务获取部门用户
|
||||
List<SysUserVo> users = sysUserService.selectUserListByDept(deptId);
|
||||
if (users == null || users.isEmpty()) {
|
||||
log.info("部门 {} 下没有找到用户", deptId);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return users.stream().map(user -> {
|
||||
Map<String, Object> userMap = Map.of(
|
||||
"userId", user.getUserId(),
|
||||
"userName", user.getUserName(),
|
||||
"nickName", user.getNickName() != null ? user.getNickName() : user.getUserName(),
|
||||
"deptId", user.getDeptId(),
|
||||
"deptName", user.getDeptName() != null ? user.getDeptName() : "",
|
||||
"phonenumber", user.getPhonenumber() != null ? user.getPhonenumber() : "",
|
||||
"email", user.getEmail() != null ? user.getEmail() : ""
|
||||
);
|
||||
return (Object) userMap;
|
||||
}).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取部门 {} 的用户列表失败: {}", deptId, e.getMessage(), e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新审核人状态
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateStatus(List<Long> ids, String status) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<CwProjectAuditor> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.in(CwProjectAuditor::getId, ids);
|
||||
|
||||
CwProjectAuditor update = new CwProjectAuditor();
|
||||
update.setStatus(status);
|
||||
update.setUpdateBy(LoginHelper.getUserId());
|
||||
|
||||
return baseMapper.update(update, wrapper) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getStatistics() {
|
||||
// 获取总审核人数
|
||||
long totalAuditors = baseMapper.selectCount(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0));
|
||||
|
||||
// 获取正常状态审核人数
|
||||
long activeAuditors = baseMapper.selectCount(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.eq(CwProjectAuditor::getStatus, "1"));
|
||||
|
||||
// 获取停用状态审核人数
|
||||
long inactiveAuditors = baseMapper.selectCount(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.eq(CwProjectAuditor::getStatus, "0"));
|
||||
|
||||
// 获取部门数量(去重)
|
||||
List<CwProjectAuditor> auditors = baseMapper.selectList(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.select(CwProjectAuditor::getDeptId));
|
||||
long totalDepts = auditors.stream().map(CwProjectAuditor::getDeptId).distinct().count();
|
||||
|
||||
// 获取今日新增
|
||||
LocalDateTime startOfDay = LocalDateTime.now().with(LocalTime.MIN);
|
||||
LocalDateTime endOfDay = LocalDateTime.now().with(LocalTime.MAX);
|
||||
long todayAdded = baseMapper.selectCount(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.ge(CwProjectAuditor::getCreateTime, startOfDay)
|
||||
.le(CwProjectAuditor::getCreateTime, endOfDay));
|
||||
|
||||
// 获取本月新增
|
||||
LocalDateTime startOfMonth = LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
|
||||
LocalDateTime endOfMonth = LocalDateTime.now().with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
|
||||
long monthlyAdded = baseMapper.selectCount(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.ge(CwProjectAuditor::getCreateTime, startOfMonth)
|
||||
.le(CwProjectAuditor::getCreateTime, endOfMonth));
|
||||
|
||||
// 获取我配置的审核人数
|
||||
long myConfigured = baseMapper.selectCount(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.eq(CwProjectAuditor::getConfigBy, LoginHelper.getUserId()));
|
||||
|
||||
// 获取最近活跃数(近7天有更新的)
|
||||
LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7);
|
||||
long recentActive = baseMapper.selectCount(Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.ge(CwProjectAuditor::getUpdateTime, sevenDaysAgo));
|
||||
|
||||
// 构建统计结果
|
||||
Map<String, Object> statistics = new HashMap<>();
|
||||
statistics.put("totalAuditors", totalAuditors);
|
||||
statistics.put("activeAuditors", activeAuditors);
|
||||
statistics.put("inactiveAuditors", inactiveAuditors);
|
||||
statistics.put("totalDepts", totalDepts);
|
||||
statistics.put("todayAdded", todayAdded);
|
||||
statistics.put("monthlyAdded", monthlyAdded);
|
||||
statistics.put("myConfigured", myConfigured);
|
||||
statistics.put("recentActive", recentActive);
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查询项目审核人配置
|
||||
*/
|
||||
@Override
|
||||
public CwProjectAuditorVo queryByUserId(Long userId) {
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<CwProjectAuditor> wrapper = Wrappers.<CwProjectAuditor>lambdaQuery()
|
||||
.eq(CwProjectAuditor::getUserId, userId)
|
||||
.eq(CwProjectAuditor::getDeleted, 0)
|
||||
.orderByDesc(CwProjectAuditor::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
|
||||
CwProjectAuditor entity = baseMapper.selectOne(wrapper);
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MapstructUtils.convert(entity, CwProjectAuditorVo.class);
|
||||
}
|
||||
}
|
||||
+24
-1
@@ -72,6 +72,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="approvalStatus" column="approval_status" />
|
||||
<result property="submitTime" column="submit_time" />
|
||||
<result property="approvalTime" column="approval_time" />
|
||||
<!-- 流程相关字段 -->
|
||||
<result property="totalNodes" column="total_nodes" />
|
||||
<result property="currentNode" column="current_node" />
|
||||
<result property="processName" column="process_name" />
|
||||
<result property="processStatus" column="process_status" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCwApprovalRecordVo">
|
||||
@@ -90,7 +95,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
ar.approver_name as current_approver_name,
|
||||
ar.node_status as approval_status,
|
||||
pa.create_time as submit_time,
|
||||
ar.end_time as approval_time
|
||||
ar.end_time as approval_time,
|
||||
-- 流程相关字段(通过审批记录计算)
|
||||
(SELECT COUNT(DISTINCT ar2.node_order)
|
||||
FROM cw_approval_record ar2
|
||||
WHERE ar2.application_id = ar.application_id
|
||||
AND ar2.deleted = 0) as total_nodes,
|
||||
ar.node_order as current_node,
|
||||
'项目审批' as process_name,
|
||||
(CASE
|
||||
WHEN EXISTS(SELECT 1 FROM cw_approval_record ar3
|
||||
WHERE ar3.application_id = ar.application_id
|
||||
AND ar3.node_status = 'rejected'
|
||||
AND ar3.deleted = 0) THEN 'rejected'
|
||||
WHEN NOT EXISTS(SELECT 1 FROM cw_approval_record ar4
|
||||
WHERE ar4.application_id = ar.application_id
|
||||
AND ar4.node_status IN ('waiting', 'processing')
|
||||
AND ar4.deleted = 0) THEN 'completed'
|
||||
ELSE 'processing'
|
||||
END) as process_status
|
||||
from cw_approval_record ar
|
||||
left join cw_project_application pa on ar.application_id = pa.id
|
||||
left join sys_user u on pa.create_by = u.user_id
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.cw.mapper.CwProjectApprovalProcessMapper">
|
||||
|
||||
<resultMap type="org.dromara.cw.domain.vo.CwProjectApprovalProcessVo" id="CwProjectApprovalProcessResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="applicationId" column="application_id" />
|
||||
<result property="applicationNo" column="application_no" />
|
||||
<result property="processName" column="process_name" />
|
||||
<result property="processStatus" column="process_status" />
|
||||
<result property="totalNodes" column="total_nodes" />
|
||||
<result property="currentNode" column="current_node" />
|
||||
<result property="startTime" column="start_time" />
|
||||
<result property="endTime" column="end_time" />
|
||||
<result property="submitterId" column="submitter_id" />
|
||||
<result property="submitterName" column="submitter_name" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCwProjectApprovalProcessVo">
|
||||
select
|
||||
p.id, p.application_id, p.application_no, p.process_name, p.process_status,
|
||||
p.total_nodes, p.current_node, p.start_time, p.end_time,
|
||||
p.submitter_id, p.submitter_name,
|
||||
p.create_by, p.create_time, p.update_by, p.update_time,
|
||||
a.project_name,
|
||||
u.nick_name as submitter_nick_name
|
||||
from cw_project_approval_process p
|
||||
left join cw_project_application a on p.application_id = a.id
|
||||
left join sys_user u on p.submitter_id = u.user_id
|
||||
</sql>
|
||||
|
||||
<select id="selectProcessList" parameterType="org.dromara.cw.domain.CwProjectApprovalProcess" resultMap="CwProjectApprovalProcessResult">
|
||||
<include refid="selectCwProjectApprovalProcessVo"/>
|
||||
<where>
|
||||
p.deleted = 0
|
||||
<if test="applicationId != null"> and p.application_id = #{applicationId}</if>
|
||||
<if test="applicationNo != null and applicationNo != ''"> and p.application_no like concat('%', #{applicationNo}, '%')</if>
|
||||
<if test="processName != null and processName != ''"> and p.process_name like concat('%', #{processName}, '%')</if>
|
||||
<if test="processStatus != null and processStatus != ''"> and p.process_status = #{processStatus}</if>
|
||||
<if test="submitterId != null"> and p.submitter_id = #{submitterId}</if>
|
||||
<if test="submitterName != null and submitterName != ''"> and p.submitter_name like concat('%', #{submitterName}, '%')</if>
|
||||
<if test="params.beginCreateTime != null and params.beginCreateTime != ''"> and p.create_time >= #{params.beginCreateTime}</if>
|
||||
<if test="params.endCreateTime != null and params.endCreateTime != ''"> and p.create_time <= #{params.endCreateTime}</if>
|
||||
<if test="params.beginStartTime != null and params.beginStartTime != ''"> and p.start_time >= #{params.beginStartTime}</if>
|
||||
<if test="params.endStartTime != null and params.endStartTime != ''"> and p.start_time <= #{params.endStartTime}</if>
|
||||
</where>
|
||||
order by p.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectByApplicationId" resultMap="CwProjectApprovalProcessResult">
|
||||
<include refid="selectCwProjectApprovalProcessVo"/>
|
||||
where p.application_id = #{applicationId} and p.deleted = 0
|
||||
</select>
|
||||
|
||||
<select id="selectByApplicationNo" resultMap="CwProjectApprovalProcessResult">
|
||||
<include refid="selectCwProjectApprovalProcessVo"/>
|
||||
where p.application_no = #{applicationNo} and p.deleted = 0
|
||||
</select>
|
||||
|
||||
<select id="selectMyProcesses" resultMap="CwProjectApprovalProcessResult">
|
||||
<include refid="selectCwProjectApprovalProcessVo"/>
|
||||
where p.submitter_id = #{submitterId} and p.deleted = 0
|
||||
order by p.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectRunningProcesses" resultMap="CwProjectApprovalProcessResult">
|
||||
<include refid="selectCwProjectApprovalProcessVo"/>
|
||||
where p.process_status = 'running' and p.deleted = 0
|
||||
order by p.start_time
|
||||
</select>
|
||||
|
||||
<select id="selectOvertimeProcesses" resultMap="CwProjectApprovalProcessResult">
|
||||
<include refid="selectCwProjectApprovalProcessVo"/>
|
||||
where p.process_status = 'running'
|
||||
and p.start_time < date_sub(now(), interval #{hours} hour)
|
||||
and p.deleted = 0
|
||||
order by p.start_time
|
||||
</select>
|
||||
|
||||
<select id="selectProcessStatistics" resultType="map">
|
||||
select
|
||||
p.process_status as status,
|
||||
count(*) as count
|
||||
from cw_project_approval_process p
|
||||
where p.deleted = 0
|
||||
<if test="submitterId != null"> and p.submitter_id = #{submitterId}</if>
|
||||
group by p.process_status
|
||||
order by count desc
|
||||
</select>
|
||||
|
||||
<update id="updateCurrentNode">
|
||||
update cw_project_approval_process
|
||||
set current_node = #{currentNode}, update_by = #{updateBy}, update_time = now()
|
||||
where id = #{id} and deleted = 0
|
||||
</update>
|
||||
|
||||
<update id="updateProcessStatus">
|
||||
update cw_project_approval_process
|
||||
set process_status = #{status},
|
||||
end_time = case when #{status} in ('completed', 'terminated') then now() else end_time end,
|
||||
update_by = #{updateBy},
|
||||
update_time = now()
|
||||
where id = #{id} and deleted = 0
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.cw.mapper.CwProjectAuditorMapper">
|
||||
|
||||
<resultMap type="org.dromara.cw.domain.vo.CwProjectAuditorVo" id="CwProjectAuditorResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="userId" column="user_id" />
|
||||
<result property="userName" column="user_name" />
|
||||
<result property="nickName" column="nick_name" />
|
||||
<result property="deptId" column="dept_id" />
|
||||
<result property="deptName" column="dept_name" />
|
||||
<result property="phoneNumber" column="phone_number" />
|
||||
<result property="email" column="email" />
|
||||
<result property="status" column="status" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="configBy" column="config_by" />
|
||||
<result property="configByName" column="config_by_name" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCwProjectAuditorVo">
|
||||
select
|
||||
a.id, a.user_id, a.user_name, a.nick_name, a.dept_id, a.dept_name,
|
||||
a.phone_number, a.email, a.status, a.remark, a.config_by, a.config_by_name,
|
||||
a.create_by, a.create_time, a.update_by, a.update_time,
|
||||
u.user_name as sys_user_name,
|
||||
d.dept_name as sys_dept_name
|
||||
from cw_project_auditor a
|
||||
left join sys_user u on a.user_id = u.user_id
|
||||
left join sys_dept d on a.dept_id = d.dept_id
|
||||
</sql>
|
||||
|
||||
<select id="selectAuditorList" parameterType="org.dromara.cw.domain.CwProjectAuditor" resultMap="CwProjectAuditorResult">
|
||||
<include refid="selectCwProjectAuditorVo"/>
|
||||
<where>
|
||||
a.deleted = 0
|
||||
<if test="userId != null"> and a.user_id = #{userId}</if>
|
||||
<if test="userName != null and userName != ''"> and a.user_name like concat('%', #{userName}, '%')</if>
|
||||
<if test="nickName != null and nickName != ''"> and a.nick_name like concat('%', #{nickName}, '%')</if>
|
||||
<if test="deptId != null"> and a.dept_id = #{deptId}</if>
|
||||
<if test="deptName != null and deptName != ''"> and a.dept_name like concat('%', #{deptName}, '%')</if>
|
||||
<if test="status != null and status != ''"> and a.status = #{status}</if>
|
||||
<if test="configBy != null"> and a.config_by = #{configBy}</if>
|
||||
<if test="configByName != null and configByName != ''"> and a.config_by_name like concat('%', #{configByName}, '%')</if>
|
||||
<if test="params.beginCreateTime != null and params.beginCreateTime != ''"> and a.create_time >= #{params.beginCreateTime}</if>
|
||||
<if test="params.endCreateTime != null and params.endCreateTime != ''"> and a.create_time <= #{params.endCreateTime}</if>
|
||||
</where>
|
||||
order by a.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectAuditorsByDeptId" resultMap="CwProjectAuditorResult">
|
||||
<include refid="selectCwProjectAuditorVo"/>
|
||||
where a.dept_id = #{deptId} and a.status = '1' and a.deleted = 0
|
||||
order by a.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectAvailableAuditors" resultMap="CwProjectAuditorResult">
|
||||
<include refid="selectCwProjectAuditorVo"/>
|
||||
where a.status = '1' and a.deleted = 0
|
||||
order by a.dept_id, a.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectMyConfigAuditors" resultMap="CwProjectAuditorResult">
|
||||
<include refid="selectCwProjectAuditorVo"/>
|
||||
where a.config_by = #{configBy} and a.deleted = 0
|
||||
order by a.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectAuditorsByUserIds" resultMap="CwProjectAuditorResult">
|
||||
<include refid="selectCwProjectAuditorVo"/>
|
||||
where a.user_id in
|
||||
<foreach item="userId" collection="userIds" open="(" separator="," close=")">
|
||||
#{userId}
|
||||
</foreach>
|
||||
and a.status = '1' and a.deleted = 0
|
||||
order by a.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectAuditorStatistics" resultType="map">
|
||||
select
|
||||
d.dept_name,
|
||||
count(*) as total_count,
|
||||
count(case when a.status = '1' then 1 end) as active_count,
|
||||
count(case when a.status = '0' then 1 end) as inactive_count
|
||||
from cw_project_auditor a
|
||||
left join sys_dept d on a.dept_id = d.dept_id
|
||||
where a.deleted = 0
|
||||
<if test="deptId != null"> and a.dept_id = #{deptId}</if>
|
||||
<if test="status != null and status != ''"> and a.status = #{status}</if>
|
||||
group by a.dept_id, d.dept_name
|
||||
order by total_count desc
|
||||
</select>
|
||||
|
||||
<select id="selectByUserId" resultMap="CwProjectAuditorResult">
|
||||
<include refid="selectCwProjectAuditorVo"/>
|
||||
where a.user_id = #{userId} and a.deleted = 0
|
||||
</select>
|
||||
|
||||
<update id="batchUpdateStatus">
|
||||
update cw_project_auditor
|
||||
set status = #{status}, update_by = #{updateBy}, update_time = now()
|
||||
where id in
|
||||
<foreach item="id" collection="ids" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
and deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 查询完整的用户信息(包含部门信息)用于批量添加 -->
|
||||
<select id="selectUsersWithDeptInfo" resultType="org.dromara.cw.domain.UserWithDeptInfo">
|
||||
select
|
||||
u.user_id as userId,
|
||||
u.user_name as userName,
|
||||
u.nick_name as nickName,
|
||||
u.dept_id as deptId,
|
||||
d.dept_name as deptName,
|
||||
u.phonenumber as phonenumber,
|
||||
u.email as email,
|
||||
u.status as status
|
||||
from sys_user u
|
||||
left join sys_dept d on u.dept_id = d.dept_id
|
||||
where u.status = '0'
|
||||
and u.user_id in
|
||||
<foreach item="userId" collection="userIds" open="(" separator="," close=")">
|
||||
#{userId}
|
||||
</foreach>
|
||||
order by u.dept_id, u.user_name
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+1
-1
@@ -297,7 +297,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
||||
@Override
|
||||
public List<SysUserVo> selectUserByIds(List<Long> userIds, Long deptId) {
|
||||
return baseMapper.selectUserList(new LambdaQueryWrapper<SysUser>()
|
||||
.select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName)
|
||||
.select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getPhonenumber)
|
||||
.eq(SysUser::getStatus, SystemConstants.NORMAL)
|
||||
.eq(ObjectUtil.isNotNull(deptId), SysUser::getDeptId, deptId)
|
||||
.in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds));
|
||||
|
||||
@@ -104,6 +104,13 @@
|
||||
<artifactId>ruoyi-workflow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 学生考核模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-xskh</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package org.dromara.xg.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.xg.domain.vo.XgDormApplicationVo;
|
||||
import org.dromara.xg.domain.bo.XgDormApplicationBo;
|
||||
import org.dromara.xg.domain.vo.StudentOptionVo;
|
||||
import org.dromara.xg.domain.bo.StudentOptionBo;
|
||||
import org.dromara.xg.service.IXgDormApplicationService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/dormApplication/dormApplication")
|
||||
public class XgDormApplicationController extends BaseController {
|
||||
|
||||
private final IXgDormApplicationService xgDormApplicationService;
|
||||
|
||||
/**
|
||||
* 查询学生宿舍申请单表(调宿+退宿)列表
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<XgDormApplicationVo> list(XgDormApplicationBo bo, PageQuery pageQuery) {
|
||||
return xgDormApplicationService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学生选择器选项列表(支持按学号、姓名检索)
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:list")
|
||||
@GetMapping("/studentOptions")
|
||||
public TableDataInfo<StudentOptionVo> getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) {
|
||||
return xgDormApplicationService.getStudentOptions(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出学生宿舍申请单表(调宿+退宿)列表
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:export")
|
||||
@Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(XgDormApplicationBo bo, HttpServletResponse response) {
|
||||
List<XgDormApplicationVo> list = xgDormApplicationService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "学生宿舍申请单表(调宿+退宿)", XgDormApplicationVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学生宿舍申请单表(调宿+退宿)详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<XgDormApplicationVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(xgDormApplicationService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增学生宿舍申请单表(调宿+退宿)
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:add")
|
||||
@Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<XgDormApplicationVo> add(@Validated(AddGroup.class) @RequestBody XgDormApplicationBo bo) {
|
||||
XgDormApplicationVo result = xgDormApplicationService.insertByBo(bo);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改学生宿舍申请单表(调宿+退宿)
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:edit")
|
||||
@Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody XgDormApplicationBo bo) {
|
||||
return toAjax(xgDormApplicationService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:remove")
|
||||
@Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||||
return toAjax(xgDormApplicationService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级学院审批人列表
|
||||
*
|
||||
* @param collegeId 学院ID
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:list")
|
||||
@GetMapping("/getCollegeApprovers")
|
||||
public R<List<Map<String, Object>>> getCollegeApprovers(@RequestParam Long collegeId) {
|
||||
List<Map<String, Object>> approvers = xgDormApplicationService.getCollegeApprovers(collegeId);
|
||||
return R.ok(approvers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学工处审批人列表
|
||||
*/
|
||||
@SaCheckPermission("dormApplication:dormApplication:list")
|
||||
@GetMapping("/getStudentAffairsApprovers")
|
||||
public R<List<Map<String, Object>>> getStudentAffairsApprovers() {
|
||||
List<Map<String, Object>> approvers = xgDormApplicationService.getStudentAffairsApprovers();
|
||||
return R.ok(approvers);
|
||||
}
|
||||
|
||||
}
|
||||
+24
-2
@@ -157,7 +157,7 @@ public class XgDormBedStudentController extends BaseController {
|
||||
|
||||
/**
|
||||
* 获取当前用户二级学院的宿舍楼树形结构
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
|
||||
*/
|
||||
@SaCheckPermission("dormBedStudent:dormBedStudent:list")
|
||||
@GetMapping("/currentUserCollegeDormTree")
|
||||
@@ -168,7 +168,7 @@ public class XgDormBedStudentController extends BaseController {
|
||||
|
||||
/**
|
||||
* 获取班主任管理的班级宿舍楼树形结构
|
||||
* 只显示班主任管理班级分配到的宿舍楼
|
||||
* 校区-宿舍楼二级结构,只显示班主任管理班级分配到的宿舍楼
|
||||
*/
|
||||
@SaCheckPermission("dormBedStudent:dormBedStudent:list")
|
||||
@GetMapping("/getClassTeacherDormTree")
|
||||
@@ -332,4 +332,26 @@ public class XgDormBedStudentController extends BaseController {
|
||||
return R.ok(roomStudents);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学生ID获取当前宿舍信息
|
||||
* 包含校区、宿舍楼、房间号、床位号等信息
|
||||
*/
|
||||
@SaCheckPermission("dormBedStudent:dormBedStudent:list")
|
||||
@GetMapping("/currentDormInfo/{studentId}")
|
||||
public R<Object> getCurrentDormInfo(@NotNull(message = "学生ID不能为空") @PathVariable Long studentId) {
|
||||
Object currentDormInfo = xgDormBedStudentService.getCurrentDormInfo(studentId);
|
||||
return R.ok(currentDormInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据床位ID获取该床位的学生信息
|
||||
* 包含学生基本信息、学院、专业、班级等信息
|
||||
*/
|
||||
@SaCheckPermission("dormBedStudent:dormBedStudent:list")
|
||||
@GetMapping("/bedStudentInfo/{bedId}")
|
||||
public R<Object> getBedStudentInfo(@NotNull(message = "床位ID不能为空") @PathVariable Long bedId) {
|
||||
Object bedStudentInfo = xgDormBedStudentService.getBedStudentInfo(bedId);
|
||||
return R.ok(bedStudentInfo);
|
||||
}
|
||||
|
||||
}
|
||||
+23
-1
@@ -18,6 +18,7 @@ import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.xg.domain.vo.XgDormBuildingVo;
|
||||
import org.dromara.xg.domain.vo.XgDormBuildingSimplifiedVo;
|
||||
import org.dromara.xg.domain.bo.XgDormBuildingBo;
|
||||
import org.dromara.xg.domain.vo.BuildingTreeSelectVo;
|
||||
import org.dromara.xg.service.IXgDormBuildingService;
|
||||
@@ -104,7 +105,7 @@ public class XgDormBuildingController extends BaseController {
|
||||
|
||||
/**
|
||||
* 获取宿舍楼树形结构数据
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
|
||||
*/
|
||||
@SaCheckPermission("sslxx:dormBuilding:list")
|
||||
@GetMapping("/buildingTree")
|
||||
@@ -112,4 +113,25 @@ public class XgDormBuildingController extends BaseController {
|
||||
List<BuildingTreeSelectVo> treeData = xgDormBuildingService.getBuildingTreeData();
|
||||
return R.ok(treeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询宿舍楼信息简化列表
|
||||
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
|
||||
*/
|
||||
@SaCheckPermission("sslxx:dormBuilding:list")
|
||||
@GetMapping("/simplifiedList")
|
||||
public TableDataInfo<XgDormBuildingSimplifiedVo> getSimplifiedList(XgDormBuildingBo bo, PageQuery pageQuery) {
|
||||
return xgDormBuildingService.querySimplifiedPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询宿舍楼信息简化列表(不分页)
|
||||
* 用于下拉选择等场景
|
||||
*/
|
||||
@SaCheckPermission("sslxx:dormBuilding:list")
|
||||
@GetMapping("/simplifiedListAll")
|
||||
public R<List<XgDormBuildingSimplifiedVo>> getSimplifiedListAll(XgDormBuildingBo bo) {
|
||||
List<XgDormBuildingSimplifiedVo> list = xgDormBuildingService.querySimplifiedList(bo);
|
||||
return R.ok(list);
|
||||
}
|
||||
}
|
||||
+52
-5
@@ -9,6 +9,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
|
||||
import org.dromara.xg.domain.vo.DormBuildingTreeSimplifiedVo;
|
||||
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
|
||||
import org.dromara.xg.service.IXgDormBuildingTreeService;
|
||||
|
||||
@@ -28,24 +29,70 @@ public class XgDormBuildingTreeController extends BaseController {
|
||||
|
||||
/**
|
||||
* 获取宿舍楼树形结构数据
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
|
||||
*/
|
||||
@SaCheckPermission("xgss:dormBuildingTree:list")
|
||||
@GetMapping("/list")
|
||||
public R<List<DormBuildingTreeVo>> getDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
List<DormBuildingTreeVo> treeData = xgDormBuildingTreeService.getDormBuildingTree(bo);
|
||||
public R<List<DormBuildingTreeSimplifiedVo>> getDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getSimplifiedDormBuildingTree(bo);
|
||||
return R.ok(treeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有宿舍楼树形结构数据(不考虑权限)
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
|
||||
*/
|
||||
@SaCheckPermission("xgss:dormBuildingTree:list")
|
||||
@GetMapping("/listAll")
|
||||
public R<List<DormBuildingTreeVo>> getAllDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
public R<List<DormBuildingTreeSimplifiedVo>> getAllDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getAllSimplifiedDormBuildingTree(bo);
|
||||
return R.ok(treeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宿舍楼树形结构完整数据(包含宿舍区层级)
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 注意:此接口仅用于特殊需求,一般情况请使用/list接口
|
||||
*/
|
||||
@SaCheckPermission("xgss:dormBuildingTree:list")
|
||||
@GetMapping("/fullList")
|
||||
public R<List<DormBuildingTreeVo>> getFullDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
List<DormBuildingTreeVo> treeData = xgDormBuildingTreeService.getDormBuildingTree(bo);
|
||||
return R.ok(treeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有宿舍楼树形结构完整数据(不考虑权限,包含宿舍区层级)
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 注意:此接口仅用于特殊需求,一般情况请使用/listAll接口
|
||||
*/
|
||||
@SaCheckPermission("xgss:dormBuildingTree:list")
|
||||
@GetMapping("/fullListAll")
|
||||
public R<List<DormBuildingTreeVo>> getAllFullDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
List<DormBuildingTreeVo> treeData = xgDormBuildingTreeService.getAllDormBuildingTree(bo);
|
||||
return R.ok(treeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宿舍楼树形结构简化数据
|
||||
* 校区-宿舍楼二级结构,隐藏宿舍区层级,校区不可选,只有宿舍楼可以选择
|
||||
*/
|
||||
@SaCheckPermission("xgss:dormBuildingTree:list")
|
||||
@GetMapping("/simplifiedList")
|
||||
public R<List<DormBuildingTreeSimplifiedVo>> getSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getSimplifiedDormBuildingTree(bo);
|
||||
return R.ok(treeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有宿舍楼树形结构简化数据(不考虑权限)
|
||||
* 校区-宿舍楼二级结构,隐藏宿舍区层级,校区不可选,只有宿舍楼可以选择
|
||||
*/
|
||||
@SaCheckPermission("xgss:dormBuildingTree:list")
|
||||
@GetMapping("/simplifiedListAll")
|
||||
public R<List<DormBuildingTreeSimplifiedVo>> getAllSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getAllSimplifiedDormBuildingTree(bo);
|
||||
return R.ok(treeData);
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -23,6 +23,7 @@ import org.dromara.xg.domain.bo.XgDormHygieneCheckBo;
|
||||
import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo;
|
||||
import org.dromara.xg.service.IXgDormHygieneCheckService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查
|
||||
@@ -164,4 +165,46 @@ public class XgDormHygieneCheckController extends BaseController {
|
||||
return xgDormHygieneCheckService.queryPageListByStudentId(studentId, bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存完整的宿舍卫生检查数据(包含学生评分和违纪记录)
|
||||
*/
|
||||
@SaCheckPermission("dormHygieneCheck:dormHygieneCheck:add")
|
||||
@Log(title = "宿舍卫生检查", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/saveCompleteData")
|
||||
public R<Long> saveCompleteData(@Validated(AddGroup.class) @RequestBody XgDormHygieneCheckCompleteBo bo) {
|
||||
Long hygieneCheckId = xgDormHygieneCheckService.saveCompleteData(bo);
|
||||
if (hygieneCheckId != null) {
|
||||
return R.ok(hygieneCheckId);
|
||||
} else {
|
||||
return R.fail("保存宿舍卫生检查数据失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新完整的宿舍卫生检查数据(包含学生评分和违纪记录)
|
||||
*/
|
||||
@SaCheckPermission("dormHygieneCheck:dormHygieneCheck:edit")
|
||||
@Log(title = "宿舍卫生检查", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/updateCompleteData")
|
||||
public R<Void> updateCompleteData(@Validated(EditGroup.class) @RequestBody XgDormHygieneCheckCompleteBo bo) {
|
||||
return toAjax(xgDormHygieneCheckService.updateCompleteData(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID查询相关的违纪记录
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 违纪记录列表
|
||||
*/
|
||||
@SaCheckPermission("dormHygieneCheck:dormHygieneCheck:query")
|
||||
@GetMapping("/getViolations/{hygieneCheckId}")
|
||||
public R<List<XgHygieneCheckViolationVo>> getViolations(
|
||||
@NotNull(message = "卫生检查ID不能为空") @PathVariable Long hygieneCheckId) {
|
||||
List<XgHygieneCheckViolationVo> violations = xgDormHygieneCheckService
|
||||
.getViolationsByHygieneCheckId(hygieneCheckId);
|
||||
return R.ok(violations);
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -1,6 +1,7 @@
|
||||
package org.dromara.xg.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -21,6 +22,7 @@ import org.dromara.xg.domain.vo.XgDormRoomVo;
|
||||
import org.dromara.xg.domain.bo.XgDormRoomBo;
|
||||
import org.dromara.xg.service.IXgDormRoomService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 宿舍房间信息
|
||||
@@ -77,6 +79,33 @@ public class XgDormRoomController extends BaseController {
|
||||
ExcelUtil.exportExcel(list, "宿舍房间信息", XgDormRoomVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出住宿清单(宿舍卡片式布局)
|
||||
*/
|
||||
@SaCheckPermission("xgss:dormRoom:export")
|
||||
@Log(title = "住宿清单", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/exportAccommodationList")
|
||||
public void exportAccommodationList(
|
||||
@RequestParam(required = false) Long buildingId,
|
||||
@RequestParam(required = false) String buildingName,
|
||||
@RequestParam(required = false) String buildingType,
|
||||
@RequestParam(required = false) String accommodationData,
|
||||
HttpServletResponse response) {
|
||||
|
||||
// 构建参数Map
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
if (buildingId != null)
|
||||
params.put("buildingId", buildingId);
|
||||
if (buildingName != null)
|
||||
params.put("buildingName", buildingName);
|
||||
if (buildingType != null)
|
||||
params.put("buildingType", buildingType);
|
||||
if (accommodationData != null)
|
||||
params.put("accommodationData", accommodationData);
|
||||
|
||||
xgDormRoomService.exportAccommodationList(params, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宿舍房间信息详细信息
|
||||
*
|
||||
|
||||
+11
@@ -111,4 +111,15 @@ public class XgHolidayStayController extends BaseController {
|
||||
public TableDataInfo<StudentOptionVo> getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) {
|
||||
return xgHolidayStayService.getStudentOptions(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单人留宿有效数据
|
||||
* 获取所有有效的假期留宿数据,筛选出在同一时间段内该宿舍只有一个学生留宿的记录
|
||||
*/
|
||||
@SaCheckPermission("holidayStay:holidayStay:list")
|
||||
@GetMapping("/singlePersonValidStay")
|
||||
public R<List<XgHolidayStayVo>> getSinglePersonValidStay() {
|
||||
List<XgHolidayStayVo> result = xgHolidayStayService.querySinglePersonValidStay();
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
+28
@@ -1,6 +1,8 @@
|
||||
package org.dromara.xg.controller;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -101,4 +103,30 @@ public class XgStudentLeaveController extends BaseController {
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||||
return toAjax(xgStudentLeaveService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学生ID列表和检查日期查询学生请假信息
|
||||
*
|
||||
* @param studentIds 学生ID列表(逗号分隔)
|
||||
* @param checkDate 检查日期
|
||||
*/
|
||||
@SaCheckPermission("studentLeave:studentLeave:query")
|
||||
@GetMapping("/queryByCheckDate")
|
||||
public R<Map<Long, XgStudentLeaveVo>> queryByCheckDate(
|
||||
@RequestParam String studentIds,
|
||||
@RequestParam String checkDate) {
|
||||
try {
|
||||
List<Long> studentIdList = List.of(studentIds.split(","))
|
||||
.stream()
|
||||
.map(String::trim)
|
||||
.map(Long::valueOf)
|
||||
.toList();
|
||||
LocalDateTime checkDateTime = LocalDateTime.parse(checkDate.replace(" ", "T"));
|
||||
Map<Long, XgStudentLeaveVo> result = xgStudentLeaveService.queryStudentLeavesByCheckDate(studentIdList,
|
||||
checkDateTime);
|
||||
return R.ok(result);
|
||||
} catch (Exception e) {
|
||||
return R.fail("参数格式错误:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package org.dromara.xg.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.xg.domain.bo.XgStudentStatisticsBo;
|
||||
import org.dromara.xg.domain.vo.XgStudentStatisticsVo;
|
||||
import org.dromara.xg.domain.vo.XgCascadeOptionVo;
|
||||
import org.dromara.xg.service.IXgStudentStatisticsService;
|
||||
|
||||
/**
|
||||
* 学生宿舍卫生检查、违纪记录统计
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-14
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/xg/studentStatistics")
|
||||
public class XgStudentStatisticsController extends BaseController {
|
||||
|
||||
private final IXgStudentStatisticsService studentStatisticsService;
|
||||
|
||||
/**
|
||||
* 查询学生宿舍卫生检查、违纪记录统计数据
|
||||
*/
|
||||
@SaCheckPermission("xg:studentStatistics:query")
|
||||
@PostMapping("/statistics")
|
||||
public R<XgStudentStatisticsVo> getStatistics(@Validated @RequestBody XgStudentStatisticsBo bo) {
|
||||
return R.ok(studentStatisticsService.getStatistics(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出学生宿舍卫生检查、违纪记录统计数据
|
||||
*/
|
||||
@SaCheckPermission("xg:studentStatistics:export")
|
||||
@Log(title = "学生统计数据", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void exportStatistics(@Validated @RequestBody XgStudentStatisticsBo bo,
|
||||
jakarta.servlet.http.HttpServletResponse response) {
|
||||
studentStatisticsService.exportStatistics(bo, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级学院列表
|
||||
*/
|
||||
@SaCheckPermission("xg:studentStatistics:query")
|
||||
@GetMapping("/colleges")
|
||||
public R<List<XgCascadeOptionVo>> getColleges() {
|
||||
return R.ok(studentStatisticsService.getColleges());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学院ID获取专业列表
|
||||
*/
|
||||
@SaCheckPermission("xg:studentStatistics:query")
|
||||
@GetMapping("/majors/{collegeId}")
|
||||
public R<List<XgCascadeOptionVo>> getMajorsByCollegeId(
|
||||
@NotNull(message = "学院ID不能为空") @PathVariable Long collegeId) {
|
||||
return R.ok(studentStatisticsService.getMajorsByCollegeId(collegeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据专业ID获取班级列表
|
||||
*/
|
||||
@SaCheckPermission("xg:studentStatistics:query")
|
||||
@GetMapping("/classes/{majorId}")
|
||||
public R<List<XgCascadeOptionVo>> getClassesByMajorId(
|
||||
@NotNull(message = "专业ID不能为空") @PathVariable Long majorId) {
|
||||
return R.ok(studentStatisticsService.getClassesByMajorId(majorId));
|
||||
}
|
||||
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package org.dromara.xg.domain;
|
||||
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 学生宿舍申请单表(调宿+退宿)对象 xg_dorm_application
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("xg_dorm_application")
|
||||
public class XgDormApplication extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申请名称
|
||||
*/
|
||||
private String applicationName;
|
||||
|
||||
/**
|
||||
* 申请类型(1:调宿申请 2:退宿申请)
|
||||
*/
|
||||
private String applicationType;
|
||||
|
||||
/**
|
||||
* 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回,
|
||||
* termination-已终止
|
||||
*/
|
||||
private String applicationStatus;
|
||||
|
||||
/**
|
||||
* 申请学生ID
|
||||
*/
|
||||
private Long studentId;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
private String studentNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 学生联系电话
|
||||
*/
|
||||
private String studentPhone;
|
||||
|
||||
/**
|
||||
* 学院ID
|
||||
*/
|
||||
private Long collegeId;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
private String collegeName;
|
||||
|
||||
/**
|
||||
* 专业ID
|
||||
*/
|
||||
private Long majorId;
|
||||
|
||||
/**
|
||||
* 专业名称
|
||||
*/
|
||||
private String majorName;
|
||||
|
||||
/**
|
||||
* 班级ID
|
||||
*/
|
||||
private Long classId;
|
||||
|
||||
/**
|
||||
* 班级名称
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 当前校区ID
|
||||
*/
|
||||
private Long currentCampusId;
|
||||
|
||||
/**
|
||||
* 当前校区名称
|
||||
*/
|
||||
private String currentCampusName;
|
||||
|
||||
/**
|
||||
* 当前宿舍楼ID
|
||||
*/
|
||||
private Long currentBuildingId;
|
||||
|
||||
/**
|
||||
* 当前宿舍楼名称
|
||||
*/
|
||||
private String currentBuildingName;
|
||||
|
||||
/**
|
||||
* 当前房间ID
|
||||
*/
|
||||
private Long currentRoomId;
|
||||
|
||||
/**
|
||||
* 当前房间号
|
||||
*/
|
||||
private String currentRoomNo;
|
||||
|
||||
/**
|
||||
* 当前床位ID
|
||||
*/
|
||||
private Long currentBedId;
|
||||
|
||||
/**
|
||||
* 当前床位号
|
||||
*/
|
||||
private String currentBedNo;
|
||||
|
||||
/**
|
||||
* 目标校区ID
|
||||
*/
|
||||
private Long targetCampusId;
|
||||
|
||||
/**
|
||||
* 目标校区名称
|
||||
*/
|
||||
private String targetCampusName;
|
||||
|
||||
/**
|
||||
* 目标宿舍楼ID
|
||||
*/
|
||||
private Long targetBuildingId;
|
||||
|
||||
/**
|
||||
* 目标宿舍楼名称
|
||||
*/
|
||||
private String targetBuildingName;
|
||||
|
||||
/**
|
||||
* 目标房间ID
|
||||
*/
|
||||
private Long targetRoomId;
|
||||
|
||||
/**
|
||||
* 目标房间号
|
||||
*/
|
||||
private String targetRoomNo;
|
||||
|
||||
/**
|
||||
* 目标床位ID
|
||||
*/
|
||||
private Long targetBedId;
|
||||
|
||||
/**
|
||||
* 目标床位号
|
||||
*/
|
||||
private String targetBedNo;
|
||||
|
||||
/**
|
||||
* 详细原因说明
|
||||
*/
|
||||
private String reasonDetail;
|
||||
|
||||
/**
|
||||
* 申请时间
|
||||
*/
|
||||
private Date applyTime;
|
||||
|
||||
/**
|
||||
* 附件OSS文件ID,多个用逗号分隔
|
||||
*/
|
||||
private String attachmentOssIds;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
*/
|
||||
private String extField1;
|
||||
|
||||
/**
|
||||
* 扩展字段2
|
||||
*/
|
||||
private String extField2;
|
||||
|
||||
/**
|
||||
* 扩展字段3
|
||||
*/
|
||||
private String extField3;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private String deleted;
|
||||
|
||||
}
|
||||
+11
-1
@@ -111,7 +111,7 @@ public class XgDormHygieneCheck extends TenantEntity {
|
||||
private String campusName;
|
||||
|
||||
/**
|
||||
* 宿舍总分(0-100分)
|
||||
* 宿舍扣分(0-100分)
|
||||
*/
|
||||
private Double totalScore;
|
||||
|
||||
@@ -140,4 +140,14 @@ public class XgDormHygieneCheck extends TenantEntity {
|
||||
*/
|
||||
private String checkRemarks;
|
||||
|
||||
/**
|
||||
* 违纪类型ID,关联xskh_discipline_type.id
|
||||
*/
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
private String disciplineTypeName;
|
||||
|
||||
}
|
||||
+1
-1
@@ -94,7 +94,7 @@ public class XgDormHygieneStudentScore extends TenantEntity {
|
||||
private String bedCode;
|
||||
|
||||
/**
|
||||
* 学生个人得分(0-100分)
|
||||
* 学生个人扣分(0-100分)
|
||||
*/
|
||||
private Double studentScore;
|
||||
|
||||
|
||||
@@ -67,6 +67,11 @@ public class XgDormRoom extends TenantEntity {
|
||||
*/
|
||||
private String roomStatus;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
private Long sort;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package org.dromara.xg.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.math.BigDecimal;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查违纪关联对象 xg_hygiene_check_violation
|
||||
*
|
||||
* @author 吴自龙
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("xg_hygiene_check_violation")
|
||||
public class XgHygieneCheckViolation extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查ID,关联xg_dorm_hygiene_check.id
|
||||
*/
|
||||
private Long hygieneCheckId;
|
||||
|
||||
/**
|
||||
* 违纪记录ID,关联xskh_discipline_record.id
|
||||
*/
|
||||
private Long disciplineRecordId;
|
||||
|
||||
/**
|
||||
* 学生ID,关联xg_student_msg.id
|
||||
*/
|
||||
private Long studentId;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
private String studentNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 违纪类型ID,关联xskh_discipline_type.id
|
||||
*/
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
private String disciplineTypeName;
|
||||
|
||||
/**
|
||||
* 违纪来源(1宿舍卫生检查 2其他)
|
||||
*/
|
||||
private String violationSource;
|
||||
|
||||
/**
|
||||
* 状态(1生效 2已撤销)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private Long deleted;
|
||||
|
||||
}
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
package org.dromara.xg.domain.bo;
|
||||
|
||||
import org.dromara.xg.domain.XgDormApplication;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 学生宿舍申请单表(调宿+退宿)业务对象 xg_dorm_application
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = XgDormApplication.class, reverseConvertGenerate = false)
|
||||
public class XgDormApplicationBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申请名称
|
||||
*/
|
||||
@NotBlank(message = "申请名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String applicationName;
|
||||
|
||||
/**
|
||||
* 申请类型(1:调宿申请 2:退宿申请)
|
||||
*/
|
||||
@NotBlank(message = "申请类型不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String applicationType;
|
||||
|
||||
/**
|
||||
* 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回,
|
||||
* termination-已终止
|
||||
*/
|
||||
private String applicationStatus;
|
||||
|
||||
/**
|
||||
* 申请学生ID
|
||||
*/
|
||||
@NotNull(message = "申请学生ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long studentId;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
@NotBlank(message = "学号不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String studentNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
@NotBlank(message = "学生姓名不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 学生联系电话
|
||||
*/
|
||||
private String studentPhone;
|
||||
|
||||
/**
|
||||
* 学院ID
|
||||
*/
|
||||
@NotNull(message = "学院ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long collegeId;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
private String collegeName;
|
||||
|
||||
/**
|
||||
* 专业ID
|
||||
*/
|
||||
private Long majorId;
|
||||
|
||||
/**
|
||||
* 专业名称
|
||||
*/
|
||||
private String majorName;
|
||||
|
||||
/**
|
||||
* 班级ID
|
||||
*/
|
||||
private Long classId;
|
||||
|
||||
/**
|
||||
* 班级名称
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 当前校区ID
|
||||
*/
|
||||
private Long currentCampusId;
|
||||
|
||||
/**
|
||||
* 当前校区名称
|
||||
*/
|
||||
private String currentCampusName;
|
||||
|
||||
/**
|
||||
* 当前宿舍楼ID
|
||||
*/
|
||||
private Long currentBuildingId;
|
||||
|
||||
/**
|
||||
* 当前宿舍楼名称
|
||||
*/
|
||||
private String currentBuildingName;
|
||||
|
||||
/**
|
||||
* 当前房间ID
|
||||
*/
|
||||
private Long currentRoomId;
|
||||
|
||||
/**
|
||||
* 当前房间号
|
||||
*/
|
||||
private String currentRoomNo;
|
||||
|
||||
/**
|
||||
* 当前床位ID
|
||||
*/
|
||||
private Long currentBedId;
|
||||
|
||||
/**
|
||||
* 当前床位号
|
||||
*/
|
||||
private String currentBedNo;
|
||||
|
||||
/**
|
||||
* 目标校区ID
|
||||
*/
|
||||
private Long targetCampusId;
|
||||
|
||||
/**
|
||||
* 目标校区名称
|
||||
*/
|
||||
private String targetCampusName;
|
||||
|
||||
/**
|
||||
* 目标宿舍楼ID
|
||||
*/
|
||||
private Long targetBuildingId;
|
||||
|
||||
/**
|
||||
* 目标宿舍楼名称
|
||||
*/
|
||||
private String targetBuildingName;
|
||||
|
||||
/**
|
||||
* 目标房间ID
|
||||
*/
|
||||
private Long targetRoomId;
|
||||
|
||||
/**
|
||||
* 目标房间号
|
||||
*/
|
||||
private String targetRoomNo;
|
||||
|
||||
/**
|
||||
* 目标床位ID
|
||||
*/
|
||||
private Long targetBedId;
|
||||
|
||||
/**
|
||||
* 目标床位号
|
||||
*/
|
||||
private String targetBedNo;
|
||||
|
||||
/**
|
||||
* 详细原因说明
|
||||
*/
|
||||
@NotBlank(message = "详细原因说明不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String reasonDetail;
|
||||
|
||||
/**
|
||||
* 申请时间
|
||||
*/
|
||||
@NotNull(message = "申请时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date applyTime;
|
||||
|
||||
/**
|
||||
* 附件OSS文件ID,多个用逗号分隔
|
||||
*/
|
||||
private String attachmentOssIds;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
*/
|
||||
private String extField1;
|
||||
|
||||
/**
|
||||
* 扩展字段2
|
||||
*/
|
||||
private String extField2;
|
||||
|
||||
/**
|
||||
* 扩展字段3
|
||||
*/
|
||||
private String extField3;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
private String deleted;
|
||||
|
||||
}
|
||||
+2
-1
@@ -41,7 +41,8 @@ public class XgDormBuildingBo extends BaseEntity {
|
||||
/**
|
||||
* 所属宿舍区ID
|
||||
*/
|
||||
@NotNull(message = "所属宿舍区ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
// @NotNull(message = "所属宿舍区ID不能为空", groups = { AddGroup.class, EditGroup.class
|
||||
// }) // 宿舍区轻量化改造:移除必填验证
|
||||
private Long areaId;
|
||||
|
||||
/**
|
||||
|
||||
+9
@@ -147,5 +147,14 @@ public class XgDormHygieneCheckBo extends BaseEntity {
|
||||
*/
|
||||
private String checkRemarks;
|
||||
|
||||
/**
|
||||
* 违纪类型ID,关联xskh_discipline_type.id
|
||||
*/
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
private String disciplineTypeName;
|
||||
|
||||
}
|
||||
+17
-1
@@ -149,6 +149,16 @@ public class XgDormHygieneCheckCompleteBo {
|
||||
*/
|
||||
private String checkRemarks;
|
||||
|
||||
/**
|
||||
* 违纪类型ID,关联xskh_discipline_type.id
|
||||
*/
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
private String disciplineTypeName;
|
||||
|
||||
/**
|
||||
* 学生评分数据列表
|
||||
*/
|
||||
@@ -156,6 +166,12 @@ public class XgDormHygieneCheckCompleteBo {
|
||||
@NotEmpty(message = "学生评分数据不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private List<XgDormHygieneStudentScoreBo> studentScores;
|
||||
|
||||
/**
|
||||
* 违纪记录数据列表
|
||||
*/
|
||||
@Valid
|
||||
private List<XgViolationDataBo> violationData;
|
||||
|
||||
// 基础字段(用于VO类映射)
|
||||
private Long createBy;
|
||||
|
||||
@@ -166,4 +182,4 @@ public class XgDormHygieneCheckCompleteBo {
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -70,6 +70,11 @@ public class XgDormRoomBo extends BaseEntity {
|
||||
*/
|
||||
private String roomStatus;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
private Long sort;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@@ -80,5 +85,4 @@ public class XgDormRoomBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package org.dromara.xg.domain.bo;
|
||||
|
||||
import org.dromara.xg.domain.XgHygieneCheckViolation;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查违纪关联业务对象 xg_hygiene_check_violation
|
||||
*
|
||||
* @author 吴自龙
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = XgHygieneCheckViolation.class, reverseConvertGenerate = false)
|
||||
public class XgHygieneCheckViolationBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查ID,关联xg_dorm_hygiene_check.id
|
||||
*/
|
||||
private Long hygieneCheckId;
|
||||
|
||||
/**
|
||||
* 违纪记录ID,关联xskh_discipline_record.id
|
||||
*/
|
||||
private Long disciplineRecordId;
|
||||
|
||||
/**
|
||||
* 学生ID,关联xg_student_msg.id
|
||||
*/
|
||||
@NotNull(message = "学生ID不能为空")
|
||||
private Long studentId;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
@NotBlank(message = "学号不能为空")
|
||||
private String studentNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
@NotBlank(message = "学生姓名不能为空")
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 违纪类型ID,关联xskh_discipline_type.id
|
||||
*/
|
||||
@NotNull(message = "违纪类型ID不能为空")
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
@NotBlank(message = "违纪类型名称不能为空")
|
||||
private String disciplineTypeName;
|
||||
|
||||
/**
|
||||
* 违纪来源(1宿舍卫生检查 2其他)
|
||||
*/
|
||||
private String violationSource;
|
||||
|
||||
/**
|
||||
* 状态(1生效 2已撤销)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 分页大小
|
||||
*/
|
||||
@JsonIgnore
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 当前页数
|
||||
*/
|
||||
@JsonIgnore
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
@JsonIgnore
|
||||
private String orderByColumn;
|
||||
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
@JsonIgnore
|
||||
private String isAsc;
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package org.dromara.xg.domain.bo;
|
||||
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 学生宿舍卫生检查、违纪记录统计查询业务对象
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-14
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class XgStudentStatisticsBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 统计开始时间
|
||||
*/
|
||||
@NotNull(message = "统计开始时间不能为空")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 统计结束时间
|
||||
*/
|
||||
@NotNull(message = "统计结束时间不能为空")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 二级学院ID
|
||||
*/
|
||||
private Long collegeId;
|
||||
|
||||
/**
|
||||
* 专业ID
|
||||
*/
|
||||
private Long majorId;
|
||||
|
||||
/**
|
||||
* 班级ID
|
||||
*/
|
||||
private Long classId;
|
||||
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package org.dromara.xg.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 违纪数据业务对象(用于前端传输)
|
||||
*
|
||||
* @author 吴自龙
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
public class XgViolationDataBo {
|
||||
|
||||
/**
|
||||
* 违纪记录ID(用于更新操作,新增时为null)
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 违纪记录ID,关联xskh_discipline_record.id(用于更新违纪记录)
|
||||
*/
|
||||
private Long disciplineRecordId;
|
||||
|
||||
/**
|
||||
* 学生ID,关联xg_student_msg.id
|
||||
*/
|
||||
@NotNull(message = "学生ID不能为空")
|
||||
private Long studentId;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
@NotBlank(message = "学号不能为空")
|
||||
private String studentNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
@NotBlank(message = "学生姓名不能为空")
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 班级ID
|
||||
*/
|
||||
private Long classId;
|
||||
|
||||
/**
|
||||
* 班级名称
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 专业ID
|
||||
*/
|
||||
private Long majorId;
|
||||
|
||||
/**
|
||||
* 专业名称
|
||||
*/
|
||||
private String majorName;
|
||||
|
||||
/**
|
||||
* 学院ID
|
||||
*/
|
||||
private Long collegeId;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
private String collegeName;
|
||||
|
||||
/**
|
||||
* 违纪类型ID,关联xskh_discipline_type.id
|
||||
*/
|
||||
@NotNull(message = "违纪类型ID不能为空")
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
@NotBlank(message = "违纪类型名称不能为空")
|
||||
private String disciplineTypeName;
|
||||
|
||||
/**
|
||||
* 违纪日期
|
||||
*/
|
||||
private String disciplineDate;
|
||||
|
||||
/**
|
||||
* 违纪原因
|
||||
*/
|
||||
@NotBlank(message = "违纪原因不能为空")
|
||||
private String disciplineReason;
|
||||
|
||||
/**
|
||||
* 违纪内容
|
||||
*/
|
||||
private String disciplineContent;
|
||||
|
||||
/**
|
||||
* 扣分分值
|
||||
*/
|
||||
private BigDecimal scoreDeduction;
|
||||
|
||||
/**
|
||||
* 违纪来源(1宿舍卫生检查 2其他)
|
||||
*/
|
||||
private String violationSource;
|
||||
|
||||
/**
|
||||
* 状态(1生效 2已撤销)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 附件OSS ID列表,逗号分隔
|
||||
*/
|
||||
private String attachmentOssIds;
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 宿舍楼树形结构简化VO
|
||||
* 用于简化界面显示,只显示校区→宿舍楼的二级结构,隐藏宿舍区层级
|
||||
*
|
||||
* @author wuzilong
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
public class DormBuildingTreeSimplifiedVo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String label;
|
||||
|
||||
/**
|
||||
* 节点类型 (campus:校区, building:宿舍楼)
|
||||
* 注意:简化版本中移除了 area:宿舍区 类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 是否禁用节点(校区节点禁用点击,宿舍楼节点可点击)
|
||||
*/
|
||||
private Boolean disabled;
|
||||
|
||||
/**
|
||||
* 宿舍楼层数(仅宿舍楼节点有此字段)
|
||||
*/
|
||||
private Integer buildingFloors;
|
||||
|
||||
/**
|
||||
* 宿舍楼类型(1男生楼 2女生楼 3混合楼)(仅宿舍楼节点有此字段)
|
||||
*/
|
||||
private String buildingType;
|
||||
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<DormBuildingTreeSimplifiedVo> children;
|
||||
|
||||
/**
|
||||
* 从完整树形VO转换为简化树形VO
|
||||
* 将三级结构(校区→宿舍区→宿舍楼)转换为二级结构(校区→宿舍楼)
|
||||
*/
|
||||
public static DormBuildingTreeSimplifiedVo fromFullTreeVo(DormBuildingTreeVo fullTreeVo) {
|
||||
if (fullTreeVo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DormBuildingTreeSimplifiedVo simplified = new DormBuildingTreeSimplifiedVo();
|
||||
simplified.setId(fullTreeVo.getId());
|
||||
simplified.setLabel(fullTreeVo.getLabel());
|
||||
simplified.setBuildingFloors(fullTreeVo.getBuildingFloors());
|
||||
simplified.setBuildingType(fullTreeVo.getBuildingType());
|
||||
|
||||
// 根据节点类型进行转换
|
||||
if ("campus".equals(fullTreeVo.getType())) {
|
||||
// 校区节点:保持不变
|
||||
simplified.setType("campus");
|
||||
simplified.setDisabled(true);
|
||||
// 处理子节点:跳过宿舍区层级,直接提取宿舍楼
|
||||
simplified.setChildren(flattenAreaChildren(fullTreeVo.getChildren()));
|
||||
} else if ("building".equals(fullTreeVo.getType())) {
|
||||
// 宿舍楼节点:保持不变
|
||||
simplified.setType("building");
|
||||
simplified.setDisabled(false);
|
||||
}
|
||||
|
||||
return simplified;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扁平化宿舍区子节点,将宿舍区下的宿舍楼直接提升为校区的子节点
|
||||
*/
|
||||
private static List<DormBuildingTreeSimplifiedVo> flattenAreaChildren(List<DormBuildingTreeVo> areaNodes) {
|
||||
if (areaNodes == null || areaNodes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return areaNodes.stream()
|
||||
.filter(node -> "area".equals(node.getType())) // 找到宿舍区节点
|
||||
.flatMap(areaNode -> areaNode.getChildren() != null ? areaNode.getChildren().stream()
|
||||
: java.util.stream.Stream.empty()) // 提取宿舍楼
|
||||
.filter(buildingNode -> "building".equals(buildingNode.getType())) // 确保是宿舍楼节点
|
||||
.map(DormBuildingTreeSimplifiedVo::fromFullTreeVo) // 转换为简化VO
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 级联选择器选项视图对象
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-14
|
||||
*/
|
||||
@Data
|
||||
public class XgCascadeOptionVo {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 显示名称
|
||||
*/
|
||||
private String label;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import org.dromara.xg.domain.XgDormApplication;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 学生宿舍申请单表(调宿+退宿)视图对象 xg_dorm_application
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = XgDormApplication.class)
|
||||
public class XgDormApplicationVo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID(雪花ID)
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 申请名称
|
||||
*/
|
||||
private String applicationName;
|
||||
|
||||
/**
|
||||
* 申请类型(1:调宿申请 2:退宿申请)
|
||||
*/
|
||||
private String applicationType;
|
||||
|
||||
/**
|
||||
* 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回,
|
||||
* termination-已终止
|
||||
*/
|
||||
private String applicationStatus;
|
||||
|
||||
/**
|
||||
* 申请学生ID
|
||||
*/
|
||||
private Long studentId;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
private String studentNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 学生联系电话
|
||||
*/
|
||||
private String studentPhone;
|
||||
|
||||
/**
|
||||
* 学院ID
|
||||
*/
|
||||
private Long collegeId;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
private String collegeName;
|
||||
|
||||
/**
|
||||
* 专业ID
|
||||
*/
|
||||
private Long majorId;
|
||||
|
||||
/**
|
||||
* 专业名称
|
||||
*/
|
||||
private String majorName;
|
||||
|
||||
/**
|
||||
* 班级ID
|
||||
*/
|
||||
private Long classId;
|
||||
|
||||
/**
|
||||
* 班级名称
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 当前校区ID
|
||||
*/
|
||||
private Long currentCampusId;
|
||||
|
||||
/**
|
||||
* 当前校区名称
|
||||
*/
|
||||
private String currentCampusName;
|
||||
|
||||
/**
|
||||
* 当前宿舍楼ID
|
||||
*/
|
||||
private Long currentBuildingId;
|
||||
|
||||
/**
|
||||
* 当前宿舍楼名称
|
||||
*/
|
||||
private String currentBuildingName;
|
||||
|
||||
/**
|
||||
* 当前房间ID
|
||||
*/
|
||||
private Long currentRoomId;
|
||||
|
||||
/**
|
||||
* 当前房间号
|
||||
*/
|
||||
private String currentRoomNo;
|
||||
|
||||
/**
|
||||
* 当前床位ID
|
||||
*/
|
||||
private Long currentBedId;
|
||||
|
||||
/**
|
||||
* 当前床位号
|
||||
*/
|
||||
private String currentBedNo;
|
||||
|
||||
/**
|
||||
* 目标校区ID
|
||||
*/
|
||||
private Long targetCampusId;
|
||||
|
||||
/**
|
||||
* 目标校区名称
|
||||
*/
|
||||
private String targetCampusName;
|
||||
|
||||
/**
|
||||
* 目标宿舍楼ID
|
||||
*/
|
||||
private Long targetBuildingId;
|
||||
|
||||
/**
|
||||
* 目标宿舍楼名称
|
||||
*/
|
||||
private String targetBuildingName;
|
||||
|
||||
/**
|
||||
* 目标房间ID
|
||||
*/
|
||||
private Long targetRoomId;
|
||||
|
||||
/**
|
||||
* 目标房间号
|
||||
*/
|
||||
private String targetRoomNo;
|
||||
|
||||
/**
|
||||
* 目标床位ID
|
||||
*/
|
||||
private Long targetBedId;
|
||||
|
||||
/**
|
||||
* 目标床位号
|
||||
*/
|
||||
private String targetBedNo;
|
||||
|
||||
/**
|
||||
* 详细原因说明
|
||||
*/
|
||||
private String reasonDetail;
|
||||
|
||||
/**
|
||||
* 申请时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date applyTime;
|
||||
|
||||
/**
|
||||
* 附件OSS文件ID,多个用逗号分隔
|
||||
*/
|
||||
private String attachmentOssIds;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
*/
|
||||
private String extField1;
|
||||
|
||||
/**
|
||||
* 扩展字段2
|
||||
*/
|
||||
private String extField2;
|
||||
|
||||
/**
|
||||
* 扩展字段3
|
||||
*/
|
||||
private String extField3;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
private String deleted;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import org.dromara.xg.domain.XgDormBuilding;
|
||||
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.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 宿舍楼信息简化视图对象 xg_dorm_building
|
||||
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = XgDormBuilding.class)
|
||||
public class XgDormBuildingSimplifiedVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 宿舍楼编码
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍楼编码")
|
||||
private String buildingCode;
|
||||
|
||||
/**
|
||||
* 宿舍楼名称
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍楼名称")
|
||||
private String buildingName;
|
||||
|
||||
/**
|
||||
* 所属校区ID
|
||||
*/
|
||||
@ExcelProperty(value = "所属校区ID")
|
||||
private Long campusId;
|
||||
|
||||
/**
|
||||
* 校区名称
|
||||
*/
|
||||
@ExcelProperty(value = "校区名称")
|
||||
private String campusName;
|
||||
|
||||
/**
|
||||
* 宿舍楼类型(1男生楼 2女生楼 3混合楼)
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍楼类型(1男生楼 2女生楼 3混合楼)", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "dorm_type")
|
||||
private String buildingType;
|
||||
|
||||
/**
|
||||
* 宿舍楼描述
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍楼描述")
|
||||
private String buildingDescription;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
@ExcelProperty(value = "排序号")
|
||||
private Long sortOrder;
|
||||
|
||||
/**
|
||||
* 总楼层数
|
||||
*/
|
||||
@ExcelProperty(value = "总楼层数")
|
||||
private Integer floors;
|
||||
|
||||
// 基础字段
|
||||
private Long createBy; // 创建者
|
||||
private Date createTime; // 创建时间
|
||||
private Long updateBy; // 更新者
|
||||
private Date updateTime; // 更新时间
|
||||
|
||||
/**
|
||||
* 从完整VO转换为简化VO
|
||||
*/
|
||||
public static XgDormBuildingSimplifiedVo fromFullVo(XgDormBuildingVo fullVo) {
|
||||
if (fullVo == null) {
|
||||
return null;
|
||||
}
|
||||
XgDormBuildingSimplifiedVo simplified = new XgDormBuildingSimplifiedVo();
|
||||
BeanUtils.copyProperties(fullVo, simplified);
|
||||
return simplified;
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import org.dromara.xg.domain.XgDormHygieneCheck;
|
||||
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.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查简化视图对象 xg_dorm_hygiene_check
|
||||
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼→房间结构
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = XgDormHygieneCheck.class)
|
||||
public class XgDormHygieneCheckSimplifiedVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除")
|
||||
private Long deleted;
|
||||
|
||||
/**
|
||||
* 检查标题
|
||||
*/
|
||||
@ExcelProperty(value = "检查标题")
|
||||
private String checkTitle;
|
||||
|
||||
/**
|
||||
* 检查日期
|
||||
*/
|
||||
@ExcelProperty(value = "检查日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date checkDate;
|
||||
|
||||
/**
|
||||
* 检查员ID,关联sys_user.id
|
||||
*/
|
||||
@ExcelProperty(value = "检查员ID,关联sys_user.id")
|
||||
private Long checkerId;
|
||||
|
||||
/**
|
||||
* 检查员姓名
|
||||
*/
|
||||
@ExcelProperty(value = "检查员姓名")
|
||||
private String checkerName;
|
||||
|
||||
/**
|
||||
* 检查类型(1日常检查 2专项检查 3突击检查 4复查)
|
||||
*/
|
||||
@ExcelProperty(value = "检查类型", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "xg_dorm_hygiene_check_type")
|
||||
private String checkType;
|
||||
|
||||
/**
|
||||
* 检查级别(1宿管员 2学院 3学校)
|
||||
*/
|
||||
@ExcelProperty(value = "检查级别", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "xg_dorm_hygiene_check_level")
|
||||
private String checkLevel;
|
||||
|
||||
/**
|
||||
* 宿舍房间ID,关联xg_dorm_room.id
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍房间ID,关联xg_dorm_room.id")
|
||||
private Long roomId;
|
||||
|
||||
/**
|
||||
* 房间编号
|
||||
*/
|
||||
@ExcelProperty(value = "房间编号")
|
||||
private String roomCode;
|
||||
|
||||
/**
|
||||
* 房间名称
|
||||
*/
|
||||
@ExcelProperty(value = "房间名称")
|
||||
private String roomName;
|
||||
|
||||
/**
|
||||
* 宿舍楼ID,关联xg_dorm_building.id
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍楼ID,关联xg_dorm_building.id")
|
||||
private Long buildingId;
|
||||
|
||||
/**
|
||||
* 宿舍楼名称
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍楼名称")
|
||||
private String buildingName;
|
||||
|
||||
/**
|
||||
* 校区ID,关联xg_dorm_campus.id
|
||||
*/
|
||||
@ExcelProperty(value = "校区ID,关联xg_dorm_campus.id")
|
||||
private Long campusId;
|
||||
|
||||
/**
|
||||
* 校区名称
|
||||
*/
|
||||
@ExcelProperty(value = "校区名称")
|
||||
private String campusName;
|
||||
|
||||
/**
|
||||
* 宿舍总分(0-100分)
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍总分", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "0=-100分")
|
||||
private Double totalScore;
|
||||
|
||||
/**
|
||||
* 卫生等级(A优秀 B良好 C合格 D不合格)
|
||||
*/
|
||||
@ExcelProperty(value = "卫生等级", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "xg_dorm_hygiene_grade")
|
||||
private String hygieneGrade;
|
||||
|
||||
/**
|
||||
* 附件OSS ID列表,逗号分隔
|
||||
*/
|
||||
@ExcelProperty(value = "附件OSS ID列表,逗号分隔")
|
||||
private String attachmentIds;
|
||||
|
||||
/**
|
||||
* 检查总结
|
||||
*/
|
||||
@ExcelProperty(value = "检查总结")
|
||||
private String checkSummary;
|
||||
|
||||
/**
|
||||
* 整改建议
|
||||
*/
|
||||
@ExcelProperty(value = "整改建议")
|
||||
private String improvementSuggestions;
|
||||
|
||||
/**
|
||||
* 检查备注
|
||||
*/
|
||||
@ExcelProperty(value = "检查备注")
|
||||
private String checkRemarks;
|
||||
|
||||
// 基础字段
|
||||
private Long createBy; // 创建者
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime; // 创建时间
|
||||
private Long updateBy; // 更新者
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime; // 更新时间
|
||||
|
||||
/**
|
||||
* 从完整VO转换为简化VO
|
||||
*/
|
||||
public static XgDormHygieneCheckSimplifiedVo fromFullVo(XgDormHygieneCheckVo fullVo) {
|
||||
if (fullVo == null) {
|
||||
return null;
|
||||
}
|
||||
XgDormHygieneCheckSimplifiedVo simplified = new XgDormHygieneCheckSimplifiedVo();
|
||||
BeanUtils.copyProperties(fullVo, simplified);
|
||||
return simplified;
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -14,8 +14,6 @@ import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查视图对象 xg_dorm_hygiene_check
|
||||
*
|
||||
@@ -173,5 +171,16 @@ public class XgDormHygieneCheckVo implements Serializable {
|
||||
@ExcelProperty(value = "检查备注")
|
||||
private String checkRemarks;
|
||||
|
||||
/**
|
||||
* 违纪类型ID,关联xskh_discipline_type.id
|
||||
*/
|
||||
@ExcelProperty(value = "违纪类型ID")
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
@ExcelProperty(value = "违纪类型名称")
|
||||
private String disciplineTypeName;
|
||||
|
||||
}
|
||||
+28
-2
@@ -11,8 +11,7 @@ import lombok.Data;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
/**
|
||||
* 宿舍房间信息视图对象 xg_dorm_room
|
||||
@@ -82,6 +81,12 @@ public class XgDormRoomVo implements Serializable {
|
||||
@ExcelProperty(value = "房间状态(0可用 1维修中 2停用)")
|
||||
private String roomStatus;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
@ExcelProperty(value = "排序号")
|
||||
private Long sort;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@@ -94,5 +99,26 @@ public class XgDormRoomVo implements Serializable {
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+1
-12
@@ -14,8 +14,6 @@ import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 假期留宿管理视图对象 xg_holiday_stay
|
||||
*
|
||||
@@ -39,8 +37,6 @@ public class XgHolidayStayVo implements Serializable {
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除")
|
||||
private Long deleted;
|
||||
|
||||
/**
|
||||
@@ -179,8 +175,7 @@ public class XgHolidayStayVo implements Serializable {
|
||||
/**
|
||||
* 留宿原因(必填)
|
||||
*/
|
||||
@ExcelProperty(value = "留宿原因", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "必=填")
|
||||
@ExcelProperty(value = "留宿原因")
|
||||
private String stayReason;
|
||||
|
||||
/**
|
||||
@@ -218,26 +213,21 @@ public class XgHolidayStayVo implements Serializable {
|
||||
/**
|
||||
* 附件OSS文件ID,多个用逗号分隔(申请材料、证明文件等)
|
||||
*/
|
||||
@ExcelProperty(value = "附件OSS文件ID,多个用逗号分隔", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "申=请材料、证明文件等")
|
||||
private String attachmentOssIds;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
*/
|
||||
@ExcelProperty(value = "扩展字段1")
|
||||
private String extField1;
|
||||
|
||||
/**
|
||||
* 扩展字段2
|
||||
*/
|
||||
@ExcelProperty(value = "扩展字段2")
|
||||
private String extField2;
|
||||
|
||||
/**
|
||||
* 扩展字段3
|
||||
*/
|
||||
@ExcelProperty(value = "扩展字段3")
|
||||
private String extField3;
|
||||
|
||||
/**
|
||||
@@ -246,5 +236,4 @@ public class XgHolidayStayVo implements Serializable {
|
||||
@ExcelProperty(value = "备注信息")
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import org.dromara.xg.domain.XgHygieneCheckViolation;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.time.LocalDate;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查违纪关联视图对象 xg_hygiene_check_violation
|
||||
*
|
||||
* @author 吴自龙
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = XgHygieneCheckViolation.class)
|
||||
public class XgHygieneCheckViolationVo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查ID
|
||||
*/
|
||||
private Long hygieneCheckId;
|
||||
|
||||
/**
|
||||
* 违纪记录ID
|
||||
*/
|
||||
private Long disciplineRecordId;
|
||||
|
||||
/**
|
||||
* 学生ID
|
||||
*/
|
||||
private Long studentId;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
private String studentNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 违纪类型ID
|
||||
*/
|
||||
private Long disciplineTypeId;
|
||||
|
||||
/**
|
||||
* 违纪类型名称
|
||||
*/
|
||||
private String disciplineTypeName;
|
||||
|
||||
/**
|
||||
* 违纪来源
|
||||
*/
|
||||
private String violationSource;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
// ============ 新增违纪详情字段 ============
|
||||
|
||||
/**
|
||||
* 违纪处分日期
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate disciplineDate;
|
||||
|
||||
/**
|
||||
* 违纪处分原因
|
||||
*/
|
||||
private String disciplineReason;
|
||||
|
||||
/**
|
||||
* 违纪处分内容
|
||||
*/
|
||||
private String disciplineContent;
|
||||
|
||||
/**
|
||||
* 扣分分值
|
||||
*/
|
||||
private BigDecimal scoreDeduction;
|
||||
|
||||
/**
|
||||
* 附件OSS ID列表,逗号分隔
|
||||
*/
|
||||
private String attachmentOssIds;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学生宿舍卫生检查、违纪记录统计视图对象
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-14
|
||||
*/
|
||||
@Data
|
||||
public class XgStudentStatisticsVo {
|
||||
|
||||
/**
|
||||
* 统计维度 (college-学院, major-专业, class-班级, classDetail-班级详情)
|
||||
*/
|
||||
private String dimension;
|
||||
|
||||
/**
|
||||
* 统计图表数据
|
||||
*/
|
||||
private List<StatisticsChartData> chartData;
|
||||
|
||||
/**
|
||||
* 明细数据
|
||||
*/
|
||||
private List<StatisticsDetailData> detailData;
|
||||
|
||||
/**
|
||||
* 图表数据内部类
|
||||
*/
|
||||
@Data
|
||||
public static class StatisticsChartData {
|
||||
/**
|
||||
* 名称 (学院名、专业名、班级名)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 宿舍扣分总数
|
||||
*/
|
||||
private Integer dormDeductionCount;
|
||||
|
||||
/**
|
||||
* 宿舍违纪记录总数
|
||||
*/
|
||||
private Integer dormViolationCount;
|
||||
|
||||
/**
|
||||
* 宿舍违纪记录扣分总数
|
||||
*/
|
||||
private Double dormViolationDeduction;
|
||||
|
||||
/**
|
||||
* 教学区违纪记录总数
|
||||
*/
|
||||
private Integer teachingViolationCount;
|
||||
|
||||
/**
|
||||
* 教学区违纪记录扣分总数
|
||||
*/
|
||||
private Double teachingViolationDeduction;
|
||||
}
|
||||
|
||||
/**
|
||||
* 明细数据内部类
|
||||
*/
|
||||
@Data
|
||||
public static class StatisticsDetailData {
|
||||
/**
|
||||
* ID (不导出到Excel)
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
@ExcelProperty(value = "名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 上级名称(如学院下的专业名称显示学院名)
|
||||
*/
|
||||
@ExcelProperty(value = "上级")
|
||||
private String parentName;
|
||||
|
||||
/**
|
||||
* 宿舍扣分总数
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍扣分总数")
|
||||
private Integer dormDeductionCount;
|
||||
|
||||
/**
|
||||
* 宿舍违纪记录总数
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍违纪记录总数")
|
||||
private Integer dormViolationCount;
|
||||
|
||||
/**
|
||||
* 宿舍违纪记录扣分总数
|
||||
*/
|
||||
@ExcelProperty(value = "宿舍违纪记录扣分总数")
|
||||
private Double dormViolationDeduction;
|
||||
|
||||
/**
|
||||
* 教学区违纪记录总数
|
||||
*/
|
||||
@ExcelProperty(value = "教学区违纪记录总数")
|
||||
private Integer teachingViolationCount;
|
||||
|
||||
/**
|
||||
* 教学区违纪记录扣分总数
|
||||
*/
|
||||
@ExcelProperty(value = "教学区违纪记录扣分总数")
|
||||
private Double teachingViolationDeduction;
|
||||
|
||||
/**
|
||||
* 合计扣分
|
||||
*/
|
||||
@ExcelProperty(value = "违纪合计扣分")
|
||||
private Double totalDeduction;
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package org.dromara.xg.mapper;
|
||||
|
||||
import org.dromara.xg.domain.XgDormApplication;
|
||||
import org.dromara.xg.domain.vo.XgDormApplicationVo;
|
||||
import org.dromara.xg.domain.vo.StudentOptionVo;
|
||||
import org.dromara.xg.domain.bo.StudentOptionBo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 学生宿舍申请单表(调宿+退宿)Mapper接口
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
public interface XgDormApplicationMapper extends BaseMapperPlus<XgDormApplication, XgDormApplicationVo> {
|
||||
|
||||
/**
|
||||
* 查询学生选择器选项列表
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param bo 查询条件
|
||||
* @return 学生选择器选项分页列表
|
||||
*/
|
||||
Page<StudentOptionVo> selectStudentOptions(IPage<?> page, @Param("bo") StudentOptionBo bo);
|
||||
|
||||
}
|
||||
+91
@@ -188,4 +188,95 @@ public interface XgDormBedStudentMapper extends BaseMapperPlus<XgDormBedStudent,
|
||||
""")
|
||||
List<Map<String, Object>> selectRoomStudents(@Param("roomId") Long roomId);
|
||||
|
||||
/**
|
||||
* 根据学生ID获取当前宿舍信息
|
||||
* 包含校区、宿舍楼、房间号、床位号等信息
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 当前宿舍信息
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
-- 校区信息
|
||||
campus.id as campus_id,
|
||||
campus.campus_name,
|
||||
|
||||
-- 宿舍区信息
|
||||
area.id as area_id,
|
||||
area.area_name,
|
||||
|
||||
-- 楼栋信息
|
||||
building.id as building_id,
|
||||
building.building_name,
|
||||
building.building_code,
|
||||
|
||||
-- 房间信息
|
||||
r.id as room_id,
|
||||
r.room_name,
|
||||
r.room_code as room_no,
|
||||
|
||||
-- 床位信息
|
||||
b.id as bed_id,
|
||||
b.bed_name,
|
||||
b.bed_code as bed_no
|
||||
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_dorm_bed b ON bs.bed_id = b.id AND b.deleted = '0'
|
||||
INNER JOIN xg_dorm_room r ON b.room_id = r.id AND r.deleted = '0'
|
||||
INNER JOIN xg_dorm_building building ON r.building_id = building.id AND building.deleted = '0'
|
||||
INNER JOIN xg_dorm_area area ON building.area_id = area.id AND area.deleted = '0'
|
||||
INNER JOIN xg_dorm_campus campus ON area.campus_id = campus.id AND campus.deleted = '0'
|
||||
|
||||
WHERE bs.student_id = #{studentId}
|
||||
AND bs.student_id IS NOT NULL
|
||||
AND bs.status = '0'
|
||||
AND bs.deleted = '0'
|
||||
|
||||
LIMIT 1
|
||||
""")
|
||||
Map<String, Object> selectCurrentDormInfo(@Param("studentId") Long studentId);
|
||||
|
||||
/**
|
||||
* 根据床位ID获取该床位的学生信息
|
||||
* 包含学生基本信息、学院、专业、班级等信息
|
||||
*
|
||||
* @param bedId 床位ID
|
||||
* @return 床位学生信息
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
bs.student_id,
|
||||
bs.bed_id,
|
||||
|
||||
-- 学生基本信息
|
||||
s.student_no,
|
||||
s.name as student_name,
|
||||
s.gender,
|
||||
s.phone,
|
||||
|
||||
-- 班级信息
|
||||
c.class_name,
|
||||
|
||||
-- 学院信息
|
||||
d.dept_name as college_name,
|
||||
|
||||
-- 专业信息
|
||||
major.dept_name as major_name
|
||||
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0
|
||||
LEFT JOIN xg_class_msg c ON s.class_id = c.id AND c.deleted = 0
|
||||
LEFT JOIN sys_dept d ON c.college_id = d.dept_id AND d.del_flag = '0'
|
||||
LEFT JOIN sys_dept major ON c.major_id = major.dept_id AND major.del_flag = '0'
|
||||
|
||||
WHERE bs.bed_id = #{bedId}
|
||||
AND bs.student_id IS NOT NULL
|
||||
AND bs.status = '0'
|
||||
AND bs.deleted = '0'
|
||||
AND s.student_status = '1'
|
||||
|
||||
LIMIT 1
|
||||
""")
|
||||
Map<String, Object> selectBedStudentInfo(@Param("bedId") Long bedId);
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package org.dromara.xg.mapper;
|
||||
|
||||
import org.dromara.xg.domain.XgHygieneCheckViolation;
|
||||
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查违纪记录Mapper接口
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-29
|
||||
*/
|
||||
public interface XgHygieneCheckViolationMapper
|
||||
extends BaseMapperPlus<XgHygieneCheckViolation, XgHygieneCheckViolationVo> {
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID查询违纪记录(包含详情)
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 违纪记录列表(包含详情)
|
||||
*/
|
||||
List<XgHygieneCheckViolationVo> selectViolationsByHygieneCheckId(@Param("hygieneCheckId") Long hygieneCheckId);
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package org.dromara.xg.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.dromara.xg.domain.bo.XgStudentStatisticsBo;
|
||||
import org.dromara.xg.domain.vo.XgStudentStatisticsVo;
|
||||
import org.dromara.xg.domain.vo.XgCascadeOptionVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学生宿舍卫生检查、违纪记录统计Mapper接口
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-14
|
||||
*/
|
||||
public interface XgStudentStatisticsMapper {
|
||||
|
||||
/**
|
||||
* 按学院维度统计图表数据
|
||||
*/
|
||||
List<XgStudentStatisticsVo.StatisticsChartData> getCollegeStatistics(@Param("bo") XgStudentStatisticsBo bo);
|
||||
|
||||
/**
|
||||
* 按学院维度统计明细数据
|
||||
*/
|
||||
List<XgStudentStatisticsVo.StatisticsDetailData> getCollegeDetailStatistics(@Param("bo") XgStudentStatisticsBo bo);
|
||||
|
||||
/**
|
||||
* 按专业维度统计图表数据
|
||||
*/
|
||||
List<XgStudentStatisticsVo.StatisticsChartData> getMajorStatistics(@Param("bo") XgStudentStatisticsBo bo);
|
||||
|
||||
/**
|
||||
* 按专业维度统计明细数据
|
||||
*/
|
||||
List<XgStudentStatisticsVo.StatisticsDetailData> getMajorDetailStatistics(@Param("bo") XgStudentStatisticsBo bo);
|
||||
|
||||
/**
|
||||
* 按班级维度统计图表数据
|
||||
*/
|
||||
List<XgStudentStatisticsVo.StatisticsChartData> getClassStatistics(@Param("bo") XgStudentStatisticsBo bo);
|
||||
|
||||
/**
|
||||
* 按班级维度统计明细数据
|
||||
*/
|
||||
List<XgStudentStatisticsVo.StatisticsDetailData> getClassDetailStatistics(@Param("bo") XgStudentStatisticsBo bo);
|
||||
|
||||
/**
|
||||
* 获取二级学院列表
|
||||
*/
|
||||
List<XgCascadeOptionVo> getColleges();
|
||||
|
||||
/**
|
||||
* 根据学院ID获取专业列表
|
||||
*/
|
||||
List<XgCascadeOptionVo> getMajorsByCollegeId(@Param("collegeId") Long collegeId);
|
||||
|
||||
/**
|
||||
* 根据专业ID获取班级列表
|
||||
*/
|
||||
List<XgCascadeOptionVo> getClassesByMajorId(@Param("majorId") Long majorId);
|
||||
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package org.dromara.xg.service;
|
||||
|
||||
import org.dromara.xg.domain.vo.XgDormApplicationVo;
|
||||
import org.dromara.xg.domain.bo.XgDormApplicationBo;
|
||||
import org.dromara.xg.domain.vo.StudentOptionVo;
|
||||
import org.dromara.xg.domain.bo.StudentOptionBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 学生宿舍申请单表(调宿+退宿)Service接口
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
public interface IXgDormApplicationService {
|
||||
|
||||
/**
|
||||
* 查询学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @param id 学生宿舍申请单表(调宿+退宿)主键
|
||||
* @return 学生宿舍申请单表(调宿+退宿)
|
||||
*/
|
||||
XgDormApplicationVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询学生宿舍申请单表(调宿+退宿)列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 学生宿舍申请单表(调宿+退宿)分页列表
|
||||
*/
|
||||
TableDataInfo<XgDormApplicationVo> queryPageList(XgDormApplicationBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的学生宿舍申请单表(调宿+退宿)列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 学生宿舍申请单表(调宿+退宿)列表
|
||||
*/
|
||||
List<XgDormApplicationVo> queryList(XgDormApplicationBo bo);
|
||||
|
||||
/**
|
||||
* 新增学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @param bo 学生宿舍申请单表(调宿+退宿)
|
||||
* @return 新增成功的学生宿舍申请单表(调宿+退宿)
|
||||
*/
|
||||
XgDormApplicationVo insertByBo(XgDormApplicationBo bo);
|
||||
|
||||
/**
|
||||
* 修改学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @param bo 学生宿舍申请单表(调宿+退宿)
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(XgDormApplicationBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除学生宿舍申请单表(调宿+退宿)信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 获取学生选择器选项列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 学生选择器选项分页列表
|
||||
*/
|
||||
TableDataInfo<StudentOptionVo> getStudentOptions(StudentOptionBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 获取二级学院审批人列表
|
||||
*
|
||||
* @param collegeId 学院ID
|
||||
* @return 审批人列表
|
||||
*/
|
||||
List<Map<String, Object>> getCollegeApprovers(Long collegeId);
|
||||
|
||||
/**
|
||||
* 获取学工处审批人列表
|
||||
*
|
||||
* @return 审批人列表
|
||||
*/
|
||||
List<Map<String, Object>> getStudentAffairsApprovers();
|
||||
|
||||
}
|
||||
+19
-1
@@ -107,7 +107,7 @@ public interface IXgDormBedStudentService {
|
||||
|
||||
/**
|
||||
* 获取当前用户二级学院的宿舍楼树形结构
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
|
||||
*
|
||||
* @return 宿舍楼树形结构数据
|
||||
*/
|
||||
@@ -242,4 +242,22 @@ public interface IXgDormBedStudentService {
|
||||
* @return 房间学生信息列表
|
||||
*/
|
||||
List<Object> getRoomStudents(Long roomId);
|
||||
|
||||
/**
|
||||
* 根据学生ID获取当前宿舍信息
|
||||
* 包含校区、宿舍楼、房间号、床位号等信息
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 当前宿舍信息
|
||||
*/
|
||||
Object getCurrentDormInfo(Long studentId);
|
||||
|
||||
/**
|
||||
* 根据床位ID获取该床位的学生信息
|
||||
* 包含学生基本信息、学院、专业、班级等信息
|
||||
*
|
||||
* @param bedId 床位ID
|
||||
* @return 床位学生信息
|
||||
*/
|
||||
Object getBedStudentInfo(Long bedId);
|
||||
}
|
||||
+20
@@ -1,6 +1,7 @@
|
||||
package org.dromara.xg.service;
|
||||
|
||||
import org.dromara.xg.domain.vo.XgDormBuildingVo;
|
||||
import org.dromara.xg.domain.vo.XgDormBuildingSimplifiedVo;
|
||||
import org.dromara.xg.domain.bo.XgDormBuildingBo;
|
||||
import org.dromara.xg.domain.vo.BuildingTreeSelectVo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
@@ -74,4 +75,23 @@ public interface IXgDormBuildingService {
|
||||
* @return 树形结构数据
|
||||
*/
|
||||
List<BuildingTreeSelectVo> getBuildingTreeData();
|
||||
|
||||
/**
|
||||
* 分页查询宿舍楼信息简化列表
|
||||
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 宿舍楼信息简化分页列表
|
||||
*/
|
||||
TableDataInfo<XgDormBuildingSimplifiedVo> querySimplifiedPageList(XgDormBuildingBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的宿舍楼信息简化列表
|
||||
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 宿舍楼信息简化列表
|
||||
*/
|
||||
List<XgDormBuildingSimplifiedVo> querySimplifiedList(XgDormBuildingBo bo);
|
||||
}
|
||||
+19
@@ -2,6 +2,7 @@ package org.dromara.xg.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
|
||||
import org.dromara.xg.domain.vo.DormBuildingTreeSimplifiedVo;
|
||||
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
|
||||
|
||||
/**
|
||||
@@ -28,4 +29,22 @@ public interface IXgDormBuildingTreeService {
|
||||
*/
|
||||
List<DormBuildingTreeVo> getAllDormBuildingTree(DormBuildingTreeBo bo);
|
||||
|
||||
/**
|
||||
* 获取宿舍楼树形结构简化数据
|
||||
* 校区-宿舍楼二级结构,隐藏宿舍区层级
|
||||
*
|
||||
* @param bo 查询参数
|
||||
* @return 简化树形结构数据
|
||||
*/
|
||||
List<DormBuildingTreeSimplifiedVo> getSimplifiedDormBuildingTree(DormBuildingTreeBo bo);
|
||||
|
||||
/**
|
||||
* 获取所有宿舍楼树形结构简化数据(不考虑权限)
|
||||
* 校区-宿舍楼二级结构,隐藏宿舍区层级
|
||||
*
|
||||
* @param bo 查询参数
|
||||
* @return 简化树形结构数据
|
||||
*/
|
||||
List<DormBuildingTreeSimplifiedVo> getAllSimplifiedDormBuildingTree(DormBuildingTreeBo bo);
|
||||
|
||||
}
|
||||
+25
@@ -2,6 +2,7 @@ package org.dromara.xg.service;
|
||||
|
||||
import org.dromara.xg.domain.vo.DormHygieneCheckExportVo;
|
||||
import org.dromara.xg.domain.vo.XgDormHygieneCheckVo;
|
||||
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
|
||||
import org.dromara.xg.domain.bo.XgDormHygieneCheckBo;
|
||||
import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
@@ -111,4 +112,28 @@ public interface IXgDormHygieneCheckService {
|
||||
TableDataInfo<XgDormHygieneCheckVo> queryPageListByStudentId(Long studentId, XgDormHygieneCheckBo bo,
|
||||
PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 保存完整的宿舍卫生检查数据(包含学生评分和违纪记录)
|
||||
*
|
||||
* @param bo 完整的宿舍卫生检查数据
|
||||
* @return 检查记录ID,保存失败返回null
|
||||
*/
|
||||
Long saveCompleteData(XgDormHygieneCheckCompleteBo bo);
|
||||
|
||||
/**
|
||||
* 更新完整的宿舍卫生检查数据(包含学生评分和违纪记录)
|
||||
*
|
||||
* @param bo 完整的宿舍卫生检查数据
|
||||
* @return 是否更新成功
|
||||
*/
|
||||
Boolean updateCompleteData(XgDormHygieneCheckCompleteBo bo);
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID查询相关的违纪记录
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 违纪记录列表
|
||||
*/
|
||||
List<XgHygieneCheckViolationVo> getViolationsByHygieneCheckId(Long hygieneCheckId);
|
||||
|
||||
}
|
||||
+10
@@ -7,6 +7,8 @@ import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 宿舍房间信息Service接口
|
||||
@@ -84,4 +86,12 @@ public interface IXgDormRoomService {
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 导出住宿清单(宿舍卡片式布局)
|
||||
*
|
||||
* @param params 导出参数
|
||||
* @param response HTTP响应对象
|
||||
*/
|
||||
void exportAccommodationList(Map<String, Object> params, HttpServletResponse response);
|
||||
}
|
||||
+8
@@ -77,4 +77,12 @@ public interface IXgHolidayStayService {
|
||||
*/
|
||||
TableDataInfo<StudentOptionVo> getStudentOptions(StudentOptionBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询单人留宿有效数据
|
||||
* 获取所有有效的假期留宿数据,筛选出在同一时间段内该宿舍只有一个学生留宿的记录
|
||||
*
|
||||
* @return 单人留宿有效数据列表
|
||||
*/
|
||||
List<XgHolidayStayVo> querySinglePersonValidStay();
|
||||
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package org.dromara.xg.service;
|
||||
|
||||
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
|
||||
import org.dromara.xg.domain.bo.XgHygieneCheckViolationBo;
|
||||
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-29
|
||||
*/
|
||||
public interface IXgHygieneCheckViolationService {
|
||||
|
||||
/**
|
||||
* 查询宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 宿舍卫生检查违纪记录
|
||||
*/
|
||||
XgHygieneCheckViolationVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询宿舍卫生检查违纪记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 宿舍卫生检查违纪记录分页列表
|
||||
*/
|
||||
TableDataInfo<XgHygieneCheckViolationVo> queryPageList(XgHygieneCheckViolationBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的宿舍卫生检查违纪记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 宿舍卫生检查违纪记录列表
|
||||
*/
|
||||
List<XgHygieneCheckViolationVo> queryList(XgHygieneCheckViolationBo bo);
|
||||
|
||||
/**
|
||||
* 新增宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param bo 宿舍卫生检查违纪记录
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(XgHygieneCheckViolationBo bo);
|
||||
|
||||
/**
|
||||
* 修改宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param bo 宿舍卫生检查违纪记录
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(XgHygieneCheckViolationBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除宿舍卫生检查违纪记录信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 批量新增宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param bos 违纪记录列表
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertBatch(List<XgHygieneCheckViolationBo> bos);
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID查询违纪记录列表
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 违纪记录列表
|
||||
*/
|
||||
List<XgHygieneCheckViolationVo> queryByHygieneCheckId(Long hygieneCheckId);
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID删除违纪关联记录
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteByHygieneCheckId(Long hygieneCheckId);
|
||||
|
||||
}
|
||||
+11
@@ -5,8 +5,10 @@ import org.dromara.xg.domain.bo.XgStudentLeaveBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 学生请假 - 记录学生的请假申请、审批和销假信息Service接口
|
||||
@@ -65,4 +67,13 @@ public interface IXgStudentLeaveService {
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 根据学生ID列表和检查日期查询学生请假信息
|
||||
*
|
||||
* @param studentIds 学生ID列表
|
||||
* @param checkDate 检查日期
|
||||
* @return 学生ID -> 请假信息的映射
|
||||
*/
|
||||
Map<Long, XgStudentLeaveVo> queryStudentLeavesByCheckDate(List<Long> studentIds, LocalDateTime checkDate);
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.dromara.xg.service;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.xg.domain.bo.XgStudentStatisticsBo;
|
||||
import org.dromara.xg.domain.vo.XgStudentStatisticsVo;
|
||||
import org.dromara.xg.domain.vo.XgCascadeOptionVo;
|
||||
|
||||
/**
|
||||
* 学生宿舍卫生检查、违纪记录统计Service接口
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-14
|
||||
*/
|
||||
public interface IXgStudentStatisticsService {
|
||||
|
||||
/**
|
||||
* 获取统计数据
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 统计结果
|
||||
*/
|
||||
XgStudentStatisticsVo getStatistics(XgStudentStatisticsBo bo);
|
||||
|
||||
/**
|
||||
* 导出统计数据
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param response HTTP响应
|
||||
*/
|
||||
void exportStatistics(XgStudentStatisticsBo bo, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 获取二级学院列表
|
||||
*
|
||||
* @return 学院列表
|
||||
*/
|
||||
List<XgCascadeOptionVo> getColleges();
|
||||
|
||||
/**
|
||||
* 根据学院ID获取专业列表
|
||||
*
|
||||
* @param collegeId 学院ID
|
||||
* @return 专业列表
|
||||
*/
|
||||
List<XgCascadeOptionVo> getMajorsByCollegeId(Long collegeId);
|
||||
|
||||
/**
|
||||
* 根据专业ID获取班级列表
|
||||
*
|
||||
* @param majorId 专业ID
|
||||
* @return 班级列表
|
||||
*/
|
||||
List<XgCascadeOptionVo> getClassesByMajorId(Long majorId);
|
||||
|
||||
}
|
||||
+698
@@ -0,0 +1,698 @@
|
||||
package org.dromara.xg.service.impl;
|
||||
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.xg.domain.bo.XgDormApplicationBo;
|
||||
import org.dromara.xg.domain.vo.XgDormApplicationVo;
|
||||
import org.dromara.xg.domain.vo.StudentOptionVo;
|
||||
import org.dromara.xg.domain.bo.StudentOptionBo;
|
||||
import org.dromara.xg.domain.XgDormApplication;
|
||||
import org.dromara.xg.mapper.XgDormApplicationMapper;
|
||||
import org.dromara.xg.service.IXgDormApplicationService;
|
||||
import org.dromara.workflow.common.ConditionalOnEnable;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.common.core.domain.event.ProcessTaskEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessEvent;
|
||||
import org.dromara.common.core.enums.BusinessStatusEnum;
|
||||
import org.dromara.common.core.service.WorkflowService;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.mapper.SysDeptMapper;
|
||||
import org.dromara.system.mapper.SysUserMapper;
|
||||
import org.dromara.system.mapper.SysUserRoleMapper;
|
||||
import org.dromara.system.mapper.SysRoleMapper;
|
||||
import org.dromara.system.domain.SysDept;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.domain.SysRole;
|
||||
import org.dromara.xg.mapper.XgStudentManagerMapper;
|
||||
import org.dromara.xg.mapper.XgStudentManagerSourceMapper;
|
||||
import org.dromara.xg.mapper.XgClassMsgMapper;
|
||||
import org.dromara.xg.mapper.XgDormBedStudentMapper;
|
||||
import org.dromara.xg.service.IXgDormBedStudentService;
|
||||
import org.dromara.xg.domain.XgStudentManager;
|
||||
import org.dromara.xg.domain.XgStudentManagerSource;
|
||||
import org.dromara.xg.domain.XgClassMsg;
|
||||
import org.dromara.xg.domain.XgDormBedStudent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 学生宿舍申请单表(调宿+退宿)Service业务层处理
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@ConditionalOnEnable
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class XgDormApplicationServiceImpl implements IXgDormApplicationService {
|
||||
|
||||
private final XgDormApplicationMapper baseMapper;
|
||||
private final WorkflowService workflowService;
|
||||
private final XgDormBedStudentMapper xgDormBedStudentMapper;
|
||||
private final IXgDormBedStudentService xgDormBedStudentService;
|
||||
// 添加权限过滤需要的依赖
|
||||
private final SysDeptMapper sysDeptMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final SysUserRoleMapper sysUserRoleMapper;
|
||||
private final SysRoleMapper sysRoleMapper;
|
||||
private final XgStudentManagerMapper xgStudentManagerMapper;
|
||||
private final XgStudentManagerSourceMapper xgStudentManagerSourceMapper;
|
||||
private final XgClassMsgMapper xgClassMsgMapper;
|
||||
|
||||
/**
|
||||
* 查询学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 学生宿舍申请单表(调宿+退宿)
|
||||
*/
|
||||
@Override
|
||||
public XgDormApplicationVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询学生宿舍申请单表(调宿+退宿)列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 学生宿舍申请单表(调宿+退宿)分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<XgDormApplicationVo> queryPageList(XgDormApplicationBo bo, PageQuery pageQuery) {
|
||||
// 增加班级数据权限的过滤功能
|
||||
// 第一步:调用权限过滤方法,获取过滤后的班级IDs(传入101作为根组织ID)
|
||||
List<Long> filteredDeptIds = filterDeptIdsByPermission(101L);
|
||||
log.info("权限过滤后的班级ID列表: {}", filteredDeptIds);
|
||||
|
||||
// 第二步:使用带权限过滤的查询条件构建器
|
||||
LambdaQueryWrapper<XgDormApplication> lqw = buildQueryWrapperWithPermission(bo, filteredDeptIds);
|
||||
Page<XgDormApplicationVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的学生宿舍申请单表(调宿+退宿)列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 学生宿舍申请单表(调宿+退宿)列表
|
||||
*/
|
||||
@Override
|
||||
public List<XgDormApplicationVo> queryList(XgDormApplicationBo bo) {
|
||||
LambdaQueryWrapper<XgDormApplication> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限过滤方法
|
||||
* 根据当前用户的权限范围过滤部门ID列表
|
||||
*
|
||||
* @param deptId 入参的部门ID
|
||||
* @return 过滤后的部门ID列表
|
||||
*/
|
||||
private List<Long> filterDeptIdsByPermission(Long deptId) {
|
||||
// 获取当前登录用户ID
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
log.info("当前登录用户ID: {}", currentUserId);
|
||||
|
||||
// 第一步:判断当前用户是否为全校管理员
|
||||
boolean isSchoolManager = checkIsSchoolManager(currentUserId);
|
||||
if (isSchoolManager) {
|
||||
log.info("用户 {} 是全校管理员,不进行权限过滤", currentUserId);
|
||||
// 全校管理员不进行过滤,返回包含入参deptId及其子组织的列表
|
||||
return getDeptIdsWithChildren(deptId);
|
||||
}
|
||||
|
||||
// 第二步:获取用户的总权限集合
|
||||
Set<Long> userAuthDeptIds = getUserAuthDeptIds(currentUserId);
|
||||
log.info("用户 {} 的权限范围: {}", currentUserId, userAuthDeptIds);
|
||||
|
||||
// 第三步:判断入参deptId是否在用户权限范围内
|
||||
if (deptId != null && userAuthDeptIds.contains(deptId)) {
|
||||
log.info("入参deptId {} 在用户权限范围内,不进行过滤", deptId);
|
||||
// 在权限范围内,返回包含入参deptId及其子组织的列表
|
||||
return getDeptIdsWithChildren(deptId);
|
||||
} else {
|
||||
log.info("入参deptId {} 不在用户权限范围内,进行权限过滤", deptId);
|
||||
// 不在权限范围内,过滤deptIds,只保留在权限范围内的
|
||||
List<Long> filteredDeptIds = new ArrayList<>();
|
||||
if (deptId != null) {
|
||||
// 如果入参deptId不为空,检查其子组织是否在权限范围内
|
||||
List<Long> deptIdsWithChildren = getDeptIdsWithChildren(deptId);
|
||||
for (Long deptIdWithChild : deptIdsWithChildren) {
|
||||
if (userAuthDeptIds.contains(deptIdWithChild)) {
|
||||
filteredDeptIds.add(deptIdWithChild);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果入参deptId为空,直接使用用户权限范围内的所有部门
|
||||
filteredDeptIds.addAll(userAuthDeptIds);
|
||||
}
|
||||
|
||||
// 对过滤后的deptIds,查找所有子组织包括他们自己
|
||||
Set<Long> finalDeptIds = new HashSet<>();
|
||||
for (Long filteredDeptId : filteredDeptIds) {
|
||||
List<Long> deptIdsWithChildren = getDeptIdsWithChildren(filteredDeptId);
|
||||
finalDeptIds.addAll(deptIdsWithChildren);
|
||||
}
|
||||
|
||||
return new ArrayList<>(finalDeptIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否为全校管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 是否为全校管理员
|
||||
*/
|
||||
private boolean checkIsSchoolManager(Long userId) {
|
||||
LambdaQueryWrapper<XgStudentManager> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(XgStudentManager::getUserId, userId)
|
||||
.eq(XgStudentManager::getDeleted, "0")
|
||||
.eq(XgStudentManager::getStatus, "0")
|
||||
.eq(XgStudentManager::getManagerType, "1");
|
||||
|
||||
return xgStudentManagerMapper.selectCount(lqw) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的总权限集合
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户权限范围内的部门ID集合
|
||||
*/
|
||||
private Set<Long> getUserAuthDeptIds(Long userId) {
|
||||
Set<Long> authDeptIds = new HashSet<>();
|
||||
|
||||
// 第三步:通过xg_student_manager和xg_student_manager_source关联,获取用户的权限范围
|
||||
LambdaQueryWrapper<XgStudentManager> managerWrapper = Wrappers.lambdaQuery();
|
||||
managerWrapper.eq(XgStudentManager::getUserId, userId)
|
||||
.eq(XgStudentManager::getDeleted, "0")
|
||||
.eq(XgStudentManager::getStatus, "0");
|
||||
|
||||
List<XgStudentManager> managers = xgStudentManagerMapper.selectList(managerWrapper);
|
||||
|
||||
for (XgStudentManager manager : managers) {
|
||||
LambdaQueryWrapper<XgStudentManagerSource> sourceWrapper = Wrappers.lambdaQuery();
|
||||
sourceWrapper.eq(XgStudentManagerSource::getManagerId, manager.getId())
|
||||
.eq(XgStudentManagerSource::getDeleted, "0")
|
||||
.eq(XgStudentManagerSource::getStatus, "0");
|
||||
|
||||
List<XgStudentManagerSource> sources = xgStudentManagerSourceMapper.selectList(sourceWrapper);
|
||||
for (XgStudentManagerSource source : sources) {
|
||||
if (source.getAuthSource() != null) {
|
||||
authDeptIds.add(source.getAuthSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 第四步:通过xg_class_msg表,class_teacher_id=当前登录人id,deleted='0',class_status='1',获取id
|
||||
LambdaQueryWrapper<XgClassMsg> classWrapper = Wrappers.lambdaQuery();
|
||||
classWrapper.eq(XgClassMsg::getClassTeacherId, userId)
|
||||
.eq(XgClassMsg::getDeleted, 0L)
|
||||
.eq(XgClassMsg::getClassStatus, "1");
|
||||
|
||||
List<XgClassMsg> classes = xgClassMsgMapper.selectList(classWrapper);
|
||||
for (XgClassMsg classMsg : classes) {
|
||||
if (classMsg.getId() != null) {
|
||||
authDeptIds.add(classMsg.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return authDeptIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门ID及其所有子组织的ID列表
|
||||
*
|
||||
* @param deptId 部门ID
|
||||
* @return 包含部门ID及其子组织的ID列表
|
||||
*/
|
||||
private List<Long> getDeptIdsWithChildren(Long deptId) {
|
||||
List<Long> deptIds = new ArrayList<>();
|
||||
if (deptId != null) {
|
||||
deptIds.add(deptId); // 包含自己
|
||||
|
||||
// 查找所有子组织
|
||||
List<SysDept> childDepts = sysDeptMapper.selectListByParentId(deptId);
|
||||
if (!childDepts.isEmpty()) {
|
||||
List<Long> childDeptIds = childDepts.stream()
|
||||
.map(SysDept::getDeptId)
|
||||
.collect(Collectors.toList());
|
||||
deptIds.addAll(childDeptIds);
|
||||
}
|
||||
}
|
||||
return deptIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建带权限过滤的查询条件包装器
|
||||
*/
|
||||
private LambdaQueryWrapper<XgDormApplication> buildQueryWrapperWithPermission(XgDormApplicationBo bo,
|
||||
List<Long> filteredDeptIds) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<XgDormApplication> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByDesc(XgDormApplication::getCreateTime);
|
||||
|
||||
// 获取当前登录用户ID
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
|
||||
// 添加权限过滤条件:(班级ID必须在权限范围内) OR (是自己创建的申请单)
|
||||
lqw.and(wrapper -> {
|
||||
if (filteredDeptIds != null && !filteredDeptIds.isEmpty()) {
|
||||
wrapper.in(XgDormApplication::getClassId, filteredDeptIds);
|
||||
} else {
|
||||
// 如果用户没有任何权限,使用一个不存在的ID
|
||||
wrapper.eq(XgDormApplication::getClassId, -1L);
|
||||
}
|
||||
// OR 用户可以查看自己创建的申请单
|
||||
wrapper.or().eq(XgDormApplication::getStudentId, currentUserId);
|
||||
});
|
||||
|
||||
// 原有的查询条件
|
||||
lqw.like(StringUtils.isNotBlank(bo.getApplicationName()), XgDormApplication::getApplicationName,
|
||||
bo.getApplicationName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getApplicationType()), XgDormApplication::getApplicationType,
|
||||
bo.getApplicationType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getApplicationStatus()), XgDormApplication::getApplicationStatus,
|
||||
bo.getApplicationStatus());
|
||||
lqw.eq(bo.getStudentId() != null, XgDormApplication::getStudentId, bo.getStudentId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgDormApplication::getStudentName, bo.getStudentName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStudentNo()), XgDormApplication::getStudentNo, bo.getStudentNo());
|
||||
lqw.eq(bo.getClassId() != null, XgDormApplication::getClassId, bo.getClassId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getClassName()), XgDormApplication::getClassName, bo.getClassName());
|
||||
lqw.eq(bo.getMajorId() != null, XgDormApplication::getMajorId, bo.getMajorId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getMajorName()), XgDormApplication::getMajorName, bo.getMajorName());
|
||||
lqw.eq(bo.getCollegeId() != null, XgDormApplication::getCollegeId, bo.getCollegeId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), XgDormApplication::getCollegeName, bo.getCollegeName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgDormApplication::getDeleted, bo.getDeleted());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<XgDormApplication> buildQueryWrapper(XgDormApplicationBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<XgDormApplication> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByDesc(XgDormApplication::getCreateTime);
|
||||
|
||||
lqw.like(StringUtils.isNotBlank(bo.getApplicationName()), XgDormApplication::getApplicationName,
|
||||
bo.getApplicationName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getApplicationType()), XgDormApplication::getApplicationType,
|
||||
bo.getApplicationType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getApplicationStatus()), XgDormApplication::getApplicationStatus,
|
||||
bo.getApplicationStatus());
|
||||
lqw.eq(bo.getStudentId() != null, XgDormApplication::getStudentId, bo.getStudentId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgDormApplication::getStudentName, bo.getStudentName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStudentNo()), XgDormApplication::getStudentNo, bo.getStudentNo());
|
||||
lqw.eq(bo.getClassId() != null, XgDormApplication::getClassId, bo.getClassId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getClassName()), XgDormApplication::getClassName, bo.getClassName());
|
||||
lqw.eq(bo.getMajorId() != null, XgDormApplication::getMajorId, bo.getMajorId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getMajorName()), XgDormApplication::getMajorName, bo.getMajorName());
|
||||
lqw.eq(bo.getCollegeId() != null, XgDormApplication::getCollegeId, bo.getCollegeId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), XgDormApplication::getCollegeName, bo.getCollegeName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgDormApplication::getDeleted, bo.getDeleted());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @param bo 学生宿舍申请单表(调宿+退宿)
|
||||
* @return 新增成功的学生宿舍申请单表(调宿+退宿)
|
||||
*/
|
||||
@Override
|
||||
public XgDormApplicationVo insertByBo(XgDormApplicationBo bo) {
|
||||
XgDormApplication add = MapstructUtils.convert(bo, XgDormApplication.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
// 返回新增成功的记录信息
|
||||
return MapstructUtils.convert(add, XgDormApplicationVo.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改学生宿舍申请单表(调宿+退宿)
|
||||
*
|
||||
* @param bo 学生宿舍申请单表(调宿+退宿)
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(XgDormApplicationBo bo) {
|
||||
XgDormApplication update = MapstructUtils.convert(bo, XgDormApplication.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(XgDormApplication entity) {
|
||||
// TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除学生宿舍申请单表(调宿+退宿)信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
|
||||
* 正常使用只需#processEvent.flowCode=='hzdyjs-tsts'
|
||||
*
|
||||
* @param processEvent 参数
|
||||
*/
|
||||
@EventListener(condition = "#processEvent.flowCode=='hzdyjs-tsts'")
|
||||
public void processHandler(ProcessEvent processEvent) {
|
||||
log.info("当前任务执行了{}", processEvent.toString());
|
||||
XgDormApplication xgDormApplication = baseMapper.selectById(Long.valueOf(processEvent.getBusinessId()));
|
||||
xgDormApplication.setApplicationStatus(processEvent.getStatus());
|
||||
|
||||
// 用于例如审批附件 审批意见等 存储到业务表内 自行根据业务实现存储流程
|
||||
Map<String, Object> params = processEvent.getParams();
|
||||
if (MapUtil.isNotEmpty(params)) {
|
||||
// 历史任务扩展(通常为附件)
|
||||
String hisTaskExt = Convert.toStr(params.get("hisTaskExt"));
|
||||
// 办理人
|
||||
String handler = Convert.toStr(params.get("handler"));
|
||||
// 办理意见
|
||||
String message = Convert.toStr(params.get("message"));
|
||||
}
|
||||
|
||||
if (processEvent.getSubmit()) {
|
||||
xgDormApplication.setApplicationStatus(BusinessStatusEnum.WAITING.getStatus());
|
||||
}
|
||||
|
||||
// 当审批完成时,执行调宿/退宿操作
|
||||
if ("finish".equals(processEvent.getStatus())) {
|
||||
executeApplicationOperation(xgDormApplication);
|
||||
}
|
||||
|
||||
baseMapper.updateById(xgDormApplication);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行调宿/退宿操作
|
||||
*
|
||||
* @param application 申请单
|
||||
*/
|
||||
private void executeApplicationOperation(XgDormApplication application) {
|
||||
try {
|
||||
if ("1".equals(application.getApplicationType())) {
|
||||
// 调宿申请 - 区分两种场景处理
|
||||
log.info("执行调宿操作: 学生ID={}, 目标床位ID={}", application.getStudentId(), application.getTargetBedId());
|
||||
|
||||
// 1. 根据目标床位ID查找当前占用该床位的学生ID
|
||||
Long targetStudentId = findStudentByBedId(application.getTargetBedId());
|
||||
|
||||
Boolean operationResult = false;
|
||||
|
||||
if (targetStudentId != null) {
|
||||
// 场景1:目标床位有学生 - 两个学生互换床位
|
||||
log.info("目标床位已有学生,执行床位互换: 申请学生ID={}, 目标学生ID={}",
|
||||
application.getStudentId(), targetStudentId);
|
||||
|
||||
operationResult = xgDormBedStudentService.classTeacherExchangeBeds(
|
||||
application.getStudentId(), // 源学生ID(申请人)
|
||||
targetStudentId, // 目标学生ID(目标床位当前占用者)
|
||||
application.getClassId() // 班级ID
|
||||
);
|
||||
|
||||
if (operationResult) {
|
||||
log.info("床位互换操作执行成功: 申请学生ID={}, 目标学生ID={}, 班级ID={}",
|
||||
application.getStudentId(), targetStudentId, application.getClassId());
|
||||
} else {
|
||||
log.error("床位互换操作执行失败: 申请学生ID={}, 目标学生ID={}, 班级ID={}",
|
||||
application.getStudentId(), targetStudentId, application.getClassId());
|
||||
throw new RuntimeException("床位互换操作执行失败");
|
||||
}
|
||||
|
||||
} else {
|
||||
// 场景2:目标床位为空 - 学生移动到新床位
|
||||
log.info("目标床位为空,执行床位移动: 申请学生ID={}, 目标床位ID={}",
|
||||
application.getStudentId(), application.getTargetBedId());
|
||||
|
||||
operationResult = xgDormBedStudentService.classTeacherMoveBed(
|
||||
application.getStudentId(), // 源学生ID(申请人)
|
||||
application.getTargetBedId(), // 目标床位ID
|
||||
application.getClassId() // 班级ID
|
||||
);
|
||||
|
||||
if (operationResult) {
|
||||
log.info("床位移动操作执行成功: 申请学生ID={}, 目标床位ID={}, 班级ID={}",
|
||||
application.getStudentId(), application.getTargetBedId(), application.getClassId());
|
||||
} else {
|
||||
log.error("床位移动操作执行失败: 申请学生ID={}, 目标床位ID={}, 班级ID={}",
|
||||
application.getStudentId(), application.getTargetBedId(), application.getClassId());
|
||||
throw new RuntimeException("床位移动操作执行失败");
|
||||
}
|
||||
}
|
||||
|
||||
} else if ("2".equals(application.getApplicationType())) {
|
||||
// 退宿申请 - 调用退宿接口
|
||||
log.info("执行退宿操作: 学生ID={}, 当前床位ID={}", application.getStudentId(), application.getCurrentBedId());
|
||||
|
||||
// 构建退宿参数并调用接口
|
||||
List<Long> bedIds = List.of(application.getCurrentBedId());
|
||||
Boolean stayawayResult = xgDormBedStudentService.classTeacherStayawaySelectedBeds(
|
||||
bedIds, // 床位ID列表
|
||||
application.getClassId() // 班级ID
|
||||
);
|
||||
|
||||
if (stayawayResult) {
|
||||
log.info("退宿操作执行成功: 学生ID={}, 床位ID={}, 班级ID={}",
|
||||
application.getStudentId(), application.getCurrentBedId(), application.getClassId());
|
||||
} else {
|
||||
log.error("退宿操作执行失败: 学生ID={}, 床位ID={}, 班级ID={}",
|
||||
application.getStudentId(), application.getCurrentBedId(), application.getClassId());
|
||||
throw new RuntimeException("退宿操作执行失败");
|
||||
}
|
||||
} else {
|
||||
log.error("未知的申请类型: {}", application.getApplicationType());
|
||||
throw new RuntimeException("未知的申请类型: " + application.getApplicationType());
|
||||
}
|
||||
|
||||
log.info("宿舍申请操作执行成功,申请ID: {}", application.getId());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("宿舍申请操作执行失败,申请ID: {}, 错误信息: {}", application.getId(), e.getMessage(), e);
|
||||
// 抛出异常,让调用方处理
|
||||
throw new RuntimeException("宿舍申请操作执行失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据床位ID查找当前占用该床位的学生ID
|
||||
*
|
||||
* @param bedId 床位ID
|
||||
* @return 学生ID,如果没有找到则返回null
|
||||
*/
|
||||
private Long findStudentByBedId(Long bedId) {
|
||||
if (bedId == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
LambdaQueryWrapper<XgDormBedStudent> queryWrapper = Wrappers.lambdaQuery();
|
||||
queryWrapper.eq(XgDormBedStudent::getBedId, bedId)
|
||||
.eq(XgDormBedStudent::getStatus, "0") // 0-正常状态
|
||||
.eq(XgDormBedStudent::getDeleted, 0L); // 未删除
|
||||
|
||||
XgDormBedStudent bedStudent = xgDormBedStudentMapper.selectOne(queryWrapper);
|
||||
if (bedStudent != null) {
|
||||
log.info("根据床位ID: {} 找到学生ID: {}", bedId, bedStudent.getStudentId());
|
||||
return bedStudent.getStudentId();
|
||||
} else {
|
||||
log.warn("床位ID: {} 没有找到对应的学生", bedId);
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("根据床位ID: {} 查找学生失败: {}", bedId, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务创建监听
|
||||
* 示例:也可通过 @EventListener(condition =
|
||||
* "#processTaskEvent.flowCode=='hzdyjs-tsts'")进行判断
|
||||
* 在方法中判断流程节点key
|
||||
* if ("xxx".equals(processTaskEvent.getNodeCode())) {
|
||||
* //执行业务逻辑
|
||||
* }
|
||||
*
|
||||
* @param processTaskEvent 参数
|
||||
*/
|
||||
@EventListener(condition = "#processTaskEvent.flowCode=='hzdyjs-tsts'")
|
||||
public void processTaskHandler(ProcessTaskEvent processTaskEvent) {
|
||||
log.info("当前任务创建了{}", processTaskEvent.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听删除流程事件
|
||||
* 正常使用只需#processDeleteEvent.flowCode=='hzdyjs-tsts'
|
||||
*
|
||||
* @param processDeleteEvent 参数
|
||||
*/
|
||||
@EventListener(condition = "#processDeleteEvent.flowCode=='hzdyjs-tsts'")
|
||||
public void processDeleteHandler(ProcessDeleteEvent processDeleteEvent) {
|
||||
log.info("监听删除流程事件,当前任务执行了{}", processDeleteEvent.toString());
|
||||
XgDormApplication xgDormApplication = baseMapper.selectById(Long.valueOf(processDeleteEvent.getBusinessId()));
|
||||
if (ObjectUtil.isNull(xgDormApplication)) {
|
||||
return;
|
||||
}
|
||||
baseMapper.deleteById(xgDormApplication.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学生选择器选项列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 学生选择器选项分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<StudentOptionVo> getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) {
|
||||
Page<StudentOptionVo> result = baseMapper.selectStudentOptions(pageQuery.build(), bo);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级学院审批人列表
|
||||
*
|
||||
* @param collegeId 学院ID
|
||||
* @return 审批人列表
|
||||
*/
|
||||
@Override
|
||||
public List<Map<String, Object>> getCollegeApprovers(Long collegeId) {
|
||||
try {
|
||||
// 查询角色key为'ejxyssgly'的角色ID
|
||||
LambdaQueryWrapper<SysRole> roleWrapper = new LambdaQueryWrapper<>();
|
||||
roleWrapper.eq(SysRole::getRoleKey, "ejxyssgly");
|
||||
SysRole role = sysRoleMapper.selectOne(roleWrapper);
|
||||
|
||||
if (role == null) {
|
||||
log.warn("未找到角色key为'ejxyssgly'的角色");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 通过角色ID查询用户角色关系表
|
||||
LambdaQueryWrapper<org.dromara.system.domain.SysUserRole> userRoleWrapper = new LambdaQueryWrapper<>();
|
||||
userRoleWrapper.eq(org.dromara.system.domain.SysUserRole::getRoleId, role.getRoleId());
|
||||
List<org.dromara.system.domain.SysUserRole> userRoles = sysUserRoleMapper.selectList(userRoleWrapper);
|
||||
|
||||
if (userRoles.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 获取用户ID列表
|
||||
List<Long> userIds = userRoles.stream()
|
||||
.map(org.dromara.system.domain.SysUserRole::getUserId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查询用户信息,过滤指定学院的用户
|
||||
LambdaQueryWrapper<SysUser> userWrapper = new LambdaQueryWrapper<>();
|
||||
userWrapper.in(SysUser::getUserId, userIds)
|
||||
.eq(SysUser::getDeptId, collegeId)
|
||||
.eq(SysUser::getStatus, "0"); // 只查询启用状态的用户
|
||||
List<SysUser> users = sysUserMapper.selectList(userWrapper);
|
||||
|
||||
// 转换为Map格式返回
|
||||
return users.stream().map(user -> {
|
||||
Map<String, Object> approver = MapUtil.newHashMap();
|
||||
approver.put("userId", user.getUserId());
|
||||
approver.put("userName", user.getUserName());
|
||||
approver.put("nickName", user.getNickName());
|
||||
approver.put("deptId", user.getDeptId());
|
||||
return approver;
|
||||
}).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取二级学院审批人列表失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学工处审批人列表
|
||||
*
|
||||
* @return 审批人列表
|
||||
*/
|
||||
@Override
|
||||
public List<Map<String, Object>> getStudentAffairsApprovers() {
|
||||
try {
|
||||
// 查询角色key为'xscxsxxgly'的角色ID
|
||||
LambdaQueryWrapper<SysRole> roleWrapper = new LambdaQueryWrapper<>();
|
||||
roleWrapper.eq(SysRole::getRoleKey, "xscxsxxgly");
|
||||
SysRole role = sysRoleMapper.selectOne(roleWrapper);
|
||||
|
||||
if (role == null) {
|
||||
log.warn("未找到角色key为'xscxsxxgly'的角色");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 通过角色ID查询用户角色关系表
|
||||
LambdaQueryWrapper<org.dromara.system.domain.SysUserRole> userRoleWrapper = new LambdaQueryWrapper<>();
|
||||
userRoleWrapper.eq(org.dromara.system.domain.SysUserRole::getRoleId, role.getRoleId());
|
||||
List<org.dromara.system.domain.SysUserRole> userRoles = sysUserRoleMapper.selectList(userRoleWrapper);
|
||||
|
||||
if (userRoles.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 获取用户ID列表
|
||||
List<Long> userIds = userRoles.stream()
|
||||
.map(org.dromara.system.domain.SysUserRole::getUserId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查询用户信息
|
||||
LambdaQueryWrapper<SysUser> userWrapper = new LambdaQueryWrapper<>();
|
||||
userWrapper.in(SysUser::getUserId, userIds)
|
||||
.eq(SysUser::getStatus, "0"); // 只查询启用状态的用户
|
||||
List<SysUser> users = sysUserMapper.selectList(userWrapper);
|
||||
|
||||
// 转换为Map格式返回
|
||||
return users.stream().map(user -> {
|
||||
Map<String, Object> approver = MapUtil.newHashMap();
|
||||
approver.put("userId", user.getUserId());
|
||||
approver.put("userName", user.getUserName());
|
||||
approver.put("nickName", user.getNickName());
|
||||
approver.put("deptId", user.getDeptId());
|
||||
return approver;
|
||||
}).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取学工处审批人列表失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+186
-4
@@ -983,7 +983,8 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
Long roomId = entry.getKey();
|
||||
List<Long> roomUnallocatedBeds = entry.getValue();
|
||||
String roomCode = roomCodeMap.get(roomId);
|
||||
log.error("房间{}({})有{}个床位未分配:{}", roomCode, roomId, roomUnallocatedBeds.size(), roomUnallocatedBeds);
|
||||
log.error("房间{}({})有{}个床位未分配:{}", roomCode, roomId, roomUnallocatedBeds.size(),
|
||||
roomUnallocatedBeds);
|
||||
}
|
||||
|
||||
throw new RuntimeException("床位分配不完整,存在" + (totalExpectedBeds - totalAllocatedBeds) + "个床位未分配");
|
||||
@@ -1008,7 +1009,7 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
|
||||
/**
|
||||
* 获取当前用户二级学院的宿舍楼树形结构
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
|
||||
*
|
||||
* @return 宿舍楼树形结构数据
|
||||
*/
|
||||
@@ -1159,8 +1160,8 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
.orderByAsc(XgDormCampus::getSortOrder, XgDormCampus::getId);
|
||||
List<XgDormCampus> campuses = xgDormCampusMapper.selectList(campusWrapper);
|
||||
|
||||
// 构建树形结构
|
||||
return buildTreeStructure(campuses, areas, buildings);
|
||||
// 构建简化版树形结构(校区-宿舍楼,去除宿舍区层级)
|
||||
return buildSimplifiedTreeStructure(campuses, areas, buildings);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("构建宿舍楼树形结构失败", e);
|
||||
@@ -1232,6 +1233,60 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
return new ArrayList<>(campusMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建简化树形结构:校区-宿舍楼(去除宿舍区层级)
|
||||
*/
|
||||
private List<DormBuildingTreeVo> buildSimplifiedTreeStructure(List<XgDormCampus> campuses,
|
||||
List<XgDormArea> areas,
|
||||
List<XgDormBuilding> buildings) {
|
||||
|
||||
// 构建校区节点(第一层)
|
||||
Map<Long, DormBuildingTreeVo> campusMap = new LinkedHashMap<>();
|
||||
for (XgDormCampus campus : campuses) {
|
||||
// 检查该校区下是否有宿舍楼(通过宿舍区关联)
|
||||
boolean hasBuildings = buildings.stream().anyMatch(building -> areas.stream().anyMatch(
|
||||
area -> area.getId().equals(building.getAreaId()) && area.getCampusId().equals(campus.getId())));
|
||||
|
||||
if (hasBuildings) {
|
||||
DormBuildingTreeVo campusNode = new DormBuildingTreeVo();
|
||||
campusNode.setId(campus.getId());
|
||||
campusNode.setLabel(campus.getCampusName());
|
||||
campusNode.setType("campus");
|
||||
campusNode.setDisabled(true); // 校区节点禁止点击
|
||||
campusNode.setChildren(new ArrayList<>());
|
||||
campusMap.put(campus.getId(), campusNode);
|
||||
}
|
||||
}
|
||||
|
||||
// 直接构建宿舍楼节点(第二层),跳过宿舍区
|
||||
for (XgDormBuilding building : buildings) {
|
||||
// 通过宿舍区找到对应的校区
|
||||
XgDormArea area = areas.stream()
|
||||
.filter(a -> a.getId().equals(building.getAreaId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (area != null) {
|
||||
DormBuildingTreeVo buildingNode = new DormBuildingTreeVo();
|
||||
buildingNode.setId(building.getId());
|
||||
buildingNode.setLabel(building.getBuildingName());
|
||||
buildingNode.setType("building");
|
||||
buildingNode.setDisabled(false); // 宿舍楼节点可以点击
|
||||
buildingNode.setBuildingFloors(building.getFloors());
|
||||
buildingNode.setBuildingType(building.getBuildingType());
|
||||
buildingNode.setChildren(new ArrayList<>());
|
||||
|
||||
// 直接添加到校区下,跳过宿舍区层级
|
||||
DormBuildingTreeVo campusNode = campusMap.get(area.getCampusId());
|
||||
if (campusNode != null) {
|
||||
campusNode.getChildren().add(buildingNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(campusMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取班主任管理的班级宿舍楼树
|
||||
* 通过当前用户所在的班级,获取该班级分配的宿舍楼
|
||||
@@ -2445,4 +2500,131 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学生ID获取当前宿舍信息
|
||||
* 包含校区、宿舍楼、房间号、床位号等信息
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 当前宿舍信息
|
||||
*/
|
||||
@Override
|
||||
public Object getCurrentDormInfo(Long studentId) {
|
||||
try {
|
||||
log.info("开始获取学生当前宿舍信息,学生ID: {}", studentId);
|
||||
|
||||
if (studentId == null) {
|
||||
log.warn("学生ID为空,无法获取宿舍信息");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用SQL关联查询,获取学生当前宿舍信息
|
||||
Map<String, Object> dormInfo = baseMapper.selectCurrentDormInfo(studentId);
|
||||
|
||||
if (dormInfo == null || dormInfo.isEmpty()) {
|
||||
log.warn("学生ID: {} 未找到宿舍分配信息", studentId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 组装返回结果
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
|
||||
// 校区信息
|
||||
result.put("campusId", dormInfo.get("campus_id"));
|
||||
result.put("campusName", dormInfo.get("campus_name"));
|
||||
|
||||
// 宿舍区信息
|
||||
result.put("areaId", dormInfo.get("area_id"));
|
||||
result.put("areaName", dormInfo.get("area_name"));
|
||||
|
||||
// 楼栋信息
|
||||
result.put("buildingId", dormInfo.get("building_id"));
|
||||
result.put("buildingName", dormInfo.get("building_name"));
|
||||
result.put("buildingCode", dormInfo.get("building_code"));
|
||||
|
||||
// 房间信息
|
||||
result.put("roomId", dormInfo.get("room_id"));
|
||||
result.put("roomName", dormInfo.get("room_name"));
|
||||
result.put("roomNo", dormInfo.get("room_no"));
|
||||
result.put("roomNumber", dormInfo.get("room_no")); // 兼容字段
|
||||
|
||||
// 床位信息
|
||||
result.put("bedId", dormInfo.get("bed_id"));
|
||||
result.put("bedName", dormInfo.get("bed_name"));
|
||||
result.put("bedNo", dormInfo.get("bed_no"));
|
||||
result.put("bedNumber", dormInfo.get("bed_no")); // 兼容字段
|
||||
|
||||
log.info("获取学生当前宿舍信息成功,学生ID: {}, 宿舍信息: {}校区-{}楼-{}房间-{}床位",
|
||||
studentId,
|
||||
result.get("campusName"),
|
||||
result.get("buildingName"),
|
||||
result.get("roomNo"),
|
||||
result.get("bedNo"));
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取学生当前宿舍信息失败,学生ID: {}", studentId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据床位ID获取该床位的学生信息
|
||||
* 包含学生基本信息、学院、专业、班级等信息
|
||||
*
|
||||
* @param bedId 床位ID
|
||||
* @return 床位学生信息
|
||||
*/
|
||||
@Override
|
||||
public Object getBedStudentInfo(Long bedId) {
|
||||
try {
|
||||
log.info("开始获取床位学生信息,床位ID: {}", bedId);
|
||||
|
||||
if (bedId == null) {
|
||||
log.warn("床位ID为空,无法获取学生信息");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用SQL关联查询,获取床位学生信息
|
||||
Map<String, Object> studentInfo = baseMapper.selectBedStudentInfo(bedId);
|
||||
|
||||
if (studentInfo == null || studentInfo.isEmpty()) {
|
||||
log.warn("床位ID: {} 未找到学生信息", bedId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 组装返回结果
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
|
||||
// 学生基本信息
|
||||
result.put("studentId", studentInfo.get("student_id"));
|
||||
result.put("studentNo", studentInfo.get("student_no"));
|
||||
result.put("studentName", studentInfo.get("student_name"));
|
||||
result.put("gender", studentInfo.get("gender"));
|
||||
result.put("phone", studentInfo.get("phone"));
|
||||
|
||||
// 学院、专业、班级信息
|
||||
result.put("collegeName", studentInfo.get("college_name"));
|
||||
result.put("majorName", studentInfo.get("major_name"));
|
||||
result.put("className", studentInfo.get("class_name"));
|
||||
|
||||
// 床位信息
|
||||
result.put("bedId", studentInfo.get("bed_id"));
|
||||
|
||||
log.info("获取床位学生信息成功,床位ID: {}, 学生信息: {}({})-{}-{}-{}",
|
||||
bedId,
|
||||
result.get("studentName"),
|
||||
result.get("studentNo"),
|
||||
result.get("collegeName"),
|
||||
result.get("majorName"),
|
||||
result.get("className"));
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取床位学生信息失败,床位ID: {}", bedId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+71
-46
@@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.xg.domain.bo.XgDormBuildingBo;
|
||||
import org.dromara.xg.domain.vo.XgDormBuildingVo;
|
||||
import org.dromara.xg.domain.vo.XgDormBuildingSimplifiedVo;
|
||||
import org.dromara.xg.domain.XgDormBuilding;
|
||||
import org.dromara.xg.domain.XgDormArea;
|
||||
import org.dromara.xg.domain.XgDormCampus;
|
||||
@@ -145,7 +146,7 @@ public class XgDormBuildingServiceImpl implements IXgDormBuildingService {
|
||||
|
||||
/**
|
||||
* 获取宿舍楼树形结构数据
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
|
||||
*
|
||||
* @return 宿舍楼树形结构数据
|
||||
*/
|
||||
@@ -156,59 +157,41 @@ public class XgDormBuildingServiceImpl implements IXgDormBuildingService {
|
||||
Wrappers.<XgDormCampus>lambdaQuery()
|
||||
.orderByAsc(XgDormCampus::getSortOrder, XgDormCampus::getId));
|
||||
|
||||
// 2. 获取所有宿舍区数据
|
||||
List<XgDormArea> areaList = areaMapper.selectList(
|
||||
Wrappers.<XgDormArea>lambdaQuery()
|
||||
.orderByAsc(XgDormArea::getSortOrder, XgDormArea::getId));
|
||||
|
||||
// 3. 获取所有宿舍楼数据
|
||||
// 2. 获取所有宿舍楼数据
|
||||
List<XgDormBuilding> buildingList = baseMapper.selectList(
|
||||
Wrappers.<XgDormBuilding>lambdaQuery()
|
||||
.orderByAsc(XgDormBuilding::getSortOrder, XgDormBuilding::getId));
|
||||
|
||||
// 4. 构建树形结构
|
||||
// 3. 构建树形结构
|
||||
List<BuildingTreeSelectVo> treeList = new ArrayList<>();
|
||||
|
||||
// 5. 先构建校区节点
|
||||
// 4. 先构建校区节点
|
||||
for (XgDormCampus campus : campusList) {
|
||||
BuildingTreeSelectVo campusNode = new BuildingTreeSelectVo();
|
||||
campusNode.setId(campus.getId());
|
||||
campusNode.setLabel(campus.getCampusName());
|
||||
campusNode.setNodeType("campus");
|
||||
campusNode.setDisabled(true); // 校区不可选
|
||||
campusNode.setChildren(new ArrayList<>());
|
||||
|
||||
// 设置创建者和更新者字段
|
||||
campusNode.setCreateBy(campus.getCreateBy());
|
||||
campusNode.setCreateTime(campus.getCreateTime());
|
||||
campusNode.setUpdateBy(campus.getUpdateBy());
|
||||
campusNode.setUpdateTime(campus.getUpdateTime());
|
||||
|
||||
// 添加该校区下的宿舍区
|
||||
List<XgDormArea> campusAreas = areaList.stream()
|
||||
.filter(area -> area.getCampusId().equals(campus.getId()))
|
||||
// 检查该校区下是否有宿舍楼
|
||||
List<XgDormBuilding> campusBuildings = buildingList.stream()
|
||||
.filter(building -> {
|
||||
// 通过宿舍区找到校区
|
||||
XgDormArea area = areaMapper.selectById(building.getAreaId());
|
||||
return area != null && area.getCampusId().equals(campus.getId());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (XgDormArea area : campusAreas) {
|
||||
BuildingTreeSelectVo areaNode = new BuildingTreeSelectVo();
|
||||
areaNode.setId(area.getId());
|
||||
areaNode.setLabel(area.getAreaName());
|
||||
areaNode.setNodeType("area");
|
||||
areaNode.setDisabled(true); // 宿舍区不可选
|
||||
areaNode.setChildren(new ArrayList<>());
|
||||
if (!campusBuildings.isEmpty()) {
|
||||
BuildingTreeSelectVo campusNode = new BuildingTreeSelectVo();
|
||||
campusNode.setId(campus.getId());
|
||||
campusNode.setLabel(campus.getCampusName());
|
||||
campusNode.setNodeType("campus");
|
||||
campusNode.setDisabled(true); // 校区不可选
|
||||
campusNode.setChildren(new ArrayList<>());
|
||||
|
||||
// 设置创建者和更新者字段
|
||||
areaNode.setCreateBy(area.getCreateBy());
|
||||
areaNode.setCreateTime(area.getCreateTime());
|
||||
areaNode.setUpdateBy(area.getUpdateBy());
|
||||
areaNode.setUpdateTime(area.getUpdateTime());
|
||||
campusNode.setCreateBy(campus.getCreateBy());
|
||||
campusNode.setCreateTime(campus.getCreateTime());
|
||||
campusNode.setUpdateBy(campus.getUpdateBy());
|
||||
campusNode.setUpdateTime(campus.getUpdateTime());
|
||||
|
||||
// 添加该宿舍区下的宿舍楼
|
||||
List<XgDormBuilding> areaBuildings = buildingList.stream()
|
||||
.filter(building -> building.getAreaId().equals(area.getId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (XgDormBuilding building : areaBuildings) {
|
||||
// 直接添加该校区下的宿舍楼,跳过宿舍区层级
|
||||
for (XgDormBuilding building : campusBuildings) {
|
||||
BuildingTreeSelectVo buildingNode = new BuildingTreeSelectVo();
|
||||
buildingNode.setId(building.getId());
|
||||
buildingNode.setLabel(building.getBuildingName());
|
||||
@@ -222,15 +205,57 @@ public class XgDormBuildingServiceImpl implements IXgDormBuildingService {
|
||||
buildingNode.setUpdateBy(building.getUpdateBy());
|
||||
buildingNode.setUpdateTime(building.getUpdateTime());
|
||||
|
||||
areaNode.getChildren().add(buildingNode);
|
||||
campusNode.getChildren().add(buildingNode);
|
||||
}
|
||||
|
||||
campusNode.getChildren().add(areaNode);
|
||||
treeList.add(campusNode);
|
||||
}
|
||||
|
||||
treeList.add(campusNode);
|
||||
}
|
||||
|
||||
return treeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询宿舍楼信息简化列表
|
||||
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 宿舍楼信息简化分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<XgDormBuildingSimplifiedVo> querySimplifiedPageList(XgDormBuildingBo bo, PageQuery pageQuery) {
|
||||
// 先查询完整的分页数据
|
||||
TableDataInfo<XgDormBuildingVo> fullPageList = queryPageList(bo, pageQuery);
|
||||
|
||||
// 转换为简化版VO
|
||||
List<XgDormBuildingSimplifiedVo> simplifiedList = fullPageList.getRows().stream()
|
||||
.map(XgDormBuildingSimplifiedVo::fromFullVo)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 创建简化版分页结果
|
||||
TableDataInfo<XgDormBuildingSimplifiedVo> simplifiedPageList = new TableDataInfo<>();
|
||||
simplifiedPageList.setRows(simplifiedList);
|
||||
simplifiedPageList.setTotal(fullPageList.getTotal());
|
||||
|
||||
return simplifiedPageList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的宿舍楼信息简化列表
|
||||
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 宿舍楼信息简化列表
|
||||
*/
|
||||
@Override
|
||||
public List<XgDormBuildingSimplifiedVo> querySimplifiedList(XgDormBuildingBo bo) {
|
||||
// 先查询完整的数据
|
||||
List<XgDormBuildingVo> fullList = queryList(bo);
|
||||
|
||||
// 转换为简化版VO
|
||||
return fullList.stream()
|
||||
.map(XgDormBuildingSimplifiedVo::fromFullVo)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+39
@@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.xg.service.IXgDormBuildingTreeService;
|
||||
import org.dromara.xg.mapper.XgDormBuildingTreeMapper;
|
||||
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
|
||||
import org.dromara.xg.domain.vo.DormBuildingTreeSimplifiedVo;
|
||||
import org.dromara.xg.domain.vo.DormBuildingInfoVo;
|
||||
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
@@ -231,4 +232,42 @@ public class XgDormBuildingTreeServiceImpl implements IXgDormBuildingTreeService
|
||||
return new ArrayList<>(campusMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宿舍楼树形结构简化数据
|
||||
* 校区-宿舍楼二级结构,隐藏宿舍区层级
|
||||
*
|
||||
* @param bo 查询参数
|
||||
* @return 简化树形结构数据
|
||||
*/
|
||||
@Override
|
||||
public List<DormBuildingTreeSimplifiedVo> getSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
// 先获取完整的树形结构数据
|
||||
List<DormBuildingTreeVo> fullTreeData = getDormBuildingTree(bo);
|
||||
|
||||
// 转换为简化版树形结构
|
||||
return fullTreeData.stream()
|
||||
.map(DormBuildingTreeSimplifiedVo::fromFullTreeVo)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有宿舍楼树形结构简化数据(不考虑权限)
|
||||
* 校区-宿舍楼二级结构,隐藏宿舍区层级
|
||||
*
|
||||
* @param bo 查询参数
|
||||
* @return 简化树形结构数据
|
||||
*/
|
||||
@Override
|
||||
public List<DormBuildingTreeSimplifiedVo> getAllSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
|
||||
// 先获取完整的树形结构数据(不考虑权限)
|
||||
List<DormBuildingTreeVo> fullTreeData = getAllDormBuildingTree(bo);
|
||||
|
||||
// 转换为简化版树形结构
|
||||
return fullTreeData.stream()
|
||||
.map(DormBuildingTreeSimplifiedVo::fromFullTreeVo)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
+769
-2
@@ -14,6 +14,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.xg.domain.bo.XgDormHygieneCheckBo;
|
||||
import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo;
|
||||
import org.dromara.xg.domain.bo.XgDormHygieneStudentScoreBo;
|
||||
import org.dromara.xg.domain.bo.XgHygieneCheckViolationBo;
|
||||
import org.dromara.xg.domain.bo.XgViolationDataBo;
|
||||
import org.dromara.xg.domain.vo.DormHygieneCheckExportVo;
|
||||
import org.dromara.xg.domain.vo.XgDormHygieneCheckVo;
|
||||
import org.dromara.xg.domain.XgDormHygieneCheck;
|
||||
@@ -22,11 +24,23 @@ import org.dromara.xg.mapper.XgDormHygieneCheckMapper;
|
||||
import org.dromara.xg.mapper.XgDormHygieneStudentScoreMapper;
|
||||
import org.dromara.xg.service.IXgDormHygieneCheckService;
|
||||
import org.dromara.xg.service.IXgStudentMsgService;
|
||||
import org.dromara.xg.service.IXgHygieneCheckViolationService;
|
||||
import org.dromara.xg.service.IXgDormHygieneStudentScoreService;
|
||||
import org.dromara.xg.mapper.XgHygieneCheckViolationMapper;
|
||||
import org.dromara.system.service.ISysDictDataService;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
|
||||
import org.dromara.xg.domain.XgHygieneCheckViolation;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查Service业务层处理
|
||||
@@ -43,6 +57,10 @@ public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService
|
||||
private final XgDormHygieneStudentScoreMapper studentScoreMapper;
|
||||
private final IXgStudentMsgService xgStudentMsgService;
|
||||
private final ISysDictDataService sysDictDataService;
|
||||
private final IXgHygieneCheckViolationService violationService;
|
||||
private final IXgDormHygieneStudentScoreService studentScoreService;
|
||||
private final XgHygieneCheckViolationMapper violationMapper;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 查询宿舍卫生检查
|
||||
@@ -154,18 +172,60 @@ public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除宿舍卫生检查信息
|
||||
* 校验并批量删除宿舍卫生检查信息(级联删除四个表)
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
|
||||
try {
|
||||
// 逐个处理每个卫生检查记录的级联删除
|
||||
for (Long checkId : ids) {
|
||||
log.info("开始级联删除宿舍卫生检查记录,检查ID: {}", checkId);
|
||||
|
||||
// 1. 查询并删除关联的违纪记录
|
||||
List<XgHygieneCheckViolationVo> violations = violationService.queryByHygieneCheckId(checkId);
|
||||
if (!violations.isEmpty()) {
|
||||
log.info("发现 {} 条违纪关联记录需要删除,检查ID: {}", violations.size(), checkId);
|
||||
|
||||
// 1.1 删除统一违纪记录 (xskh_discipline_record)
|
||||
for (XgHygieneCheckViolationVo violation : violations) {
|
||||
if (violation.getDisciplineRecordId() != null) {
|
||||
deleteDisciplineRecord(violation.getDisciplineRecordId());
|
||||
}
|
||||
}
|
||||
|
||||
// 1.2 删除违纪关联记录 (xg_hygiene_check_violation)
|
||||
violationService.deleteByHygieneCheckId(checkId);
|
||||
log.info("删除违纪关联记录完成,检查ID: {}", checkId);
|
||||
}
|
||||
|
||||
// 2. 删除学生评分记录 (xg_dorm_hygiene_student_score)
|
||||
Boolean studentScoreDeleted = studentScoreService.deleteByHygieneCheckId(checkId);
|
||||
if (studentScoreDeleted) {
|
||||
log.info("删除学生评分记录完成,检查ID: {}", checkId);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 删除主检查记录 (xg_dorm_hygiene_check)
|
||||
Boolean mainDeleted = baseMapper.deleteByIds(ids) > 0;
|
||||
if (mainDeleted) {
|
||||
log.info("删除宿舍卫生检查主记录完成,删除数量: {}", ids.size());
|
||||
}
|
||||
|
||||
return mainDeleted;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("级联删除宿舍卫生检查记录失败,检查IDs: {}, 错误: {}", ids, e.getMessage(), e);
|
||||
throw new RuntimeException("删除宿舍卫生检查记录失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,4 +634,711 @@ public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService
|
||||
return "1".equals(isResponsiblePerson) ? "是" : "否";
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存完整的宿舍卫生检查数据(包含学生评分和违纪记录)
|
||||
*
|
||||
* @param bo 完整的宿舍卫生检查数据
|
||||
* @return 检查记录ID,保存失败返回null
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long saveCompleteData(XgDormHygieneCheckCompleteBo bo) {
|
||||
try {
|
||||
// 1. 转换并保存主表数据
|
||||
XgDormHygieneCheck add = MapstructUtils.convert(bo, XgDormHygieneCheck.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
log.info("保存宿舍卫生检查主表数据: {}", add);
|
||||
|
||||
if (!flag) {
|
||||
log.error("保存宿舍卫生检查主表数据失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
Long hygieneCheckId = add.getId();
|
||||
|
||||
// 2. 保存学生评分数据
|
||||
if (bo.getStudentScores() != null && !bo.getStudentScores().isEmpty()) {
|
||||
for (XgDormHygieneStudentScoreBo studentScoreBo : bo.getStudentScores()) {
|
||||
XgDormHygieneStudentScore studentScore = MapstructUtils.convert(studentScoreBo,
|
||||
XgDormHygieneStudentScore.class);
|
||||
studentScore.setHygieneCheckId(hygieneCheckId);
|
||||
|
||||
// 根据学生ID查询学生完整信息,补充学院、专业、班级信息
|
||||
if (studentScoreBo.getStudentId() != null) {
|
||||
try {
|
||||
var studentInfo = xgStudentMsgService.queryById(studentScoreBo.getStudentId());
|
||||
if (studentInfo != null) {
|
||||
// 补充学院信息
|
||||
if (studentScore.getCollegeId() == null
|
||||
&& StringUtils.isNotBlank(studentInfo.getCollegeId())) {
|
||||
try {
|
||||
studentScore.setCollegeId(Long.valueOf(studentInfo.getCollegeId()));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("学院ID转换失败,学生ID: {}, 学院ID: {}", studentScoreBo.getStudentId(),
|
||||
studentInfo.getCollegeId());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(studentScore.getCollegeName())) {
|
||||
studentScore.setCollegeName(studentInfo.getCollegeName());
|
||||
}
|
||||
|
||||
// 补充专业信息
|
||||
if (studentScore.getMajorId() == null
|
||||
&& StringUtils.isNotBlank(studentInfo.getMajorId())) {
|
||||
try {
|
||||
studentScore.setMajorId(Long.valueOf(studentInfo.getMajorId()));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("专业ID转换失败,学生ID: {}, 专业ID: {}", studentScoreBo.getStudentId(),
|
||||
studentInfo.getMajorId());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(studentScore.getMajorName())) {
|
||||
studentScore.setMajorName(studentInfo.getMajorName());
|
||||
}
|
||||
|
||||
// 补充班级信息
|
||||
if (studentScore.getClassId() == null
|
||||
&& StringUtils.isNotBlank(studentInfo.getClassId())) {
|
||||
try {
|
||||
studentScore.setClassId(Long.valueOf(studentInfo.getClassId()));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("班级ID转换失败,学生ID: {}, 班级ID: {}", studentScoreBo.getStudentId(),
|
||||
studentInfo.getClassId());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(studentScore.getClassName())) {
|
||||
studentScore.setClassName(studentInfo.getClassName());
|
||||
}
|
||||
|
||||
log.info("补充学生信息 - 学生ID: {}, 学院: {}, 专业: {}, 班级: {}",
|
||||
studentScoreBo.getStudentId(),
|
||||
studentInfo.getCollegeName(),
|
||||
studentInfo.getMajorName(),
|
||||
studentInfo.getClassName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询学生信息失败,学生ID: {}, 错误: {}", studentScoreBo.getStudentId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("保存学生评分数据: {}", studentScore);
|
||||
studentScoreMapper.insert(studentScore);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 保存违纪记录数据(兼容架构:优先保存到统一表,失败时保存关联关系)
|
||||
if (bo.getViolationData() != null && !bo.getViolationData().isEmpty()) {
|
||||
for (XgViolationDataBo violationBo : bo.getViolationData()) {
|
||||
try {
|
||||
log.info("处理违纪记录数据: {}", violationBo);
|
||||
|
||||
// 3.1 尝试创建违纪记录到统一的xskh_discipline_record表
|
||||
Long disciplineRecordId = createDisciplineRecord(violationBo);
|
||||
|
||||
// 3.2 仅当违纪记录创建成功时才创建关联记录
|
||||
if (disciplineRecordId != null) {
|
||||
XgHygieneCheckViolationBo relationBo = new XgHygieneCheckViolationBo();
|
||||
relationBo.setHygieneCheckId(hygieneCheckId);
|
||||
relationBo.setDisciplineRecordId(disciplineRecordId);
|
||||
relationBo.setStudentId(violationBo.getStudentId());
|
||||
relationBo.setStudentNo(violationBo.getStudentNo());
|
||||
relationBo.setStudentName(violationBo.getStudentName());
|
||||
relationBo.setDisciplineTypeId(violationBo.getDisciplineTypeId());
|
||||
relationBo.setDisciplineTypeName(violationBo.getDisciplineTypeName());
|
||||
relationBo.setViolationSource("1"); // 1-宿舍卫生检查
|
||||
relationBo.setStatus("1"); // 1-生效
|
||||
|
||||
boolean relationResult = violationService.insertByBo(relationBo);
|
||||
if (!relationResult) {
|
||||
log.warn("创建卫生检查违纪关联记录失败,学生ID: {}", violationBo.getStudentId());
|
||||
} else {
|
||||
log.info("创建卫生检查违纪关联记录成功,学生ID: {},违纪记录ID: {}",
|
||||
violationBo.getStudentId(), disciplineRecordId);
|
||||
}
|
||||
} else {
|
||||
log.warn("违纪记录创建失败,跳过关联记录创建,学生ID: {}", violationBo.getStudentId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理违纪数据失败,学生ID: {}, 错误: {}", violationBo.getStudentId(), e.getMessage(), e);
|
||||
throw new RuntimeException(
|
||||
"处理违纪数据失败,学生ID: " + violationBo.getStudentId() + ", 错误: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("完整保存宿舍卫生检查数据成功,检查ID: {}", hygieneCheckId);
|
||||
return hygieneCheckId;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("保存宿舍卫生检查数据失败", e);
|
||||
throw new RuntimeException("保存宿舍卫生检查数据失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建学生违纪记录
|
||||
*
|
||||
* @param violationBo 违纪数据
|
||||
* @return 违纪记录ID
|
||||
*/
|
||||
private Long createDisciplineRecord(XgViolationDataBo violationBo) {
|
||||
try {
|
||||
// 通过接口类型动态获取IXskhDisciplineRecordService,避免硬依赖
|
||||
Class<?> serviceInterface = Class.forName("org.dromara.xskh.service.IXskhDisciplineRecordService");
|
||||
Object disciplineRecordService = applicationContext.getBean(serviceInterface);
|
||||
|
||||
if (disciplineRecordService != null) {
|
||||
// 创建XskhDisciplineRecordBo对象
|
||||
Object disciplineRecordBo = createDisciplineRecordBo(violationBo);
|
||||
if (disciplineRecordBo != null) {
|
||||
// 反射调用insertByBo方法 - 使用具体的Bo类型而不是Object
|
||||
Class<?> disciplineRecordBoClass = Class
|
||||
.forName("org.dromara.xskh.domain.bo.XskhDisciplineRecordBo");
|
||||
var insertMethod = disciplineRecordService.getClass().getMethod("insertByBo",
|
||||
disciplineRecordBoClass);
|
||||
Boolean result = (Boolean) insertMethod.invoke(disciplineRecordService, disciplineRecordBo);
|
||||
|
||||
if (result) {
|
||||
// 获取插入后的ID
|
||||
var field = disciplineRecordBo.getClass().getDeclaredField("id");
|
||||
field.setAccessible(true);
|
||||
Long id = (Long) field.get(disciplineRecordBo);
|
||||
log.info("创建学生违纪记录成功,学生ID: {}, 违纪记录ID: {}", violationBo.getStudentId(), id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.warn("学生考核管理模块(xskh)未启用,跳过违纪记录创建");
|
||||
} catch (Exception e) {
|
||||
log.error("创建学生违纪记录失败: {}", e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建学生违纪记录Bo对象
|
||||
*
|
||||
* @param violationBo 违纪数据
|
||||
* @return 学生违纪记录Bo对象
|
||||
*/
|
||||
private Object createDisciplineRecordBo(XgViolationDataBo violationBo) {
|
||||
try {
|
||||
// 通过反射创建XskhDisciplineRecordBo对象
|
||||
Class<?> disciplineRecordBoClass = Class.forName("org.dromara.xskh.domain.bo.XskhDisciplineRecordBo");
|
||||
Object disciplineRecordBo = disciplineRecordBoClass.getDeclaredConstructor().newInstance();
|
||||
|
||||
// 设置基本字段
|
||||
setFieldValue(disciplineRecordBo, "studentId", violationBo.getStudentId());
|
||||
setFieldValue(disciplineRecordBo, "studentNo", violationBo.getStudentNo());
|
||||
setFieldValue(disciplineRecordBo, "studentName", violationBo.getStudentName());
|
||||
|
||||
// 修复:确保collegeId、majorId、classId不为null
|
||||
// 如果ID为空,尝试通过学生ID查询完整信息
|
||||
Long collegeId = violationBo.getCollegeId();
|
||||
Long majorId = violationBo.getMajorId();
|
||||
Long classId = violationBo.getClassId();
|
||||
String collegeName = violationBo.getCollegeName();
|
||||
String majorName = violationBo.getMajorName();
|
||||
String className = violationBo.getClassName();
|
||||
|
||||
// 如果ID字段为空,尝试从学生信息中获取
|
||||
if ((collegeId == null || collegeId == 0) && violationBo.getStudentId() != null) {
|
||||
try {
|
||||
// 查询学生详细信息以获取缺失的ID字段
|
||||
Map<String, Object> studentDetail = getStudentDetailInfo(violationBo.getStudentId());
|
||||
if (studentDetail != null) {
|
||||
// 修复:安全地获取Long类型的ID字段
|
||||
Object collegeIdObj = studentDetail.get("collegeId");
|
||||
Object majorIdObj = studentDetail.get("majorId");
|
||||
Object classIdObj = studentDetail.get("classId");
|
||||
|
||||
collegeId = (collegeIdObj instanceof Long) ? (Long) collegeIdObj
|
||||
: (collegeIdObj != null ? Long.valueOf(collegeIdObj.toString()) : null);
|
||||
majorId = (majorIdObj instanceof Long) ? (Long) majorIdObj
|
||||
: (majorIdObj != null ? Long.valueOf(majorIdObj.toString()) : null);
|
||||
classId = (classIdObj instanceof Long) ? (Long) classIdObj
|
||||
: (classIdObj != null ? Long.valueOf(classIdObj.toString()) : null);
|
||||
|
||||
// 如果名称为空,也从查询结果中获取
|
||||
if (StringUtils.isBlank(collegeName)) {
|
||||
collegeName = (String) studentDetail.getOrDefault("collegeName", "未知学院");
|
||||
}
|
||||
if (StringUtils.isBlank(majorName)) {
|
||||
majorName = (String) studentDetail.getOrDefault("majorName", "未知专业");
|
||||
}
|
||||
if (StringUtils.isBlank(className)) {
|
||||
className = (String) studentDetail.getOrDefault("className", "未知班级");
|
||||
}
|
||||
|
||||
log.info("从学生信息中补充ID字段,学生ID: {}, 学院ID: {}, 专业ID: {}, 班级ID: {}",
|
||||
violationBo.getStudentId(), collegeId, majorId, classId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询学生详细信息失败,使用默认值,学生ID: {}, 错误: {}", violationBo.getStudentId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
setFieldValue(disciplineRecordBo, "collegeId", collegeId != null ? collegeId : 0L);
|
||||
setFieldValue(disciplineRecordBo, "collegeName",
|
||||
StringUtils.isNotBlank(collegeName) ? collegeName : "未知学院");
|
||||
setFieldValue(disciplineRecordBo, "majorId", majorId != null ? majorId : 0L);
|
||||
setFieldValue(disciplineRecordBo, "majorName",
|
||||
StringUtils.isNotBlank(majorName) ? majorName : "未知专业");
|
||||
setFieldValue(disciplineRecordBo, "classId", classId != null ? classId : 0L);
|
||||
setFieldValue(disciplineRecordBo, "className",
|
||||
StringUtils.isNotBlank(className) ? className : "未知班级");
|
||||
|
||||
setFieldValue(disciplineRecordBo, "disciplineTypeId", violationBo.getDisciplineTypeId());
|
||||
setFieldValue(disciplineRecordBo, "disciplineTypeName", violationBo.getDisciplineTypeName());
|
||||
|
||||
// 设置违纪日期
|
||||
if (StringUtils.isNotBlank(violationBo.getDisciplineDate())) {
|
||||
setFieldValue(disciplineRecordBo, "disciplineDate",
|
||||
java.time.LocalDate.parse(violationBo.getDisciplineDate()));
|
||||
} else {
|
||||
setFieldValue(disciplineRecordBo, "disciplineDate", java.time.LocalDate.now());
|
||||
}
|
||||
|
||||
// 设置违纪原因和内容
|
||||
setFieldValue(disciplineRecordBo, "disciplineReason", violationBo.getDisciplineReason());
|
||||
setFieldValue(disciplineRecordBo, "disciplineContent", violationBo.getDisciplineContent());
|
||||
|
||||
// 设置扣分分值
|
||||
if (violationBo.getScoreDeduction() != null) {
|
||||
setFieldValue(disciplineRecordBo, "scoreDeduction", violationBo.getScoreDeduction());
|
||||
}
|
||||
|
||||
// 设置状态和违纪来源
|
||||
setFieldValue(disciplineRecordBo, "status",
|
||||
StringUtils.isNotBlank(violationBo.getStatus()) ? violationBo.getStatus() : "1");
|
||||
setFieldValue(disciplineRecordBo, "violationSource", "1"); // 1-宿舍卫生检查
|
||||
|
||||
// 设置附件信息(如果有)
|
||||
if (StringUtils.isNotBlank(violationBo.getAttachmentOssIds())) {
|
||||
setFieldValue(disciplineRecordBo, "attachmentOssIds", violationBo.getAttachmentOssIds());
|
||||
}
|
||||
|
||||
log.info("创建学生违纪记录Bo对象成功,学生ID: {}, 违纪类型: {}",
|
||||
violationBo.getStudentId(), violationBo.getDisciplineTypeName());
|
||||
|
||||
return disciplineRecordBo;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建学生违纪记录Bo对象失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射设置对象字段值
|
||||
*
|
||||
* @param target 目标对象
|
||||
* @param fieldName 字段名
|
||||
* @param value 字段值
|
||||
*/
|
||||
private void setFieldValue(Object target, String fieldName, Object value) {
|
||||
try {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var field = target.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(target, value);
|
||||
} catch (Exception e) {
|
||||
log.warn("设置字段值失败,字段名: {}, 值: {}, 错误: {}", fieldName, value, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学生详细信息(包含学院、专业、班级ID)
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 学生详细信息Map
|
||||
*/
|
||||
private Map<String, Object> getStudentDetailInfo(Long studentId) {
|
||||
try {
|
||||
var studentInfo = xgStudentMsgService.queryById(studentId);
|
||||
if (studentInfo != null) {
|
||||
Map<String, Object> detailMap = new HashMap<>();
|
||||
|
||||
// 修复:安全地将String类型的ID转换为Long类型
|
||||
detailMap.put("collegeId", parseStringToLong(studentInfo.getCollegeId()));
|
||||
detailMap.put("collegeName", studentInfo.getCollegeName());
|
||||
detailMap.put("majorId", parseStringToLong(studentInfo.getMajorId()));
|
||||
detailMap.put("majorName", studentInfo.getMajorName());
|
||||
detailMap.put("classId", parseStringToLong(studentInfo.getClassId()));
|
||||
detailMap.put("className", studentInfo.getClassName());
|
||||
|
||||
log.debug("获取学生详细信息成功,学生ID: {}, 学院ID: {}, 专业ID: {}, 班级ID: {}",
|
||||
studentId, detailMap.get("collegeId"), detailMap.get("majorId"), detailMap.get("classId"));
|
||||
|
||||
return detailMap;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询学生详细信息失败,学生ID: {}", studentId, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地将String类型转换为Long类型
|
||||
*
|
||||
* @param str 字符串值
|
||||
* @return Long值,转换失败时返回null
|
||||
*/
|
||||
private Long parseStringToLong(String str) {
|
||||
if (StringUtils.isBlank(str)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.valueOf(str.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("字符串转Long失败,原值: {}", str);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateCompleteData(XgDormHygieneCheckCompleteBo bo) {
|
||||
try {
|
||||
// 1. 更新主表数据
|
||||
XgDormHygieneCheck update = MapstructUtils.convert(bo, XgDormHygieneCheck.class);
|
||||
validEntityBeforeSave(update);
|
||||
boolean flag = baseMapper.updateById(update) > 0;
|
||||
log.info("更新宿舍卫生检查主表数据: {}", update);
|
||||
|
||||
if (!flag) {
|
||||
log.error("更新宿舍卫生检查主表数据失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
Long hygieneCheckId = update.getId();
|
||||
|
||||
// 2. 更新学生评分数据 - 先删除旧数据,再插入新数据
|
||||
LambdaQueryWrapper<XgDormHygieneStudentScore> scoreWrapper = Wrappers.lambdaQuery();
|
||||
scoreWrapper.eq(XgDormHygieneStudentScore::getHygieneCheckId, hygieneCheckId);
|
||||
studentScoreMapper.delete(scoreWrapper);
|
||||
log.info("删除旧的学生评分数据,检查ID: {}", hygieneCheckId);
|
||||
|
||||
if (bo.getStudentScores() != null && !bo.getStudentScores().isEmpty()) {
|
||||
for (XgDormHygieneStudentScoreBo studentScoreBo : bo.getStudentScores()) {
|
||||
XgDormHygieneStudentScore studentScore = MapstructUtils.convert(studentScoreBo,
|
||||
XgDormHygieneStudentScore.class);
|
||||
studentScore.setHygieneCheckId(hygieneCheckId);
|
||||
studentScore.setId(null); // 确保是新插入
|
||||
|
||||
// 根据学生ID查询学生完整信息,补充学院、专业、班级信息
|
||||
if (studentScoreBo.getStudentId() != null) {
|
||||
try {
|
||||
var studentInfo = xgStudentMsgService.queryById(studentScoreBo.getStudentId());
|
||||
if (studentInfo != null) {
|
||||
// 补充学院信息
|
||||
if (studentScore.getCollegeId() == null
|
||||
&& StringUtils.isNotBlank(studentInfo.getCollegeId())) {
|
||||
try {
|
||||
studentScore.setCollegeId(Long.valueOf(studentInfo.getCollegeId()));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("学院ID转换失败,学生ID: {}, 学院ID: {}", studentScoreBo.getStudentId(),
|
||||
studentInfo.getCollegeId());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(studentScore.getCollegeName())) {
|
||||
studentScore.setCollegeName(studentInfo.getCollegeName());
|
||||
}
|
||||
|
||||
// 补充专业信息
|
||||
if (studentScore.getMajorId() == null
|
||||
&& StringUtils.isNotBlank(studentInfo.getMajorId())) {
|
||||
try {
|
||||
studentScore.setMajorId(Long.valueOf(studentInfo.getMajorId()));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("专业ID转换失败,学生ID: {}, 专业ID: {}", studentScoreBo.getStudentId(),
|
||||
studentInfo.getMajorId());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(studentScore.getMajorName())) {
|
||||
studentScore.setMajorName(studentInfo.getMajorName());
|
||||
}
|
||||
|
||||
// 补充班级信息
|
||||
if (studentScore.getClassId() == null
|
||||
&& StringUtils.isNotBlank(studentInfo.getClassId())) {
|
||||
try {
|
||||
studentScore.setClassId(Long.valueOf(studentInfo.getClassId()));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("班级ID转换失败,学生ID: {}, 班级ID: {}", studentScoreBo.getStudentId(),
|
||||
studentInfo.getClassId());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(studentScore.getClassName())) {
|
||||
studentScore.setClassName(studentInfo.getClassName());
|
||||
}
|
||||
|
||||
log.info("补充学生信息 - 学生ID: {}, 学院: {}, 专业: {}, 班级: {}",
|
||||
studentScoreBo.getStudentId(),
|
||||
studentInfo.getCollegeName(),
|
||||
studentInfo.getMajorName(),
|
||||
studentInfo.getClassName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询学生信息失败,学生ID: {}, 错误: {}", studentScoreBo.getStudentId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("保存学生评分数据: {}", studentScore);
|
||||
studentScoreMapper.insert(studentScore);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 处理违纪记录数据 - 完整的增删改逻辑
|
||||
handleViolationRecords(hygieneCheckId, bo.getViolationData());
|
||||
|
||||
log.info("完整更新宿舍卫生检查数据成功,检查ID: {}", hygieneCheckId);
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新宿舍卫生检查数据失败", e);
|
||||
throw new RuntimeException("更新宿舍卫生检查数据失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理违纪记录数据 - 完整的增删改逻辑
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @param newViolationData 新的违纪记录数据列表
|
||||
*/
|
||||
private void handleViolationRecords(Long hygieneCheckId, List<XgViolationDataBo> newViolationData) {
|
||||
try {
|
||||
// 1. 查询数据库中现有的违纪记录
|
||||
List<XgHygieneCheckViolationVo> existingViolations = getViolationsByHygieneCheckId(hygieneCheckId);
|
||||
log.info("数据库中现有违纪记录数量: {}", existingViolations.size());
|
||||
|
||||
// 2. 收集现有记录的违纪记录ID(disciplineRecordId)
|
||||
Set<Long> existingDisciplineIds = existingViolations.stream()
|
||||
.map(XgHygieneCheckViolationVo::getDisciplineRecordId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
log.info("现有违纪记录ID集合: {}", existingDisciplineIds);
|
||||
|
||||
// 3. 收集新数据中的违纪记录ID(优先使用disciplineRecordId字段,兼容旧版本的id字段)
|
||||
Set<Long> newDisciplineIds = new HashSet<>();
|
||||
if (newViolationData != null) {
|
||||
newDisciplineIds = newViolationData.stream()
|
||||
.map(violationBo -> {
|
||||
// 优先使用disciplineRecordId字段,如果为空则使用id字段(向后兼容)
|
||||
return violationBo.getDisciplineRecordId() != null
|
||||
? violationBo.getDisciplineRecordId()
|
||||
: violationBo.getId();
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
log.info("新数据中的违纪记录ID集合: {}", newDisciplineIds);
|
||||
|
||||
// 4. 找出需要删除的记录(在数据库中存在,但不在新数据中)
|
||||
Set<Long> toDeleteIds = new HashSet<>(existingDisciplineIds);
|
||||
toDeleteIds.removeAll(newDisciplineIds);
|
||||
log.info("需要删除的违纪记录ID集合: {}", toDeleteIds);
|
||||
|
||||
// 5. 删除不再存在的记录
|
||||
for (Long deleteId : toDeleteIds) {
|
||||
deleteViolationRecord(hygieneCheckId, deleteId);
|
||||
}
|
||||
|
||||
// 6. 处理新数据中的记录(新增或更新)
|
||||
if (newViolationData != null && !newViolationData.isEmpty()) {
|
||||
for (XgViolationDataBo violationBo : newViolationData) {
|
||||
try {
|
||||
log.info("处理违纪记录数据: {}", violationBo);
|
||||
|
||||
// 如果是已存在的违纪记录(有违纪记录ID且在现有记录中),更新统一表中的记录
|
||||
Long currentDisciplineRecordId = violationBo.getDisciplineRecordId() != null
|
||||
? violationBo.getDisciplineRecordId()
|
||||
: violationBo.getId();
|
||||
if (currentDisciplineRecordId != null
|
||||
&& existingDisciplineIds.contains(currentDisciplineRecordId)) {
|
||||
log.info("更新现有违纪记录,违纪记录ID: {}", currentDisciplineRecordId);
|
||||
updateDisciplineRecord(violationBo);
|
||||
} else {
|
||||
// 新增违纪记录
|
||||
log.info("新增违纪记录,学生ID: {}", violationBo.getStudentId());
|
||||
Long disciplineRecordId = createDisciplineRecord(violationBo);
|
||||
|
||||
if (disciplineRecordId != null && violationService != null) {
|
||||
XgHygieneCheckViolationBo relationBo = new XgHygieneCheckViolationBo();
|
||||
relationBo.setHygieneCheckId(hygieneCheckId);
|
||||
relationBo.setDisciplineRecordId(disciplineRecordId);
|
||||
relationBo.setStudentId(violationBo.getStudentId());
|
||||
relationBo.setStudentNo(violationBo.getStudentNo());
|
||||
relationBo.setStudentName(violationBo.getStudentName());
|
||||
relationBo.setDisciplineTypeId(violationBo.getDisciplineTypeId());
|
||||
relationBo.setDisciplineTypeName(violationBo.getDisciplineTypeName());
|
||||
relationBo.setViolationSource("1"); // 1-宿舍卫生检查
|
||||
relationBo.setStatus("1"); // 1-生效
|
||||
|
||||
boolean relationResult = violationService.insertByBo(relationBo);
|
||||
if (!relationResult) {
|
||||
log.warn("创建卫生检查违纪关联记录失败,学生ID: {}", violationBo.getStudentId());
|
||||
} else {
|
||||
log.info("创建卫生检查违纪关联记录成功,学生ID: {},违纪记录ID: {}",
|
||||
violationBo.getStudentId(), disciplineRecordId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理违纪数据失败,学生ID: {}, 错误: {}", violationBo.getStudentId(), e.getMessage(), e);
|
||||
throw new RuntimeException(
|
||||
"处理违纪数据失败,学生ID: " + violationBo.getStudentId() + ", 错误: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("违纪记录处理完成,卫生检查ID: {}", hygieneCheckId);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理违纪记录数据失败,卫生检查ID: {}, 错误: {}", hygieneCheckId, e.getMessage(), e);
|
||||
throw new RuntimeException("处理违纪记录数据失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除违纪记录(同时删除关联表和统一表中的记录)
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @param disciplineRecordId 违纪记录ID
|
||||
*/
|
||||
private void deleteViolationRecord(Long hygieneCheckId, Long disciplineRecordId) {
|
||||
try {
|
||||
log.info("开始逻辑删除违纪记录,卫生检查ID: {}, 违纪记录ID: {}", hygieneCheckId, disciplineRecordId);
|
||||
|
||||
// 1. 逻辑删除关联表中的记录 (xg_hygiene_check_violation)
|
||||
if (violationMapper != null) {
|
||||
LambdaQueryWrapper<XgHygieneCheckViolation> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(XgHygieneCheckViolation::getHygieneCheckId, hygieneCheckId)
|
||||
.eq(XgHygieneCheckViolation::getDisciplineRecordId, disciplineRecordId);
|
||||
|
||||
// 使用逻辑删除
|
||||
int deleteCount = violationMapper.delete(wrapper);
|
||||
if (deleteCount > 0) {
|
||||
log.info("逻辑删除卫生检查违纪关联记录成功,违纪记录ID: {}, 删除数量: {}", disciplineRecordId, deleteCount);
|
||||
} else {
|
||||
log.warn("逻辑删除卫生检查违纪关联记录失败,违纪记录ID: {}", disciplineRecordId);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 逻辑删除统一表中的记录 (xskh_discipline_record)
|
||||
deleteDisciplineRecord(disciplineRecordId);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("逻辑删除违纪记录失败,违纪记录ID: {}, 错误: {}", disciplineRecordId, e.getMessage(), e);
|
||||
throw new RuntimeException("逻辑删除违纪记录失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑删除统一表中的违纪记录
|
||||
*
|
||||
* @param disciplineRecordId 违纪记录ID
|
||||
*/
|
||||
private void deleteDisciplineRecord(Long disciplineRecordId) {
|
||||
try {
|
||||
// 通过接口类型动态获取IXskhDisciplineRecordService,避免硬依赖
|
||||
Class<?> serviceInterface = Class.forName("org.dromara.xskh.service.IXskhDisciplineRecordService");
|
||||
Object disciplineRecordService = applicationContext.getBean(serviceInterface);
|
||||
|
||||
if (disciplineRecordService != null) {
|
||||
// 使用deleteWithValidByIds方法进行逻辑删除
|
||||
var deleteMethod = disciplineRecordService.getClass().getMethod("deleteWithValidByIds",
|
||||
Collection.class, Boolean.class);
|
||||
Boolean result = (Boolean) deleteMethod.invoke(disciplineRecordService,
|
||||
Collections.singletonList(disciplineRecordId), false);
|
||||
|
||||
if (result) {
|
||||
log.info("逻辑删除统一违纪记录成功,违纪记录ID: {}", disciplineRecordId);
|
||||
} else {
|
||||
log.error("逻辑删除统一违纪记录失败,违纪记录ID: {}", disciplineRecordId);
|
||||
}
|
||||
} else {
|
||||
log.error("未找到违纪记录服务,无法删除统一违纪记录,违纪记录ID: {}", disciplineRecordId);
|
||||
}
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.warn("学生考核管理模块(xskh)未启用,跳过统一违纪记录删除");
|
||||
} catch (Exception e) {
|
||||
log.error("逻辑删除统一违纪记录失败,违纪记录ID: {}, 错误: {}", disciplineRecordId, e.getMessage(), e);
|
||||
throw new RuntimeException("逻辑删除统一违纪记录失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新学生违纪记录
|
||||
*
|
||||
* @param violationBo 违纪数据
|
||||
*/
|
||||
private void updateDisciplineRecord(XgViolationDataBo violationBo) {
|
||||
try {
|
||||
// 通过接口类型动态获取IXskhDisciplineRecordService,避免硬依赖
|
||||
Class<?> serviceInterface = Class.forName("org.dromara.xskh.service.IXskhDisciplineRecordService");
|
||||
Object disciplineRecordService = applicationContext.getBean(serviceInterface);
|
||||
|
||||
if (disciplineRecordService != null) {
|
||||
// 创建XskhDisciplineRecordBo对象
|
||||
Object disciplineRecordBo = createDisciplineRecordBo(violationBo);
|
||||
if (disciplineRecordBo != null) {
|
||||
// 设置ID用于更新
|
||||
setFieldValue(disciplineRecordBo, "id", violationBo.getId());
|
||||
|
||||
// 反射调用updateByBo方法
|
||||
Class<?> disciplineRecordBoClass = Class
|
||||
.forName("org.dromara.xskh.domain.bo.XskhDisciplineRecordBo");
|
||||
var updateMethod = disciplineRecordService.getClass().getMethod("updateByBo",
|
||||
disciplineRecordBoClass);
|
||||
Boolean result = (Boolean) updateMethod.invoke(disciplineRecordService, disciplineRecordBo);
|
||||
|
||||
if (result) {
|
||||
log.info("更新学生违纪记录成功,学生ID: {}, 违纪记录ID: {}",
|
||||
violationBo.getStudentId(), violationBo.getId());
|
||||
} else {
|
||||
log.warn("更新学生违纪记录失败,学生ID: {}", violationBo.getStudentId());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.warn("学生考核管理模块(xskh)未启用,跳过违纪记录更新");
|
||||
} catch (Exception e) {
|
||||
log.error("更新学生违纪记录失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID查询相关的违纪记录(包含详情)
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 违纪记录列表(包含详情)
|
||||
*/
|
||||
@Override
|
||||
public List<XgHygieneCheckViolationVo> getViolationsByHygieneCheckId(Long hygieneCheckId) {
|
||||
if (hygieneCheckId == null) {
|
||||
log.warn("卫生检查ID为空,无法查询违纪记录");
|
||||
return List.of();
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用自定义查询方法,关联查询违纪详情
|
||||
List<XgHygieneCheckViolationVo> violations = violationMapper
|
||||
.selectViolationsByHygieneCheckId(hygieneCheckId);
|
||||
|
||||
log.info("卫生检查ID: {} 查询到 {} 条违纪记录(包含详情)", hygieneCheckId, violations.size());
|
||||
return violations;
|
||||
} catch (Exception e) {
|
||||
log.error("查询卫生检查违纪记录失败,检查ID: {}, 错误: {}", hygieneCheckId, e.getMessage(), e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+284
-2
@@ -26,6 +26,24 @@ import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFRow;
|
||||
import org.apache.poi.xssf.usermodel.XSSFCell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.BorderStyle;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.dromara.xg.domain.XgDormBed;
|
||||
import org.dromara.xg.mapper.XgDormBedMapper;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* 宿舍房间信息Service业务层处理
|
||||
@@ -41,6 +59,7 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
|
||||
private final XgDormRoomMapper baseMapper;
|
||||
private final XgDormBedStudentMapper xgDormBedStudentMapper;
|
||||
private final XgSysDeptMapper xgSysDeptMapper;
|
||||
private final XgDormBedMapper xgDormBedMapper;
|
||||
|
||||
/**
|
||||
* 查询宿舍房间信息
|
||||
@@ -144,8 +163,9 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
|
||||
private LambdaQueryWrapper<XgDormRoom> buildQueryWrapper(XgDormRoomBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<XgDormRoom> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByAsc(XgDormRoom::getFloorNum); // 优先按楼层排序
|
||||
lqw.orderByAsc(XgDormRoom::getRoomName); // 其次按房间名称排序
|
||||
lqw.orderByAsc(XgDormRoom::getFloorNum); // 第一优先级:按楼层升序
|
||||
lqw.orderByAsc(XgDormRoom::getSort); // 第二优先级:按排序号升序
|
||||
lqw.orderByAsc(XgDormRoom::getRoomName); // 第三优先级:按房间名称升序
|
||||
lqw.like(StringUtils.isNotBlank(bo.getRoomCode()), XgDormRoom::getRoomCode, bo.getRoomCode());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getRoomName()), XgDormRoom::getRoomName, bo.getRoomName());
|
||||
lqw.eq(bo.getBuildingId() != null, XgDormRoom::getBuildingId, bo.getBuildingId());
|
||||
@@ -154,6 +174,7 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
|
||||
lqw.eq(bo.getActualBeds() != null, XgDormRoom::getActualBeds, bo.getActualBeds());
|
||||
lqw.eq(bo.getRoomArea() != null, XgDormRoom::getRoomArea, bo.getRoomArea());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getRoomStatus()), XgDormRoom::getRoomStatus, bo.getRoomStatus());
|
||||
lqw.eq(bo.getSort() != null, XgDormRoom::getSort, bo.getSort());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgDormRoom::getDeleted, bo.getDeleted());
|
||||
return lqw;
|
||||
}
|
||||
@@ -310,4 +331,265 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
|
||||
return Set.of();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出住宿清单(宿舍卡片式布局)
|
||||
*
|
||||
* @param params 导出参数
|
||||
* @param response HTTP响应对象
|
||||
*/
|
||||
@Override
|
||||
public void exportAccommodationList(Map<String, Object> params, HttpServletResponse response) {
|
||||
try {
|
||||
// 解析参数
|
||||
Long buildingId = Long.valueOf(params.get("buildingId").toString());
|
||||
String buildingName = params.get("buildingName").toString();
|
||||
String buildingType = params.get("buildingType").toString();
|
||||
String buildingTypeText = "1".equals(buildingType) ? "男生楼" : "女生楼";
|
||||
|
||||
// 解析JSON字符串
|
||||
String accommodationDataJson = params.get("accommodationData").toString();
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> accommodationData = objectMapper.readValue(accommodationDataJson, Map.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> buildingInfo = (Map<String, Object>) accommodationData.get("buildingInfo");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> floorDataList = (List<Map<String, Object>>) accommodationData.get("floorData");
|
||||
|
||||
// 创建Excel工作簿
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
|
||||
XSSFSheet sheet = workbook.createSheet(buildingName + "住宿清单");
|
||||
|
||||
// 创建样式
|
||||
CellStyle titleStyle = createTitleStyle(workbook);
|
||||
CellStyle headerStyle = createHeaderStyle(workbook);
|
||||
CellStyle dataStyle = createDataStyle(workbook);
|
||||
CellStyle roomNameStyle = createRoomNameStyle(workbook);
|
||||
|
||||
int currentRow = 0;
|
||||
|
||||
// 添加标题
|
||||
XSSFRow titleRow = sheet.createRow(currentRow++);
|
||||
XSSFCell titleCell = titleRow.createCell(0);
|
||||
titleCell.setCellValue(buildingName + " " + buildingTypeText + " 住宿清单");
|
||||
titleCell.setCellStyle(titleStyle);
|
||||
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 19)); // 合并20列
|
||||
currentRow++; // 空行
|
||||
|
||||
// 处理每个楼层
|
||||
for (Map<String, Object> floorData : floorDataList) {
|
||||
Integer floorNum = (Integer) floorData.get("floorNum");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> rooms = (List<Map<String, Object>>) floorData.get("rooms");
|
||||
|
||||
if (rooms == null || rooms.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 添加楼层标题
|
||||
XSSFRow floorRow = sheet.createRow(currentRow++);
|
||||
XSSFCell floorCell = floorRow.createCell(0);
|
||||
floorCell.setCellValue("第" + floorNum + "层");
|
||||
floorCell.setCellStyle(headerStyle);
|
||||
sheet.addMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 0, 19));
|
||||
currentRow++; // 空行
|
||||
|
||||
// 按4个宿舍一行进行分组处理
|
||||
for (int i = 0; i < rooms.size(); i += 4) {
|
||||
int endIndex = Math.min(i + 4, rooms.size());
|
||||
List<Map<String, Object>> roomGroup = rooms.subList(i, endIndex);
|
||||
|
||||
// 处理当前组的宿舍
|
||||
currentRow = processRoomGroup(sheet, roomGroup, currentRow, roomNameStyle, headerStyle,
|
||||
dataStyle);
|
||||
currentRow += 2; // 组间空行
|
||||
}
|
||||
}
|
||||
|
||||
// 设置列宽 (4个宿舍,每个宿舍4列,宿舍间3个空列,共19列)
|
||||
for (int i = 0; i < 20; i++) {
|
||||
sheet.setColumnWidth(i, 15 * 256); // 15个字符宽度
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
String fileName = buildingName + "_住宿清单_" + System.currentTimeMillis() + ".xlsx";
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition",
|
||||
"attachment; filename=\"" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1") + "\"");
|
||||
|
||||
// 输出Excel文件
|
||||
try (OutputStream outputStream = response.getOutputStream()) {
|
||||
workbook.write(outputStream);
|
||||
outputStream.flush();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("导出住宿清单失败", e);
|
||||
throw new RuntimeException("导出住宿清单失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理一组宿舍(最多4个)
|
||||
*/
|
||||
private int processRoomGroup(XSSFSheet sheet, List<Map<String, Object>> roomGroup, int startRow,
|
||||
CellStyle roomNameStyle, CellStyle headerStyle, CellStyle dataStyle) {
|
||||
int currentRow = startRow;
|
||||
|
||||
// 宿舍名称行
|
||||
XSSFRow roomNameRow = sheet.createRow(currentRow++);
|
||||
for (int i = 0; i < roomGroup.size(); i++) {
|
||||
Map<String, Object> room = roomGroup.get(i);
|
||||
String roomName = (String) room.get("roomName");
|
||||
|
||||
// 每个宿舍占4列,宿舍之间增加1列间隔 (i * 5)
|
||||
int startCol = i * 5;
|
||||
|
||||
// 为整个合并区域的所有单元格创建并设置样式
|
||||
for (int col = startCol; col <= startCol + 3; col++) {
|
||||
XSSFCell cell = roomNameRow.createCell(col);
|
||||
cell.setCellStyle(roomNameStyle);
|
||||
if (col == startCol) {
|
||||
cell.setCellValue(roomName); // 只在第一个单元格设置值
|
||||
}
|
||||
}
|
||||
|
||||
sheet.addMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, startCol, startCol + 3));
|
||||
}
|
||||
|
||||
// 表头行
|
||||
XSSFRow headerRow = sheet.createRow(currentRow++);
|
||||
String[] headers = { "床位", "姓名", "班级", "备注" };
|
||||
for (int i = 0; i < roomGroup.size(); i++) {
|
||||
for (int j = 0; j < headers.length; j++) {
|
||||
// 每个宿舍占4列,宿舍之间增加1列间隔 (i * 5)
|
||||
XSSFCell headerCell = headerRow.createCell(i * 5 + j);
|
||||
headerCell.setCellValue(headers[j]);
|
||||
headerCell.setCellStyle(headerStyle);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算最大床位数
|
||||
int maxBeds = roomGroup.stream()
|
||||
.mapToInt(room -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> beds = (List<Map<String, Object>>) room.get("beds");
|
||||
return beds != null ? beds.size() : 0;
|
||||
})
|
||||
.max()
|
||||
.orElse(0);
|
||||
|
||||
// 床位数据行
|
||||
for (int bedIndex = 0; bedIndex < maxBeds; bedIndex++) {
|
||||
XSSFRow dataRow = sheet.createRow(currentRow++);
|
||||
|
||||
for (int roomIndex = 0; roomIndex < roomGroup.size(); roomIndex++) {
|
||||
Map<String, Object> room = roomGroup.get(roomIndex);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> beds = (List<Map<String, Object>>) room.get("beds");
|
||||
|
||||
if (beds != null && bedIndex < beds.size()) {
|
||||
Map<String, Object> bed = beds.get(bedIndex);
|
||||
|
||||
// 每个宿舍占4列,宿舍之间增加1列间隔 (roomIndex * 5)
|
||||
// 床位
|
||||
XSSFCell bedNameCell = dataRow.createCell(roomIndex * 5);
|
||||
bedNameCell.setCellValue((String) bed.get("bedName"));
|
||||
bedNameCell.setCellStyle(dataStyle);
|
||||
|
||||
// 姓名
|
||||
XSSFCell studentNameCell = dataRow.createCell(roomIndex * 5 + 1);
|
||||
studentNameCell.setCellValue((String) bed.get("studentName"));
|
||||
studentNameCell.setCellStyle(dataStyle);
|
||||
|
||||
// 班级
|
||||
XSSFCell classNameCell = dataRow.createCell(roomIndex * 5 + 2);
|
||||
classNameCell.setCellValue((String) bed.get("className"));
|
||||
classNameCell.setCellStyle(dataStyle);
|
||||
|
||||
// 备注
|
||||
XSSFCell remarkCell = dataRow.createCell(roomIndex * 5 + 3);
|
||||
remarkCell.setCellValue((String) bed.get("bedRemark"));
|
||||
remarkCell.setCellStyle(dataStyle);
|
||||
} else {
|
||||
// 空床位
|
||||
for (int j = 0; j < 4; j++) {
|
||||
XSSFCell emptyCell = dataRow.createCell(roomIndex * 5 + j);
|
||||
emptyCell.setCellValue("");
|
||||
emptyCell.setCellStyle(dataStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建标题样式
|
||||
*/
|
||||
private CellStyle createTitleStyle(XSSFWorkbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
font.setFontHeightInPoints((short) 16);
|
||||
style.setFont(font);
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表头样式
|
||||
*/
|
||||
private CellStyle createHeaderStyle(XSSFWorkbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
font.setFontHeightInPoints((short) 12);
|
||||
style.setFont(font);
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据样式
|
||||
*/
|
||||
private CellStyle createDataStyle(XSSFWorkbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建宿舍名称样式
|
||||
*/
|
||||
private CellStyle createRoomNameStyle(XSSFWorkbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
font.setFontHeightInPoints((short) 14);
|
||||
style.setFont(font);
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
return style;
|
||||
}
|
||||
}
|
||||
+552
-11
@@ -21,6 +21,16 @@ import org.dromara.xg.service.IXgHolidayStayService;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Calendar;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
/**
|
||||
* 假期留宿管理Service业务层处理
|
||||
@@ -90,15 +100,42 @@ public class XgHolidayStayServiceImpl implements IXgHolidayStayService {
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCampusName()), XgHolidayStay::getCampusName, bo.getCampusName());
|
||||
lqw.eq(bo.getAreaId() != null, XgHolidayStay::getAreaId, bo.getAreaId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getAreaName()), XgHolidayStay::getAreaName, bo.getAreaName());
|
||||
lqw.eq(bo.getBuildingId() != null, XgHolidayStay::getBuildingId, bo.getBuildingId());
|
||||
|
||||
// 楼栋ID过滤逻辑:支持单个楼栋ID和多个楼栋ID的查询
|
||||
if (bo.getBuildingId() != null) {
|
||||
// 单个楼栋ID查询
|
||||
lqw.eq(XgHolidayStay::getBuildingId, bo.getBuildingId());
|
||||
} else if (params != null && params.containsKey("buildingIds")) {
|
||||
// 多个楼栋ID查询,从params中获取buildingIds(逗号分隔的字符串)
|
||||
String buildingIdsStr = (String) params.get("buildingIds");
|
||||
if (StringUtils.isNotBlank(buildingIdsStr)) {
|
||||
String[] buildingIdArray = buildingIdsStr.split(",");
|
||||
if (buildingIdArray.length > 0) {
|
||||
List<Long> buildingIds = new ArrayList<>();
|
||||
for (String buildingIdStr : buildingIdArray) {
|
||||
try {
|
||||
Long buildingId = Long.parseLong(buildingIdStr.trim());
|
||||
buildingIds.add(buildingId);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无效的楼栋ID: {}", buildingIdStr);
|
||||
}
|
||||
}
|
||||
if (!buildingIds.isEmpty()) {
|
||||
lqw.in(XgHolidayStay::getBuildingId, buildingIds);
|
||||
log.debug("添加多楼栋ID过滤条件: {}", buildingIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lqw.like(StringUtils.isNotBlank(bo.getBuildingName()), XgHolidayStay::getBuildingName, bo.getBuildingName());
|
||||
lqw.eq(bo.getRoomId() != null, XgHolidayStay::getRoomId, bo.getRoomId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getRoomName()), XgHolidayStay::getRoomName, bo.getRoomName());
|
||||
lqw.eq(bo.getBedId() != null, XgHolidayStay::getBedId, bo.getBedId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getBedName()), XgHolidayStay::getBedName, bo.getBedName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getHolidayType()), XgHolidayStay::getHolidayType, bo.getHolidayType());
|
||||
lqw.ge(bo.getApplyStartDate() != null, XgHolidayStay::getApplyStartDate, bo.getApplyStartDate());
|
||||
lqw.le(bo.getApplyEndDate() != null, XgHolidayStay::getApplyEndDate, bo.getApplyEndDate());
|
||||
lqw.eq(bo.getApplyStartDate() != null, XgHolidayStay::getApplyStartDate, bo.getApplyStartDate());
|
||||
lqw.eq(bo.getApplyEndDate() != null, XgHolidayStay::getApplyEndDate, bo.getApplyEndDate());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStayReason()), XgHolidayStay::getStayReason, bo.getStayReason());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStayReasonType()), XgHolidayStay::getStayReasonType,
|
||||
bo.getStayReasonType());
|
||||
@@ -110,9 +147,12 @@ public class XgHolidayStayServiceImpl implements IXgHolidayStayService {
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getParentConsent()), XgHolidayStay::getParentConsent, bo.getParentConsent());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getAttachmentOssIds()), XgHolidayStay::getAttachmentOssIds,
|
||||
bo.getAttachmentOssIds());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getExtField1()), XgHolidayStay::getExtField1, bo.getExtField1());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getExtField2()), XgHolidayStay::getExtField2, bo.getExtField2());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getExtField3()), XgHolidayStay::getExtField3, bo.getExtField3());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getExtField1()), XgHolidayStay::getExtField1, bo.getExtField1());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getExtField2()), XgHolidayStay::getExtField2, bo.getExtField2());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getExtField3()), XgHolidayStay::getExtField3, bo.getExtField3());
|
||||
|
||||
// 添加创建人过滤条件 - 实现班主任权限控制,只能查看自己创建的数据
|
||||
lqw.eq(bo.getCreateBy() != null, XgHolidayStay::getCreateBy, bo.getCreateBy());
|
||||
|
||||
// 添加状态过滤逻辑
|
||||
addStatusFilter(lqw, bo);
|
||||
@@ -122,7 +162,7 @@ public class XgHolidayStayServiceImpl implements IXgHolidayStayService {
|
||||
|
||||
/**
|
||||
* 添加状态过滤条件
|
||||
*
|
||||
*
|
||||
* @param lqw 查询条件构造器
|
||||
* @param bo 查询参数对象
|
||||
*/
|
||||
@@ -134,17 +174,19 @@ public class XgHolidayStayServiceImpl implements IXgHolidayStayService {
|
||||
|
||||
if (StringUtils.isNotBlank(statusType) && StringUtils.isNotBlank(currentTime)) {
|
||||
if ("valid".equals(statusType)) {
|
||||
// 有效状态:结束时间 >= 当前时间 AND 结束时间不为空
|
||||
// 有效状态:当前时间还没有超过留宿结束时间(包括预约状态)
|
||||
// 条件:applyEndDate >= currentTime AND applyEndDate IS NOT NULL
|
||||
lqw.ge(XgHolidayStay::getApplyEndDate, currentTime)
|
||||
.isNotNull(XgHolidayStay::getApplyEndDate);
|
||||
log.debug("添加有效状态过滤条件: apply_end_date >= {} AND apply_end_date IS NOT NULL", currentTime);
|
||||
log.debug("添加有效状态过滤条件: apply_end_date >= {} AND end_date is not null", currentTime);
|
||||
} else if ("invalid".equals(statusType)) {
|
||||
// 失效状态:结束时间 < 当前时间 OR 结束时间为空
|
||||
// 失效状态:当前时间已超过留宿结束时间
|
||||
// 条件:applyEndDate < currentTime OR applyEndDate IS NULL
|
||||
lqw.and(wrapper -> wrapper
|
||||
.lt(XgHolidayStay::getApplyEndDate, currentTime)
|
||||
.or()
|
||||
.isNull(XgHolidayStay::getApplyEndDate));
|
||||
log.debug("添加失效状态过滤条件: apply_end_date < {} OR apply_end_date IS NULL", currentTime);
|
||||
log.debug("添加失效状态过滤条件: apply_end_date < {} OR end_date is null", currentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,4 +256,503 @@ public class XgHolidayStayServiceImpl implements IXgHolidayStayService {
|
||||
Page<StudentOptionVo> result = baseMapper.selectStudentOptions(pageQuery.build(), bo);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单人留宿有效数据
|
||||
* 获取所有有效的假期留宿数据,筛选出在同一时间段内该宿舍只有一个学生留宿的记录
|
||||
*
|
||||
* @return 单人留宿有效数据列表
|
||||
*/
|
||||
@Override
|
||||
public List<XgHolidayStayVo> querySinglePersonValidStay() {
|
||||
// 1. 获取所有有效的假期留宿数据
|
||||
XgHolidayStayBo bo = new XgHolidayStayBo();
|
||||
Map<String, Object> params = bo.getParams();
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
bo.setParams(params);
|
||||
}
|
||||
|
||||
// 设置查询有效状态的留宿数据
|
||||
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
|
||||
params.put("statusType", "valid");
|
||||
params.put("currentTime", currentTime);
|
||||
|
||||
List<XgHolidayStayVo> validStayList = queryList(bo);
|
||||
log.info("查询到有效的假期留宿数据总数: {}", validStayList.size());
|
||||
|
||||
if (validStayList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 2. 按房间分组,筛选出单人留宿的数据
|
||||
List<XgHolidayStayVo> singlePersonStayList = new ArrayList<>();
|
||||
|
||||
// 按房间ID分组
|
||||
Map<Long, List<XgHolidayStayVo>> roomGroupMap = validStayList.stream()
|
||||
.filter(stay -> stay.getRoomId() != null)
|
||||
.collect(Collectors.groupingBy(XgHolidayStayVo::getRoomId));
|
||||
|
||||
log.info("按房间分组后,涉及房间数量: {}", roomGroupMap.size());
|
||||
|
||||
// 遍历每个房间的留宿数据
|
||||
for (Map.Entry<Long, List<XgHolidayStayVo>> entry : roomGroupMap.entrySet()) {
|
||||
Long roomId = entry.getKey();
|
||||
List<XgHolidayStayVo> roomStayList = entry.getValue();
|
||||
|
||||
log.debug("处理房间ID: {}, 该房间留宿人数: {}", roomId, roomStayList.size());
|
||||
|
||||
// 3. 对于每个房间,检查是否存在单人留宿的时间段
|
||||
List<XgHolidayStayVo> singlePersonInRoom = findSinglePersonPeriodsInRoom(roomStayList);
|
||||
singlePersonStayList.addAll(singlePersonInRoom);
|
||||
}
|
||||
|
||||
log.info("筛选出单人留宿有效数据总数: {}", singlePersonStayList.size());
|
||||
|
||||
// 按ID倒序排序(作为时间排序的替代)
|
||||
return singlePersonStayList.stream()
|
||||
.sorted((a, b) -> {
|
||||
if (a.getId() == null && b.getId() == null)
|
||||
return 0;
|
||||
if (a.getId() == null)
|
||||
return 1;
|
||||
if (b.getId() == null)
|
||||
return -1;
|
||||
return b.getId().compareTo(a.getId());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查房间内是否存在单人留宿的时间段
|
||||
*
|
||||
* @param roomStayList 同一房间内的留宿数据列表
|
||||
* @return 单人留宿的数据列表
|
||||
*/
|
||||
private List<XgHolidayStayVo> findSinglePersonPeriodsInRoom(List<XgHolidayStayVo> roomStayList) {
|
||||
List<XgHolidayStayVo> singlePersonList = new ArrayList<>();
|
||||
|
||||
if (roomStayList == null || roomStayList.isEmpty()) {
|
||||
return singlePersonList;
|
||||
}
|
||||
|
||||
// 如果房间内只有一个人留宿,直接返回
|
||||
if (roomStayList.size() == 1) {
|
||||
singlePersonList.add(roomStayList.get(0));
|
||||
log.debug("房间 {} 只有一个人留宿: {}", roomStayList.get(0).getRoomName(), roomStayList.get(0).getStudentName());
|
||||
return singlePersonList;
|
||||
}
|
||||
|
||||
// 按学生分组,处理每个学生的多个留宿记录
|
||||
Map<Long, List<XgHolidayStayVo>> studentStaysMap = roomStayList.stream()
|
||||
.collect(Collectors.groupingBy(XgHolidayStayVo::getStudentId));
|
||||
|
||||
// 为每个学生创建时间段
|
||||
Map<Long, List<TimeSegment>> studentTimeSegments = new HashMap<>();
|
||||
for (Map.Entry<Long, List<XgHolidayStayVo>> entry : studentStaysMap.entrySet()) {
|
||||
Long studentId = entry.getKey();
|
||||
List<XgHolidayStayVo> stays = entry.getValue();
|
||||
|
||||
List<TimeSegment> segments = new ArrayList<>();
|
||||
for (XgHolidayStayVo stay : stays) {
|
||||
if (stay.getApplyStartDate() != null && stay.getApplyEndDate() != null) {
|
||||
segments.add(new TimeSegment(stay.getApplyStartDate(), stay.getApplyEndDate(), studentId, stay));
|
||||
}
|
||||
}
|
||||
|
||||
// 合并同一学生的重叠时间段
|
||||
List<TimeSegment> mergedSegments = mergeTimeSegments(segments);
|
||||
studentTimeSegments.put(studentId, mergedSegments);
|
||||
}
|
||||
|
||||
// 收集所有关键时间点
|
||||
Set<Date> keyTimePoints = new TreeSet<>();
|
||||
for (List<TimeSegment> segments : studentTimeSegments.values()) {
|
||||
for (TimeSegment segment : segments) {
|
||||
keyTimePoints.add(segment.getStartTime());
|
||||
keyTimePoints.add(segment.getEndTime());
|
||||
}
|
||||
}
|
||||
|
||||
List<Date> sortedTimePoints = new ArrayList<>(keyTimePoints);
|
||||
log.debug("房间 {} 关键时间点数量: {}", roomStayList.get(0).getRoomName(), sortedTimePoints.size());
|
||||
|
||||
// 分析每个时间段内的留宿人数
|
||||
for (int i = 0; i < sortedTimePoints.size() - 1; i++) {
|
||||
Date segmentStart = sortedTimePoints.get(i);
|
||||
Date segmentEnd = sortedTimePoints.get(i + 1);
|
||||
|
||||
// 找出在这个时间段内留宿的所有学生
|
||||
List<Long> studentsInSegment = new ArrayList<>();
|
||||
Map<Long, XgHolidayStayVo> studentStayMap = new HashMap<>();
|
||||
|
||||
for (Map.Entry<Long, List<TimeSegment>> entry : studentTimeSegments.entrySet()) {
|
||||
Long studentId = entry.getKey();
|
||||
List<TimeSegment> segments = entry.getValue();
|
||||
|
||||
for (TimeSegment segment : segments) {
|
||||
// 检查时间段是否重叠(注意边界条件)
|
||||
if (segment.getStartTime().compareTo(segmentEnd) < 0 &&
|
||||
segment.getEndTime().compareTo(segmentStart) > 0) {
|
||||
studentsInSegment.add(studentId);
|
||||
studentStayMap.put(studentId, segment.getOriginalStay());
|
||||
break; // 一个学生在同一时间段内只能有一个记录
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只有一个学生,这是单人留宿时间段
|
||||
if (studentsInSegment.size() == 1) {
|
||||
Long singleStudentId = studentsInSegment.get(0);
|
||||
XgHolidayStayVo originalStay = studentStayMap.get(singleStudentId);
|
||||
|
||||
// 创建单人留宿记录
|
||||
XgHolidayStayVo singlePersonStay = createSinglePersonStayFromSegment(
|
||||
originalStay, segmentStart, segmentEnd);
|
||||
singlePersonList.add(singlePersonStay);
|
||||
|
||||
log.debug("找到单人留宿时间段: {} ({} - {}) 学生: {}",
|
||||
originalStay.getRoomName(),
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(segmentStart),
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(segmentEnd),
|
||||
originalStay.getStudentName());
|
||||
}
|
||||
}
|
||||
|
||||
// 合并同一学生的连续单人留宿时间段
|
||||
return mergeConsecutiveSinglePersonStays(singlePersonList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个日期之间的所有日期(包含起始和结束日期)
|
||||
*
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @param dateFormat 日期格式化器
|
||||
* @return 日期字符串列表
|
||||
*/
|
||||
private List<String> getDatesBetween(Date startDate, Date endDate, SimpleDateFormat dateFormat) {
|
||||
List<String> dates = new ArrayList<>();
|
||||
|
||||
if (startDate == null || endDate == null) {
|
||||
return dates;
|
||||
}
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(startDate);
|
||||
|
||||
// 只取日期部分,忽略时间
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Calendar endCalendar = Calendar.getInstance();
|
||||
endCalendar.setTime(endDate);
|
||||
endCalendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
endCalendar.set(Calendar.MINUTE, 0);
|
||||
endCalendar.set(Calendar.SECOND, 0);
|
||||
endCalendar.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
// 生成日期列表
|
||||
while (!calendar.after(endCalendar)) {
|
||||
dates.add(dateFormat.format(calendar.getTime()));
|
||||
calendar.add(Calendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
|
||||
return dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单人留宿记录,调整时间范围为单人留宿的日期范围
|
||||
*
|
||||
* @param originalStay 原始留宿记录
|
||||
* @param singlePersonDates 单人留宿的日期列表
|
||||
* @param dateFormat 日期格式化器
|
||||
* @return 调整后的单人留宿记录
|
||||
*/
|
||||
private XgHolidayStayVo createSinglePersonStayRecord(XgHolidayStayVo originalStay, List<String> singlePersonDates,
|
||||
SimpleDateFormat dateFormat) {
|
||||
if (singlePersonDates.isEmpty()) {
|
||||
return originalStay;
|
||||
}
|
||||
|
||||
try {
|
||||
// 排序日期列表
|
||||
singlePersonDates.sort(String::compareTo);
|
||||
|
||||
// 获取单人留宿的开始和结束日期
|
||||
String startDateStr = singlePersonDates.get(0);
|
||||
String endDateStr = singlePersonDates.get(singlePersonDates.size() - 1);
|
||||
|
||||
// 克隆原始记录
|
||||
XgHolidayStayVo singlePersonStay = new XgHolidayStayVo();
|
||||
BeanUtils.copyProperties(originalStay, singlePersonStay);
|
||||
|
||||
// 调整时间范围为单人留宿的日期范围
|
||||
Date adjustedStartDate = dateFormat.parse(startDateStr);
|
||||
Date adjustedEndDate = dateFormat.parse(endDateStr);
|
||||
|
||||
// 结束日期设置为当天的23:59:59
|
||||
Calendar endCalendar = Calendar.getInstance();
|
||||
endCalendar.setTime(adjustedEndDate);
|
||||
endCalendar.set(Calendar.HOUR_OF_DAY, 23);
|
||||
endCalendar.set(Calendar.MINUTE, 59);
|
||||
endCalendar.set(Calendar.SECOND, 59);
|
||||
adjustedEndDate = endCalendar.getTime();
|
||||
|
||||
singlePersonStay.setApplyStartDate(adjustedStartDate);
|
||||
singlePersonStay.setApplyEndDate(adjustedEndDate);
|
||||
|
||||
// 在备注中添加单人留宿信息
|
||||
String originalRemark = singlePersonStay.getRemark() == null ? "" : singlePersonStay.getRemark();
|
||||
String singlePersonInfo = String.format("【单人留宿】%s天(%s至%s)",
|
||||
singlePersonDates.size(), startDateStr, endDateStr);
|
||||
singlePersonStay
|
||||
.setRemark(originalRemark.isEmpty() ? singlePersonInfo : originalRemark + " " + singlePersonInfo);
|
||||
|
||||
log.debug("为学生 {} 创建单人留宿记录: {} 天 ({} - {})",
|
||||
singlePersonStay.getStudentName(), singlePersonDates.size(), startDateStr, endDateStr);
|
||||
|
||||
return singlePersonStay;
|
||||
} catch (Exception e) {
|
||||
log.error("创建单人留宿记录失败", e);
|
||||
return originalStay;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查两个留宿时间段是否重叠
|
||||
*
|
||||
* @param stay1 留宿记录1
|
||||
* @param stay2 留宿记录2
|
||||
* @return 是否重叠
|
||||
*/
|
||||
private boolean isTimeOverlap(XgHolidayStayVo stay1, XgHolidayStayVo stay2) {
|
||||
if (stay1.getApplyStartDate() == null || stay1.getApplyEndDate() == null ||
|
||||
stay2.getApplyStartDate() == null || stay2.getApplyEndDate() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Date start1 = stay1.getApplyStartDate();
|
||||
Date end1 = stay1.getApplyEndDate();
|
||||
Date start2 = stay2.getApplyStartDate();
|
||||
Date end2 = stay2.getApplyEndDate();
|
||||
|
||||
// 检查时间段是否重叠:两个时间段重叠当且仅当 start1 <= end2 && start2 <= end1
|
||||
boolean overlap = start1.compareTo(end2) <= 0 && start2.compareTo(end1) <= 0;
|
||||
|
||||
if (overlap) {
|
||||
log.debug("时间段重叠: {} ({} - {}) 与 {} ({} - {})",
|
||||
stay1.getStudentName(), start1, end1,
|
||||
stay2.getStudentName(), start2, end2);
|
||||
}
|
||||
|
||||
return overlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并同一学生的重叠时间段
|
||||
*/
|
||||
private List<TimeSegment> mergeTimeSegments(List<TimeSegment> segments) {
|
||||
if (segments.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 按开始时间排序
|
||||
segments.sort(Comparator.comparing(TimeSegment::getStartTime));
|
||||
|
||||
List<TimeSegment> merged = new ArrayList<>();
|
||||
TimeSegment current = segments.get(0);
|
||||
|
||||
for (int i = 1; i < segments.size(); i++) {
|
||||
TimeSegment next = segments.get(i);
|
||||
|
||||
// 如果时间段重叠或相邻,合并它们
|
||||
if (current.getEndTime().compareTo(next.getStartTime()) >= 0) {
|
||||
// 扩展当前时间段
|
||||
if (next.getEndTime().compareTo(current.getEndTime()) > 0) {
|
||||
current = new TimeSegment(
|
||||
current.getStartTime(),
|
||||
next.getEndTime(),
|
||||
current.getStudentId(),
|
||||
current.getOriginalStay()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 不重叠,添加当前时间段到结果中
|
||||
merged.add(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一个时间段
|
||||
merged.add(current);
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从时间段创建单人留宿记录
|
||||
*/
|
||||
private XgHolidayStayVo createSinglePersonStayFromSegment(XgHolidayStayVo template, Date startTime, Date endTime) {
|
||||
XgHolidayStayVo singlePersonStay = new XgHolidayStayVo();
|
||||
|
||||
// 复制基本信息
|
||||
BeanUtils.copyProperties(template, singlePersonStay);
|
||||
|
||||
// 设置ID为null,表示这是计算出来的记录
|
||||
singlePersonStay.setId(null);
|
||||
|
||||
// 设置单人留宿的时间段
|
||||
singlePersonStay.setApplyStartDate(startTime);
|
||||
singlePersonStay.setApplyEndDate(endTime);
|
||||
|
||||
// 计算单人留宿天数
|
||||
long timeDiff = endTime.getTime() - startTime.getTime();
|
||||
long days = Math.max(1, timeDiff / (24 * 60 * 60 * 1000) + 1); // 至少1天
|
||||
|
||||
// 设置备注信息
|
||||
String originalRemark = template.getRemark() != null ? template.getRemark() : "";
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String singlePersonRemark = String.format("单人留宿%d天 (%s至%s)",
|
||||
days,
|
||||
sdf.format(startTime),
|
||||
sdf.format(endTime)
|
||||
);
|
||||
|
||||
if (!originalRemark.isEmpty()) {
|
||||
singlePersonStay.setRemark(originalRemark + " | " + singlePersonRemark);
|
||||
} else {
|
||||
singlePersonStay.setRemark(singlePersonRemark);
|
||||
}
|
||||
|
||||
return singlePersonStay;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并同一学生的连续单人留宿时间段
|
||||
*/
|
||||
private List<XgHolidayStayVo> mergeConsecutiveSinglePersonStays(List<XgHolidayStayVo> singlePersonStays) {
|
||||
if (singlePersonStays.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 按学生ID和开始时间分组排序
|
||||
Map<Long, List<XgHolidayStayVo>> studentStaysMap = singlePersonStays.stream()
|
||||
.collect(Collectors.groupingBy(XgHolidayStayVo::getStudentId));
|
||||
|
||||
List<XgHolidayStayVo> mergedStays = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<Long, List<XgHolidayStayVo>> entry : studentStaysMap.entrySet()) {
|
||||
List<XgHolidayStayVo> stays = entry.getValue();
|
||||
|
||||
// 按开始时间排序
|
||||
stays.sort(Comparator.comparing(XgHolidayStayVo::getApplyStartDate));
|
||||
|
||||
XgHolidayStayVo current = stays.get(0);
|
||||
|
||||
for (int i = 1; i < stays.size(); i++) {
|
||||
XgHolidayStayVo next = stays.get(i);
|
||||
|
||||
// 如果时间段连续或重叠,合并它们
|
||||
if (current.getApplyEndDate().compareTo(next.getApplyStartDate()) >= 0) {
|
||||
// 扩展当前时间段
|
||||
if (next.getApplyEndDate().compareTo(current.getApplyEndDate()) > 0) {
|
||||
current.setApplyEndDate(next.getApplyEndDate());
|
||||
}
|
||||
|
||||
// 更新备注信息
|
||||
updateMergedRemark(current, next);
|
||||
} else {
|
||||
// 不连续,添加当前时间段到结果中
|
||||
mergedStays.add(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一个时间段
|
||||
mergedStays.add(current);
|
||||
}
|
||||
|
||||
return mergedStays;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新合并后的备注信息
|
||||
*/
|
||||
private void updateMergedRemark(XgHolidayStayVo current, XgHolidayStayVo next) {
|
||||
// 重新计算合并后的天数
|
||||
long timeDiff = current.getApplyEndDate().getTime() - current.getApplyStartDate().getTime();
|
||||
long totalDays = Math.max(1, timeDiff / (24 * 60 * 60 * 1000) + 1);
|
||||
|
||||
// 更新备注
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String mergedRemark = String.format("单人留宿%d天 (%s至%s)",
|
||||
totalDays,
|
||||
sdf.format(current.getApplyStartDate()),
|
||||
sdf.format(current.getApplyEndDate())
|
||||
);
|
||||
|
||||
// 保留原始备注的非单人留宿信息
|
||||
String originalPart = extractOriginalRemark(current.getRemark());
|
||||
if (!originalPart.isEmpty()) {
|
||||
current.setRemark(originalPart + " | " + mergedRemark);
|
||||
} else {
|
||||
current.setRemark(mergedRemark);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取原始备注信息(去除单人留宿相关的备注)
|
||||
*/
|
||||
private String extractOriginalRemark(String remark) {
|
||||
if (remark == null || remark.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 如果包含单人留宿标记,提取原始部分
|
||||
if (remark.contains(" | 单人留宿")) {
|
||||
return remark.split(" \\| 单人留宿")[0].trim();
|
||||
}
|
||||
|
||||
// 如果以单人留宿开头,返回空
|
||||
if (remark.startsWith("单人留宿")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return remark;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间段内部类
|
||||
*/
|
||||
private static class TimeSegment {
|
||||
private Date startTime;
|
||||
private Date endTime;
|
||||
private Long studentId;
|
||||
private XgHolidayStayVo originalStay;
|
||||
|
||||
public TimeSegment(Date startTime, Date endTime, Long studentId, XgHolidayStayVo originalStay) {
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.studentId = studentId;
|
||||
this.originalStay = originalStay;
|
||||
}
|
||||
|
||||
public Date getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public Date getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public Long getStudentId() {
|
||||
return studentId;
|
||||
}
|
||||
|
||||
public XgHolidayStayVo getOriginalStay() {
|
||||
return originalStay;
|
||||
}
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
package org.dromara.xg.service.impl;
|
||||
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.xg.domain.bo.XgHygieneCheckViolationBo;
|
||||
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
|
||||
import org.dromara.xg.domain.XgHygieneCheckViolation;
|
||||
import org.dromara.xg.mapper.XgHygieneCheckViolationMapper;
|
||||
import org.dromara.xg.service.IXgHygieneCheckViolationService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 宿舍卫生检查违纪记录Service业务层处理
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-29
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class XgHygieneCheckViolationServiceImpl implements IXgHygieneCheckViolationService {
|
||||
|
||||
private final XgHygieneCheckViolationMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 宿舍卫生检查违纪记录
|
||||
*/
|
||||
@Override
|
||||
public XgHygieneCheckViolationVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询宿舍卫生检查违纪记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 宿舍卫生检查违纪记录分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<XgHygieneCheckViolationVo> queryPageList(XgHygieneCheckViolationBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<XgHygieneCheckViolation> lqw = buildQueryWrapper(bo);
|
||||
Page<XgHygieneCheckViolationVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的宿舍卫生检查违纪记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 宿舍卫生检查违纪记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<XgHygieneCheckViolationVo> queryList(XgHygieneCheckViolationBo bo) {
|
||||
LambdaQueryWrapper<XgHygieneCheckViolation> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<XgHygieneCheckViolation> buildQueryWrapper(XgHygieneCheckViolationBo bo) {
|
||||
LambdaQueryWrapper<XgHygieneCheckViolation> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getHygieneCheckId() != null, XgHygieneCheckViolation::getHygieneCheckId, bo.getHygieneCheckId());
|
||||
lqw.eq(bo.getStudentId() != null, XgHygieneCheckViolation::getStudentId, bo.getStudentId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStudentNo()), XgHygieneCheckViolation::getStudentNo, bo.getStudentNo());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgHygieneCheckViolation::getStudentName,
|
||||
bo.getStudentName());
|
||||
lqw.eq(bo.getDisciplineTypeId() != null, XgHygieneCheckViolation::getDisciplineTypeId,
|
||||
bo.getDisciplineTypeId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getDisciplineTypeName()), XgHygieneCheckViolation::getDisciplineTypeName,
|
||||
bo.getDisciplineTypeName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getViolationSource()), XgHygieneCheckViolation::getViolationSource,
|
||||
bo.getViolationSource());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), XgHygieneCheckViolation::getStatus, bo.getStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param bo 宿舍卫生检查违纪记录
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(XgHygieneCheckViolationBo bo) {
|
||||
XgHygieneCheckViolation add = MapstructUtils.convert(bo, XgHygieneCheckViolation.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param bo 宿舍卫生检查违纪记录
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(XgHygieneCheckViolationBo bo) {
|
||||
XgHygieneCheckViolation update = MapstructUtils.convert(bo, XgHygieneCheckViolation.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*
|
||||
* @param entity 实体类数据
|
||||
*/
|
||||
private void validEntityBeforeSave(XgHygieneCheckViolation entity) {
|
||||
// TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除宿舍卫生检查违纪记录信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量新增宿舍卫生检查违纪记录
|
||||
*
|
||||
* @param bos 违纪记录列表
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertBatch(List<XgHygieneCheckViolationBo> bos) {
|
||||
if (bos == null || bos.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<XgHygieneCheckViolation> entities = MapstructUtils.convert(bos, XgHygieneCheckViolation.class);
|
||||
for (XgHygieneCheckViolation entity : entities) {
|
||||
validEntityBeforeSave(entity);
|
||||
}
|
||||
|
||||
return baseMapper.insertBatch(entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID查询违纪记录列表
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 违纪记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<XgHygieneCheckViolationVo> queryByHygieneCheckId(Long hygieneCheckId) {
|
||||
LambdaQueryWrapper<XgHygieneCheckViolation> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(XgHygieneCheckViolation::getHygieneCheckId, hygieneCheckId);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据卫生检查ID删除违纪关联记录
|
||||
*
|
||||
* @param hygieneCheckId 卫生检查ID
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteByHygieneCheckId(Long hygieneCheckId) {
|
||||
LambdaQueryWrapper<XgHygieneCheckViolation> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(XgHygieneCheckViolation::getHygieneCheckId, hygieneCheckId);
|
||||
return baseMapper.delete(lqw) > 0;
|
||||
}
|
||||
}
|
||||
+34
@@ -51,6 +51,7 @@ import org.dromara.xg.domain.XgStudentManager;
|
||||
import org.dromara.xg.domain.XgStudentManagerSource;
|
||||
import org.dromara.xg.domain.XgClassMsg;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
@@ -582,4 +583,37 @@ public class XgStudentLeaveServiceImpl implements IXgStudentLeaveService {
|
||||
baseMapper.updateById(xgStudentLeave);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学生ID列表和检查日期查询学生请假信息
|
||||
*
|
||||
* @param studentIds 学生ID列表
|
||||
* @param checkDate 检查日期
|
||||
* @return 学生ID -> 请假信息的映射
|
||||
*/
|
||||
@Override
|
||||
public Map<Long, XgStudentLeaveVo> queryStudentLeavesByCheckDate(List<Long> studentIds, LocalDateTime checkDate) {
|
||||
if (studentIds == null || studentIds.isEmpty() || checkDate == null) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<XgStudentLeave> lqw = Wrappers.lambdaQuery();
|
||||
lqw.in(XgStudentLeave::getStudentId, studentIds);
|
||||
// 查询审核状态为"waiting"(待审核)或"finish"(已完成)的请假记录
|
||||
lqw.in(XgStudentLeave::getApprovalStatus, "waiting", "finish");
|
||||
// 检查日期在请假时间段内
|
||||
lqw.le(XgStudentLeave::getStartTime, checkDate);
|
||||
lqw.ge(XgStudentLeave::getEndTime, checkDate);
|
||||
lqw.orderByDesc(XgStudentLeave::getCreateTime);
|
||||
|
||||
List<XgStudentLeave> leaveList = baseMapper.selectList(lqw);
|
||||
|
||||
// 转换为VO并按学生ID分组,每个学生只取最新的一条请假记录
|
||||
return leaveList.stream()
|
||||
.collect(Collectors.toMap(
|
||||
XgStudentLeave::getStudentId,
|
||||
leave -> MapstructUtils.convert(leave, XgStudentLeaveVo.class),
|
||||
(existing, replacement) -> existing // 保留第一条(最新的)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package org.dromara.xg.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.xg.service.IXgStudentStatisticsService;
|
||||
import org.dromara.xg.domain.bo.XgStudentStatisticsBo;
|
||||
import org.dromara.xg.domain.vo.XgStudentStatisticsVo;
|
||||
import org.dromara.xg.domain.vo.XgCascadeOptionVo;
|
||||
import org.dromara.xg.mapper.XgStudentStatisticsMapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 学生宿舍卫生检查、违纪记录统计Service业务层处理
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-14
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class XgStudentStatisticsServiceImpl implements IXgStudentStatisticsService {
|
||||
|
||||
private final XgStudentStatisticsMapper statisticsMapper;
|
||||
|
||||
@Override
|
||||
public XgStudentStatisticsVo getStatistics(XgStudentStatisticsBo bo) {
|
||||
XgStudentStatisticsVo result = new XgStudentStatisticsVo();
|
||||
|
||||
// 确定统计维度
|
||||
String dimension = determineDimension(bo);
|
||||
result.setDimension(dimension);
|
||||
|
||||
// 获取图表数据和明细数据
|
||||
switch (dimension) {
|
||||
case "college":
|
||||
result.setChartData(statisticsMapper.getCollegeStatistics(bo));
|
||||
result.setDetailData(statisticsMapper.getCollegeDetailStatistics(bo));
|
||||
break;
|
||||
case "major":
|
||||
result.setChartData(statisticsMapper.getMajorStatistics(bo));
|
||||
result.setDetailData(statisticsMapper.getMajorDetailStatistics(bo));
|
||||
break;
|
||||
case "class":
|
||||
result.setChartData(statisticsMapper.getClassStatistics(bo));
|
||||
result.setDetailData(statisticsMapper.getClassDetailStatistics(bo));
|
||||
break;
|
||||
case "classDetail":
|
||||
// 班级详情统计:图表数据和明细数据都只显示指定班级
|
||||
List<XgStudentStatisticsVo.StatisticsDetailData> detailData = statisticsMapper
|
||||
.getClassDetailStatistics(bo);
|
||||
result.setDetailData(detailData);
|
||||
// 将明细数据转换为图表数据格式
|
||||
result.setChartData(convertDetailToChartData(detailData));
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportStatistics(XgStudentStatisticsBo bo, HttpServletResponse response) {
|
||||
XgStudentStatisticsVo statistics = getStatistics(bo);
|
||||
ExcelUtil.exportExcel(statistics.getDetailData(), "学生统计数据", XgStudentStatisticsVo.StatisticsDetailData.class,
|
||||
response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<XgCascadeOptionVo> getColleges() {
|
||||
return statisticsMapper.getColleges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<XgCascadeOptionVo> getMajorsByCollegeId(Long collegeId) {
|
||||
return statisticsMapper.getMajorsByCollegeId(collegeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<XgCascadeOptionVo> getClassesByMajorId(Long majorId) {
|
||||
return statisticsMapper.getClassesByMajorId(majorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定统计维度
|
||||
*/
|
||||
private String determineDimension(XgStudentStatisticsBo bo) {
|
||||
if (bo.getClassId() != null) {
|
||||
return "classDetail";
|
||||
} else if (bo.getMajorId() != null) {
|
||||
return "class";
|
||||
} else if (bo.getCollegeId() != null) {
|
||||
return "major";
|
||||
} else {
|
||||
return "college";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将明细数据转换为图表数据格式
|
||||
*/
|
||||
private List<XgStudentStatisticsVo.StatisticsChartData> convertDetailToChartData(
|
||||
List<XgStudentStatisticsVo.StatisticsDetailData> detailData) {
|
||||
return detailData.stream().map(detail -> {
|
||||
XgStudentStatisticsVo.StatisticsChartData chartData = new XgStudentStatisticsVo.StatisticsChartData();
|
||||
chartData.setName(detail.getName());
|
||||
chartData.setDormDeductionCount(detail.getDormDeductionCount());
|
||||
chartData.setDormViolationCount(detail.getDormViolationCount());
|
||||
chartData.setDormViolationDeduction(detail.getDormViolationDeduction());
|
||||
chartData.setTeachingViolationCount(detail.getTeachingViolationCount());
|
||||
chartData.setTeachingViolationDeduction(detail.getTeachingViolationDeduction());
|
||||
return chartData;
|
||||
}).collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.xg.mapper.XgHygieneCheckViolationMapper">
|
||||
|
||||
<resultMap type="org.dromara.xg.domain.vo.XgHygieneCheckViolationVo" id="XgHygieneCheckViolationResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="hygieneCheckId" column="hygiene_check_id" />
|
||||
<result property="disciplineRecordId" column="discipline_record_id" />
|
||||
<result property="studentId" column="student_id" />
|
||||
<result property="studentNo" column="student_no" />
|
||||
<result property="studentName" column="student_name" />
|
||||
<result property="disciplineTypeId" column="discipline_type_id" />
|
||||
<result property="disciplineTypeName" column="discipline_type_name" />
|
||||
<result property="violationSource" column="violation_source" />
|
||||
<result property="status" column="status" />
|
||||
<!-- 违纪详情字段 -->
|
||||
<result property="disciplineDate" column="discipline_date" />
|
||||
<result property="disciplineReason" column="discipline_reason" />
|
||||
<result property="disciplineContent" column="discipline_content" />
|
||||
<result property="scoreDeduction" column="score_deduction" />
|
||||
<result property="attachmentOssIds" column="attachment_oss_ids" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectXgHygieneCheckViolationVo">
|
||||
SELECT
|
||||
hcv.id,
|
||||
hcv.hygiene_check_id,
|
||||
hcv.discipline_record_id,
|
||||
hcv.student_id,
|
||||
hcv.student_no,
|
||||
hcv.student_name,
|
||||
hcv.discipline_type_id,
|
||||
hcv.discipline_type_name,
|
||||
hcv.violation_source,
|
||||
hcv.status,
|
||||
hcv.create_by,
|
||||
hcv.create_time,
|
||||
hcv.update_by,
|
||||
hcv.update_time,
|
||||
-- 关联违纪详情
|
||||
dr.discipline_date,
|
||||
dr.discipline_reason,
|
||||
dr.discipline_content,
|
||||
dr.score_deduction,
|
||||
dr.attachment_oss_ids
|
||||
FROM xg_hygiene_check_violation hcv
|
||||
LEFT JOIN xskh_discipline_record dr ON hcv.discipline_record_id = dr.id AND dr.deleted = 0
|
||||
</sql>
|
||||
|
||||
<select id="selectVoList" parameterType="com.baomidou.mybatisplus.core.conditions.Wrapper" resultMap="XgHygieneCheckViolationResult">
|
||||
<include refid="selectXgHygieneCheckViolationVo"/>
|
||||
<where>
|
||||
hcv.deleted = 0
|
||||
${ew.customSqlSegment}
|
||||
</where>
|
||||
ORDER BY hcv.create_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectVoById" parameterType="Long" resultMap="XgHygieneCheckViolationResult">
|
||||
<include refid="selectXgHygieneCheckViolationVo"/>
|
||||
WHERE hcv.id = #{id} AND hcv.deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 根据卫生检查ID查询违纪记录(包含详情) -->
|
||||
<select id="selectViolationsByHygieneCheckId" parameterType="Long" resultMap="XgHygieneCheckViolationResult">
|
||||
<include refid="selectXgHygieneCheckViolationVo"/>
|
||||
WHERE hcv.hygiene_check_id = #{hygieneCheckId}
|
||||
AND hcv.deleted = 0
|
||||
ORDER BY hcv.create_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+504
@@ -0,0 +1,504 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.xg.mapper.XgStudentStatisticsMapper">
|
||||
|
||||
<!-- 按学院维度统计图表数据 -->
|
||||
<select id="getCollegeStatistics" resultType="org.dromara.xg.domain.vo.XgStudentStatisticsVo$StatisticsChartData">
|
||||
SELECT
|
||||
d.dept_name as name,
|
||||
COALESCE(dorm_stats.dormDeductionCount, 0) as dormDeductionCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationCount, 0) as dormViolationCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationDeduction, 0) as dormViolationDeduction,
|
||||
COALESCE(teaching_violation_stats.teachingViolationCount, 0) as teachingViolationCount,
|
||||
COALESCE(teaching_violation_stats.teachingViolationDeduction, 0) as teachingViolationDeduction
|
||||
FROM sys_dept d
|
||||
LEFT JOIN (
|
||||
-- 宿舍卫生检查扣分统计
|
||||
SELECT
|
||||
c.college_id,
|
||||
COUNT(h.id) as dormDeductionCount
|
||||
FROM xg_dorm_hygiene_check h
|
||||
INNER JOIN xg_class_msg c ON h.room_id IN (
|
||||
SELECT DISTINCT bs.room_id
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id
|
||||
WHERE s.class_id = c.id AND s.deleted = 0
|
||||
)
|
||||
WHERE h.deleted = 0
|
||||
AND h.check_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
AND h.total_score > 0
|
||||
GROUP BY c.college_id
|
||||
) dorm_stats ON d.dept_id = dorm_stats.college_id
|
||||
LEFT JOIN (
|
||||
-- 宿舍区违纪记录统计
|
||||
SELECT
|
||||
dr.college_id,
|
||||
COUNT(dr.id) as dormViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as dormViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.college_id
|
||||
) dorm_violation_stats ON d.dept_id = dorm_violation_stats.college_id
|
||||
LEFT JOIN (
|
||||
-- 教学区违纪记录统计
|
||||
SELECT
|
||||
dr.college_id,
|
||||
COUNT(dr.id) as teachingViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as teachingViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.college_id
|
||||
) teaching_violation_stats ON d.dept_id = teaching_violation_stats.college_id
|
||||
WHERE d.parent_id = 101 AND d.del_flag = '0'
|
||||
ORDER BY d.order_num ASC, d.dept_name ASC
|
||||
</select>
|
||||
|
||||
<!-- 按学院维度统计明细数据 -->
|
||||
<select id="getCollegeDetailStatistics" resultType="org.dromara.xg.domain.vo.XgStudentStatisticsVo$StatisticsDetailData">
|
||||
SELECT
|
||||
d.dept_id as id,
|
||||
d.dept_name as name,
|
||||
'' as parentName,
|
||||
COALESCE(dorm_stats.dormDeductionCount, 0) as dormDeductionCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationCount, 0) as dormViolationCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationDeduction, 0) as dormViolationDeduction,
|
||||
COALESCE(teaching_violation_stats.teachingViolationCount, 0) as teachingViolationCount,
|
||||
COALESCE(teaching_violation_stats.teachingViolationDeduction, 0) as teachingViolationDeduction,
|
||||
COALESCE(dorm_violation_stats.dormViolationDeduction, 0) + COALESCE(teaching_violation_stats.teachingViolationDeduction, 0) as totalDeduction
|
||||
FROM sys_dept d
|
||||
LEFT JOIN (
|
||||
-- 宿舍卫生检查扣分统计
|
||||
SELECT
|
||||
c.college_id,
|
||||
COUNT(h.id) as dormDeductionCount
|
||||
FROM xg_dorm_hygiene_check h
|
||||
INNER JOIN xg_class_msg c ON h.room_id IN (
|
||||
SELECT DISTINCT bs.room_id
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id
|
||||
WHERE s.class_id = c.id AND s.deleted = 0
|
||||
)
|
||||
WHERE h.deleted = 0
|
||||
AND h.check_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
AND h.total_score > 0
|
||||
GROUP BY c.college_id
|
||||
) dorm_stats ON d.dept_id = dorm_stats.college_id
|
||||
LEFT JOIN (
|
||||
-- 宿舍区违纪记录统计
|
||||
SELECT
|
||||
dr.college_id,
|
||||
COUNT(dr.id) as dormViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as dormViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.college_id
|
||||
) dorm_violation_stats ON d.dept_id = dorm_violation_stats.college_id
|
||||
LEFT JOIN (
|
||||
-- 教学区违纪记录统计
|
||||
SELECT
|
||||
dr.college_id,
|
||||
COUNT(dr.id) as teachingViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as teachingViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.college_id
|
||||
) teaching_violation_stats ON d.dept_id = teaching_violation_stats.college_id
|
||||
WHERE d.parent_id = 101 AND d.del_flag = '0'
|
||||
ORDER BY d.order_num ASC, d.dept_name ASC
|
||||
</select>
|
||||
|
||||
<!-- 按专业维度统计图表数据 -->
|
||||
<select id="getMajorStatistics" resultType="org.dromara.xg.domain.vo.XgStudentStatisticsVo$StatisticsChartData">
|
||||
SELECT
|
||||
d.dept_name as name,
|
||||
COALESCE(dorm_stats.dormDeductionCount, 0) as dormDeductionCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationCount, 0) as dormViolationCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationDeduction, 0) as dormViolationDeduction,
|
||||
COALESCE(teaching_violation_stats.teachingViolationCount, 0) as teachingViolationCount,
|
||||
COALESCE(teaching_violation_stats.teachingViolationDeduction, 0) as teachingViolationDeduction
|
||||
FROM sys_dept d
|
||||
LEFT JOIN (
|
||||
-- 宿舍卫生检查扣分统计
|
||||
SELECT
|
||||
c.major_id,
|
||||
COUNT(h.id) as dormDeductionCount
|
||||
FROM xg_dorm_hygiene_check h
|
||||
INNER JOIN xg_class_msg c ON h.room_id IN (
|
||||
SELECT DISTINCT bs.room_id
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id
|
||||
WHERE s.class_id = c.id AND s.deleted = 0
|
||||
)
|
||||
WHERE h.deleted = 0
|
||||
AND h.check_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
AND h.total_score > 0
|
||||
AND c.college_id = #{bo.collegeId}
|
||||
GROUP BY c.major_id
|
||||
) dorm_stats ON d.dept_id = dorm_stats.major_id
|
||||
LEFT JOIN (
|
||||
-- 宿舍区违纪记录统计
|
||||
SELECT
|
||||
dr.major_id,
|
||||
COUNT(dr.id) as dormViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as dormViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.major_id
|
||||
) dorm_violation_stats ON d.dept_id = dorm_violation_stats.major_id
|
||||
LEFT JOIN (
|
||||
-- 教学区违纪记录统计
|
||||
SELECT
|
||||
dr.major_id,
|
||||
COUNT(dr.id) as teachingViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as teachingViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.major_id
|
||||
) teaching_violation_stats ON d.dept_id = teaching_violation_stats.major_id
|
||||
WHERE d.parent_id = #{bo.collegeId} AND d.dept_category LIKE 'zy-%' AND d.del_flag = '0'
|
||||
ORDER BY d.order_num ASC, d.dept_name ASC
|
||||
</select>
|
||||
|
||||
<!-- 按专业维度统计明细数据 -->
|
||||
<select id="getMajorDetailStatistics" resultType="org.dromara.xg.domain.vo.XgStudentStatisticsVo$StatisticsDetailData">
|
||||
SELECT
|
||||
d.dept_id as id,
|
||||
d.dept_name as name,
|
||||
p.dept_name as parentName,
|
||||
COALESCE(dorm_stats.dormDeductionCount, 0) as dormDeductionCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationCount, 0) as dormViolationCount,
|
||||
COALESCE(dorm_violation_stats.dormViolationDeduction, 0) as dormViolationDeduction,
|
||||
COALESCE(teaching_violation_stats.teachingViolationCount, 0) as teachingViolationCount,
|
||||
COALESCE(teaching_violation_stats.teachingViolationDeduction, 0) as teachingViolationDeduction,
|
||||
COALESCE(dorm_violation_stats.dormViolationDeduction, 0) + COALESCE(teaching_violation_stats.teachingViolationDeduction, 0) as totalDeduction
|
||||
FROM sys_dept d
|
||||
INNER JOIN sys_dept p ON d.parent_id = p.dept_id
|
||||
LEFT JOIN (
|
||||
-- 宿舍卫生检查扣分统计
|
||||
SELECT
|
||||
c.major_id,
|
||||
COUNT(h.id) as dormDeductionCount
|
||||
FROM xg_dorm_hygiene_check h
|
||||
INNER JOIN xg_class_msg c ON h.room_id IN (
|
||||
SELECT DISTINCT bs.room_id
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id
|
||||
WHERE s.class_id = c.id AND s.deleted = 0
|
||||
)
|
||||
WHERE h.deleted = 0
|
||||
AND h.check_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
AND h.total_score > 0
|
||||
AND c.college_id = #{bo.collegeId}
|
||||
GROUP BY c.major_id
|
||||
) dorm_stats ON d.dept_id = dorm_stats.major_id
|
||||
LEFT JOIN (
|
||||
-- 宿舍区违纪记录统计
|
||||
SELECT
|
||||
dr.major_id,
|
||||
COUNT(dr.id) as dormViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as dormViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.major_id
|
||||
) dorm_violation_stats ON d.dept_id = dorm_violation_stats.major_id
|
||||
LEFT JOIN (
|
||||
-- 教学区违纪记录统计
|
||||
SELECT
|
||||
dr.major_id,
|
||||
COUNT(dr.id) as teachingViolationCount,
|
||||
COALESCE(SUM(dr.score_deduction), 0) as teachingViolationDeduction
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
GROUP BY dr.major_id
|
||||
) teaching_violation_stats ON d.dept_id = teaching_violation_stats.major_id
|
||||
WHERE d.parent_id = #{bo.collegeId} AND d.dept_category LIKE 'zy-%' AND d.del_flag = '0'
|
||||
ORDER BY d.order_num ASC, d.dept_name ASC
|
||||
</select>
|
||||
|
||||
<!-- 按班级维度统计图表数据 -->
|
||||
<select id="getClassStatistics" resultType="org.dromara.xg.domain.vo.XgStudentStatisticsVo$StatisticsChartData">
|
||||
SELECT
|
||||
c.class_name as name,
|
||||
(SELECT COUNT(h.id)
|
||||
FROM xg_dorm_hygiene_check h
|
||||
WHERE h.deleted = 0
|
||||
AND h.check_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
AND h.total_score > 0
|
||||
AND h.room_id IN (
|
||||
SELECT DISTINCT bs.room_id
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id
|
||||
WHERE s.class_id = c.id AND s.deleted = 0
|
||||
)
|
||||
) as dormDeductionCount,
|
||||
(SELECT COUNT(dr.id)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as dormViolationCount,
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as dormViolationDeduction,
|
||||
(SELECT COUNT(dr.id)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as teachingViolationCount,
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as teachingViolationDeduction
|
||||
FROM xg_class_msg c
|
||||
WHERE c.college_id = #{bo.collegeId} AND c.major_id = #{bo.majorId} AND c.deleted = 0
|
||||
ORDER BY c.class_name ASC
|
||||
</select>
|
||||
|
||||
<!-- 按班级维度统计明细数据 -->
|
||||
<select id="getClassDetailStatistics" resultType="org.dromara.xg.domain.vo.XgStudentStatisticsVo$StatisticsDetailData">
|
||||
<choose>
|
||||
<when test="bo.classId != null">
|
||||
<!-- 班级详情统计 -->
|
||||
SELECT
|
||||
c.id as id,
|
||||
c.class_name as name,
|
||||
CONCAT(c.college_name, '-', c.major_name) as parentName,
|
||||
(SELECT COUNT(h.id)
|
||||
FROM xg_dorm_hygiene_check h
|
||||
WHERE h.deleted = 0
|
||||
AND h.check_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
AND h.total_score > 0
|
||||
AND h.room_id IN (
|
||||
SELECT DISTINCT bs.room_id
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id
|
||||
WHERE s.class_id = c.id AND s.deleted = 0
|
||||
)
|
||||
) as dormDeductionCount,
|
||||
(SELECT COUNT(dr.id)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = #{bo.classId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as dormViolationCount,
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = #{bo.classId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as dormViolationDeduction,
|
||||
(SELECT COUNT(dr.id)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = #{bo.classId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as teachingViolationCount,
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = #{bo.classId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as teachingViolationDeduction,
|
||||
((SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = #{bo.classId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) +
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = #{bo.classId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
)) as totalDeduction
|
||||
FROM xg_class_msg c
|
||||
WHERE c.id = #{bo.classId} AND c.deleted = 0
|
||||
</when>
|
||||
<otherwise>
|
||||
<!-- 班级列表统计 -->
|
||||
SELECT
|
||||
c.id as id,
|
||||
c.class_name as name,
|
||||
c.major_name as parentName,
|
||||
(SELECT COUNT(h.id)
|
||||
FROM xg_dorm_hygiene_check h
|
||||
WHERE h.deleted = 0
|
||||
AND h.check_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
AND h.total_score > 0
|
||||
AND h.room_id IN (
|
||||
SELECT DISTINCT bs.room_id
|
||||
FROM xg_dorm_bed_student bs
|
||||
INNER JOIN xg_student_msg s ON bs.student_id = s.id
|
||||
WHERE s.class_id = c.id AND s.deleted = 0
|
||||
)
|
||||
) as dormDeductionCount,
|
||||
(SELECT COUNT(dr.id)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as dormViolationCount,
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as dormViolationDeduction,
|
||||
(SELECT COUNT(dr.id)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as teachingViolationCount,
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) as teachingViolationDeduction,
|
||||
((SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '1'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
) +
|
||||
(SELECT COALESCE(SUM(dr.score_deduction), 0)
|
||||
FROM xskh_discipline_record dr
|
||||
WHERE dr.deleted = 0
|
||||
AND dr.violation_source = '2'
|
||||
AND dr.status = '1'
|
||||
AND dr.class_id = c.id
|
||||
AND dr.college_id = #{bo.collegeId}
|
||||
AND dr.major_id = #{bo.majorId}
|
||||
AND dr.discipline_date BETWEEN #{bo.startTime} AND #{bo.endTime}
|
||||
)) as totalDeduction
|
||||
FROM xg_class_msg c
|
||||
WHERE c.college_id = #{bo.collegeId} AND c.major_id = #{bo.majorId} AND c.deleted = 0
|
||||
ORDER BY c.class_name ASC
|
||||
</otherwise>
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
<!-- 获取二级学院列表 -->
|
||||
<select id="getColleges" resultType="org.dromara.xg.domain.vo.XgCascadeOptionVo">
|
||||
SELECT
|
||||
dept_id as id,
|
||||
dept_name as label,
|
||||
CAST(dept_id AS CHAR) as value,
|
||||
order_num as sort
|
||||
FROM sys_dept
|
||||
WHERE parent_id = 101 AND del_flag = '0'
|
||||
ORDER BY order_num ASC, dept_name ASC
|
||||
</select>
|
||||
|
||||
<!-- 根据学院ID获取专业列表 -->
|
||||
<select id="getMajorsByCollegeId" resultType="org.dromara.xg.domain.vo.XgCascadeOptionVo">
|
||||
SELECT
|
||||
dept_id as id,
|
||||
dept_name as label,
|
||||
CAST(dept_id AS CHAR) as value,
|
||||
order_num as sort
|
||||
FROM sys_dept
|
||||
WHERE parent_id = #{collegeId}
|
||||
AND dept_category LIKE 'zy-%'
|
||||
AND del_flag = '0'
|
||||
ORDER BY order_num ASC, dept_name ASC
|
||||
</select>
|
||||
|
||||
<!-- 根据专业ID获取班级列表 -->
|
||||
<select id="getClassesByMajorId" resultType="org.dromara.xg.domain.vo.XgCascadeOptionVo">
|
||||
SELECT
|
||||
id,
|
||||
class_name as label,
|
||||
CAST(id AS CHAR) as value,
|
||||
0 as sort
|
||||
FROM xg_class_msg
|
||||
WHERE major_id = #{majorId} AND deleted = 0
|
||||
ORDER BY class_name ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+44
-14
@@ -134,17 +134,6 @@ public class XskhDisciplineRevokeController extends BaseController {
|
||||
return toAjax(xskhDisciplineRevokeService.applyRevoke(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核撤销申请
|
||||
*/
|
||||
@SaCheckPermission("disciplineRevoke:disciplineRevoke:approve")
|
||||
@Log(title = "审核撤销申请", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/approve")
|
||||
public R<Void> approveRevoke(@Validated(EditGroup.class) @RequestBody XskhDisciplineRevokeBo bo) {
|
||||
return toAjax(xskhDisciplineRevokeService.approveRevoke(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询班主任班级的违纪撤销申请列表(班主任专用)
|
||||
*/
|
||||
@@ -155,15 +144,56 @@ public class XskhDisciplineRevokeController extends BaseController {
|
||||
return xskhDisciplineRevokeService.queryClassTeacherPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询班主任已审核的违纪撤销申请列表(根据审核人查询历史记录)
|
||||
*/
|
||||
@SaCheckPermission("disciplineRevoke:classTeacher:list")
|
||||
@GetMapping("/classTeacherReviewedList")
|
||||
public TableDataInfo<XskhDisciplineRevokeVo> getClassTeacherReviewedRevokeApplications(XskhDisciplineRevokeBo bo,
|
||||
PageQuery pageQuery) {
|
||||
return xskhDisciplineRevokeService.queryClassTeacherReviewedPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 班主任审核撤销申请
|
||||
*/
|
||||
@SaCheckPermission("disciplineRevoke:classTeacher:approve")
|
||||
@Log(title = "班主任审核撤销申请", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/classTeacherApprove")
|
||||
public R<Void> classTeacherApproveRevoke(@Validated(EditGroup.class) @RequestBody XskhDisciplineRevokeBo bo) {
|
||||
return toAjax(xskhDisciplineRevokeService.classTeacherApproveRevoke(bo));
|
||||
@PutMapping("/teacherApprove")
|
||||
public R<Void> teacherApproveRevoke(@Validated(EditGroup.class) @RequestBody XskhDisciplineRevokeBo bo) {
|
||||
return toAjax(xskhDisciplineRevokeService.teacherApproveRevoke(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询学工处待审核的撤销申请列表(学工处专用)
|
||||
*/
|
||||
@SaCheckPermission("disciplineRevoke:studentAffairs:list")
|
||||
@GetMapping("/studentAffairsList")
|
||||
public TableDataInfo<XskhDisciplineRevokeVo> getStudentAffairsRevokeApplications(XskhDisciplineRevokeBo bo,
|
||||
PageQuery pageQuery) {
|
||||
return xskhDisciplineRevokeService.queryStudentAffairsPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 学工处审核撤销申请
|
||||
*/
|
||||
@SaCheckPermission("disciplineRevoke:studentAffairs:approve")
|
||||
@Log(title = "学工处审核撤销申请", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/studentAffairsApprove")
|
||||
public R<Void> studentAffairsApproveRevoke(@Validated(EditGroup.class) @RequestBody XskhDisciplineRevokeBo bo) {
|
||||
return toAjax(xskhDisciplineRevokeService.studentAffairsApproveRevoke(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询学工处已审核的撤销申请列表(学工处专用)
|
||||
*/
|
||||
@SaCheckPermission("disciplineRevoke:studentAffairs:list")
|
||||
@GetMapping("/studentAffairsReviewedList")
|
||||
public TableDataInfo<XskhDisciplineRevokeVo> getStudentAffairsReviewedRevokeApplications(XskhDisciplineRevokeBo bo,
|
||||
PageQuery pageQuery) {
|
||||
return xskhDisciplineRevokeService.queryStudentAffairsReviewedPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
}
|
||||
+22
-4
@@ -63,8 +63,7 @@ public class XskhDisciplineTypeController extends BaseController {
|
||||
*/
|
||||
@SaCheckPermission("disciplineType:disciplineType:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<XskhDisciplineTypeVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
public R<XskhDisciplineTypeVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(xskhDisciplineTypeService.queryById(id));
|
||||
}
|
||||
|
||||
@@ -98,8 +97,27 @@ public class XskhDisciplineTypeController extends BaseController {
|
||||
@SaCheckPermission("disciplineType:disciplineType:remove")
|
||||
@Log(title = "学生违纪处分类型", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||||
return toAjax(xskhDisciplineTypeService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据区域类型查询学生违纪处分类型列表
|
||||
*
|
||||
* @param areaType 区域类型(1-宿舍区违纪 2-教学区违纪)
|
||||
* @param status 状态(0-正常 1-停用)
|
||||
* @return 学生违纪处分类型列表
|
||||
*/
|
||||
@SaCheckPermission("disciplineType:disciplineType:list")
|
||||
@GetMapping("/listByAreaType")
|
||||
public R<List<XskhDisciplineTypeVo>> listByAreaType(@RequestParam String areaType,
|
||||
@RequestParam(required = false) String status) {
|
||||
XskhDisciplineTypeBo bo = new XskhDisciplineTypeBo();
|
||||
bo.setAreaType(areaType);
|
||||
if (status != null) {
|
||||
bo.setStatus(status);
|
||||
}
|
||||
List<XskhDisciplineTypeVo> list = xskhDisciplineTypeService.queryList(bo);
|
||||
return R.ok(list);
|
||||
}
|
||||
}
|
||||
+5
@@ -110,6 +110,11 @@ public class XskhDisciplineRecord extends TenantEntity {
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 违纪来源(1-宿舍卫生检查 2-教学区违纪)
|
||||
*/
|
||||
private String violationSource;
|
||||
|
||||
/**
|
||||
* 附件OSS ID列表,逗号分隔
|
||||
*/
|
||||
|
||||
+19
-4
@@ -59,22 +59,37 @@ public class XskhDisciplineRevoke extends TenantEntity {
|
||||
private Date applyTime;
|
||||
|
||||
/**
|
||||
* 状态(1-待审核 2-审核通过 3-审核拒绝)
|
||||
* 状态(1-待班主任审核 2-待学工处审核 3-审核通过 4-班主任审核拒绝 5-学工处审核拒绝)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 审核人ID
|
||||
* 班主任审核人ID
|
||||
*/
|
||||
private Long teacherApprovalBy;
|
||||
|
||||
/**
|
||||
* 班主任审核时间
|
||||
*/
|
||||
private Date teacherApprovalTime;
|
||||
|
||||
/**
|
||||
* 班主任审核备注
|
||||
*/
|
||||
private String teacherApprovalRemark;
|
||||
|
||||
/**
|
||||
* 学工处审核人ID
|
||||
*/
|
||||
private Long approvalBy;
|
||||
|
||||
/**
|
||||
* 审核时间
|
||||
* 学工处审核时间
|
||||
*/
|
||||
private Date approvalTime;
|
||||
|
||||
/**
|
||||
* 审核备注
|
||||
* 学工处审核备注
|
||||
*/
|
||||
private String approvalRemark;
|
||||
|
||||
|
||||
+25
-1
@@ -42,6 +42,31 @@ public class XskhDisciplineType extends TenantEntity {
|
||||
*/
|
||||
private String typeCategory;
|
||||
|
||||
/**
|
||||
* 区域类型(1-宿舍区违纪 2-教学区违纪)
|
||||
*/
|
||||
private String areaType;
|
||||
|
||||
/**
|
||||
* 最低扣分
|
||||
*/
|
||||
private java.math.BigDecimal minScore;
|
||||
|
||||
/**
|
||||
* 最高扣分
|
||||
*/
|
||||
private java.math.BigDecimal maxScore;
|
||||
|
||||
/**
|
||||
* 是否均化(0-否 1-是)
|
||||
*/
|
||||
private String isEqualized;
|
||||
|
||||
/**
|
||||
* 均化阈值
|
||||
*/
|
||||
private java.math.BigDecimal equalizedThreshold;
|
||||
|
||||
/**
|
||||
* 类型说明
|
||||
*/
|
||||
@@ -63,5 +88,4 @@ public class XskhDisciplineType extends TenantEntity {
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private Long deleted;
|
||||
|
||||
|
||||
}
|
||||
Loaded 100 of 263 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user