diff --git a/assets/admin/src/api/modules/users.js b/assets/admin/src/api/modules/users.js
index 39fa3c68..34818810 100644
--- a/assets/admin/src/api/modules/users.js
+++ b/assets/admin/src/api/modules/users.js
@@ -27,8 +27,4 @@ export const usersApiMethods = {
adjustUserCredits(id, asset, delta, remark) {
return this.request(`/assets/accounts/${id}/adjust`, { method: 'POST', body: JSON.stringify({ asset, delta, remark }) })
},
- getCreditLog(params = {}) {
- const qs = new URLSearchParams(params).toString()
- return this.request('/assets/transactions' + (qs ? `?${qs}` : ''))
- },
}
diff --git a/assets/admin/src/pages/CmsCredits.vue b/assets/admin/src/pages/CmsCredits.vue
index 56437018..4c5ddac7 100644
--- a/assets/admin/src/pages/CmsCredits.vue
+++ b/assets/admin/src/pages/CmsCredits.vue
@@ -23,33 +23,6 @@
-
-
-
-
-
-
-
- 查询
- 重置
-
-
-
-
-
-
@@ -162,79 +135,6 @@ const cfg = reactive({
})
const streakJson = ref('{}')
const saving = ref(false)
-const ledgerLoading = ref(false)
-const ledgerRows = ref([])
-const ledgerPage = ref(1)
-const ledgerPageCount = ref(1)
-const ledgerFilters = reactive({
- user_id: '',
- asset: 'points',
- date_from: '',
- date_to: '',
-})
-const assetOptions = [
- { label: '积分', value: 'points' },
- { label: '余额', value: 'balance' },
-]
-const ledgerColumns = [
- { title: '类型', key: 'reason', width: 150 },
- { title: '订单号', key: 'ref_key', width: 200 },
- { title: '变动 / 变动后', key: 'delta_label', width: 190 },
- { title: '说明', key: 'remark', width: 240, ellipsis: { tooltip: true } },
- { title: '时间', key: 'created_at', width: 170 },
-]
-
-function formatAssetValue(asset, value) {
- const raw = Number(value ?? 0)
- if (!Number.isFinite(raw)) return asset === 'balance' ? '0.00' : '0'
- if (asset !== 'balance') return String(raw)
- const negative = raw < 0
- const digits = String(Math.abs(Math.trunc(raw))).padStart(3, '0')
- const whole = digits.slice(0, -2).replace(/^0+(?=\d)/, '')
- return (negative ? '-' : '') + whole + '.' + digits.slice(-2)
-}
-
-async function loadLedger() {
- ledgerLoading.value = true
- try {
- if (!/^\d+$/.test(String(ledgerFilters.user_id).trim())) {
- message.warning('请输入用户 ID 后查询该用户近期记录')
- return
- }
- const params = { page: ledgerPage.value, per_page: 10 }
- Object.entries(ledgerFilters).forEach(([key, value]) => {
- if (value !== null && String(value).trim() !== '') params[key] = value
- })
- const response = await api.getCreditLog(params)
- const data = response?.data || response || {}
- ledgerRows.value = (data.items || []).map((row) => {
- const entries = Array.isArray(row.entries) ? row.entries : []
- const entry = entries[0] || {}
- return {
- ...row,
- delta_label: `${formatAssetValue(entry.asset, entry.delta)} / ${formatAssetValue(entry.asset, entry.balance_after)}`,
- remark: row.remark || '—',
- }
- })
- ledgerPageCount.value = Math.max(1, Math.ceil(Number(data.total || 0) / Number(data.per_page || 10)))
- } catch (e) {
- message.error(e.message || '加载资产记录失败')
- } finally {
- ledgerLoading.value = false
- }
-}
-
-function searchLedger() {
- ledgerPage.value = 1
- loadLedger()
-}
-
-function resetLedger() {
- Object.assign(ledgerFilters, { user_id: '', asset: 'points', date_from: '', date_to: '' })
- ledgerRows.value = []
- ledgerPage.value = 1
- ledgerPageCount.value = 1
-}
async function loadCfg() {
try {
diff --git a/src/Admin/UserController.php b/src/Admin/UserController.php
index 620b735a..8979736e 100644
--- a/src/Admin/UserController.php
+++ b/src/Admin/UserController.php
@@ -135,20 +135,6 @@ class UserController extends ControllerBase {
],
]);
- register_rest_route(self::API_NAMESPACE, '/assets/transactions', [
- 'methods' => 'GET',
- 'callback' => [$this, 'rest_get_credits_log'],
- 'permission_callback' => [$this, 'check_permission'],
- 'args' => [
- 'user_id' => ['type' => 'integer', 'minimum' => 1, 'required' => true],
- 'asset' => ['type' => 'string', 'enum' => ['points', 'balance'], 'required' => true],
- 'page' => ['type' => 'integer', 'minimum' => 1, 'default' => 1],
- 'per_page' => ['type' => 'integer', 'minimum' => 1, 'maximum' => 50, 'default' => 10],
- 'date_from' => ['type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
- 'date_to' => ['type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
- ],
- ]);
-
// Report ticket list (admin)
register_rest_route(self::API_NAMESPACE, '/users/reports', [
'methods' => 'GET',
@@ -740,54 +726,4 @@ class UserController extends ControllerBase {
], 'message' => __('资产调整已保存', 'aksrt')]);
}
- public function rest_get_credits_log(\WP_REST_Request $request): \WP_REST_Response {
- $user_id = max(0, (int) $request->get_param('user_id'));
- $asset = sanitize_key((string) $request->get_param('asset'));
- if ($user_id <= 0 || !get_userdata($user_id) || !in_array($asset, ['points', 'balance'], true)) {
- return $this->error(__('用户或资产类型无效', 'aksrt'), 400);
- }
- $meta_key = $asset === 'points' ? 'points_record' : 'balance_record';
- $records = get_user_meta($user_id, $meta_key, true);
- $records = is_array($records) ? array_slice($records, 0, 50) : [];
- $date_from = sanitize_text_field((string) $request->get_param('date_from'));
- $date_to = sanitize_text_field((string) $request->get_param('date_to'));
- $records = array_values(array_filter($records, static function ($record) use ($date_from, $date_to): bool {
- if (!is_array($record)) return false;
- $day = substr((string) ($record['time'] ?? ''), 0, 10);
- return ($date_from === '' || $day >= $date_from) && ($date_to === '' || $day <= $date_to);
- }));
- $page = max(1, (int) $request->get_param('page'));
- $per_page = min(50, max(1, (int) $request->get_param('per_page')));
- $items = [];
- foreach (array_slice($records, ($page - 1) * $per_page, $per_page) as $index => $record) {
- $value = $asset === 'points' ? (int) ($record['value'] ?? 0) : $this->asset_value_to_minor((string) ($record['value'] ?? '0'));
- $after = $asset === 'points' ? (int) ($record['points'] ?? 0) : $this->asset_value_to_minor((string) ($record['balance'] ?? '0'));
- $items[] = [
- 'id' => (($page - 1) * $per_page) + $index + 1,
- 'operation_key' => (string) ($record['operation_key'] ?? ''),
- 'reason' => (string) ($record['type'] ?? ''),
- 'ref_type' => !empty($record['order_num']) ? 'order' : '',
- 'ref_key' => (string) ($record['order_num'] ?? ''),
- 'actor_user_id' => 0,
- 'assets' => [$asset],
- 'created_at' => (string) ($record['time'] ?? ''),
- 'remark' => (string) ($record['desc'] ?? ''),
- 'entries' => [[
- 'user_id' => $user_id, 'asset' => $asset, 'entry_type' => $value < 0 ? 'debit' : 'credit',
- 'delta' => $value, 'balance_after' => $after, 'remark' => (string) ($record['desc'] ?? ''),
- ]],
- ];
- }
- $data = ['items' => $items, 'total' => count($records), 'page' => $page, 'per_page' => $per_page];
- return $this->success(['data' => $data]);
- }
-
- private function asset_value_to_minor(string $value): int {
- $value = trim($value);
- $negative = str_starts_with($value, '-');
- if ($negative) $value = substr($value, 1);
- try { $minor = \AKSRT\Ledger\AssetAmount::balance($value)->minor(); } catch (\InvalidArgumentException $e) { return 0; }
- return $negative ? -$minor : $minor;
- }
-
}
diff --git a/tests/widget-system/asset-ledger-contract.test.cjs b/tests/widget-system/asset-ledger-contract.test.cjs
index 53a04da0..bd33db36 100644
--- a/tests/widget-system/asset-ledger-contract.test.cjs
+++ b/tests/widget-system/asset-ledger-contract.test.cjs
@@ -324,18 +324,22 @@ test('VIP card redemption is one locked transaction with bounded retry and repla
assert.match(method, /vip_card_replay_result/)
})
-test('admin recent asset records read the user-meta histories', () => {
- const controller = fs.readFileSync(path.join(root, 'src', 'Admin', 'UserController.php'), 'utf8')
+test('the SPA credits page is configuration only, with no asset record browser', () => {
+ // Recent per-user asset records live on the native 用户 → 积分/余额 screens
+ // (src/User/CreditsAdmin.php, src/User/BalanceAdmin.php). The SPA keeps
+ // configuration only, so the record table, its REST route and its API method
+ // must not come back.
const page = fs.readFileSync(path.join(root, 'assets', 'admin', 'src', 'pages', 'CmsCredits.vue'), 'utf8')
- assert.match(controller, /'balance_record'/)
- assert.match(controller, /'points_record'/)
- assert.match(controller, /'balance_value'/)
- assert.match(controller, /'points_value'/)
- assert.match(page, /用户近期资产记录/)
- assert.match(page, /api\.getCreditLog\(params\)/)
- assert.doesNotMatch(page, /统一资产账本|transaction_id|operation_key|toFixed|number_format/)
- assert.match(page, /Math\.trunc\(raw\)/)
- assert.match(page, /padStart\(3, '0'\)/)
+ const api = fs.readFileSync(path.join(root, 'assets', 'admin', 'src', 'api', 'modules', 'users.js'), 'utf8')
+ const controller = fs.readFileSync(path.join(root, 'src', 'Admin', 'UserController.php'), 'utf8')
+
+ assert.match(page, /积分配置/)
+ assert.match(page, /api\.getCreditSettings\(\)/)
+ assert.doesNotMatch(page, /用户近期资产记录/)
+ assert.doesNotMatch(page, /getCreditLog|ledgerRows|ledgerFilters|assetOptions/)
+ assert.doesNotMatch(api, /getCreditLog|assets\/transactions/)
+ assert.doesNotMatch(controller, /assets\/transactions/)
+ assert.doesNotMatch(controller, /rest_get_credits_log|asset_value_to_minor/)
})
test('asset purchases share the order center and refresh every wallet surface', () => {