Files
wordpress-theme-aksrt/inc/classes/payment/class-aksrt-discount.php
T
kate522 ce1c13b947 refactor(classes): organize inc/classes into domain folders
Move ~94 root class shells and root *-traits into domain directories
(core, auth, user, credits, content, payment/gateways, forum, shop,
sms, ai, messages, extensions, widgets, settings, admin). Update
bootstrap require paths, co-locate traits with owners, fix forum/user
relative requires, drop duplicate shop controller requires, and wire
home-modules load. Path-only; class names and init order unchanged.
2026-07-17 11:11:57 +08:00

172 lines
5.2 KiB
PHP

<?php
/**
* AKSRT Discount Engine
*
* 优惠活动计算引擎 — reduction 立减 / discount 打折 / gift 买赠。
* scope: item / product / author / order。
* 绑定在 shop_discount taxonomy 上,商品可挂多个活动。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_Discount {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Get all active discount campaigns for a product.
*
* @param int $product_id
* @return array<int, array> Sorted by priority desc.
*/
public function get_active_discounts(int $product_id): array {
$terms = wp_get_object_terms($product_id, 'shop_discount', ['fields' => 'all']);
if (is_wp_error($terms) || empty($terms)) {
return [];
}
$now = current_time('timestamp');
$active = [];
foreach ($terms as $term) {
$meta = AKSRT_Shop_Term_Meta::get_discount_meta($term->term_id);
// Time limit check
if (!empty($meta['start']) && strtotime($meta['start']) > $now) {
continue;
}
if (!empty($meta['end']) && strtotime($meta['end']) < $now) {
continue;
}
$meta['term_id'] = $term->term_id;
$meta['name'] = $term->name;
$active[] = $meta;
}
// Sort by priority desc
usort($active, function ($a, $b) {
return ((int) $b['priority']) - ((int) $a['priority']);
});
return $active;
}
/**
* Calculate discount for a single line item.
*
* @param float $unit_price Unit price
* @param int $quantity Quantity
* @param int $product_id Product ID
* @param int $user_id User ID (for user_limit checks)
* @return array{
* original: float,
* discount_amount: float,
* final_price: float,
* gifts: array,
* applied: array<int, array>
* }
*/
public function calculate(float $unit_price, int $quantity, int $product_id, int $user_id = 0): array {
$original = $unit_price * $quantity;
$discount_amount = 0.0;
$gifts = [];
$applied = [];
$discounts = $this->get_active_discounts($product_id);
foreach ($discounts as $d) {
// User limit check
if (!empty($d['user_limit']) && $user_id > 0) {
if (!$this->check_user_limit($d['user_limit'], $user_id)) {
continue;
}
}
// Price limit check
if ((float) $d['price_limit'] > 0 && $original < (float) $d['price_limit']) {
continue;
}
$type = $d['type'] ?? 'reduction';
$value = (float) ($d['value'] ?? 0);
switch ($type) {
case 'reduction':
// 立减
$discount_amount += min($value * $quantity, $original - $discount_amount);
break;
case 'discount':
// 打折 (value = 折扣系数 e.g. 8.5 = 85折)
if ($value > 0 && $value < 10) {
$rate = $value / 10.0;
$save = ($original - $discount_amount) * (1 - $rate);
$discount_amount += $save;
}
break;
case 'gift':
// 买赠 — collect gift config, no price reduction
if (!empty($d['gift_config'])) {
$gift_data = json_decode($d['gift_config'], true);
if (is_array($gift_data)) {
$gifts[] = $gift_data;
}
}
break;
}
$applied[] = [
'term_id' => $d['term_id'],
'name' => $d['name'] ?? '',
'type' => $type,
'value' => $value,
];
}
$discount_amount = min($discount_amount, $original);
return [
'original' => $original,
'discount_amount' => round($discount_amount, 2),
'final_price' => round($original - $discount_amount, 2),
'gifts' => $gifts,
'applied' => $applied,
];
}
/**
* Check if a user meets the user_limit requirement.
*
* @param string $limit 'vip' / 'vip_2' / 'auth'
* @param int $user_id
* @return bool
*/
private function check_user_limit(string $limit, int $user_id): bool {
if (empty($limit)) {
return true;
}
switch ($limit) {
case 'vip':
return function_exists('aksrt_is_vip') ? aksrt_is_vip($user_id) : false;
case 'vip_2':
return function_exists('aksrt_is_vip2') ? aksrt_is_vip2($user_id) : false;
case 'auth':
return function_exists('aksrt_is_verified') ? aksrt_is_verified($user_id) : false;
default:
return true;
}
}
}