update tmall refund parse

This commit is contained in:
2026-03-04 14:08:46 +08:00
parent cbe37d659b
commit 26e5c6806c
2 changed files with 650 additions and 8 deletions
@@ -0,0 +1,339 @@
<?php
declare(strict_types=1);
namespace HyperfTest\Cases\Platform\Tmall\EntityParse;
use App\Constants\OrderStatus;
use App\Constants\OrderType;
use App\Constants\PaymentMethod;
use App\Constants\RefundType;
use App\Platform\Tmall\EntityParse\Refund;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionMethod;
/**
* Tmall 闪电退款订单重建 - 数据映射逻辑测试
*
* 测试 fixInstantRefundOrders 中的纯数据转换逻辑,
* 不涉及数据库操作(DB 交互由集成测试覆盖)。
*
* @internal
* @coversNothing
*/
class FixInstantRefundOrdersTest extends TestCase
{
private Refund $parser;
/**
* 样本A attribute: 商品单价 ¥399,退款 ¥177,优惠 ¥22.11
*/
private string $sampleAttrA = ';fundFlowInfo:CASH^ALIPAY_FUND^支付宝^15489|OTHER_ASSETS^OTHER_ASSETS_GROUP^优惠^2211;bizCode:tmall.hk.refund;disputeRequest:1;leavesCat:50026924;apply_reason_text:不喜欢/不想要;itemBuyAmount:1;seller_batch:true;clj_zero_second_refund:1;DBT:InterceptUnconsignRefund;sku:5085821404607|口味#3B橘子味#3A颜色分类#3B维C 250mg;bgmtc:2026-02-24 13#3B47#3B41;shop_name:TestShop;pmtR:TAPP_USERCOUPON_SP^2211|;tmgSimpleZeroRefund:1;isVirtual:0;EXmrf:17700;warehouseRefund:1;apply_init_refund_fee:17700;mainOrderPostFee:0;itemPrice:39900;toSellerFee:0;';
/**
* 样本B attribute: 商品单价 ¥1199,退款 ¥669,优惠 ¥65.89
*/
private string $sampleAttrB = ';bizCode:tmall.hk.refund;disputeRequest:1;leavesCat:50026872;apply_reason_text:不喜欢/不想要;itemBuyAmount:1;seller_batch:true;clj_zero_second_refund:1;DBT:InterceptUnconsignRefund;sku:5112591851366|颜色分类#3B120粒;bgmtc:2026-02-24 00#3B03#3B54;shop_name:TestShop;pmtR:TAPP_USERCOUPON_SP^6589|;tmgSimpleZeroRefund:1;isVirtual:0;EXmrf:66900;warehouseRefund:1;apply_init_refund_fee:66900;mainOrderPostFee:0;itemPrice:119900;toSellerFee:0;';
/**
* 非闪电退款 attribute (无 clj_zero_second_refund)
*/
private string $sampleAttrNormal = ';bizCode:tmall.hk.refund;itemBuyAmount:2;itemPrice:19900;mainOrderPostFee:1000;sku:123456|颜色#3B红色;pmtR:;';
protected function setUp(): void
{
parent::setUp();
$reflection = new ReflectionClass(Refund::class);
$this->parser = $reflection->newInstanceWithoutConstructor();
}
// ========================================================
// 闪电退款检测
// ========================================================
/**
* 验证 parseRefundItemAttribute 正确识别闪电退款标识
*/
public function testDetectsInstantRefundFromAttribute(): void
{
$attrA = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
$attrB = $this->parser->parseRefundItemAttribute($this->sampleAttrB);
$this->assertTrue($attrA['clj_zero_second_refund']);
$this->assertTrue($attrB['clj_zero_second_refund']);
}
/**
* 非闪电退款记录不被识别
*/
public function testNonInstantRefundNotDetected(): void
{
$attr = $this->parser->parseRefundItemAttribute($this->sampleAttrNormal);
$this->assertFalse($attr['clj_zero_second_refund']);
}
/**
* 空 attribute 返回空数组
*/
public function testEmptyAttributeReturnsEmptyArray(): void
{
$attr = $this->parser->parseRefundItemAttribute('');
$this->assertSame([], $attr);
}
// ========================================================
// 金额解析验证
// ========================================================
/**
* 验证 attribute 中的金额字段(单位: 分)正确解析
*/
public function testAttributeAmountsInFen(): void
{
$attr = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
// item_price 39900分 = ¥399.00
$this->assertSame(39900, $attr['item_price']);
$this->assertSame(1, $attr['item_buy_amount']);
// 转换为元: 39900 * 1 / 100 = ¥399
$this->assertEquals(399, $attr['item_price'] * $attr['item_buy_amount'] / 100);
}
/**
* 验证优惠金额聚合(单位: 分 → 元)
*/
public function testPromotionAmountAggregation(): void
{
$attrA = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
$attrB = $this->parser->parseRefundItemAttribute($this->sampleAttrB);
// 样本A: 优惠 2211分 = ¥22.11
$total_discount_a = 0;
foreach ($attrA['promotions'] as $promo) {
$total_discount_a += $promo['amount'];
}
$this->assertSame(2211, $total_discount_a);
$this->assertSame(22.11, $total_discount_a / 100);
// 样本B: 优惠 6589分 = ¥65.89
$total_discount_b = 0;
foreach ($attrB['promotions'] as $promo) {
$total_discount_b += $promo['amount'];
}
$this->assertSame(6589, $total_discount_b);
$this->assertSame(65.89, $total_discount_b / 100);
}
// ========================================================
// 多子项同 tid 金额聚合
// ========================================================
/**
* 同一主订单下多个子订单的金额正确聚合
*/
public function testMultiSubOrderAmountAggregation(): void
{
$attrA = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
$attrB = $this->parser->parseRefundItemAttribute($this->sampleAttrB);
// 模拟两条同 tid 记录的 Order 金额聚合
$total_amount_fen = 0;
$total_paid_yuan = 0;
$total_discount_fen = 0;
// 记录A: item_price=39900, refund_fee=177.00
$total_amount_fen += $attrA['item_price'] * $attrA['item_buy_amount'];
$total_paid_yuan += 177.00; // refund_fee in yuan
foreach ($attrA['promotions'] as $promo) {
$total_discount_fen += $promo['amount'];
}
// 记录B: item_price=119900, refund_fee=669.00
$total_amount_fen += $attrB['item_price'] * $attrB['item_buy_amount'];
$total_paid_yuan += 669.00;
foreach ($attrB['promotions'] as $promo) {
$total_discount_fen += $promo['amount'];
}
// 验证聚合结果
$this->assertSame(159800, $total_amount_fen); // 39900 + 119900
$this->assertEquals(1598, $total_amount_fen / 100); // ¥1598.00
$this->assertSame(846.0, $total_paid_yuan); // ¥177 + ¥669
$this->assertSame(8800, $total_discount_fen); // 2211 + 6589
$this->assertEquals(88, $total_discount_fen / 100); // ¥88.00
}
// ========================================================
// Order 字段映射验证
// ========================================================
/**
* 验证 bgmtc 时间字段正确解码(#3B → 冒号)
*/
public function testBgmtcTimeParsing(): void
{
$attr = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
// bgmtc 原始: 2026-02-24 13#3B47#3B41 → 解码: 2026-02-24 13:47:41
$this->assertSame('2026-02-24 13:47:41', $attr['bgmtc']);
}
/**
* 验证运费取最大值逻辑
*/
public function testFreightFeeMaxSelection(): void
{
$attrA = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
$attrB = $this->parser->parseRefundItemAttribute($this->sampleAttrB);
// 两个样本 mainOrderPostFee 都是 0
$max_post_fee = max($attrA['main_order_post_fee'], $attrB['main_order_post_fee']);
$this->assertSame(0, $max_post_fee);
$this->assertEquals(0, $max_post_fee / 100);
}
/**
* 验证运费正确解析(非零场景)
*/
public function testFreightFeeNonZero(): void
{
$attr = $this->parser->parseRefundItemAttribute($this->sampleAttrNormal);
// mainOrderPostFee:1000 = ¥10.00
$this->assertSame(1000, $attr['main_order_post_fee']);
$this->assertEquals(10, $attr['main_order_post_fee'] / 100);
}
// ========================================================
// OrderItem 字段映射验证
// ========================================================
/**
* 验证 SKU 正确提取用于产品查询键
*/
public function testProductKeyFromAttribute(): void
{
$attrA = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
$attrB = $this->parser->parseRefundItemAttribute($this->sampleAttrB);
// 产品查询键: num_iid:sku_id
$this->assertSame('5085821404607', $attrA['sku']['sku_id']);
$this->assertSame('5112591851366', $attrB['sku']['sku_id']);
}
/**
* 验证 quantity 使用 item_buy_amount
*/
public function testQuantityFromAttribute(): void
{
$attr = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
$this->assertSame(1, $attr['item_buy_amount']);
// item_buy_amount > 0 时使用 attribute 值
$quantity = $attr['item_buy_amount'] > 0 ? $attr['item_buy_amount'] : 1;
$this->assertSame(1, $quantity);
}
/**
* item_buy_amount = 0 时回退到 record['num']
*/
public function testQuantityFallbackToNum(): void
{
// 当 attribute 中 itemBuyAmount 为 0 或缺失
$attr = $this->parser->parseRefundItemAttribute(';bizCode:test;itemBuyAmount:0;');
$record_num = 3;
$quantity = $attr['item_buy_amount'] > 0 ? $attr['item_buy_amount'] : $record_num;
$this->assertSame(3, $quantity);
}
// ========================================================
// RefundType 判定验证
// ========================================================
/**
* 闪电退款应返回 INSTANT_REFUND_WITHOUT_RETURN
*/
public function testInstantRefundTypeOverride(): void
{
$attr = $this->parser->parseRefundItemAttribute($this->sampleAttrA);
$this->assertTrue($attr['clj_zero_second_refund']);
// 当 clj_zero_second_refund=true 时,refund_type_id 应为 5
$this->assertSame(5, RefundType::INSTANT_REFUND_WITHOUT_RETURN->value);
}
/**
* 非闪电退款维持原有判定逻辑
*/
public function testNormalRefundTypePreserved(): void
{
// has_good_return=false, good_status=BUYER_NOT_RECEIVED → RETURN_BEFORE_SHIPPING (1)
$record = ['has_good_return' => false, 'good_status' => 'BUYER_NOT_RECEIVED'];
$type_id = $this->parser->getRefundTypeId($record);
$this->assertSame(RefundType::RETURN_BEFORE_SHIPPING->value, $type_id);
// has_good_return=true → RETURN_AND_REFUND (2)
$record = ['has_good_return' => true, 'good_status' => ''];
$type_id = $this->parser->getRefundTypeId($record);
$this->assertSame(RefundType::RETURN_AND_REFUND->value, $type_id);
}
// ========================================================
// Order 常量值验证
// ========================================================
/**
* 确认重建 Order 使用的常量值
*/
public function testReconstructedOrderConstants(): void
{
$this->assertSame(9, OrderStatus::CANCEL_BEFORE_SHIPPING->value);
$this->assertSame(9, OrderType::REGRET_ORDER->value);
$this->assertSame(2, PaymentMethod::ALIPAY_CN->value);
}
// ========================================================
// 边缘情况
// ========================================================
/**
* attribute 无 SKU 信息时 sku 为 null
*/
public function testMissingSkuInAttribute(): void
{
$attr = $this->parser->parseRefundItemAttribute(';clj_zero_second_refund:1;itemPrice:10000;itemBuyAmount:1;');
$this->assertTrue($attr['clj_zero_second_refund']);
$this->assertNull($attr['sku']);
// 无 SKU 时产品查询键的 model_id 应为 null
$model_id = $attr['sku']['sku_id'] ?? null;
$this->assertNull($model_id);
// 查询键格式: item_id:null
$key = '12345:' . ($model_id ?? 'null');
$this->assertSame('12345:null', $key);
}
/**
* 无优惠信息时 discount = 0
*/
public function testNoPromotionsDiscount(): void
{
$attr = $this->parser->parseRefundItemAttribute(';clj_zero_second_refund:1;itemPrice:10000;pmtR:;');
$total_discount = 0;
foreach ($attr['promotions'] as $promo) {
$total_discount += $promo['amount'];
}
$this->assertSame(0, $total_discount);
}
}