Files
wordpress-theme-aksrt/inc/modules/user-parts/account-ajax.php
T
kate522 aaf458af1c refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
2026-07-17 13:29:22 +08:00

393 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* User profile/password/email AJAX handlers
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
// ============================================================
add_action('wp_ajax_aksrt_save_profile', function () {
if (!isset($_POST['_profile_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_profile_nonce'])), 'aksrt_profile_save')) {
wp_send_json_error(['message' => __('安全验证失败,请刷新页面重试', 'aksrt')]);
}
$user_id = isset($_POST['user_id']) ? (int) $_POST['user_id'] : 0;
$result = function_exists('aksrt_save_user_center_profile')
? aksrt_save_user_center_profile($user_id, $_POST)
: new WP_Error('aksrt_profile_unavailable', __('保存失败', 'aksrt'));
if (is_wp_error($result)) {
wp_send_json_error(['message' => $result->get_error_message()]);
}
wp_send_json_success(['message' => __('保存成功!', 'aksrt')]);
});
// ============================================================
// AJAX: 上传打赏收款码
// ============================================================
add_action('wp_ajax_aksrt_upload_reward_qr', function () {
if (!isset($_POST['_profile_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_profile_nonce'])), 'aksrt_profile_save')) {
wp_send_json_error(['message' => __('安全验证失败,请刷新页面重试', 'aksrt')]);
}
$user_id = get_current_user_id();
if ($user_id <= 0) {
wp_send_json_error(['message' => __('请先登录', 'aksrt')]);
}
if (function_exists('aksrt_rewards_enabled') && !aksrt_rewards_enabled()) {
wp_send_json_error(['message' => __('打赏功能未启用', 'aksrt')]);
}
$kind = isset($_POST['kind']) ? sanitize_key((string) $_POST['kind']) : '';
if (!in_array($kind, ['weixin', 'alipay'], true)) {
wp_send_json_error(['message' => __('无效类型', 'aksrt')]);
}
if (empty($_FILES['file']['name'])) {
wp_send_json_error(['message' => __('请选择图片', 'aksrt')]);
}
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
$file = $_FILES['file'];
$check = wp_check_filetype_and_ext($file['tmp_name'], $file['name']);
$allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$mime = (string) ($check['type'] ?? $file['type'] ?? '');
if ($mime === '' || !in_array($mime, $allowed, true)) {
wp_send_json_error(['message' => __('仅支持 JPG/PNG/GIF/WebP 图片', 'aksrt')]);
}
if (!empty($file['size']) && (int) $file['size'] > 2 * 1024 * 1024) {
wp_send_json_error(['message' => __('图片不能超过 2MB', 'aksrt')]);
}
$attachment_id = media_handle_upload('file', 0);
if (is_wp_error($attachment_id)) {
wp_send_json_error(['message' => $attachment_id->get_error_message()]);
}
$meta = $kind === 'weixin' ? 'aksrt_rewards_weixin' : 'aksrt_rewards_alipay';
update_user_meta($user_id, $meta, (int) $attachment_id);
$url = wp_get_attachment_image_url((int) $attachment_id, 'full');
wp_send_json_success([
'message' => __('上传成功', 'aksrt'),
'id' => (int) $attachment_id,
'url' => is_string($url) ? $url : '',
'kind' => $kind,
]);
});
// ============================================================
// AJAX: 发送邮箱验证码(修改密码/更改邮箱通用)
// ============================================================
if (!function_exists('aksrt_get_email_code_key')) :
function aksrt_get_email_code_key(int $user_id, string $scene): string {
return 'aksrt_email_code_' . $scene . '_' . $user_id;
}
endif;
if (!function_exists('aksrt_get_email_code_rate_key')) :
function aksrt_get_email_code_rate_key(int $user_id, string $scene, string $target = ''): string {
$suffix = $target !== '' ? '_' . md5(strtolower($target)) : '';
return 'aksrt_email_code_rate_' . $scene . '_' . $user_id . $suffix;
}
endif;
if (!function_exists('aksrt_store_email_code')) :
function aksrt_store_email_code(int $user_id, string $scene, string $code, string $target = ''): void {
set_transient(
aksrt_get_email_code_key($user_id, $scene),
[
'hash' => wp_hash_password($code),
'target' => $target,
'issued_at' => time(),
],
5 * MINUTE_IN_SECONDS
);
}
endif;
if (!function_exists('aksrt_consume_email_code')) :
function aksrt_consume_email_code(int $user_id, string $scene, string $code, string $target = ''): bool {
$key = aksrt_get_email_code_key($user_id, $scene);
$stored = get_transient($key);
if (!is_array($stored) || empty($stored['hash']) || !wp_check_password($code, $stored['hash'])) {
return false;
}
if ($target !== '' && strtolower((string) ($stored['target'] ?? '')) !== strtolower($target)) {
return false;
}
delete_transient($key);
return true;
}
endif;
add_action('wp_ajax_aksrt_send_email_code', function () {
if ( ! isset( $_POST['_settings_nonce'] ) || ! wp_verify_nonce( $_POST['_settings_nonce'], 'aksrt_account_settings' ) ) {
wp_send_json_error( array( 'message' => __( '安全验证失败,请刷新页面重试', 'aksrt' ) ) );
}
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( '请先登录', 'aksrt' ) ) );
}
$scene = isset( $_POST['scene'] ) ? sanitize_key( (string) $_POST['scene'] ) : '';
if ( ! in_array( $scene, array( 'change_password', 'change_email' ), true ) ) {
wp_send_json_error( array( 'message' => __( '无效的验证场景', 'aksrt' ) ) );
}
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( $scene, wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
}
$current_user = wp_get_current_user();
$target_email = '';
$success_message = '';
if ( $scene === 'change_password' ) {
$target_email = $current_user->user_email;
if ( empty( $target_email ) || ! is_email( $target_email ) ) {
wp_send_json_error( array( 'message' => __( '当前账号未绑定有效邮箱', 'aksrt' ) ) );
}
$success_message = __( '验证码已发送到您的注册邮箱', 'aksrt' );
} else {
$new_email = isset( $_POST['new_email'] ) ? trim( wp_unslash( $_POST['new_email'] ) ) : '';
$current_password = isset( $_POST['current_password'] ) ? trim( wp_unslash( $_POST['current_password'] ) ) : '';
if ( empty( $current_password ) || ! wp_check_password( $current_password, $current_user->user_pass, $current_user->ID ) ) {
wp_send_json_error( array( 'message' => __( '当前密码不正确', 'aksrt' ) ) );
}
if ( empty( $new_email ) || ! is_email( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '请输入有效的新邮箱地址', 'aksrt' ) ) );
}
if ( strtolower( $new_email ) === strtolower( $current_user->user_email ) ) {
wp_send_json_error( array( 'message' => __( '新邮箱与当前邮箱相同', 'aksrt' ) ) );
}
if ( email_exists( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '该邮箱已被其他账号使用', 'aksrt' ) ) );
}
$target_email = $new_email;
$success_message = __( '验证码已发送到新邮箱,请完成验证后再提交', 'aksrt' );
}
// 频率限制:60秒内不能重复发送
$rate_key = aksrt_get_email_code_rate_key( $current_user->ID, $scene, $target_email );
if ( get_transient( $rate_key ) ) {
wp_send_json_error( array( 'message' => __( '操作过于频繁,请稍后再试', 'aksrt' ) ) );
}
set_transient( $rate_key, true, 60 );
// 生成验证码并同步发送,只有真正发信成功后才写入验证码缓存。
$code = str_pad( (string) random_int( 0, 999999 ), 6, '0', STR_PAD_LEFT );
$site_name = get_bloginfo( 'name' );
$default_subject = '[' . $site_name . '] ' . __( '邮箱验证码', 'aksrt' );
$default_content = sprintf(
__( "您好 %s,\n\n您正在进行敏感操作验证,验证码为:\n\n【%s】\n\n验证码有效期为 5 分钟。如非本人操作,请忽略此邮件。", 'aksrt' ),
$current_user->display_name,
$code
);
$sent = class_exists( '\AKSRT\Settings\Settings' )
? \AKSRT\Settings\Settings::send_event_email(
'change_code', $target_email, $default_subject, $default_content,
[ '{username}' => $current_user->display_name, '{site_name}' => $site_name, '{code}' => $code ]
)
: wp_mail( $target_email, $default_subject, $default_content, [ 'Content-Type: text/plain; charset=UTF-8' ] );
if ( ! $sent ) {
delete_transient( $rate_key );
error_log( '[AKSRT] 验证码邮件发送失败 user_id=' . $current_user->ID . ' email=' . $target_email . ' scene=' . $scene );
wp_send_json_error( array( 'message' => __( '验证码邮件发送失败,请稍后重试', 'aksrt' ) ) );
}
aksrt_store_email_code( $current_user->ID, $scene, $code, $target_email );
wp_send_json_success( array( 'message' => $success_message ) );
});
// ============================================================
// AJAX: 修改密码
// ============================================================
add_action('wp_ajax_aksrt_change_password', function () {
if ( ! isset( $_POST['_settings_nonce'] ) || ! wp_verify_nonce( $_POST['_settings_nonce'], 'aksrt_account_settings' ) ) {
wp_send_json_error( array( 'message' => __( '安全验证失败,请刷新页面重试', 'aksrt' ) ) );
}
$user_id = get_current_user_id();
if ( ! $user_id ) {
wp_send_json_error( array( 'message' => __( '请先登录', 'aksrt' ) ) );
}
$old_password = isset( $_POST['old_password'] ) ? trim( wp_unslash( $_POST['old_password'] ) ) : '';
$new_password = isset( $_POST['new_password'] ) ? trim( wp_unslash( $_POST['new_password'] ) ) : '';
$new_password_confirm = isset( $_POST['new_password_confirm'] ) ? trim( wp_unslash( $_POST['new_password_confirm'] ) ) : '';
$email_code = isset( $_POST['email_code'] ) ? sanitize_text_field( $_POST['email_code'] ) : '';
// 验证原密码
$user = get_userdata( $user_id );
if ( ! $user || ! wp_check_password( $old_password, $user->data->user_pass, $user_id ) ) {
wp_send_json_error( array( 'message' => __( '原密码不正确', 'aksrt' ) ) );
}
// 验证新密码强度
if ( mb_strlen( $new_password ) < 6 ) {
wp_send_json_error( array( 'message' => __( '新密码长度至少6位', 'aksrt' ) ) );
}
if ( mb_strlen( $new_password ) > 128 ) {
wp_send_json_error( array( 'message' => __( '新密码过长', 'aksrt' ) ) );
}
if ( $new_password !== $new_password_confirm ) {
wp_send_json_error( array( 'message' => __( '两次输入的新密码不一致', 'aksrt' ) ) );
}
// 人机验证
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( 'change_password', wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
}
// 验证邮箱验证码
if ( ! aksrt_consume_email_code( $user_id, 'change_password', $email_code, $user->user_email ) ) {
wp_send_json_error( array( 'message' => __( '邮箱验证码错误或已过期', 'aksrt' ) ) );
}
// 设置新密码
wp_set_password( $new_password, $user_id );
if ( class_exists( 'WP_Session_Tokens' ) ) {
WP_Session_Tokens::get_instance( $user_id )->destroy_all();
}
// 清除登录态
wp_clear_auth_cookie();
wp_send_json_success( array(
'message' => __( '密码修改成功,即将跳转到登录页…', 'aksrt' ),
'redirect_to' => aksrt_get_login_url(),
));
});
// ============================================================
// AJAX: 更改邮箱
// ============================================================
add_action('wp_ajax_aksrt_change_email', function () {
if ( ! isset( $_POST['_settings_nonce'] ) || ! wp_verify_nonce( $_POST['_settings_nonce'], 'aksrt_account_settings' ) ) {
wp_send_json_error( array( 'message' => __( '安全验证失败,请刷新页面重试', 'aksrt' ) ) );
}
$user_id = get_current_user_id();
if ( ! $user_id ) {
wp_send_json_error( array( 'message' => __( '请先登录', 'aksrt' ) ) );
}
$current_password = isset( $_POST['current_password'] ) ? trim( wp_unslash( $_POST['current_password'] ) ) : '';
$new_email = isset( $_POST['new_email'] ) ? trim( wp_unslash( $_POST['new_email'] ) ) : '';
$email_code = isset( $_POST['email_code'] ) ? sanitize_text_field( $_POST['email_code'] ) : '';
$current_user = wp_get_current_user();
if ( empty( $current_password ) || ! wp_check_password( $current_password, $current_user->user_pass, $current_user->ID ) ) {
wp_send_json_error( array( 'message' => __( '当前密码不正确', 'aksrt' ) ) );
}
// 验证新邮箱格式
if ( empty( $new_email ) || ! is_email( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '请输入有效的邮箱地址', 'aksrt' ) ) );
}
if ( strtolower( $new_email ) === strtolower( $current_user->user_email ) ) {
wp_send_json_error( array( 'message' => __( '新邮箱与当前邮箱相同', 'aksrt' ) ) );
}
// 检查邮箱是否已被使用
if ( email_exists( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '该邮箱已被其他账号使用', 'aksrt' ) ) );
}
// 人机验证
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( 'change_email', wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
}
// 验证邮箱验证码
if ( ! aksrt_consume_email_code( $user_id, 'change_email', $email_code, $new_email ) ) {
wp_send_json_error( array( 'message' => __( '邮箱验证码错误或已过期', 'aksrt' ) ) );
}
// 更新邮箱
$old_email = $current_user->user_email;
$update_result = wp_update_user( [
'ID' => $user_id,
'user_email' => $new_email,
] );
if ( is_wp_error( $update_result ) ) {
wp_send_json_error( array( 'message' => $update_result->get_error_message() ?: __( '邮箱更新失败,请稍后重试', 'aksrt' ) ) );
}
if ( is_email( $old_email ) ) {
$site_name = get_bloginfo( 'name' );
$default_subject = '[' . $site_name . '] ' . __( '账号邮箱已变更', 'aksrt' );
$default_content = sprintf(
__( "您好 %s,\n\n您的账号邮箱已成功修改为:%s\n\n如果这不是您的操作,请立即联系站点管理员。", 'aksrt' ),
$current_user->display_name,
$new_email
);
if ( class_exists( '\AKSRT\Settings\Settings' ) ) {
\AKSRT\Settings\Settings::send_event_email(
'change_notice', $old_email, $default_subject, $default_content,
[ '{username}' => $current_user->display_name, '{site_name}' => $site_name, '{new_email}' => $new_email ]
);
} else {
wp_mail( $old_email, $default_subject, $default_content, [ 'Content-Type: text/plain; charset=UTF-8' ] );
}
}
// ====== 邮箱变更后重置验证状态(阶段 5)======
// 1. 写入 14 天回滚窗口 meta(管理员可在此期内还原旧邮箱)
// 2. 把 aksrt_email_verified 重置为 0
// 3. 若功能启用 → 立即给新邮箱发送验证邮件
update_user_meta( $user_id, 'aksrt_email_previous', $old_email );
update_user_meta( $user_id, 'aksrt_email_changed_at', time() );
$verify_url = '';
$verify_message = '';
if ( class_exists( '\AKSRT\Settings\Settings' ) && class_exists( '\AKSRT\Auth\Service' )
&& \AKSRT\Settings\Settings::registration_requires_email_verification() ) {
update_user_meta( $user_id, 'aksrt_email_verified', 0 );
// 用户对自身的 edit_user 权限映射到 'read',因此 admin_send_verification 会通过权限校验。
$verify_result = \AKSRT\Auth\Service::admin_send_verification( $user_id, $user_id );
if ( ! empty( $verify_result['success'] ) ) {
$verify_url = function_exists('aksrt_get_verify_email_url') ? aksrt_get_verify_email_url( $user_id ) : '';
$verify_message = __( '请前往新邮箱完成验证,否则下次登录将被拦截。', 'aksrt' );
}
}
if ( class_exists( 'WP_Session_Tokens' ) ) {
WP_Session_Tokens::get_instance( $user_id )->destroy_all();
}
// 清除登录态
wp_clear_auth_cookie();
wp_send_json_success( array(
'message' => $verify_message !== ''
? __( '邮箱修改成功,请前往新邮箱完成验证后重新登录…', 'aksrt' )
: __( '邮箱修改成功,即将跳转到登录页…', 'aksrt' ),
'redirect_to' => $verify_url !== '' ? $verify_url : aksrt_get_login_url(),
'verify_url' => $verify_url,
));
});