User
型号class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
const TYPE_MERCHANT = 'merchant';
const TYPE_AFFILIATE = 'affiliate';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'type'
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function merchant(): HasOne
{
return $this->hasOne(Merchant::class);
}
public function affiliate(): HasOne
{
return $this->hasOne(Affiliate::class);
}
}
Merchant
型号class Merchant extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'domain',
'display_name',
'turn_customers_into_affiliates',
'default_commission_rate'
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
}
Affiliate
型号class Affiliate extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'merchant_id',
'commission_rate',
'discount_code'
];
public function merchant()
{
return $this->belongsTo(Merchant::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
public function orders()
{
return $this->hasMany(Order::class);
}
}
Order
型号class Order extends Model
{
use HasFactory;
const STATUS_UNPAID = 'unpaid';
const STATUS_PAID = 'paid';
protected $fillable = [
'external_order_id',
'merchant_id',
'affiliate_id',
'subtotal',
'commission_owed',
'payout_status',
'customer_email',
'created_at'
];
public function merchant()
{
return $this->belongsTo(Merchant::class);
}
public function affiliate()
{
return $this->belongsTo(Affiliate::class);
}
}
这是我想要通过的测试用例,我们根本无法修改测试用例,这不是选择。
这是我已经实施的解决方案,但我无法通过测试,并且收到以下错误:
Mockery\Exception\BadMethodCallException : Received Mockery_3_App_Models_Affiliate::getAttribute(), but no expectations were specified
/Users/mac/Downloads/testProject/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:2222
/Users/mac/Downloads/testProject/app/Services/OrderService.php:43
/Users/mac/Downloads/testProject/tests/Feature/Services/OrderServiceTest.php:59
class OrderService
{
public function __construct(
protected AffiliateService $affiliateService,
protected ApiService $apiService
) {}
/**
* Process an order and log any commissions.
* This should create a new affiliate if the customer_email is not already associated with one.
* This method should also ignore duplicates based on order_id.
*
* @param array{order_id: string, subtotal_price: float, merchant_domain: string, discount_code: string, customer_email: string, customer_name: string} $data
* @return void
*/
public function processOrder(array $data): void
{
if (!$this->getOrderByExternalOrderId($data['order_id'])) {
$merchant = Merchant::where('domain', $data['merchant_domain'])->firstOrFail();
$affiliate = Affiliate::where('user_id', function ($query) use ($data) {
$query->select('id')
->from('users')
->where('email', $data['customer_email']);
})
->first();
if (!$affiliate) {
$affiliate = $this->affiliateService->register($merchant, $data['customer_email'], $data['customer_name'], 0.1);
}
$order = new Order([
'external_order_id' => $data['order_id'],
'subtotal' => $data['subtotal_price'],
'affiliate_id' => $affiliate->id,
'merchant_id' => $merchant->id,
'commission_owed' => $data['subtotal_price'] * $affiliate->commission_rate,
]);
$order->save();
}
}
public function getOrderByExternalOrderId($externalOrderId)
{
return Order::where('external_order_id', $externalOrderId)->first();
}
}