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 * } */ 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; } } }