问答答疑多选最佳答案功能实现说明.md 9.7 KB

问答答疑多选最佳答案功能实现说明

功能概述

将原有的单选最佳答案功能升级为多选最佳答案功能,支持以下特性:

  • ✅ 最多可选择5条内容作为最佳答案
  • ✅ 五条内容可以是同一人
  • ✅ 每个内容均进行赋分
  • ✅ 不限制每个人只能获得1次的分数
  • ✅ 统一提交后对回答人和提问人员进行赋分

数据库变更

1. 新建表: work_knowledge_base_best_answer

文件: alter_work_knowledge_base_add_best_answers.sql

CREATE TABLE IF NOT EXISTS work_knowledge_base_best_answer (
    id VARCHAR(64) NOT NULL COMMENT '主键ID',
    question_id VARCHAR(64) NOT NULL COMMENT '问题ID',
    answer_id VARCHAR(64) NOT NULL COMMENT '答案ID',
    sort_order INT DEFAULT 0 COMMENT '排序号',
    create_by VARCHAR(64) COMMENT '创建人ID',
    create_date DATETIME COMMENT '创建时间',
    del_flag CHAR(1) DEFAULT '0' COMMENT '删除标记',
    PRIMARY KEY (id),
    KEY idx_question_id (question_id),
    KEY idx_answer_id (answer_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='问答最佳答案关联表';

说明:

  • work_knowledge_base_share_info表中的answer_id字段保留但不再使用
  • 新表支持一对多关系,一个问题可对应多条最佳答案

后端实现

1. 实体类 - WorkKnowledgeBaseBestAnswer.java

位置: src/main/java/com/jeeplus/modules/WorkKnowledgeBase/entity/WorkKnowledgeBaseBestAnswer.java

主要字段:

  • questionId: 问题ID
  • answerId: 答案ID
  • sortOrder: 排序号
  • answerContent: 回答内容(关联查询)
  • answerUserName: 回答人姓名(关联查询)
  • answerUserId: 回答人ID(关联查询)

2. DAO层 - WorkKnowledgeBaseBestAnswerDao.java

位置: src/main/java/com/jeeplus/modules/WorkKnowledgeBase/dao/WorkKnowledgeBaseBestAnswerDao.java

主要方法:

  • findByQuestionId(): 根据问题ID查询最佳答案列表
  • countByQuestionId(): 统计问题的最佳答案数量
  • isBestAnswer(): 检查答案是否已被设为最佳答案
  • batchInsert(): 批量插入最佳答案
  • deleteByQuestionId(): 删除问题的所有最佳答案

3. MyBatis映射 - WorkKnowledgeBaseBestAnswerDao.xml

位置: src/main/resources/mappings/modules/WorkKnowledgeBase/WorkKnowledgeBaseBestAnswerDao.xml

关键SQL:

  • 关联查询最佳答案及回答内容、回答人信息
  • 批量插入最佳答案记录

4. Service层 - WorkKnowledgeBaseShareService.java

新增方法:

confirmMultipleBestAnswers(String questionId, List answerIds)

/**
 * 确认多个最佳答案(最多5条)
 * @param questionId 问题ID
 * @param answerIds 答案ID列表
 * @return 返回所有回答人ID列表(不重复)
 */

核心逻辑:

  1. 验证参数(至少1条,最多5条)
  2. 检查问题状态和分类
  3. 验证每个答案是否存在且属于该问题
  4. 删除旧的最佳答案记录
  5. 批量插入新的最佳答案记录
  6. 更新问题状态为已结束(8)
  7. 收集并返回所有不重复的回答人ID

findBestAnswers(String questionId)

  • 查询问题的最佳答案列表

isBestAnswer(String questionId, String answerId)

  • 检查答案是否已被设为最佳答案

修改方法:

grantQaPointsToMultipleUsers(String questionId, List answerUserIds)

/**
 * 给问答参与人员发放积分(支持多个回答人)
 * @param questionId 问题ID
 * @param answerUserIds 所有回答人ID列表(不重复)
 */

核心逻辑:

  1. 查询"问答答疑"分类配置
  2. 获取提问积分规则和回答积分规则
  3. 给问题发起人发放积分(一次)
  4. 遍历每个回答人发放积分(同一个人如果有多条最佳答案,会多次发放)

5. Controller层 - WorkKnowledgeBaseShareController.java

修改接口:

confirmMultipleBestAnswers()

@RequestMapping(value = "confirmMultipleBestAnswers")
@ResponseBody
public Map<String, Object> confirmMultipleBestAnswers(
    @RequestParam String questionId,
    @RequestParam List<String> answerIds)

处理流程:

  1. 验证是否为问题发起人
  2. 调用Service保存多条最佳答案
  3. 调用Service发放积分给所有回答人
  4. 返回成功信息和回答人数量

前端实现

1. 详情页 - workKnowledgeBaseShareDetail.jsp

UI变更:

新增确认区域:

<div id="confirmBestAnswersArea" style="display:none;">
    <div style="text-align:right;">
        <span>已选择 <strong id="selectedCount">0</strong>/5 条</span>
        <button id="batchConfirmBtn" disabled>确认选择的答案</button>
    </div>
</div>

回答项变更:

  • 原"确认为最佳答案"链接改为复选框
  • 复选框样式: <input type="checkbox" class="best-answer-checkbox">
  • 仅问题发起人且未确认答案时显示复选框

JavaScript变更:

新增变量:

var hasConfirmedAnswers = false; // 是否已确认答案

新增函数:

bindCheckboxEvents()

  • 监听复选框变化
  • 限制最多选择5条
  • 实时更新已选择数量
  • 启用/禁用确认按钮

batchConfirmBestAnswers()

  • 收集所有选中的答案ID
  • 调用后端API批量确认
  • 成功后刷新列表并隐藏确认区域

修改函数:

renderAnswers()

  • 渲染完成后绑定复选框事件
  • 显示确认区域(仅问题发起人)
  • 更新hasConfirmedAnswers全局变量

renderAnswerItem()

  • 将"确认最佳答案"链接改为复选框
  • 条件判断使用hasConfirmedAnswers替代hasBestAnswer

2. 列表页 - workKnowledgeBaseShareList.jsp

变更:

确认答案按钮:

  • 原来: openDetailDialogWithRead() - 只读模式
  • 现在: openQaDetailDialogWithRead() - 带确认功能的只读模式
  • 目的: 打开详情页让用户选择多个最佳答案

积分发放规则

发放时机

  • 问题发起人确认最佳答案后立即发放

发放对象

1. 问题发起人

  • 发放一次提问积分(rule_type=1)
  • 从"问答答疑"分类的积分规则中读取固定分值

2. 回答人

  • 每条最佳答案都发放回答积分(rule_type=2)
  • 五条内容可以是同一人
  • 每个内容均进行赋分
  • 不限制每个人只能获得1次的分数

示例场景

场景1: 5条最佳答案来自5个不同的人

  • 问题发起人获得1次提问积分
  • 5个回答人各获得1次回答积分

场景2: 5条最佳答案全部来自同一个人

  • 问题发起人获得1次提问积分
  • 该回答人获得5次回答积分(5 × 单次积分值)

场景3: 3条最佳答案,其中2条来自A,1条来自B

  • 问题发起人获得1次提问积分
  • A获得2次回答积分
  • B获得1次回答积分

使用说明

操作流程

  1. 问题发起人点击"确认答案"按钮
  2. 系统打开详情页,显示所有回答
  3. 每个回答右侧显示复选框"选择为最佳答案"
  4. 发起人勾选最多5条最佳答案
  5. 页面实时显示已选择数量(X/5)
  6. 点击"确认选择的答案"按钮
  7. 系统提示确认信息
  8. 确认后:
    • 问题状态变为"已结束"(8)
    • 最佳答案被标记
    • 积分自动发放
    • 其他人无法继续回答

限制条件

  • ✅ 只有问题发起人可以确认答案
  • ✅ 最多选择5条最佳答案
  • ✅ 至少选择1条最佳答案
  • ✅ 确认后不可撤销
  • ✅ 问题结束后无法继续回答

技术要点

1. 数据去重

  • Service层使用HashSet收集回答人ID
  • 确保即使同一人有多条最佳答案,在列表中只出现一次
  • 但积分发放时会遍历原始答案列表,保证多次发放

2. 事务控制

  • @Transactional(readOnly = false)确保原子性
  • 最佳答案保存和积分发放要么全部成功,要么全部失败

3. 批量操作

  • 使用MyBatis的<foreach>标签实现批量插入
  • 减少数据库交互次数,提高性能

4. 前端交互

  • 使用jQuery事件委托(on('change'))绑定动态元素
  • 实时反馈用户操作(计数更新、按钮启用/禁用)
  • Layer弹窗提供友好的确认提示

测试建议

功能测试

  1. 基本功能

    • 选择1-5条最佳答案并确认
    • 尝试选择6条,验证限制
    • 不选择任何答案,验证提示
  2. 积分发放

    • 5条答案来自5个人,验证每人获得1次积分
    • 5条答案来自1个人,验证该人获得5次积分
    • 混合场景,验证积分正确发放
  3. 边界情况

    • 问题已确认后再次确认,验证拦截
    • 非发起人尝试确认,验证权限
    • 只读模式打开,验证复选框不显示

性能测试

  • 大量答案场景下的选择性能
  • 批量插入5条记录的数据库性能
  • 并发确认同一问题的锁机制

注意事项

  1. 数据库迁移

    • 执行SQL脚本前备份数据
    • 建议在测试环境先验证
  2. 兼容性

    • 旧的单条最佳答案数据仍然保留在answer_id字段
    • 新数据存储在关联表中
    • 后续可编写迁移脚本将旧数据迁移到新表
  3. 日志记录

    • 建议在Service层添加操作日志
    • 记录谁在何时确认了哪些答案为最佳
  4. 权限控制

    • 已有@RequiresPermissions注解保护
    • 前端也进行了发起人身份验证
    • 双重保障确保安全

相关文件清单

后端文件

  • WorkKnowledgeBaseBestAnswer.java - 实体类
  • WorkKnowledgeBaseBestAnswerDao.java - DAO接口
  • WorkKnowledgeBaseBestAnswerDao.xml - MyBatis映射
  • WorkKnowledgeBaseShareService.java - Service层(修改)
  • WorkKnowledgeBaseShareController.java - Controller层(修改)

前端文件

  • workKnowledgeBaseShareDetail.jsp - 详情页(修改)
  • workKnowledgeBaseShareList.jsp - 列表页(修改)

数据库脚本

  • alter_work_knowledge_base_add_best_answers.sql - 建表脚本

完成日期: 2026-01-16
版本: v1.0
作者: AI Assistant