Browse Source

知识库积分部分功能提交,问答答疑部分功能提交

徐滕 6 days ago
parent
commit
a3e26be855

+ 37 - 0
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/dao/WorkKnowledgeBaseQaAnswerDao.java

@@ -0,0 +1,37 @@
+package com.jeeplus.modules.WorkKnowledgeBase.dao;
+
+import com.jeeplus.common.persistence.CrudDao;
+import com.jeeplus.common.persistence.annotation.MyBatisDao;
+import com.jeeplus.modules.WorkKnowledgeBase.entity.WorkKnowledgeBaseQaAnswer;
+
+import java.util.List;
+
+/**
+ * 知识库-问答答疑回答DAO接口
+ * @author 
+ * @version 2026-01-16
+ */
+@MyBatisDao
+public interface WorkKnowledgeBaseQaAnswerDao extends CrudDao<WorkKnowledgeBaseQaAnswer> {
+    
+    /**
+     * 查询某个问题的所有回答列表(时间倒序)
+     * @param questionId 问题ID
+     * @return 回答列表
+     */
+    List<WorkKnowledgeBaseQaAnswer> findAnswersByQuestionId(String questionId);
+    
+    /**
+     * 统计问题的回答数量
+     * @param questionId 问题ID
+     * @return 回答数量
+     */
+    int countAnswersByQuestionId(String questionId);
+    
+    /**
+     * 插入回答
+     * @param answer 回答对象
+     * @return 影响行数
+     */
+    int insertAnswer(WorkKnowledgeBaseQaAnswer answer);
+}

+ 81 - 0
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/entity/WorkKnowledgeBaseQaAnswer.java

@@ -0,0 +1,81 @@
+package com.jeeplus.modules.WorkKnowledgeBase.entity;
+
+import com.jeeplus.common.persistence.DataEntity;
+import com.jeeplus.modules.sys.entity.User;
+
+/**
+ * 知识库-问答答疑回答实体类
+ * @author 
+ * @version 2026-01-16
+ */
+public class WorkKnowledgeBaseQaAnswer extends DataEntity<WorkKnowledgeBaseQaAnswer> {
+    
+    private static final long serialVersionUID = 1L;
+    
+    private String questionId;        // 问题ID(关联work_knowledge_base_share_info.id)
+    private String answerContent;     // 回答内容
+    
+    // 查询条件(用于关联查询)
+    private String questionTitle;     // 问题标题(用于展示)
+    private String answerUserName;    // 回答人姓名(用于展示)
+    private Integer isBestAnswer;     // 是否为最佳答案:0-否,1-是
+    
+    // 关联对象
+    private User answerUser;          // 回答人对象
+
+    public WorkKnowledgeBaseQaAnswer() {
+        super();
+    }
+
+    public WorkKnowledgeBaseQaAnswer(String id) {
+        super(id);
+    }
+
+    public String getQuestionId() {
+        return questionId;
+    }
+
+    public void setQuestionId(String questionId) {
+        this.questionId = questionId;
+    }
+
+    public String getAnswerContent() {
+        return answerContent;
+    }
+
+    public void setAnswerContent(String answerContent) {
+        this.answerContent = answerContent;
+    }
+
+    public String getQuestionTitle() {
+        return questionTitle;
+    }
+
+    public void setQuestionTitle(String questionTitle) {
+        this.questionTitle = questionTitle;
+    }
+
+    public String getAnswerUserName() {
+        return answerUserName;
+    }
+
+    public void setAnswerUserName(String answerUserName) {
+        this.answerUserName = answerUserName;
+    }
+
+    public Integer getIsBestAnswer() {
+        return isBestAnswer;
+    }
+
+    public void setIsBestAnswer(Integer isBestAnswer) {
+        this.isBestAnswer = isBestAnswer;
+    }
+
+    public User getAnswerUser() {
+        return answerUser;
+    }
+
+    public void setAnswerUser(User answerUser) {
+        this.answerUser = answerUser;
+    }
+}

+ 21 - 0
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/entity/WorkKnowledgeBaseShareInfo.java

@@ -60,6 +60,11 @@ public class WorkKnowledgeBaseShareInfo extends DataEntity<WorkKnowledgeBaseShar
     /** 附件列表(与 ossupload.js 生成的 workAttachments[N].xxx 隐藏域绑定) */
     private List<Workattachment> workAttachments;
 
+    /** 最佳答案ID(问答答疑专用) */
+    private String answerId;
+    /** 确认答案时间(问答答疑专用) */
+    private Date confirmDate;
+
     public String getTreeNodeId() {
         return treeNodeId;
     }
@@ -211,4 +216,20 @@ public class WorkKnowledgeBaseShareInfo extends DataEntity<WorkKnowledgeBaseShar
     public void setContentAttribute(String contentAttribute) {
         this.contentAttribute = contentAttribute;
     }
+
+    public String getAnswerId() {
+        return answerId;
+    }
+
+    public void setAnswerId(String answerId) {
+        this.answerId = answerId;
+    }
+
+    public Date getConfirmDate() {
+        return confirmDate;
+    }
+
+    public void setConfirmDate(Date confirmDate) {
+        this.confirmDate = confirmDate;
+    }
 }

+ 261 - 22
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/service/WorkKnowledgeBaseShareService.java

@@ -4,14 +4,7 @@ import com.jeeplus.common.persistence.Page;
 import com.jeeplus.common.service.CrudService;
 import com.jeeplus.common.utils.IdGen;
 import com.jeeplus.common.utils.StringUtils;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBaseAuditRecordDao;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBaseDynamicInfoDao;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBaseDynamicValueInfoDao;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBasePointDetailDao;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBasePointRuleDao;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBaseShareInfoDao;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBaseTreeInfoDao;
-import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeExpertDao;
+import com.jeeplus.modules.WorkKnowledgeBase.dao.*;
 import com.jeeplus.modules.WorkKnowledgeBase.entity.*;
 import com.jeeplus.modules.sys.entity.User;
 import com.jeeplus.modules.sys.entity.Workattachment;
@@ -51,6 +44,13 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
     public static final String AUDIT_STATUS_REJECTED = "4";
     /** 审核状态常量:审核通过 */
     public static final String AUDIT_STATUS_PASSED = "5";
+    
+    /** 问答状态常量:待回答(问答专用) */
+    public static final String QA_STATUS_WAITING = "6";
+    /** 问答状态常量:回答中(问答专用,有人回答但未确认) */
+    public static final String QA_STATUS_ANSWERING = "7";
+    /** 问答状态常量:已结束(问答专用,已确认最佳答案) */
+    public static final String QA_STATUS_CLOSED = "8";
 
     /** 审核通过所需人数 */
     public static final int AUDIT_PASS_REQUIRED = 3;
@@ -63,6 +63,10 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
     private static final Integer POINT_CHANGE_CREATE = 1;
     /** 积分明细变动类型:审核加分 */
     private static final Integer POINT_CHANGE_AUDIT = 2;
+    /** 问答积分规则类型:提问积分 */
+    private static final String QA_RULE_TYPE_QUESTION = "1";
+    /** 问答积分规则类型:回答积分 */
+    private static final String QA_RULE_TYPE_ANSWER = "2";
 
     @Autowired
     private WorkKnowledgeBaseDynamicInfoDao dynamicInfoDao;
@@ -89,7 +93,10 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
     private WorkKnowledgeExpertDao expertDao;
 
     @Autowired
-    private com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgePointCategoryDao categoryDao;
+    private WorkKnowledgePointCategoryDao categoryDao;
+
+    @Autowired
+    private WorkKnowledgeBaseQaAnswerDao qaAnswerDao;
 
     /**
      * 根据文件id加载附件列表(编辑表单回显用)
@@ -236,36 +243,56 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
         // ========== 审核状态自动判定逻辑 ==========
         // 根据附件有无自动赋值 audit_status:无文件->草稿0,有文件->未审核 1
         // 审核未通过重新编辑保存时也按此规则处理
+        
+        // 判断是否为问答答疑分类
+        boolean isQaCategory = false;
+        if (StringUtils.isNotBlank(shareInfo.getTreeNodeId())) {
+            WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(shareInfo.getTreeNodeId());
+            if (treeInfo != null && "问答答疑".equals(treeInfo.getTreeName())) {
+                isQaCategory = true;
+            }
+        }
+        
         boolean hasValidAttachment = hasValidAttachment(attachments);
         // 编辑时,若前端未提交附件列表(用户未更换文件),回查DB已有附件进行判定
         if (!isNew && (attachments == null || attachments.isEmpty()) && StringUtils.isNotBlank(shareInfo.getId())) {
             List<Workattachment> existingAttachments = findAttachmentsByFileId(shareInfo.getId());
             hasValidAttachment = hasValidAttachment(existingAttachments);
         }
-        if (isNew) {
-            // 新增:无文件草稿0,有文件未审核 1
-            shareInfo.setAuditStatus(hasValidAttachment ? AUDIT_STATUS_PENDING : AUDIT_STATUS_DRAFT);
+        
+        if (isQaCategory) {
+            // ========== 问答答疑分类:无需审核,设置为待回答状态 ==========
+            shareInfo.setAuditStatus(QA_STATUS_WAITING);
             shareInfo.setAuditPassCount(0);
             shareInfo.setAuditRejectCount(0);
-            // 有附件时记录提交审核人为当前登录人,同时初始化本轮审核起始时间
-            if (hasValidAttachment) {
-                shareInfo.setSubmitAuditUserId(UserUtils.getUser().getId());
-                shareInfo.setAuditStartDate(new Date());
-            }
+            // 问答分类不需要设置submitAuditUserId和auditStartDate
         } else {
-            // 编辑:仅当状态为草稿(0)或审核未通过(4)时允许重新判定
-            String currentStatus = shareInfo.getAuditStatus();
-            if (currentStatus != null && (currentStatus.equals(AUDIT_STATUS_DRAFT) || currentStatus.equals(AUDIT_STATUS_REJECTED))) {
+            // ========== 非问答分类:走原有审核流程 ==========
+            if (isNew) {
+                // 新增:无文件草稿0,有文件未审核 1
                 shareInfo.setAuditStatus(hasValidAttachment ? AUDIT_STATUS_PENDING : AUDIT_STATUS_DRAFT);
                 shareInfo.setAuditPassCount(0);
                 shareInfo.setAuditRejectCount(0);
-                // 重新提交时更新提交审核人为当前登录人,并刷新本轮审核起始时间(历史审核记录依靠该时间隔离)
+                // 有附件时记录提交审核人为当前登录人,同时初始化本轮审核起始时间
                 if (hasValidAttachment) {
                     shareInfo.setSubmitAuditUserId(UserUtils.getUser().getId());
                     shareInfo.setAuditStartDate(new Date());
                 }
+            } else {
+                // 编辑:仅当状态为草稿(0)或审核未通过(4)时允许重新判定
+                String currentStatus = shareInfo.getAuditStatus();
+                if (currentStatus != null && (currentStatus.equals(AUDIT_STATUS_DRAFT) || currentStatus.equals(AUDIT_STATUS_REJECTED))) {
+                    shareInfo.setAuditStatus(hasValidAttachment ? AUDIT_STATUS_PENDING : AUDIT_STATUS_DRAFT);
+                    shareInfo.setAuditPassCount(0);
+                    shareInfo.setAuditRejectCount(0);
+                    // 重新提交时更新提交审核人为当前登录人,并刷新本轮审核起始时间(历史审核记录依靠该时间隔离)
+                    if (hasValidAttachment) {
+                        shareInfo.setSubmitAuditUserId(UserUtils.getUser().getId());
+                        shareInfo.setAuditStartDate(new Date());
+                    }
+                }
+                // 审核中(2)和审核通过(5)不允许编辑,由Controller层校验
             }
-            // 审核中(2)和审核通过(5)不允许编辑,由Controller层校验
         }
 
         if (isNew) {
@@ -724,6 +751,74 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
     }
 
     /**
+     * 问答答疑确认最佳答案后发放积分
+     * @param questionId 问题ID
+     * @param answerUserId 回答人ID
+     */
+    @Transactional(readOnly = false)
+    public void grantQaPoints(String questionId, String answerUserId) {
+        // 1. 查询“问答答疑”分类
+        List<WorkKnowledgePointCategory> categories = findCategoriesByName("问答答疑");
+        if (categories == null || categories.isEmpty()) {
+            // 没有配置问答答疑分类,不发放积分
+            return;
+        }
+        
+        // 取第一个匹配的分类
+        WorkKnowledgePointCategory qaCategory = categories.get(0);
+        String categoryId = qaCategory.getId();
+        
+        // 2. 查询提问积分规则(rule_type=1)
+        WorkKnowledgeBasePointRule questionRule = pointRuleDao.findByCategoryIdAndRuleType(categoryId, QA_RULE_TYPE_QUESTION);
+        
+        // 3. 查询回答积分规则(rule_type=2)
+        WorkKnowledgeBasePointRule answerRule = pointRuleDao.findByCategoryIdAndRuleType(categoryId, QA_RULE_TYPE_ANSWER);
+        
+        // 4. 给问题发起人发放积分(使用固定分值)
+        WorkKnowledgeBaseShareInfo question = dao.get(questionId);
+        if (question != null && StringUtils.isNotBlank(question.getSubmitAuditUserId())) {
+            if (questionRule != null && StringUtils.isNotBlank(questionRule.getPointValue())) {
+                try {
+                    Integer questionPoint = Integer.parseInt(questionRule.getPointValue());
+                    if (questionPoint > 0) {
+                        insertPointDetail(
+                            question.getSubmitAuditUserId(),  // 问题发起人ID
+                            questionId,                        // 问题ID
+                            POINT_CHANGE_CREATE,              // 变动类型:创建加分
+                            categoryId,                       // 分类ID
+                            questionRule.getId(),             // 规则ID
+                            questionPoint                     // 积分值
+                        );
+                    }
+                } catch (NumberFormatException e) {
+                    // 积分值格式错误,跳过
+                }
+            }
+        }
+        
+        // 5. 给回答人发放积分(使用固定分值)
+        if (StringUtils.isNotBlank(answerUserId)) {
+            if (answerRule != null && StringUtils.isNotBlank(answerRule.getPointValue())) {
+                try {
+                    Integer answerPoint = Integer.parseInt(answerRule.getPointValue());
+                    if (answerPoint > 0) {
+                        insertPointDetail(
+                            answerUserId,                      // 回答人ID
+                            questionId,                        // 问题ID
+                            POINT_CHANGE_AUDIT,               // 变动类型:审核加分(复用)
+                            categoryId,                       // 分类ID
+                            answerRule.getId(),               // 规则ID
+                            answerPoint                       // 积分值
+                        );
+                    }
+                } catch (NumberFormatException e) {
+                    // 积分值格式错误,跳过
+                }
+            }
+        }
+    }
+
+    /**
      * 逻辑删除文件(含动态字段值)
      */
     @Transactional(readOnly = false)
@@ -854,4 +949,148 @@ public class WorkKnowledgeBaseShareService extends CrudService<WorkKnowledgeBase
         valueInfo.preInsert();
         dynamicValueInfoDao.insert(valueInfo);
     }
+
+    // ==================== 问答答疑相关方法 ====================
+
+    /**
+     * 查询问题的回答列表(时间倒序)
+     * @param questionId 问题ID
+     * @return 回答列表
+     */
+    public List<WorkKnowledgeBaseQaAnswer> findQuestionAnswers(String questionId) {
+        if (StringUtils.isBlank(questionId)) {
+            return new ArrayList<>();
+        }
+        return qaAnswerDao.findAnswersByQuestionId(questionId);
+    }
+
+    /**
+     * 统计问题的回答数量
+     * @param questionId 问题ID
+     * @return 回答数量
+     */
+    public int countQuestionAnswers(String questionId) {
+        if (StringUtils.isBlank(questionId)) {
+            return 0;
+        }
+        return qaAnswerDao.countAnswersByQuestionId(questionId);
+    }
+
+    /**
+     * 提交回答
+     * @param answer 回答对象
+     */
+    @Transactional(readOnly = false)
+    public void submitAnswer(WorkKnowledgeBaseQaAnswer answer) {
+        // 验证问题是否存在且未确认答案
+        WorkKnowledgeBaseShareInfo question = dao.get(answer.getQuestionId());
+        if (question == null) {
+            throw new RuntimeException("问题不存在");
+        }
+        
+        // 检查是否为问答分类
+        boolean isQaCategory = false;
+        if (StringUtils.isNotBlank(question.getTreeNodeId())) {
+            WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(question.getTreeNodeId());
+            if (treeInfo != null && "问答答疑".equals(treeInfo.getTreeName())) {
+                isQaCategory = true;
+            }
+        }
+        
+        if (!isQaCategory) {
+            throw new RuntimeException("该分类不支持问答功能");
+        }
+        
+        // 检查问题状态:待回答(6)或已回答(7)都可以继续回答,已结束(8)不可回答
+        if (QA_STATUS_CLOSED.equals(question.getAuditStatus())) {
+            throw new RuntimeException("该问题已被确认答案,无法继续回答");
+        }
+
+        // 保存回答
+        answer.setId(IdGen.uuid());
+        answer.preInsert();
+        qaAnswerDao.insertAnswer(answer);
+        
+        // 更新问题状态:待回答(6) -> 回答中(7)
+        if (QA_STATUS_WAITING.equals(question.getAuditStatus())) {
+            question.setAuditStatus(QA_STATUS_ANSWERING);
+            question.preUpdate();
+            dao.update(question);
+        }
+    }
+
+    /**
+     * 确认最佳答案
+     * @param questionId 问题ID
+     * @param answerId 答案ID
+     * @return 返回回答人ID(用于发放积分)
+     */
+    @Transactional(readOnly = false)
+    public String confirmBestAnswer(String questionId, String answerId) {
+        // 检查问题状态
+        WorkKnowledgeBaseShareInfo question = dao.get(questionId);
+        if (question == null) {
+            throw new RuntimeException("问题不存在");
+        }
+        
+        // 检查是否为问答分类
+        boolean isQaCategory = false;
+        if (StringUtils.isNotBlank(question.getTreeNodeId())) {
+            WorkKnowledgeBaseTreeInfo treeInfo = getTreeInfoById(question.getTreeNodeId());
+            if (treeInfo != null && "问答答疑".equals(treeInfo.getTreeName())) {
+                isQaCategory = true;
+            }
+        }
+        
+        if (!isQaCategory) {
+            throw new RuntimeException("该分类不支持问答功能");
+        }
+        
+        // 检查问题是否已结束
+        if (QA_STATUS_CLOSED.equals(question.getAuditStatus())) {
+            throw new RuntimeException("该问题已被确认答案,无法重复确认");
+        }
+
+        // 验证答案是否属于该问题
+        WorkKnowledgeBaseQaAnswer answer = qaAnswerDao.get(answerId);
+        if (answer == null || !questionId.equals(answer.getQuestionId())) {
+            throw new RuntimeException("答案不存在或不属于该问题");
+        }
+
+        // 更新问题状态为已结束(8),记录最佳答案ID和确认时间
+        question.setAuditStatus(QA_STATUS_CLOSED);
+        question.setAnswerId(answerId);
+        question.setConfirmDate(new Date());
+        question.preUpdate();
+        dao.update(question);
+
+        // 返回回答人ID,用于后续积分发放
+        return answer.getCreateBy().getId();
+    }
+
+    /**
+     * 检查是否为问题发起人
+     * @param questionId 问题ID
+     * @param userId 用户ID
+     * @return true-是发起人,false-不是发起人
+     */
+    public boolean isQuestionCreator(String questionId, String userId) {
+        WorkKnowledgeBaseShareInfo question = dao.get(questionId);
+        return question != null && userId.equals(question.getCreateBy().getId());
+    }
+
+    /**
+     * 检查问题是否可以回答
+     * @param questionId 问题ID
+     * @return true-可以回答,false-已确认答案不可回答
+     */
+    public boolean canAnswer(String questionId) {
+        WorkKnowledgeBaseShareInfo question = dao.get(questionId);
+        if (question == null) {
+            return false;
+        }
+        // 待回答(6)或回答中(7)可以继续回答,已结束(8)不可回答
+        return QA_STATUS_WAITING.equals(question.getAuditStatus()) 
+            || QA_STATUS_ANSWERING.equals(question.getAuditStatus());
+    }
 }

+ 101 - 5
src/main/java/com/jeeplus/modules/WorkKnowledgeBase/web/WorkKnowledgeBaseShareController.java

@@ -117,15 +117,21 @@ public class WorkKnowledgeBaseShareController extends BaseController {
 
             // 获取treeNodeId对应的treeName,用于前端判断是否需要专家审核
             Object treeNodeIdObj = map.get("treeNodeId");
+            String treeName = null;
             if (treeNodeIdObj != null) {
                 String tid = treeNodeIdObj.toString();
                 WorkKnowledgeBaseTreeInfo treeInfo =
                         shareService.getTreeInfoById(tid);
                 if (treeInfo != null) {
-                    map.put("treeName", treeInfo.getTreeName());
+                    treeName = treeInfo.getTreeName();
+                    map.put("treeName", treeName);
                 }
             }
             
+            // 判断是否为问答答疑分类
+            boolean isQaCategory = "问答答疑".equals(treeName);
+            map.put("isQaCategory", isQaCategory);
+            
             // 获取阅读量和点赞量
             Object fileIdObj = map.get("id");
             if (fileIdObj != null) {
@@ -204,18 +210,23 @@ public class WorkKnowledgeBaseShareController extends BaseController {
             model.addAttribute("auditRecords", shareService.findAuditRecordsByFileId(shareInfo.getId()));
         }
 
-        // 查询treeName,判断是否为需要评分的特殊分类
+        // 查询treeName,判断是否为需要评分的特殊分类或问答答疑分类
         // 注意:必须使用文件本身的treeNodeId,而不是请求参数中的(可能是父节点或空)
         WorkKnowledgeBaseShareInfo workKnowledgeBaseShareInfo = shareService.get(shareInfo.getId());
         if (StringUtils.isNotBlank(workKnowledgeBaseShareInfo.getTreeNodeId())) {
             WorkKnowledgeBaseTreeInfo treeInfo = shareService.getTreeInfoById(workKnowledgeBaseShareInfo.getTreeNodeId());
             if (treeInfo != null) {
-                model.addAttribute("treeName", treeInfo.getTreeName());
+                String treeName = treeInfo.getTreeName();
+                model.addAttribute("treeName", treeName);
+                
+                // 判断是否为问答答疑分类
+                boolean isQaCategory = "问答答疑".equals(treeName);
+                model.addAttribute("isQaCategory", isQaCategory);
                 
                 // 如果是技术总结或培训心得,查询对应的积分规则
-                if ("技术总结".equals(treeInfo.getTreeName()) || "培训心得".equals(treeInfo.getTreeName())) {
+                if ("技术总结".equals(treeName) || "培训心得".equals(treeName)) {
                     // 根据treeName查询work_knowledge_point_category表获取categoryId
-                    List<WorkKnowledgePointCategory> categories = shareService.findCategoriesByName(treeInfo.getTreeName());
+                    List<WorkKnowledgePointCategory> categories = shareService.findCategoriesByName(treeName);
                     if (categories != null && !categories.isEmpty()) {
                         String categoryId = categories.get(0).getId();
                         // 查询该分类下rule_type=2(创建积分)的规则
@@ -500,4 +511,89 @@ public class WorkKnowledgeBaseShareController extends BaseController {
         }
         return result;
     }
+
+    // ==================== 问答答疑相关接口 ====================
+
+    /**
+     * 提交回答(Ajax)
+     */
+    @RequiresPermissions("workKnowledgeBase:share:list")
+    @RequestMapping(value = "submitAnswer")
+    @ResponseBody
+    public Map<String, Object> submitAnswer(WorkKnowledgeBaseQaAnswer answer) {
+        Map<String, Object> result = new HashMap<>();
+        try {
+            // 设置回答人
+            answer.setCreateBy(UserUtils.getUser());
+            // 调用Service保存回答
+            shareService.submitAnswer(answer);
+            result.put("success", true);
+            result.put("message", "回答提交成功");
+        } catch (Exception e) {
+            result.put("success", false);
+            result.put("message", e.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * 确认最佳答案(Ajax)
+     */
+    @RequiresPermissions("workKnowledgeBase:share:list")
+    @RequestMapping(value = "confirmAnswer")
+    @ResponseBody
+    public Map<String, Object> confirmAnswer(@RequestParam String questionId,
+                                             @RequestParam String answerId) {
+        Map<String, Object> result = new HashMap<>();
+        try {
+            // 验证是否为问题发起人
+            String currentUserId = UserUtils.getUser().getId();
+            if (!shareService.isQuestionCreator(questionId, currentUserId)) {
+                result.put("success", false);
+                result.put("message", "只有问题发起人可以确认答案");
+                return result;
+            }
+
+            // Service层会自动处理积分发放逻辑:
+            // 1. 给问题发起人发放积分(rule_type=1,创建积分)
+            // 2. 给回答人发放积分(rule_type=2,回答积分)
+            // 积分规则从“问答答疑”分类对应的配置中读取
+            String answerUserId = shareService.confirmBestAnswer(questionId, answerId);
+
+            // 积分处理:调用Service层方法发放积分
+            shareService.grantQaPoints(questionId, answerUserId);
+
+
+            result.put("success", true);
+            result.put("message", "答案确认成功,积分已发放");
+            result.put("answerUserId", answerUserId);
+        } catch (Exception e) {
+            result.put("success", false);
+            result.put("message", e.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * 查询问题的回答列表(Ajax)
+     */
+    @RequestMapping(value = "getAnswers")
+    @ResponseBody
+    public Map<String, Object> getAnswers(@RequestParam String questionId) {
+        Map<String, Object> result = new HashMap<>();
+        try {
+            List<WorkKnowledgeBaseQaAnswer> answers = shareService.findQuestionAnswers(questionId);
+            int answerCount = shareService.countQuestionAnswers(questionId);
+            boolean canAnswer = shareService.canAnswer(questionId);
+
+            result.put("success", true);
+            result.put("answers", answers);
+            result.put("answerCount", answerCount);
+            result.put("canAnswer", canAnswer);
+        } catch (Exception e) {
+            result.put("success", false);
+            result.put("message", e.getMessage());
+        }
+        return result;
+    }
 }

+ 79 - 0
src/main/resources/mappings/modules/WorkKnowledgeBase/WorkKnowledgeBaseQaAnswerDao.xml

@@ -0,0 +1,79 @@
+<?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="com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBaseQaAnswerDao">
+    
+    <sql id="workKnowledgeBaseQaAnswerColumns">
+        a.id AS "id",
+        a.question_id AS "questionId",
+        a.answer_content AS "answerContent",
+        a.create_by AS "createBy.id",
+        a.create_date AS "createDate",
+        a.update_by AS "updateBy.id",
+        a.update_date AS "updateDate",
+        a.remarks AS "remarks",
+        a.del_flag AS "delFlag",
+        u.name AS "answerUserName",
+        q.name AS "questionTitle",
+        CASE WHEN q.answer_id = a.id THEN 1 ELSE 0 END AS "isBestAnswer"
+    </sql>
+    
+    <sql id="workKnowledgeBaseQaAnswerJoins">
+        LEFT JOIN sys_user u ON u.id = a.create_by AND u.del_flag = '0'
+        LEFT JOIN work_knowledge_base_share_info q ON q.id = a.question_id AND q.del_flag = '0'
+    </sql>
+    
+    <!-- 根据ID查询单条记录 -->
+    <select id="get" resultType="com.jeeplus.modules.WorkKnowledgeBase.entity.WorkKnowledgeBaseQaAnswer">
+        SELECT
+            <include refid="workKnowledgeBaseQaAnswerColumns"/>
+        FROM work_knowledge_base_qa_answer a
+        <include refid="workKnowledgeBaseQaAnswerJoins"/>
+        WHERE a.id = #{id}
+          AND a.del_flag = '0'
+    </select>
+    
+    <!-- 查询某个问题的所有回答列表(时间倒序) -->
+    <select id="findAnswersByQuestionId" resultType="com.jeeplus.modules.WorkKnowledgeBase.entity.WorkKnowledgeBaseQaAnswer">
+        SELECT
+            <include refid="workKnowledgeBaseQaAnswerColumns"/>
+        FROM work_knowledge_base_qa_answer a
+        <include refid="workKnowledgeBaseQaAnswerJoins"/>
+        WHERE a.question_id = #{questionId}
+          AND a.del_flag = '0'
+        ORDER BY a.create_date DESC
+    </select>
+    
+    <!-- 统计问题的回答数量 -->
+    <select id="countAnswersByQuestionId" resultType="int">
+        SELECT COUNT(*)
+        FROM work_knowledge_base_qa_answer
+        WHERE question_id = #{questionId}
+          AND del_flag = '0'
+    </select>
+    
+    <!-- 插入回答 -->
+    <insert id="insertAnswer">
+        INSERT INTO work_knowledge_base_qa_answer (
+            id,
+            question_id,
+            answer_content,
+            create_by,
+            create_date,
+            update_by,
+            update_date,
+            remarks,
+            del_flag
+        ) VALUES (
+            #{id},
+            #{questionId},
+            #{answerContent},
+            #{createBy.id},
+            #{createDate},
+            #{updateBy.id},
+            #{updateDate},
+            #{remarks},
+            #{delFlag}
+        )
+    </insert>
+    
+</mapper>

+ 3 - 1
src/main/resources/mappings/modules/WorkKnowledgeBase/WorkKnowledgeBaseShareInfoDao.xml

@@ -254,7 +254,9 @@
             audit_reject_count = #{auditRejectCount},
             submit_audit_user_id = #{submitAuditUserId},
             content_attribute = #{contentAttribute},
-            audit_start_date   = #{auditStartDate}
+            audit_start_date   = #{auditStartDate},
+            answer_id          = #{answerId},
+            confirm_date       = #{confirmDate}
         WHERE id = #{id}
     </update>
 

+ 13 - 0
src/main/resources/mysqlDateBase/alter_work_knowledge_base_share_info_add_qa_fields.sql

@@ -0,0 +1,13 @@
+-- 为知识库分享信息表添加问答答疑相关字段
+-- 说明: 用于存储问答答疑的最佳答案ID和状态
+
+-- 添加最佳答案ID字段
+ALTER TABLE work_knowledge_base_share_info 
+ADD COLUMN answer_id VARCHAR(64) DEFAULT NULL COMMENT '最佳答案ID(问答答疑专用)' AFTER audit_status;
+
+-- 添加确认答案时间字段
+ALTER TABLE work_knowledge_base_share_info 
+ADD COLUMN confirm_date DATETIME DEFAULT NULL COMMENT '确认答案时间(问答答疑专用)' AFTER answer_id;
+
+-- 添加索引优化查询性能
+CREATE INDEX idx_answer_id ON work_knowledge_base_share_info(answer_id);

+ 18 - 0
src/main/resources/mysqlDateBase/create_work_knowledge_base_qa_answer_table.sql

@@ -0,0 +1,18 @@
+-- 知识库-问答答疑回答表
+-- 说明: 问题复用 work_knowledge_base_share_info 表,只需创建回答表
+
+CREATE TABLE IF NOT EXISTS work_knowledge_base_qa_answer (
+    id VARCHAR(64) NOT NULL COMMENT '主键ID',
+    question_id VARCHAR(64) NOT NULL COMMENT '问题ID(关联work_knowledge_base_share_info.id)',
+    answer_content TEXT NOT NULL COMMENT '回答内容',
+    create_by VARCHAR(64) DEFAULT NULL COMMENT '创建人ID(回答人)',
+    create_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_by VARCHAR(64) DEFAULT NULL COMMENT '更新人ID',
+    update_date DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    remarks VARCHAR(1000) DEFAULT NULL COMMENT '备注',
+    del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志:0-正常,1-删除',
+    PRIMARY KEY (id),
+    KEY idx_question_id (question_id),
+    KEY idx_create_by (create_by),
+    KEY idx_create_date (create_date)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库-问答答疑回答表';

+ 46 - 0
src/main/resources/mysqlDateBase/create_work_knowledge_base_qa_tables.sql

@@ -0,0 +1,46 @@
+-- ============================================
+-- 知识库-问答答疑模块数据表
+-- 作者:徐滕
+-- 日期:2026-06-17
+-- 说明:问答答疑功能,无需审核,支持多人回答,发起人确认最佳答案
+-- ============================================
+
+-- 1. 问答问题表
+CREATE TABLE IF NOT EXISTS work_knowledge_base_qa_question (
+    id VARCHAR(64) NOT NULL COMMENT '主键ID',
+    tree_node_id VARCHAR(64) DEFAULT NULL COMMENT '所属节点ID(问答答疑分类对应的节点)',
+    question_title VARCHAR(500) NOT NULL COMMENT '问题标题',
+    question_content TEXT COMMENT '问题详细描述',
+    answer_user_id VARCHAR(64) DEFAULT NULL COMMENT '被选中的最佳答案回答人ID',
+    answer_id VARCHAR(64) DEFAULT NULL COMMENT '被选中的最佳答案ID',
+    status CHAR(1) DEFAULT '0' COMMENT '状态:0-待回答,1-已解决(已确认答案)',
+    confirm_date DATETIME DEFAULT NULL COMMENT '确认答案时间',
+    create_by VARCHAR(64) DEFAULT NULL COMMENT '创建人ID(问题发起人)',
+    create_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_by VARCHAR(64) DEFAULT NULL COMMENT '更新人ID',
+    update_date DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    remarks VARCHAR(1000) DEFAULT NULL COMMENT '备注',
+    del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志:0-正常,1-删除',
+    PRIMARY KEY (id),
+    KEY idx_tree_node_id (tree_node_id),
+    KEY idx_create_by (create_by),
+    KEY idx_status (status),
+    KEY idx_create_date (create_date)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库-问答答疑问题表';
+
+-- 2. 问答回答表
+CREATE TABLE IF NOT EXISTS work_knowledge_base_qa_answer (
+    id VARCHAR(64) NOT NULL COMMENT '主键ID',
+    question_id VARCHAR(64) NOT NULL COMMENT '问题ID',
+    answer_content TEXT NOT NULL COMMENT '回答内容',
+    create_by VARCHAR(64) DEFAULT NULL COMMENT '创建人ID(回答人)',
+    create_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_by VARCHAR(64) DEFAULT NULL COMMENT '更新人ID',
+    update_date DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    remarks VARCHAR(1000) DEFAULT NULL COMMENT '备注',
+    del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志:0-正常,1-删除',
+    PRIMARY KEY (id),
+    KEY idx_question_id (question_id),
+    KEY idx_create_by (create_by),
+    KEY idx_create_date (create_date)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库-问答答疑回答表';

+ 211 - 0
src/main/webapp/webpage/modules/WorkKnowledgeBase/workKnowledgeBaseShareDetail.jsp

@@ -447,8 +447,43 @@
 </div>
 </c:if>
 
+<!-- 问答答疑区域(仅当为问答分类时显示) -->
+<c:if test="${isQaCategory}">
+<div class="single-form" style="margin-top:10px;">
+    <div class="container">
+        <!-- 提交回答区域 -->
+        <div class="form-group layui-row">
+            <div class="form-group-label"><h2>发表回答</h2></div>
+            <div class="layui-item layui-col-xs12 with-textarea">
+                <div class="layui-input-block" style="margin-left:0px;position: relative">
+                    <textarea id="answerContent" placeholder="请输入回答内容:" class="form-control required" rows="4" maxlength="2000"></textarea>
+                </div>
+                <div style="float: right;margin-right: 10%">
+                    <br/>
+                    <a href="javascript:void(0)" id="submitAnswerBtn" class="layui-btn" onclick="submitAnswer()">发表</a>
+                    <a href="javascript:void(0)" id="resetAnswerBtn" class="layui-btn layui-btn-primary" onclick="resetAnswer()">清空</a>
+                </div>
+            </div>
+        </div>
+
+        <!-- 回答列表区域 -->
+        <div class="form-group layui-row">
+            <h2>全部回答(<span id="answerTotalCount">0</span>条)</h2>
+            <hr>
+            <div id="answerListContainer"></div>
+        </div>
+    </div>
+</div>
+</c:if>
+
 <script src="${ctxStatic}/layer-v2.3/layui/layui.all.js" charset="utf-8"></script>
 <script>
+    // 问答相关变量
+    var isQaCategory = ${isQaCategory != null ? isQaCategory : false};
+    var questionId = '${entity.id}';
+    var currentUserId = '${fns:getUser().id}';
+    var questionCreatorId = '${entity.createBy.id}';
+
     /** 显示阅读详情弹窗 */
     function showReadDetail(fileId) {
         top.layer.open({
@@ -474,6 +509,182 @@
             btn1: function(index) { top.layer.close(index); }
         });
     }
+
+    // ========== 问答相关函数 ==========
+
+    /** 加载回答列表 */
+    function loadAnswers() {
+        if (!isQaCategory) return;
+        
+        $.ajax({
+            type: 'GET',
+            url: '${ctx}/workKnowledgeBase/share/getAnswers',
+            data: { questionId: questionId },
+            success: function(data) {
+                if (data.success) {
+                    renderAnswers(data.answers);
+                    $('#answerTotalCount').text(data.answerCount);
+                    
+                    // 如果已确认答案,隐藏发表回答框和所有"确认最佳答案"按钮
+                    if (!data.canAnswer) {
+                        // 隐藏整个发表回答区域
+                        $('.form-group-label:has(h2)').filter(function() {
+                            return $(this).text().indexOf('发表回答') !== -1;
+                        }).parent().hide();
+                    }
+                }
+            }
+        });
+    }
+
+    /** 渲染回答列表 */
+    function renderAnswers(answers) {
+        var html = '';
+        if (answers && answers.length > 0) {
+            // 分离最佳答案和其他答案
+            var bestAnswer = null;
+            var otherAnswers = [];
+            
+            for (var i = 0; i < answers.length; i++) {
+                var ans = answers[i];
+                if (ans.isBestAnswer == 1) {
+                    bestAnswer = ans;
+                } else {
+                    otherAnswers.push(ans);
+                }
+            }
+            
+            // 如果有最佳答案,先渲染最佳答案(置顶)
+            if (bestAnswer) {
+                html += renderAnswerItem(bestAnswer, true);
+            }
+            
+            // 渲染其他答案
+            for (var i = 0; i < otherAnswers.length; i++) {
+                html += renderAnswerItem(otherAnswers[i], false);
+            }
+        } else {
+            html = '<div style="text-align:center;color:#999;padding:20px;">暂无回答</div>';
+        }
+        $('#answerListContainer').html(html);
+    }
+    
+    /** 渲染单个回答项 */
+    function renderAnswerItem(ans, isBest) {
+        var isCreator = ans.createBy && ans.createBy.id == questionCreatorId;
+        var html = '';
+        
+        // 最佳答案使用特殊背景色突出显示
+        if (isBest) {
+            html += '<div class="media" style="padding:15px;border:2px solid #52c41a;background-color:#f6ffed;margin-bottom:15px;border-radius:5px;">';
+        } else {
+            html += '<div class="media" style="padding:15px;border-bottom:1px solid #eee;">';
+        }
+        html += '  <div class="media-body">';
+        
+        // 最佳答案标识
+        if (isBest) {
+            html += '    <div style="color:#52c41a;font-weight:bold;font-size:16px;margin-bottom:8px;">✓ 最佳答案</div>';
+        }
+        
+        // 回答人信息
+        html += '    <div style="color:#666;margin-bottom:8px;">';
+        html += '      <span style="font-weight:bold;color:' + (isBest ? '#52c41a' : '#333') + ';">' + (ans.answerUserName || '未知') + '</span>';
+        html += '      <span style="margin-left:10px;color:#999;">' + (ans.createDate || '') + '</span>';
+        if (isCreator) {
+            html += '      <span style="margin-left:10px;color:#1890ff;">(提问者)</span>';
+        }
+        html += '    </div>';
+        
+        // 回答内容(最佳答案字体颜色更深)
+        if (isBest) {
+            html += '    <div style="color:#333;line-height:1.8;font-size:15px;">' + (ans.answerContent || '') + '</div>';
+        } else {
+            html += '    <div style="color:#333;line-height:1.8;">' + (ans.answerContent || '') + '</div>';
+        }
+        
+        // 确认按钮(仅发起人且未确认答案时显示)
+        if (!isBest && currentUserId == questionCreatorId) {
+            html += '    <div style="margin-top:10px;">';
+            html += '      <a href="javascript:void(0)" onclick="confirmAnswer(\'' + ans.id + '\')" class="layui-btn layui-btn-xs layui-bg-green">确认为最佳答案</a>';
+            html += '    </div>';
+        }
+        
+        html += '  </div>';
+        html += '</div>';
+        return html;
+    }
+
+    /** 提交回答 */
+    function submitAnswer() {
+        var content = $('#answerContent').val().trim();
+        if (!content) {
+            layer.msg('请输入回答内容', {icon: 2});
+            return;
+        }
+        
+        $.ajax({
+            type: 'POST',
+            url: '${ctx}/workKnowledgeBase/share/submitAnswer',
+            data: {
+                questionId: questionId,
+                answerContent: content
+            },
+            success: function(data) {
+                if (data.success) {
+                    layer.msg('回答提交成功', {icon: 1});
+                    $('#answerContent').val('');
+                    loadAnswers(); // 刷新列表
+                } else {
+                    layer.msg(data.message || '回答失败', {icon: 2});
+                }
+            },
+            error: function() {
+                layer.msg('请求失败,请重试', {icon: 2});
+            }
+        });
+    }
+
+    /** 清空回答 */
+    function resetAnswer() {
+        $('#answerContent').val('');
+    }
+
+    /** 确认最佳答案 */
+    function confirmAnswer(answerId) {
+        layer.confirm('确认选择此答案为最佳答案吗?确认后问题将关闭,其他人无法继续回答。', {
+            icon: 3,
+            title: '确认提示'
+        }, function(index){
+            $.ajax({
+                type: 'POST',
+                url: '${ctx}/workKnowledgeBase/share/confirmAnswer',
+                data: {
+                    questionId: questionId,
+                    answerId: answerId
+                },
+                success: function(data) {
+                    if (data.success) {
+                        layer.msg('答案确认成功,积分已发放', {icon: 1});
+                        layer.close(index);
+                        loadAnswers(); // 刷新列表
+                    } else {
+                        layer.msg(data.message || '确认失败', {icon: 2});
+                    }
+                },
+                error: function() {
+                    layer.msg('请求失败,请重试', {icon: 2});
+                }
+            });
+        });
+    }
+
+    // 页面加载完成后,如果是问答分类,则加载回答列表
+    $(document).ready(function() {
+        if (isQaCategory) {
+            loadAnswers();
+        }
+    });
 </script>
 
 </body>

+ 73 - 38
src/main/webapp/webpage/modules/WorkKnowledgeBase/workKnowledgeBaseShareList.jsp

@@ -1,4 +1,4 @@
-<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ page contentType="text/html;charset=UTF-8" %>
 <%@ include file="/webpage/include/taglib.jsp"%>
 <html>
 <head>
@@ -398,8 +398,8 @@
 
         /** 获取审核状态显示配置(label值对应 style.css 中 status-label-xxx 样式) */
         function getWorkKnowledgeAuditState(auditStatus) {
-            var statusMap = {0:'草稿', 1:'未审核', 2:'审核中', 4:'审核未通过', 5:'审核通过'};
-            var labelMap = {0:'tempstore', 1:'tempstore', 2:'auditing', 4:'reject', 5:'signed'};
+            var statusMap = {0:'草稿', 1:'未审核', 2:'审核中', 4:'审核未通过', 5:'审核通过', 6:'待回答', 7:'回答中', 8:'已结束'};
+            var labelMap = {0:'tempstore', 1:'tempstore', 2:'auditing', 4:'reject', 5:'signed', 6:'tempstore', 7:'auditing', 8:'signed'};
             return {
                 status: statusMap[auditStatus] || '未知',
                 label: labelMap[auditStatus] || 'unknown'
@@ -622,50 +622,84 @@
                 {align: 'center', title: '操作', width: 260, fixed: 'right', templet: function(d) {
                 var xml = '<div class="layui-btn-group">';
                 var status = parseInt(d.auditStatus);
-                var isCreator = (d.submitAuditUserId) === d.userid;
-                            
+                // 判断是否为记录所有者(可撤回/修改/删除):
+                // 如果submitAuditUserId为空,则判定当前用户是否是创建人
+                // 如果submitAuditUserId有值,则判定当前用户是否是最后修改人(提交人)
+                var isCreator = (d.submitAuditUserId && d.submitAuditUserId !== '') 
+                    ? (d.submitAuditUserId === d.userid) 
+                    : (d.createbyid === d.userid);
+
                 // 判断是否为需要专家审核的特殊分类
                 var needsExpertAudit = (d.treeName === '技术总结' || d.treeName === '培训心得');
                 // 判断当前用户是否有审核权限(管理员、专家或普通分类)
                 var canAudit = !isCreator && !d.currentUserAudited && (d.isAdmin || d.isExpert || !needsExpertAudit);
             
-                // 草稿0:可修改、删除
-                if (status === 0) {
-                    xml += '<a href="javascript:void(0)" onclick="openDialog(\'编辑文件\',\'${ctx}/workKnowledgeBase/share/form?id=' + d.id + '&treeNodeId=${treeNodeId}&rootId=${rootId}\',\'80%\',\'80%\')" class="layui-btn layui-btn-xs layui-bg-green"> 修改</a>';
-                    xml += '<a href="javascript:void(0)" onclick="deleteShare(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-red"> 删除</a>';
-                }
-            
-                // 未审核 1/审核中2:撤回(提交审核人/管理员)、查看/审核(非提交审核人且有权限)
-                if (status === 1 || status === 2) {
-                    // 撤回:提交审核人/管理员
-                    if (isCreator || d.isAdmin) {
-                        xml += '<a href="javascript:void(0)" onclick="withdrawAudit(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-orange"> 撤回</a>';
+                // ========== 问答答疑分类特殊处理 ==========
+                if (d.isQaCategory) {
+                    // 问答分类:不显示审核相关按钮,不显示“查看”按钮
+                    
+                    var qaStatus = parseInt(d.auditStatus);
+
+                    // 仅创建人可以撤回、修改、删除(确认答案前)
+                    if (isCreator) {
+                        // 待回答(6)或回答中(7)状态:显示撤回和确认答案按钮
+                        if (qaStatus === 7) {
+                            xml += '<a href="javascript:void(0)" onclick="openDetailDialogWithRead(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-green"> 确认答案</a>';
+                        }
+                        if (qaStatus === 6 || qaStatus === 7) {
+                            xml += '<a href="javascript:void(0)" onclick="withdrawAudit(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-orange"> 撤回</a>';
+                        }
+                        // 撤回后状态变为草稿(0):显示修改和删除按钮
+                        if (qaStatus === 0) {
+                            xml += '<a href="javascript:void(0)" onclick="openDialog(\'编辑文件\',\'${ctx}/workKnowledgeBase/share/form?id=' + d.id + '&treeNodeId=${treeNodeId}&rootId=${rootId}\',\'80%\',\'80%\')" class="layui-btn layui-btn-xs layui-bg-green"> 修改</a>';
+                            xml += '<a href="javascript:void(0)" onclick="deleteShare(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-red"> 删除</a>';
+                        }
                     }
-                    // 审核:非提交审核人且当前登录人未审核过且有权限
-                    if (canAudit) {
-                        xml += '<a href="javascript:void(0)" onclick="openAuditDetailDialog(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-blue"> 审核</a>';
+                    
+                    // 非创建人且问题未结束时,显示回答按钮
+                    if (!isCreator && (qaStatus === 6 || qaStatus === 7)) {
+                        xml += '<a href="javascript:void(0)" onclick="openDetailDialogWithRead(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-orange"> 回答</a>';
+                    }
+                } else {
+                    // ========== 非问答分类:原有审核流程 ==========
+                    // 草稿0:可修改、删除
+                    if (status === 0) {
+                        xml += '<a href="javascript:void(0)" onclick="openDialog(\'编辑文件\',\'${ctx}/workKnowledgeBase/share/form?id=' + d.id + '&treeNodeId=${treeNodeId}&rootId=${rootId}\',\'80%\',\'80%\')" class="layui-btn layui-btn-xs layui-bg-green"> 修改</a>';
+                        xml += '<a href="javascript:void(0)" onclick="deleteShare(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-red"> 删除</a>';
+                    }
+                
+                    // 未审核 1/审核中2:撤回(提交审核人/管理员)、查看/审核(非提交审核人且有权限)
+                    if (status === 1 || status === 2) {
+                        // 撤回:提交审核人/管理员
+                        if (isCreator || d.isAdmin) {
+                            xml += '<a href="javascript:void(0)" onclick="withdrawAudit(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-orange"> 撤回</a>';
+                        }
+                        // 审核:非提交审核人且当前登录人未审核过且有权限
+                        if (canAudit) {
+                            xml += '<a href="javascript:void(0)" onclick="openAuditDetailDialog(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-blue"> 审核</a>';
+                        }
+                    }
+                
+                    // 审核未通过4:可修改、删除
+                    if (status === 4) {
+                        xml += '<a href="javascript:void(0)" onclick="openDialog(\'编辑文件\',\'${ctx}/workKnowledgeBase/share/form?id=' + d.id + '&treeNodeId=${treeNodeId}&rootId=${rootId}\',\'80%\',\'80%\')" class="layui-btn layui-btn-xs layui-bg-green"> 修改</a>';
+                        xml += '<a href="javascript:void(0)" onclick="deleteShare(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-red"> 删除</a>';
+                    }
+                
+                    // 审核通过5:普通用户仅查看,管理员可修改删除
+                    if (status === 5 && d.isAdmin) {
+                        xml += '<a href="javascript:void(0)" onclick="openDialog(\'编辑文件\',\'${ctx}/workKnowledgeBase/share/form?id=' + d.id + '&treeNodeId=${treeNodeId}&rootId=${rootId}\',\'80%\',\'80%\')" class="layui-btn layui-btn-xs layui-bg-green"> 修改</a>';
+                        xml += '<a href="javascript:void(0)" onclick="deleteShare(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-red"> 删除</a>';
+                    }
+                
+                    // 审核记录按钮(审核中和审核通过/未通过时展示)
+                    if (status === 1 || status === 2 || status === 4 || status === 5) {
+                        xml += '<a href="javascript:void(0)" onclick="viewAuditRecords(\'' + d.id + '\')" class="layui-btn layui-btn-xs"> 记录</a>';
                     }
-                }
-            
-                // 审核未通过4:可修改、删除
-                if (status === 4) {
-                    xml += '<a href="javascript:void(0)" onclick="openDialog(\'编辑文件\',\'${ctx}/workKnowledgeBase/share/form?id=' + d.id + '&treeNodeId=${treeNodeId}&rootId=${rootId}\',\'80%\',\'80%\')" class="layui-btn layui-btn-xs layui-bg-green"> 修改</a>';
-                    xml += '<a href="javascript:void(0)" onclick="deleteShare(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-red"> 删除</a>';
-                }
-            
-                // 审核通过5:普通用户仅查看,管理员可修改删除
-                if (status === 5 && d.isAdmin) {
-                    xml += '<a href="javascript:void(0)" onclick="openDialog(\'编辑文件\',\'${ctx}/workKnowledgeBase/share/form?id=' + d.id + '&treeNodeId=${treeNodeId}&rootId=${rootId}\',\'80%\',\'80%\')" class="layui-btn layui-btn-xs layui-bg-green"> 修改</a>';
-                    xml += '<a href="javascript:void(0)" onclick="deleteShare(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-red"> 删除</a>';
-                }
-            
-                // 审核记录按钮(审核中和审核通过/未通过时展示)
-                if (status === 1 || status === 2 || status === 4 || status === 5) {
-                    xml += '<a href="javascript:void(0)" onclick="viewAuditRecords(\'' + d.id + '\')" class="layui-btn layui-btn-xs"> 记录</a>';
                 }
                 
-                // 点赞/取消点赞按钮(仅审核通过的文件显示)
-                if (status === 5) {
+                // 点赞/取消点赞按钮(仅审核通过的文件显示,问答分类不需要)
+                if (!d.isQaCategory && status === 5) {
                     if (d.liked) {
                         xml += '<a href="javascript:void(0)" onclick="cancelLikeFile(\'' + d.id + '\')" class="layui-btn layui-btn-xs layui-bg-orange"><i class="fa fa-heart"></i> 已赞</a>';
                     } else {
@@ -709,6 +743,7 @@
                     ,"auditStatus": ${row.auditStatus != null ? row.auditStatus : 0}
                     ,"treeNodeId": "${row.treeNodeId}"
                     ,"treeName": "<c:out value='${row.treeName}'/>"
+                    ,"isQaCategory": ${row.isQaCategory == true ? 'true' : 'false'}
                     ,"readCount": ${row.readCount != null ? row.readCount : 0}
                     ,"likeCount": ${row.likeCount != null ? row.likeCount : 0}
                     ,"liked": ${row.liked == true ? 'true' : 'false'}