[ 'name' => __('优惠活动', 'aksrt'), 'singular_name' => __('优惠活动', 'aksrt'), 'search_items' => __('搜索优惠活动', 'aksrt'), 'all_items' => __('所有优惠活动', 'aksrt'), 'edit_item' => __('编辑优惠活动', 'aksrt'), 'add_new_item' => __('新增优惠活动', 'aksrt'), 'menu_name' => __('优惠活动', 'aksrt'), ], 'public' => true, 'hierarchical' => false, 'show_ui' => $shop_show_ui, 'show_in_menu' => $shop_show_menu, 'show_in_rest' => true, 'show_admin_column' => true, 'rewrite' => ['slug' => 'shop-discount', 'with_front' => false], ]); } // ─── Term Meta Registration ────────────────────────────────── /** * Register all term meta fields for REST API exposure. */ public function register_term_meta_fields(): void { $auth = function () { return current_user_can('manage_categories'); }; // ── aksrt_product_cat ── $cat_fields = $this->get_cat_field_definitions(); foreach ($cat_fields as $key => $def) { register_term_meta('aksrt_product_cat', $key, [ 'type' => $def['type'], 'description' => $def['description'] ?? $key, 'single' => true, 'default' => $def['default'] ?? '', 'show_in_rest' => true, 'sanitize_callback' => $def['sanitize'] ?? 'sanitize_text_field', 'auth_callback' => $auth, ]); } // ── aksrt_product_tag ── $tag_fields = $this->get_tag_field_definitions(); foreach ($tag_fields as $key => $def) { register_term_meta('aksrt_product_tag', $key, [ 'type' => $def['type'], 'description' => $def['description'] ?? $key, 'single' => true, 'default' => $def['default'] ?? '', 'show_in_rest' => true, 'sanitize_callback' => $def['sanitize'] ?? 'sanitize_text_field', 'auth_callback' => $auth, ]); } // ── shop_discount ── $discount_fields = $this->get_discount_field_definitions(); foreach ($discount_fields as $key => $def) { register_term_meta('shop_discount', $key, [ 'type' => $def['type'], 'description' => $def['description'] ?? $key, 'single' => true, 'default' => $def['default'] ?? '', 'show_in_rest' => true, 'sanitize_callback' => $def['sanitize'] ?? 'sanitize_text_field', 'auth_callback' => $auth, ]); } } // ─── Field Definitions ─────────────────────────────────────── /** * Product category term meta field definitions. * * @return array */ private function get_cat_field_definitions(): array { return [ 'shop_cat_limit_buy' => [ 'type' => 'integer', 'description' => '分类级限购 (-1 不限)', 'default' => -1, 'sanitize' => function ($v) { return max(-1, min(9999, (int) $v)); }, ], 'shop_cat_shipping_fee' => [ 'type' => 'string', 'description' => '分类级运费类型', 'default' => '', 'sanitize' => function ($v) { $v = sanitize_key((string) $v); return in_array($v, ['', 'free', 'fixed', 'amount'], true) ? $v : ''; }, ], 'shop_cat_guest_buy' => [ 'type' => 'string', 'description' => '分类级免登录购买', 'default' => '', ], 'shop_cat_email_fill' => [ 'type' => 'string', 'description' => '分类级邮箱必留', 'default' => '', ], 'shop_cat_after_sale' => [ 'type' => 'string', 'description' => '分类级售后政策', 'default' => '', 'sanitize' => function ($v) { $v = sanitize_key((string) $v); return in_array($v, ['', 'refund', 'refund_return', 'none'], true) ? $v : ''; }, ], 'shop_cat_rebate' => [ 'type' => 'string', 'description' => '分类级返佣覆盖', 'default' => '', ], 'shop_cat_service' => [ 'type' => 'string', 'description' => '分类级服务保障', 'default' => '', 'sanitize' => 'sanitize_textarea_field', ], 'shop_cat_single_tab' => [ 'type' => 'string', 'description' => '分类级自定义 TAB', 'default' => '', 'sanitize' => 'sanitize_textarea_field', ], 'shop_cat_content_after' => [ 'type' => 'string', 'description' => '分类级详情页底部内容', 'default' => '', 'sanitize' => 'wp_kses_post', ], 'shop_cat_content_layout' => [ 'type' => 'string', 'description' => '分类级内容布局', 'default' => '', 'sanitize' => function ($v) { $v = sanitize_key((string) $v); return in_array($v, ['', 'adapt', 'full', 'sidebar'], true) ? $v : ''; }, ], 'shop_cat_content_show_bg' => [ 'type' => 'string', 'description' => '分类级详情背景盒子', 'default' => '', ], 'shop_cat_list_style' => [ 'type' => 'string', 'description' => '分类页列表样式', 'default' => '', 'sanitize' => function ($v) { $v = sanitize_key((string) $v); return in_array($v, ['', 'default', 'card-lg', 'card-sm'], true) ? $v : ''; }, ], ]; } /** * Product tag term meta field definitions. * * @return array */ private function get_tag_field_definitions(): array { return [ 'shop_tag_priority' => [ 'type' => 'integer', 'description' => '标签优先级(影响显示顺序)', 'default' => 0, 'sanitize' => function ($v) { return max(0, min(9999, (int) $v)); }, ], 'shop_tag_class' => [ 'type' => 'string', 'description' => '标签颜色 class', 'default' => '', 'sanitize' => 'sanitize_html_class', ], 'shop_tag_is_important' => [ 'type' => 'string', 'description' => '重点标签(商品价格位着重显示)', 'default' => '', ], ]; } /** * Shop discount term meta field definitions. * * @return array */ private function get_discount_field_definitions(): array { return [ 'shop_discount_small_badge' => [ 'type' => 'string', 'description' => '小标签文案', 'default' => '', ], 'shop_discount_priority' => [ 'type' => 'integer', 'description' => '优先级(数字越大越优先)', 'default' => 0, 'sanitize' => function ($v) { return max(0, min(9999, (int) $v)); }, ], 'shop_discount_is_important' => [ 'type' => 'string', 'description' => '重点活动', 'default' => '', ], 'shop_discount_important_class' => [ 'type' => 'string', 'description' => '重点活动背景色 class', 'default' => '', 'sanitize' => 'sanitize_html_class', ], 'shop_discount_scope' => [ 'type' => 'string', 'description' => '优惠范围', 'default' => 'item', 'sanitize' => function ($v) { $v = sanitize_key((string) $v); return in_array($v, ['item', 'product', 'author', 'order'], true) ? $v : 'item'; }, ], 'shop_discount_type' => [ 'type' => 'string', 'description' => '优惠类型', 'default' => 'reduction', 'sanitize' => function ($v) { $v = sanitize_key((string) $v); return in_array($v, ['reduction', 'discount', 'gift'], true) ? $v : 'reduction'; }, ], 'shop_discount_value' => [ 'type' => 'string', 'description' => '优惠数值(立减金额/折扣 0.01-9.99)', 'default' => '0', 'sanitize' => function ($v) { return sanitize_text_field((string) $v); }, ], 'shop_discount_gift_config' => [ 'type' => 'string', 'description' => '赠品配置 JSON', 'default' => '', 'sanitize' => 'sanitize_textarea_field', ], 'shop_discount_price_limit' => [ 'type' => 'string', 'description' => '金额门槛', 'default' => '0', 'sanitize' => function ($v) { return max(0, (float) $v); }, ], 'shop_discount_user_limit' => [ 'type' => 'string', 'description' => '用户身份限制', 'default' => '', 'sanitize' => function ($v) { $v = sanitize_key((string) $v); return in_array($v, ['', 'vip', 'vip_2', 'auth'], true) ? $v : ''; }, ], 'shop_discount_start' => [ 'type' => 'string', 'description' => '活动开始时间', 'default' => '', 'sanitize' => 'sanitize_text_field', ], 'shop_discount_end' => [ 'type' => 'string', 'description' => '活动结束时间', 'default' => '', 'sanitize' => 'sanitize_text_field', ], 'shop_discount_countdown' => [ 'type' => 'string', 'description' => '倒计时显示', 'default' => '', ], ]; } // ─── Getters ───────────────────────────────────────────────── /** * Get all shop term meta for a product category. * * @param int $term_id * @return array */ public static function get_cat_meta(int $term_id): array { $defaults = [ 'limit_buy' => -1, 'shipping_fee' => '', 'guest_buy' => '', 'email_fill' => '', 'after_sale' => '', 'rebate' => '', 'service' => '', 'single_tab' => '', 'content_after' => '', 'content_layout' => '', 'content_show_bg' => '', 'list_style' => '', ]; $result = []; foreach ($defaults as $key => $default) { $meta_key = "shop_cat_{$key}"; $val = get_term_meta($term_id, $meta_key, true); $result[$key] = ($val !== '' && $val !== false) ? $val : $default; } return $result; } /** * Get all shop term meta for a product tag. * * @param int $term_id * @return array */ public static function get_tag_meta(int $term_id): array { $priority = get_term_meta($term_id, 'shop_tag_priority', true); $class = get_term_meta($term_id, 'shop_tag_class', true); $is_important = get_term_meta($term_id, 'shop_tag_is_important', true); return [ 'priority' => $priority !== '' ? (int) $priority : 0, 'class' => is_string($class) ? $class : '', 'is_important' => !empty($is_important), ]; } /** * Get all shop discount term meta. * * @param int $term_id * @return array */ public static function get_discount_meta(int $term_id): array { $defaults = [ 'small_badge' => '', 'priority' => 0, 'is_important' => false, 'important_class' => '', 'scope' => 'item', 'type' => 'reduction', 'value' => '0', 'gift_config' => '', 'price_limit' => '0', 'user_limit' => '', 'start' => '', 'end' => '', 'countdown' => '', ]; $result = []; foreach ($defaults as $key => $default) { $meta_key = "shop_discount_{$key}"; $val = get_term_meta($term_id, $meta_key, true); $result[$key] = ($val !== '' && $val !== false) ? $val : $default; } $result['priority'] = (int) $result['priority']; $result['is_important'] = !empty($result['is_important']); $result['price_limit'] = (float) $result['price_limit']; return $result; } /** * Resolve parameter inheritance: product → category → theme settings. * * Returns the effective value for a given parameter key by checking: * 1. Product-level override (from post meta) * 2. Category-level override (from term meta) * 3. Theme-level default (from AKSRT_Settings::get_shop_settings()) * * @param int $product_id Product post ID * @param string $param_key Parameter key (e.g. 'limit_buy', 'shipping_fee') * @param mixed $product_value Product-level value (null if not set) * @return mixed */ public static function resolve_inheritance(int $product_id, string $param_key, $product_value = null) { // 1. Product-level override if ($product_value !== null && $product_value !== '' && $product_value !== -1) { return $product_value; } // 2. Category-level override $terms = get_the_terms($product_id, 'aksrt_product_cat'); if (is_array($terms) && !empty($terms)) { $cat_meta_key = "shop_cat_{$param_key}"; foreach ($terms as $term) { $val = get_term_meta($term->term_id, $cat_meta_key, true); if ($val !== '' && $val !== false && $val !== -1) { return $val; } } } // 3. Theme-level default $shop = AKSRT_Settings::get_shop_settings(); $defaults = $shop['product_defaults'] ?? []; return $defaults[$param_key] ?? $product_value; } /** * Add form fields for shop_discount taxonomy. */ public function render_discount_add_fields(): void { $fields = $this->get_discount_field_definitions(); foreach ($fields as $key => $def) { $label = (string) ($def['description'] ?? $key); $default = $def['default'] ?? ''; echo '
'; echo ''; $this->render_discount_field_control($key, $default, false); echo '
'; } } /** * Edit form fields for shop_discount taxonomy. */ public function render_discount_edit_fields($term): void { if (!($term instanceof WP_Term)) { return; } $fields = $this->get_discount_field_definitions(); foreach ($fields as $key => $def) { $label = (string) ($def['description'] ?? $key); $value = get_term_meta($term->term_id, $key, true); if ($value === '' || $value === false) { $value = $def['default'] ?? ''; } echo ''; echo ''; echo ''; $this->render_discount_field_control($key, $value, true); echo ''; } } /** * @param string|int|float $value */ private function render_discount_field_control(string $key, $value, bool $edit): void { $id = esc_attr($key); $name = esc_attr($key); $val = (string) $value; if ($key === 'shop_discount_type') { $opts = [ 'reduction' => __('立减', 'aksrt'), 'discount' => __('折扣', 'aksrt'), 'gift' => __('赠品', 'aksrt'), ]; echo ''; return; } if ($key === 'shop_discount_scope') { $opts = [ 'item' => __('单品', 'aksrt'), 'product' => __('商品', 'aksrt'), 'author' => __('商家', 'aksrt'), 'order' => __('整单', 'aksrt'), ]; echo ''; return; } if ($key === 'shop_discount_user_limit') { $opts = [ '' => __('不限', 'aksrt'), 'vip' => __('VIP1', 'aksrt'), 'vip_2' => __('VIP2', 'aksrt'), 'auth' => __('已认证', 'aksrt'), ]; echo ''; return; } if (in_array($key, ['shop_discount_is_important', 'shop_discount_countdown'], true)) { echo ''; return; } if ($key === 'shop_discount_gift_config') { echo ''; return; } $type = in_array($key, ['shop_discount_priority'], true) ? 'number' : 'text'; if (in_array($key, ['shop_discount_start', 'shop_discount_end'], true)) { $type = 'datetime-local'; // Convert mysql-ish values to datetime-local if needed. if ($val !== '' && str_contains($val, ' ')) { $val = str_replace(' ', 'T', substr($val, 0, 16)); } } printf( '', esc_attr($type), $name, $id, esc_attr($val), $edit ? 'regular-text' : '' ); } public function save_discount_term_fields(int $term_id): void { if (!current_user_can('manage_categories')) { return; } $fields = $this->get_discount_field_definitions(); foreach ($fields as $key => $def) { if (in_array($key, ['shop_discount_is_important', 'shop_discount_countdown'], true)) { $raw = isset($_POST[$key]) ? '1' : ''; } else { if (!isset($_POST[$key])) { continue; } $raw = wp_unslash($_POST[$key]); } if (isset($def['sanitize']) && is_callable($def['sanitize'])) { $value = call_user_func($def['sanitize'], $raw); } else { $value = sanitize_text_field((string) $raw); } if (in_array($key, ['shop_discount_start', 'shop_discount_end'], true) && is_string($value) && str_contains($value, 'T')) { $value = str_replace('T', ' ', $value); if (strlen($value) === 16) { $value .= ':00'; } } update_term_meta($term_id, $key, $value); } } public function render_cat_add_fields(): void { foreach ($this->get_cat_field_definitions() as $key => $def) { echo '
'; echo ''; $this->render_cat_field_control($key, $def['default'] ?? '', false); echo '
'; } } public function render_cat_edit_fields($term): void { if (!($term instanceof WP_Term)) { return; } foreach ($this->get_cat_field_definitions() as $key => $def) { $value = get_term_meta($term->term_id, $key, true); if ($value === '' || $value === false) { $value = $def['default'] ?? ''; } echo ''; $this->render_cat_field_control($key, $value, true); echo ''; } } /** * @param mixed $value */ private function render_cat_field_control(string $key, $value, bool $edit): void { $val = (string) $value; if (in_array($key, ['shop_cat_guest_buy', 'shop_cat_email_fill', 'shop_cat_content_show_bg'], true)) { echo ''; return; } if (in_array($key, ['shop_cat_service', 'shop_cat_single_tab', 'shop_cat_content_after'], true)) { echo ''; return; } if ($key === 'shop_cat_shipping_fee') { $opts = ['' => __('继承', 'aksrt'), 'free' => __('包邮', 'aksrt'), 'fixed' => __('固定运费', 'aksrt'), 'amount' => __('满额包邮', 'aksrt')]; echo ''; return; } if ($key === 'shop_cat_after_sale') { $opts = ['' => __('继承', 'aksrt'), 'refund' => __('仅退款', 'aksrt'), 'refund_return' => __('退货退款', 'aksrt'), 'none' => __('不支持', 'aksrt')]; echo ''; return; } if ($key === 'shop_cat_content_layout') { $opts = ['' => __('继承', 'aksrt'), 'adapt' => __('自适应', 'aksrt'), 'full' => __('通栏', 'aksrt'), 'sidebar' => __('侧栏', 'aksrt')]; echo ''; return; } if ($key === 'shop_cat_list_style') { $opts = ['' => __('继承', 'aksrt'), 'default' => __('默认', 'aksrt'), 'card-lg' => __('大卡片', 'aksrt'), 'card-sm' => __('小卡片', 'aksrt')]; echo ''; return; } $type = $key === 'shop_cat_limit_buy' ? 'number' : 'text'; printf( '', esc_attr($type), $edit ? 'regular-text' : '', esc_attr($key), esc_attr($key), esc_attr($val) ); } public function save_cat_term_fields(int $term_id): void { if (!current_user_can('manage_categories')) { return; } foreach ($this->get_cat_field_definitions() as $key => $def) { if (in_array($key, ['shop_cat_guest_buy', 'shop_cat_email_fill', 'shop_cat_content_show_bg'], true)) { $raw = isset($_POST[$key]) ? '1' : ''; } else { if (!isset($_POST[$key])) { continue; } $raw = wp_unslash($_POST[$key]); } if (isset($def['sanitize']) && is_callable($def['sanitize'])) { $value = call_user_func($def['sanitize'], $raw); } else { $value = sanitize_text_field((string) $raw); } update_term_meta($term_id, $key, $value); } } public function render_tag_add_fields(): void { foreach ($this->get_tag_field_definitions() as $key => $def) { echo '
'; echo ''; $this->render_tag_field_control($key, $def['default'] ?? ''); echo '
'; } } public function render_tag_edit_fields($term): void { if (!($term instanceof WP_Term)) { return; } foreach ($this->get_tag_field_definitions() as $key => $def) { $value = get_term_meta($term->term_id, $key, true); if ($value === '' || $value === false) { $value = $def['default'] ?? ''; } echo ''; $this->render_tag_field_control($key, $value); echo ''; } } /** * @param mixed $value */ private function render_tag_field_control(string $key, $value): void { $val = (string) $value; if ($key === 'shop_tag_is_important') { echo ''; return; } $type = $key === 'shop_tag_priority' ? 'number' : 'text'; printf( '', esc_attr($type), esc_attr($key), esc_attr($key), esc_attr($val) ); } public function save_tag_term_fields(int $term_id): void { if (!current_user_can('manage_categories')) { return; } foreach ($this->get_tag_field_definitions() as $key => $def) { if ($key === 'shop_tag_is_important') { $raw = isset($_POST[$key]) ? '1' : ''; } else { if (!isset($_POST[$key])) { continue; } $raw = wp_unslash($_POST[$key]); } if (isset($def['sanitize']) && is_callable($def['sanitize'])) { $value = call_user_func($def['sanitize'], $raw); } else { $value = sanitize_text_field((string) $raw); } update_term_meta($term_id, $key, $value); } } }