进行了一些小优化

This commit is contained in:
2026-08-25 06:30:29 +08:00
parent eef73ee9e7
commit 4c81980804
3 changed files with 157 additions and 66 deletions
+27 -7
View File
@@ -32,6 +32,16 @@ final class TransitionService {
/** @param array<string,mixed> $fields @param array<string,mixed> $event */
public function transition(array $application, string $next, array $fields = [], array $event = []) {
return $this->transition_with_policy($application, $next, $fields, $event, false);
}
/** @param array<string,mixed> $fields @param array<string,mixed> $event */
public function admin_transition(array $application, string $next, array $fields = [], array $event = []) {
return $this->transition_with_policy($application, $next, $fields, $event, true);
}
/** @param array<string,mixed> $fields @param array<string,mixed> $event */
private function transition_with_policy(array $application, string $next, array $fields, array $event, bool $admin_override) {
$next = sanitize_key($next);
$dedupe = isset($event['dedupe_key']) ? (string) $event['dedupe_key'] : '';
if ($dedupe !== '') {
@@ -62,7 +72,7 @@ final class TransitionService {
}
}
$changed = $this->transition_locked($locked, $next, $fields, $event);
$changed = $this->apply_transition_locked($locked, $next, $fields, $event, $admin_override);
if (is_wp_error($changed)) {
$wpdb->query('ROLLBACK');
return $changed;
@@ -76,11 +86,6 @@ final class TransitionService {
return $updated;
}
/** @return array<int,string> */
public static function allowed_targets(string $status): array {
return self::ALLOWED[sanitize_key($status)] ?? [];
}
/**
* Apply an identity transition inside an already-owned database transaction.
* The caller must lock the application row before invoking this method.
@@ -91,14 +96,29 @@ final class TransitionService {
* @return true|\WP_Error
*/
public function transition_locked(array $application, string $next, array $fields = [], array $event = []) {
return $this->apply_transition_locked($application, $next, $fields, $event, false);
}
/**
* @param array<string,mixed> $application
* @param array<string,mixed> $fields
* @param array<string,mixed> $event
* @return true|\WP_Error
*/
private function apply_transition_locked(array $application, string $next, array $fields, array $event, bool $admin_override) {
$current = sanitize_key((string) ($application['status'] ?? ''));
$next = sanitize_key($next);
if (!in_array($next, self::ALLOWED[$current] ?? [], true)) {
$allowed = $admin_override
? in_array($next, ['manual_pending', 'verified', 'rejected', 'cancelled'], true) && $next !== $current
: in_array($next, self::ALLOWED[$current] ?? [], true);
if (!$allowed) {
return new \WP_Error('aksrt_identity_transition', __('该认证记录当前不可执行此操作', 'aksrt'), ['status' => 409]);
}
if (in_array($next, ['verified', 'rejected', 'cancelled'], true)) {
$fields['completed_at'] = current_time('mysql', true);
$fields['sensitive_blob'] = null;
} else {
$fields['completed_at'] = null;
}
if (!$this->applications->transition((int) $application['id'], $current, $next, $fields)) {
return new \WP_Error('aksrt_identity_conflict', __('认证状态已变化,请刷新后重试', 'aksrt'), ['status' => 409]);
+116 -54
View File
@@ -256,21 +256,21 @@ class AuthApply {
}
$status = sanitize_key($status);
$note = sanitize_textarea_field($note);
if (!in_array($status, ['verified', 'rejected', 'cancelled'], true)) {
if (!in_array($status, ['manual_pending', 'verified', 'rejected', 'cancelled'], true)) {
return new \WP_Error('aksrt_verify_status', __('审核状态无效', 'aksrt'));
}
if (in_array($status, ['rejected', 'cancelled'], true) && $note === '') {
return new \WP_Error('aksrt_verify_note', __('请填写处理原因', 'aksrt'));
}
$fields = [];
foreach (['public_name', 'public_desc'] as $key) {
foreach (['type', 'public_name', 'public_desc'] as $key) {
if (array_key_exists($key, $profile_fields)) {
$fields[$key] = (string) $profile_fields[$key];
}
}
$fields['review_note'] = $note;
$fields['reviewer_id'] = $reviewer_id;
$result = (new TransitionService($repo))->transition($application, $status, $fields, [
$result = (new TransitionService($repo))->admin_transition($application, $status, $fields, [
'source' => 'admin',
'event_type' => 'manual_review',
'actor_user_id' => $reviewer_id,
@@ -289,7 +289,15 @@ class AuthApply {
}
$status = self::get_status($user_id);
$labels = ['pending' => __('进行中', 'aksrt'), 'verified' => __('已认证', 'aksrt'), 'rejected' => __('未通过', 'aksrt'), 'cancelled' => __('已取消', 'aksrt')];
return $status === '' ? esc_html__('未认证', 'aksrt') : esc_html($labels[$status] ?? $status);
if ($status === '') {
return esc_html__('未认证', 'aksrt');
}
$label = (string) ($labels[$status] ?? $status);
if ($status === 'verified') {
$info = self::get_auth_info($user_id);
$label .= ' · ' . self::admin_type_label((string) ($info['type'] ?? 'personal'));
}
return esc_html($label);
}
public function render_admin_profile_status(\WP_User $user): void {
@@ -299,29 +307,52 @@ class AuthApply {
? $snapshot['verified']
: (is_array($snapshot['latest'] ?? null) ? $snapshot['latest'] : null);
$raw_status = (string) ($application['status'] ?? '');
$can_manage = $application !== null
&& current_user_can('aksrt_review_identity')
$can_manage = current_user_can('aksrt_review_identity')
&& current_user_can('edit_user', $user_id);
$allowed_actions = array_values(array_intersect(
TransitionService::allowed_targets($raw_status),
['verified', 'rejected', 'cancelled']
));
$public_name = (string) ($application['public_name'] ?? '');
$public_desc = (string) ($application['public_desc'] ?? '');
$identity_type = in_array((string) ($application['type'] ?? ''), ['personal', 'enterprise'], true)
? (string) $application['type']
: 'personal';
$selected_status = $raw_status === 'cancelled' ? '' : $raw_status;
$status_options = [
'' => __('未认证', 'aksrt'),
'manual_pending' => __('待审核', 'aksrt'),
'verified' => __('已认证', 'aksrt'),
'rejected' => __('已驳回', 'aksrt'),
];
if ($raw_status !== '' && $raw_status !== 'cancelled' && !isset($status_options[$raw_status])) {
$status_options = [$raw_status => self::admin_status_label($raw_status)] + $status_options;
}
?>
<h2><?php esc_html_e('身份认证(AKSRT)', 'aksrt'); ?></h2>
<table class="form-table" role="presentation">
<?php if ($can_manage) : ?>
<tr>
<th><?php esc_html_e('认证状态', 'aksrt'); ?></th>
<td><strong><?php echo esc_html($raw_status === '' ? __('未认证', 'aksrt') : self::admin_status_label($raw_status)); ?></strong></td>
<th><label for="aksrt_identity_status"><?php esc_html_e('认证状态', 'aksrt'); ?></label></th>
<td>
<select id="aksrt_identity_status" name="aksrt_identity_status">
<?php foreach ($status_options as $value => $label) : ?>
<option value="<?php echo esc_attr($value); ?>" <?php selected($selected_status, $value); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
<input type="hidden" name="aksrt_identity_application_uuid" value="<?php echo esc_attr((string) ($application['application_uuid'] ?? '')); ?>">
<?php wp_nonce_field('aksrt_identity_profile_' . $user_id, 'aksrt_identity_profile_nonce'); ?>
</td>
</tr>
<tr>
<th><label for="aksrt_identity_type"><?php esc_html_e('认证类型', 'aksrt'); ?></label></th>
<td>
<select id="aksrt_identity_type" name="aksrt_identity_type">
<option value="personal" <?php selected($identity_type, 'personal'); ?>><?php esc_html_e('个人认证', 'aksrt'); ?></option>
<option value="enterprise" <?php selected($identity_type, 'enterprise'); ?>><?php esc_html_e('企业认证', 'aksrt'); ?></option>
</select>
</td>
</tr>
<?php if ($application !== null && $can_manage) : ?>
<tr>
<th><label for="aksrt_identity_public_name"><?php esc_html_e('认证身份', 'aksrt'); ?></label></th>
<td>
<input type="text" class="regular-text" id="aksrt_identity_public_name" name="aksrt_identity_public_name" maxlength="64" value="<?php echo esc_attr($public_name); ?>">
<input type="hidden" name="aksrt_identity_application_uuid" value="<?php echo esc_attr((string) $application['application_uuid']); ?>">
<?php wp_nonce_field('aksrt_identity_profile_' . $user_id, 'aksrt_identity_profile_nonce'); ?>
</td>
</tr>
<tr>
@@ -330,23 +361,19 @@ class AuthApply {
<textarea class="large-text" id="aksrt_identity_public_desc" name="aksrt_identity_public_desc" rows="3" maxlength="300"><?php echo esc_textarea($public_desc); ?></textarea>
</td>
</tr>
<?php if ($allowed_actions !== []) : ?>
<tr>
<th><label for="aksrt_identity_action"><?php esc_html_e('更改认证状态', 'aksrt'); ?></label></th>
<th><label for="aksrt_identity_review_note"><?php esc_html_e('处理说明', 'aksrt'); ?></label></th>
<td>
<select id="aksrt_identity_action" name="aksrt_identity_action">
<option value=""><?php esc_html_e('保持当前状态', 'aksrt'); ?></option>
<?php foreach ($allowed_actions as $action) : ?>
<option value="<?php echo esc_attr($action); ?>"><?php echo esc_html(self::admin_action_label($action)); ?></option>
<?php endforeach; ?>
</select>
<p><label for="aksrt_identity_review_note"><?php esc_html_e('处理说明', 'aksrt'); ?></label></p>
<textarea class="large-text" id="aksrt_identity_review_note" name="aksrt_identity_review_note" rows="2"></textarea>
<p class="description"><?php esc_html_e('驳回或取消认证时必须填写处理说明。', 'aksrt'); ?></p>
<p class="description"><?php esc_html_e('改为“已驳回”或“未认证”时必须填写处理说明。', 'aksrt'); ?></p>
</td>
</tr>
<?php endif; ?>
<?php elseif ($application !== null) : ?>
<tr>
<th><?php esc_html_e('认证状态', 'aksrt'); ?></th>
<td><strong><?php echo esc_html(self::admin_status_label($raw_status)); ?></strong></td>
</tr>
<tr><th><?php esc_html_e('认证类型', 'aksrt'); ?></th><td><?php echo esc_html(self::admin_type_label($identity_type)); ?></td></tr>
<?php if ($public_name !== '') : ?><tr><th><?php esc_html_e('认证身份', 'aksrt'); ?></th><td><?php echo esc_html($public_name); ?></td></tr><?php endif; ?>
<?php if ($public_desc !== '') : ?><tr><th><?php esc_html_e('认证公开说明', 'aksrt'); ?></th><td><?php echo nl2br(esc_html($public_desc)); ?></td></tr><?php endif; ?>
<?php endif; ?>
@@ -372,51 +399,90 @@ class AuthApply {
$application = is_array($snapshot['verified'] ?? null)
? $snapshot['verified']
: (is_array($snapshot['latest'] ?? null) ? $snapshot['latest'] : null);
if ($application === null) {
return;
}
$application_uuid = sanitize_text_field((string) wp_unslash($_POST['aksrt_identity_application_uuid'] ?? ''));
if ($application_uuid === '' || !hash_equals((string) $application['application_uuid'], $application_uuid)) {
$current_uuid = (string) ($application['application_uuid'] ?? '');
if (!hash_equals($current_uuid, $application_uuid)) {
$this->set_admin_profile_notice('error', __('身份认证记录已变化,请刷新后重试。', 'aksrt'));
return;
}
$public_name = wp_html_excerpt(sanitize_text_field((string) wp_unslash($_POST['aksrt_identity_public_name'] ?? '')), 64, '');
$public_desc = wp_html_excerpt(sanitize_textarea_field((string) wp_unslash($_POST['aksrt_identity_public_desc'] ?? '')), 300, '');
$action = sanitize_key((string) wp_unslash($_POST['aksrt_identity_action'] ?? ''));
$identity_type = sanitize_key((string) wp_unslash($_POST['aksrt_identity_type'] ?? ''));
$status = sanitize_key((string) wp_unslash($_POST['aksrt_identity_status'] ?? ''));
$note = sanitize_textarea_field((string) wp_unslash($_POST['aksrt_identity_review_note'] ?? ''));
if (!in_array($identity_type, ['personal', 'enterprise'], true)) {
$this->set_admin_profile_notice('error', __('认证类型无效。', 'aksrt'));
return;
}
$fields = [
'type' => $identity_type,
'public_name' => $public_name,
'public_desc' => $public_desc,
];
if ($action === '') {
$current_status = (string) ($application['status'] ?? '');
if (!in_array($status, ['', 'manual_pending', 'verified', 'rejected'], true) && $status !== $current_status) {
$this->set_admin_profile_notice('error', __('认证状态无效。', 'aksrt'));
return;
}
$target_status = $status === '' ? 'cancelled' : $status;
if ($target_status === 'verified' && $public_name === '') {
$this->set_admin_profile_notice('error', __('设为已认证时请填写认证身份。', 'aksrt'));
return;
}
if (in_array($target_status, ['rejected', 'cancelled'], true) && $target_status !== $current_status && $note === '') {
$this->set_admin_profile_notice('error', __('改为已驳回或未认证时请填写处理说明。', 'aksrt'));
return;
}
if ($application === null && $status === '') {
return;
}
if ($application === null) {
$application = $this->create_admin_application($repo, $user_id, $fields);
if (is_wp_error($application)) {
$this->set_admin_profile_notice('error', $application->get_error_message());
return;
}
$application_uuid = (string) $application['application_uuid'];
$current_status = (string) $application['status'];
}
if ($target_status === $current_status) {
$updated = $repo->update((int) $application['id'], $fields);
$this->set_admin_profile_notice($updated ? 'success' : 'error', $updated ? __('身份认证资料已更新。', 'aksrt') : __('身份认证资料更新失败。', 'aksrt'));
return;
}
$allowed = TransitionService::allowed_targets((string) $application['status']);
if (!in_array($action, $allowed, true) || !in_array($action, ['verified', 'rejected', 'cancelled'], true)) {
$this->set_admin_profile_notice('error', __('当前认证状态不能执行该操作。', 'aksrt'));
return;
}
if ($action === 'verified' && $public_name === '') {
$this->set_admin_profile_notice('error', __('通过认证时请填写认证身份。', 'aksrt'));
return;
}
if (in_array($action, ['rejected', 'cancelled'], true) && $note === '') {
$this->set_admin_profile_notice('error', __('驳回或取消认证时请填写处理说明。', 'aksrt'));
return;
}
$result = self::review($user_id, $application_uuid, $action, $note, get_current_user_id(), $fields);
$result = self::review($user_id, $application_uuid, $target_status, $note, get_current_user_id(), $fields);
$this->set_admin_profile_notice(
is_wp_error($result) ? 'error' : 'success',
is_wp_error($result) ? $result->get_error_message() : __('身份认证已更新。', 'aksrt')
);
}
/** @param array<string,string> $fields */
private function create_admin_application(ApplicationRepository $repo, int $user_id, array $fields) {
$application_uuid = wp_generate_uuid4();
$client_request_id = wp_generate_uuid4();
return $repo->create([
'application_uuid' => $application_uuid,
'client_request_id' => $client_request_id,
'request_payload_hash' => hash('sha256', 'admin-profile|' . $user_id . '|' . $client_request_id),
'user_id' => $user_id,
'type' => $fields['type'],
'channel' => 'manual',
'provider' => '',
'status' => 'created',
'public_name' => $fields['public_name'],
'public_desc' => $fields['public_desc'],
'fee_amount' => '0.00',
'fee_status' => 'not_required',
'attempt_ordinal' => $repo->count_user_type($user_id, $fields['type']) + 1,
]);
}
public function render_admin_profile_notice(): void {
$key = 'aksrt_identity_profile_notice_' . get_current_user_id();
$notice = get_transient($key);
@@ -451,12 +517,8 @@ class AuthApply {
return $labels[$status] ?? $status;
}
private static function admin_action_label(string $action): string {
$labels = [
'verified' => __('通过认证', 'aksrt'),
'rejected' => __('驳回申请', 'aksrt'),
'cancelled' => __('取消认证', 'aksrt'),
];
return $labels[$action] ?? $action;
private static function admin_type_label(string $type): string {
return $type === 'enterprise' ? __('企业认证', 'aksrt') : __('个人认证', 'aksrt');
}
}
@@ -185,7 +185,7 @@ test('native user profile omits ledger tables and renders canonical images eager
assert.doesNotMatch(level, /Level::get_badge_html/);
});
test('identity details and governed status actions are editable on the native profile', () => {
test('identity status and public details are directly editable on the native profile', () => {
const auth = read('src/User/AuthApply.php');
const transitions = read('src/Identity/TransitionService.php');
@@ -193,10 +193,19 @@ test('identity details and governed status actions are editable on the native pr
assert.match(auth, /current_user_can\('aksrt_review_identity'\)/);
assert.match(auth, /wp_nonce_field\('aksrt_identity_profile_' \.[\s\S]*?'aksrt_identity_profile_nonce'\)/);
assert.match(auth, /name="aksrt_identity_application_uuid"/);
assert.match(auth, /name="aksrt_identity_type"/);
assert.match(auth, /value="personal"[\s\S]*?个人认证/);
assert.match(auth, /value="enterprise"[\s\S]*?企业认证/);
assert.match(auth, /name="aksrt_identity_public_name"/);
assert.match(auth, /name="aksrt_identity_public_desc"/);
assert.match(auth, /name="aksrt_identity_action"/);
assert.match(auth, /TransitionService::allowed_targets/);
assert.match(auth, /self::review\(\$user_id, \$application_uuid, \$action, \$note, get_current_user_id\(\), \$fields\)/);
assert.match(transitions, /public static function allowed_targets/);
assert.match(auth, /name="aksrt_identity_status"/);
assert.match(auth, /'manual_pending' => __\('待审核'/);
assert.match(auth, /'verified' => __\('已认证'/);
assert.match(auth, /'rejected' => __\('已驳回'/);
assert.match(auth, /create_admin_application\(\$repo, \$user_id, \$fields\)/);
assert.match(auth, /'type' => \$fields\['type'\]/);
assert.match(auth, /self::review\(\$user_id, \$application_uuid, \$target_status, \$note, get_current_user_id\(\), \$fields\)/);
assert.match(transitions, /public function admin_transition/);
assert.match(transitions, /\? in_array\(\$next, \['manual_pending', 'verified', 'rejected', 'cancelled'\], true\)/);
assert.match(transitions, /'verified' => \['cancelled'\]/);
});