logger->info('Wechat Integration: Attempting to get openid', ['code' => $code]); // 获取微信session_key和openid $sessionData = $this->wechatClient->getSessionKey($code); if (!isset($sessionData['openid'])) { $this->logger->error('Wechat Integration: Failed to get openid'); throw new LocalizedException(__('获取微信openid失败')); } $this->logger->info('Wechat Integration: Openid obtained successfully', ['openid' => $sessionData['openid']]); $result = [ 'openid' => $sessionData['openid'] ]; } catch (\Exception $e) { $this->logger->error('Wechat Integration: Exception when getting openid', ['exception' => $e->getMessage()]); $result = [ 'error' => "获取openid失败,请稍后重试", ]; } return WebapiResponse::json($result); } /** * {@inheritdoc} */ public function loginByMobile($code, $openid) { try { $this->logger->info('Wechat Integration: Attempting to create user by WeChat', ['openid' => $openid]); // 1. 使用微信API获取手机号 $phoneNumber = $this->wechatClient->getPhoneNumberByApi($code); $this->logger->info('Wechat Integration: Phone number obtained', ['phone_number_masked' => substr($phoneNumber, 0, 3) . '****' . substr($phoneNumber, -4)]); // 2. 检查手机号是否已存在 $existingCustomer = $this->getCustomerByMobile($phoneNumber); if ($existingCustomer) { // 如果用户已存在,更新其openid $this->logger->info('Wechat Integration: Customer already exists, updating openid', ['customer_id' => $existingCustomer->getId()]); $existingCustomer->setCustomAttribute('wechat_openid', $openid); $customer = $this->customerRepository->save($existingCustomer); } else { // 3. 创建新用户 $this->logger->info('Wechat Integration: Creating new customer', ['phone_number_masked' => substr($phoneNumber, 0, 3) . '****' . substr($phoneNumber, -4)]); $customer = $this->customerDataFactory->create(); $customer->setWebsiteId(1); // 默认网站ID $customer->setStoreId(1); // 默认店铺ID $customer->setFirstname('wx_' . $phoneNumber); $customer->setLastname(''); // 生成临时邮箱 $tempEmail = $this->generateTempEmail($phoneNumber); $customer->setEmail($tempEmail); // 设置手机号 $customer->setCustomAttribute('mobile', $phoneNumber); // 设置微信openid $this->logger->info('Wechat Integration: Setting wechat_openid for new customer', ['openid' => $openid]); $customer->setCustomAttribute('wechat_openid', $openid); // 设置随机密码 $password = $this->random->getRandomString(10); // 保存用户 $customer = $this->customerRepository->save($customer); // 使用CustomerRepository的setPasswordHash方法设置密码 $customerSecure = $this->customerRegistry->retrieveSecureData($customer->getId()); $hashedPassword = $this->encryptor->getHash($password, true); $customerSecure->setPasswordHash($hashedPassword); } $result = $this->generateCustomerToken($customer); $this->logger->info('Wechat Integration: Login successful, token generated', ['customer_id' => $customer->getId(), 'token' => $result]); } catch (\Exception $e) { $this->logger->error('Wechat Integration: Exception creating user by WeChat', ['exception' => $e->getMessage()]); $result = [ 'error' => $e->getMessage(), ]; } return WebapiResponse::json($result); } /** * {@inheritdoc} */ public function updateCustomerInfo($name, $email, $mobile, $company, $wechatAvatar = null) { try { // 1. 加载用户 $customerId = $this->userContext->getUserId(); $customer = $this->customerRepository->getById($customerId); // 2. 更新用户信息 if (!empty($name)) { $customer->setFirstname($name); } if (!empty($email)) { // 检查邮箱是否已被使用 try { $existingCustomer = $this->customerRepository->get($email); $existingCustomerId = $existingCustomer->getId(); if (!empty($existingCustomerId) && $existingCustomerId != $customerId) { throw new LocalizedException(__('该邮箱已被使用')); } } catch (\Exception $e) { // 邮箱未被使用,可以设置 } $customer->setEmail($email); } if (!empty($mobile)) { // 检查手机号是否已被使用 // 手机号不可修改,此逻辑可根据需求调整 /** $existingCustomer = $this->getCustomerByMobile($mobile); if ($existingCustomer && $existingCustomer->getId() != $customerId) { throw new LocalizedException(__('该手机号已被使用')); } $customer->setCustomAttribute('mobile', $mobile);**/ } if (!empty($company)) { // 设置公司属性 $customer->setCustomAttribute('company', $company); } // 处理微信头像 $this->processWechatAvatar($customer, $wechatAvatar, $this->getCustomerAttribute($customer, 'wechat_openid')); // 3. 保存更新 $customer = $this->customerRepository->save($customer); // 使用 Magento2 时区处理类格式化更新时间 $formattedUpdatedAt = $this->timezone->formatDateTime( $customer->getUpdatedAt(), \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM ); $result = [ 'customer_id' => $customer->getId(), 'customer_updated' => $formattedUpdatedAt, ]; } catch (\Exception $e) { $result = ['error' => "更新用户信息失败,请稍后重试, " . $e->getMessage()]; } return WebapiResponse::json($result); } /** * {@inheritdoc} */ public function wechatLogin($code) { try { $this->logger->info('Wechat Integration: WeChat login attempt', ['code' => $code]); // 1. 获取微信session_key和openid $sessionData = $this->wechatClient->getSessionKey($code); if (!isset($sessionData['openid'])) { $this->logger->error('Wechat Integration: Failed to get openid during login'); throw new LocalizedException(__('获取微信openid失败')); } $openid = $sessionData['openid']; $this->logger->info('Wechat Integration: Openid obtained for login', ['openid' => $openid]); // 2. 检查是否有绑定该openid的用户 $customer = $this->getCustomerByWechatOpenid($openid); if ($customer) { $this->logger->info('Wechat Integration: Customer found with openid', ['customer_id' => $customer->getId()]); // 已存在用户,生成access token $result = $this->generateCustomerToken($customer); $this->logger->info('Wechat Integration: Login successful, token generated', ['customer_id' => $customer->getId(), 'token' => $result]); } else { // 3. 新用户,返回需要绑定手机号的提示 $this->logger->info('Wechat Integration: New user detected, needs phone binding', ['openid' => $openid]); $result = [ 'error' => '请先绑定手机号', ]; } } catch (\Exception $e) { $this->logger->error('Wechat Integration: Exception during WeChat login', ['exception' => $e->getMessage()]); $result = [ 'error' => $e->getMessage(), ]; } return WebapiResponse::json($result); } /** * 根据手机号获取用户 * * @param string $mobile * @return CustomerInterface|null */ protected function getCustomerByMobile($mobile) { try { $this->logger->info('Wechat Integration: Attempting to find customer by mobile', ['mobile' => $mobile]); // 使用集合方式查找用户 $customerCollection = $this->customerCollectionFactory->create(); $customerCollection->addAttributeToSelect('*') ->addAttributeToFilter('mobile', $mobile) ->addAttributeToFilter('website_id', 1) ->setPageSize(1); $customer = $customerCollection->getFirstItem(); if ($customer->getId()) { $this->logger->info('Wechat Integration: Customer found by mobile', ['customer_id' => $customer->getId(), 'mobile' => $mobile]); return $this->customerRepository->getById($customer->getId()); } else { $this->logger->info('Wechat Integration: No customer found with mobile', ['mobile' => $mobile]); } } catch (\Exception $e) { $this->logger->error('Wechat Integration: Error finding customer by mobile', ['mobile' => $mobile, 'exception' => $e->getMessage()]); } return null; } /** * 根据微信openid获取用户 * * @param string $openid * @return CustomerInterface|null */ protected function getCustomerByWechatOpenid($openid) { try { $this->logger->info('Wechat Integration: Attempting to find customer by openid', ['openid' => $openid]); // 使用集合方式查找用户 $customerCollection = $this->customerCollectionFactory->create(); $customerCollection->addAttributeToSelect('*') ->addAttributeToFilter('wechat_openid', $openid) ->addAttributeToFilter('website_id', 1) ->setPageSize(1); $customer = $customerCollection->getFirstItem(); if ($customer->getId()) { $this->logger->info('Wechat Integration: Customer found by openid', ['customer_id' => $customer->getId(), 'openid' => $openid]); return $this->customerRepository->getById($customer->getId()); } else { $this->logger->info('Wechat Integration: No customer found with openid', ['openid' => $openid]); } } catch (\Exception $e) { $this->logger->error('Wechat Integration: Error finding customer by openid', ['openid' => $openid, 'exception' => $e->getMessage()]); } return null; } /** * 生成临时邮箱 * * @param string $phoneNumber * @return string */ protected function generateTempEmail($phoneNumber) { $randomString = $this->random->getRandomString(6); return 'wx_' . $phoneNumber . '_' . $randomString . '@stemcell.cn'; } /** * 生成用户访问令牌 * * @param CustomerInterface $customer * @return array * @throws \Exception */ protected function generateCustomerToken(CustomerInterface $customer) { try { $this->logger->info('Wechat Integration: Attempting to generate token for customer', ['customer_id' => $customer->getId()]); // 获取客户实体以访问密码哈希 $customerSecure = $this->customerRegistry->retrieveSecureData($customer->getId()); $originalPasswordHash = $customerSecure->getPasswordHash(); // 生成临时密码 $tempPassword = $this->random->getRandomString(20); try { // 设置临时密码 $customerSecure->setPasswordHash($this->encryptor->getHash($tempPassword, true)); // 使用临时密码生成令牌 $token = $this->customerTokenService->createCustomerAccessToken( $customer->getEmail(), $tempPassword ); $this->logger->info('Wechat Integration: Token generated successfully', ['customer_id' => $customer->getId()]); $expiresIn = $this->scopeConfig->getValue(self::CUSTOMER_EXPIRATION_CONFIG_PATH, \Magento\Store\Model\ScopeInterface::SCOPE_STORE); return [ 'token_type' => 'Bearer', 'token' => $token, 'expires_in' => (int)$expiresIn * 60 ]; } catch (\Exception $e) { $this->logger->error('Wechat Integration: Error generating token', [ 'customer_id' => $customer->getId(), 'error' => $e->getMessage() ]); throw $e; } finally { // 恢复原始密码哈希,确保用户密码不变 $customerSecure->setPasswordHash($originalPasswordHash); $this->logger->info('Wechat Integration: Restored original password hash', ['customer_id' => $customer->getId()]); } } catch (\Exception $e) { $this->logger->error('Wechat Integration: Failed to generate customer token', ['error' => $e->getMessage()]); throw new \Exception('Failed to generate customer token: ' . $e->getMessage(), $e->getCode(), $e); } } /** * 用户登出 * * @return bool * @throws Exception */ public function logout() { try { $this->logger->info('Wechat Integration: User logout attempt'); // 获取当前登录的用户信息 $customerId = $this->getCurrentCustomerId(); $this->logger->info('Wechat Integration: User logout processing', ['customer_id' => $customerId]); // 记录用户登出行为到数据库 $this->customerTokenService->revokeCustomerAccessToken($customerId); $this->logger->info('Wechat Integration: User logout successful', ['customer_id' => $customerId]); $result = ['logout' => 'success']; } catch (\Exception $e) { $this->logger->error('Wechat Integration: Exception during user logout', ['exception' => $e->getMessage()]); $result = ['error' => 'Logout failed: ' . $e->getMessage()]; } return WebapiResponse::json($result); } /** * {@inheritdoc} */ public function getCustomerInfo() { try { $this->logger->info('Wechat Integration: Getting customer info'); // 获取当前登录的用户ID $customerId = $this->userContext->getUserId(); $this->logger->info('Wechat Integration: Processing customer info request', ['customer_id' => $customerId]); // 获取用户信息 $customer = $this->customerRepository->getById($customerId); // 构建返回数据 $mobile = $this->getCustomerAttribute($customer, 'mobile'); $result = [ 'name' => str_contains($customer->getFirstname(), 'wx_' . $mobile) ? '' : $customer->getFirstname(), 'email' => str_contains($customer->getEmail(), 'wx_' . $mobile) ? '' : $customer->getEmail(), 'company' => $this->getCustomerAttribute($customer, 'company'), 'mobile' => $mobile, 'wechatAvatar' => $this->getAbsoluteAvatarUrl($this->getCustomerAttribute($customer, 'wechat_avatar')) ]; $this->logger->info('Wechat Integration: Customer info retrieved successfully', ['customer_id' => $customerId]); } catch (\Exception $e) { $this->logger->error('Wechat Integration: Exception getting customer info', ['exception' => $e->getMessage()]); $result = ['error' => 'Failed to get customer info: ' . $e->getMessage()]; } return WebapiResponse::json($result); } /** * 获取用户自定义属性值 * * @param CustomerInterface $customer * @param string $attributeCode * @return string|null */ protected function getCustomerAttribute(CustomerInterface $customer, string $attributeCode) { try { return $customer->getCustomAttribute($attributeCode)?->getValue(); } catch (\Exception $e) { $this->logger->error('Wechat Integration: Error getting customer attribute', ['attribute' => $attributeCode, 'exception' => $e->getMessage()]); return null; } } /** * 获取头像的绝对URL * * @param string|null $relativePath 相对路径 * @return string|null 返回绝对URL,如果输入为空则返回null */ private function getAbsoluteAvatarUrl(?string $relativePath): ?string { if (empty($relativePath)) { return null; } try { // 获取媒体目录的绝对URL $mediaUrl = $this->urlInterface->getBaseUrl(['_type' => UrlInterface::URL_TYPE_MEDIA]); // 拼接完整的绝对URL $absoluteUrl = rtrim($mediaUrl, '/') . '/' . ltrim($relativePath, '/'); $this->logger->info('Wechat Integration: Generated absolute avatar URL', [ 'relative_path' => $relativePath, 'absolute_url' => $absoluteUrl ]); return $absoluteUrl . '?r=' . time(); } catch (\Exception $e) { $this->logger->error('Wechat Integration: Error generating absolute avatar URL', [ 'relative_path' => $relativePath, 'exception' => $e->getMessage() ]); return null; } } /** * 处理微信头像 * * @param CustomerInterface $customer 用户对象 * @param string|null $wechatAvatar 微信头像URL或base64数据 * @param string $openid 用户openid * @return void */ private function processWechatAvatar(CustomerInterface $customer, ?string $wechatAvatar, string $openid): void { // 如果提供了有效的头像数据,则更新用户头像 if (!empty($wechatAvatar)) { $this->logger->info('Wechat Integration: Processing wechat avatar', [ 'customer_id' => $customer->getId(), 'data_type' => $this->isBase64Data($wechatAvatar) ? 'base64' : 'url' ]); // 判断是URL还是base64数据 if ($this->isBase64Data($wechatAvatar)) { // 处理base64数据 $localAvatarUrl = $this->wechatAvatarService->saveBase64Avatar($wechatAvatar, $openid); } else { // 处理URL $localAvatarUrl = $this->wechatAvatarService->downloadAndSaveAvatar($wechatAvatar, $openid); } if ($localAvatarUrl) { $customer->setCustomAttribute('wechat_avatar', $localAvatarUrl); $this->logger->info('Wechat Integration: Wechat avatar saved', [ 'customer_id' => $customer->getId(), 'local_url' => $localAvatarUrl ]); } else { $this->logger->warning('Wechat Integration: Failed to save wechat avatar', [ 'customer_id' => $customer->getId(), 'data_type' => $this->isBase64Data($wechatAvatar) ? 'base64' : 'url' ]); } } } /** * 判断是否为base64数据 * * @param string $data * @return bool */ private function isBase64Data(string $data): bool { // 检查是否包含data:前缀(数据URL格式) if (strpos($data, 'data:') === 0) { return true; } // 检查是否为有效的base64编码 // base64编码通常只包含字母、数字、+、/、=字符 $base64Pattern = '/^[A-Za-z0-9+\/]*={0,2}$/'; if (preg_match($base64Pattern, $data)) { // 进一步验证解码是否成功 $decoded = base64_decode($data, true); return $decoded !== false; } return false; } /** * 获取当前登录用户的ID * * @return int * @throws LocalizedException */ protected function getCurrentCustomerId() { try { $this->logger->info('Wechat Integration: Getting current customer ID'); $customerId = $this->userContext->getUserId(); $this->logger->info('Wechat Integration: Current customer ID retrieved', ['customer_id' => $customerId]); return $customerId; } catch (\Exception $e) { $this->logger->error('Wechat Integration: Exception getting current customer ID', ['exception' => $e->getMessage()]); throw new LocalizedException(__('获取当前用户ID失败')); } } }