refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
This commit is contained in:
1 parent
8f349b40fb
commit
aaf458af1c
404 files changed
+13602
-7705
No files matched your search
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Payment;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class 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\TermMeta::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user