613 lines
16 KiB
PHP
613 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* 混装产品类
|
|
*
|
|
* 处理产品组合的创建、管理和计算
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* 混装产品类
|
|
*/
|
|
class Yoone_Bundle extends Abstract_Yoone_Data {
|
|
|
|
/**
|
|
* 对象类型
|
|
*/
|
|
protected $object_type = 'yoone_bundle';
|
|
|
|
/**
|
|
* 数据结构
|
|
*/
|
|
protected $data = array(
|
|
'name' => '',
|
|
'description' => '',
|
|
'status' => 'active',
|
|
'discount_type' => 'percentage',
|
|
'discount_value' => 0.00,
|
|
'min_quantity' => 1,
|
|
'max_quantity' => null,
|
|
'created_at' => null,
|
|
'updated_at' => null
|
|
);
|
|
|
|
/**
|
|
* 混装商品
|
|
*/
|
|
protected $items = array();
|
|
|
|
/**
|
|
* 构造函数
|
|
*/
|
|
public function __construct($id = 0) {
|
|
parent::__construct($id);
|
|
|
|
if ($this->get_id() > 0) {
|
|
$this->load_items();
|
|
}
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Getters
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* 获取名称
|
|
*/
|
|
public function get_name($context = 'view') {
|
|
return $this->get_prop('name', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取描述
|
|
*/
|
|
public function get_description($context = 'view') {
|
|
return $this->get_prop('description', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取状态
|
|
*/
|
|
public function get_status($context = 'view') {
|
|
return $this->get_prop('status', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取折扣类型
|
|
*/
|
|
public function get_discount_type($context = 'view') {
|
|
return $this->get_prop('discount_type', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取折扣值
|
|
*/
|
|
public function get_discount_value($context = 'view') {
|
|
return $this->get_prop('discount_value', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取最小数量
|
|
*/
|
|
public function get_min_quantity($context = 'view') {
|
|
return $this->get_prop('min_quantity', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取最大数量
|
|
*/
|
|
public function get_max_quantity($context = 'view') {
|
|
return $this->get_prop('max_quantity', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取创建时间
|
|
*/
|
|
public function get_created_at($context = 'view') {
|
|
return $this->get_prop('created_at', $context);
|
|
}
|
|
|
|
/**
|
|
* 获取更新时间
|
|
*/
|
|
public function get_updated_at($context = 'view') {
|
|
return $this->get_prop('updated_at', $context);
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Setters
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* 设置名称
|
|
*/
|
|
public function set_name($name) {
|
|
$this->set_prop('name', sanitize_text_field($name));
|
|
}
|
|
|
|
/**
|
|
* 设置描述
|
|
*/
|
|
public function set_description($description) {
|
|
$this->set_prop('description', wp_kses_post($description));
|
|
}
|
|
|
|
/**
|
|
* 设置状态
|
|
*/
|
|
public function set_status($status) {
|
|
$valid_statuses = array('active', 'inactive', 'draft');
|
|
if (in_array($status, $valid_statuses)) {
|
|
$this->set_prop('status', $status);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置折扣类型
|
|
*/
|
|
public function set_discount_type($type) {
|
|
$valid_types = array('percentage', 'fixed', 'none');
|
|
if (in_array($type, $valid_types)) {
|
|
$this->set_prop('discount_type', $type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置折扣值
|
|
*/
|
|
public function set_discount_value($value) {
|
|
$this->set_prop('discount_value', floatval($value));
|
|
}
|
|
|
|
/**
|
|
* 设置最小数量
|
|
*/
|
|
public function set_min_quantity($quantity) {
|
|
$this->set_prop('min_quantity', absint($quantity));
|
|
}
|
|
|
|
/**
|
|
* 设置最大数量
|
|
*/
|
|
public function set_max_quantity($quantity) {
|
|
$this->set_prop('max_quantity', $quantity ? absint($quantity) : null);
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| 商品管理
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* 获取混装商品
|
|
*/
|
|
public function get_items() {
|
|
return $this->items;
|
|
}
|
|
|
|
/**
|
|
* 添加商品
|
|
*/
|
|
public function add_item($product_id, $quantity = 1, $args = array()) {
|
|
$defaults = array(
|
|
'discount_type' => 'none',
|
|
'discount_value' => 0.00,
|
|
'is_required' => false,
|
|
'sort_order' => 0
|
|
);
|
|
|
|
$args = wp_parse_args($args, $defaults);
|
|
|
|
$item = array(
|
|
'product_id' => absint($product_id),
|
|
'quantity' => absint($quantity),
|
|
'discount_type' => sanitize_text_field($args['discount_type']),
|
|
'discount_value' => floatval($args['discount_value']),
|
|
'is_required' => (bool) $args['is_required'],
|
|
'sort_order' => absint($args['sort_order'])
|
|
);
|
|
|
|
$this->items[] = $item;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 移除商品
|
|
*/
|
|
public function remove_item($index) {
|
|
if (isset($this->items[$index])) {
|
|
unset($this->items[$index]);
|
|
$this->items = array_values($this->items); // 重新索引
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 清空商品
|
|
*/
|
|
public function clear_items() {
|
|
$this->items = array();
|
|
}
|
|
|
|
/**
|
|
* 加载商品
|
|
*/
|
|
protected function load_items() {
|
|
global $wpdb;
|
|
|
|
if (!$this->get_id()) {
|
|
return;
|
|
}
|
|
|
|
$items = $wpdb->get_results($wpdb->prepare("
|
|
SELECT product_id, quantity, discount_type, discount_value, is_required, sort_order
|
|
FROM {$wpdb->prefix}yoone_bundle_items
|
|
WHERE bundle_id = %d
|
|
ORDER BY sort_order ASC, id ASC
|
|
", $this->get_id()), ARRAY_A);
|
|
|
|
$this->items = $items ?: array();
|
|
}
|
|
|
|
/**
|
|
* 保存商品
|
|
*/
|
|
protected function save_items() {
|
|
global $wpdb;
|
|
|
|
if (!$this->get_id()) {
|
|
return false;
|
|
}
|
|
|
|
// 删除现有商品
|
|
$wpdb->delete(
|
|
$wpdb->prefix . 'yoone_bundle_items',
|
|
array('bundle_id' => $this->get_id()),
|
|
array('%d')
|
|
);
|
|
|
|
// 插入新商品
|
|
foreach ($this->items as $item) {
|
|
$wpdb->insert(
|
|
$wpdb->prefix . 'yoone_bundle_items',
|
|
array(
|
|
'bundle_id' => $this->get_id(),
|
|
'product_id' => $item['product_id'],
|
|
'quantity' => $item['quantity'],
|
|
'discount_type' => $item['discount_type'],
|
|
'discount_value' => $item['discount_value'],
|
|
'is_required' => $item['is_required'] ? 1 : 0,
|
|
'sort_order' => $item['sort_order']
|
|
),
|
|
array('%d', '%d', '%d', '%s', '%f', '%d', '%d')
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| 价格计算
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* 计算混装价格
|
|
*/
|
|
public function calculate_price($selected_items = array()) {
|
|
$total = 0;
|
|
$items = $this->get_items();
|
|
|
|
if (empty($selected_items)) {
|
|
$selected_items = $items;
|
|
}
|
|
|
|
foreach ($selected_items as $item) {
|
|
$product = wc_get_product($item['product_id']);
|
|
if (!$product) {
|
|
continue;
|
|
}
|
|
|
|
$item_price = $product->get_price() * $item['quantity'];
|
|
|
|
// 应用商品级别折扣
|
|
if ($item['discount_type'] === 'percentage' && $item['discount_value'] > 0) {
|
|
$item_price = $item_price * (1 - $item['discount_value'] / 100);
|
|
} elseif ($item['discount_type'] === 'fixed' && $item['discount_value'] > 0) {
|
|
$item_price = max(0, $item_price - $item['discount_value']);
|
|
}
|
|
|
|
$total += $item_price;
|
|
}
|
|
|
|
// 应用混装级别折扣
|
|
if ($this->get_discount_type() === 'percentage' && $this->get_discount_value() > 0) {
|
|
$total = $total * (1 - $this->get_discount_value() / 100);
|
|
} elseif ($this->get_discount_type() === 'fixed' && $this->get_discount_value() > 0) {
|
|
$total = max(0, $total - $this->get_discount_value());
|
|
}
|
|
|
|
return apply_filters('yoone_bundle_calculate_price', $total, $this, $selected_items);
|
|
}
|
|
|
|
/**
|
|
* 获取节省金额
|
|
*/
|
|
public function get_savings($selected_items = array()) {
|
|
$original_total = 0;
|
|
$bundle_total = $this->calculate_price($selected_items);
|
|
|
|
$items = empty($selected_items) ? $this->get_items() : $selected_items;
|
|
|
|
foreach ($items as $item) {
|
|
$product = wc_get_product($item['product_id']);
|
|
if ($product) {
|
|
$original_total += $product->get_price() * $item['quantity'];
|
|
}
|
|
}
|
|
|
|
return max(0, $original_total - $bundle_total);
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| 验证方法
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* 验证数量
|
|
*/
|
|
public function validate_quantity($quantity) {
|
|
$min = $this->get_min_quantity();
|
|
$max = $this->get_max_quantity();
|
|
|
|
if ($quantity < $min) {
|
|
return false;
|
|
}
|
|
|
|
if ($max && $quantity > $max) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 检查是否可用
|
|
*/
|
|
public function is_available() {
|
|
return $this->get_status() === 'active' && !empty($this->get_items());
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| 数据库操作
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* 从数据库读取
|
|
*/
|
|
protected function read() {
|
|
global $wpdb;
|
|
|
|
$data = $wpdb->get_row($wpdb->prepare("
|
|
SELECT * FROM {$wpdb->prefix}yoone_bundles WHERE id = %d
|
|
", $this->get_id()));
|
|
|
|
if ($data) {
|
|
$this->set_props(array(
|
|
'name' => $data->name,
|
|
'description' => $data->description,
|
|
'status' => $data->status,
|
|
'discount_type' => $data->discount_type,
|
|
'discount_value' => $data->discount_value,
|
|
'min_quantity' => $data->min_quantity,
|
|
'max_quantity' => $data->max_quantity,
|
|
'created_at' => $data->created_at,
|
|
'updated_at' => $data->updated_at
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建记录
|
|
*/
|
|
protected function create() {
|
|
global $wpdb;
|
|
|
|
$data = array(
|
|
'name' => $this->get_name('edit'),
|
|
'description' => $this->get_description('edit'),
|
|
'status' => $this->get_status('edit'),
|
|
'discount_type' => $this->get_discount_type('edit'),
|
|
'discount_value' => $this->get_discount_value('edit'),
|
|
'min_quantity' => $this->get_min_quantity('edit'),
|
|
'max_quantity' => $this->get_max_quantity('edit'),
|
|
'created_at' => current_time('mysql'),
|
|
'updated_at' => current_time('mysql')
|
|
);
|
|
|
|
$result = $wpdb->insert(
|
|
$wpdb->prefix . 'yoone_bundles',
|
|
$data,
|
|
array('%s', '%s', '%s', '%s', '%f', '%d', '%d', '%s', '%s')
|
|
);
|
|
|
|
if ($result) {
|
|
$this->set_id($wpdb->insert_id);
|
|
$this->save_items();
|
|
do_action('yoone_bundle_created', $this->get_id(), $this);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 更新记录
|
|
*/
|
|
protected function update() {
|
|
global $wpdb;
|
|
|
|
$changes = $this->get_changes();
|
|
|
|
if (empty($changes)) {
|
|
return true;
|
|
}
|
|
|
|
$changes['updated_at'] = current_time('mysql');
|
|
|
|
$result = $wpdb->update(
|
|
$wpdb->prefix . 'yoone_bundles',
|
|
$changes,
|
|
array('id' => $this->get_id()),
|
|
null,
|
|
array('%d')
|
|
);
|
|
|
|
if ($result !== false) {
|
|
$this->save_items();
|
|
do_action('yoone_bundle_updated', $this->get_id(), $this);
|
|
}
|
|
|
|
return $result !== false;
|
|
}
|
|
|
|
/**
|
|
* 从数据库删除
|
|
*/
|
|
protected function delete_from_database($force_delete = false) {
|
|
global $wpdb;
|
|
|
|
// 删除商品
|
|
$wpdb->delete(
|
|
$wpdb->prefix . 'yoone_bundle_items',
|
|
array('bundle_id' => $this->get_id()),
|
|
array('%d')
|
|
);
|
|
|
|
// 删除混装
|
|
$result = $wpdb->delete(
|
|
$wpdb->prefix . 'yoone_bundles',
|
|
array('id' => $this->get_id()),
|
|
array('%d')
|
|
);
|
|
|
|
return $result !== false;
|
|
}
|
|
|
|
/**
|
|
* 设置多个属性
|
|
*/
|
|
protected function set_props($props) {
|
|
foreach ($props as $prop => $value) {
|
|
if (array_key_exists($prop, $this->data)) {
|
|
$this->data[$prop] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| 前端辅助方法
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* 根据产品ID获取套装数据
|
|
*/
|
|
public function get_bundle_by_product_id($product_id) {
|
|
global $wpdb;
|
|
|
|
$bundle_data = $wpdb->get_row($wpdb->prepare("
|
|
SELECT * FROM {$wpdb->prefix}yoone_bundles
|
|
WHERE product_id = %d AND status = 'active'
|
|
LIMIT 1
|
|
", $product_id), ARRAY_A);
|
|
|
|
return $bundle_data;
|
|
}
|
|
|
|
/**
|
|
* 获取套装项目
|
|
*/
|
|
public function get_bundle_items($bundle_id = null) {
|
|
global $wpdb;
|
|
|
|
$id = $bundle_id ? $bundle_id : $this->get_id();
|
|
|
|
if (!$id) {
|
|
return array();
|
|
}
|
|
|
|
$items = $wpdb->get_results($wpdb->prepare("
|
|
SELECT bi.*, p.post_title as product_name
|
|
FROM {$wpdb->prefix}yoone_bundle_items bi
|
|
LEFT JOIN {$wpdb->posts} p ON bi.product_id = p.ID
|
|
WHERE bi.bundle_id = %d
|
|
ORDER BY bi.sort_order ASC
|
|
", $id), ARRAY_A);
|
|
|
|
return $items ? $items : array();
|
|
}
|
|
|
|
/**
|
|
* 计算套装价格
|
|
*/
|
|
public function calculate_bundle_price($selected_items) {
|
|
if (empty($selected_items)) {
|
|
return 0;
|
|
}
|
|
|
|
$total_price = 0;
|
|
|
|
// 计算原始总价
|
|
foreach ($selected_items as $item) {
|
|
$product = wc_get_product($item['product_id']);
|
|
if ($product) {
|
|
$total_price += $product->get_price() * $item['quantity'];
|
|
}
|
|
}
|
|
|
|
// 应用折扣
|
|
$discount_type = $this->get_discount_type();
|
|
$discount_value = $this->get_discount_value();
|
|
|
|
if ($discount_type === 'percentage' && $discount_value > 0) {
|
|
$discount_amount = $total_price * ($discount_value / 100);
|
|
$total_price -= $discount_amount;
|
|
} elseif ($discount_type === 'fixed' && $discount_value > 0) {
|
|
$total_price -= $discount_value;
|
|
}
|
|
|
|
// 确保价格不为负数
|
|
return max(0, $total_price);
|
|
}
|
|
|
|
/**
|
|
* 获取关联的产品ID
|
|
*/
|
|
public function get_product_id() {
|
|
return $this->get_prop('product_id');
|
|
}
|
|
|
|
/**
|
|
* 设置关联的产品ID
|
|
*/
|
|
public function set_product_id($product_id) {
|
|
$this->set_prop('product_id', absint($product_id));
|
|
}
|
|
} |