Files
huangjingfen/pro_v3.5.1_副本/crmeb/services/upload/XML.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

155 lines
3.4 KiB
PHP

<?php
namespace crmeb\services\upload;
class XML
{
/**
* XML to array.
*
* @param string $xml XML string
*
* @return array
*/
public static function parse($xml)
{
$backup = PHP_MAJOR_VERSION < 8 ? libxml_disable_entity_loader(true) : null;
$result = self::normalize(simplexml_load_string(self::sanitize($xml), 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_NOCDATA | LIBXML_NOBLANKS));
PHP_MAJOR_VERSION < 8 && libxml_disable_entity_loader($backup);
return $result;
}
/**
* XML encode.
*
* @param mixed $data
* @param string $root
* @param string $item
* @param string $attr
* @param string $id
*
* @return string
*/
public static function build(
$data,
$root = 'xml',
$item = 'item',
$attr = '',
$id = 'id'
)
{
if (is_array($attr)) {
$_attr = [];
foreach ($attr as $key => $value) {
$_attr[] = "{$key}=\"{$value}\"";
}
$attr = implode(' ', $_attr);
}
$attr = trim($attr);
$attr = empty($attr) ? '' : " {$attr}";
$xml = "<{$root}{$attr}>";
$xml .= self::data2Xml($data, $item, $id);
$xml .= "</{$root}>";
return $xml;
}
/**
* Build CDATA.
*
* @param string $string
*
* @return string
*/
public static function cdata($string)
{
return sprintf('<![CDATA[%s]]>', $string);
}
/**
* Object to array.
*
*
* @param SimpleXMLElement $obj
*
* @return array
*/
protected static function normalize($obj)
{
$result = null;
if (is_object($obj)) {
$obj = (array)$obj;
}
if (is_array($obj)) {
foreach ($obj as $key => $value) {
$res = self::normalize($value);
if (('@attributes' === $key) && ($key)) {
$result = $res; // @codeCoverageIgnore
} else {
$result[$key] = $res;
}
}
} else {
$result = $obj;
}
return $result;
}
/**
* Array to XML.
*
* @param array $data
* @param string $item
* @param string $id
*
* @return string
*/
protected static function data2Xml($data, $item = 'item', $id = 'id')
{
$xml = $attr = '';
foreach ($data as $key => $val) {
if (is_numeric($key)) {
$id && $attr = " {$id}=\"{$key}\"";
$key = $item;
}
$xml .= "<{$key}{$attr}>";
if ((is_array($val) || is_object($val))) {
$xml .= self::data2Xml((array)$val, $item, $id);
} else {
$xml .= is_numeric($val) ? $val : self::cdata($val);
}
$xml .= "</{$key}>";
}
return $xml;
}
/**
* Delete invalid characters in XML.
*
* @see https://www.w3.org/TR/2008/REC-xml-20081126/#charsets - XML charset range
* @see http://php.net/manual/en/regexp.reference.escape.php - escape in UTF-8 mode
*
* @param string $xml
*
* @return string
*/
public static function sanitize($xml)
{
return preg_replace('/[^\x{9}\x{A}\x{D}\x{20}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u', '', $xml);
}
}