心理健康2.0

This commit is contained in:
李旭明 committed 2025-09-16 10:34:38 +08:00
1 parent 067957256d
commit 3b492479b5
63 files changed
+10278 -642

No files matched your search

+6
View File
@@ -93,6 +93,12 @@
<artifactId>ruoyi-xg</artifactId>
</dependency>
<!-- 系统模块 - 用于访问用户信息 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,113 @@
package org.dromara.stgl.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.stgl.domain.StglAnnualReviewAudit;
import org.dromara.stgl.domain.bo.StglAnnualReviewApprovalBo;
import org.dromara.stgl.service.IStglAnnualReviewAuditService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* 社团年审审批
*
* @author ruoyi
* @date 2024-01-01
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/stgl/annualReviewAudit")
public class StglAnnualReviewAuditController extends BaseController {
private final IStglAnnualReviewAuditService stglAnnualReviewAuditService;
/**
* 查询社团年审审批详细
*/
@SaCheckPermission("stgl:annualReviewAudit:query")
@GetMapping("/{id}")
public R<StglAnnualReviewAudit> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(stglAnnualReviewAuditService.queryById(id));
}
/**
* 根据年审记录ID查询审批记录
*/
@SaCheckPermission("stgl:annualReviewAudit:query")
@GetMapping("/byAnnualReview/{annualReviewId}")
public R<List<StglAnnualReviewAudit>> getByAnnualReviewId(@NotNull(message = "年审记录ID不能为空")
@PathVariable Long annualReviewId) {
return R.ok(stglAnnualReviewAuditService.queryByAnnualReviewId(annualReviewId));
}
/**
* 根据审批人ID查询待审批记录
*/
@SaCheckPermission("stgl:annualReviewAudit:query")
@GetMapping("/pending/{approverId}")
public R<List<StglAnnualReviewAudit>> getPendingByApproverId(@NotNull(message = "审批人ID不能为空")
@PathVariable Long approverId) {
return R.ok(stglAnnualReviewAuditService.queryPendingByApproverId(approverId));
}
/**
* 新增社团年审审批
*/
@SaCheckPermission("stgl:annualReviewAudit:add")
@Log(title = "社团年审审批", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody StglAnnualReviewAudit stglAnnualReviewAudit) {
return toAjax(stglAnnualReviewAuditService.insertByBo(stglAnnualReviewAudit));
}
/**
* 修改社团年审审批
*/
@SaCheckPermission("stgl:annualReviewAudit:edit")
@Log(title = "社团年审审批", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody StglAnnualReviewAudit stglAnnualReviewAudit) {
return toAjax(stglAnnualReviewAuditService.updateByBo(stglAnnualReviewAudit));
}
/**
* 删除社团年审审批
*/
@SaCheckPermission("stgl:annualReviewAudit:remove")
@Log(title = "社团年审审批", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(stglAnnualReviewAuditService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 审批年审记录
*/
@SaCheckPermission("stgl:annualReviewAudit:approve")
@Log(title = "社团年审审批", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/approve")
public R<Void> approve(@Validated @RequestBody StglAnnualReviewApprovalBo approvalBo) {
return toAjax(stglAnnualReviewAuditService.approveAnnualReview(approvalBo));
}
}
@@ -0,0 +1,179 @@
package org.dromara.stgl.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.stgl.domain.vo.StglAuditorVo;
import org.dromara.stgl.domain.bo.StglAuditorBo;
import org.dromara.stgl.service.IStglAuditorService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.domain.vo.SysUserVo;
/**
* 社团管理审核人配置
*
* @author ruoyi
* @date 2024-01-01
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/stgl/auditor")
public class StglAuditorController extends BaseController {
private final IStglAuditorService stglAuditorService;
private final ISysUserService sysUserService;
/**
* 查询社团管理审核人配置列表
*/
@SaCheckPermission("stgl:auditor:list")
@GetMapping("/list")
public TableDataInfo<StglAuditorVo> list(StglAuditorBo bo, PageQuery pageQuery) {
return stglAuditorService.queryPageList(bo, pageQuery);
}
/**
* 导出社团管理审核人配置列表
*/
@SaCheckPermission("stgl:auditor:export")
@Log(title = "社团管理审核人配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(StglAuditorBo bo, HttpServletResponse response) {
List<StglAuditorVo> list = stglAuditorService.queryList(bo);
ExcelUtil.exportExcel(list, "社团管理审核人配置", StglAuditorVo.class, response);
}
/**
* 获取社团管理审核人配置详细信息
*/
@SaCheckPermission("stgl:auditor:query")
@GetMapping("/{id}")
public R<StglAuditorVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(stglAuditorService.queryById(id));
}
/**
* 新增社团管理审核人配置
*/
@SaCheckPermission("stgl:auditor:add")
@Log(title = "社团管理审核人配置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody StglAuditorBo bo) {
return toAjax(stglAuditorService.insertByBo(bo));
}
/**
* 修改社团管理审核人配置
*/
@SaCheckPermission("stgl:auditor:edit")
@Log(title = "社团管理审核人配置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody StglAuditorBo bo) {
return toAjax(stglAuditorService.updateByBo(bo));
}
/**
* 删除社团管理审核人配置
*/
@SaCheckPermission("stgl:auditor:remove")
@Log(title = "社团管理审核人配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(stglAuditorService.deleteWithValidByIds(List.of(ids)));
}
/**
* 批量添加审核人
*/
@SaCheckPermission("stgl:auditor:add")
@Log(title = "批量添加审核人", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/batchAdd")
public R<Void> batchAdd(@RequestBody BatchAddRequest request) {
return toAjax(stglAuditorService.batchAddAuditors(request.getUserIds(), request.getRemark()));
}
/**
* 查询我配置的审核人列表
*/
@SaCheckPermission("stgl:auditor:list")
@GetMapping("/myConfig")
public TableDataInfo<StglAuditorVo> myConfigList(StglAuditorBo bo, PageQuery pageQuery) {
return stglAuditorService.queryMyConfigList(bo, pageQuery);
}
/**
* 批量启用审核人
*/
@SaCheckPermission("stgl:auditor:edit")
@Log(title = "启用审核人", businessType = BusinessType.UPDATE)
@PostMapping("/enable")
public R<Void> enable(@RequestBody List<Long> ids) {
return toAjax(stglAuditorService.enableAuditors(ids));
}
/**
* 批量停用审核人
*/
@SaCheckPermission("stgl:auditor:edit")
@Log(title = "停用审核人", businessType = BusinessType.UPDATE)
@PostMapping("/disable")
public R<Void> disable(@RequestBody List<Long> ids) {
return toAjax(stglAuditorService.disableAuditors(ids));
}
/**
* 根据部门ID获取用户列表
*/
@SaCheckPermission("stgl:auditor:list")
@GetMapping("/users/{deptId}")
public R<List<SysUserVo>> listUserByDeptId(@PathVariable String deptId) {
List<SysUserVo> users = sysUserService.selectUserListByDept(Long.valueOf(deptId));
return R.ok(users);
}
/**
* 批量添加请求对象
*/
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;
}
}
}
@@ -0,0 +1,90 @@
package org.dromara.stgl.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
import java.util.Date;
/**
* 社团年审审批对象 stgl_annual_review_audit
*
* @author ruoyi
* @date 2024-01-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("stgl_annual_review_audit")
public class StglAnnualReviewAudit extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 删除标志(0代表存在 1代表删除)
*/
@TableLogic
private Integer deleted;
/**
* 年审记录ID
*/
private Long annualReviewId;
/**
* 节点代码(advisor_approve/college_approve/youth_league_approve)
*/
private String nodeCode;
/**
* 节点名称(指导老师审批/学院审批/校团委审批)
*/
private String nodeName;
/**
* 节点顺序(1.1,1.2,2,3)
*/
private BigDecimal nodeOrder;
/**
* 节点状态(pending/waiting/approved)
*/
@TableField("audit_status")
private String nodeStatus;
/**
* 审批人ID
*/
private Long approverId;
/**
* 审批人姓名
*/
private String approverName;
/**
* 审批结果(通过/不通过)
*/
private String approveResult;
/**
* 审批意见
*/
@TableField("audit_opinion")
private String approveOpinion;
/**
* 审批时间
*/
@TableField("audit_time")
private Date approveTime;
}
@@ -0,0 +1,86 @@
package org.dromara.stgl.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;
/**
* 社团管理审核人配置对象 stgl_auditor
*
* @author ruoyi
* @date 2024-01-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("stgl_auditor")
public class StglAuditor extends TenantEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键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,0 +1,39 @@
package org.dromara.stgl.domain;
import lombok.Data;
/**
* 社团管理用户和部门完整信息
* 用于批量添加审核人时获取完整的用户数据
*
* @author ruoyi
* @date 2025-01-03
*/
@Data
public class StglUserWithDeptInfo {
/** 用户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;
}
@@ -0,0 +1,60 @@
package org.dromara.stgl.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
/**
* 社团年审审批请求业务对象
*
* @author system
* @date 2025-01-03
*/
@Data
public class StglAnnualReviewApprovalBo {
/**
* 审批记录ID
*/
@NotNull(message = "审批记录ID不能为空")
private Long auditId;
/**
* 审批结果
*/
@NotBlank(message = "审批结果不能为空")
private String approveResult;
/**
* 审批意见
*/
private String approveOpinion;
/**
* 下一步审核人列表
*/
private List<NextAuditor> nextAuditors;
/**
* 下一步审核人信息
*/
@Data
public static class NextAuditor {
/**
* 用户ID
*/
private Long userId;
/**
* 用户名
*/
private String userName;
/**
* 昵称
*/
private String nickName;
}
}
@@ -0,0 +1,113 @@
package org.dromara.stgl.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.stgl.domain.StglAuditor;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
/**
* 社团管理审核人配置业务对象 stgl_auditor
*
* @author ruoyi
* @date 2024-01-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = StglAuditor.class, reverseConvertGenerate = false)
public class StglAuditorBo extends BaseEntity {
/**
* 主键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;
/**
* 用户昵称
*/
@NotBlank(message = "用户昵称不能为空", groups = { AddGroup.class, EditGroup.class })
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;
/**
* 分页大小
*/
private Integer pageSize;
/**
* 页码
*/
private Integer pageNum;
/**
* 搜索值
*/
private String searchValue;
/**
* 创建时间范围
*/
private List<Date> createTimeRange;
}
@@ -0,0 +1,133 @@
package org.dromara.stgl.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.stgl.domain.StglAuditor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 社团管理审核人配置视图对象 stgl_auditor
*
* @author ruoyi
* @date 2024-01-01
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = StglAuditor.class)
public class StglAuditorVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键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
*/
private Long configBy;
/**
* 配置人姓名
*/
@ExcelProperty(value = "配置人")
private String configByName;
/**
* 租户编号
*/
private String tenantId;
/**
* 创建部门
*/
private Long createDept;
/**
* 创建者ID
*/
private Long createBy;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新者ID
*/
private Long updateBy;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,40 @@
package org.dromara.stgl.mapper;
import org.dromara.stgl.domain.StglAnnualReviewAudit;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.List;
/**
* 社团年审审批Mapper接口
*
* @author ruoyi
* @date 2024-01-01
*/
public interface StglAnnualReviewAuditMapper extends BaseMapperPlus<StglAnnualReviewAudit, StglAnnualReviewAudit> {
/**
* 根据年审记录ID查询审批记录
*
* @param annualReviewId 年审记录ID
* @return 审批记录列表
*/
List<StglAnnualReviewAudit> selectByAnnualReviewId(Long annualReviewId);
/**
* 根据年审记录ID和节点代码查询审批记录
*
* @param annualReviewId 年审记录ID
* @param nodeCode 节点代码
* @return 审批记录列表
*/
List<StglAnnualReviewAudit> selectByAnnualReviewIdAndNodeCode(Long annualReviewId, String nodeCode);
/**
* 根据审批人ID查询待审批记录
*
* @param approverId 审批人ID
* @return 审批记录列表
*/
List<StglAnnualReviewAudit> selectPendingByApproverId(Long approverId);
}
@@ -0,0 +1,66 @@
package org.dromara.stgl.mapper;
import org.dromara.stgl.domain.StglAuditor;
import org.dromara.stgl.domain.vo.StglAuditorVo;
import org.dromara.stgl.domain.StglUserWithDeptInfo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 社团管理审核人配置Mapper接口
*
* @author ruoyi
* @date 2024-01-01
*/
public interface StglAuditorMapper extends BaseMapperPlus<StglAuditor, StglAuditorVo> {
/**
* 查询社团管理审核人配置
*
* @param id 社团管理审核人配置主键
* @return 社团管理审核人配置
*/
StglAuditorVo selectStglAuditorVoById(Long id);
/**
* 查询社团管理审核人配置列表
*
* @param stglAuditor 社团管理审核人配置
* @return 社团管理审核人配置集合
*/
List<StglAuditorVo> selectStglAuditorVoList(StglAuditor stglAuditor);
/**
* 根据用户ID检查是否已存在审核人配置
*
* @param userId 用户ID
* @return 记录数
*/
int checkUserExists(@Param("userId") Long userId);
/**
* 批量启用审核人
*
* @param ids 审核人ID列表
* @return 影响行数
*/
int enableAuditorsByIds(@Param("ids") List<Long> ids);
/**
* 批量停用审核人
*
* @param ids 审核人ID列表
* @return 影响行数
*/
int disableAuditorsByIds(@Param("ids") List<Long> ids);
/**
* 查询完整的用户信息(包含部门信息)用于批量添加
*
* @param userIds 用户ID列表
* @return 用户和部门完整信息列表
*/
List<StglUserWithDeptInfo> selectUsersWithDeptInfo(@Param("userIds") List<Long> userIds);
}
@@ -0,0 +1,104 @@
package org.dromara.stgl.service;
import org.dromara.stgl.domain.StglAnnualReviewAudit;
import org.dromara.stgl.domain.bo.StglAnnualReviewApprovalBo;
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 2024-01-01
*/
public interface IStglAnnualReviewAuditService {
/**
* 查询社团年审审批
*
* @param id 社团年审审批主键
* @return 社团年审审批
*/
StglAnnualReviewAudit queryById(Long id);
/**
* 根据年审记录ID查询审批记录
*
* @param annualReviewId 年审记录ID
* @return 审批记录列表
*/
List<StglAnnualReviewAudit> queryByAnnualReviewId(Long annualReviewId);
/**
* 根据年审记录ID和节点代码查询审批记录
*
* @param annualReviewId 年审记录ID
* @param nodeCode 节点代码
* @return 审批记录列表
*/
List<StglAnnualReviewAudit> queryByAnnualReviewIdAndNodeCode(Long annualReviewId, String nodeCode);
/**
* 根据审批人ID查询待审批记录
*
* @param approverId 审批人ID
* @return 审批记录列表
*/
List<StglAnnualReviewAudit> queryPendingByApproverId(Long approverId);
/**
* 新增社团年审审批
*
* @param stglAnnualReviewAudit 社团年审审批
* @return 结果
*/
Boolean insertByBo(StglAnnualReviewAudit stglAnnualReviewAudit);
/**
* 修改社团年审审批
*
* @param stglAnnualReviewAudit 社团年审审批
* @return 结果
*/
Boolean updateByBo(StglAnnualReviewAudit stglAnnualReviewAudit);
/**
* 校验并批量删除社团年审审批信息
*
* @param ids 需要删除的社团年审审批主键集合
* @param isValid 是否校验,true-删除前校验,false-不校验
* @return 结果
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 创建指导老师审批记录
*
* @param annualReviewId 年审记录ID
* @param advisorIds 指导老师ID列表
* @param advisorNames 指导老师姓名列表
* @return 结果
*/
Boolean createAdvisorApprovalRecords(Long annualReviewId, List<Long> advisorIds, List<String> advisorNames);
/**
* 审批年审记录
*
* @param auditId 审批记录ID
* @param approveResult 审批结果
* @param approveOpinion 审批意见
* @return 结果
*/
Boolean approveAnnualReview(Long auditId, String approveResult, String approveOpinion);
/**
* 审批年审记录(支持动态审核人选择)
*
* @param approvalBo 审批请求对象
* @return 结果
*/
Boolean approveAnnualReview(StglAnnualReviewApprovalBo approvalBo);
}
@@ -0,0 +1,102 @@
package org.dromara.stgl.service;
import org.dromara.stgl.domain.vo.StglAuditorVo;
import org.dromara.stgl.domain.bo.StglAuditorBo;
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 2024-01-01
*/
public interface IStglAuditorService {
/**
* 查询社团管理审核人配置
*
* @param id 主键
* @return 社团管理审核人配置
*/
StglAuditorVo queryById(Long id);
/**
* 查询社团管理审核人配置列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 社团管理审核人配置集合
*/
TableDataInfo<StglAuditorVo> queryPageList(StglAuditorBo bo, PageQuery pageQuery);
/**
* 查询社团管理审核人配置列表
*
* @param bo 查询条件
* @return 社团管理审核人配置集合
*/
List<StglAuditorVo> queryList(StglAuditorBo bo);
/**
* 新增社团管理审核人配置
*
* @param bo 社团管理审核人配置
* @return 结果
*/
Boolean insertByBo(StglAuditorBo bo);
/**
* 修改社团管理审核人配置
*
* @param bo 社团管理审核人配置
* @return 结果
*/
Boolean updateByBo(StglAuditorBo bo);
/**
* 校验并批量删除社团管理审核人配置信息
*
* @param ids 需要删除的主键集合
* @return 结果
*/
Boolean deleteWithValidByIds(Collection<Long> ids);
/**
* 批量添加审核人
*
* @param userIds 用户ID列表
* @param remark 备注
* @return 结果
*/
Boolean batchAddAuditors(List<Long> userIds, String remark);
/**
* 查询我配置的审核人列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 审核人列表
*/
TableDataInfo<StglAuditorVo> queryMyConfigList(StglAuditorBo bo, PageQuery pageQuery);
/**
* 批量启用审核人
*
* @param ids 审核人ID列表
* @return 结果
*/
Boolean enableAuditors(List<Long> ids);
/**
* 批量停用审核人
*
* @param ids 审核人ID列表
* @return 结果
*/
Boolean disableAuditors(List<Long> ids);
}
@@ -0,0 +1,409 @@
package org.dromara.stgl.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
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.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.stgl.domain.StglAnnualReviewAudit;
import org.dromara.stgl.domain.StglClubAnnualReview;
import org.dromara.stgl.domain.bo.StglAnnualReviewApprovalBo;
import org.dromara.stgl.mapper.StglAnnualReviewAuditMapper;
import org.dromara.stgl.mapper.StglClubAnnualReviewMapper;
import org.dromara.stgl.service.IStglAnnualReviewAuditService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 社团年审审批Service业务层处理
*
* @author ruoyi
* @date 2024-01-01
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class StglAnnualReviewAuditServiceImpl implements IStglAnnualReviewAuditService {
private final StglAnnualReviewAuditMapper baseMapper;
private final StglClubAnnualReviewMapper annualReviewMapper;
/**
* 查询社团年审审批
*/
@Override
public StglAnnualReviewAudit queryById(Long id) {
return baseMapper.selectById(id);
}
/**
* 根据年审记录ID查询审批记录
*/
@Override
public List<StglAnnualReviewAudit> queryByAnnualReviewId(Long annualReviewId) {
return baseMapper.selectByAnnualReviewId(annualReviewId);
}
/**
* 根据年审记录ID和节点代码查询审批记录
*/
@Override
public List<StglAnnualReviewAudit> queryByAnnualReviewIdAndNodeCode(Long annualReviewId, String nodeCode) {
return baseMapper.selectByAnnualReviewIdAndNodeCode(annualReviewId, nodeCode);
}
/**
* 根据审批人ID查询待审批记录
*/
@Override
public List<StglAnnualReviewAudit> queryPendingByApproverId(Long approverId) {
return baseMapper.selectPendingByApproverId(approverId);
}
/**
* 新增社团年审审批
*/
@Override
public Boolean insertByBo(StglAnnualReviewAudit bo) {
StglAnnualReviewAudit add = BeanUtil.toBean(bo, StglAnnualReviewAudit.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改社团年审审批
*/
@Override
public Boolean updateByBo(StglAnnualReviewAudit bo) {
StglAnnualReviewAudit update = BeanUtil.toBean(bo, StglAnnualReviewAudit.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(StglAnnualReviewAudit entity) {
// TODO 做一些数据校验,如唯一约束
}
/**
* 批量删除社团年审审批
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
* 创建指导老师审批记录
*/
@Override
@Transactional
public Boolean createAdvisorApprovalRecords(Long annualReviewId, List<Long> advisorIds, List<String> advisorNames) {
if (ObjectUtil.isEmpty(advisorIds) || ObjectUtil.isEmpty(advisorNames) || advisorIds.size() != advisorNames.size()) {
return false;
}
// 删除已存在的指导老师审批记录
LambdaQueryWrapper<StglAnnualReviewAudit> wrapper = Wrappers.lambdaQuery();
wrapper.eq(StglAnnualReviewAudit::getAnnualReviewId, annualReviewId)
.eq(StglAnnualReviewAudit::getNodeCode, "advisor_approve");
baseMapper.delete(wrapper);
// 创建新的指导老师审批记录
for (int i = 0; i < advisorIds.size(); i++) {
StglAnnualReviewAudit audit = new StglAnnualReviewAudit();
audit.setAnnualReviewId(annualReviewId);
audit.setNodeCode("advisor_approve");
audit.setNodeName("指导老师审批");
// 设置节点顺序:单个指导老师为1.0,多个指导老师为1.1, 1.2, 1.3...
if (advisorIds.size() == 1) {
audit.setNodeOrder(new BigDecimal("1.0"));
} else {
audit.setNodeOrder(new BigDecimal("1." + (i + 1)));
}
// 设置节点状态:第一个为pending,其他为waiting
audit.setNodeStatus(i == 0 ? "pending" : "waiting");
audit.setApproverId(advisorIds.get(i));
audit.setApproverName(advisorNames.get(i));
baseMapper.insert(audit);
}
return true;
}
/**
* 审批年审记录
*/
@Override
@Transactional
public Boolean approveAnnualReview(Long auditId, String approveResult, String approveOpinion) {
// 1. 获取当前审批记录
StglAnnualReviewAudit currentAudit = baseMapper.selectById(auditId);
if (currentAudit == null || !"pending".equals(currentAudit.getNodeStatus())) {
return false;
}
// 2. 更新当前审批记录
currentAudit.setNodeStatus("approved");
currentAudit.setApproveResult(approveResult);
currentAudit.setApproveOpinion(approveOpinion);
currentAudit.setApproveTime(new Date());
baseMapper.updateById(currentAudit);
// 3. 如果审批不通过,直接更新年审记录状态为4(审核不通过)
if ("不通过".equals(approveResult)) {
StglClubAnnualReview annualReview = annualReviewMapper.selectById(currentAudit.getAnnualReviewId());
if (annualReview != null) {
annualReview.setStatus("4");
annualReviewMapper.updateById(annualReview);
}
return true;
}
// 4. 如果审批通过,检查是否有下一个待审批的同环节审批人
List<StglAnnualReviewAudit> samePhaseAudits = baseMapper.selectByAnnualReviewIdAndNodeCode(
currentAudit.getAnnualReviewId(), currentAudit.getNodeCode());
// 查找下一个waiting状态的审批记录
StglAnnualReviewAudit nextAudit = null;
for (StglAnnualReviewAudit audit : samePhaseAudits) {
if ("waiting".equals(audit.getNodeStatus()) &&
audit.getNodeOrder().compareTo(currentAudit.getNodeOrder()) > 0) {
if (nextAudit == null || audit.getNodeOrder().compareTo(nextAudit.getNodeOrder()) < 0) {
nextAudit = audit;
}
}
}
// 5. 如果有下一个审批人,将其状态改为pending
if (nextAudit != null) {
nextAudit.setNodeStatus("pending");
baseMapper.updateById(nextAudit);
} else {
// 6. 如果同环节所有审批人都审批通过,检查是否是第一个审批节点,决定是否更新状态为2(审核中)
boolean allApproved = samePhaseAudits.stream()
.allMatch(audit -> "approved".equals(audit.getNodeStatus()) && "通过".equals(audit.getApproveResult()));
if (allApproved) {
// 只有第一个审批节点(node_order=1.1)通过时才设置为2-审核中
if (isFirstApprovalNode(currentAudit)) {
StglClubAnnualReview annualReview = annualReviewMapper.selectById(currentAudit.getAnnualReviewId());
if (annualReview != null) {
annualReview.setStatus("2");
annualReviewMapper.updateById(annualReview);
log.info("第一个审批节点通过,年审记录状态更新为2-审核中,年审记录ID: {}", annualReview.getId());
}
} else {
log.info("非第一个审批节点通过,保持当前状态,当前节点: {}, node_order: {}",
currentAudit.getNodeName(), currentAudit.getNodeOrder());
}
}
}
return true;
}
/**
* 审批年审记录(支持动态审核人选择)
*/
@Override
@Transactional
public Boolean approveAnnualReview(StglAnnualReviewApprovalBo approvalBo) {
// 调用原有方法进行基础审批
Boolean result = approveAnnualReview(approvalBo.getAuditId(),
approvalBo.getApproveResult(),
approvalBo.getApproveOpinion());
// 如果审批通过且有指定下一步审核人,创建新的审批记录
if (result && "通过".equals(approvalBo.getApproveResult()) &&
approvalBo.getNextAuditors() != null && !approvalBo.getNextAuditors().isEmpty()) {
// 获取当前审批记录
StglAnnualReviewAudit currentAudit = baseMapper.selectById(approvalBo.getAuditId());
if (currentAudit != null) {
// 创建下一步审核人的审批记录
createNextStepAuditRecords(currentAudit.getAnnualReviewId(), approvalBo.getNextAuditors());
}
} else if (result && "通过".equals(approvalBo.getApproveResult()) &&
(approvalBo.getNextAuditors() == null || approvalBo.getNextAuditors().isEmpty())) {
// 如果审批通过且没有下一步审核人,检查是否是校团委最后节点,决定是否结束流程
StglAnnualReviewAudit currentAudit = baseMapper.selectById(approvalBo.getAuditId());
if (currentAudit != null && isSchoolCommitteeLastNode(currentAudit)) {
// 校团委最后节点审批通过,流程结束,将年审记录状态设置为3(审核通过)
StglClubAnnualReview annualReview = annualReviewMapper.selectById(currentAudit.getAnnualReviewId());
if (annualReview != null) {
annualReview.setStatus("3");
annualReviewMapper.updateById(annualReview);
log.info("校团委最后节点审批通过,年审流程结束,年审记录ID: {}, 状态更新为: 3", annualReview.getId());
}
}
}
return result;
}
/**
* 创建下一步审批记录
*/
private void createNextStepAuditRecords(Long annualReviewId, List<StglAnnualReviewApprovalBo.NextAuditor> nextAuditors) {
// 查询已有的审批记录,确定下一个审批层级
List<StglAnnualReviewAudit> existingAudits = baseMapper.selectByAnnualReviewId(annualReviewId);
// 确定下一个审批层级
int nextLevel = determineNextApprovalLevel(existingAudits);
for (int i = 0; i < nextAuditors.size(); i++) {
StglAnnualReviewApprovalBo.NextAuditor auditor = nextAuditors.get(i);
StglAnnualReviewAudit nextAudit = new StglAnnualReviewAudit();
nextAudit.setAnnualReviewId(annualReviewId);
nextAudit.setNodeCode(getNodeCodeByLevel(nextLevel));
nextAudit.setNodeName(getNodeNameByLevel(nextLevel));
// 根据层级和顺序设置node_order
if (nextAuditors.size() == 1 && nextLevel == 3) {
// 校团委审批单个审核人为3.0
nextAudit.setNodeOrder(new BigDecimal(nextLevel + ".0"));
} else {
// 其他情况按 层级.序号 格式设置
nextAudit.setNodeOrder(new BigDecimal(nextLevel + "." + (i + 1)));
}
nextAudit.setApproverId(auditor.getUserId());
nextAudit.setApproverName(auditor.getNickName() != null ? auditor.getNickName() : auditor.getUserName());
nextAudit.setNodeStatus(i == 0 ? "pending" : "waiting"); // 第一个为pending,其余为waiting
nextAudit.setCreateTime(new Date());
baseMapper.insert(nextAudit);
}
}
/**
* 确定下一个审批层级
*/
private int determineNextApprovalLevel(List<StglAnnualReviewAudit> existingAudits) {
int maxLevel = 1; // 默认从指导老师审批层级开始
for (StglAnnualReviewAudit audit : existingAudits) {
BigDecimal nodeOrder = audit.getNodeOrder();
if (nodeOrder != null) {
int level = nodeOrder.intValue(); // 获取整数部分作为层级
if (level > maxLevel) {
maxLevel = level;
}
}
}
return maxLevel + 1; // 下一个层级
}
/**
* 根据层级获取节点代码
*/
private String getNodeCodeByLevel(int level) {
switch (level) {
case 1:
return "advisor_approve";
case 2:
return "college_approve";
case 3:
return "youth_league_approve";
default:
return "college_approve"; // 默认为二级学院审批
}
}
/**
* 根据层级获取节点名称
*/
private String getNodeNameByLevel(int level) {
switch (level) {
case 1:
return "指导老师审批";
case 2:
return "二级学院审批";
case 3:
return "校团委审批";
default:
return "二级学院审批"; // 默认为二级学院审批
}
}
/**
* 判断是否为第一个审批节点(node_order=1.1)
*/
private boolean isFirstApprovalNode(StglAnnualReviewAudit currentAudit) {
// 检查node_order是否为1.1
return currentAudit != null &&
currentAudit.getNodeOrder() != null &&
currentAudit.getNodeOrder().compareTo(new BigDecimal("1.1")) == 0;
}
/**
* 判断当前是否是校团委审批的最后一个节点
*/
private boolean isSchoolCommitteeLastNode(StglAnnualReviewAudit currentAudit) {
if (currentAudit == null || currentAudit.getNodeOrder() == null) {
return false;
}
String currentNodeName = currentAudit.getNodeName();
BigDecimal currentNodeOrder = currentAudit.getNodeOrder();
// 检查是否是校团委审批节点且node_order是3.X格式
boolean isSchoolCommitteeNode = "校团委审批".equals(currentNodeName);
boolean isNodeOrder3X = currentNodeOrder.compareTo(new BigDecimal("3")) >= 0 &&
currentNodeOrder.compareTo(new BigDecimal("4")) < 0;
if (!isSchoolCommitteeNode || !isNodeOrder3X) {
return false;
}
// 获取同一环节的所有节点
List<StglAnnualReviewAudit> samePhaseNodes = baseMapper.selectByAnnualReviewIdAndNodeCode(
currentAudit.getAnnualReviewId(), currentAudit.getNodeCode());
if (samePhaseNodes.isEmpty()) {
return false;
}
// 找出同一环节中最大的nodeOrder
BigDecimal maxNodeOrder = samePhaseNodes.stream()
.map(StglAnnualReviewAudit::getNodeOrder)
.filter(order -> order != null)
.max(BigDecimal::compareTo)
.orElse(BigDecimal.ZERO);
// 当前节点的nodeOrder等于同环节最大nodeOrder时,表示是最后一个节点
boolean isLastNode = currentNodeOrder.compareTo(maxNodeOrder) == 0;
log.info("校团委最后节点判断: 节点名称={}, nodeOrder={}, 是否校团委={}, 是否3.X={}, 是否最后节点={}",
currentNodeName, currentNodeOrder, isSchoolCommitteeNode, isNodeOrder3X, isLastNode);
return isLastNode;
}
}
@@ -0,0 +1,266 @@
package org.dromara.stgl.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.dromara.stgl.domain.bo.StglAuditorBo;
import org.dromara.stgl.domain.vo.StglAuditorVo;
import org.dromara.stgl.domain.StglAuditor;
import org.dromara.stgl.domain.StglUserWithDeptInfo;
import org.dromara.stgl.mapper.StglAuditorMapper;
import org.dromara.stgl.service.IStglAuditorService;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.core.exception.ServiceException;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.ArrayList;
import java.util.stream.Collectors;
/**
* 社团管理审核人配置Service业务层处理
*
* @author ruoyi
* @date 2024-01-01
*/
@RequiredArgsConstructor
@Service
public class StglAuditorServiceImpl implements IStglAuditorService {
private final StglAuditorMapper baseMapper;
private final ISysUserService sysUserService;
/**
* 查询社团管理审核人配置
*/
@Override
public StglAuditorVo queryById(Long id) {
StglAuditor entity = baseMapper.selectById(id);
if (entity == null) {
return null;
}
return MapstructUtils.convert(entity, StglAuditorVo.class);
}
/**
* 查询社团管理审核人配置列表
*/
@Override
public TableDataInfo<StglAuditorVo> queryPageList(StglAuditorBo bo, PageQuery pageQuery) {
// 直接使用查询条件构建器
LambdaQueryWrapper<StglAuditor> lqw = buildQueryWrapper(bo);
Page<StglAuditor> entityPage = baseMapper.selectPage(pageQuery.build(), lqw);
// 转换为 VO 对象
List<StglAuditorVo> voList = new ArrayList<>();
for (StglAuditor entity : entityPage.getRecords()) {
StglAuditorVo vo = MapstructUtils.convert(entity, StglAuditorVo.class);
voList.add(vo);
}
// 构建分页结果
Page<StglAuditorVo> voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
voPage.setRecords(voList);
return TableDataInfo.build(voPage);
}
/**
* 查询社团管理审核人配置列表
*/
@Override
public List<StglAuditorVo> queryList(StglAuditorBo bo) {
LambdaQueryWrapper<StglAuditor> lqw = buildQueryWrapper(bo);
List<StglAuditor> entityList = baseMapper.selectList(lqw);
// 转换为 VO 对象
List<StglAuditorVo> voList = new ArrayList<>();
for (StglAuditor entity : entityList) {
StglAuditorVo vo = MapstructUtils.convert(entity, StglAuditorVo.class);
voList.add(vo);
}
return voList;
}
private LambdaQueryWrapper<StglAuditor> buildQueryWrapper(StglAuditorBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<StglAuditor> lqw = Wrappers.<StglAuditor>lambdaQuery();
lqw.eq(bo.getUserId() != null, StglAuditor::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getUserName()), StglAuditor::getUserName, bo.getUserName());
lqw.like(StringUtils.isNotBlank(bo.getNickName()), StglAuditor::getNickName, bo.getNickName());
lqw.eq(bo.getDeptId() != null, StglAuditor::getDeptId, bo.getDeptId());
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), StglAuditor::getStatus, bo.getStatus());
lqw.eq(bo.getConfigBy() != null, StglAuditor::getConfigBy, bo.getConfigBy());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
StglAuditor::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
lqw.orderByDesc(StglAuditor::getCreateTime);
return lqw;
}
/**
* 新增社团管理审核人配置
*/
@Override
public Boolean insertByBo(StglAuditorBo bo) {
StglAuditor add = MapstructUtils.convert(bo, StglAuditor.class);
validEntityBeforeSave(add);
// 设置配置人信息
Long currentUserId = LoginHelper.getUserId();
add.setConfigBy(currentUserId);
// 获取当前用户信息设置配置人姓名
try {
SysUserVo currentUser = sysUserService.selectUserById(currentUserId);
if (currentUser != null) {
add.setConfigByName(currentUser.getUserName());
}
} catch (Exception e) {
// 如果获取失败,使用默认值
add.setConfigByName("系统管理员");
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改社团管理审核人配置
*/
@Override
public Boolean updateByBo(StglAuditorBo bo) {
StglAuditor update = MapstructUtils.convert(bo, StglAuditor.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(StglAuditor entity) {
if (entity.getUserId() != null) {
// 检查用户是否已存在(新增时或修改用户ID时)
if (entity.getId() == null) {
// 新增时检查
int count = baseMapper.checkUserExists(entity.getUserId());
if (count > 0) {
throw new ServiceException("该用户已配置为审核人,不能重复添加");
}
}
}
}
/**
* 批量删除社团管理审核人配置
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids) {
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 批量添加审核人
*/
@Override
public Boolean batchAddAuditors(List<Long> userIds, String remark) {
if (userIds == null || userIds.isEmpty()) {
throw new ServiceException("用户ID列表不能为空");
}
Long currentUserId = LoginHelper.getUserId();
String currentUserName = "系统管理员";
// 获取当前用户信息
try {
SysUserVo currentUser = sysUserService.selectUserById(currentUserId);
if (currentUser != null) {
currentUserName = currentUser.getUserName();
}
} catch (Exception e) {
// 使用默认值
}
// 1. 获取完整的用户信息(包含部门信息)
List<StglUserWithDeptInfo> users = baseMapper.selectUsersWithDeptInfo(userIds);
if (users.isEmpty()) {
throw new ServiceException("没有找到有效的用户信息");
}
// 2. 过滤已存在的审核人
List<Long> existingUserIds = baseMapper.selectList(Wrappers.<StglAuditor>lambdaQuery()
.in(StglAuditor::getUserId, userIds))
.stream().map(StglAuditor::getUserId).collect(Collectors.toList());
// 3. 构建新增审核人列表
List<StglAuditor> auditorList = new ArrayList<>();
for (StglUserWithDeptInfo user : users) {
if (!existingUserIds.contains(user.getUserId())) {
StglAuditor auditor = new StglAuditor();
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.setRemark(remark);
auditor.setConfigBy(currentUserId);
auditor.setConfigByName(currentUserName);
auditorList.add(auditor);
}
}
// 4. 批量保存
if (auditorList.isEmpty()) {
throw new ServiceException("所有用户都已存在,无需重复添加");
}
return baseMapper.insertBatch(auditorList);
}
/**
* 查询我配置的审核人列表
*/
@Override
public TableDataInfo<StglAuditorVo> queryMyConfigList(StglAuditorBo bo, PageQuery pageQuery) {
bo.setConfigBy(LoginHelper.getUserId());
return queryPageList(bo, pageQuery);
}
/**
* 批量启用审核人
*/
@Override
public Boolean enableAuditors(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return false;
}
return baseMapper.enableAuditorsByIds(ids) > 0;
}
/**
* 批量停用审核人
*/
@Override
public Boolean disableAuditors(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return false;
}
return baseMapper.disableAuditorsByIds(ids) > 0;
}
}
@@ -24,8 +24,10 @@ import org.dromara.stgl.mapper.StglClubAnnualReviewActivityMapper;
import org.dromara.stgl.service.IStglClubAnnualReviewService;
import org.dromara.stgl.service.IStglActivityService;
import org.dromara.stgl.service.IStglClubService;
import org.dromara.stgl.service.IStglAnnualReviewAuditService;
import org.dromara.stgl.domain.bo.StglActivityBo;
import org.dromara.stgl.domain.vo.StglClubVo;
import org.dromara.stgl.domain.StglClub;
import java.util.List;
import java.util.Map;
@@ -60,6 +62,7 @@ public class StglClubAnnualReviewServiceImpl implements IStglClubAnnualReviewSer
private final StglClubAnnualReviewActivityMapper activityMapper;
private final IStglActivityService stglActivityService;
private final IStglClubService stglClubService;
private final IStglAnnualReviewAuditService auditService;
private final ObjectMapper objectMapper = new ObjectMapper();
/**
@@ -539,9 +542,82 @@ public class StglClubAnnualReviewServiceImpl implements IStglClubAnnualReviewSer
throw new RuntimeException("只能提交草稿状态的年审记录");
}
// 获取社团信息,提取指导老师信息
StglClubVo club = stglClubService.queryById(entity.getClubId());
if (club == null) {
throw new RuntimeException("社团信息不存在");
}
// 提取指导老师信息
List<Long> advisorIds = new ArrayList<>();
List<String> advisorNames = new ArrayList<>();
try {
// 优先从JSON字段读取指导老师数据
if (StringUtils.isNotBlank(club.getAdvisorsJson())) {
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> advisors = mapper.readValue(club.getAdvisorsJson(),
new com.fasterxml.jackson.core.type.TypeReference<List<Map<String, Object>>>() {});
for (Map<String, Object> advisor : advisors) {
// 提取用户ID和姓名
Object userIdObj = null;
String advisorName = null;
// 尝试直接从advisor对象获取userId和name(兼容旧格式)
if (advisor.containsKey("userId")) {
userIdObj = advisor.get("userId");
advisorName = (String) advisor.get("name");
}
// 尝试从teacher子对象获取userId和name(新格式)
else if (advisor.containsKey("teacher")) {
Object teacherObj = advisor.get("teacher");
if (teacherObj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> teacher = (Map<String, Object>) teacherObj;
userIdObj = teacher.get("userId");
advisorName = (String) teacher.get("nickName");
if (StringUtils.isBlank(advisorName)) {
advisorName = (String) teacher.get("userName");
}
}
}
if (userIdObj != null && StringUtils.isNotBlank(advisorName)) {
Long userId = Long.valueOf(userIdObj.toString());
advisorIds.add(userId);
advisorNames.add(advisorName);
}
}
}
// 如果没有JSON数据,尝试从单一字段获取
if (advisorIds.isEmpty() && StringUtils.isNotBlank(club.getAdvisorName())) {
// 这种情况下无法获取用户ID,暂时跳过审批流程
log.warn("社团 {} 的指导老师信息不完整,无法创建审批记录", club.getClubName());
}
} catch (Exception e) {
log.error("解析指导老师信息失败", e);
throw new RuntimeException("解析指导老师信息失败");
}
// 更新年审记录状态
entity.setStatus("1");
entity.setReviewTime(new Date());
return baseMapper.updateById(entity) > 0;
boolean updateResult = baseMapper.updateById(entity) > 0;
// 如果有指导老师信息,创建审批记录
if (!advisorIds.isEmpty() && updateResult) {
try {
auditService.createAdvisorApprovalRecords(id, advisorIds, advisorNames);
} catch (Exception e) {
log.error("创建审批记录失败", e);
throw new RuntimeException("创建审批记录失败");
}
}
return updateResult;
}
/**
@@ -0,0 +1,56 @@
<?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.stgl.mapper.StglAnnualReviewAuditMapper">
<resultMap type="org.dromara.stgl.domain.StglAnnualReviewAudit" id="StglAnnualReviewAuditResult">
<result property="id" column="id" />
<result property="tenantId" column="tenant_id" />
<result property="createDept" column="create_dept" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="deleted" column="deleted" />
<result property="annualReviewId" column="annual_review_id" />
<result property="nodeCode" column="node_code" />
<result property="nodeName" column="node_name" />
<result property="nodeOrder" column="node_order" />
<result property="nodeStatus" column="audit_status" />
<result property="approverId" column="approver_id" />
<result property="approverName" column="approver_name" />
<result property="approveResult" column="approve_result" />
<result property="approveOpinion" column="audit_opinion" />
<result property="approveTime" column="audit_time" />
</resultMap>
<sql id="selectStglAnnualReviewAuditVo">
select id, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted,
annual_review_id, node_code, node_name, node_order, audit_status,
approver_id, approver_name, approve_result, audit_opinion, audit_time
from stgl_annual_review_audit
</sql>
<!-- 根据年审记录ID查询审批记录 -->
<select id="selectByAnnualReviewId" parameterType="Long" resultMap="StglAnnualReviewAuditResult">
<include refid="selectStglAnnualReviewAuditVo"/>
where annual_review_id = #{annualReviewId} and deleted = 0
order by node_order
</select>
<!-- 根据年审记录ID和节点代码查询审批记录 -->
<select id="selectByAnnualReviewIdAndNodeCode" resultMap="StglAnnualReviewAuditResult">
<include refid="selectStglAnnualReviewAuditVo"/>
where annual_review_id = #{annualReviewId} and node_code = #{nodeCode} and deleted = 0
order by node_order
</select>
<!-- 根据审批人ID查询待审批记录 -->
<select id="selectPendingByApproverId" parameterType="Long" resultMap="StglAnnualReviewAuditResult">
<include refid="selectStglAnnualReviewAuditVo"/>
where approver_id = #{approverId} and audit_status = 'pending' and deleted = 0
order by create_time desc
</select>
</mapper>
@@ -0,0 +1,132 @@
<?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.stgl.mapper.StglAuditorMapper">
<resultMap type="org.dromara.stgl.domain.vo.StglAuditorVo" id="StglAuditorResult">
<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="selectStglAuditorVo">
SELECT
sa.id,
sa.user_id,
sa.user_name,
sa.nick_name,
sa.dept_id,
sa.dept_name,
sa.phone_number,
sa.email,
sa.status,
sa.remark,
sa.config_by,
sa.config_by_name,
sa.create_by,
sa.create_time,
sa.update_by,
sa.update_time
FROM stgl_auditor sa
</sql>
<select id="selectStglAuditorVoList" parameterType="org.dromara.stgl.domain.StglAuditor" resultMap="StglAuditorResult">
<include refid="selectStglAuditorVo"/>
<where>
sa.deleted = 0
<if test="userId != null">
AND sa.user_id = #{userId}
</if>
<if test="userName != null and userName != ''">
AND sa.user_name LIKE CONCAT('%', #{userName}, '%')
</if>
<if test="nickName != null and nickName != ''">
AND sa.nick_name LIKE CONCAT('%', #{nickName}, '%')
</if>
<if test="deptId != null">
AND sa.dept_id = #{deptId}
</if>
<if test="status != null and status != ''">
AND sa.status = #{status}
</if>
<if test="configBy != null">
AND sa.config_by = #{configBy}
</if>
<if test="params.beginCreateTime != null and params.beginCreateTime != ''">
AND sa.create_time &gt;= #{params.beginCreateTime}
</if>
<if test="params.endCreateTime != null and params.endCreateTime != ''">
AND sa.create_time &lt;= #{params.endCreateTime}
</if>
</where>
ORDER BY sa.create_time DESC
</select>
<select id="selectStglAuditorVoById" parameterType="Long" resultMap="StglAuditorResult">
<include refid="selectStglAuditorVo"/>
<where>
sa.id = #{id} AND sa.deleted = 0
</where>
</select>
<select id="checkUserExists" parameterType="Long" resultType="int">
SELECT COUNT(1) FROM stgl_auditor
WHERE user_id = #{userId} AND deleted = 0
</select>
<update id="enableAuditorsByIds">
UPDATE stgl_auditor
SET status = '1', update_time = NOW()
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND deleted = 0
</update>
<update id="disableAuditorsByIds">
UPDATE stgl_auditor
SET status = '0', update_time = NOW()
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND deleted = 0
</update>
<!-- 查询完整的用户信息(包含部门信息)用于批量添加 -->
<select id="selectUsersWithDeptInfo" resultType="org.dromara.stgl.domain.StglUserWithDeptInfo">
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>
@@ -6,6 +6,7 @@ import org.dromara.common.web.core.BaseController;
import org.dromara.xljk.domain.bo.XljkConsultationAppointmentBo;
import org.dromara.xljk.domain.vo.XljkConsultationAppointmentVo;
import org.dromara.xljk.domain.vo.TimeSlotDetailVo;
import org.dromara.xljk.domain.vo.ScheduleWithStatusVo;
import org.dromara.xljk.service.IXljkConsultationAppointmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
@@ -161,52 +162,10 @@ public class XljkConsultationAppointmentController extends BaseController {
// 从请求体中获取参数
String status = requestBody.get("status").toString();
Date actualVisitTime = null;
Integer communicationDuration = null;
String consultationContent = "";
String counselorSuggestion = "";
// 处理actualVisitTime
if (requestBody.get("actualVisitTime") != null) {
try {
String actualVisitTimeStr = requestBody.get("actualVisitTime").toString();
log.info("解析实际到访时间字符串: {}", actualVisitTimeStr);
// 尝试多种日期格式
SimpleDateFormat[] dateFormats = {
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"),
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"),
new SimpleDateFormat("yyyy-MM-dd")
};
boolean parsed = false;
for (SimpleDateFormat sdf : dateFormats) {
try {
actualVisitTime = sdf.parse(actualVisitTimeStr);
parsed = true;
log.info("成功解析实际到访时间: {}, 使用格式: {}", actualVisitTime, sdf.toPattern());
break;
} catch (Exception e) {
// 继续尝试下一个格式
}
}
if (!parsed) {
log.error("无法解析实际到访时间: {}", actualVisitTimeStr);
// 使用当前时间作为默认值
actualVisitTime = new Date();
log.info("使用当前时间作为默认值: {}", actualVisitTime);
}
} catch (Exception e) {
log.error("解析actualVisitTime失败", e);
// 使用当前时间作为默认值
actualVisitTime = new Date();
log.info("使用当前时间作为默认值: {}", actualVisitTime);
}
}
// 处理communicationDuration
if (requestBody.get("communicationDuration") != null) {
try {
@@ -227,8 +186,47 @@ public class XljkConsultationAppointmentController extends BaseController {
counselorSuggestion = requestBody.get("counselorSuggestion").toString();
}
boolean result = service.recordConsultation(id, actualVisitTime, communicationDuration,
consultationContent, counselorSuggestion, status);
// 处理心理病史相关字段
Integer psychologicalHistory = null;
String psychologicalHistoryDetail = "";
String basicSituation = "";
String majorLifeEvents = "";
Integer needReferral = null;
if (requestBody.get("psychologicalHistory") != null) {
try {
psychologicalHistory = Integer.parseInt(requestBody.get("psychologicalHistory").toString());
} catch (Exception e) {
log.error("解析psychologicalHistory失败", e);
}
}
if (requestBody.get("psychologicalHistoryDetail") != null) {
psychologicalHistoryDetail = requestBody.get("psychologicalHistoryDetail").toString();
}
if (requestBody.get("basicSituation") != null) {
basicSituation = requestBody.get("basicSituation").toString();
}
if (requestBody.get("majorLifeEvents") != null) {
majorLifeEvents = requestBody.get("majorLifeEvents").toString();
}
if (requestBody.get("needReferral") != null) {
try {
needReferral = Integer.parseInt(requestBody.get("needReferral").toString());
} catch (Exception e) {
log.error("解析needReferral失败", e);
}
}
log.info("解析的心理病史相关字段: psychologicalHistory={}, psychologicalHistoryDetail={}, basicSituation={}, majorLifeEvents={}, needReferral={}",
psychologicalHistory, psychologicalHistoryDetail, basicSituation, majorLifeEvents, needReferral);
boolean result = service.recordConsultation(id, communicationDuration,
consultationContent, counselorSuggestion, status,
psychologicalHistory, psychologicalHistoryDetail, basicSituation, majorLifeEvents, needReferral);
if (result) {
return R.ok(true);
} else {
@@ -284,7 +282,7 @@ public class XljkConsultationAppointmentController extends BaseController {
XSSFSheet sheet = workbook.createSheet("预约记录");
// 表头
String[] headers = {"序号", "学生姓名", "学号", "院系", "专业", "班级", "咨询原因", "咨询师", "预约日期", "预约时段", "状态", "咨询结果", "实际到访时间", "沟通时长", "咨询内容", "咨询师建议"};
String[] headers = {"序号", "学生姓名", "学号", "院系", "专业", "班级", "咨询主题", "咨询原因", "咨询师", "预约日期", "预约时段", "状态", "咨询结果", "沟通时长", "心理病史", "心理病史详情", "基本情况", "重大生活事件", "本次咨询过程", "是否需要转介", "本学期到访次数"};
XSSFRow headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
headerRow.createCell(i).setCellValue(headers[i]);
@@ -303,6 +301,7 @@ public class XljkConsultationAppointmentController extends BaseController {
row.createCell(col++).setCellValue(vo.getCollegeName());
row.createCell(col++).setCellValue(vo.getMajorName());
row.createCell(col++).setCellValue(vo.getClassName());
row.createCell(col++).setCellValue(vo.getConsultTheme() != null ? vo.getConsultTheme() : ""); // 咨询主题
row.createCell(col++).setCellValue(vo.getReason());
row.createCell(col++).setCellValue(vo.getCounselorName());
row.createCell(col++).setCellValue(vo.getAppointmentDate() != null ? dateFormat.format(vo.getAppointmentDate()) : "");
@@ -315,15 +314,43 @@ public class XljkConsultationAppointmentController extends BaseController {
case 1: status = "已到访"; break;
case 2: status = "已取消"; break;
case 3: status = "未到访"; break;
case 4: status = "待审核"; break;
case 5: status = "已驳回"; break;
default: status = "未知状态";
}
}
row.createCell(col++).setCellValue(status);
row.createCell(col++).setCellValue(status); // 咨询结果
row.createCell(col++).setCellValue(vo.getActualVisitTime() != null ? dateTimeFormat.format(vo.getActualVisitTime()) : "");
row.createCell(col++).setCellValue(vo.getCommunicationDuration() != null ? vo.getCommunicationDuration().toString() : "");
row.createCell(col++).setCellValue(vo.getConsultationContent());
row.createCell(col++).setCellValue(vo.getCounselorSuggestion());
row.createCell(col++).setCellValue(vo.getCommunicationDuration() != null ? vo.getCommunicationDuration().toString() + "分钟" : "");
// 心理病史
String psychologicalHistory = "";
if (vo.getPsychologicalHistory() != null) {
psychologicalHistory = vo.getPsychologicalHistory() == 2 ? "是" : "否";
}
row.createCell(col++).setCellValue(psychologicalHistory);
// 心理病史详情
row.createCell(col++).setCellValue(vo.getPsychologicalHistoryDetail() != null ? vo.getPsychologicalHistoryDetail() : "");
// 基本情况
row.createCell(col++).setCellValue(vo.getBasicSituation() != null ? vo.getBasicSituation() : "");
// 重大生活事件
row.createCell(col++).setCellValue(vo.getMajorLifeEvents() != null ? vo.getMajorLifeEvents() : "");
// 本次咨询过程(原咨询内容)
row.createCell(col++).setCellValue(vo.getConsultationContent() != null ? vo.getConsultationContent() : "");
// 是否需要转介
String needReferral = "";
if (vo.getNeedReferral() != null) {
needReferral = vo.getNeedReferral() == 2 ? "是" : "否";
}
row.createCell(col++).setCellValue(needReferral);
// 本学期到访次数
row.createCell(col++).setCellValue(vo.getSemesterVisitCount() != null ? vo.getSemesterVisitCount().toString() + "次" : "0次");
}
// 写入字节流
@@ -350,6 +377,15 @@ public class XljkConsultationAppointmentController extends BaseController {
.body(data);
}
/**
* 获取日历视图的排班数据(带预约状态)
*/
@GetMapping("/calendar-schedules")
public R<List<ScheduleWithStatusVo>> getCalendarSchedules(@RequestParam String startDate, @RequestParam String endDate) {
List<ScheduleWithStatusVo> schedules = service.getCalendarSchedules(startDate, endDate);
return R.ok(schedules);
}
/**
* 转义CSV字段中的特殊字符
*/
@@ -14,6 +14,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.dromara.xljk.domain.vo.XljkConsultationScheduleSummaryVo;
@RestController
@@ -57,10 +58,14 @@ public class XljkConsultationScheduleController {
batchBo.setCounselorId(Long.valueOf(requestMap.get("counselorId").toString()));
}
if (requestMap.containsKey("office")) {
batchBo.setOffice(requestMap.get("office").toString());
}
batchBo.setStartDate(requestMap.get("startDate").toString());
batchBo.setEndDate(requestMap.get("endDate").toString());
// 设置上午时段
// 设置中午时段(字段名保持morning以兼容前端)
if (requestMap.containsKey("morningEnabled")) {
batchBo.setMorningEnabled(Boolean.valueOf(requestMap.get("morningEnabled").toString()));
}
@@ -73,6 +78,26 @@ public class XljkConsultationScheduleController {
batchBo.setMorningEndTime(requestMap.get("morningEndTime").toString());
}
// 设置中午时段(新版本)
if (requestMap.containsKey("noonEnabled")) {
batchBo.setNoonEnabled(Boolean.valueOf(requestMap.get("noonEnabled").toString()));
}
if (requestMap.containsKey("noonTimeSlots")) {
List<Map<String, Object>> noonSlots = (List<Map<String, Object>>) requestMap.get("noonTimeSlots");
if (noonSlots != null) {
List<XljkConsultationScheduleBatchBo.TimeSlot> timeSlots = noonSlots.stream()
.map(slot -> {
XljkConsultationScheduleBatchBo.TimeSlot timeSlot = new XljkConsultationScheduleBatchBo.TimeSlot();
timeSlot.setStartTime(slot.get("startTime").toString());
timeSlot.setEndTime(slot.get("endTime").toString());
return timeSlot;
})
.collect(Collectors.toList());
batchBo.setNoonTimeSlots(timeSlots);
}
}
// 设置下午时段
if (requestMap.containsKey("afternoonEnabled")) {
batchBo.setAfternoonEnabled(Boolean.valueOf(requestMap.get("afternoonEnabled").toString()));
@@ -86,6 +111,21 @@ public class XljkConsultationScheduleController {
batchBo.setAfternoonEndTime(requestMap.get("afternoonEndTime").toString());
}
if (requestMap.containsKey("afternoonTimeSlots")) {
List<Map<String, Object>> afternoonSlots = (List<Map<String, Object>>) requestMap.get("afternoonTimeSlots");
if (afternoonSlots != null) {
List<XljkConsultationScheduleBatchBo.TimeSlot> timeSlots = afternoonSlots.stream()
.map(slot -> {
XljkConsultationScheduleBatchBo.TimeSlot timeSlot = new XljkConsultationScheduleBatchBo.TimeSlot();
timeSlot.setStartTime(slot.get("startTime").toString());
timeSlot.setEndTime(slot.get("endTime").toString());
return timeSlot;
})
.collect(Collectors.toList());
batchBo.setAfternoonTimeSlots(timeSlots);
}
}
// 设置其他字段
if (requestMap.containsKey("status")) {
batchBo.setStatus(requestMap.get("status").toString());
@@ -166,10 +206,13 @@ public class XljkConsultationScheduleController {
*/
@GetMapping("/options/counselors")
public R<List<Map<String, Object>>> getCounselorOptions() {
String sql = "SELECT u.user_id as id, u.nick_name as name FROM sys_user u " +
String sql = "SELECT u.user_id as id, u.nick_name as name, c.office as office " +
"FROM sys_user u " +
"JOIN sys_user_role ur ON u.user_id = ur.user_id " +
"JOIN sys_role r ON ur.role_id = r.role_id " +
"WHERE r.role_key = 'xljk_consultant' AND u.del_flag = '0'";
"LEFT JOIN xljk_consulter c ON u.user_id = c.user_id AND c.deleted = 0 " +
"WHERE r.role_key = 'xljk_consultant' AND u.del_flag = '0' " +
"ORDER BY u.nick_name";
List<Map<String, Object>> counselorOptions = jdbcTemplate.queryForList(sql);
return R.ok(counselorOptions);
@@ -0,0 +1,179 @@
package org.dromara.xljk.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.xljk.domain.vo.XljkConsulterVo;
import org.dromara.xljk.domain.bo.XljkConsulterBo;
import org.dromara.xljk.service.IXljkConsulterService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.domain.vo.SysUserVo;
/**
* 心理咨询师配置
*
* @author ruoyi
* @date 2024-01-01
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/xljk/consulter")
public class XljkConsulterController extends BaseController {
private final IXljkConsulterService xljkConsulterService;
private final ISysUserService sysUserService;
/**
* 查询心理咨询师配置列表
*/
@SaCheckPermission("xljk:consulter:list")
@GetMapping("/list")
public TableDataInfo<XljkConsulterVo> list(XljkConsulterBo bo, PageQuery pageQuery) {
return xljkConsulterService.queryPageList(bo, pageQuery);
}
/**
* 导出心理咨询师配置列表
*/
@SaCheckPermission("xljk:consulter:export")
@Log(title = "心理咨询师配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(XljkConsulterBo bo, HttpServletResponse response) {
List<XljkConsulterVo> list = xljkConsulterService.queryList(bo);
ExcelUtil.exportExcel(list, "心理咨询师配置", XljkConsulterVo.class, response);
}
/**
* 获取心理咨询师配置详细信息
*/
@SaCheckPermission("xljk:consulter:query")
@GetMapping("/{id}")
public R<XljkConsulterVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(xljkConsulterService.queryById(id));
}
/**
* 新增心理咨询师配置
*/
@SaCheckPermission("xljk:consulter:add")
@Log(title = "心理咨询师配置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody XljkConsulterBo bo) {
return toAjax(xljkConsulterService.insertByBo(bo));
}
/**
* 修改心理咨询师配置
*/
@SaCheckPermission("xljk:consulter:edit")
@Log(title = "心理咨询师配置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody XljkConsulterBo bo) {
return toAjax(xljkConsulterService.updateByBo(bo));
}
/**
* 删除心理咨询师配置
*/
@SaCheckPermission("xljk:consulter:remove")
@Log(title = "心理咨询师配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(xljkConsulterService.deleteWithValidByIds(List.of(ids)));
}
/**
* 批量添加咨询师
*/
@SaCheckPermission("xljk:consulter:add")
@Log(title = "批量添加咨询师", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/batchAdd")
public R<Void> batchAdd(@RequestBody BatchAddRequest request) {
return toAjax(xljkConsulterService.batchAddConsulters(request.getUserIds(), request.getRemark()));
}
/**
* 查询我配置的咨询师列表
*/
@SaCheckPermission("xljk:consulter:list")
@GetMapping("/myConfig")
public TableDataInfo<XljkConsulterVo> myConfigList(XljkConsulterBo bo, PageQuery pageQuery) {
return xljkConsulterService.queryMyConfigList(bo, pageQuery);
}
/**
* 批量启用咨询师
*/
@SaCheckPermission("xljk:consulter:edit")
@Log(title = "启用咨询师", businessType = BusinessType.UPDATE)
@PostMapping("/enable")
public R<Void> enable(@RequestBody List<Long> ids) {
return toAjax(xljkConsulterService.enableConsulters(ids));
}
/**
* 批量停用咨询师
*/
@SaCheckPermission("xljk:consulter:edit")
@Log(title = "停用咨询师", businessType = BusinessType.UPDATE)
@PostMapping("/disable")
public R<Void> disable(@RequestBody List<Long> ids) {
return toAjax(xljkConsulterService.disableConsulters(ids));
}
/**
* 根据部门ID获取用户列表
*/
@SaCheckPermission("xljk:consulter:list")
@GetMapping("/users/{deptId}")
public R<List<SysUserVo>> listUserByDeptId(@PathVariable String deptId) {
List<SysUserVo> users = sysUserService.selectUserListByDept(Long.valueOf(deptId));
return R.ok(users);
}
/**
* 批量添加请求对象
*/
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;
}
}
}
@@ -3,6 +3,7 @@ package org.dromara.xljk.domain;
import java.util.Date;
import java.io.Serializable;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.TableField;
@Data
public class XljkConsultationAppointment implements Serializable {
@@ -21,9 +22,20 @@ public class XljkConsultationAppointment implements Serializable {
private String appointmentTime;
private String appointmentDetailTime; // 具体预约时间段,如"08:30-09:00"
private String reason;
@TableField("consult_theme")
private String consultTheme; // 咨询主题
private String status;
private Date actualVisitTime;
private Integer communicationDuration;
private String consultationContent;
private String counselorSuggestion;
// 新增心理健康相关字段
private String studentName; // 学生姓名(冗余字段)
private Integer psychologicalHistory; // 心理病史(1-否,2-是)
private String psychologicalHistoryDetail; // 心理病史详情
private String basicSituation; // 基本情况
private String majorLifeEvents; // 重大生活事件
private Integer needReferral; // 是否需要转介(1-不需要,2-需要)
}
@@ -15,8 +15,9 @@ public class XljkConsultationSchedule implements Serializable {
private Date updateTime;
private Integer deleted;
private Long counselorId;
private String office; // 办公室
private Date scheduleDate;
private String timeSlot; // 0上午 1下午
private String timeSlot; // 0中午 1下午
private String startTime; // 开始时间 HH:mm 格式
private String endTime; // 结束时间 HH:mm 格式
private String status; // 0正常 1停用
@@ -0,0 +1,97 @@
package org.dromara.xljk.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;
/**
* 心理咨询师配置对象 xljk_consulter
*
* @author ruoyi
* @date 2024-01-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("xljk_consulter")
public class XljkConsulter extends TenantEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键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 office;
/**
* 邮箱地址
*/
private String email;
/**
* 状态(1正常 0停用)
*/
private String status;
/**
* 备注说明
*/
private String remark;
/**
* 配置人ID
*/
private Long configBy;
/**
* 配置人姓名
*/
private String configByName;
/**
* 删除标志(0代表存在 1代表删除)
*/
@TableLogic
private Integer deleted;
}
@@ -0,0 +1,39 @@
package org.dromara.xljk.domain;
import lombok.Data;
/**
* 心理咨询用户和部门完整信息
* 用于批量添加咨询师时获取完整的用户数据
*
* @author ruoyi
* @date 2025-01-03
*/
@Data
public class XljkUserWithDeptInfo {
/** 用户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;
}
@@ -22,12 +22,19 @@ public class XljkConsultationAppointmentBo {
private String appointmentTime;
private String appointmentDetailTime; // 具体预约时间段,如"08:30-09:00"
private String reason;
private String consultTheme; // 咨询主题
private String status;
private Date actualVisitTime;
private Integer communicationDuration;
private String consultationContent;
private String counselorSuggestion;
// 新增心理健康相关字段
private Integer psychologicalHistory; // 心理病史(1-否,2-是)
private String psychologicalHistoryDetail; // 心理病史详情
private String basicSituation; // 基本情况
private String majorLifeEvents; // 重大生活事件
private Integer needReferral; // 是否需要转介(1-不需要,2-需要)
// 查询参数字段
private String studentName;
private String studentNo;
@@ -2,6 +2,7 @@ package org.dromara.xljk.domain.bo;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 批量创建预约时段的业务对象
@@ -13,6 +14,11 @@ public class XljkConsultationScheduleBatchBo {
*/
private Long counselorId;
/**
* 办公室
*/
private String office;
/**
* 开始日期
*/
@@ -24,35 +30,50 @@ public class XljkConsultationScheduleBatchBo {
private String endDate;
/**
* 是否启用上午时段
* 是否启用中午时段(字段名保持morningEnabled以兼容前端)
*/
private Boolean morningEnabled;
/**
* 上午开始时间 (HH:mm)
* 中午开始时间 (HH:mm)(字段名保持morningStartTime以兼容前端)
*/
private String morningStartTime;
/**
* 上午结束时间 (HH:mm)
* 中午结束时间 (HH:mm)(字段名保持morningEndTime以兼容前端)
*/
private String morningEndTime;
/**
* 是否启用中午时段
*/
private Boolean noonEnabled;
/**
* 中午时段列表
*/
private List<TimeSlot> noonTimeSlots;
/**
* 是否启用下午时段
*/
private Boolean afternoonEnabled;
/**
* 下午开始时间 (HH:mm)
* 下午开始时间 (HH:mm)(兼容旧版本)
*/
private String afternoonStartTime;
/**
* 下午结束时间 (HH:mm)
* 下午结束时间 (HH:mm)(兼容旧版本)
*/
private String afternoonEndTime;
/**
* 下午时段列表
*/
private List<TimeSlot> afternoonTimeSlots;
/**
* 状态 (0正常 1停用)
*/
@@ -67,4 +88,13 @@ public class XljkConsultationScheduleBatchBo {
* 批次ID,用于标识同一批次创建的预约时段
*/
private String batchId;
/**
* 时段内部类
*/
@Data
public static class TimeSlot {
private String startTime;
private String endTime;
}
}
@@ -14,8 +14,9 @@ public class XljkConsultationScheduleBo {
private Date updateTime;
private Integer deleted;
private Long counselorId;
private String office; // 办公室
private Date scheduleDate;
private String timeSlot; // 0上午 1下午
private String timeSlot; // 0中午 1下午
private String startTime; // 开始时间 HH:mm 格式
private String endTime; // 结束时间 HH:mm 格式
private String status;
@@ -0,0 +1,118 @@
package org.dromara.xljk.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.xljk.domain.XljkConsulter;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
/**
* 心理咨询师配置业务对象 xljk_consulter
*
* @author ruoyi
* @date 2024-01-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = XljkConsulter.class, reverseConvertGenerate = false)
public class XljkConsulterBo extends BaseEntity {
/**
* 主键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;
/**
* 用户昵称
*/
@NotBlank(message = "用户昵称不能为空", groups = { AddGroup.class, EditGroup.class })
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 office;
/**
* 邮箱地址
*/
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;
/**
* 分页大小
*/
private Integer pageSize;
/**
* 页码
*/
private Integer pageNum;
/**
* 搜索值
*/
private String searchValue;
/**
* 创建时间范围
*/
private List<Date> createTimeRange;
}
@@ -0,0 +1,82 @@
package org.dromara.xljk.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 带预约状态的排班数据视图对象
*/
@Data
public class ScheduleWithStatusVo {
/**
* 排班ID
*/
private Long scheduleId;
/**
* 咨询师ID
*/
private Long counselorId;
/**
* 咨询师姓名
*/
private String counselorName;
/**
* 排班日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private String date;
/**
* 时段类型(0中午 1下午 2晚上)
*/
private String timeSlot;
/**
* 开始时间(HH:mm)
*/
private String startTime;
/**
* 结束时间(HH:mm)
*/
private String endTime;
/**
* 时段显示文本(如:中午 08:30-12:00)
*/
private String timeDisplay;
/**
* 是否已预约满
*/
private Boolean isFullyBooked;
/**
* 是否已过期
*/
private Boolean isExpired;
/**
* 可预约数量
*/
private Integer availableSlots;
/**
* 总预约数量
*/
private Integer totalSlots;
// 以下是基础字段,用于避免MyBatis映射错误
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; // 更新时间
}
@@ -30,14 +30,12 @@ public class XljkConsultationAppointmentVo {
private String appointmentTime;
private String appointmentDetailTime; // 具体预约时间段,如"08:30-09:00"
private String reason;
private String consultTheme; // 咨询主题
/**
* 状态(0待咨询 1已完成 2已取消 3未到访)
*/
private Integer status;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date actualVisitTime;
private Integer communicationDuration;
private String consultationContent;
private String counselorSuggestion;
@@ -51,4 +49,14 @@ public class XljkConsultationAppointmentVo {
// 新增院系和专业字段
private String collegeName; // 院系名称
private String majorName; // 专业名称
// 新增心理健康相关字段
private Integer psychologicalHistory; // 心理病史(1-否,2-是)
private String psychologicalHistoryDetail; // 心理病史详情
private String basicSituation; // 基本情况
private String majorLifeEvents; // 重大生活事件
private Integer needReferral; // 是否需要转介(1-不需要,2-需要)
// 本学期到访次数(计算字段,不存储在数据库)
private Integer semesterVisitCount;
}
@@ -18,11 +18,12 @@ public class XljkConsultationScheduleSummaryVo {
private Integer deleted;
private Long counselorId;
private String counselorName; // 咨询师姓名,用于前端显示
private String office; // 办公室,用于前端显示
// 汇总信息
private String dateRange; // 日期范围,如"2025-07-01 至 2025-07-10"
private Boolean morningEnabled; // 是否启用上午时段
private String morningTime; // 上午时间,如"08:30-12:00"
private Boolean morningEnabled; // 是否启用中午时段(字段名保持不变以兼容前端)
private String morningTime; // 中午时间,如"08:30-12:00"
private Boolean afternoonEnabled; // 是否启用下午时段
private String afternoonTime; // 下午时间,如"14:30-17:00"
private String status; // 状态
@@ -15,8 +15,9 @@ public class XljkConsultationScheduleVo {
private Integer deleted;
private Long counselorId;
private String counselorName; // 咨询师姓名,用于前端显示
private String office; // 办公室
private Date scheduleDate;
private String timeSlot; // 0上午 1下午
private String timeSlot; // 0中午 1下午
private String startTime; // 开始时间 HH:mm 格式
private String endTime; // 结束时间 HH:mm 格式
private String status;
@@ -0,0 +1,139 @@
package org.dromara.xljk.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.xljk.domain.XljkConsulter;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 心理咨询师配置视图对象 xljk_consulter
*
* @author ruoyi
* @date 2024-01-01
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = XljkConsulter.class)
public class XljkConsulterVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键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 office;
/**
* 邮箱地址
*/
@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
*/
private Long configBy;
/**
* 配置人姓名
*/
@ExcelProperty(value = "配置人")
private String configByName;
/**
* 租户编号
*/
private String tenantId;
/**
* 创建部门
*/
private Long createDept;
/**
* 创建者ID
*/
private Long createBy;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新者ID
*/
private Long updateBy;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,66 @@
package org.dromara.xljk.mapper;
import org.dromara.xljk.domain.XljkConsulter;
import org.dromara.xljk.domain.vo.XljkConsulterVo;
import org.dromara.xljk.domain.XljkUserWithDeptInfo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 心理咨询师配置Mapper接口
*
* @author ruoyi
* @date 2024-01-01
*/
public interface XljkConsulterMapper extends BaseMapperPlus<XljkConsulter, XljkConsulterVo> {
/**
* 查询心理咨询师配置
*
* @param id 心理咨询师配置主键
* @return 心理咨询师配置
*/
XljkConsulterVo selectXljkConsulterVoById(Long id);
/**
* 查询心理咨询师配置列表
*
* @param xljkConsulter 心理咨询师配置
* @return 心理咨询师配置集合
*/
List<XljkConsulterVo> selectXljkConsulterVoList(XljkConsulter xljkConsulter);
/**
* 根据用户ID检查是否已存在咨询师配置
*
* @param userId 用户ID
* @return 记录数
*/
int checkUserExists(@Param("userId") Long userId);
/**
* 批量启用咨询师
*
* @param ids 咨询师ID列表
* @return 影响行数
*/
int enableConsultersByIds(@Param("ids") List<Long> ids);
/**
* 批量停用咨询师
*
* @param ids 咨询师ID列表
* @return 影响行数
*/
int disableConsultersByIds(@Param("ids") List<Long> ids);
/**
* 查询完整的用户信息(包含部门信息)用于批量添加
*
* @param userIds 用户ID列表
* @return 用户和部门完整信息列表
*/
List<XljkUserWithDeptInfo> selectUsersWithDeptInfo(@Param("userIds") List<Long> userIds);
}
@@ -4,6 +4,7 @@ import org.dromara.xljk.domain.XljkConsultationAppointment;
import org.dromara.xljk.domain.bo.XljkConsultationAppointmentBo;
import org.dromara.xljk.domain.vo.XljkConsultationAppointmentVo;
import org.dromara.xljk.domain.vo.TimeSlotDetailVo;
import org.dromara.xljk.domain.vo.ScheduleWithStatusVo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.List;
@@ -43,7 +44,7 @@ public interface IXljkConsultationAppointmentService {
/**
* 获取可用的详细时段列表(30分钟一个时段)
* @param date 日期
* @param timeSlot 时段类型(0上午 1下午)
* @param timeSlot 时段类型(0中午 1下午)
* @return 详细时段列表
*/
List<TimeSlotDetailVo> getAvailableDetailSlots(Date date, String timeSlot);
@@ -65,15 +66,21 @@ public interface IXljkConsultationAppointmentService {
/**
* 记录咨询结果
* @param id 预约ID
* @param actualVisitTime 实际到访时间
* @param communicationDuration 沟通时长
* @param consultationContent 咨询内容
* @param counselorSuggestion 咨询师建议
* @param status 状态(1已完成/3未到访)
* @param psychologicalHistory 心理病史(1-否,2-是)
* @param psychologicalHistoryDetail 心理病史详情
* @param basicSituation 基本情况
* @param majorLifeEvents 重大生活事件
* @param needReferral 是否需要转介(1-不需要,2-需要)
* @return 是否成功
*/
boolean recordConsultation(Long id, Date actualVisitTime, Integer communicationDuration,
String consultationContent, String counselorSuggestion, String status);
boolean recordConsultation(Long id, Integer communicationDuration,
String consultationContent, String counselorSuggestion, String status,
Integer psychologicalHistory, String psychologicalHistoryDetail,
String basicSituation, String majorLifeEvents, Integer needReferral);
/**
* 获取院系选项
@@ -95,4 +102,12 @@ public interface IXljkConsultationAppointmentService {
* @return 班级选项列表
*/
List<Map<String, Object>> getClassOptions(String collegeName, String majorName);
/**
* 获取日历视图的排班数据(带预约状态)
* @param startDate 开始日期
* @param endDate 结束日期
* @return 排班数据列表
*/
List<ScheduleWithStatusVo> getCalendarSchedules(String startDate, String endDate);
}
@@ -0,0 +1,102 @@
package org.dromara.xljk.service;
import org.dromara.xljk.domain.vo.XljkConsulterVo;
import org.dromara.xljk.domain.bo.XljkConsulterBo;
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 2024-01-01
*/
public interface IXljkConsulterService {
/**
* 查询心理咨询师配置
*
* @param id 主键
* @return 心理咨询师配置
*/
XljkConsulterVo queryById(Long id);
/**
* 查询心理咨询师配置列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 心理咨询师配置集合
*/
TableDataInfo<XljkConsulterVo> queryPageList(XljkConsulterBo bo, PageQuery pageQuery);
/**
* 查询心理咨询师配置列表
*
* @param bo 查询条件
* @return 心理咨询师配置集合
*/
List<XljkConsulterVo> queryList(XljkConsulterBo bo);
/**
* 新增心理咨询师配置
*
* @param bo 心理咨询师配置
* @return 结果
*/
Boolean insertByBo(XljkConsulterBo bo);
/**
* 修改心理咨询师配置
*
* @param bo 心理咨询师配置
* @return 结果
*/
Boolean updateByBo(XljkConsulterBo bo);
/**
* 校验并批量删除心理咨询师配置信息
*
* @param ids 需要删除的主键集合
* @return 结果
*/
Boolean deleteWithValidByIds(Collection<Long> ids);
/**
* 批量添加咨询师
*
* @param userIds 用户ID列表
* @param remark 备注
* @return 结果
*/
Boolean batchAddConsulters(List<Long> userIds, String remark);
/**
* 查询我配置的咨询师列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 咨询师列表
*/
TableDataInfo<XljkConsulterVo> queryMyConfigList(XljkConsulterBo bo, PageQuery pageQuery);
/**
* 批量启用咨询师
*
* @param ids 咨询师ID列表
* @return 结果
*/
Boolean enableConsulters(List<Long> ids);
/**
* 批量停用咨询师
*
* @param ids 咨询师ID列表
* @return 结果
*/
Boolean disableConsulters(List<Long> ids);
}
@@ -10,6 +10,7 @@ import org.dromara.xljk.domain.XljkConsultationSchedule;
import org.dromara.xljk.domain.bo.XljkConsultationAppointmentBo;
import org.dromara.xljk.domain.vo.XljkConsultationAppointmentVo;
import org.dromara.xljk.domain.vo.TimeSlotDetailVo;
import org.dromara.xljk.domain.vo.ScheduleWithStatusVo;
import org.dromara.xljk.mapper.XljkConsultationAppointmentMapper;
import org.dromara.xljk.mapper.XljkConsultationScheduleMapper;
import org.dromara.xljk.service.IXljkConsultationAppointmentService;
@@ -38,6 +39,8 @@ import org.dromara.common.core.utils.SpringUtils;
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 java.time.LocalDate;
import java.time.Month;
@Slf4j
@Service
@@ -71,9 +74,8 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
BeanUtils.copyProperties(entity, vo);
// 记录关键字段
log.info("复制后的VO对象关键字段:status={}, actualVisitTime={}, communicationDuration={}, consultationContent长度={}, counselorSuggestion长度={}",
log.info("复制后的VO对象关键字段:status={}, communicationDuration={}, consultationContent长度={}, counselorSuggestion长度={}",
vo.getStatus(),
vo.getActualVisitTime(),
vo.getCommunicationDuration(),
vo.getConsultationContent() != null ? vo.getConsultationContent().length() : 0,
vo.getCounselorSuggestion() != null ? vo.getCounselorSuggestion().length() : 0);
@@ -128,23 +130,8 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
// 再次检查关键字段
if (vo.getStatus() != null && vo.getStatus() == 1) {
log.info("已到访状态,检查咨询结果相关字段:actualVisitTime={}, communicationDuration={}",
vo.getActualVisitTime(), vo.getCommunicationDuration());
// 如果是已到访状态但实际到访时间为空,尝试从数据库直接查询
if (vo.getActualVisitTime() == null) {
log.warn("已到访状态但实际到访时间为空,尝试从数据库直接查询");
try {
String timeSql = "SELECT actual_visit_time FROM xljk_consultation_appointment WHERE id = " + id;
Date actualVisitTime = jdbcTemplate.queryForObject(timeSql, Date.class);
if (actualVisitTime != null) {
log.info("从数据库查询到实际到访时间: {}", actualVisitTime);
vo.setActualVisitTime(actualVisitTime);
}
} catch (Exception e) {
log.error("查询实际到访时间失败", e);
}
}
log.info("已到访状态,检查咨询结果相关字段:communicationDuration={}",
vo.getCommunicationDuration());
}
log.info("返回预约记录详情: {}", vo);
@@ -238,6 +225,9 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
return resultList.stream().map(row -> {
XljkConsultationAppointmentVo vo = new XljkConsultationAppointmentVo();
// 获取学生ID用于计算本学期到访次数
Long studentId = row.get("student_id") != null ? ((Number) row.get("student_id")).longValue() : null;
// 设置基本字段
vo.setId(((Number) row.get("id")).longValue());
vo.setTenantId((String) row.get("tenant_id"));
@@ -291,11 +281,44 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
vo.setStatus(0); // 默认为待咨询
}
vo.setActualVisitTime(convertToDate(row.get("actual_visit_time")));
vo.setCommunicationDuration(row.get("communication_duration") != null ? ((Number) row.get("communication_duration")).intValue() : null);
vo.setConsultationContent((String) row.get("consultation_content"));
vo.setCounselorSuggestion((String) row.get("counselor_suggestion"));
// 设置咨询主题字段
vo.setConsultTheme((String) row.get("consult_theme"));
// 设置心理健康相关字段
Object psychologicalHistoryObj = row.get("psychological_history");
if (psychologicalHistoryObj != null) {
if (psychologicalHistoryObj instanceof Number) {
vo.setPsychologicalHistory(((Number) psychologicalHistoryObj).intValue());
} else if (psychologicalHistoryObj instanceof String) {
try {
vo.setPsychologicalHistory(Integer.parseInt((String) psychologicalHistoryObj));
} catch (NumberFormatException e) {
vo.setPsychologicalHistory(null);
}
}
}
vo.setPsychologicalHistoryDetail((String) row.get("psychological_history_detail"));
vo.setBasicSituation((String) row.get("basic_situation"));
vo.setMajorLifeEvents((String) row.get("major_life_events"));
Object needReferralObj = row.get("need_referral");
if (needReferralObj != null) {
if (needReferralObj instanceof Number) {
vo.setNeedReferral(((Number) needReferralObj).intValue());
} else if (needReferralObj instanceof String) {
try {
vo.setNeedReferral(Integer.parseInt((String) needReferralObj));
} catch (NumberFormatException e) {
vo.setNeedReferral(null);
}
}
}
// 设置关联字段
vo.setStudentName((String) row.get("student_name"));
vo.setStudentNo((String) row.get("student_no"));
@@ -304,9 +327,68 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
vo.setCollegeName((String) row.get("college_name"));
vo.setMajorName((String) row.get("major_name"));
// 计算本学期到访次数
if (studentId != null) {
vo.setSemesterVisitCount(calculateSemesterVisitCount(studentId));
} else {
vo.setSemesterVisitCount(0);
}
return vo;
}).collect(Collectors.toList());
}
/**
* 计算学生本学期到访次数
* 上学期:3月1日-8月31日
* 下学期:9月1日-2月29日
* @param studentId 学生ID
* @return 本学期到访次数
*/
private int calculateSemesterVisitCount(Long studentId) {
try {
// 获取当前日期
LocalDate now = LocalDate.now();
String startDate, endDate;
// 判断当前处于哪个学期
if (now.getMonthValue() >= 3 && now.getMonthValue() <= 8) {
// 上学期:3月1日-8月31日
startDate = now.getYear() + "-03-01";
endDate = now.getYear() + "-08-31";
} else if (now.getMonthValue() >= 9) {
// 下学期:9月1日-次年2月29日(当年9月到次年2月)
startDate = now.getYear() + "-09-01";
// 计算次年2月的最后一天
int nextYear = now.getYear() + 1;
LocalDate nextFeb = LocalDate.of(nextYear, Month.FEBRUARY, 1);
LocalDate lastDayOfFeb = nextFeb.withDayOfMonth(nextFeb.lengthOfMonth());
endDate = lastDayOfFeb.toString();
} else {
// 1月、2月属于上一学年的下学期
int prevYear = now.getYear() - 1;
startDate = prevYear + "-09-01";
// 当年2月的最后一天
LocalDate currentFeb = LocalDate.of(now.getYear(), Month.FEBRUARY, 1);
LocalDate lastDayOfFeb = currentFeb.withDayOfMonth(currentFeb.lengthOfMonth());
endDate = lastDayOfFeb.toString();
}
// 查询该学生在当前学期内状态为已到访(status=1)的记录数
String sql = "SELECT COUNT(*) FROM xljk_consultation_appointment " +
"WHERE student_id = ? AND status = 1 AND deleted = 0 " +
"AND appointment_date >= ? AND appointment_date <= ?";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class, studentId, startDate, endDate);
log.debug("学生ID: {}, 学期时间段: {} - {}, 到访次数: {}", studentId, startDate, endDate, count);
return count != null ? count : 0;
} catch (Exception e) {
log.error("计算学生本学期到访次数失败,学生ID: {}", studentId, e);
return 0;
}
}
@Override
public boolean insert(XljkConsultationAppointmentBo bo) {
@@ -341,7 +423,7 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
LambdaQueryWrapper<XljkConsultationAppointment> appointmentQueryWrapper = new LambdaQueryWrapper<>();
appointmentQueryWrapper.ge(XljkConsultationAppointment::getAppointmentDate, startDate)
.le(XljkConsultationAppointment::getAppointmentDate, endDate)
.in(XljkConsultationAppointment::getStatus, Arrays.asList("0", "1")); // 待咨询或已完成的
.in(XljkConsultationAppointment::getStatus, Arrays.asList("0", "1", "4")); // 待咨询、已完成或待审核的
List<XljkConsultationAppointment> appointments = mapper.selectList(appointmentQueryWrapper);
@@ -365,9 +447,10 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(schedule.getScheduleDate());
String key = dateStr + "_" + schedule.getId();
// 检查该时段的可用详细时段
// 检查该时段的可用详细时段(用于时间判断,但总容量固定为1)
List<TimeSlotDetailVo> detailSlots = TimeSlotUtils.generateTimeSlots(schedule.getStartTime(), schedule.getEndTime());
int totalDetailSlots = detailSlots.size();
int totalDetailSlots = detailSlots.size(); // 保留用于时间过期判断
int totalSlots = 1; // 每个时段只能预约1个人
int bookedCount = 0;
boolean isExpired = false;
@@ -408,15 +491,11 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
isExpired = false;
}
// 检查已预约的详细时段
// 检查已预约数量(按整个时段计算,不再按详细时段计算)
if (bookedDetailSlots.containsKey(key)) {
List<String> bookedTimes = bookedDetailSlots.get(key);
for (TimeSlotDetailVo slot : detailSlots) {
String detailTime = slot.getStartTime() + "-" + slot.getEndTime();
if (bookedTimes.contains(detailTime)) {
bookedCount++;
}
}
// 只要有任何预约,就算已预约1个(因为每个时段只能预约1个人)
bookedCount = bookedTimes.isEmpty() ? 0 : 1;
}
// 创建时段信息
@@ -427,8 +506,8 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
slot.put("timeSlot", schedule.getTimeSlot());
slot.put("startTime", schedule.getStartTime());
slot.put("endTime", schedule.getEndTime());
slot.put("allBooked", bookedCount >= totalDetailSlots); // 是否全部约满
slot.put("availableCount", totalDetailSlots - bookedCount); // 可用数量
slot.put("allBooked", bookedCount >= totalSlots); // 是否全部约满
slot.put("availableCount", totalSlots - bookedCount); // 可用数量
slot.put("isExpired", isExpired); // 是否已过期
// 查询咨询师姓名
@@ -463,21 +542,34 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
.eq(XljkConsultationSchedule::getTimeSlot, timeSlot)
.eq(XljkConsultationSchedule::getStatus, "0"); // 0表示正常可用
XljkConsultationSchedule schedule = scheduleMapper.selectOne(scheduleQueryWrapper);
// 修改:使用selectList()而不是selectOne(),因为可能有多条记录
List<XljkConsultationSchedule> schedules = scheduleMapper.selectList(scheduleQueryWrapper);
if (schedule == null) {
if (schedules == null || schedules.isEmpty()) {
// 该日期没有排班
return new ArrayList<>();
}
// 2. 生成30分钟时段列表
List<TimeSlotDetailVo> timeSlots = TimeSlotUtils.generateTimeSlots(schedule.getStartTime(), schedule.getEndTime());
// 2. 合并所有排班的时段,生成完整的30分钟时段列表
List<TimeSlotDetailVo> allTimeSlots = new ArrayList<>();
Set<String> uniqueTimeSlots = new LinkedHashSet<>(); // 用于去重,保持顺序
// 3. 查询已预约的时段
for (XljkConsultationSchedule schedule : schedules) {
List<TimeSlotDetailVo> timeSlots = TimeSlotUtils.generateTimeSlots(schedule.getStartTime(), schedule.getEndTime());
for (TimeSlotDetailVo slot : timeSlots) {
String slotKey = slot.getStartTime() + "-" + slot.getEndTime();
if (uniqueTimeSlots.add(slotKey)) {
allTimeSlots.add(slot);
}
}
}
// 3. 查询已预约的时段(查询所有相关排班的预约)
List<Long> scheduleIds = schedules.stream().map(XljkConsultationSchedule::getId).collect(Collectors.toList());
LambdaQueryWrapper<XljkConsultationAppointment> appointmentQueryWrapper = new LambdaQueryWrapper<>();
appointmentQueryWrapper.eq(XljkConsultationAppointment::getAppointmentDate, date)
.eq(XljkConsultationAppointment::getScheduleId, schedule.getId()) // 按排班ID过滤,确保只查询当前时段的预约
.in(XljkConsultationAppointment::getStatus, Arrays.asList("0", "1")); // 待咨询或已完成的
.in(XljkConsultationAppointment::getScheduleId, scheduleIds) // 按排班ID列表过滤
.in(XljkConsultationAppointment::getStatus, Arrays.asList("0", "1", "4")); // 待咨询、已完成或待审核的
List<XljkConsultationAppointment> appointments = mapper.selectList(appointmentQueryWrapper);
@@ -491,7 +583,7 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
for (TimeSlotDetailVo slot : timeSlots) {
for (TimeSlotDetailVo slot : allTimeSlots) {
String detailTime = slot.getStartTime() + "-" + slot.getEndTime();
// 检查是否已预约
@@ -532,7 +624,7 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
}
}
return timeSlots;
return allTimeSlots;
}
@Override
@@ -582,7 +674,7 @@ public class XljkConsultationAppointmentServiceImpl implements IXljkConsultation
XljkConsultationAppointment entity = new XljkConsultationAppointment();
BeanUtils.copyProperties(bo, entity);
entity.setStudentId(studentId); // 使用验证后的学生ID
entity.setStatus("0"); // 0表示待咨询
entity.setStatus("4"); // 4表示待审核
entity.setCreateTime(new Date()); // 设置创建时间
return mapper.insert(entity) > 0;
}
@@ -645,10 +737,6 @@ if (statusObj != null) {
vo.setScheduleId(((Number) row.get("schedule_id")).longValue());
}
if (row.get("actual_visit_time") != null) {
vo.setActualVisitTime(convertToDate(row.get("actual_visit_time")));
}
if (row.get("communication_duration") != null) {
vo.setCommunicationDuration(((Number) row.get("communication_duration")).intValue());
}
@@ -691,9 +779,11 @@ if (statusObj != null) {
@Override
@Transactional
public boolean recordConsultation(Long id, Date actualVisitTime, Integer communicationDuration,
String consultationContent, String counselorSuggestion, String status) {
log.info("记录咨询结果,ID: {}, 状态: {}, 实际到访时间: {}", id, status, actualVisitTime);
public boolean recordConsultation(Long id, Integer communicationDuration,
String consultationContent, String counselorSuggestion, String status,
Integer psychologicalHistory, String psychologicalHistoryDetail,
String basicSituation, String majorLifeEvents, Integer needReferral) {
log.info("记录咨询结果,ID: {}, 状态: {}, 心理病史: {}, 转介: {}", id, status, psychologicalHistory, needReferral);
XljkConsultationAppointment entity = mapper.selectById(id);
if (entity == null) {
@@ -717,29 +807,42 @@ if (statusObj != null) {
// 只有在已到访状态(1)时,才设置咨询结果相关字段
if (statusInt == 1) {
if (actualVisitTime == null) {
log.warn("实际到访时间为空,使用当前时间");
actualVisitTime = new Date();
}
entity.setActualVisitTime(actualVisitTime);
entity.setCommunicationDuration(communicationDuration != null ? communicationDuration : 30);
entity.setConsultationContent(consultationContent);
entity.setCounselorSuggestion(counselorSuggestion);
log.info("设置咨询结果相关字段:实际到访时间={}, 沟通时长={}, 咨询内容长度={}, 咨询师建议长度={}",
actualVisitTime,
// 设置心理病史相关字段
entity.setPsychologicalHistory(psychologicalHistory);
entity.setPsychologicalHistoryDetail(psychologicalHistoryDetail);
entity.setBasicSituation(basicSituation);
entity.setMajorLifeEvents(majorLifeEvents);
entity.setNeedReferral(needReferral);
log.info("设置咨询结果相关字段:沟通时长={}, 咨询内容长度={}, 咨询师建议长度={}",
communicationDuration,
consultationContent != null ? consultationContent.length() : 0,
counselorSuggestion != null ? counselorSuggestion.length() : 0);
log.info("设置心理病史相关字段:心理病史={}, 心理病史详情长度={}, 基本情况长度={}, 重大生活事件长度={}, 转介={}",
psychologicalHistory,
psychologicalHistoryDetail != null ? psychologicalHistoryDetail.length() : 0,
basicSituation != null ? basicSituation.length() : 0,
majorLifeEvents != null ? majorLifeEvents.length() : 0,
needReferral);
} else {
// 未到访状态,清空咨询结果相关字段
log.info("非已到访状态,清空咨询结果相关字段");
entity.setActualVisitTime(null);
entity.setCommunicationDuration(0);
entity.setConsultationContent("");
entity.setCounselorSuggestion("");
// 清空心理病史相关字段
entity.setPsychologicalHistory(null);
entity.setPsychologicalHistoryDetail("");
entity.setBasicSituation("");
entity.setMajorLifeEvents("");
entity.setNeedReferral(null);
// 如果是未到访状态(3),创建未到访记录
if (statusInt == 3) {
log.info("记录未到访,创建未到访记录");
@@ -1323,10 +1426,19 @@ if (statusObj != null) {
public TableDataInfo<XljkConsultationAppointmentVo> selectPageList(XljkConsultationAppointmentBo bo, PageQuery pageQuery) {
log.info("分页查询预约记录列表,页码: {}, 每页数量: {}", pageQuery.getPageNum(), pageQuery.getPageSize());
// 将变量定义移到try块外,确保catch块可以访问
StringBuilder sqlBuilder = new StringBuilder();
List<Object> params = new ArrayList<>();
try {
// 构建查询条件
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("SELECT a.*, s.name AS student_name, s.student_no, ");
sqlBuilder.append("SELECT a.id, a.tenant_id, a.student_id, a.counselor_id, a.schedule_id, ");
sqlBuilder.append("a.appointment_date, a.appointment_time, a.appointment_detail_time, ");
sqlBuilder.append("a.reason, a.status, a.communication_duration, a.consultation_content, ");
sqlBuilder.append("a.counselor_suggestion, a.psychological_history, a.psychological_history_detail, ");
sqlBuilder.append("a.basic_situation, a.major_life_events, a.need_referral, ");
sqlBuilder.append("a.create_by, a.create_time, a.update_by, a.update_time, ");
sqlBuilder.append("s.name AS student_name, s.student_no, ");
sqlBuilder.append("s.college_name, s.major_name, s.class_name, ");
sqlBuilder.append("u.nick_name AS counselor_name ");
sqlBuilder.append("FROM xljk_consultation_appointment a ");
@@ -1334,8 +1446,6 @@ if (statusObj != null) {
sqlBuilder.append("LEFT JOIN sys_user u ON a.counselor_id = u.user_id ");
sqlBuilder.append("WHERE a.deleted = 0 ");
List<Object> params = new ArrayList<>();
// 添加查询条件
if (bo.getStudentName() != null && !bo.getStudentName().isEmpty()) {
sqlBuilder.append("AND s.name LIKE ? ");
@@ -1457,10 +1567,6 @@ if (statusObj != null) {
vo.setScheduleId(((Number) row.get("schedule_id")).longValue());
}
if (row.get("actual_visit_time") != null) {
vo.setActualVisitTime(convertToDate(row.get("actual_visit_time")));
}
if (row.get("communication_duration") != null) {
vo.setCommunicationDuration(((Number) row.get("communication_duration")).intValue());
}
@@ -1468,6 +1574,37 @@ if (statusObj != null) {
vo.setConsultationContent((String) row.get("consultation_content"));
vo.setCounselorSuggestion((String) row.get("counselor_suggestion"));
// 新增字段映射
Object psychologicalHistoryObj = row.get("psychological_history");
if (psychologicalHistoryObj != null) {
if (psychologicalHistoryObj instanceof Number) {
vo.setPsychologicalHistory(((Number) psychologicalHistoryObj).intValue());
} else if (psychologicalHistoryObj instanceof String) {
try {
vo.setPsychologicalHistory(Integer.parseInt((String) psychologicalHistoryObj));
} catch (NumberFormatException e) {
vo.setPsychologicalHistory(null);
}
}
}
vo.setPsychologicalHistoryDetail((String) row.get("psychological_history_detail"));
vo.setBasicSituation((String) row.get("basic_situation"));
vo.setMajorLifeEvents((String) row.get("major_life_events"));
Object needReferralObj = row.get("need_referral");
if (needReferralObj != null) {
if (needReferralObj instanceof Number) {
vo.setNeedReferral(((Number) needReferralObj).intValue());
} else if (needReferralObj instanceof String) {
try {
vo.setNeedReferral(Integer.parseInt((String) needReferralObj));
} catch (NumberFormatException e) {
vo.setNeedReferral(null);
}
}
}
// 其他通用字段
if (row.get("create_by") != null) {
vo.setCreateBy(((Number) row.get("create_by")).longValue());
@@ -1485,6 +1622,13 @@ if (statusObj != null) {
vo.setUpdateTime(convertToDate(row.get("update_time")));
}
// 计算本学期到访次数
if (vo.getStudentId() != null) {
vo.setSemesterVisitCount(calculateSemesterVisitCount(vo.getStudentId()));
} else {
vo.setSemesterVisitCount(0);
}
result.add(vo);
}
@@ -1496,12 +1640,194 @@ if (statusObj != null) {
return tableDataInfo;
} catch (Exception e) {
log.error("分页查询预约记录列表异常", e);
log.error("SQL: {}", sqlBuilder.toString());
log.error("参数: {}", params);
TableDataInfo<XljkConsultationAppointmentVo> emptyResult = new TableDataInfo<>();
emptyResult.setRows(new ArrayList<>());
emptyResult.setTotal(0);
emptyResult.setCode(200);
emptyResult.setMsg("查询失败");
emptyResult.setMsg("查询失败: " + e.getMessage());
return emptyResult;
}
}
@Override
public List<ScheduleWithStatusVo> getCalendarSchedules(String startDate, String endDate) {
try {
// 1. 查询指定日期范围内的所有排班数据
LambdaQueryWrapper<XljkConsultationSchedule> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.ge(XljkConsultationSchedule::getScheduleDate, startDate)
.le(XljkConsultationSchedule::getScheduleDate, endDate)
.eq(XljkConsultationSchedule::getStatus, "0") // 0表示正常可用
.orderByAsc(XljkConsultationSchedule::getScheduleDate)
.orderByAsc(XljkConsultationSchedule::getTimeSlot)
.orderByAsc(XljkConsultationSchedule::getStartTime);
List<XljkConsultationSchedule> schedules = scheduleMapper.selectList(queryWrapper);
if (schedules.isEmpty()) {
return new ArrayList<>();
}
// 2. 查询该日期范围内的所有预约记录
List<Long> scheduleIds = schedules.stream().map(XljkConsultationSchedule::getId).collect(Collectors.toList());
LambdaQueryWrapper<XljkConsultationAppointment> appointmentQueryWrapper = new LambdaQueryWrapper<>();
appointmentQueryWrapper.in(XljkConsultationAppointment::getScheduleId, scheduleIds)
.in(XljkConsultationAppointment::getStatus, Arrays.asList("0", "1", "4")); // 待咨询、已完成或待审核的
List<XljkConsultationAppointment> appointments = mapper.selectList(appointmentQueryWrapper);
// 3. 构建排班ID到预约记录的映射
Map<Long, List<XljkConsultationAppointment>> appointmentMap = appointments.stream()
.collect(Collectors.groupingBy(XljkConsultationAppointment::getScheduleId));
// 4. 获取咨询师信息映射
Map<Long, String> counselorMap = getCounselorNameMap();
// 5. 获取当前时间
Date now = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
// 6. 构建结果列表
List<ScheduleWithStatusVo> result = new ArrayList<>();
for (XljkConsultationSchedule schedule : schedules) {
ScheduleWithStatusVo vo = new ScheduleWithStatusVo();
// 基本信息
vo.setScheduleId(schedule.getId());
vo.setCounselorId(schedule.getCounselorId());
vo.setCounselorName(counselorMap.get(schedule.getCounselorId()));
vo.setDate(dateFormat.format(schedule.getScheduleDate()));
vo.setTimeSlot(schedule.getTimeSlot());
vo.setStartTime(schedule.getStartTime());
vo.setEndTime(schedule.getEndTime());
// 构建时段显示文本
String timeSlotText = getTimeSlotText(schedule.getTimeSlot());
vo.setTimeDisplay(timeSlotText + " " + schedule.getStartTime() + "-" + schedule.getEndTime());
// 计算预约状态
List<XljkConsultationAppointment> scheduleAppointments = appointmentMap.get(schedule.getId());
int bookedCount = scheduleAppointments != null ? scheduleAppointments.size() : 0;
// 每个时段只能预约1个人(无论时段长度)
// 生成30分钟时段用于详细时段显示,但总预约数量固定为1
List<TimeSlotDetailVo> timeSlots = TimeSlotUtils.generateTimeSlots(schedule.getStartTime(), schedule.getEndTime());
int totalSlots = 1; // 每个时段只能预约1个人
vo.setTotalSlots(totalSlots);
vo.setAvailableSlots(Math.max(0, totalSlots - bookedCount));
vo.setIsFullyBooked(bookedCount >= totalSlots);
// 检查是否已过期
boolean isExpired = false;
try {
Date scheduleDate = schedule.getScheduleDate();
Calendar calendar = Calendar.getInstance();
calendar.setTime(scheduleDate);
// 设置具体的结束时间
String[] endTimeParts = schedule.getEndTime().split(":");
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(endTimeParts[0]));
calendar.set(Calendar.MINUTE, Integer.parseInt(endTimeParts[1]));
calendar.set(Calendar.SECOND, 0);
Date scheduleEndTime = calendar.getTime();
isExpired = scheduleEndTime.before(now);
} catch (Exception e) {
log.warn("检查时段过期状态失败: " + schedule.getId(), e);
}
vo.setIsExpired(isExpired);
// 设置基础字段
vo.setCreateBy(schedule.getCreateBy());
vo.setCreateTime(schedule.getCreateTime());
vo.setUpdateBy(schedule.getUpdateBy());
vo.setUpdateTime(schedule.getUpdateTime());
result.add(vo);
}
return result;
} catch (Exception e) {
log.error("获取日历排班数据失败", e);
return new ArrayList<>();
}
}
/**
* 获取时段类型文本
*/
private String getTimeSlotText(String timeSlot) {
switch (timeSlot) {
case "0": return "中午";
case "1": return "下午";
case "2": return "晚上";
default: return "未知";
}
}
/**
* 获取咨询师名称映射
*/
private Map<Long, String> getCounselorNameMap() {
try {
Map<Long, String> counselorMap = new HashMap<>();
// 1. 查询所有已创建排班的咨询师ID
LambdaQueryWrapper<XljkConsultationSchedule> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(XljkConsultationSchedule::getCounselorId)
.eq(XljkConsultationSchedule::getStatus, "0") // 只查询正常状态的
.groupBy(XljkConsultationSchedule::getCounselorId); // 去重
List<XljkConsultationSchedule> schedules = scheduleMapper.selectList(queryWrapper);
if (schedules.isEmpty()) {
return counselorMap;
}
// 2. 提取咨询师ID列表
List<Long> counselorIds = schedules.stream()
.map(XljkConsultationSchedule::getCounselorId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (counselorIds.isEmpty()) {
return counselorMap;
}
// 3. 批量查询咨询师信息
List<SysUserVo> counselors = userService.selectUserByIds(counselorIds, null);
// 4. 构建ID到姓名的映射
counselorMap = counselors.stream()
.filter(user -> user.getUserId() != null)
.collect(Collectors.toMap(
SysUserVo::getUserId,
user -> {
// 优先使用昵称,如果没有昵称则使用用户名
String name = user.getNickName();
if (StringUtils.isBlank(name)) {
name = user.getUserName();
}
if (StringUtils.isBlank(name)) {
name = "未知咨询师";
}
return name;
},
(existing, replacement) -> existing // 如果ID重复,保留第一个
));
log.info("成功加载咨询师信息映射,共{}个咨询师", counselorMap.size());
return counselorMap;
} catch (Exception e) {
log.error("获取咨询师信息失败", e);
return new HashMap<>();
}
}
}
@@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -26,6 +27,7 @@ import java.util.UUID;
import java.util.stream.Collectors;
import java.util.LinkedHashMap;
@Slf4j
@Service
public class XljkConsultationScheduleServiceImpl implements IXljkConsultationScheduleService {
@Autowired
@@ -43,11 +45,16 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
// 查询咨询师姓名
if (entity.getCounselorId() != null) {
String sql = "SELECT nick_name FROM sys_user WHERE user_id = ?";
List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, entity.getCounselorId());
if (!result.isEmpty()) {
vo.setCounselorName((String) result.get(0).get("nick_name"));
try {
String counselorName = jdbcTemplate.queryForObject(sql, String.class, entity.getCounselorId());
vo.setCounselorName(counselorName);
} catch (Exception e) {
log.error("查询咨询师姓名失败,counselorId: {}", entity.getCounselorId(), e);
}
}
// office字段直接从排班记录中获取
vo.setOffice(entity.getOffice());
return vo;
}
@@ -65,11 +72,15 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
// 查询咨询师姓名
if (entity.getCounselorId() != null) {
String sql = "SELECT nick_name FROM sys_user WHERE user_id = ?";
List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, entity.getCounselorId());
if (!result.isEmpty()) {
vo.setCounselorName((String) result.get(0).get("nick_name"));
try {
String counselorName = jdbcTemplate.queryForObject(sql, String.class, entity.getCounselorId());
vo.setCounselorName(counselorName);
} catch (Exception e) {
log.error("查询咨询师姓名失败,counselorId: {}", entity.getCounselorId(), e);
}
}
// office字段直接从排班记录中获取
vo.setOffice(entity.getOffice());
return vo;
}).collect(Collectors.toList());
}
@@ -87,6 +98,11 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
queryWrapper.eq(XljkConsultationSchedule::getCounselorId, bo.getCounselorId());
}
// 添加办公室筛选条件
if (bo.getOffice() != null && !bo.getOffice().isEmpty()) {
queryWrapper.like(XljkConsultationSchedule::getOffice, bo.getOffice());
}
// 按创建时间倒序查询所有记录
queryWrapper.orderByDesc(XljkConsultationSchedule::getCreateTime);
@@ -144,12 +160,17 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
// 查询咨询师姓名
if (batchGroup.getCounselorId() != null) {
String sql = "SELECT nick_name FROM sys_user WHERE user_id = ?";
List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, batchGroup.getCounselorId());
if (!result.isEmpty()) {
summary.setCounselorName((String) result.get(0).get("nick_name"));
try {
String counselorName = jdbcTemplate.queryForObject(sql, String.class, batchGroup.getCounselorId());
summary.setCounselorName(counselorName);
} catch (Exception e) {
log.error("查询咨询师姓名失败,counselorId: {}", batchGroup.getCounselorId(), e);
}
}
// office字段直接从排班记录中获取
summary.setOffice(batchGroup.getOffice());
// 确保创建时间不为空
if (summary.getCreateTime() == null) {
summary.setCreateTime(new Date());
@@ -226,12 +247,17 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
// 查询咨询师姓名
if (firstSchedule.getCounselorId() != null) {
String sql = "SELECT nick_name FROM sys_user WHERE user_id = ?";
List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, firstSchedule.getCounselorId());
if (!result.isEmpty()) {
summary.setCounselorName((String) result.get(0).get("nick_name"));
try {
String counselorName = jdbcTemplate.queryForObject(sql, String.class, firstSchedule.getCounselorId());
summary.setCounselorName(counselorName);
} catch (Exception e) {
log.error("查询咨询师姓名失败,counselorId: {}", firstSchedule.getCounselorId(), e);
}
}
// office字段直接从排班记录中获取
summary.setOffice(firstSchedule.getOffice());
summaryList.add(summary);
}
}
@@ -283,9 +309,12 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
return 0;
}
// 检查是否至少启用了一个时段
if ((batchBo.getMorningEnabled() == null || !batchBo.getMorningEnabled()) &&
(batchBo.getAfternoonEnabled() == null || !batchBo.getAfternoonEnabled())) {
// 检查是否至少启用了一个时段(兼容新旧版本)
boolean hasMorning = (batchBo.getMorningEnabled() != null && batchBo.getMorningEnabled());
boolean hasNoon = (batchBo.getNoonEnabled() != null && batchBo.getNoonEnabled());
boolean hasAfternoon = (batchBo.getAfternoonEnabled() != null && batchBo.getAfternoonEnabled());
if (!hasMorning && !hasNoon && !hasAfternoon) {
return 0;
}
@@ -321,12 +350,13 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
// 如果是周末且设置了跳过周末,则跳过
if (!(isWeekend && Boolean.TRUE.equals(batchBo.getSkipWeekends()))) {
// 创建上午时段
// 创建中午时段(字段名保持morning以兼容前端)
if (Boolean.TRUE.equals(batchBo.getMorningEnabled())) {
XljkConsultationSchedule morningSchedule = new XljkConsultationSchedule();
morningSchedule.setCounselorId(batchBo.getCounselorId());
morningSchedule.setOffice(batchBo.getOffice());
morningSchedule.setScheduleDate(currentDate);
morningSchedule.setTimeSlot("0"); // 上午
morningSchedule.setTimeSlot("0"); // 中午
morningSchedule.setStartTime(batchBo.getMorningStartTime());
morningSchedule.setEndTime(batchBo.getMorningEndTime());
morningSchedule.setStatus(batchBo.getStatus());
@@ -335,18 +365,54 @@ public class XljkConsultationScheduleServiceImpl implements IXljkConsultationSch
schedules.add(morningSchedule);
}
// 创建下午时段
// 创建中午时段(新版本)
if (Boolean.TRUE.equals(batchBo.getNoonEnabled()) && batchBo.getNoonTimeSlots() != null) {
for (XljkConsultationScheduleBatchBo.TimeSlot timeSlot : batchBo.getNoonTimeSlots()) {
XljkConsultationSchedule noonSchedule = new XljkConsultationSchedule();
noonSchedule.setCounselorId(batchBo.getCounselorId());
noonSchedule.setOffice(batchBo.getOffice());
noonSchedule.setScheduleDate(currentDate);
noonSchedule.setTimeSlot("0"); // 中午时段
noonSchedule.setStartTime(timeSlot.getStartTime());
noonSchedule.setEndTime(timeSlot.getEndTime());
noonSchedule.setStatus(batchBo.getStatus());
noonSchedule.setBatchId(batchId); // 设置批次ID
noonSchedule.setCreateTime(now); // 设置创建时间
schedules.add(noonSchedule);
}
}
// 创建下午时段(兼容新旧版本)
if (Boolean.TRUE.equals(batchBo.getAfternoonEnabled())) {
XljkConsultationSchedule afternoonSchedule = new XljkConsultationSchedule();
afternoonSchedule.setCounselorId(batchBo.getCounselorId());
afternoonSchedule.setScheduleDate(currentDate);
afternoonSchedule.setTimeSlot("1"); // 下午
afternoonSchedule.setStartTime(batchBo.getAfternoonStartTime());
afternoonSchedule.setEndTime(batchBo.getAfternoonEndTime());
afternoonSchedule.setStatus(batchBo.getStatus());
afternoonSchedule.setBatchId(batchId); // 设置批次ID
afternoonSchedule.setCreateTime(now); // 设置创建时间
schedules.add(afternoonSchedule);
if (batchBo.getAfternoonTimeSlots() != null && !batchBo.getAfternoonTimeSlots().isEmpty()) {
// 新版本:支持多时段
for (XljkConsultationScheduleBatchBo.TimeSlot timeSlot : batchBo.getAfternoonTimeSlots()) {
XljkConsultationSchedule afternoonSchedule = new XljkConsultationSchedule();
afternoonSchedule.setCounselorId(batchBo.getCounselorId());
afternoonSchedule.setOffice(batchBo.getOffice());
afternoonSchedule.setScheduleDate(currentDate);
afternoonSchedule.setTimeSlot("1"); // 下午
afternoonSchedule.setStartTime(timeSlot.getStartTime());
afternoonSchedule.setEndTime(timeSlot.getEndTime());
afternoonSchedule.setStatus(batchBo.getStatus());
afternoonSchedule.setBatchId(batchId); // 设置批次ID
afternoonSchedule.setCreateTime(now); // 设置创建时间
schedules.add(afternoonSchedule);
}
} else if (batchBo.getAfternoonStartTime() != null && batchBo.getAfternoonEndTime() != null) {
// 旧版本:单时段
XljkConsultationSchedule afternoonSchedule = new XljkConsultationSchedule();
afternoonSchedule.setCounselorId(batchBo.getCounselorId());
afternoonSchedule.setOffice(batchBo.getOffice());
afternoonSchedule.setScheduleDate(currentDate);
afternoonSchedule.setTimeSlot("1"); // 下午
afternoonSchedule.setStartTime(batchBo.getAfternoonStartTime());
afternoonSchedule.setEndTime(batchBo.getAfternoonEndTime());
afternoonSchedule.setStatus(batchBo.getStatus());
afternoonSchedule.setBatchId(batchId); // 设置批次ID
afternoonSchedule.setCreateTime(now); // 设置创建时间
schedules.add(afternoonSchedule);
}
}
}
@@ -0,0 +1,329 @@
package org.dromara.xljk.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.dromara.xljk.domain.bo.XljkConsulterBo;
import org.dromara.xljk.domain.vo.XljkConsulterVo;
import org.dromara.xljk.domain.XljkConsulter;
import org.dromara.xljk.domain.XljkUserWithDeptInfo;
import org.dromara.xljk.mapper.XljkConsulterMapper;
import org.dromara.xljk.service.IXljkConsulterService;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.service.ISysRoleService;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.domain.vo.SysRoleVo;
import org.dromara.system.domain.SysRole;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.bo.SysRoleBo;
import org.dromara.system.mapper.SysUserRoleMapper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.core.exception.ServiceException;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.ArrayList;
import java.util.stream.Collectors;
/**
* 心理咨询师配置Service业务层处理
*
* @author ruoyi
* @date 2024-01-01
*/
@RequiredArgsConstructor
@Service
public class XljkConsulterServiceImpl implements IXljkConsulterService {
private final XljkConsulterMapper baseMapper;
private final ISysUserService sysUserService;
private final ISysRoleService sysRoleService;
private final SysUserRoleMapper sysUserRoleMapper;
/**
* 查询心理咨询师配置
*/
@Override
public XljkConsulterVo queryById(Long id) {
XljkConsulter entity = baseMapper.selectById(id);
if (entity == null) {
return null;
}
return MapstructUtils.convert(entity, XljkConsulterVo.class);
}
/**
* 查询心理咨询师配置列表
*/
@Override
public TableDataInfo<XljkConsulterVo> queryPageList(XljkConsulterBo bo, PageQuery pageQuery) {
// 直接使用查询条件构建器
LambdaQueryWrapper<XljkConsulter> lqw = buildQueryWrapper(bo);
Page<XljkConsulter> entityPage = baseMapper.selectPage(pageQuery.build(), lqw);
// 转换为 VO 对象
List<XljkConsulterVo> voList = new ArrayList<>();
for (XljkConsulter entity : entityPage.getRecords()) {
XljkConsulterVo vo = MapstructUtils.convert(entity, XljkConsulterVo.class);
voList.add(vo);
}
// 构建分页结果
Page<XljkConsulterVo> voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
voPage.setRecords(voList);
return TableDataInfo.build(voPage);
}
/**
* 查询心理咨询师配置列表
*/
@Override
public List<XljkConsulterVo> queryList(XljkConsulterBo bo) {
LambdaQueryWrapper<XljkConsulter> lqw = buildQueryWrapper(bo);
List<XljkConsulter> entityList = baseMapper.selectList(lqw);
// 转换为 VO 对象
List<XljkConsulterVo> voList = new ArrayList<>();
for (XljkConsulter entity : entityList) {
XljkConsulterVo vo = MapstructUtils.convert(entity, XljkConsulterVo.class);
voList.add(vo);
}
return voList;
}
private LambdaQueryWrapper<XljkConsulter> buildQueryWrapper(XljkConsulterBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<XljkConsulter> lqw = Wrappers.<XljkConsulter>lambdaQuery();
lqw.eq(bo.getUserId() != null, XljkConsulter::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getUserName()), XljkConsulter::getUserName, bo.getUserName());
lqw.like(StringUtils.isNotBlank(bo.getNickName()), XljkConsulter::getNickName, bo.getNickName());
lqw.eq(bo.getDeptId() != null, XljkConsulter::getDeptId, bo.getDeptId());
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), XljkConsulter::getStatus, bo.getStatus());
lqw.eq(bo.getConfigBy() != null, XljkConsulter::getConfigBy, bo.getConfigBy());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
XljkConsulter::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
lqw.orderByDesc(XljkConsulter::getCreateTime);
return lqw;
}
/**
* 新增心理咨询师配置
*/
@Override
public Boolean insertByBo(XljkConsulterBo bo) {
XljkConsulter add = MapstructUtils.convert(bo, XljkConsulter.class);
validEntityBeforeSave(add);
// 设置配置人信息
Long currentUserId = LoginHelper.getUserId();
add.setConfigBy(currentUserId);
// 获取当前用户信息设置配置人姓名
try {
SysUserVo currentUser = sysUserService.selectUserById(currentUserId);
if (currentUser != null) {
add.setConfigByName(currentUser.getUserName());
}
} catch (Exception e) {
// 如果获取失败,使用默认值
add.setConfigByName("系统管理员");
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改心理咨询师配置
*/
@Override
public Boolean updateByBo(XljkConsulterBo bo) {
XljkConsulter update = MapstructUtils.convert(bo, XljkConsulter.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(XljkConsulter entity) {
if (entity.getUserId() != null) {
// 检查用户是否已存在(新增时或修改用户ID时)
if (entity.getId() == null) {
// 新增时检查
int count = baseMapper.checkUserExists(entity.getUserId());
if (count > 0) {
throw new ServiceException("该用户已配置为咨询师,不能重复添加");
}
}
}
}
/**
* 批量删除心理咨询师配置
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids) {
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 批量添加咨询师
*/
@Override
public Boolean batchAddConsulters(List<Long> userIds, String remark) {
if (userIds == null || userIds.isEmpty()) {
throw new ServiceException("用户ID列表不能为空");
}
Long currentUserId = LoginHelper.getUserId();
String currentUserName = "系统管理员";
// 获取当前用户信息
try {
SysUserVo currentUser = sysUserService.selectUserById(currentUserId);
if (currentUser != null) {
currentUserName = currentUser.getUserName();
}
} catch (Exception e) {
// 使用默认值
}
// 1. 获取完整的用户信息(包含部门信息)
List<XljkUserWithDeptInfo> users = baseMapper.selectUsersWithDeptInfo(userIds);
if (users.isEmpty()) {
throw new ServiceException("没有找到有效的用户信息");
}
// 2. 过滤已存在的咨询师
List<Long> existingUserIds = baseMapper.selectList(Wrappers.<XljkConsulter>lambdaQuery()
.in(XljkConsulter::getUserId, userIds))
.stream().map(XljkConsulter::getUserId).collect(Collectors.toList());
// 3. 构建新增咨询师列表
List<XljkConsulter> consulterList = new ArrayList<>();
for (XljkUserWithDeptInfo user : users) {
if (!existingUserIds.contains(user.getUserId())) {
XljkConsulter consulter = new XljkConsulter();
consulter.setUserId(user.getUserId());
consulter.setUserName(user.getUserName());
consulter.setNickName(user.getNickName());
consulter.setDeptId(user.getDeptId());
consulter.setDeptName(user.getDeptName() != null ? user.getDeptName() : "");
consulter.setPhoneNumber(user.getPhonenumber());
consulter.setEmail(user.getEmail());
consulter.setStatus("1"); // 默认正常状态
consulter.setRemark(remark);
consulter.setConfigBy(currentUserId);
consulter.setConfigByName(currentUserName);
consulterList.add(consulter);
}
}
// 4. 批量保存
if (consulterList.isEmpty()) {
throw new ServiceException("所有用户都已存在,无需重复添加");
}
boolean result = baseMapper.insertBatch(consulterList);
// 5. 为新添加的咨询师自动分配 xljk_consultant 角色
if (result) {
assignConsultantRole(consulterList);
}
return result;
}
/**
* 为咨询师分配 xljk_consultant 角色
*/
private void assignConsultantRole(List<XljkConsulter> consulterList) {
try {
// 1. 根据角色键查询角色信息
SysRoleBo roleBo = new SysRoleBo();
roleBo.setRoleKey("xljk_consultant");
List<SysRoleVo> roleList = sysRoleService.selectRoleList(roleBo);
if (roleList == null || roleList.isEmpty()) {
throw new ServiceException("未找到角色键为 'xljk_consultant' 的角色,请联系管理员创建该角色");
}
Long roleId = roleList.get(0).getRoleId();
// 2. 为每个用户分配角色
List<SysUserRole> userRoleList = new ArrayList<>();
for (XljkConsulter consulter : consulterList) {
// 检查用户是否已有该角色
Long count = sysUserRoleMapper.selectCount(
Wrappers.<SysUserRole>lambdaQuery()
.eq(SysUserRole::getUserId, consulter.getUserId())
.eq(SysUserRole::getRoleId, roleId)
);
// 如果用户还没有该角色,则添加
if (count == 0) {
SysUserRole userRole = new SysUserRole();
userRole.setUserId(consulter.getUserId());
userRole.setRoleId(roleId);
userRoleList.add(userRole);
}
}
// 3. 批量插入用户角色关联
if (!userRoleList.isEmpty()) {
sysUserRoleMapper.insertBatch(userRoleList);
System.out.println("成功为 " + userRoleList.size() + " 个用户分配了 'xljk_consultant' 角色");
}
} catch (Exception e) {
// 记录错误但不影响主流程
System.err.println("为咨询师分配角色失败: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 查询我配置的咨询师列表
*/
@Override
public TableDataInfo<XljkConsulterVo> queryMyConfigList(XljkConsulterBo bo, PageQuery pageQuery) {
bo.setConfigBy(LoginHelper.getUserId());
return queryPageList(bo, pageQuery);
}
/**
* 批量启用咨询师
*/
@Override
public Boolean enableConsulters(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return false;
}
return baseMapper.enableConsultersByIds(ids) > 0;
}
/**
* 批量停用咨询师
*/
@Override
public Boolean disableConsulters(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return false;
}
return baseMapper.disableConsultersByIds(ids) > 0;
}
}
@@ -0,0 +1,98 @@
<?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.xljk.mapper.XljkConsulterMapper">
<resultMap type="org.dromara.xljk.domain.vo.XljkConsulterVo" id="XljkConsulterVoResult">
<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="office" column="office"/>
<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="tenantId" column="tenant_id"/>
<result property="createDept" column="create_dept"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<sql id="selectXljkConsulterVo">
select id, user_id, user_name, nick_name, dept_id, dept_name, phone_number, office, email, status, remark, config_by, config_by_name, tenant_id, create_dept, create_by, create_time, update_by, update_time from xljk_consulter
</sql>
<select id="selectXljkConsulterVoById" parameterType="Long" resultMap="XljkConsulterVoResult">
<include refid="selectXljkConsulterVo"/>
where id = #{id}
</select>
<select id="selectXljkConsulterVoList" parameterType="org.dromara.xljk.domain.XljkConsulter" resultMap="XljkConsulterVoResult">
<include refid="selectXljkConsulterVo"/>
<where>
<if test="userId != null">and user_id = #{userId}</if>
<if test="userName != null and userName != ''">and user_name like concat('%', #{userName}, '%')</if>
<if test="nickName != null and nickName != ''">and nick_name like concat('%', #{nickName}, '%')</if>
<if test="deptId != null">and dept_id = #{deptId}</if>
<if test="status != null and status != ''">and status = #{status}</if>
<if test="configBy != null">and config_by = #{configBy}</if>
<if test="params.beginCreateTime != null and params.beginCreateTime != '' and params.endCreateTime != null and params.endCreateTime != ''">
and create_time between #{params.beginCreateTime} and #{params.endCreateTime}
</if>
</where>
order by create_time desc
</select>
<!-- 检查用户是否已存在 -->
<select id="checkUserExists" resultType="int">
SELECT COUNT(1) FROM xljk_consulter WHERE user_id = #{userId} AND deleted = 0
</select>
<!-- 批量启用咨询师 -->
<update id="enableConsultersByIds">
UPDATE xljk_consulter SET status = '1' WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND deleted = 0
</update>
<!-- 批量停用咨询师 -->
<update id="disableConsultersByIds">
UPDATE xljk_consulter SET status = '0' WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND deleted = 0
</update>
<!-- 查询完整的用户信息(包含部门信息)用于批量添加 -->
<select id="selectUsersWithDeptInfo" resultType="org.dromara.xljk.domain.XljkUserWithDeptInfo">
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.user_id IN
<foreach collection="userIds" item="userId" open="(" separator="," close=")">
#{userId}
</foreach>
AND u.del_flag = '0'
AND u.status = '0'
</select>
</mapper>