update tmall refund parse
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user