文章目录[隐藏]
WordPress小批量定制插件实现原材料溯源管理的教程
引言:为什么需要原材料溯源管理?
在当今注重产品质量和安全的市场环境中,原材料溯源管理变得至关重要。无论是食品行业、化妆品生产还是制造业,能够追踪原材料从采购到成品的全过程,不仅能提高产品质量控制能力,还能增强消费者信任,满足法规要求。
对于中小型企业来说,昂贵的专业溯源系统往往难以承受。本教程将指导您如何通过WordPress定制插件,以较低成本实现小批量的原材料溯源管理功能。
系统设计与功能规划
1.1 核心功能需求分析
我们的原材料溯源管理系统需要包含以下核心功能:
- 原材料信息录入与管理
- 供应商信息管理
- 生产批次追踪
- 扫码查询功能
- 数据可视化报表
1.2 数据库结构设计
我们将创建以下数据表来存储溯源信息:
- 原材料表 (raw_materials)
- 供应商表 (suppliers)
- 入库记录表 (inventory_records)
- 生产批次表 (production_batches)
- 产品表 (products)
插件开发环境搭建
2.1 创建插件基础结构
首先,在WordPress的wp-content/plugins目录下创建新文件夹"material-traceability",然后创建主插件文件:
<?php
/**
* Plugin Name: 原材料溯源管理系统
* Plugin URI: https://yourwebsite.com/
* Description: 用于管理原材料溯源的小批量定制插件
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
* Text Domain: material-traceability
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('MT_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('MT_PLUGIN_URL', plugin_dir_url(__FILE__));
define('MT_VERSION', '1.0.0');
// 初始化插件
require_once MT_PLUGIN_PATH . 'includes/class-init.php';
require_once MT_PLUGIN_PATH . 'includes/class-database.php';
require_once MT_PLUGIN_PATH . 'includes/class-admin.php';
require_once MT_PLUGIN_PATH . 'includes/class-public.php';
// 激活和停用钩子
register_activation_hook(__FILE__, array('MT_Database', 'create_tables'));
register_deactivation_hook(__FILE__, array('MT_Database', 'drop_tables'));
// 初始化插件类
function mt_init_plugin() {
$init = new MT_Init();
$init->run();
}
add_action('plugins_loaded', 'mt_init_plugin');
?>
2.2 创建数据库管理类
<?php
/**
* 数据库管理类
* 处理插件的数据库表创建和更新
*/
class MT_Database {
/**
* 创建插件所需的数据表
*/
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 原材料表
$table_materials = $wpdb->prefix . 'mt_materials';
$sql_materials = "CREATE TABLE IF NOT EXISTS $table_materials (
id INT(11) NOT NULL AUTO_INCREMENT,
material_code VARCHAR(50) NOT NULL,
material_name VARCHAR(255) NOT NULL,
material_type VARCHAR(100) NOT NULL,
supplier_id INT(11) NOT NULL,
unit VARCHAR(20) NOT NULL,
safety_standard TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY material_code (material_code)
) $charset_collate;";
// 供应商表
$table_suppliers = $wpdb->prefix . 'mt_suppliers';
$sql_suppliers = "CREATE TABLE IF NOT EXISTS $table_suppliers (
id INT(11) NOT NULL AUTO_INCREMENT,
supplier_code VARCHAR(50) NOT NULL,
supplier_name VARCHAR(255) NOT NULL,
contact_person VARCHAR(100),
phone VARCHAR(50),
email VARCHAR(100),
address TEXT,
qualification_cert TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY supplier_code (supplier_code)
) $charset_collate;";
// 入库记录表
$table_inventory = $wpdb->prefix . 'mt_inventory';
$sql_inventory = "CREATE TABLE IF NOT EXISTS $table_inventory (
id INT(11) NOT NULL AUTO_INCREMENT,
material_id INT(11) NOT NULL,
batch_number VARCHAR(100) NOT NULL,
quantity DECIMAL(10,2) NOT NULL,
unit_price DECIMAL(10,2),
storage_location VARCHAR(255),
production_date DATE,
expiry_date DATE,
inspector VARCHAR(100),
qr_code_path VARCHAR(500),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY material_id (material_id),
KEY batch_number (batch_number)
) $charset_collate;";
// 生产批次表
$table_batches = $wpdb->prefix . 'mt_batches';
$sql_batches = "CREATE TABLE IF NOT EXISTS $table_batches (
id INT(11) NOT NULL AUTO_INCREMENT,
batch_number VARCHAR(100) NOT NULL,
product_id INT(11) NOT NULL,
production_date DATE NOT NULL,
quantity INT(11) NOT NULL,
used_materials TEXT,
operator VARCHAR(100),
quality_check VARCHAR(50),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY batch_number (batch_number)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql_materials);
dbDelta($sql_suppliers);
dbDelta($sql_inventory);
dbDelta($sql_batches);
// 添加默认数据(可选)
self::add_default_data();
}
/**
* 添加默认数据
*/
private static function add_default_data() {
// 这里可以添加一些默认的供应商或材料类型
}
/**
* 删除数据表(插件停用时)
*/
public static function drop_tables() {
global $wpdb;
$tables = array(
$wpdb->prefix . 'mt_materials',
$wpdb->prefix . 'mt_suppliers',
$wpdb->prefix . 'mt_inventory',
$wpdb->prefix . 'mt_batches'
);
foreach ($tables as $table) {
$wpdb->query("DROP TABLE IF EXISTS $table");
}
}
}
?>
后台管理界面开发
3.1 创建管理菜单和页面
<?php
/**
* 后台管理类
* 处理插件在WordPress后台的管理界面
*/
class MT_Admin {
private $page_hook;
public function __construct() {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
$this->page_hook = add_menu_page(
'原材料溯源管理', // 页面标题
'溯源管理', // 菜单标题
'manage_options', // 权限
'material-traceability', // 菜单slug
array($this, 'render_dashboard'), // 回调函数
'dashicons-database', // 图标
30 // 位置
);
// 添加子菜单
add_submenu_page(
'material-traceability',
'原材料管理',
'原材料管理',
'manage_options',
'mt-materials',
array($this, 'render_materials_page')
);
add_submenu_page(
'material-traceability',
'供应商管理',
'供应商管理',
'manage_options',
'mt-suppliers',
array($this, 'render_suppliers_page')
);
add_submenu_page(
'material-traceability',
'入库记录',
'入库记录',
'manage_options',
'mt-inventory',
array($this, 'render_inventory_page')
);
add_submenu_page(
'material-traceability',
'生产批次',
'生产批次',
'manage_options',
'mt-batches',
array($this, 'render_batches_page')
);
add_submenu_page(
'material-traceability',
'溯源查询',
'溯源查询',
'manage_options',
'mt-trace',
array($this, 'render_trace_page')
);
}
/**
* 加载后台脚本和样式
*/
public function enqueue_admin_scripts($hook) {
if ($hook != $this->page_hook && strpos($hook, 'material-traceability') === false) {
return;
}
wp_enqueue_style(
'mt-admin-style',
MT_PLUGIN_URL . 'assets/css/admin-style.css',
array(),
MT_VERSION
);
wp_enqueue_script(
'mt-admin-script',
MT_PLUGIN_URL . 'assets/js/admin-script.js',
array('jquery'),
MT_VERSION,
true
);
// 本地化脚本,传递数据到JavaScript
wp_localize_script('mt-admin-script', 'mt_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mt_ajax_nonce')
));
}
/**
* 渲染仪表板页面
*/
public function render_dashboard() {
global $wpdb;
// 获取统计数据
$materials_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}mt_materials");
$suppliers_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}mt_suppliers");
$inventory_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}mt_inventory");
$batches_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}mt_batches");
?>
<div class="wrap mt-dashboard">
<h1>原材料溯源管理系统</h1>
<div class="mt-stats-container">
<div class="mt-stat-card">
<h3>原材料种类</h3>
<p class="stat-number"><?php echo $materials_count; ?></p>
</div>
<div class="mt-stat-card">
<h3>供应商数量</h3>
<p class="stat-number"><?php echo $suppliers_count; ?></p>
</div>
<div class="mt-stat-card">
<h3>入库记录</h3>
<p class="stat-number"><?php echo $inventory_count; ?></p>
</div>
<div class="mt-stat-card">
<h3>生产批次</h3>
<p class="stat-number"><?php echo $batches_count; ?></p>
</div>
</div>
<div class="mt-quick-actions">
<h2>快速操作</h2>
<div class="action-buttons">
<a href="<?php echo admin_url('admin.php?page=mt-materials&action=add'); ?>" class="button button-primary">
添加新材料
</a>
<a href="<?php echo admin_url('admin.php?page=mt-inventory&action=add'); ?>" class="button button-primary">
添加入库记录
</a>
<a href="<?php echo admin_url('admin.php?page=mt-batches&action=add'); ?>" class="button button-primary">
添加生产批次
</a>
<a href="<?php echo admin_url('admin.php?page=mt-trace'); ?>" class="button">
溯源查询
</a>
</div>
</div>
<div class="mt-recent-activity">
<h2>最近活动</h2>
<?php $this->display_recent_activity(); ?>
</div>
</div>
<?php
}
/**
* 显示最近活动
*/
private function display_recent_activity() {
global $wpdb;
// 获取最近的入库记录
$recent_inventory = $wpdb->get_results(
"SELECT i.*, m.material_name
FROM {$wpdb->prefix}mt_inventory i
LEFT JOIN {$wpdb->prefix}mt_materials m ON i.material_id = m.id
ORDER BY i.created_at DESC LIMIT 10"
);
if ($recent_inventory) {
echo '<table class="wp-list-table widefat fixed striped">';
echo '<thead><tr>
<th>批次号</th>
<th>原材料</th>
<th>数量</th>
<th>入库时间</th>
</tr></thead>';
echo '<tbody>';
foreach ($recent_inventory as $item) {
echo '<tr>';
echo '<td>' . esc_html($item->batch_number) . '</td>';
echo '<td>' . esc_html($item->material_name) . '</td>';
echo '<td>' . esc_html($item->quantity) . '</td>';
echo '<td>' . esc_html($item->created_at) . '</td>';
echo '</tr>';
}
echo '</tbody></table>';
} else {
echo '<p>暂无活动记录</p>';
}
}
/**
* 渲染原材料管理页面
*/
public function render_materials_page() {
// 这里实现原材料管理页面的代码
echo '<div class="wrap"><h1>原材料管理</h1></div>';
}
// 其他页面渲染方法...
}
?>
前端查询功能实现
4.1 创建短代码用于前端查询
<?php
/**
* 前端公共类
* 处理插件在前端的显示和功能
*/
class MT_Public {
public function __construct() {
add_shortcode('material_trace', array($this, 'trace_shortcode'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_public_scripts'));
add_action('wp_ajax_mt_trace_query', array($this, 'ajax_trace_query'));
add_action('wp_ajax_nopriv_mt_trace_query', array($this, 'ajax_trace_query'));
}
/**
* 加载前端脚本和样式
*/
public function enqueue_public_scripts() {
wp_enqueue_style(
'mt-public-style',
MT_PLUGIN_URL . 'assets/css/public-style.css',
array(),
MT_VERSION
);
wp_enqueue_script(
'mt-public-script',
MT_PLUGIN_URL . 'assets/js/public-script.js',
array('jquery'),
MT_VERSION,
true
);
wp_localize_script('mt-public-script', 'mt_public_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mt_public_ajax_nonce')
));
}
/**
* 溯源查询短代码
*/
public function trace_shortcode($atts) {
ob_start();
?>
<div class="mt-trace-container">
<h2>原材料溯源查询</h2>
<p>请输入批次号或扫描二维码查询原材料来源信息</p>
<div class="mt-search-box">
<input type="text" id="mt-batch-number" placeholder="输入批次号" class="mt-input">
<button id="mt-trace-btn" class="mt-button">查询</button>
<div class="mt-qr-scanner">
<button id="mt-scan-btn" class="mt-button secondary">扫码查询</button>
<div id="mt-qr-reader" style="display:none;"></div>
</div>
</div>
<div id="mt-trace-results" class="mt-results-container" style="display:none;">
<!-- 查询结果将在这里显示 -->
</div>
<div id="mt-loading" class="mt-loading" style="display:none;">
查询中...
</div>
</div>
<?php
return ob_get_clean();
}
/**
* AJAX溯源查询处理
*/
public function ajax_trace_query() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'mt_public_ajax_nonce')) {
wp_die('安全验证失败');
}
$batch_number = sanitize_text_field($_POST['batch_number']);
if (empty($batch_number)) {
wp_send_json_error('请输入批次号');
}
global $wpdb;
// 查询入库记录
$inventory_record = $wpdb->get_row($wpdb->prepare(
"SELECT i.*, m.material_name, m.material_code, s.supplier_name
FROM {$wpdb->prefix}mt_inventory i
LEFT JOIN {$wpdb->prefix}mt_materials m ON i.material_id = m.id
LEFT JOIN {$wpdb->prefix}mt_suppliers s ON m.supplier_id = s.id
WHERE i.batch_number = %s",
$batch_number
));
if (!$inventory_record) {
// 尝试查询生产批次
$production_batch = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}mt_batches WHERE batch_number = %s",
$batch_number
));
if ($production_batch) {
$response = array(
'type' => 'production',
data' => $production_batch,
'used_materials' => json_decode($production_batch->used_materials, true)
);
wp_send_json_success($response);
} else {
wp_send_json_error('未找到相关批次信息');
}
} else {
// 查询该原材料的所有生产批次
$production_batches = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}mt_batches
WHERE used_materials LIKE %s",
'%"' . $inventory_record->material_id . '"%'
));
$response = array(
'type' => 'material',
'data' => $inventory_record,
'production_batches' => $production_batches
);
wp_send_json_success($response);
}
}
}
?>
### 4.2 前端JavaScript处理
/**
- 前端JavaScript处理
- 处理用户交互和AJAX请求
*/
jQuery(document).ready(function($) {
// 溯源查询按钮点击事件
$('#mt-trace-btn').on('click', function() {
var batchNumber = $('#mt-batch-number').val().trim();
if (!batchNumber) {
alert('请输入批次号');
return;
}
performTraceQuery(batchNumber);
});
// 回车键触发查询
$('#mt-batch-number').on('keypress', function(e) {
if (e.which === 13) {
$('#mt-trace-btn').click();
}
});
// 扫码查询功能
$('#mt-scan-btn').on('click', function() {
if (typeof Html5QrcodeScanner === 'undefined') {
alert('请确保已加载二维码扫描库');
return;
}
const qrScanner = new Html5QrcodeScanner(
"mt-qr-reader",
{ fps: 10, qrbox: 250 }
);
$('#mt-qr-reader').show();
qrScanner.render(
function(decodedText) {
$('#mt-batch-number').val(decodedText);
$('#mt-qr-reader').hide();
qrScanner.clear();
performTraceQuery(decodedText);
},
function(errorMessage) {
console.error(errorMessage);
}
);
});
/**
* 执行溯源查询
*/
function performTraceQuery(batchNumber) {
$('#mt-loading').show();
$('#mt-trace-results').hide().empty();
$.ajax({
url: mt_public_ajax.ajax_url,
type: 'POST',
data: {
action: 'mt_trace_query',
nonce: mt_public_ajax.nonce,
batch_number: batchNumber
},
success: function(response) {
$('#mt-loading').hide();
if (response.success) {
displayTraceResults(response.data);
} else {
$('#mt-trace-results')
.html('<div class="mt-error">' + response.data + '</div>')
.show();
}
},
error: function() {
$('#mt-loading').hide();
$('#mt-trace-results')
.html('<div class="mt-error">查询失败,请稍后重试</div>')
.show();
}
});
}
/**
* 显示查询结果
*/
function displayTraceResults(data) {
var html = '';
if (data.type === 'material') {
html = `
<div class="mt-result-card material">
<h3>原材料溯源信息</h3>
<div class="mt-info-grid">
<div class="mt-info-item">
<strong>批次号:</strong>${data.data.batch_number}
</div>
<div class="mt-info-item">
<strong>原材料:</strong>${data.data.material_name}
</div>
<div class="mt-info-item">
<strong>原材料编码:</strong>${data.data.material_code}
</div>
<div class="mt-info-item">
<strong>供应商:</strong>${data.data.supplier_name}
</div>
<div class="mt-info-item">
<strong>入库数量:</strong>${data.data.quantity}
</div>
<div class="mt-info-item">
<strong>生产日期:</strong>${data.data.production_date}
</div>
<div class="mt-info-item">
<strong>检验员:</strong>${data.data.inspector || '未记录'}
</div>
</div>
`;
if (data.production_batches && data.production_batches.length > 0) {
html += `
<h4>使用该原材料的生产批次</h4>
<table class="mt-production-table">
<thead>
<tr>
<th>生产批次号</th>
<th>生产日期</th>
<th>产量</th>
<th>质检状态</th>
</tr>
</thead>
<tbody>
`;
data.production_batches.forEach(function(batch) {
html += `
<tr>
<td>${batch.batch_number}</td>
<td>${batch.production_date}</td>
<td>${batch.quantity}</td>
<td>${batch.quality_check}</td>
</tr>
`;
});
html += `</tbody></table>`;
}
html += `</div>`;
} else if (data.type === 'production') {
html = `
<div class="mt-result-card production">
<h3>生产批次信息</h3>
<div class="mt-info-grid">
<div class="mt-info-item">
<strong>生产批次号:</strong>${data.data.batch_number}
</div>
<div class="mt-info-item">
<strong>生产日期:</strong>${data.data.production_date}
</div>
<div class="mt-info-item">
<strong>产量:</strong>${data.data.quantity}
</div>
<div class="mt-info-item">
<strong>操作员:</strong>${data.data.operator}
</div>
<div class="mt-info-item">
<strong>质检状态:</strong>${data.data.quality_check}
</div>
</div>
`;
if (data.used_materials && data.used_materials.length > 0) {
html += `
<h4>使用的原材料</h4>
<div class="mt-materials-list">
`;
data.used_materials.forEach(function(materialId) {
// 这里可以添加获取原材料详情的AJAX调用
html += `<span class="mt-material-tag">原材料ID: ${materialId}</span>`;
});
html += `</div>`;
}
html += `</div>`;
}
$('#mt-trace-results')
.html(html)
.show();
}
});
## 二维码生成与扫描功能
### 5.1 二维码生成类
<?php
/**
- 二维码生成类
- 使用PHP QR Code库生成批次二维码
*/
class MT_QRCode {
/**
* 生成批次二维码
* @param string $batchNumber 批次号
* @param string $type 类型:material或production
* @return string 二维码图片路径
*/
public static function generate_batch_qr($batchNumber, $type = 'material') {
// 确保二维码库已加载
if (!class_exists('QRcode')) {
require_once MT_PLUGIN_PATH . 'includes/libs/phpqrcode/qrlib.php';
}
// 创建二维码存储目录
$upload_dir = wp_upload_dir();
$qr_dir = $upload_dir['basedir'] . '/material-traceability-qr/';
if (!file_exists($qr_dir)) {
wp_mkdir_p($qr_dir);
}
// 生成文件名
$filename = $type . '_' . $batchNumber . '_' . time() . '.png';
$filepath = $qr_dir . $filename;
// 二维码内容:包含批次号和查询URL
$site_url = get_site_url();
$qr_content = $site_url . '/trace?batch=' . urlencode($batchNumber) . '&type=' . $type;
// 生成二维码
QRcode::png($qr_content, $filepath, 'L', 10, 2);
// 返回URL路径
return $upload_dir['baseurl'] . '/material-traceability-qr/' . $filename;
}
/**
* 为入库记录生成二维码
*/
public static function generate_for_inventory($inventory_id) {
global $wpdb;
$inventory = $wpdb->get_row($wpdb->prepare(
"SELECT batch_number FROM {$wpdb->prefix}mt_inventory WHERE id = %d",
$inventory_id
));
if (!$inventory) {
return false;
}
$qr_url = self::generate_batch_qr($inventory->batch_number, 'material');
// 更新数据库记录
$wpdb->update(
$wpdb->prefix . 'mt_inventory',
array('qr_code_path' => $qr_url),
array('id' => $inventory_id),
array('%s'),
array('%d')
);
return $qr_url;
}
/**
* 获取二维码图片
*/
public static function get_qr_image($batchNumber, $type = 'material') {
global $wpdb;
if ($type === 'material') {
$record = $wpdb->get_row($wpdb->prepare(
"SELECT qr_code_path FROM {$wpdb->prefix}mt_inventory WHERE batch_number = %s",
$batchNumber
));
if ($record && $record->qr_code_path) {
return $record->qr_code_path;
}
}
// 如果不存在,生成新的二维码
return self::generate_batch_qr($batchNumber, $type);
}
}
?>
## 数据导入导出功能
### 6.1 批量导入类
<?php
/**
- 数据导入导出类
- 处理Excel/CSV格式的数据导入导出
*/
class MT_ImportExport {
/**
* 导入原材料数据
*/
public static function import_materials($file_path) {
global $wpdb;
// 检查文件类型
$file_ext = pathinfo($file_path, PATHINFO_EXTENSION);
if ($file_ext === 'csv') {
return self::import_csv_materials($file_path);
} elseif (in_array($file_ext, ['xls', 'xlsx'])) {
return self::import_excel_materials($file_path);
} else {
return new WP_Error('invalid_format', '不支持的文件格式');
}
}
/**
* 导入CSV格式的原材料数据
*/
private static function import_csv_materials($file_path) {
global $wpdb;
$handle = fopen($file_path, 'r');
if (!$handle) {
return new WP_Error('file_error', '无法打开文件');
}
$headers = fgetcsv($handle);
$imported = 0;
$errors = array();
// 验证CSV头部
$required_headers = ['material_code', 'material_name', 'material_type', 'supplier_code'];
foreach ($required_headers as $required) {
if (!in_array($required, $headers)) {
fclose($handle);
return new WP_Error('missing_header', 'CSV文件缺少必要列: ' . $required);
}
}
$row_number = 1;
while (($row = fgetcsv($handle)) !== false) {
$row_number++;
// 跳过空行
if (empty(array_filter($row))) {
continue;
}
// 将行数据转换为关联数组
$data = array_combine($headers, $row);
// 验证数据
if (empty($data['material_code']) || empty($data['material_name'])) {
$errors[] = "第{$row_number}行: 材料编码和名称不能为空";
continue;
}
// 查找供应商ID
$supplier_id = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM {$wpdb->prefix}mt_suppliers WHERE supplier_code = %s",
$data['supplier_code']
));
if (!$supplier_id) {
$errors[] = "第{$row_number}行: 供应商编码不存在 - " . $data['supplier_code'];
continue;
}
// 准备插入数据
$insert_data = array(
'material_code' => sanitize_text_field($data['material_code']),
'material_name' => sanitize_text_field($data['material_name']),
'material_type' => sanitize_text_field($data['material_type']),
'supplier_id' => $supplier_id,
'unit' => isset($data['unit']) ? sanitize_text_field($data['unit']) : '件',
'safety_standard' => isset($data['safety_standard']) ? sanitize_textarea_field($data['safety_standard']) : ''
);
// 检查是否已存在
$exists = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM {$wpdb->prefix}mt_materials WHERE material_code = %s",
$insert_data['material_code']
));
if ($exists) {
// 更新现有记录
$wpdb->update(
$wpdb->prefix . 'mt_materials',
$insert_data,
array('id' => $exists),
array('%s', '%s', '%s', '%d', '%s', '%s'),
array('%d')
);
} else {
// 插入新记录
$wpdb->insert(
$wpdb->prefix . 'mt_materials',
$insert_data,
array('%s', '%s', '%s', '%d', '%s', '%s')
);
}
$imported++;
}
fclose($handle);
return array(
'imported' => $imported,
'errors' => $errors
);
}
/**
* 导出原材料数据为CSV
*/
public static function export_materials() {
global $wpdb;
// 获取所有原材料数据
$materials = $wpdb->get_results("
SELECT m.*, s.supplier_code, s.supplier_name
FROM {$wpdb->prefix}mt_materials m
LEFT JOIN {$wpdb->prefix}mt_suppliers s ON m.supplier_id = s.id
ORDER BY m.created_at DESC
");
if (empty($materials)) {
return false;
}
// 设置CSV头部
$headers = array(
'材料编码',
'材料名称',
'材料类型',
'供应商编码',
'供应商名称',
'单位',
'安全标准',
'创建时间',
'更新时间'
);
// 生成文件名
$filename = 'materials_export_' . date('Ymd_His') . '.csv';
// 设置HTTP头
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=' . $filename);
// 输出CSV文件
$output = fopen('php://output', 'w');
fputcsv($output, $headers);
foreach ($materials as $material) {
$row = array(
$material->material_code,
$material->material_name,
$material->material_type,
$material->supplier_code,
$material->supplier_name,
$material->unit,
$material->safety_standard,
$material->created_at,
$material->updated_at
);
fputcsv($output, $row);
}
fclose($output);
exit;
}
}
?>
## 插件优化与安全考虑
### 7.1 安全防护措施
<?php
/**
- 安全防护类
- 处理插件安全相关功能
*/
class MT_Security {
/**
* 验证用户权限
*/
public static function check_permission($capability = 'manage_options') {
if (!current_user_can($capability)) {
wp_die('您没有权限执行此操作');
}
}
/**
* 清理和验证输入数据
*/
public static function sanitize_input($data, $type = 'text') {
switch ($type) {
case 'email':
return sanitize_email($data);
case 'url':
return esc_url_raw($data);
case 'textarea':
return sanitize_textarea_field($data);
case 'int':
return intval($data);
case 'float':
return floatval($data);
case 'html':
return wp_kses_post($data);
default:
return sanitize_text_field($data);
}
}
/**
* 防止SQL注入
*/
public static function prepare_query($query, $args) {
global $wpdb;
return $wpdb->prepare($query, $args);
}
/**
* 验证批次号格式
*/
public static function validate_batch_number($batch_number) {
// 批次号格式:至少包含字母和数字,长度6-20位
if (!preg_match('/^[A-Za-z0-9]{6,20}$/', $batch_number)) {
return new WP_Error('invalid_batch', '批次号格式不正确');
}
return $batch_number;
}
/**
* 记录操作日志
*/
public static function log_operation($user_id, $operation, $details =
