refactor: rename the theme brand and every identifier from AKSRT to Jio

Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-11-01 20:05:04 +08:00
1 parent 5b16bc6e15
commit 35bb96e82f
997 files changed
+28216 -28188

No files matched your search

@@ -9,12 +9,12 @@
* `btn-block but mb10 padding-lg c-green mini-radius` 按钮);
* - /user/vip/ 档位购买卡下方(zibpay_user_page_tab_content_vip 的
* `c-yellow zib-widget flex ac jsb` 全宽横条,仅非会员展示)。
* 两处入口均带 data-aksrt-vip-points-open,由 pay-vip.js 文档级委托打开
* 两处入口均带 data-jio-vip-points-open,由 pay-vip.js 文档级委托打开
* 同一静态弹窗。
* 2) 静止态必须被 vip-modal.css 显式隐藏(.flex 覆盖回归护栏),打开态为
* flex 而非 block,且层级高于 runtime 宿主(100050),以复现参考主题在
* 会员弹窗之上再叠一层兑换弹窗的行为。
* 3) 旧的客户端自实现弹窗(renderPointsModal / aksrt-vip-points-option /
* 3) 旧的客户端自实现弹窗(renderPointsModal / jio-vip-points-option /
* catalog.vip_url)与旧版 /user/vip/ 自定义入口(uc-vip-points-link)
* 必须已彻底移除。
*/
@@ -29,7 +29,7 @@ const read = (...seg) => fs.readFileSync(path.join(root, ...seg), 'utf8');
const modal = read('src', 'Content', 'VipServiceModal.php');
const catalogSource = read('src', 'Content', 'VipServicePurchase.php');
const footer = read('inc', 'helpers', 'aksrt-vip.php');
const footer = read('inc', 'helpers', 'jio-vip.php');
const vipJs = read('assets', 'js', 'pay-vip.js');
const creditsJs = read('assets', 'js', 'credits.js');
const vipCss = read('assets', 'css', 'frontend', 'vip-modal.css');
@@ -39,12 +39,12 @@ const ledger = read('src', 'Ledger', 'LedgerPresentation.php');
test('弹窗为参考主题 refresh_modal 同构静态壳并带 data 钩子', () => {
assert.match(modal, /public function get_points_exchange_modal_html/);
assert.match(modal, /class="modal flex jc fade"[^']*data-aksrt-vip-points-modal/);
assert.match(modal, /class="modal flex jc fade"[^']*data-jio-vip-points-modal/);
assert.match(modal, /class="modal-dialog modal-mini full-sm"/);
assert.match(modal, /class="modal-content"><div class="modal-body">/);
assert.match(modal, /data-dismiss="modal"/);
assert.match(modal, /#icon-close/);
assert.match(modal, /data-aksrt-vip-points-form/);
assert.match(modal, /data-jio-vip-points-form/);
});
test('弹窗内容保持参考主题结构:标题/我的{货币名}/卡片选择/立即兑换', () => {
@@ -54,11 +54,11 @@ test('弹窗内容保持参考主题结构:标题/我的{货币名}/卡片选
assert.match(modal, /#icon-vip_1/);
// 我的{货币名}盒(点回用户中心积分页)。货币名走自定义名称,不硬编码。
assert.match(modal, /muted-box padding-h10 mb20/);
assert.match(modal, /sprintf\(__\('我的%s', 'aksrt'\), \$currency\)/);
assert.match(modal, /sprintf\(__\('我的%s', 'jio'\), \$currency\)/);
assert.match(modal, /#icon-points/);
assert.match(modal, /aksrt_get_user_center_tab_url\('credits'\)/);
assert.match(modal, /jio_get_user_center_tab_url\('credits'\)/);
// 标题同样跟随货币名。
assert.match(modal, /sprintf\(__\('%s兑换会员', 'aksrt'\), \$currency\)/);
assert.match(modal, /sprintf\(__\('%s兑换会员', 'jio'\), \$currency\)/);
// 卡片列表 + 参考主题 data-for/data-value 选中模型。
assert.match(modal, /请选择需兑换的会员/);
assert.match(modal, /flex hh exchange-card-box/);
@@ -69,15 +69,15 @@ test('弹窗内容保持参考主题结构:标题/我的{货币名}/卡片选
assert.match(modal, /name="order_type" value="4"/);
assert.match(modal, /name="action" value="points_initiate_pay"/);
assert.match(modal, /mt6 but jb-yellow btn-block radius padding-lg/);
assert.match(modal, /data-aksrt-vip-points-submit/);
assert.match(modal, /data-jio-vip-points-submit/);
// CTA 与积分不足状态都从卡片选择区留出 20px 上间距,避免状态切换时布局贴紧。
assert.match(modal, /\$form = '<div class="mt20">'/);
assert.match(modal, /'<div class="mt20"><div class="badg c-red btn-block">'/);
});
test('弹窗壳随 wp_footer 全局输出且不进入 catalog 载荷', () => {
assert.match(footer, /function aksrt_render_vip_points_exchange_modal/);
assert.match(footer, /add_action\('wp_footer', 'aksrt_render_vip_points_exchange_modal'\)/);
assert.match(footer, /function jio_render_vip_points_exchange_modal/);
assert.match(footer, /add_action\('wp_footer', 'jio_render_vip_points_exchange_modal'\)/);
assert.match(footer, /VipService::get_instance\(\)->get_points_exchange_modal_html/);
// 兑换入口不再跳用户中心:catalog 里的 vip_url 已清除。
assert.doesNotMatch(catalogSource, /'vip_url'/);
@@ -85,25 +85,25 @@ test('弹窗壳随 wp_footer 全局输出且不进入 catalog 载荷', () => {
});
test('我的{货币名}盒恢复参考主题 muted-box 的背景、圆角与内边距', () => {
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s+\.muted-box\.padding-h10\s*\{\s*background-color:\s*var\(--this-bg,\s*var\(--muted-border-color\)\);\s*border-radius:\s*var\(--main-radius\);\s*padding:\s*10px 15px;\s*\}/);
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s+\.muted-box\.padding-10,\s*\.modal\[data-aksrt-vip-points-modal\]\s+\.aksrt-vip-points-desc\s*\{\s*background-color:\s*var\(--this-bg,\s*var\(--muted-border-color\)\);\s*border-radius:\s*var\(--main-radius\);\s*padding:\s*10px;\s*\}/);
assert.match(vipCss, /\.modal\[data-jio-vip-points-modal\]\s+\.muted-box\.padding-h10\s*\{\s*background-color:\s*var\(--this-bg,\s*var\(--muted-border-color\)\);\s*border-radius:\s*var\(--main-radius\);\s*padding:\s*10px 15px;\s*\}/);
assert.match(vipCss, /\.modal\[data-jio-vip-points-modal\]\s+\.muted-box\.padding-10,\s*\.modal\[data-jio-vip-points-modal\]\s+\.jio-vip-points-desc\s*\{\s*background-color:\s*var\(--this-bg,\s*var\(--muted-border-color\)\);\s*border-radius:\s*var\(--main-radius\);\s*padding:\s*10px;\s*\}/);
});
test('后台兑换说明直接从 VIP 设置读取并作为富文本置于套餐与提交动作之间', () => {
assert.match(modal, /\$desc_text = trim\(\(string\) \(\$vip\['points_exchange'\]\['desc'\] \?\? ''\)\);/);
assert.match(modal, /aksrt-vip-points-desc/);
assert.match(modal, /jio-vip-points-desc/);
assert.match(modal, /wp_kses_post\(\$desc_text\)/);
assert.match(modal, /\. \$header\s*\. '<form class="" data-aksrt-vip-points-form>' \. \$user_box \. \$lists \. \$desc \. \$form/);
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s+\.aksrt-vip-points-desc\s*\{[^}]*display:\s*block;[^}]*margin-top:\s*20px;[^}]*color:\s*var\(--muted-2-color,\s*#999\);/s);
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s+\.muted-box\.padding-10,\s*\.modal\[data-aksrt-vip-points-modal\]\s+\.aksrt-vip-points-desc\s*\{[^}]*padding:\s*10px;/s);
assert.match(modal, /\. \$header\s*\. '<form class="" data-jio-vip-points-form>' \. \$user_box \. \$lists \. \$desc \. \$form/);
assert.match(vipCss, /\.modal\[data-jio-vip-points-modal\]\s+\.jio-vip-points-desc\s*\{[^}]*display:\s*block;[^}]*margin-top:\s*20px;[^}]*color:\s*var\(--muted-2-color,\s*#999\);/s);
assert.match(vipCss, /\.modal\[data-jio-vip-points-modal\]\s+\.muted-box\.padding-10,\s*\.modal\[data-jio-vip-points-modal\]\s+\.jio-vip-points-desc\s*\{[^}]*padding:\s*10px;/s);
});
test('兑换说明从后台设置实际输出,保留 HTML 并显示在卡片与 CTA 之间', () => {
const rendered = execFileSync(process.env.PHP_BINARY || 'php', [
path.join(__dirname, 'vip-points-exchange-description-harness.php'),
], { encoding: 'utf8' });
assert.match(rendered, /class="muted-box muted-2-color padding-10 em09 mt20 aksrt-vip-points-desc"><p>后台配置的兑换说明<\/p><\/div>/);
assert.ok(rendered.indexOf('aksrt-vip-points-desc') < rendered.indexOf('data-aksrt-vip-points-submit'));
assert.match(rendered, /class="muted-box muted-2-color padding-10 em09 mt20 jio-vip-points-desc"><p>后台配置的兑换说明<\/p><\/div>/);
assert.ok(rendered.indexOf('jio-vip-points-desc') < rendered.indexOf('data-jio-vip-points-submit'));
});
test('运行时:自定义货币名改写弹窗标题/我的X/卡片单位,不足提示同源', () => {
@@ -119,7 +119,7 @@ test('运行时:自定义货币名改写弹窗标题/我的X/卡片单位,
assert.doesNotMatch(rendered, /积分兑换会员/);
assert.doesNotMatch(rendered, /我的积分/);
// 余额充足 → 输出兑换 CTA 而非不足提示。
assert.match(rendered, /data-aksrt-vip-points-submit/);
assert.match(rendered, /data-jio-vip-points-submit/);
});
test('运行时:余额不足时提示文案同样跟随自定义货币名', () => {
@@ -130,7 +130,7 @@ test('运行时:余额不足时提示文案同样跟随自定义货币名', ()
], { encoding: 'utf8' });
assert.match(rendered, /书币不足,暂时无法兑换/);
assert.doesNotMatch(rendered, /积分不足/);
assert.doesNotMatch(rendered, /data-aksrt-vip-points-submit/);
assert.doesNotMatch(rendered, /data-jio-vip-points-submit/);
});
test('运行时:/user/vip/ 入口横条实际渲染出自定义货币名', () => {
@@ -139,7 +139,7 @@ test('运行时:/user/vip/ 入口横条实际渲染出自定义货币名', ()
], { encoding: 'utf8' }).split('\n');
// 场景 1:后台改名后,横条标题 = 「{货币名}兑换会员」,不再硬编码「积分」。
assert.match(out[0], /<a class="uc-vip-points-entry flex ac jsb"[^>]*data-aksrt-vip-points-open>/);
assert.match(out[0], /<a class="uc-vip-points-entry flex ac jsb"[^>]*data-jio-vip-points-open>/);
assert.match(out[0], /书币兑换会员/);
assert.doesNotMatch(out[0], /积分兑换会员/);
// 场景 2:已是会员 → 入口不输出(参考主题门控)。
@@ -149,12 +149,12 @@ test('运行时:/user/vip/ 入口横条实际渲染出自定义货币名', ()
});
test('弹窗壳静止态必须被 vip-modal.css 显式隐藏(.flex 覆盖回归护栏)', () => {
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s*\{\s*display:\s*none;/);
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\.in\s*\{\s*display:\s*flex;/);
assert.doesNotMatch(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\.in\s*\{\s*display:\s*block;/);
// 层级必须高于 runtime 宿主(.aksrt-modal-runtime 的 z-index: 100050)。
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s*\{[^}]*z-index:\s*100060;/s);
assert.match(vipCss, /\.modal-backdrop\[data-aksrt-vip-points-backdrop\]\s*\{\s*z-index:\s*100055;/);
assert.match(vipCss, /\.modal\[data-jio-vip-points-modal\]\s*\{\s*display:\s*none;/);
assert.match(vipCss, /\.modal\[data-jio-vip-points-modal\]\.in\s*\{\s*display:\s*flex;/);
assert.doesNotMatch(vipCss, /\.modal\[data-jio-vip-points-modal\]\.in\s*\{\s*display:\s*block;/);
// 层级必须高于 runtime 宿主(.jio-modal-runtime 的 z-index: 100050)。
assert.match(vipCss, /\.modal\[data-jio-vip-points-modal\]\s*\{[^}]*z-index:\s*100060;/s);
assert.match(vipCss, /\.modal-backdrop\[data-jio-vip-points-backdrop\]\s*\{\s*z-index:\s*100055;/);
});
test('开关自包含:不依赖 jQuery/Bootstrap modal 运行时', () => {
@@ -175,15 +175,15 @@ test('会员弹窗内的入口为参考主题同款按钮并开本地弹窗', ()
// 图标顺序与 class 串保持参考主题一致;FA5 样式类 `fas` 由图标契约(
// legacy-navigation-contract 的 FA5-only 约束)强制要求,故在此一并断言。
assert.match(vipJs, /fa fas fa-angle-right em12 ml10/);
assert.match(vipJs, /data-aksrt-vip-points-open/);
assert.match(vipJs, /data-jio-vip-points-open/);
assert.match(vipJs, /openPointsModal\(pointsLink\)/);
});
test('/user/vip/ 档位购买卡下方的入口为参考主题用户中心同款横条', () => {
// 参考主题 zibpay_user_page_tab_content_vip:my_box → vip_card → points link,
// 横条为 `c-yellow zib-widget flex ac jsb`(zib-widget → 本主题aksrt-panel表面)。
assert.match(footer, /function aksrt_get_vip_points_exchange_entry_html/);
assert.match(vipTab, /aksrt_get_vip_points_exchange_entry_html\(\$user_id, \$level\)/);
// 横条为 `c-yellow zib-widget flex ac jsb`(zib-widget → 本主题jio-panel表面)。
assert.match(footer, /function jio_get_vip_points_exchange_entry_html/);
assert.match(vipTab, /jio_get_vip_points_exchange_entry_html\(\$user_id, \$level\)/);
// 结构 1:1:左 points-color 图标 + 标题(flex1 em12 c-yellow),
// 右「立即兑换」badg + 右箭头。
assert.match(footer, /uc-vip-points-entry flex ac jsb/);
@@ -191,7 +191,7 @@ test('/user/vip/ 档位购买卡下方的入口为参考主题用户中心同款
assert.match(footer, /#icon-points-color/);
assert.match(footer, /badg radius c-yellow/);
assert.match(footer, /fa fas fa-chevron-circle-right ml6/);
assert.match(footer, /data-aksrt-vip-points-open/);
assert.match(footer, /data-jio-vip-points-open/);
});
test('/user/vip/ 入口门控与弹窗壳同源:非会员 + 兑换开启 + 有可兑换套餐', () => {
@@ -209,29 +209,29 @@ test('/user/vip/ 入口文案跟随自定义货币名称,不硬编码「积分
// 横条标题取 catalog.currency_name(后台「积分配置 → 自定义货币名称」),
// 与它打开的兑换弹窗标题(VipServiceModal)和会员弹窗内入口(pay-vip.js)同源。
assert.match(footer, /\$currency = \(string\) \(\$catalog\['currency_name'\] \?\? ''\);/);
assert.match(footer, /esc_html\(sprintf\(__\('%s兑换会员', 'aksrt'\), \$currency\)\)/);
assert.match(footer, /esc_html\(sprintf\(__\('%s兑换会员', 'jio'\), \$currency\)\)/);
// 缺失时回退设置里的名称,再回退「积分」。
assert.match(footer, /Settings::get_currency_name\(\)/);
assert.doesNotMatch(footer, /esc_html__\('积分兑换会员', 'aksrt'\)/);
assert.doesNotMatch(footer, /esc_html__\('积分兑换会员', 'jio'\)/);
// 弹窗内同一命名路径:标题 / 我的{货币名} / 不足提示都走 $currency。
assert.match(modal, /\$currency = \(string\) \(\$catalog\['currency_name'\] \?\? __\('积分', 'aksrt'\)\);/);
assert.doesNotMatch(modal, /esc_html__\('积分兑换会员', 'aksrt'\)/);
assert.match(modal, /\$currency = \(string\) \(\$catalog\['currency_name'\] \?\? __\('积分', 'jio'\)\);/);
assert.doesNotMatch(modal, /esc_html__\('积分兑换会员', 'jio'\)/);
});
test('兑换链路文案(弹窗/错误/订单备注/流水)统一跟随货币名', () => {
// 不足提示与后端 WP_Error 同文案,均以货币名为前缀。
assert.match(modal, /esc_html\(sprintf\(__\('%s不足,暂时无法兑换', 'aksrt'\), \$currency\)\)/);
assert.match(catalogSource, /aksrt_points_insufficient', sprintf\(__\('%s不足,暂时无法兑换', 'aksrt'\), \$currency_name\)/);
assert.match(catalogSource, /aksrt_points_exchange_disabled', sprintf\(__\('%s兑换会员未启用', 'aksrt'\), \$currency_name\)/);
assert.match(catalogSource, /aksrt_invalid_price', sprintf\(__\('兑换%s无效', 'aksrt'\), \$currency_name\)/);
assert.match(modal, /esc_html\(sprintf\(__\('%s不足,暂时无法兑换', 'jio'\), \$currency\)\)/);
assert.match(catalogSource, /jio_points_insufficient', sprintf\(__\('%s不足,暂时无法兑换', 'jio'\), \$currency_name\)/);
assert.match(catalogSource, /jio_points_exchange_disabled', sprintf\(__\('%s兑换会员未启用', 'jio'\), \$currency_name\)/);
assert.match(catalogSource, /jio_invalid_price', sprintf\(__\('兑换%s无效', 'jio'\), \$currency_name\)/);
// exchange_with_points 自行解析 currency_name(get_catalog 内的局部变量不在该作用域)。
assert.match(catalogSource, /\$currency_name = \(string\) \(\$catalog\['currency_name'\] \?\? ''\);/);
// 订单备注与流水说明。
assert.match(catalogSource, /sprintf\(__\('%1\$s兑换会员 %2\$d %1\$s。', 'aksrt'\), \$currency_name, \$cost\)/);
assert.match(catalogSource, /sprintf\(__\('%s兑换会员 · %s', 'aksrt'\), \$currency_name, \(string\) \(\$match\['title'\] \?\? ''\)\)/);
assert.match(catalogSource, /sprintf\(__\('%1\$s兑换会员 %2\$d %1\$s。', 'jio'\), \$currency_name, \$cost\)/);
assert.match(catalogSource, /sprintf\(__\('%s兑换会员 · %s', 'jio'\), \$currency_name, \(string\) \(\$match\['title'\] \?\? ''\)\)/);
// 流水原因标签(用户中心明细里 desc 为空时的回退)。
assert.match(ledger, /'vip_points_exchange' => sprintf\(__\('%s兑换会员', 'aksrt'\), self::points_name\(\)\)/);
assert.doesNotMatch(ledger, /'vip_points_exchange' => __\('积分兑换会员', 'aksrt'\)/);
assert.match(ledger, /'vip_points_exchange' => sprintf\(__\('%s兑换会员', 'jio'\), self::points_name\(\)\)/);
assert.doesNotMatch(ledger, /'vip_points_exchange' => __\('积分兑换会员', 'jio'\)/);
});
test('页面入口样式落在 credits.css 且与档位卡同一表面体系', () => {
@@ -244,7 +244,7 @@ test('入口开合由文档级委托驱动,弹窗内不再单独挂监听', ()
// /user/vip/ 静态横条与弹窗内按钮共用一处 document 委托(冒泡)。
assert.match(vipJs, /function bindPointsEntry\(\)/);
assert.match(vipJs, /bindPointsEntry\(\);/);
assert.match(vipJs, /document\.addEventListener\("click", function \(event\) \{[\s\S]*?data-aksrt-vip-points-open/);
assert.match(vipJs, /document\.addEventListener\("click", function \(event\) \{[\s\S]*?data-jio-vip-points-open/);
// bindModalUi 内的重复分支已移除:openPointsModal(pointsLink) 只保留一处调用。
const openCalls = vipJs.match(/openPointsModal\(pointsLink\)/g) || [];
assert.strictEqual(openCalls.length, 1);
@@ -252,12 +252,12 @@ test('入口开合由文档级委托驱动,弹窗内不再单独挂监听', ()
test('旧的客户端自实现弹窗与旧版自定义入口已彻底移除', () => {
assert.doesNotMatch(vipJs, /renderPointsModal|exchangeFromModal|vipPointsPay/);
assert.doesNotMatch(vipJs, /aksrt-vip-points-option|aksrt-vip-points-modal__/);
assert.doesNotMatch(vipCss, /\.aksrt-vip-points-option|\.aksrt-vip-points-submit/);
assert.doesNotMatch(vipJs, /jio-vip-points-option|jio-vip-points-modal__/);
assert.doesNotMatch(vipCss, /\.jio-vip-points-option|\.jio-vip-points-submit/);
// 旧版 /user/vip/ 自定义入口(uc-vip-points-link)不得回归;现行入口为
// 参考主题 1:1 的 uc-vip-points-entry 横条。
assert.doesNotMatch(vipTab, /uc-vip-points-link/);
assert.doesNotMatch(creditsCss, /\.uc-vip-points-link/);
// 用户中心侧的旧兑换控制器([data-aksrt-vip-points],全站无对应 DOM)。
assert.doesNotMatch(creditsJs, /initVipPointsExchange|data-aksrt-vip-points/);
// 用户中心侧的旧兑换控制器([data-jio-vip-points],全站无对应 DOM)。
assert.doesNotMatch(creditsJs, /initVipPointsExchange|data-jio-vip-points/);
});