Files
huangjingfen/pro_v3.5.1_副本/vendor/ergebnis/classy/src/Constructs.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

257 lines
7.3 KiB
PHP

<?php
declare(strict_types=1);
/**
* Copyright (c) 2017-2022 Andreas Möller
*
* For the full copyright and license information, please view
* the LICENSE.md file that was distributed with this source code.
*
* @see https://github.com/ergebnis/classy
*/
namespace Ergebnis\Classy;
final class Constructs
{
/**
* Returns an array of names of classy constructs (classes, interfaces, traits) found in source.
*
* @throws Exception\ParseError
*
* @return array<int, Construct>
*/
public static function fromSource(string $source): array
{
$constructs = [];
try {
$sequence = \token_get_all(
$source,
\TOKEN_PARSE,
);
} catch (\ParseError $exception) {
throw Exception\ParseError::fromParseError($exception);
}
$count = \count($sequence);
$namespacePrefix = '';
$namespaceSegmentOrNamespaceTokens = [
\T_STRING,
\T_NAME_QUALIFIED,
];
$classyTokens = [
\T_CLASS,
\T_INTERFACE,
\T_TRAIT,
];
// https://wiki.php.net/rfc/enumerations
if (\PHP_VERSION_ID >= 80100 && \defined('T_ENUM')) {
$classyTokens = [
\T_CLASS,
\T_ENUM,
\T_INTERFACE,
\T_TRAIT,
];
}
for ($index = 0; $index < $count; ++$index) {
$token = $sequence[$index];
// collect namespace name
if (\is_array($token) && \T_NAMESPACE === $token[0]) {
$namespaceSegments = [];
// collect namespace segments
for ($index = self::significantAfter($index, $sequence, $count); $index < $count; ++$index) {
$token = $sequence[$index];
if (\is_array($token) && !\in_array($token[0], $namespaceSegmentOrNamespaceTokens, true)) {
continue;
}
$content = self::content($token);
if (\in_array($content, ['{', ';'], true)) {
break;
}
$namespaceSegments[] = $content;
}
$namespace = \implode('\\', $namespaceSegments);
$namespacePrefix = $namespace . '\\';
}
// skip non-classy tokens
if (!\is_array($token) || !\in_array($token[0], $classyTokens, true)) {
continue;
}
// skip anonymous classes
if (\T_CLASS === $token[0]) {
$current = self::significantBefore($index, $sequence);
$token = $sequence[$current];
// if significant token before T_CLASS is T_NEW, it's an instantiation of an anonymous class
if (\is_array($token) && \T_NEW === $token[0]) {
continue;
}
}
$index = self::significantAfter($index, $sequence, $count);
$token = $sequence[$index];
$constructs[] = Construct::fromName($namespacePrefix . self::content($token));
}
\usort($constructs, static function (Construct $a, Construct $b): int {
return \strcmp(
$a->name(),
$b->name(),
);
});
return $constructs;
}
/**
* Returns an array of constructs defined in a directory.
*
* @throws Exception\DirectoryDoesNotExist
* @throws Exception\MultipleDefinitionsFound
*
* @return array<int, Construct>
*/
public static function fromDirectory(string $directory): array
{
if (!\is_dir($directory)) {
throw Exception\DirectoryDoesNotExist::fromDirectory($directory);
}
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(
$directory,
\RecursiveDirectoryIterator::FOLLOW_SYMLINKS,
));
$constructs = [];
foreach ($iterator as $fileInfo) {
/** @var \SplFileInfo $fileInfo */
if (!$fileInfo->isFile()) {
continue;
}
if ($fileInfo->getBasename('.php') === $fileInfo->getBasename()) {
continue;
}
/** @var string $fileName */
$fileName = $fileInfo->getRealPath();
/** @var string $source */
$source = \file_get_contents($fileName);
try {
$constructsFromFile = self::fromSource($source);
} catch (Exception\ParseError $exception) {
throw Exception\ParseError::fromFileNameAndParseError(
$fileName,
$exception,
);
}
if (0 === \count($constructsFromFile)) {
continue;
}
foreach ($constructsFromFile as $construct) {
$name = $construct->name();
if (\array_key_exists($name, $constructs)) {
$construct = $constructs[$name];
}
$constructs[$name] = $construct->definedIn($fileName);
}
}
\usort($constructs, static function (Construct $a, Construct $b): int {
return \strcmp(
$a->name(),
$b->name(),
);
});
$constructsWithMultipleDefinitions = \array_filter($constructs, static function (Construct $construct): bool {
return 1 < \count($construct->fileNames());
});
if (0 < \count($constructsWithMultipleDefinitions)) {
throw Exception\MultipleDefinitionsFound::fromConstructs($constructsWithMultipleDefinitions);
}
return \array_values($constructs);
}
/**
* Returns the index of the significant token after the index.
*
* @param array<int, array{0: int, 1: string, 2: int}|string> $sequence
*/
private static function significantAfter(int $index, array $sequence, int $count): int
{
for ($current = $index + 1; $current < $count; ++$current) {
$token = $sequence[$current];
if (\is_array($token) && \in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT, \T_WHITESPACE], true)) {
continue;
}
return $current;
}
throw Exception\ShouldNotHappen::create();
}
/**
* Returns the index of the significant token after the index.
*
* @param array<int, array{0: int, 1: string, 2: int}|string> $sequence
*/
private static function significantBefore(
int $index,
array $sequence,
): int {
for ($current = $index - 1; -1 < $current; --$current) {
$token = $sequence[$current];
if (\is_array($token) && \in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT, \T_WHITESPACE], true)) {
continue;
}
return $current;
}
throw Exception\ShouldNotHappen::create();
}
/**
* Returns the string content of a token.
*
* @param array{0: int, 1: string, 2: int}|string $token
*/
private static function content($token): string
{
if (\is_array($token)) {
return $token[1];
}
return $token;
}
}