Files
huangjingfen/pro_v3.5.1_副本/vendor/symfony/http-client/Response/StreamWrapper.php
apple 434aa8c69d feat(fsgx): 完成全部24项开发任务 Phase1-7
Phase1 后端核心:
- 新增 fsgx_v1.sql 迁移脚本(is_queue_goods/frozen_points/available_points/no_assess)
- SystemConfigServices 返佣设置扩展(周期人数/分档比例/范围/时机)
- StoreOrderCreateServices 周期循环佣金计算
- StoreOrderTakeServices 佣金发放后同步冻结积分
- StoreProductServices/StoreProduct 保存 is_queue_goods

Phase2 后端接口:
- GET /api/hjf/brokerage/progress 佣金周期进度
- GET /api/hjf/assets/overview 资产总览
- HjfPointsServices 每日 frozen_points 0.4‰ 释放定时任务
- PUT /adminapi/hjf/member/{uid}/no_assess 不考核接口
- GET /adminapi/hjf/points/release_log 积分日志接口

Phase3 前端清理:
- hjfCustom.js 路由精简(仅保留 points/log)
- hjfQueue.js/hjfMember.js API 清理/重定向至 CRMEB 原生接口
- pages.json 公排→推荐佣金/佣金记录/佣金规则

Phase4-5 前端改造:
- queue/status.vue 推荐佣金进度页整体重写
- 商品详情/订单确认/支付结果页文案与逻辑改造
- 个人中心/资产页/引导页/规则页文案改造
- HjfQueueProgress/HjfRefundNotice/HjfAssetCard 组件改造
- 推广中心嵌入佣金进度摘要
- hjfMockData.js 全量更新(公排字段→佣金字段)

Phase6 Admin 增强:
- 用户列表新增 frozen_points/available_points 列及不考核操作按钮
- hjfPoints.js USE_MOCK=false 对接真实积分日志接口

Phase7 配置文档:
- docs/fsgx-phase7-config-checklist.md 后台配置与全链路验收清单

Made-with: Cursor
2026-03-23 22:32:19 +08:00

312 lines
9.1 KiB
PHP

<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpClient\Response;
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
/**
* Allows turning ResponseInterface instances to PHP streams.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class StreamWrapper
{
/** @var resource|null */
public $context;
private $client;
private $response;
/** @var resource|string|null */
private $content;
/** @var resource|null */
private $handle;
private bool $blocking = true;
private ?float $timeout = null;
private bool $eof = false;
private ?int $offset = 0;
/**
* Creates a PHP stream resource from a ResponseInterface.
*
* @return resource
*/
public static function createResource(ResponseInterface $response, HttpClientInterface $client = null)
{
if ($response instanceof StreamableInterface) {
$stack = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 2);
if ($response !== ($stack[1]['object'] ?? null)) {
return $response->toStream(false);
}
}
if (null === $client && !method_exists($response, 'stream')) {
throw new \InvalidArgumentException(sprintf('Providing a client to "%s()" is required when the response doesn\'t have any "stream()" method.', __CLASS__));
}
static $registered = false;
if (!$registered = $registered || stream_wrapper_register(strtr(__CLASS__, '\\', '-'), __CLASS__)) {
throw new \RuntimeException(error_get_last()['message'] ?? 'Registering the "symfony" stream wrapper failed.');
}
$context = [
'client' => $client ?? $response,
'response' => $response,
];
return fopen(strtr(__CLASS__, '\\', '-').'://'.$response->getInfo('url'), 'r', false, stream_context_create(['symfony' => $context]));
}
public function getResponse(): ResponseInterface
{
return $this->response;
}
/**
* @param resource|callable|null $handle The resource handle that should be monitored when
* stream_select() is used on the created stream
* @param resource|null $content The seekable resource where the response body is buffered
*/
public function bindHandles(&$handle, &$content): void
{
$this->handle = &$handle;
$this->content = &$content;
$this->offset = null;
}
public function stream_open(string $path, string $mode, int $options): bool
{
if ('r' !== $mode) {
if ($options & \STREAM_REPORT_ERRORS) {
trigger_error(sprintf('Invalid mode "%s": only "r" is supported.', $mode), \E_USER_WARNING);
}
return false;
}
$context = stream_context_get_options($this->context)['symfony'] ?? null;
$this->client = $context['client'] ?? null;
$this->response = $context['response'] ?? null;
$this->context = null;
if (null !== $this->client && null !== $this->response) {
return true;
}
if ($options & \STREAM_REPORT_ERRORS) {
trigger_error('Missing options "client" or "response" in "symfony" stream context.', \E_USER_WARNING);
}
return false;
}
public function stream_read(int $count)
{
if (\is_resource($this->content)) {
// Empty the internal activity list
foreach ($this->client->stream([$this->response], 0) as $chunk) {
try {
if (!$chunk->isTimeout() && $chunk->isFirst()) {
$this->response->getStatusCode(); // ignore 3/4/5xx
}
} catch (ExceptionInterface $e) {
trigger_error($e->getMessage(), \E_USER_WARNING);
return false;
}
}
if (0 !== fseek($this->content, $this->offset ?? 0)) {
return false;
}
if ('' !== $data = fread($this->content, $count)) {
fseek($this->content, 0, \SEEK_END);
$this->offset += \strlen($data);
return $data;
}
}
if (\is_string($this->content)) {
if (\strlen($this->content) <= $count) {
$data = $this->content;
$this->content = null;
} else {
$data = substr($this->content, 0, $count);
$this->content = substr($this->content, $count);
}
$this->offset += \strlen($data);
return $data;
}
foreach ($this->client->stream([$this->response], $this->blocking ? $this->timeout : 0) as $chunk) {
try {
$this->eof = true;
$this->eof = !$chunk->isTimeout();
if (!$this->eof && !$this->blocking) {
return '';
}
$this->eof = $chunk->isLast();
if ($chunk->isFirst()) {
$this->response->getStatusCode(); // ignore 3/4/5xx
}
if ('' !== $data = $chunk->getContent()) {
if (\strlen($data) > $count) {
if (null === $this->content) {
$this->content = substr($data, $count);
}
$data = substr($data, 0, $count);
}
$this->offset += \strlen($data);
return $data;
}
} catch (ExceptionInterface $e) {
trigger_error($e->getMessage(), \E_USER_WARNING);
return false;
}
}
return '';
}
public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
{
if (\STREAM_OPTION_BLOCKING === $option) {
$this->blocking = (bool) $arg1;
} elseif (\STREAM_OPTION_READ_TIMEOUT === $option) {
$this->timeout = $arg1 + $arg2 / 1e6;
} else {
return false;
}
return true;
}
public function stream_tell(): int
{
return $this->offset ?? 0;
}
public function stream_eof(): bool
{
return $this->eof && !\is_string($this->content);
}
public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
{
if (null === $this->content && null === $this->offset) {
$this->response->getStatusCode();
$this->offset = 0;
}
if (!\is_resource($this->content) || 0 !== fseek($this->content, 0, \SEEK_END)) {
return false;
}
$size = ftell($this->content);
if (\SEEK_CUR === $whence) {
$offset += $this->offset ?? 0;
}
if (\SEEK_END === $whence || $size < $offset) {
foreach ($this->client->stream([$this->response]) as $chunk) {
try {
if ($chunk->isFirst()) {
$this->response->getStatusCode(); // ignore 3/4/5xx
}
// Chunks are buffered in $this->content already
$size += \strlen($chunk->getContent());
if (\SEEK_END !== $whence && $offset <= $size) {
break;
}
} catch (ExceptionInterface $e) {
trigger_error($e->getMessage(), \E_USER_WARNING);
return false;
}
}
if (\SEEK_END === $whence) {
$offset += $size;
}
}
if (0 <= $offset && $offset <= $size) {
$this->eof = false;
$this->offset = $offset;
return true;
}
return false;
}
public function stream_cast(int $castAs)
{
if (\STREAM_CAST_FOR_SELECT === $castAs) {
$this->response->getHeaders(false);
return (\is_callable($this->handle) ? ($this->handle)() : $this->handle) ?? false;
}
return false;
}
public function stream_stat(): array
{
try {
$headers = $this->response->getHeaders(false);
} catch (ExceptionInterface $e) {
trigger_error($e->getMessage(), \E_USER_WARNING);
$headers = [];
}
return [
'dev' => 0,
'ino' => 0,
'mode' => 33060,
'nlink' => 0,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => (int) ($headers['content-length'][0] ?? -1),
'atime' => 0,
'mtime' => strtotime($headers['last-modified'][0] ?? '') ?: 0,
'ctime' => 0,
'blksize' => 0,
'blocks' => 0,
];
}
private function __construct()
{
}
}