|
@@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
|
import java.util.HashSet;
|
|
import java.util.HashSet;
|
|
@@ -66,6 +67,8 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
private static final Integer POINT_CHANGE_CREATE = 1;
|
|
private static final Integer POINT_CHANGE_CREATE = 1;
|
|
|
/** 积分明细变动类型:审核加分 */
|
|
/** 积分明细变动类型:审核加分 */
|
|
|
private static final Integer POINT_CHANGE_AUDIT = 2;
|
|
private static final Integer POINT_CHANGE_AUDIT = 2;
|
|
|
|
|
+ /** 积分明细变动类型:手动赋分加分 */
|
|
|
|
|
+ private static final Integer POINT_CHANGE_MANUAL = 3;
|
|
|
/** 问答积分规则类型:提问积分 */
|
|
/** 问答积分规则类型:提问积分 */
|
|
|
private static final String QA_RULE_TYPE_QUESTION = "1";
|
|
private static final String QA_RULE_TYPE_QUESTION = "1";
|
|
|
/** 问答积分规则类型:回答积分 */
|
|
/** 问答积分规则类型:回答积分 */
|
|
@@ -380,10 +383,12 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
boolean isQaCategory = false;
|
|
boolean isQaCategory = false;
|
|
|
boolean isTypicalCase = false; // 是否为典型案例分类
|
|
boolean isTypicalCase = false; // 是否为典型案例分类
|
|
|
boolean isInternalDocRootNode = false; // 是否为"公司内部发文"根节点(parent_id=0)
|
|
boolean isInternalDocRootNode = false; // 是否为"公司内部发文"根节点(parent_id=0)
|
|
|
|
|
+ String treeNameForNotify = null; // 保存treeName,用于后续通知判断
|
|
|
|
|
|
|
|
if (StringUtils.isNotBlank(shareInfo.getTreeNodeId())) {
|
|
if (StringUtils.isNotBlank(shareInfo.getTreeNodeId())) {
|
|
|
WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(shareInfo.getTreeNodeId());
|
|
WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(shareInfo.getTreeNodeId());
|
|
|
if (treeInfo != null) {
|
|
if (treeInfo != null) {
|
|
|
|
|
+ treeNameForNotify = treeInfo.getTreeName();
|
|
|
if ("问答答疑".equals(treeInfo.getTreeName())) {
|
|
if ("问答答疑".equals(treeInfo.getTreeName())) {
|
|
|
isQaCategory = true;
|
|
isQaCategory = true;
|
|
|
}
|
|
}
|
|
@@ -494,6 +499,145 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
dynamicValueInfoDao.insert(valueInfo);
|
|
dynamicValueInfoDao.insert(valueInfo);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 仅针对典型案例(原创)、培训心得、技术总结 才发送代办/通知 ==========
|
|
|
|
|
+ boolean needsNotify = "技术总结".equals(treeNameForNotify) || "培训心得".equals(treeNameForNotify)
|
|
|
|
|
+ || ("典型案例".equals(treeNameForNotify) && "0".equals(shareInfo.getContentAttribute()));
|
|
|
|
|
+
|
|
|
|
|
+ if (needsNotify) {
|
|
|
|
|
+ //在往work_project_notify表中添加代办或者通知数据之前 需要根据shareInfo.getId()(即WorkProjectNotify表中的notify_id字段)进行判断,如果已经存在数据,则需要全部删除之后重新添加,否则可能导致重复数据
|
|
|
|
|
+ WorkProjectNotify deleteEntity = new WorkProjectNotify();
|
|
|
|
|
+ deleteEntity.setNotifyId(shareInfo.getId());
|
|
|
|
|
+ workProjectNotifyService.deleteByNotifyId(deleteEntity);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取模块名称(根节点名)
|
|
|
|
|
+ String moduleName = "";
|
|
|
|
|
+ if (StringUtils.isNotBlank(shareInfo.getRootId())) {
|
|
|
|
|
+ WorkKnowledgeBaseTreeInfo rootTreeInfo = getTreeInfoById(shareInfo.getRootId());
|
|
|
|
|
+ if (rootTreeInfo != null && StringUtils.isNotBlank(rootTreeInfo.getTreeName())) {
|
|
|
|
|
+ moduleName = rootTreeInfo.getTreeName();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 主审专家信息处理 ==========
|
|
|
|
|
+ String expertReviewerIds = shareInfo.getExpertReviewerIds();
|
|
|
|
|
+ String[] expertIdArr = null;
|
|
|
|
|
+ if (StringUtils.isNotBlank(expertReviewerIds)) {
|
|
|
|
|
+ expertIdArr = expertReviewerIds.split(",");
|
|
|
|
|
+ String expertReviewerNames = shareInfo.getExpertReviewerNames();
|
|
|
|
|
+ String[] expertNameArr = (StringUtils.isNotBlank(expertReviewerNames)) ? expertReviewerNames.split(",") : new String[0];
|
|
|
|
|
+ for (int i = 0; i < expertIdArr.length; i++) {
|
|
|
|
|
+ String expertId = expertIdArr[i].trim();
|
|
|
|
|
+ String expertName = (i < expertNameArr.length) ? expertNameArr[i].trim() : "";
|
|
|
|
|
+ if (StringUtils.isBlank(expertId)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 确定所属人:如果submit_audit_user_id为空,则为create_by;否则为submit_audit_user_id
|
|
|
|
|
+ String ownerUserId = StringUtils.isNotBlank(shareInfo.getSubmitAuditUserId())
|
|
|
|
|
+ ? shareInfo.getSubmitAuditUserId()
|
|
|
|
|
+ : (shareInfo.getCreateBy() != null ? shareInfo.getCreateBy().getId() : "");
|
|
|
|
|
+ User ownerUser = UserUtils.get(ownerUserId);
|
|
|
|
|
+ String ownerName = (ownerUser != null && StringUtils.isNotBlank(ownerUser.getName()))
|
|
|
|
|
+ ? ownerUser.getName()
|
|
|
|
|
+ : "用户";
|
|
|
|
|
+ String notifyStr = "创建人:" + ownerName + "在【" + moduleName + "】模块中创建了一个名为【" + shareInfo.getName() + "】的知识库文件,需要审核";
|
|
|
|
|
+ String titleStr = "创建人:" + ownerName + "在【" + moduleName + "】模块中创建了一个名为【" + shareInfo.getName() + "】的知识库文件,需要审核";
|
|
|
|
|
+
|
|
|
|
|
+ User user = UserUtils.get(expertId);
|
|
|
|
|
+
|
|
|
|
|
+ // 向主审专家发送代办
|
|
|
|
|
+ if (StringUtils.isNotBlank(user.getId())) {
|
|
|
|
|
+ workProjectNotifyService
|
|
|
|
|
+ .save(UtilNotify.saveNotify(shareInfo.getId(),
|
|
|
|
|
+ user,
|
|
|
|
|
+ user.getCompany().getId(),
|
|
|
|
|
+ titleStr,
|
|
|
|
|
+ notifyStr,
|
|
|
|
|
+ "200",
|
|
|
|
|
+ "0",
|
|
|
|
|
+ "待审批",
|
|
|
|
|
+ ""));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 通知剩余专家(排除主审专家);如果没有主审专家,则通知所有专家
|
|
|
|
|
+ notifyRemainingExperts(shareInfo, expertIdArr, moduleName);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 通知剩余专家(排除主审专家)
|
|
|
|
|
+ * @param shareInfo 知识库文件信息
|
|
|
|
|
+ * @param mainExpertIds 主审专家ID数组
|
|
|
|
|
+ * @param moduleName 模块名称(根节点名)
|
|
|
|
|
+ */
|
|
|
|
|
+ private void notifyRemainingExperts(WorkKnowledgeBaseShareInfo shareInfo, String[] mainExpertIds, String moduleName) {
|
|
|
|
|
+ // 构建主审专家ID集合,用于排除
|
|
|
|
|
+ Set<String> mainExpertIdSet = new HashSet<>();
|
|
|
|
|
+ if (mainExpertIds != null) {
|
|
|
|
|
+ for (String expertId : mainExpertIds) {
|
|
|
|
|
+ if (StringUtils.isNotBlank(expertId)) {
|
|
|
|
|
+ mainExpertIdSet.add(expertId.trim());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询所有启用的专家列表
|
|
|
|
|
+ List<WorkKnowledgeExpert> allEnabledExperts = expertService.findEnabledList();
|
|
|
|
|
+ if (allEnabledExperts == null || allEnabledExperts.isEmpty()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 过滤出非主审专家的剩余专家
|
|
|
|
|
+ List<WorkKnowledgeExpert> remainingExperts = new ArrayList<>();
|
|
|
|
|
+ for (WorkKnowledgeExpert expert : allEnabledExperts) {
|
|
|
|
|
+ if (!mainExpertIdSet.contains(expert.getUserId())) {
|
|
|
|
|
+ remainingExperts.add(expert);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有剩余专家,直接返回
|
|
|
|
|
+ if (remainingExperts.isEmpty()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 对剩余专家发送通知
|
|
|
|
|
+ // 确定所属人:如果submit_audit_user_id为空,则为create_by;否则为submit_audit_user_id
|
|
|
|
|
+ String ownerUserId = StringUtils.isNotBlank(shareInfo.getSubmitAuditUserId())
|
|
|
|
|
+ ? shareInfo.getSubmitAuditUserId()
|
|
|
|
|
+ : (shareInfo.getCreateBy() != null ? shareInfo.getCreateBy().getId() : "");
|
|
|
|
|
+ User ownerUser = UserUtils.get(ownerUserId);
|
|
|
|
|
+ String ownerName = (ownerUser != null && StringUtils.isNotBlank(ownerUser.getName()))
|
|
|
|
|
+ ? ownerUser.getName()
|
|
|
|
|
+ : "用户";
|
|
|
|
|
+ String notifyStr = "创建人:" + ownerName + "在【" + moduleName + "】模块中创建了一个名为【" + shareInfo.getName() + "】的知识库文件,需要审核。您可登录知识库进行审核";
|
|
|
|
|
+ String titleStr = "创建人:" + ownerName + "在【" + moduleName + "】模块中创建了一个名为【" + shareInfo.getName() + "】的知识库文件,需要审核。您可登录知识库进行审核";
|
|
|
|
|
+
|
|
|
|
|
+ for (WorkKnowledgeExpert expert : remainingExperts) {
|
|
|
|
|
+ User user = UserUtils.get(expert.getUserId());
|
|
|
|
|
+ if (user == null || StringUtils.isBlank(user.getId())) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ workProjectNotifyService.save(UtilNotify.saveNotify(
|
|
|
|
|
+ shareInfo.getId(),
|
|
|
|
|
+ user,
|
|
|
|
|
+ user.getCompany().getId(),
|
|
|
|
|
+ titleStr,
|
|
|
|
|
+ notifyStr,
|
|
|
|
|
+ "200",
|
|
|
|
|
+ "0",
|
|
|
|
|
+ "待通知",
|
|
|
|
|
+ ""
|
|
|
|
|
+ ));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 记录异常但不中断其他专家的通知发送
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -564,7 +708,7 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
if (entity == null) {
|
|
if (entity == null) {
|
|
|
throw new RuntimeException("文件记录不存在");
|
|
throw new RuntimeException("文件记录不存在");
|
|
|
}
|
|
}
|
|
|
- if (entity.getAuditStatus() == null || entity.getAuditStatus() != AUDIT_STATUS_PENDING) {
|
|
|
|
|
|
|
+ if (entity.getAuditStatus() == null || !AUDIT_STATUS_PENDING.equals(entity.getAuditStatus())) {
|
|
|
throw new RuntimeException("当前状态不允许发起审核");
|
|
throw new RuntimeException("当前状态不允许发起审核");
|
|
|
}
|
|
}
|
|
|
entity.setAuditStatus(AUDIT_STATUS_AUDITING);
|
|
entity.setAuditStatus(AUDIT_STATUS_AUDITING);
|
|
@@ -582,11 +726,12 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
*/
|
|
*/
|
|
|
@Transactional(readOnly = false)
|
|
@Transactional(readOnly = false)
|
|
|
public void auditPass(String fileId, String auditUserId, String auditOpinion, Integer score) {
|
|
public void auditPass(String fileId, String auditUserId, String auditOpinion, Integer score) {
|
|
|
- WorkKnowledgeBaseShareInfo entity = dao.get(fileId);
|
|
|
|
|
|
|
+ // 使用 SELECT ... FOR UPDATE 加行锁,防止并发审核
|
|
|
|
|
+ WorkKnowledgeBaseShareInfo entity = dao.getForUpdate(fileId);
|
|
|
if (entity == null) {
|
|
if (entity == null) {
|
|
|
throw new RuntimeException("文件记录不存在");
|
|
throw new RuntimeException("文件记录不存在");
|
|
|
}
|
|
}
|
|
|
- if (entity.getAuditStatus() == null || entity.getAuditStatus() == AUDIT_STATUS_REJECTED || entity.getAuditStatus() == AUDIT_STATUS_PASSED) {
|
|
|
|
|
|
|
+ if (entity.getAuditStatus() == null || AUDIT_STATUS_REJECTED.equals(entity.getAuditStatus()) || AUDIT_STATUS_PASSED.equals(entity.getAuditStatus())) {
|
|
|
throw new RuntimeException("当前状态不允许审核");
|
|
throw new RuntimeException("当前状态不允许审核");
|
|
|
}
|
|
}
|
|
|
// 禁止审核自己提交的文件
|
|
// 禁止审核自己提交的文件
|
|
@@ -603,15 +748,17 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
boolean needsScore = false;
|
|
boolean needsScore = false;
|
|
|
boolean isTypicalCaseOriginal = false; // 典型案例(原创)
|
|
boolean isTypicalCaseOriginal = false; // 典型案例(原创)
|
|
|
boolean isTypicalCaseReprint = false; // 典型案例(转载)
|
|
boolean isTypicalCaseReprint = false; // 典型案例(转载)
|
|
|
|
|
+ boolean needsExpertAudit = false; // 是否需要专家审核(技术总结/培训心得/典型案例原创)
|
|
|
|
|
|
|
|
if (StringUtils.isNotBlank(entity.getTreeNodeId())) {
|
|
if (StringUtils.isNotBlank(entity.getTreeNodeId())) {
|
|
|
WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(entity.getTreeNodeId());
|
|
WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(entity.getTreeNodeId());
|
|
|
if (treeInfo != null) {
|
|
if (treeInfo != null) {
|
|
|
String treeName = treeInfo.getTreeName();
|
|
String treeName = treeInfo.getTreeName();
|
|
|
|
|
|
|
|
- // 技术总结/培训心得:需要评分
|
|
|
|
|
|
|
+ // 技术总结/培训心得:需要评分,需要专家审核
|
|
|
if ("技术总结".equals(treeName) || "培训心得".equals(treeName)) {
|
|
if ("技术总结".equals(treeName) || "培训心得".equals(treeName)) {
|
|
|
needsScore = true;
|
|
needsScore = true;
|
|
|
|
|
+ needsExpertAudit = true;
|
|
|
// 验证评分必填
|
|
// 验证评分必填
|
|
|
if (score == null) {
|
|
if (score == null) {
|
|
|
throw new RuntimeException("请输入评分分数");
|
|
throw new RuntimeException("请输入评分分数");
|
|
@@ -624,6 +771,7 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
// 典型案例(原创):需要专家审核,使用评分机制
|
|
// 典型案例(原创):需要专家审核,使用评分机制
|
|
|
isTypicalCaseOriginal = true;
|
|
isTypicalCaseOriginal = true;
|
|
|
needsScore = true;
|
|
needsScore = true;
|
|
|
|
|
+ needsExpertAudit = true;
|
|
|
// 校验当前用户是否为专家
|
|
// 校验当前用户是否为专家
|
|
|
boolean isExpert = expertService.isUserBound(auditUserId);
|
|
boolean isExpert = expertService.isUserBound(auditUserId);
|
|
|
if (!isExpert) {
|
|
if (!isExpert) {
|
|
@@ -640,6 +788,41 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // ========== 主审专家抢办名额验证(仅对需要专家审核的分类生效) ==========
|
|
|
|
|
+ if (needsExpertAudit) {
|
|
|
|
|
+ String expertReviewerIds = entity.getExpertReviewerIds();
|
|
|
|
|
+ if (StringUtils.isNotBlank(expertReviewerIds)) {
|
|
|
|
|
+ Set<String> mainExpertIdSet = new HashSet<>(Arrays.asList(expertReviewerIds.split(",")));
|
|
|
|
|
+ boolean isMainExpert = mainExpertIdSet.contains(auditUserId);
|
|
|
|
|
+ if (!isMainExpert) {
|
|
|
|
|
+ // 非主审专家:检查是否还有剩余抢办名额
|
|
|
|
|
+ int mainExpertCount = 0;
|
|
|
|
|
+ for (String eid : mainExpertIdSet) {
|
|
|
|
|
+ if (StringUtils.isNotBlank(eid.trim())) {
|
|
|
|
|
+ mainExpertCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ int nonMainSlots = AUDIT_PASS_REQUIRED - mainExpertCount;
|
|
|
|
|
+ if (nonMainSlots <= 0) {
|
|
|
|
|
+ throw new RuntimeException("主审专家名额已满,非指定专家无法审核");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 统计本轮非主审专家已审核人数
|
|
|
|
|
+ int nonMainAuditedCount = 0;
|
|
|
|
|
+ List<WorkKnowledgeBaseAuditRecord> currentRecords = auditRecordDao.findByFileIdSinceTime(fileId, startTime);
|
|
|
|
|
+ if (currentRecords != null) {
|
|
|
|
|
+ for (WorkKnowledgeBaseAuditRecord rec : currentRecords) {
|
|
|
|
|
+ if (!mainExpertIdSet.contains(rec.getAuditUserId())) {
|
|
|
|
|
+ nonMainAuditedCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (nonMainAuditedCount >= nonMainSlots) {
|
|
|
|
|
+ throw new RuntimeException("非主审专家审核名额已满,无法审核");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 保存审核记录
|
|
// 保存审核记录
|
|
|
WorkKnowledgeBaseAuditRecord record = new WorkKnowledgeBaseAuditRecord();
|
|
WorkKnowledgeBaseAuditRecord record = new WorkKnowledgeBaseAuditRecord();
|
|
|
record.setFileId(fileId);
|
|
record.setFileId(fileId);
|
|
@@ -681,6 +864,69 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
entity.preUpdate();
|
|
entity.preUpdate();
|
|
|
dao.updateAuditStatus(entity);
|
|
dao.updateAuditStatus(entity);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ //此处是审核通过,查询审核人是否存在代办,如果存在,则将审核人的代办进行逻辑删除
|
|
|
|
|
+ WorkProjectNotify notify = workProjectNotifyService.getByNotifyIdAndNotifyUser(entity.getId(),auditUserId);// 查询该条数据是否存在为审核的代办和通知
|
|
|
|
|
+ if (notify != null) {
|
|
|
|
|
+ notify.setAuditor(auditUserId);
|
|
|
|
|
+ notify.setStatus("1");
|
|
|
|
|
+ workProjectNotifyService.updateNewReadStateByNotifyId(notify);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 非主审专家审核完最后一席后,清理剩余非主审专家的通知 ==========
|
|
|
|
|
+ if (needsExpertAudit) {
|
|
|
|
|
+ String expertReviewerIds = entity.getExpertReviewerIds();
|
|
|
|
|
+ if (StringUtils.isNotBlank(expertReviewerIds)) {
|
|
|
|
|
+ Set<String> mainExpertIdSet = new HashSet<>(Arrays.asList(expertReviewerIds.split(",")));
|
|
|
|
|
+ boolean isMainExpert = mainExpertIdSet.contains(auditUserId);
|
|
|
|
|
+ if (!isMainExpert) {
|
|
|
|
|
+ // 统计主审专家人数
|
|
|
|
|
+ int mainExpertCount = 0;
|
|
|
|
|
+ for (String eid : mainExpertIdSet) {
|
|
|
|
|
+ if (StringUtils.isNotBlank(eid.trim())) {
|
|
|
|
|
+ mainExpertCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ int nonMainSlots = AUDIT_PASS_REQUIRED - mainExpertCount;
|
|
|
|
|
+ // 统计本轮非主审专家已通过人数(包含当前审核人),并收集已审核的非主审专家ID
|
|
|
|
|
+ int nonMainPassCount = 0;
|
|
|
|
|
+ Set<String> auditedNonMainIds = new HashSet<>();
|
|
|
|
|
+ List<WorkKnowledgeBaseAuditRecord> currentRecords = auditRecordDao.findByFileIdSinceTime(fileId, startTime);
|
|
|
|
|
+ if (currentRecords != null) {
|
|
|
|
|
+ for (WorkKnowledgeBaseAuditRecord rec : currentRecords) {
|
|
|
|
|
+ if (!mainExpertIdSet.contains(rec.getAuditUserId())) {
|
|
|
|
|
+ auditedNonMainIds.add(rec.getAuditUserId());
|
|
|
|
|
+ if (rec.getAuditResult() != null && rec.getAuditResult() == 1) {
|
|
|
|
|
+ nonMainPassCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 非主审专家席位已满,筛选出还未审核的非主审专家
|
|
|
|
|
+ if (nonMainPassCount >= nonMainSlots) {
|
|
|
|
|
+ // 查询所有启用的专家,筛选出非主审且未审核的专家
|
|
|
|
|
+ List<WorkKnowledgeExpert> allEnabledExperts = expertService.findEnabledList();
|
|
|
|
|
+ if (allEnabledExperts != null) {
|
|
|
|
|
+ for (WorkKnowledgeExpert expert : allEnabledExperts) {
|
|
|
|
|
+ String expertUid = expert.getUserId();
|
|
|
|
|
+ // 排除主审专家、排除已审核的非主审专家
|
|
|
|
|
+ if (!mainExpertIdSet.contains(expertUid)
|
|
|
|
|
+ && !auditedNonMainIds.contains(expertUid)
|
|
|
|
|
+ && StringUtils.isNotBlank(expertUid)) {
|
|
|
|
|
+ // 对剩余未审核的非主审专家进行通知逻辑删除(按用户精确删除,不影响主审专家通知)
|
|
|
|
|
+ WorkProjectNotify disposenotify = new WorkProjectNotify();
|
|
|
|
|
+ disposenotify.setNotifyId(entity.getId());
|
|
|
|
|
+ User expertUser = new User();
|
|
|
|
|
+ expertUser.setId(expertUid);
|
|
|
|
|
+ disposenotify.setUser(expertUser);
|
|
|
|
|
+ workProjectNotifyService.modifyDelflagByUser(disposenotify);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -689,11 +935,12 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
*/
|
|
*/
|
|
|
@Transactional(readOnly = false)
|
|
@Transactional(readOnly = false)
|
|
|
public void auditReject(String fileId, String auditUserId, String auditOpinion) {
|
|
public void auditReject(String fileId, String auditUserId, String auditOpinion) {
|
|
|
- WorkKnowledgeBaseShareInfo entity = dao.get(fileId);
|
|
|
|
|
|
|
+ // 使用 SELECT ... FOR UPDATE 加行锁,防止并发审核
|
|
|
|
|
+ WorkKnowledgeBaseShareInfo entity = dao.getForUpdate(fileId);
|
|
|
if (entity == null) {
|
|
if (entity == null) {
|
|
|
throw new RuntimeException("文件记录不存在");
|
|
throw new RuntimeException("文件记录不存在");
|
|
|
}
|
|
}
|
|
|
- if (entity.getAuditStatus() == null || entity.getAuditStatus() == AUDIT_STATUS_REJECTED || entity.getAuditStatus() == AUDIT_STATUS_PASSED) {
|
|
|
|
|
|
|
+ if (entity.getAuditStatus() == null || AUDIT_STATUS_REJECTED.equals(entity.getAuditStatus()) || AUDIT_STATUS_PASSED.equals(entity.getAuditStatus())) {
|
|
|
throw new RuntimeException("当前状态不允许审核");
|
|
throw new RuntimeException("当前状态不允许审核");
|
|
|
}
|
|
}
|
|
|
// 禁止审核自己提交的文件
|
|
// 禁止审核自己提交的文件
|
|
@@ -705,6 +952,51 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
if (auditRecordDao.countByFileIdAndUserIdSinceTime(fileId, auditUserId, startTime) > 0) {
|
|
if (auditRecordDao.countByFileIdAndUserIdSinceTime(fileId, auditUserId, startTime) > 0) {
|
|
|
throw new RuntimeException("您已审核过该文件,不可重复审核");
|
|
throw new RuntimeException("您已审核过该文件,不可重复审核");
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 主审专家抢办名额验证(仅对需要专家审核的分类生效) ==========
|
|
|
|
|
+ boolean needsExpertAudit = false;
|
|
|
|
|
+ if (StringUtils.isNotBlank(entity.getTreeNodeId())) {
|
|
|
|
|
+ WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(entity.getTreeNodeId());
|
|
|
|
|
+ if (treeInfo != null) {
|
|
|
|
|
+ String treeName = treeInfo.getTreeName();
|
|
|
|
|
+ needsExpertAudit = "技术总结".equals(treeName) || "培训心得".equals(treeName)
|
|
|
|
|
+ || ("典型案例".equals(treeName) && "0".equals(entity.getContentAttribute()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (needsExpertAudit) {
|
|
|
|
|
+ String expertReviewerIds = entity.getExpertReviewerIds();
|
|
|
|
|
+ if (StringUtils.isNotBlank(expertReviewerIds)) {
|
|
|
|
|
+ Set<String> mainExpertIdSet = new HashSet<>(Arrays.asList(expertReviewerIds.split(",")));
|
|
|
|
|
+ boolean isMainExpert = mainExpertIdSet.contains(auditUserId);
|
|
|
|
|
+ if (!isMainExpert) {
|
|
|
|
|
+ // 非主审专家:检查是否还有剩余抢办名额
|
|
|
|
|
+ int mainExpertCount = 0;
|
|
|
|
|
+ for (String eid : mainExpertIdSet) {
|
|
|
|
|
+ if (StringUtils.isNotBlank(eid.trim())) {
|
|
|
|
|
+ mainExpertCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ int nonMainSlots = AUDIT_PASS_REQUIRED - mainExpertCount;
|
|
|
|
|
+ if (nonMainSlots <= 0) {
|
|
|
|
|
+ throw new RuntimeException("主审专家名额已满,非指定专家无法审核");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 统计本轮非主审专家已审核人数
|
|
|
|
|
+ int nonMainAuditedCount = 0;
|
|
|
|
|
+ List<WorkKnowledgeBaseAuditRecord> currentRecords = auditRecordDao.findByFileIdSinceTime(fileId, startTime);
|
|
|
|
|
+ if (currentRecords != null) {
|
|
|
|
|
+ for (WorkKnowledgeBaseAuditRecord rec : currentRecords) {
|
|
|
|
|
+ if (!mainExpertIdSet.contains(rec.getAuditUserId())) {
|
|
|
|
|
+ nonMainAuditedCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (nonMainAuditedCount >= nonMainSlots) {
|
|
|
|
|
+ throw new RuntimeException("非主审专家审核名额已满,无法审核");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 保存审核记录
|
|
// 保存审核记录
|
|
|
WorkKnowledgeBaseAuditRecord record = new WorkKnowledgeBaseAuditRecord();
|
|
WorkKnowledgeBaseAuditRecord record = new WorkKnowledgeBaseAuditRecord();
|
|
|
record.setFileId(fileId);
|
|
record.setFileId(fileId);
|
|
@@ -722,6 +1014,17 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
entity.setAuditRejectCount(auditRecordDao.countRejectByFileIdSinceTime(fileId, startTime));
|
|
entity.setAuditRejectCount(auditRecordDao.countRejectByFileIdSinceTime(fileId, startTime));
|
|
|
entity.preUpdate();
|
|
entity.preUpdate();
|
|
|
dao.updateAuditStatus(entity);
|
|
dao.updateAuditStatus(entity);
|
|
|
|
|
+
|
|
|
|
|
+ //此处是驳回,则需要将所有的通知和代办都给逻辑删除
|
|
|
|
|
+ List<WorkProjectNotify> listByNotifyId = workProjectNotifyService.getListByNotifyId(entity.getId());// 查询该条数据是否存在为审核的代办和通知
|
|
|
|
|
+ if (listByNotifyId != null && !listByNotifyId.isEmpty()) {
|
|
|
|
|
+ //循环 对这些数据进行逻辑删除
|
|
|
|
|
+ for (WorkProjectNotify notify : listByNotifyId) {
|
|
|
|
|
+ notify.setDelFlag(WorkProjectNotify.DEL_FLAG_DELETE);
|
|
|
|
|
+ workProjectNotifyService.modifyDelflag(notify);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -734,7 +1037,7 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
if (entity == null) {
|
|
if (entity == null) {
|
|
|
throw new RuntimeException("文件记录不存在");
|
|
throw new RuntimeException("文件记录不存在");
|
|
|
}
|
|
}
|
|
|
- if (entity.getAuditStatus() == null || entity.getAuditStatus() == AUDIT_STATUS_REJECTED || entity.getAuditStatus() == AUDIT_STATUS_PASSED) {
|
|
|
|
|
|
|
+ if (entity.getAuditStatus() == null || AUDIT_STATUS_REJECTED.equals(entity.getAuditStatus()) || AUDIT_STATUS_PASSED.equals(entity.getAuditStatus())) {
|
|
|
throw new RuntimeException("当前状态不允许撤回");
|
|
throw new RuntimeException("当前状态不允许撤回");
|
|
|
}
|
|
}
|
|
|
// 校验权限:仅提交审核人或管理员可撤回
|
|
// 校验权限:仅提交审核人或管理员可撤回
|
|
@@ -748,6 +1051,17 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
entity.setAuditRejectCount(0);
|
|
entity.setAuditRejectCount(0);
|
|
|
entity.preUpdate();
|
|
entity.preUpdate();
|
|
|
dao.updateAuditStatus(entity);
|
|
dao.updateAuditStatus(entity);
|
|
|
|
|
+
|
|
|
|
|
+ //撤回操作,需要到work_project_notify表中查询该条数据是否存在为审核的代办和通知,如果有,则需要进行逻辑删除,或者进行不展示处理
|
|
|
|
|
+ List<WorkProjectNotify> listByNotifyId = workProjectNotifyService.getListByNotifyId(entity.getId());// 查询该条数据是否存在为审核的代办和通知
|
|
|
|
|
+ if (listByNotifyId != null && !listByNotifyId.isEmpty()) {
|
|
|
|
|
+ //循环 对这些数据进行逻辑删除
|
|
|
|
|
+ for (WorkProjectNotify notify : listByNotifyId) {
|
|
|
|
|
+ notify.setDelFlag(WorkProjectNotify.DEL_FLAG_DELETE);
|
|
|
|
|
+ workProjectNotifyService.modifyDelflag(notify);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -787,7 +1101,7 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
*/
|
|
*/
|
|
|
public boolean canWithdraw(WorkKnowledgeBaseShareInfo entity) {
|
|
public boolean canWithdraw(WorkKnowledgeBaseShareInfo entity) {
|
|
|
if (entity == null || entity.getAuditStatus() == null) return false;
|
|
if (entity == null || entity.getAuditStatus() == null) return false;
|
|
|
- if (entity.getAuditStatus() != AUDIT_STATUS_PENDING && entity.getAuditStatus() != AUDIT_STATUS_AUDITING) return false;
|
|
|
|
|
|
|
+ if (!AUDIT_STATUS_PENDING.equals(entity.getAuditStatus()) && !AUDIT_STATUS_AUDITING.equals(entity.getAuditStatus())) return false;
|
|
|
User currentUser = UserUtils.getUser();
|
|
User currentUser = UserUtils.getUser();
|
|
|
return currentUser.isAdmin() || UserUtils.isManager()
|
|
return currentUser.isAdmin() || UserUtils.isManager()
|
|
|
|| currentUser.getId().equals(entity.getSubmitAuditUserId());
|
|
|| currentUser.getId().equals(entity.getSubmitAuditUserId());
|
|
@@ -795,13 +1109,16 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 判断当前用户是否可以审核(通过/驳回)该文件
|
|
* 判断当前用户是否可以审核(通过/驳回)该文件
|
|
|
|
|
+ * 包含主审专家抢办逻辑:如果指定了主审专家,非主审专家只有在剩余名额内才能审核
|
|
|
*/
|
|
*/
|
|
|
public boolean canAudit(WorkKnowledgeBaseShareInfo entity) {
|
|
public boolean canAudit(WorkKnowledgeBaseShareInfo entity) {
|
|
|
if (entity == null || entity.getAuditStatus() == null) return false;
|
|
if (entity == null || entity.getAuditStatus() == null) return false;
|
|
|
- if (entity.getAuditStatus() != AUDIT_STATUS_PENDING && entity.getAuditStatus() != AUDIT_STATUS_AUDITING) return false;
|
|
|
|
|
|
|
+ if (!AUDIT_STATUS_PENDING.equals(entity.getAuditStatus()) && !AUDIT_STATUS_AUDITING.equals(entity.getAuditStatus())) return false;
|
|
|
String currentUserId = UserUtils.getUser().getId();
|
|
String currentUserId = UserUtils.getUser().getId();
|
|
|
// 提交审核人不可审核自己的文件
|
|
// 提交审核人不可审核自己的文件
|
|
|
if (currentUserId.equals(entity.getSubmitAuditUserId())) return false;
|
|
if (currentUserId.equals(entity.getSubmitAuditUserId())) return false;
|
|
|
|
|
+ // 创建人不可审核自己的文件
|
|
|
|
|
+ if (entity.getCreateBy() != null && currentUserId.equals(entity.getCreateBy().getId())) return false;
|
|
|
|
|
|
|
|
// 检查是否为典型案例(原创),只有专家可以审核
|
|
// 检查是否为典型案例(原创),只有专家可以审核
|
|
|
boolean isTypicalCaseOriginal = false;
|
|
boolean isTypicalCaseOriginal = false;
|
|
@@ -820,8 +1137,56 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 本轮未审核过的用户可审核(按 audit_start_date 隔离历史记录)
|
|
|
|
|
- return auditRecordDao.countByFileIdAndUserIdSinceTime(entity.getId(), currentUserId, entity.getAuditStartDate()) == 0;
|
|
|
|
|
|
|
+ // 本轮未审核过的用户才可审核(按 audit_start_date 隔离历史记录)
|
|
|
|
|
+ Date startTime = entity.getAuditStartDate();
|
|
|
|
|
+ if (auditRecordDao.countByFileIdAndUserIdSinceTime(entity.getId(), currentUserId, startTime) > 0) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 主审专家抢办逻辑(仅对需要专家审核的分类生效) ==========
|
|
|
|
|
+ boolean needsExpertAudit = false;
|
|
|
|
|
+ if (StringUtils.isNotBlank(entity.getTreeNodeId())) {
|
|
|
|
|
+ WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(entity.getTreeNodeId());
|
|
|
|
|
+ if (treeInfo != null) {
|
|
|
|
|
+ String treeName = treeInfo.getTreeName();
|
|
|
|
|
+ needsExpertAudit = "技术总结".equals(treeName) || "培训心得".equals(treeName)
|
|
|
|
|
+ || ("典型案例".equals(treeName) && "0".equals(entity.getContentAttribute()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (needsExpertAudit) {
|
|
|
|
|
+ String expertReviewerIds = entity.getExpertReviewerIds();
|
|
|
|
|
+ if (StringUtils.isNotBlank(expertReviewerIds)) {
|
|
|
|
|
+ Set<String> mainExpertIdSet = new HashSet<>(Arrays.asList(expertReviewerIds.split(",")));
|
|
|
|
|
+ boolean isMainExpert = mainExpertIdSet.contains(currentUserId);
|
|
|
|
|
+ if (isMainExpert) {
|
|
|
|
|
+ return true; // 主审专家始终可以审核
|
|
|
|
|
+ }
|
|
|
|
|
+ // 非主审专家:检查剩余名额
|
|
|
|
|
+ int mainExpertCount = 0;
|
|
|
|
|
+ for (String eid : mainExpertIdSet) {
|
|
|
|
|
+ if (StringUtils.isNotBlank(eid.trim())) {
|
|
|
|
|
+ mainExpertCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ int nonMainSlots = AUDIT_PASS_REQUIRED - mainExpertCount;
|
|
|
|
|
+ if (nonMainSlots <= 0) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 统计本轮非主审专家已审核人数
|
|
|
|
|
+ int nonMainAuditedCount = 0;
|
|
|
|
|
+ List<WorkKnowledgeBaseAuditRecord> currentRecords = auditRecordDao.findByFileIdSinceTime(entity.getId(), startTime);
|
|
|
|
|
+ if (currentRecords != null) {
|
|
|
|
|
+ for (WorkKnowledgeBaseAuditRecord rec : currentRecords) {
|
|
|
|
|
+ if (!mainExpertIdSet.contains(rec.getAuditUserId())) {
|
|
|
|
|
+ nonMainAuditedCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return nonMainAuditedCount < nonMainSlots;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -829,8 +1194,106 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
*/
|
|
*/
|
|
|
public boolean canSubmitAudit(WorkKnowledgeBaseShareInfo entity) {
|
|
public boolean canSubmitAudit(WorkKnowledgeBaseShareInfo entity) {
|
|
|
if (entity == null || entity.getAuditStatus() == null) return false;
|
|
if (entity == null || entity.getAuditStatus() == null) return false;
|
|
|
- return entity.getAuditStatus() == AUDIT_STATUS_PENDING
|
|
|
|
|
- || entity.getAuditStatus() == AUDIT_STATUS_REJECTED;
|
|
|
|
|
|
|
+ return AUDIT_STATUS_PENDING.equals(entity.getAuditStatus())
|
|
|
|
|
+ || AUDIT_STATUS_REJECTED.equals(entity.getAuditStatus());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 列表页专用:根据行数据判断当前用户是否可以审核
|
|
|
|
|
+ * 包含主审专家抢办名额精确计算(查询本轮审核记录)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param row 列表行数据(来自 findDynamicList SQL 结果)
|
|
|
|
|
+ * @param currentUserId 当前登录用户ID
|
|
|
|
|
+ * @param isAdmin 当前用户是否为管理员
|
|
|
|
|
+ * @param isExpert 当前用户是否为专家
|
|
|
|
|
+ * @return 是否可以审核
|
|
|
|
|
+ */
|
|
|
|
|
+ public boolean checkCanAuditForList(Map<String, Object> row, String currentUserId, boolean isAdmin, boolean isExpert) {
|
|
|
|
|
+ Object auditStatusObj = row.get("auditStatus");
|
|
|
|
|
+ if (auditStatusObj == null) return false;
|
|
|
|
|
+ String auditStatus = auditStatusObj.toString();
|
|
|
|
|
+ // 只有未审核(1)和审核中(2)状态才显示审核按钮
|
|
|
|
|
+ if (!"1".equals(auditStatus) && !"2".equals(auditStatus)) return false;
|
|
|
|
|
+
|
|
|
|
|
+ // 创建人不能审核自己的文件
|
|
|
|
|
+ Object submitAuditUserIdObj = row.get("submitAuditUserId");
|
|
|
|
|
+ Object createByIdObj = row.get("createById");
|
|
|
|
|
+ String submitAuditUserId = submitAuditUserIdObj != null ? submitAuditUserIdObj.toString() : "";
|
|
|
|
|
+ String createById = createByIdObj != null ? createByIdObj.toString() : "";
|
|
|
|
|
+ boolean isCreator = StringUtils.isNotBlank(submitAuditUserId)
|
|
|
|
|
+ ? currentUserId.equals(submitAuditUserId)
|
|
|
|
|
+ : currentUserId.equals(createById);
|
|
|
|
|
+ if (isCreator) return false;
|
|
|
|
|
+
|
|
|
|
|
+ // 当前用户已审核过则不再显示审核按钮
|
|
|
|
|
+ Object currentUserAuditedObj = row.get("currentUserAudited");
|
|
|
|
|
+ if (currentUserAuditedObj != null && Boolean.TRUE.equals(currentUserAuditedObj)) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否为需要专家审核的分类
|
|
|
|
|
+ Object treeNameObj = row.get("treeName");
|
|
|
|
|
+ Object contentAttrObj = row.get("contentAttribute");
|
|
|
|
|
+ String treeName = treeNameObj != null ? treeNameObj.toString() : "";
|
|
|
|
|
+ String contentAttribute = contentAttrObj != null ? contentAttrObj.toString() : "";
|
|
|
|
|
+ boolean needsExpertAudit = "技术总结".equals(treeName) || "培训心得".equals(treeName)
|
|
|
|
|
+ || ("典型案例".equals(treeName) && "0".equals(contentAttribute));
|
|
|
|
|
+
|
|
|
|
|
+ // 基本权限:管理员、专家或普通分类
|
|
|
|
|
+ if (!isAdmin && !isExpert && needsExpertAudit) return false;
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 主审专家抢办名额精确计算(仅对需要专家审核的分类生效) ==========
|
|
|
|
|
+ if (needsExpertAudit) {
|
|
|
|
|
+ Object expertIdsObj = row.get("expertReviewerIds");
|
|
|
|
|
+ String expertReviewerIds = expertIdsObj != null ? expertIdsObj.toString() : "";
|
|
|
|
|
+ if (StringUtils.isNotBlank(expertReviewerIds)) {
|
|
|
|
|
+ Set<String> mainExpertIdSet = new HashSet<>();
|
|
|
|
|
+ for (String eid : expertReviewerIds.split(",")) {
|
|
|
|
|
+ if (StringUtils.isNotBlank(eid.trim())) {
|
|
|
|
|
+ mainExpertIdSet.add(eid.trim());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!mainExpertIdSet.isEmpty()) {
|
|
|
|
|
+ boolean isMainExpert = mainExpertIdSet.contains(currentUserId);
|
|
|
|
|
+ if (isAdmin || isMainExpert) {
|
|
|
|
|
+ return true; // 管理员和主审专家始终可以审核
|
|
|
|
|
+ }
|
|
|
|
|
+ // 非主审专家:计算剩余名额
|
|
|
|
|
+ int mainExpertCount = mainExpertIdSet.size();
|
|
|
|
|
+ int nonMainSlots = AUDIT_PASS_REQUIRED - mainExpertCount;
|
|
|
|
|
+ if (nonMainSlots <= 0) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 查询本轮审核记录,统计非主审专家已审核人数
|
|
|
|
|
+ Object fileIdObj = row.get("id");
|
|
|
|
|
+ if (fileIdObj != null) {
|
|
|
|
|
+ Object auditStartDateObj = row.get("auditStartDate");
|
|
|
|
|
+ Date startTime = null;
|
|
|
|
|
+ if (auditStartDateObj != null) {
|
|
|
|
|
+ if (auditStartDateObj instanceof Date) {
|
|
|
|
|
+ startTime = (Date) auditStartDateObj;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (startTime != null) {
|
|
|
|
|
+ List<WorkKnowledgeBaseAuditRecord> records = auditRecordDao.findByFileIdSinceTime(fileIdObj.toString(), startTime);
|
|
|
|
|
+ int nonMainAuditedCount = 0;
|
|
|
|
|
+ if (records != null) {
|
|
|
|
|
+ for (WorkKnowledgeBaseAuditRecord rec : records) {
|
|
|
|
|
+ if (!mainExpertIdSet.contains(rec.getAuditUserId())) {
|
|
|
|
|
+ nonMainAuditedCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return nonMainAuditedCount < nonMainSlots;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 无法确定时默认放行(详情页会精确校验)
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -1122,6 +1585,8 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
detail.setCategoryId(categoryId);
|
|
detail.setCategoryId(categoryId);
|
|
|
detail.setRuleId(ruleId);
|
|
detail.setRuleId(ruleId);
|
|
|
detail.setPoint(point);
|
|
detail.setPoint(point);
|
|
|
|
|
+ // 非手动赋分,清空手动赋分相关字段
|
|
|
|
|
+ detail.setIsManualScore("0");
|
|
|
detail.preInsert();
|
|
detail.preInsert();
|
|
|
pointDetailDao.insert(detail);
|
|
pointDetailDao.insert(detail);
|
|
|
|
|
|
|
@@ -1130,6 +1595,40 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
|
+ * 插入积分明细(手动赋分专用)
|
|
|
|
|
+ * @param userId 用户ID
|
|
|
|
|
+ * @param shareId 文件ID
|
|
|
|
|
+ * @param changeType 变动类型(3:手动赋分加分)
|
|
|
|
|
+ * @param categoryId 分类ID
|
|
|
|
|
+ * @param ruleId 规则ID
|
|
|
|
|
+ * @param point 积分值
|
|
|
|
|
+ * @param scoreUserId 赋分人ID
|
|
|
|
|
+ * @param targetUserId 被赋分人ID
|
|
|
|
|
+ * @param manualScoreTime 赋分时间
|
|
|
|
|
+ */
|
|
|
|
|
+ private void insertPointDetailForManualScore(String userId, String shareId, Integer changeType, String categoryId, String ruleId, Integer point,
|
|
|
|
|
+ String scoreUserId, String targetUserId, java.util.Date manualScoreTime) {
|
|
|
|
|
+ WorkKnowledgeBasePointDetail detail = new WorkKnowledgeBasePointDetail();
|
|
|
|
|
+ detail.setId(IdGen.uuid());
|
|
|
|
|
+ detail.setUserId(userId);
|
|
|
|
|
+ detail.setShareId(shareId);
|
|
|
|
|
+ detail.setChangeType(changeType);
|
|
|
|
|
+ detail.setCategoryId(categoryId);
|
|
|
|
|
+ detail.setRuleId(ruleId);
|
|
|
|
|
+ detail.setPoint(point);
|
|
|
|
|
+ // 手动赋分标记
|
|
|
|
|
+ detail.setIsManualScore("1");
|
|
|
|
|
+ detail.setManualScoreUserId(scoreUserId);
|
|
|
|
|
+ detail.setManualScoreTargetUserId(targetUserId);
|
|
|
|
|
+ detail.setManualScoreTime(manualScoreTime);
|
|
|
|
|
+ detail.preInsert();
|
|
|
|
|
+ pointDetailDao.insert(detail);
|
|
|
|
|
+
|
|
|
|
|
+ // 确保该用户的兑换标记记录存在
|
|
|
|
|
+ ensureExchangeFlagExists(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
* 确保用户的兑换标记记录存在,不存在则自动创建
|
|
* 确保用户的兑换标记记录存在,不存在则自动创建
|
|
|
* @param userId 用户ID
|
|
* @param userId 用户ID
|
|
|
*/
|
|
*/
|
|
@@ -1727,4 +2226,179 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
|
|
|
public void incrementClickCount(String id) {
|
|
public void incrementClickCount(String id) {
|
|
|
dao.incrementClickCount(id);
|
|
dao.incrementClickCount(id);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 手动赋分功能(独立于审核流程) ====================
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 判断文件是否可以进行手动赋分
|
|
|
|
|
+ * 条件:未审核通过 且 非问答答疑分类
|
|
|
|
|
+ */
|
|
|
|
|
+ public boolean canManualScore(WorkKnowledgeBaseShareInfo entity) {
|
|
|
|
|
+ if (entity == null || entity.getAuditStatus() == null) return false;
|
|
|
|
|
+ String status = entity.getAuditStatus();
|
|
|
|
|
+ // 已审核通过的不需要
|
|
|
|
|
+ if (AUDIT_STATUS_PASSED.equals(status)) return false;
|
|
|
|
|
+ // 问答答疑分类不走手动赋分
|
|
|
|
|
+ if (QA_STATUS_WAITING.equals(status) || QA_STATUS_ANSWERING.equals(status) || QA_STATUS_CLOSED.equals(status)) return false;
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据文档所属类别获取对应的赋分类型选项列表
|
|
|
|
|
+ * 规则:
|
|
|
|
|
+ * 1. 只从"贡献积分"下获取规则(排除"基础积分")
|
|
|
|
|
+ * 2. 特定分类(技术总结、培训心得、问答答疑、典型案例原创/转载)直接匹配其下的规则
|
|
|
|
|
+ * 3. 其余所有分类都走"相关资料"分类下的规则
|
|
|
|
|
+ * @param treeNodeId 文档所属树节点ID
|
|
|
|
|
+ * @param contentAttribute 内容属性(0=原创 1=转载)
|
|
|
|
|
+ * @return 赋分类型选项列表(每项包含 id, name,id为积分规则ID,name为规则名称pointName)
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> getScoreTypeOptions(String treeNodeId, String contentAttribute) {
|
|
|
|
|
+ List<Map<String, Object>> options = new ArrayList<>();
|
|
|
|
|
+ if (StringUtils.isBlank(treeNodeId)) {
|
|
|
|
|
+ return options;
|
|
|
|
|
+ }
|
|
|
|
|
+ WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(treeNodeId);
|
|
|
|
|
+ if (treeInfo == null) {
|
|
|
|
|
+ return options;
|
|
|
|
|
+ }
|
|
|
|
|
+ String treeName = treeInfo.getTreeName();
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 查找"贡献积分"一级分类
|
|
|
|
|
+ List<WorkKnowledgePointCategory> contributeCats = findCategoriesByName("贡献积分");
|
|
|
|
|
+ if (contributeCats == null || contributeCats.isEmpty()) {
|
|
|
|
|
+ // 如果没有配置贡献积分分类,返回空列表
|
|
|
|
|
+ return options;
|
|
|
|
|
+ }
|
|
|
|
|
+ String contributeCategoryId = contributeCats.get(0).getId();
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 查询"贡献积分"下的所有子分类
|
|
|
|
|
+ List<WorkKnowledgePointCategory> children = categoryDao.findChildrenByParentId(contributeCategoryId);
|
|
|
|
|
+ if (children == null || children.isEmpty()) {
|
|
|
|
|
+ return options;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 确定目标分类名称
|
|
|
|
|
+ String targetCategoryName = treeName;
|
|
|
|
|
+ if ("典型案例".equals(treeName)) {
|
|
|
|
|
+ // 典型案例:根据contentAttribute区分原创/转载
|
|
|
|
|
+ if ("0".equals(contentAttribute)) {
|
|
|
|
|
+ targetCategoryName = "典型案例(原创)";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ targetCategoryName = "典型案例(转载)";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 定义特定分类列表(这些分类直接匹配)
|
|
|
|
|
+ Set<String> specialCategories = new HashSet<>();
|
|
|
|
|
+ specialCategories.add("技术总结");
|
|
|
|
|
+ specialCategories.add("培训心得");
|
|
|
|
|
+ specialCategories.add("问答答疑");
|
|
|
|
|
+ specialCategories.add("典型案例(原创)");
|
|
|
|
|
+ specialCategories.add("典型案例(转载)");
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 判断是否为特定分类
|
|
|
|
|
+ boolean isSpecialCategory = specialCategories.contains(targetCategoryName);
|
|
|
|
|
+
|
|
|
|
|
+ String finalCategoryId = null;
|
|
|
|
|
+ if (isSpecialCategory) {
|
|
|
|
|
+ // 特定分类:在贡献积分的子分类中查找匹配的
|
|
|
|
|
+ for (WorkKnowledgePointCategory child : children) {
|
|
|
|
|
+ if (targetCategoryName.equals(child.getCategoryName())) {
|
|
|
|
|
+ finalCategoryId = child.getId();
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 非特定分类:使用"相关资料"分类
|
|
|
|
|
+ for (WorkKnowledgePointCategory child : children) {
|
|
|
|
|
+ if ("相关资料".equals(child.getCategoryName())) {
|
|
|
|
|
+ finalCategoryId = child.getId();
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 如果找到目标分类,查询其下的启用积分规则
|
|
|
|
|
+ if (StringUtils.isNotBlank(finalCategoryId)) {
|
|
|
|
|
+ addRulesToOptions(options, finalCategoryId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return options;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将指定分类下的启用积分规则添加到选项列表
|
|
|
|
|
+ * @param options 选项列表
|
|
|
|
|
+ * @param categoryId 分类ID
|
|
|
|
|
+ */
|
|
|
|
|
+ private void addRulesToOptions(List<Map<String, Object>> options, String categoryId) {
|
|
|
|
|
+ List<WorkKnowledgeBasePointRule> rules = pointRuleDao.findEnabledByCategoryId(categoryId);
|
|
|
|
|
+ if (rules != null && !rules.isEmpty()) {
|
|
|
|
|
+ for (WorkKnowledgeBasePointRule rule : rules) {
|
|
|
|
|
+ Map<String, Object> opt = new HashMap<>();
|
|
|
|
|
+ opt.put("id", rule.getId()); // 积分规则ID
|
|
|
|
|
+ opt.put("name", rule.getPointName()); // 规则名称(如"审核"、"创建")
|
|
|
|
|
+ opt.put("categoryId", categoryId); // 所属分类ID(用于后续发放积分)
|
|
|
|
|
+ options.add(opt);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 手动赋分操作(独立于审核流程)
|
|
|
|
|
+ * 规则:
|
|
|
|
|
+ * 1. 仅未审核通过的数据可操作
|
|
|
|
|
+ * 2. 赋分后直接标记为审核通过
|
|
|
|
|
+ * 3. 给被赋分人发放积分(使用用户填写的分数)
|
|
|
|
|
+ * 4. 四个字段全部赋值
|
|
|
|
|
+ * @param fileId 文件ID
|
|
|
|
|
+ * @param scoreUserId 赋分人ID(当前操作人)
|
|
|
|
|
+ * @param targetUserId 被赋分人ID
|
|
|
|
|
+ * @param score 赋分分数(正整数)
|
|
|
|
|
+ * @param scoreRuleId 赋分类型对应的积分规则ID
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional(readOnly = false)
|
|
|
|
|
+ public void manualScore(String fileId, String scoreUserId, String targetUserId, Integer score, String scoreRuleId) {
|
|
|
|
|
+ WorkKnowledgeBaseShareInfo entity = dao.get(fileId);
|
|
|
|
|
+ if (entity == null) {
|
|
|
|
|
+ throw new RuntimeException("文件记录不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (AUDIT_STATUS_PASSED.equals(entity.getAuditStatus())) {
|
|
|
|
|
+ throw new RuntimeException("该文件已审核通过,无需手动赋分");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (score == null || score <= 0) {
|
|
|
|
|
+ throw new RuntimeException("赋分分数必须为正整数");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isBlank(targetUserId)) {
|
|
|
|
|
+ throw new RuntimeException("请选择被赋分人");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isBlank(scoreRuleId)) {
|
|
|
|
|
+ throw new RuntimeException("请选择赋分类型");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 查询选中的积分规则,获取其分类ID
|
|
|
|
|
+ WorkKnowledgeBasePointRule selectedRule = pointRuleDao.get(scoreRuleId);
|
|
|
|
|
+ if (selectedRule == null) {
|
|
|
|
|
+ throw new RuntimeException("积分规则不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ String categoryId = selectedRule.getCategoryId();
|
|
|
|
|
+ if (StringUtils.isBlank(categoryId)) {
|
|
|
|
|
+ throw new RuntimeException("积分规则未关联分类");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 更新文件状态为审核通过 + 写入手动赋分四个字段
|
|
|
|
|
+ entity.setManualScoreUserId(scoreUserId);
|
|
|
|
|
+ entity.setManualScoreTargetUserId(targetUserId);
|
|
|
|
|
+ entity.setManualScoreTime(new Date());
|
|
|
|
|
+ entity.setIsManualScore("1");
|
|
|
|
|
+ entity.setAuditStatus(AUDIT_STATUS_PASSED);
|
|
|
|
|
+ entity.preUpdate();
|
|
|
|
|
+ dao.updateManualScore(entity);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 给被赋分人发放积分(使用用户填写的分数值)
|
|
|
|
|
+ java.util.Date manualScoreTime = new Date();
|
|
|
|
|
+ insertPointDetailForManualScore(targetUserId, fileId, POINT_CHANGE_MANUAL, categoryId,
|
|
|
|
|
+ scoreRuleId, score, scoreUserId, targetUserId, manualScoreTime);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|