'integer', 'level_id' => 'integer', 'price' => 'decimal:2', 'price_type' => 'integer', 'percent' => 'decimal:2', ]; /** 序列化时附带实际销售价(后台列表/小程序列表直接展示) */ protected $appends = ['actual_price']; /** * 所属商品 */ public function product(): BelongsTo { return $this->belongsTo(ProductModel::class, 'product_id', 'id'); } /** * 所属客户等级 */ public function level(): BelongsTo { return $this->belongsTo(CustomerLevelModel::class, 'level_id', 'id'); } /** * 按商品 + 等级筛选价格 */ public function scopeForProductLevel($query, int $productId, int $levelId) { return $query->where('product_id', $productId)->where('level_id', $levelId); } /** * 计算实际销售价(统一换算入口,金额走 bcmath 保证两位小数精度) * * 固定价返回 price 原值;成本百分比返回 cost × (100 + percent) / 100(四舍五入保留两位)。 * * @param string|int|float $price 固定价(decimal cast 后为 '5.50' 形式字符串) * @param string|int|float $percent 成本上浮百分点(30 = 上浮 30%) * @param string|int|float $costPrice 商品成本价(decimal cast 字符串) * @return string 两位小数字符串,如 '13.05' */ public static function calcActualPrice( int $priceType, string|int|float $price, string|int|float $percent, string|int|float $costPrice, ): string { if ($priceType === self::PRICE_TYPE_PERCENT) { $multiplier = bcadd('100', (string) $percent, 4); return bcdiv(bcmul((string) $costPrice, $multiplier, 4), '100', 2); } // 固定价:归一化为两位小数字符串 return bcadd((string) $price, '0', 2); } /** * 实际销售价访问器(供 toArray 输出 actual_price) * * 依赖 product 关系取成本价;prices 经商品 eager load 加载时逆向关系自动填充,无 N+1。 * 注意:单独序列化本模型且未加载 product 关系时会触发一次查询,成本价缺失按 0 兜底。 */ protected function getActualPriceAttribute(): string { return self::calcActualPrice( (int) $this->price_type, (string) $this->price, (string) $this->percent, (string) ($this->product?->cost_price ?? 0), ); } }