文章目录
-
- 在现代团队协作中,任务优先级管理是决定工作效率的关键因素。根据项目管理协会(PMI)的调查,超过37%的项目失败是由于需求优先级不明确造成的。对于使用WordPress搭建的企业网站、团队协作平台或项目管理门户而言,集成一个在线任务投票与优先级排序工具可以显著提升团队决策效率。 传统的任务优先级确定方式往往依赖于少数管理者的主观判断,而在线投票工具能够让每个团队成员参与决策过程,通过集体智慧确定任务的真实优先级。这不仅提高了决策的透明度,也增强了团队成员的责任感和参与感。 本文将带领你通过WordPress代码二次开发,实现一个功能完整的在线团队任务投票与优先级排序工具。我们将从需求分析开始,逐步完成数据库设计、前端界面开发、后端逻辑实现以及安全性优化,最终打造一个适合团队协作的实用工具。
-
- 在开始开发之前,我们需要确保WordPress环境已正确配置: 本地开发环境:建议使用Local by Flywheel、XAMPP或MAMP搭建本地WordPress环境 WordPress版本:确保使用最新版本的WordPress(5.8+) 代码编辑器:推荐使用VS Code、PHPStorm或Sublime Text 浏览器开发者工具:用于调试前端代码
- 我们的在线团队任务投票与优先级排序工具需要包含以下核心功能: 任务管理功能: 创建、编辑、删除任务 任务描述、截止日期、负责人设置 任务分类与标签 投票系统功能: 团队成员对任务进行投票 支持多种投票方式(优先级评分、简单投票等) 实时显示投票结果 优先级计算功能: 根据投票结果自动计算任务优先级 支持多种优先级算法(加权平均、多数决等) 可视化优先级展示 用户权限管理: 不同用户角色权限控制 投票权限管理 任务管理权限控制 数据可视化功能: 任务优先级看板 投票结果图表 团队参与度统计
- 为了实现上述功能,我们将采用以下技术方案: 后端:WordPress原生PHP函数 + 自定义数据库表 前端:HTML5、CSS3、JavaScript(Vanilla JS + 少量jQuery) 数据可视化:Chart.js库 AJAX通信:WordPress REST API + admin-ajax.php 安全性:WordPress非ces、能力检查、数据验证
-
- 我们需要创建三个自定义数据库表来存储任务、投票和用户数据: -- 任务表 CREATE TABLE wp_team_tasks ( task_id INT AUTO_INCREMENT PRIMARY KEY, task_title VARCHAR(255) NOT NULL, task_description TEXT, task_status ENUM('pending', 'active', 'completed', 'archived') DEFAULT 'pending', created_by INT NOT NULL, assigned_to INT, due_date DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (created_by) REFERENCES wp_users(ID), FOREIGN KEY (assigned_to) REFERENCES wp_users(ID) ); -- 投票表 CREATE TABLE wp_task_votes ( vote_id INT AUTO_INCREMENT PRIMARY KEY, task_id INT NOT NULL, user_id INT NOT NULL, vote_value INT NOT NULL CHECK (vote_value BETWEEN 1 AND 10), vote_comment TEXT, voted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY unique_user_task (user_id, task_id), FOREIGN KEY (task_id) REFERENCES wp_team_tasks(task_id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES wp_users(ID) ); -- 任务分类表 CREATE TABLE wp_task_categories ( category_id INT AUTO_INCREMENT PRIMARY KEY, category_name VARCHAR(100) NOT NULL, category_color VARCHAR(7) DEFAULT '#3498db', created_by INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (created_by) REFERENCES wp_users(ID) );
- 为了提高查询效率,我们需要为常用查询字段添加索引: -- 为任务表添加索引 ALTER TABLE wp_team_tasks ADD INDEX idx_task_status (task_status); ALTER TABLE wp_team_tasks ADD INDEX idx_due_date (due_date); ALTER TABLE wp_team_tasks ADD INDEX idx_assigned_to (assigned_to); -- 为投票表添加索引 ALTER TABLE wp_task_votes ADD INDEX idx_task_id (task_id); ALTER TABLE wp_task_votes ADD INDEX idx_user_id (user_id); -- 为分类表添加索引 ALTER TABLE wp_task_categories ADD INDEX idx_created_by (created_by);
- 在WordPress中,我们通常在插件激活时创建自定义数据库表: <?php /** * 创建自定义数据库表 */ function team_task_voting_create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_prefix = $wpdb->prefix; // 任务表 $tasks_table = $table_prefix . 'team_tasks'; $tasks_sql = "CREATE TABLE IF NOT EXISTS $tasks_table ( task_id INT AUTO_INCREMENT PRIMARY KEY, task_title VARCHAR(255) NOT NULL, task_description TEXT, task_status ENUM('pending', 'active', 'completed', 'archived') DEFAULT 'pending', created_by INT NOT NULL, assigned_to INT, due_date DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) $charset_collate;"; // 投票表 $votes_table = $table_prefix . 'task_votes'; $votes_sql = "CREATE TABLE IF NOT EXISTS $votes_table ( vote_id INT AUTO_INCREMENT PRIMARY KEY, task_id INT NOT NULL, user_id INT NOT NULL, vote_value INT NOT NULL, vote_comment TEXT, voted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY unique_user_task (user_id, task_id) ) $charset_collate;"; // 分类表 $categories_table = $table_prefix . 'task_categories'; $categories_sql = "CREATE TABLE IF NOT EXISTS $categories_table ( category_id INT AUTO_INCREMENT PRIMARY KEY, category_name VARCHAR(100) NOT NULL, category_color VARCHAR(7) DEFAULT '#3498db', created_by INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($tasks_sql); dbDelta($votes_sql); dbDelta($categories_sql); // 添加外键约束(如果支持) if (strpos($wpdb->dbname, 'mysql') !== false) { $wpdb->query("ALTER TABLE $tasks_table ADD FOREIGN KEY (created_by) REFERENCES {$table_prefix}users(ID) ON DELETE CASCADE"); $wpdb->query("ALTER TABLE $tasks_table ADD FOREIGN KEY (assigned_to) REFERENCES {$table_prefix}users(ID) ON DELETE SET NULL"); $wpdb->query("ALTER TABLE $votes_table ADD FOREIGN KEY (task_id) REFERENCES $tasks_table(task_id) ON DELETE CASCADE"); $wpdb->query("ALTER TABLE $votes_table ADD FOREIGN KEY (user_id) REFERENCES {$table_prefix}users(ID) ON DELETE CASCADE"); $wpdb->query("ALTER TABLE $categories_table ADD FOREIGN KEY (created_by) REFERENCES {$table_prefix}users(ID) ON DELETE CASCADE"); } } register_activation_hook(__FILE__, 'team_task_voting_create_tables'); ?>
-
- 虽然我们使用了自定义数据库表,但为了利用WordPress的权限系统和部分功能,我们也可以创建自定义文章类型: <?php /** * 注册任务自定义文章类型 */ function register_task_post_type() { $labels = array( 'name' => '团队任务', 'singular_name' => '团队任务', 'menu_name' => '团队任务', 'add_new' => '添加新任务', 'add_new_item' => '添加新任务', 'edit_item' => '编辑任务', 'new_item' => '新任务', 'view_item' => '查看任务', 'search_items' => '搜索任务', 'not_found' => '未找到任务', 'not_found_in_trash' => '回收站中无任务' ); $args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, 'show_in_menu' => true, 'query_var' => true, 'rewrite' => array('slug' => 'team-task'), 'capability_type' => 'post', 'has_archive' => true, 'hierarchical' => false, 'menu_position' => 5, 'menu_icon' => 'dashicons-clipboard', 'supports' => array('title', 'editor', 'author', 'custom-fields'), 'show_in_rest' => true, // 启用Gutenberg编辑器支持 ); register_post_type('team_task', $args); // 注册任务分类 register_taxonomy( 'task_category', 'team_task', array( 'label' => '任务分类', 'rewrite' => array('slug' => 'task-category'), 'hierarchical' => true, 'show_in_rest' => true, ) ); } add_action('init', 'register_task_post_type'); ?>
- 我们将创建一个任务管理类来处理所有与任务相关的操作: <?php /** * 任务管理类 */ class Team_Task_Manager { private $db; private $tasks_table; private $votes_table; private $categories_table; public function __construct() { global $wpdb; $this->db = $wpdb; $this->tasks_table = $wpdb->prefix . 'team_tasks'; $this->votes_table = $wpdb->prefix . 'task_votes'; $this->categories_table = $wpdb->prefix . 'task_categories'; } /** * 创建新任务 */ public function create_task($data) { $defaults = array( 'task_title' => '', 'task_description' => '', 'task_status' => 'pending', 'created_by' => get_current_user_id(), 'assigned_to' => null, 'due_date' => null, 'category_id' => null ); $data = wp_parse_args($data, $defaults); // 验证数据 if (empty($data['task_title'])) { return new WP_Error('empty_title', '任务标题不能为空'); } // 插入数据 $result = $this->db->insert( $this->tasks_table, array( 'task_title' => sanitize_text_field($data['task_title']), 'task_description' => wp_kses_post($data['task_description']), 'task_status' => sanitize_text_field($data['task_status']), 'created_by' => intval($data['created_by']), 'assigned_to' => !empty($data['assigned_to']) ? intval($data['assigned_to']) : null, 'due_date' => !empty($data['due_date']) ? sanitize_text_field($data['due_date']) : null ), array('%s', '%s', '%s', '%d', '%d', '%s') ); if ($result === false) { return new WP_Error('db_error', '数据库插入失败'); } $task_id = $this->db->insert_id; // 如果有分类,添加到分类关系表 if (!empty($data['category_id'])) { $this->add_task_to_category($task_id, intval($data['category_id'])); } return $task_id; } /** * 获取任务列表 */ public function get_tasks($args = array()) { $defaults = array( 'status' => null, 'assigned_to' => null, 'created_by' => null, 'category_id' => null, 'page' => 1, 'per_page' => 10, 'orderby' => 'created_at', 'order' => 'DESC' ); $args = wp_parse_args($args, $defaults); $where = array('1=1'); $values = array(); if (!empty($args['status'])) { $where[] = 'task_status = %s'; $values[] = $args['status']; } if (!empty($args['assigned_to'])) { $where[] = 'assigned_to = %d'; $values[] = $args['assigned_to']; } if (!empty($args['created_by'])) { $where[] = 'created_by = %d'; $values[] = $args['created_by']; } $where_clause = implode(' AND ', $where); // 分页计算 $offset = ($args['page'] - 1) * $args['per_page']; // 如果有分类筛选,需要联表查询 if (!empty($args['category_id'])) { $query = $this->db->prepare( "SELECT t.* FROM {$this->tasks_table} t INNER JOIN {$this->db->prefix}task_category_relationships r ON t.task_id = r.task_id WHERE $where_clause AND r.category_id = %d ORDER BY {$args['orderby']} {$args['order']} LIMIT %d OFFSET %d", array_merge($values, array($args['category_id'], $args['per_page'], $offset)) ); } else { $query = $this->db->prepare( "SELECT * FROM {$this->tasks_table} WHERE $where_clause ORDER BY {$args['orderby']} {$args['order']} LIMIT %d OFFSET %d", array_merge($values, array($args['per_page'], $offset)) ); } $tasks = $this->db->get_results($query, ARRAY_A); // 获取总数量用于分页 if (!empty($args['category_id'])) { $count_query = $this->db->prepare( "SELECT COUNT(*) FROM {$this->tasks_table} t INNER JOIN {$this->db->prefix}task_category_relationships r ON t.task_id = r.task_id WHERE $where_clause AND r.category_id = %d", array_merge($values, array($args['category_id'])) ); } else { $count_query = $this->db->prepare( "SELECT COUNT(*) FROM {$this->tasks_table} WHERE $where_clause", $values ); } $total = $this->db->get_var($count_query); return array( 'tasks' => $tasks, 'total' => $total, 'total_pages' => ceil($total / $args['per_page']) ); } /** * 提交投票 */ public function submit_vote($task_id, $user_id, $vote_value, $comment = '') { // 验证投票值 $vote_value = intval($vote_value); if ($vote_value < 1 || $vote_value > 10) { return new WP_Error('invalid_vote', '投票值必须在1-10之间'); } // 检查用户是否已投票 $existing_vote = $this->db->get_var($this->db->prepare( "SELECT vote_id FROM {$this->votes_table} WHERE task_id = %d AND user_id = %d", $task_id, $user_id )); if ($existing_vote) { // 更新现有投票 $result = $this->db->update( $this->votes_table, array( 'vote_value' => $vote_value, 'vote_comment' => sanitize_textarea_field($comment), 'voted_at' => current_time('mysql') ), array('vote_id' => $existing_vote), array('%d', '%s', '%s'), array('%d') ); } else { // 插入新投票 $result = $this->db->insert( $this->votes_table, array( 'task_id' => $task_id, 'user_id' => $user_id, 'vote_value' => $vote_value, 'vote_comment' => sanitize_textarea_field($comment) ), array('%d', '%d', '%d', '%s') ); } if ($result === false) { return new WP_Error('db_error', '投票提交失败'); } return true; } /** * 计算任务优先级 */ public function calculate_task_priority($task_id) { // 获取所有投票 $votes = $this->db->get_results($this->db->prepare( "SELECT vote_value FROM {$this->votes_table} WHERE task_id = %d", $task_id ), ARRAY_A); if (empty($votes)) { return 0; // 没有投票,优先级为0
- /** * 计算任务优先级 */ public function calculate_task_priority($task_id) { // 获取所有投票 $votes = $this->db->get_results($this->db->prepare( "SELECT vote_value FROM {$this->votes_table} WHERE task_id = %d", $task_id ), ARRAY_A); if (empty($votes)) { return 0; // 没有投票,优先级为0 } // 提取投票值 $vote_values = array_column($votes, 'vote_value'); // 方法1:简单平均值 $simple_average = array_sum($vote_values) / count($vote_values); // 方法2:加权平均值(考虑投票者权重) $weighted_average = $this->calculate_weighted_average($task_id, $vote_values); // 方法3:去除极端值后的平均值 $trimmed_average = $this->calculate_trimmed_average($vote_values); // 综合优先级计算(可根据需求调整权重) $final_priority = ( $simple_average * 0.4 + $weighted_average * 0.4 + $trimmed_average * 0.2 ); // 获取投票数量作为置信度因子 $vote_count = count($votes); $confidence_factor = min(1, $vote_count / 10); // 最多10票达到完全置信 // 最终优先级(0-10分制) $final_score = $final_priority * $confidence_factor; return round($final_score, 2); } /** * 计算加权平均值 */ private function calculate_weighted_average($task_id, $vote_values) { global $wpdb; // 获取投票者信息 $voters = $wpdb->get_results($wpdb->prepare( "SELECT v.user_id, v.vote_value, u.user_login FROM {$this->votes_table} v LEFT JOIN {$wpdb->users} u ON v.user_id = u.ID WHERE v.task_id = %d", $task_id ), ARRAY_A); if (empty($voters)) { return 0; } $total_weight = 0; $weighted_sum = 0; foreach ($voters as $voter) { // 计算用户权重(基于用户角色、历史投票准确度等) $user_weight = $this->calculate_user_weight($voter['user_id']); $weighted_sum += $voter['vote_value'] * $user_weight; $total_weight += $user_weight; } return $total_weight > 0 ? $weighted_sum / $total_weight : 0; } /** * 计算用户权重 */ private function calculate_user_weight($user_id) { // 基础权重 $weight = 1.0; // 基于用户角色调整权重 $user = get_userdata($user_id); if ($user) { if (in_array('administrator', $user->roles)) { $weight *= 1.5; // 管理员权重更高 } elseif (in_array('editor', $user->roles)) { $weight *= 1.3; } elseif (in_array('author', $user->roles)) { $weight *= 1.2; } } // 基于历史投票参与度调整权重 $participation_rate = $this->get_user_participation_rate($user_id); $weight *= (0.5 + $participation_rate * 0.5); // 参与度影响权重 return $weight; } /** * 计算去除极端值后的平均值 */ private function calculate_trimmed_average($values, $trim_percent = 20) { if (count($values) < 3) { return array_sum($values) / count($values); } sort($values); $trim_count = floor(count($values) * $trim_percent / 100); $trimmed_values = array_slice($values, $trim_count, count($values) - 2 * $trim_count); return array_sum($trimmed_values) / count($trimmed_values); } /** * 获取用户投票参与度 */ private function get_user_participation_rate($user_id) { $total_tasks = $this->db->get_var( "SELECT COUNT(*) FROM {$this->tasks_table} WHERE task_status IN ('pending', 'active')" ); $user_votes = $this->db->get_var($this->db->prepare( "SELECT COUNT(DISTINCT task_id) FROM {$this->votes_table} WHERE user_id = %d", $user_id )); if ($total_tasks == 0) { return 1.0; } return min(1.0, $user_votes / $total_tasks); } /** * 获取任务投票详情 */ public function get_task_voting_details($task_id) { $votes = $this->db->get_results($this->db->prepare( "SELECT v.*, u.display_name, u.user_email FROM {$this->votes_table} v LEFT JOIN {$this->db->users} u ON v.user_id = u.ID WHERE v.task_id = %d ORDER BY v.voted_at DESC", $task_id ), ARRAY_A); $priority_score = $this->calculate_task_priority($task_id); // 统计投票分布 $vote_distribution = array_fill(1, 10, 0); foreach ($votes as $vote) { if ($vote['vote_value'] >= 1 && $vote['vote_value'] <= 10) { $vote_distribution[$vote['vote_value']]++; } } return array( 'votes' => $votes, 'vote_count' => count($votes), 'priority_score' => $priority_score, 'vote_distribution' => $vote_distribution, 'average_vote' => count($votes) > 0 ? array_sum(array_column($votes, 'vote_value')) / count($votes) : 0 ); } } ?>
- 为了支持前后端分离和AJAX调用,我们需要创建REST API端点: <?php /** * 注册REST API端点 */ class Task_Voting_REST_Controller extends WP_REST_Controller { private $task_manager; public function __construct() { $this->namespace = 'team-tasks/v1'; $this->task_manager = new Team_Task_Manager(); add_action('rest_api_init', array($this, 'register_routes')); } public function register_routes() { // 获取任务列表 register_rest_route($this->namespace, '/tasks', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array($this, 'get_tasks'), 'permission_callback' => array($this, 'get_tasks_permissions_check'), 'args' => $this->get_collection_params(), ), array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array($this, 'create_task'), 'permission_callback' => array($this, 'create_task_permissions_check'), ), )); // 单个任务操作 register_rest_route($this->namespace, '/tasks/(?P<id>d+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array($this, 'get_task'), 'permission_callback' => array($this, 'get_task_permissions_check'), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array($this, 'update_task'), 'permission_callback' => array($this, 'update_task_permissions_check'), ), array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array($this, 'delete_task'), 'permission_callback' => array($this, 'delete_task_permissions_check'), ), )); // 投票相关端点 register_rest_route($this->namespace, '/tasks/(?P<task_id>d+)/vote', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array($this, 'submit_vote'), 'permission_callback' => array($this, 'vote_permissions_check'), ), )); // 获取投票结果 register_rest_route($this->namespace, '/tasks/(?P<task_id>d+)/voting-results', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array($this, 'get_voting_results'), 'permission_callback' => array($this, 'get_voting_results_permissions_check'), ), )); // 优先级看板数据 register_rest_route($this->namespace, '/priority-board', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array($this, 'get_priority_board'), 'permission_callback' => array($this, 'get_priority_board_permissions_check'), ), )); } /** * 获取任务列表 */ public function get_tasks($request) { $params = $request->get_params(); $args = array( 'status' => isset($params['status']) ? sanitize_text_field($params['status']) : null, 'assigned_to' => isset($params['assigned_to']) ? intval($params['assigned_to']) : null, 'category_id' => isset($params['category_id']) ? intval($params['category_id']) : null, 'page' => isset($params['page']) ? intval($params['page']) : 1, 'per_page' => isset($params['per_page']) ? intval($params['per_page']) : 10, 'orderby' => isset($params['orderby']) ? sanitize_text_field($params['orderby']) : 'created_at', 'order' => isset($params['order']) ? sanitize_text_field($params['order']) : 'DESC', ); $result = $this->task_manager->get_tasks($args); // 为每个任务添加优先级分数 foreach ($result['tasks'] as &$task) { $task['priority_score'] = $this->task_manager->calculate_task_priority($task['task_id']); $task['vote_count'] = $this->task_manager->get_vote_count($task['task_id']); } $response = new WP_REST_Response($result); return $response; } /** * 创建新任务 */ public function create_task($request) { $params = $request->get_params(); $task_data = array( 'task_title' => sanitize_text_field($params['title']), 'task_description' => wp_kses_post($params['description']), 'assigned_to' => isset($params['assigned_to']) ? intval($params['assigned_to']) : null, 'due_date' => isset($params['due_date']) ? sanitize_text_field($params['due_date']) : null, 'category_id' => isset($params['category_id']) ? intval($params['category_id']) : null, ); $task_id = $this->task_manager->create_task($task_data); if (is_wp_error($task_id)) { return $task_id; } $response = new WP_REST_Response(array( 'success' => true, 'task_id' => $task_id, 'message' => '任务创建成功' )); return $response; } /** * 提交投票 */ public function submit_vote($request) { $task_id = intval($request['task_id']); $user_id = get_current_user_id(); $params = $request->get_params(); $vote_value = isset($params['vote_value']) ? intval($params['vote_value']) : 0; $comment = isset($params['comment']) ? sanitize_textarea_field($params['comment']) : ''; if ($vote_value < 1 || $vote_value > 10) { return new WP_Error('invalid_vote', '投票值必须在1-10之间', array('status' => 400)); } $result = $this->task_manager->submit_vote($task_id, $user_id, $vote_value, $comment); if (is_wp_error($result)) { return $result; } // 重新计算优先级 $priority_score = $this->task_manager->calculate_task_priority($task_id); $response = new WP_REST_Response(array( 'success' => true, 'priority_score' => $priority_score, 'message' => '投票成功' )); return $response; } /** * 获取优先级看板数据 */ public function get_priority_board($request) { global $wpdb; // 获取所有活跃任务 $tasks = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}team_tasks WHERE task_status IN ('pending', 'active') ORDER BY created_at DESC", ARRAY_A ); $board_data = array(); foreach ($tasks as $task) { $voting_details = $this->task_manager->get_task_voting_details($task['task_id']); // 获取负责人信息 $assigned_to_name = ''; if ($task['assigned_to']) { $user = get_userdata($task['assigned_to']); $assigned_to_name = $user ? $user->display_name : '未知用户'; } // 获取创建者信息 $creator = get_userdata($task['created_by']); $created_by_name = $creator ? $creator->display_name : '未知用户'; $board_data[] = array( 'id' => $task['task_id'], 'title' => $task['task_title'], 'description' => wp_trim_words($task['task_description'], 20), 'status' => $task['task_status'], 'priority_score' => $voting_details['priority_score'], 'vote_count' => $voting_details['vote_count'], 'average_vote' => $voting_details['average_vote'], 'assigned_to' => $assigned_to_name, 'created_by' => $created_by_name, 'due_date' => $task['due_date'], 'created_at' => $task['created_at'], 'vote_distribution' => $voting_details['vote_distribution'] ); } // 按优先级排序 usort($board_data, function($a, $b) { return $b['priority_score'] <=> $a['priority_score']; }); // 计算统计信息 $stats = array( 'total_tasks' => count($board_data), 'total_votes' => array_sum(array_column($board_data, 'vote_count')), 'average_priority' => count($board_data) > 0 ? array_sum(array_column($board_data, 'priority_score')) / count($board_data) : 0, 'participation_rate' => $this->calculate_overall_participation_rate(), ); $response = new WP_REST_Response(array( 'tasks' => $board_data, 'stats' => $stats )); return $response; } /** * 计算整体参与率 */ private function calculate_overall_participation_rate() { global $wpdb; $total_users = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->users}"); $active_tasks = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}team_tasks WHERE task_status IN ('pending', 'active')" ); if ($active_tasks == 0 || $total_users == 0) { return 0; } $users_voted = $wpdb->get_var( "SELECT COUNT(DISTINCT user_id) FROM {$wpdb->prefix}task_votes" ); // 参与率 = (已投票用户数 / 总用户数) * (平均投票任务数 / 总活跃任务数) $user_participation = $users_voted / $total_users; $total_votes = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}task_votes"); $average_votes_per_user = $users_voted > 0 ? $total_votes / $users_voted : 0; $task_participation = min(1, $average_votes_per_user / $active_tasks); return round(($user_participation + $task_participation) / 2 * 100, 2); } // 权限检查方法 public function get_tasks_permissions_check($request) { return current_user_can('read'); } public function create_task_permissions_check($request) { return current_user_can('publish_posts'); } public function vote_permissions_check($request) { return is_user_logged_in(); } public function get_priority_board_permissions_check($request) { return current_user_can('read'); } } // 初始化REST控制器 new Task_Voting_REST_Controller(); ?>
在现代团队协作中,任务优先级管理是决定工作效率的关键因素。根据项目管理协会(PMI)的调查,超过37%的项目失败是由于需求优先级不明确造成的。对于使用WordPress搭建的企业网站、团队协作平台或项目管理门户而言,集成一个在线任务投票与优先级排序工具可以显著提升团队决策效率。
传统的任务优先级确定方式往往依赖于少数管理者的主观判断,而在线投票工具能够让每个团队成员参与决策过程,通过集体智慧确定任务的真实优先级。这不仅提高了决策的透明度,也增强了团队成员的责任感和参与感。
本文将带领你通过WordPress代码二次开发,实现一个功能完整的在线团队任务投票与优先级排序工具。我们将从需求分析开始,逐步完成数据库设计、前端界面开发、后端逻辑实现以及安全性优化,最终打造一个适合团队协作的实用工具。
在开始开发之前,我们需要确保WordPress环境已正确配置:
- 本地开发环境:建议使用Local by Flywheel、XAMPP或MAMP搭建本地WordPress环境
- WordPress版本:确保使用最新版本的WordPress(5.8+)
- 代码编辑器:推荐使用VS Code、PHPStorm或Sublime Text
- 浏览器开发者工具:用于调试前端代码
我们的在线团队任务投票与优先级排序工具需要包含以下核心功能:
-
任务管理功能:
- 创建、编辑、删除任务
- 任务描述、截止日期、负责人设置
- 任务分类与标签
-
投票系统功能:
- 团队成员对任务进行投票
- 支持多种投票方式(优先级评分、简单投票等)
- 实时显示投票结果
-
优先级计算功能:
- 根据投票结果自动计算任务优先级
- 支持多种优先级算法(加权平均、多数决等)
- 可视化优先级展示
-
用户权限管理:
- 不同用户角色权限控制
- 投票权限管理
- 任务管理权限控制
-
数据可视化功能:
- 任务优先级看板
- 投票结果图表
- 团队参与度统计
为了实现上述功能,我们将采用以下技术方案:
- 后端:WordPress原生PHP函数 + 自定义数据库表
- 前端:HTML5、CSS3、JavaScript(Vanilla JS + 少量jQuery)
- 数据可视化:Chart.js库
- AJAX通信:WordPress REST API + admin-ajax.php
- 安全性:WordPress非ces、能力检查、数据验证
我们需要创建三个自定义数据库表来存储任务、投票和用户数据:
-- 任务表
CREATE TABLE wp_team_tasks (
task_id INT AUTO_INCREMENT PRIMARY KEY,
task_title VARCHAR(255) NOT NULL,
task_description TEXT,
task_status ENUM('pending', 'active', 'completed', 'archived') DEFAULT 'pending',
created_by INT NOT NULL,
assigned_to INT,
due_date DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (created_by) REFERENCES wp_users(ID),
FOREIGN KEY (assigned_to) REFERENCES wp_users(ID)
);
-- 投票表
CREATE TABLE wp_task_votes (
vote_id INT AUTO_INCREMENT PRIMARY KEY,
task_id INT NOT NULL,
user_id INT NOT NULL,
vote_value INT NOT NULL CHECK (vote_value BETWEEN 1 AND 10),
vote_comment TEXT,
voted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_user_task (user_id, task_id),
FOREIGN KEY (task_id) REFERENCES wp_team_tasks(task_id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES wp_users(ID)
);
-- 任务分类表
CREATE TABLE wp_task_categories (
category_id INT AUTO_INCREMENT PRIMARY KEY,
category_name VARCHAR(100) NOT NULL,
category_color VARCHAR(7) DEFAULT '#3498db',
created_by INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (created_by) REFERENCES wp_users(ID)
);
为了提高查询效率,我们需要为常用查询字段添加索引:
-- 为任务表添加索引
ALTER TABLE wp_team_tasks ADD INDEX idx_task_status (task_status);
ALTER TABLE wp_team_tasks ADD INDEX idx_due_date (due_date);
ALTER TABLE wp_team_tasks ADD INDEX idx_assigned_to (assigned_to);
-- 为投票表添加索引
ALTER TABLE wp_task_votes ADD INDEX idx_task_id (task_id);
ALTER TABLE wp_task_votes ADD INDEX idx_user_id (user_id);
-- 为分类表添加索引
ALTER TABLE wp_task_categories ADD INDEX idx_created_by (created_by);
在WordPress中,我们通常在插件激活时创建自定义数据库表:
<?php
/**
* 创建自定义数据库表
*/
function team_task_voting_create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_prefix = $wpdb->prefix;
// 任务表
$tasks_table = $table_prefix . 'team_tasks';
$tasks_sql = "CREATE TABLE IF NOT EXISTS $tasks_table (
task_id INT AUTO_INCREMENT PRIMARY KEY,
task_title VARCHAR(255) NOT NULL,
task_description TEXT,
task_status ENUM('pending', 'active', 'completed', 'archived') DEFAULT 'pending',
created_by INT NOT NULL,
assigned_to INT,
due_date DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) $charset_collate;";
// 投票表
$votes_table = $table_prefix . 'task_votes';
$votes_sql = "CREATE TABLE IF NOT EXISTS $votes_table (
vote_id INT AUTO_INCREMENT PRIMARY KEY,
task_id INT NOT NULL,
user_id INT NOT NULL,
vote_value INT NOT NULL,
vote_comment TEXT,
voted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_user_task (user_id, task_id)
) $charset_collate;";
// 分类表
$categories_table = $table_prefix . 'task_categories';
$categories_sql = "CREATE TABLE IF NOT EXISTS $categories_table (
category_id INT AUTO_INCREMENT PRIMARY KEY,
category_name VARCHAR(100) NOT NULL,
category_color VARCHAR(7) DEFAULT '#3498db',
created_by INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($tasks_sql);
dbDelta($votes_sql);
dbDelta($categories_sql);
// 添加外键约束(如果支持)
if (strpos($wpdb->dbname, 'mysql') !== false) {
$wpdb->query("ALTER TABLE $tasks_table ADD FOREIGN KEY (created_by) REFERENCES {$table_prefix}users(ID) ON DELETE CASCADE");
$wpdb->query("ALTER TABLE $tasks_table ADD FOREIGN KEY (assigned_to) REFERENCES {$table_prefix}users(ID) ON DELETE SET NULL");
$wpdb->query("ALTER TABLE $votes_table ADD FOREIGN KEY (task_id) REFERENCES $tasks_table(task_id) ON DELETE CASCADE");
$wpdb->query("ALTER TABLE $votes_table ADD FOREIGN KEY (user_id) REFERENCES {$table_prefix}users(ID) ON DELETE CASCADE");
$wpdb->query("ALTER TABLE $categories_table ADD FOREIGN KEY (created_by) REFERENCES {$table_prefix}users(ID) ON DELETE CASCADE");
}
}
register_activation_hook(__FILE__, 'team_task_voting_create_tables');
?>
虽然我们使用了自定义数据库表,但为了利用WordPress的权限系统和部分功能,我们也可以创建自定义文章类型:
<?php
/**
* 注册任务自定义文章类型
*/
function register_task_post_type() {
$labels = array(
'name' => '团队任务',
'singular_name' => '团队任务',
'menu_name' => '团队任务',
'add_new' => '添加新任务',
'add_new_item' => '添加新任务',
'edit_item' => '编辑任务',
'new_item' => '新任务',
'view_item' => '查看任务',
'search_items' => '搜索任务',
'not_found' => '未找到任务',
'not_found_in_trash' => '回收站中无任务'
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'team-task'),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 5,
'menu_icon' => 'dashicons-clipboard',
'supports' => array('title', 'editor', 'author', 'custom-fields'),
'show_in_rest' => true, // 启用Gutenberg编辑器支持
);
register_post_type('team_task', $args);
// 注册任务分类
register_taxonomy(
'task_category',
'team_task',
array(
'label' => '任务分类',
'rewrite' => array('slug' => 'task-category'),
'hierarchical' => true,
'show_in_rest' => true,
)
);
}
add_action('init', 'register_task_post_type');
?>
我们将创建一个任务管理类来处理所有与任务相关的操作:
<?php
/**
* 任务管理类
*/
class Team_Task_Manager {
private $db;
private $tasks_table;
private $votes_table;
private $categories_table;
public function __construct() {
global $wpdb;
$this->db = $wpdb;
$this->tasks_table = $wpdb->prefix . 'team_tasks';
$this->votes_table = $wpdb->prefix . 'task_votes';
$this->categories_table = $wpdb->prefix . 'task_categories';
}
/**
* 创建新任务
*/
public function create_task($data) {
$defaults = array(
'task_title' => '',
'task_description' => '',
'task_status' => 'pending',
'created_by' => get_current_user_id(),
'assigned_to' => null,
'due_date' => null,
'category_id' => null
);
$data = wp_parse_args($data, $defaults);
// 验证数据
if (empty($data['task_title'])) {
return new WP_Error('empty_title', '任务标题不能为空');
}
// 插入数据
$result = $this->db->insert(
$this->tasks_table,
array(
'task_title' => sanitize_text_field($data['task_title']),
'task_description' => wp_kses_post($data['task_description']),
'task_status' => sanitize_text_field($data['task_status']),
'created_by' => intval($data['created_by']),
'assigned_to' => !empty($data['assigned_to']) ? intval($data['assigned_to']) : null,
'due_date' => !empty($data['due_date']) ? sanitize_text_field($data['due_date']) : null
),
array('%s', '%s', '%s', '%d', '%d', '%s')
);
if ($result === false) {
return new WP_Error('db_error', '数据库插入失败');
}
$task_id = $this->db->insert_id;
// 如果有分类,添加到分类关系表
if (!empty($data['category_id'])) {
$this->add_task_to_category($task_id, intval($data['category_id']));
}
return $task_id;
}
/**
* 获取任务列表
*/
public function get_tasks($args = array()) {
$defaults = array(
'status' => null,
'assigned_to' => null,
'created_by' => null,
'category_id' => null,
'page' => 1,
'per_page' => 10,
'orderby' => 'created_at',
'order' => 'DESC'
);
$args = wp_parse_args($args, $defaults);
$where = array('1=1');
$values = array();
if (!empty($args['status'])) {
$where[] = 'task_status = %s';
$values[] = $args['status'];
}
if (!empty($args['assigned_to'])) {
$where[] = 'assigned_to = %d';
$values[] = $args['assigned_to'];
}
if (!empty($args['created_by'])) {
$where[] = 'created_by = %d';
$values[] = $args['created_by'];
}
$where_clause = implode(' AND ', $where);
// 分页计算
$offset = ($args['page'] - 1) * $args['per_page'];
// 如果有分类筛选,需要联表查询
if (!empty($args['category_id'])) {
$query = $this->db->prepare(
"SELECT t.* FROM {$this->tasks_table} t
INNER JOIN {$this->db->prefix}task_category_relationships r ON t.task_id = r.task_id
WHERE $where_clause AND r.category_id = %d
ORDER BY {$args['orderby']} {$args['order']}
LIMIT %d OFFSET %d",
array_merge($values, array($args['category_id'], $args['per_page'], $offset))
);
} else {
$query = $this->db->prepare(
"SELECT * FROM {$this->tasks_table}
WHERE $where_clause
ORDER BY {$args['orderby']} {$args['order']}
LIMIT %d OFFSET %d",
array_merge($values, array($args['per_page'], $offset))
);
}
$tasks = $this->db->get_results($query, ARRAY_A);
// 获取总数量用于分页
if (!empty($args['category_id'])) {
$count_query = $this->db->prepare(
"SELECT COUNT(*) FROM {$this->tasks_table} t
INNER JOIN {$this->db->prefix}task_category_relationships r ON t.task_id = r.task_id
WHERE $where_clause AND r.category_id = %d",
array_merge($values, array($args['category_id']))
);
} else {
$count_query = $this->db->prepare(
"SELECT COUNT(*) FROM {$this->tasks_table} WHERE $where_clause",
$values
);
}
$total = $this->db->get_var($count_query);
return array(
'tasks' => $tasks,
'total' => $total,
'total_pages' => ceil($total / $args['per_page'])
);
}
/**
* 提交投票
*/
public function submit_vote($task_id, $user_id, $vote_value, $comment = '') {
// 验证投票值
$vote_value = intval($vote_value);
if ($vote_value < 1 || $vote_value > 10) {
return new WP_Error('invalid_vote', '投票值必须在1-10之间');
}
// 检查用户是否已投票
$existing_vote = $this->db->get_var($this->db->prepare(
"SELECT vote_id FROM {$this->votes_table} WHERE task_id = %d AND user_id = %d",
$task_id, $user_id
));
if ($existing_vote) {
// 更新现有投票
$result = $this->db->update(
$this->votes_table,
array(
'vote_value' => $vote_value,
'vote_comment' => sanitize_textarea_field($comment),
'voted_at' => current_time('mysql')
),
array('vote_id' => $existing_vote),
array('%d', '%s', '%s'),
array('%d')
);
} else {
// 插入新投票
$result = $this->db->insert(
$this->votes_table,
array(
'task_id' => $task_id,
'user_id' => $user_id,
'vote_value' => $vote_value,
'vote_comment' => sanitize_textarea_field($comment)
),
array('%d', '%d', '%d', '%s')
);
}
if ($result === false) {
return new WP_Error('db_error', '投票提交失败');
}
return true;
}
/**
* 计算任务优先级
*/
public function calculate_task_priority($task_id) {
// 获取所有投票
$votes = $this->db->get_results($this->db->prepare(
"SELECT vote_value FROM {$this->votes_table} WHERE task_id = %d",
$task_id
), ARRAY_A);
if (empty($votes)) {
return 0; // 没有投票,优先级为0
/**
* 计算任务优先级
*/
public function calculate_task_priority($task_id) {
// 获取所有投票
$votes = $this->db->get_results($this->db->prepare(
"SELECT vote_value FROM {$this->votes_table} WHERE task_id = %d",
$task_id
), ARRAY_A);
if (empty($votes)) {
return 0; // 没有投票,优先级为0
}
// 提取投票值
$vote_values = array_column($votes, 'vote_value');
// 方法1:简单平均值
$simple_average = array_sum($vote_values) / count($vote_values);
// 方法2:加权平均值(考虑投票者权重)
$weighted_average = $this->calculate_weighted_average($task_id, $vote_values);
// 方法3:去除极端值后的平均值
$trimmed_average = $this->calculate_trimmed_average($vote_values);
// 综合优先级计算(可根据需求调整权重)
$final_priority = (
$simple_average * 0.4 +
$weighted_average * 0.4 +
$trimmed_average * 0.2
);
// 获取投票数量作为置信度因子
$vote_count = count($votes);
$confidence_factor = min(1, $vote_count / 10); // 最多10票达到完全置信
// 最终优先级(0-10分制)
$final_score = $final_priority * $confidence_factor;
return round($final_score, 2);
}
/**
* 计算加权平均值
*/
private function calculate_weighted_average($task_id, $vote_values) {
global $wpdb;
// 获取投票者信息
$voters = $wpdb->get_results($wpdb->prepare(
"SELECT v.user_id, v.vote_value, u.user_login
FROM {$this->votes_table} v
LEFT JOIN {$wpdb->users} u ON v.user_id = u.ID
WHERE v.task_id = %d",
$task_id
), ARRAY_A);
if (empty($voters)) {
return 0;
}
$total_weight = 0;
$weighted_sum = 0;
foreach ($voters as $voter) {
// 计算用户权重(基于用户角色、历史投票准确度等)
$user_weight = $this->calculate_user_weight($voter['user_id']);
$weighted_sum += $voter['vote_value'] * $user_weight;
$total_weight += $user_weight;
}
return $total_weight > 0 ? $weighted_sum / $total_weight : 0;
}
/**
* 计算用户权重
*/
private function calculate_user_weight($user_id) {
// 基础权重
$weight = 1.0;
// 基于用户角色调整权重
$user = get_userdata($user_id);
if ($user) {
if (in_array('administrator', $user->roles)) {
$weight *= 1.5; // 管理员权重更高
} elseif (in_array('editor', $user->roles)) {
$weight *= 1.3;
} elseif (in_array('author', $user->roles)) {
$weight *= 1.2;
}
}
// 基于历史投票参与度调整权重
$participation_rate = $this->get_user_participation_rate($user_id);
$weight *= (0.5 + $participation_rate * 0.5); // 参与度影响权重
return $weight;
}
/**
* 计算去除极端值后的平均值
*/
private function calculate_trimmed_average($values, $trim_percent = 20) {
if (count($values) < 3) {
return array_sum($values) / count($values);
}
sort($values);
$trim_count = floor(count($values) * $trim_percent / 100);
$trimmed_values = array_slice($values, $trim_count, count($values) - 2 * $trim_count);
return array_sum($trimmed_values) / count($trimmed_values);
}
/**
* 获取用户投票参与度
*/
private function get_user_participation_rate($user_id) {
$total_tasks = $this->db->get_var(
"SELECT COUNT(*) FROM {$this->tasks_table} WHERE task_status IN ('pending', 'active')"
);
$user_votes = $this->db->get_var($this->db->prepare(
"SELECT COUNT(DISTINCT task_id) FROM {$this->votes_table} WHERE user_id = %d",
$user_id
));
if ($total_tasks == 0) {
return 1.0;
}
return min(1.0, $user_votes / $total_tasks);
}
/**
* 获取任务投票详情
*/
public function get_task_voting_details($task_id) {
$votes = $this->db->get_results($this->db->prepare(
"SELECT v.*, u.display_name, u.user_email
FROM {$this->votes_table} v
LEFT JOIN {$this->db->users} u ON v.user_id = u.ID
WHERE v.task_id = %d
ORDER BY v.voted_at DESC",
$task_id
), ARRAY_A);
$priority_score = $this->calculate_task_priority($task_id);
// 统计投票分布
$vote_distribution = array_fill(1, 10, 0);
foreach ($votes as $vote) {
if ($vote['vote_value'] >= 1 && $vote['vote_value'] <= 10) {
$vote_distribution[$vote['vote_value']]++;
}
}
return array(
'votes' => $votes,
'vote_count' => count($votes),
'priority_score' => $priority_score,
'vote_distribution' => $vote_distribution,
'average_vote' => count($votes) > 0 ?
array_sum(array_column($votes, 'vote_value')) / count($votes) : 0
);
}
}
?>
/**
* 计算任务优先级
*/
public function calculate_task_priority($task_id) {
// 获取所有投票
$votes = $this->db->get_results($this->db->prepare(
"SELECT vote_value FROM {$this->votes_table} WHERE task_id = %d",
$task_id
), ARRAY_A);
if (empty($votes)) {
return 0; // 没有投票,优先级为0
}
// 提取投票值
$vote_values = array_column($votes, 'vote_value');
// 方法1:简单平均值
$simple_average = array_sum($vote_values) / count($vote_values);
// 方法2:加权平均值(考虑投票者权重)
$weighted_average = $this->calculate_weighted_average($task_id, $vote_values);
// 方法3:去除极端值后的平均值
$trimmed_average = $this->calculate_trimmed_average($vote_values);
// 综合优先级计算(可根据需求调整权重)
$final_priority = (
$simple_average * 0.4 +
$weighted_average * 0.4 +
$trimmed_average * 0.2
);
// 获取投票数量作为置信度因子
$vote_count = count($votes);
$confidence_factor = min(1, $vote_count / 10); // 最多10票达到完全置信
// 最终优先级(0-10分制)
$final_score = $final_priority * $confidence_factor;
return round($final_score, 2);
}
/**
* 计算加权平均值
*/
private function calculate_weighted_average($task_id, $vote_values) {
global $wpdb;
// 获取投票者信息
$voters = $wpdb->get_results($wpdb->prepare(
"SELECT v.user_id, v.vote_value, u.user_login
FROM {$this->votes_table} v
LEFT JOIN {$wpdb->users} u ON v.user_id = u.ID
WHERE v.task_id = %d",
$task_id
), ARRAY_A);
if (empty($voters)) {
return 0;
}
$total_weight = 0;
$weighted_sum = 0;
foreach ($voters as $voter) {
// 计算用户权重(基于用户角色、历史投票准确度等)
$user_weight = $this->calculate_user_weight($voter['user_id']);
$weighted_sum += $voter['vote_value'] * $user_weight;
$total_weight += $user_weight;
}
return $total_weight > 0 ? $weighted_sum / $total_weight : 0;
}
/**
* 计算用户权重
*/
private function calculate_user_weight($user_id) {
// 基础权重
$weight = 1.0;
// 基于用户角色调整权重
$user = get_userdata($user_id);
if ($user) {
if (in_array('administrator', $user->roles)) {
$weight *= 1.5; // 管理员权重更高
} elseif (in_array('editor', $user->roles)) {
$weight *= 1.3;
} elseif (in_array('author', $user->roles)) {
$weight *= 1.2;
}
}
// 基于历史投票参与度调整权重
$participation_rate = $this->get_user_participation_rate($user_id);
$weight *= (0.5 + $participation_rate * 0.5); // 参与度影响权重
return $weight;
}
/**
* 计算去除极端值后的平均值
*/
private function calculate_trimmed_average($values, $trim_percent = 20) {
if (count($values) < 3) {
return array_sum($values) / count($values);
}
sort($values);
$trim_count = floor(count($values) * $trim_percent / 100);
$trimmed_values = array_slice($values, $trim_count, count($values) - 2 * $trim_count);
return array_sum($trimmed_values) / count($trimmed_values);
}
/**
* 获取用户投票参与度
*/
private function get_user_participation_rate($user_id) {
$total_tasks = $this->db->get_var(
"SELECT COUNT(*) FROM {$this->tasks_table} WHERE task_status IN ('pending', 'active')"
);
$user_votes = $this->db->get_var($this->db->prepare(
"SELECT COUNT(DISTINCT task_id) FROM {$this->votes_table} WHERE user_id = %d",
$user_id
));
if ($total_tasks == 0) {
return 1.0;
}
return min(1.0, $user_votes / $total_tasks);
}
/**
* 获取任务投票详情
*/
public function get_task_voting_details($task_id) {
$votes = $this->db->get_results($this->db->prepare(
"SELECT v.*, u.display_name, u.user_email
FROM {$this->votes_table} v
LEFT JOIN {$this->db->users} u ON v.user_id = u.ID
WHERE v.task_id = %d
ORDER BY v.voted_at DESC",
$task_id
), ARRAY_A);
$priority_score = $this->calculate_task_priority($task_id);
// 统计投票分布
$vote_distribution = array_fill(1, 10, 0);
foreach ($votes as $vote) {
if ($vote['vote_value'] >= 1 && $vote['vote_value'] <= 10) {
$vote_distribution[$vote['vote_value']]++;
}
}
return array(
'votes' => $votes,
'vote_count' => count($votes),
'priority_score' => $priority_score,
'vote_distribution' => $vote_distribution,
'average_vote' => count($votes) > 0 ?
array_sum(array_column($votes, 'vote_value')) / count($votes) : 0
);
}
}
?>
为了支持前后端分离和AJAX调用,我们需要创建REST API端点:
<?php
/**
* 注册REST API端点
*/
class Task_Voting_REST_Controller extends WP_REST_Controller {
private $task_manager;
public function __construct() {
$this->namespace = 'team-tasks/v1';
$this->task_manager = new Team_Task_Manager();
add_action('rest_api_init', array($this, 'register_routes'));
}
public function register_routes() {
// 获取任务列表
register_rest_route($this->namespace, '/tasks', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_tasks'),
'permission_callback' => array($this, 'get_tasks_permissions_check'),
'args' => $this->get_collection_params(),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_task'),
'permission_callback' => array($this, 'create_task_permissions_check'),
),
));
// 单个任务操作
register_rest_route($this->namespace, '/tasks/(?P<id>d+)', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_task'),
'permission_callback' => array($this, 'get_task_permissions_check'),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_task'),
'permission_callback' => array($this, 'update_task_permissions_check'),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array($this, 'delete_task'),
'permission_callback' => array($this, 'delete_task_permissions_check'),
),
));
// 投票相关端点
register_rest_route($this->namespace, '/tasks/(?P<task_id>d+)/vote', array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'submit_vote'),
'permission_callback' => array($this, 'vote_permissions_check'),
),
));
// 获取投票结果
register_rest_route($this->namespace, '/tasks/(?P<task_id>d+)/voting-results', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_voting_results'),
'permission_callback' => array($this, 'get_voting_results_permissions_check'),
),
));
// 优先级看板数据
register_rest_route($this->namespace, '/priority-board', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_priority_board'),
'permission_callback' => array($this, 'get_priority_board_permissions_check'),
),
));
}
/**
* 获取任务列表
*/
public function get_tasks($request) {
$params = $request->get_params();
$args = array(
'status' => isset($params['status']) ? sanitize_text_field($params['status']) : null,
'assigned_to' => isset($params['assigned_to']) ? intval($params['assigned_to']) : null,
'category_id' => isset($params['category_id']) ? intval($params['category_id']) : null,
'page' => isset($params['page']) ? intval($params['page']) : 1,
'per_page' => isset($params['per_page']) ? intval($params['per_page']) : 10,
'orderby' => isset($params['orderby']) ? sanitize_text_field($params['orderby']) : 'created_at',
'order' => isset($params['order']) ? sanitize_text_field($params['order']) : 'DESC',
);
$result = $this->task_manager->get_tasks($args);
// 为每个任务添加优先级分数
foreach ($result['tasks'] as &$task) {
$task['priority_score'] = $this->task_manager->calculate_task_priority($task['task_id']);
$task['vote_count'] = $this->task_manager->get_vote_count($task['task_id']);
}
$response = new WP_REST_Response($result);
return $response;
}
/**
* 创建新任务
*/
public function create_task($request) {
$params = $request->get_params();
$task_data = array(
'task_title' => sanitize_text_field($params['title']),
'task_description' => wp_kses_post($params['description']),
'assigned_to' => isset($params['assigned_to']) ? intval($params['assigned_to']) : null,
'due_date' => isset($params['due_date']) ? sanitize_text_field($params['due_date']) : null,
'category_id' => isset($params['category_id']) ? intval($params['category_id']) : null,
);
$task_id = $this->task_manager->create_task($task_data);
if (is_wp_error($task_id)) {
return $task_id;
}
$response = new WP_REST_Response(array(
'success' => true,
'task_id' => $task_id,
'message' => '任务创建成功'
));
return $response;
}
/**
* 提交投票
*/
public function submit_vote($request) {
$task_id = intval($request['task_id']);
$user_id = get_current_user_id();
$params = $request->get_params();
$vote_value = isset($params['vote_value']) ? intval($params['vote_value']) : 0;
$comment = isset($params['comment']) ? sanitize_textarea_field($params['comment']) : '';
if ($vote_value < 1 || $vote_value > 10) {
return new WP_Error('invalid_vote', '投票值必须在1-10之间', array('status' => 400));
}
$result = $this->task_manager->submit_vote($task_id, $user_id, $vote_value, $comment);
if (is_wp_error($result)) {
return $result;
}
// 重新计算优先级
$priority_score = $this->task_manager->calculate_task_priority($task_id);
$response = new WP_REST_Response(array(
'success' => true,
'priority_score' => $priority_score,
'message' => '投票成功'
));
return $response;
}
/**
* 获取优先级看板数据
*/
public function get_priority_board($request) {
global $wpdb;
// 获取所有活跃任务
$tasks = $wpdb->get_results(
"SELECT * FROM {$wpdb->prefix}team_tasks
WHERE task_status IN ('pending', 'active')
ORDER BY created_at DESC",
ARRAY_A
);
$board_data = array();
foreach ($tasks as $task) {
$voting_details = $this->task_manager->get_task_voting_details($task['task_id']);
// 获取负责人信息
$assigned_to_name = '';
if ($task['assigned_to']) {
$user = get_userdata($task['assigned_to']);
$assigned_to_name = $user ? $user->display_name : '未知用户';
}
// 获取创建者信息
$creator = get_userdata($task['created_by']);
$created_by_name = $creator ? $creator->display_name : '未知用户';
$board_data[] = array(
'id' => $task['task_id'],
'title' => $task['task_title'],
'description' => wp_trim_words($task['task_description'], 20),
'status' => $task['task_status'],
'priority_score' => $voting_details['priority_score'],
'vote_count' => $voting_details['vote_count'],
'average_vote' => $voting_details['average_vote'],
'assigned_to' => $assigned_to_name,
'created_by' => $created_by_name,
'due_date' => $task['due_date'],
'created_at' => $task['created_at'],
'vote_distribution' => $voting_details['vote_distribution']
);
}
// 按优先级排序
usort($board_data, function($a, $b) {
return $b['priority_score'] <=> $a['priority_score'];
});
// 计算统计信息
$stats = array(
'total_tasks' => count($board_data),
'total_votes' => array_sum(array_column($board_data, 'vote_count')),
'average_priority' => count($board_data) > 0 ?
array_sum(array_column($board_data, 'priority_score')) / count($board_data) : 0,
'participation_rate' => $this->calculate_overall_participation_rate(),
);
$response = new WP_REST_Response(array(
'tasks' => $board_data,
'stats' => $stats
));
return $response;
}
/**
* 计算整体参与率
*/
private function calculate_overall_participation_rate() {
global $wpdb;
$total_users = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->users}");
$active_tasks = $wpdb->get_var(
"SELECT COUNT(*) FROM {$wpdb->prefix}team_tasks WHERE task_status IN ('pending', 'active')"
);
if ($active_tasks == 0 || $total_users == 0) {
return 0;
}
$users_voted = $wpdb->get_var(
"SELECT COUNT(DISTINCT user_id) FROM {$wpdb->prefix}task_votes"
);
// 参与率 = (已投票用户数 / 总用户数) * (平均投票任务数 / 总活跃任务数)
$user_participation = $users_voted / $total_users;
$total_votes = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}task_votes");
$average_votes_per_user = $users_voted > 0 ? $total_votes / $users_voted : 0;
$task_participation = min(1, $average_votes_per_user / $active_tasks);
return round(($user_participation + $task_participation) / 2 * 100, 2);
}
// 权限检查方法
public function get_tasks_permissions_check($request) {
return current_user_can('read');
}
public function create_task_permissions_check($request) {
return current_user_can('publish_posts');
}
public function vote_permissions_check($request) {
return is_user_logged_in();
}
public function get_priority_board_permissions_check($request) {
return current_user_can('read');
}
}
// 初始化REST控制器
new Task_Voting_REST_Controller();
?>


