给 Tigshop开源商城 单商户加会员等级折扣,只改一张表是不够的(附代码)
需求本身很简单:普通会员原价、银卡 95 折、金卡 9 折,等级不同,买东西的价格就不一样。
看了下 tigshop 开源版,user_rank 这张表是有的,字段里也能塞折扣。我一开始想得挺美,以为在表里填个 discount 就完事了。结果填完测,列表价没变、结算价也没变,就个人中心那个等级图标跟着换了下。价格纹丝不动。
后来把代码翻了一遍才明白,等级权益是 Pro 的完整能力,开源版只留了等级这张表,折扣并不会自动作用到价格上。想让它生效,得自己把折扣接进价格链路里。这篇就把要改的地方记一下。
价格是一路算下来的,不是存死的
先搞清楚为什么改表没用。tigshop 里商品的价格不是存一个「最终价」就完事,它是一路算下来的:
商品详情价 → 列表价 → 加购价 → 结算价(OrderCheckService) → 下单落库价
你只改 user_rank.discount,没有任何一处代码去读它、拿它去乘,价格自然不动。所以要做的是:在这条链的合适位置,把等级折扣乘进去。
先给等级表补上折扣字段
如果表里还没有 discount 字段,先加上:
ALTER TABLE `user_rank` ADD COLUMN `discount` decimal(5,2) DEFAULT 100.00
COMMENT '等级折扣,百分比,95 表示 95 折';
然后找个统一的地方,根据用户拿到他当前等级的折扣。我放在 php/app/service/admin/user/UserRankService.php:
// 拿当前用户的等级折扣,返回 0.01~1 之间的系数
public function getUserDiscount(int $userId): float
{
$user = \app\model\user\User::field('user_id,rank_id')->find($userId);
if (!$user) return 1.0;
$rank = \app\model\user\UserRank::where('rank_id', $user->rank_id)->find();
if (!$rank || $rank->discount >= 100) return 1.0;
return bcdiv((string)$rank->discount, '100', 4);
}
返回的是系数,95 折就是 0.95,不打折就是 1,后面直接拿来乘价格。
关键在结算这一步
这是最容易漏的地方。结算逻辑在 php/app/service/admin/order/OrderCheckService.php,购物车的促销组装在 php/app/service/front/cart/CartService.php 的 buildCartPromotion() 里。等级折扣就当成一种促销,叠进去:
// buildCartPromotion 里遍历购物车行的时候
$discount = app(UserRankService::class)->getUserDiscount($userId);
if ($discount < 1) {
foreach ($cart_list['carts'] as &$shopCart) {
foreach ($shopCart['items'] as &$item) {
$rankPrice = bcmul((string)$item['price'], (string)$discount, 2);
$item['rank_discount'] = bcsub((string)$item['price'], $rankPrice, 2);
$item['price'] = $rankPrice; // 这里覆盖的是参与结算的价格
}
}
}
注意这里改的是参与结算的 price,原价字段(market_price 那些)要留着。前端要靠原价划线,才能给用户显示「省了多少」,你把原价也改了就没得对比了。
券和等级折扣谁先谁后
如果店里还有优惠券,就得想清楚顺序问题。先打折再用券,和先用券再打折,最后金额是不一样的。
我这边的口径是先算等级价、再用券,所以上面 getUserDiscount 的乘法要放在券分摊之前。放反了的话,券按原价抵扣完再打折,商家会比预期多亏一截,对账也对不平。
还有一点顺手提醒:等级价加券叠完,别让实付减成了负数。OrderCheckService 提交之前留一道 <= 0 的兜底校验,不然会冒出 0 元甚至负数的订单。
前端得把会员价显示出来
后端算对了,前端不展示也白搭。结算接口返回体里带上 rank_discount,Uniapp 结算页就能显示了:
<!-- view/Tigshop-Uniapp/src/pages/order/confirm.vue -->
<view class="price-line">
<text>会员{{ rankName }}价</text>
<text class="red">¥{{ item.price }}</text>
<text class="through">¥{{ item.origin_price }}</text>
</view>
<view v-if="item.rank_discount > 0" class="tip">
会员已省 ¥{{ item.rank_discount }}
</view>
后台 Tigshop-Admin 的等级配置页也别忘了加个折扣输入框,不然运营改不了:
<!-- Tigshop-Admin 等级编辑表单 -->
<el-form-item label="等级折扣">
<el-input-number v-model="form.discount" :min="1" :max="100" />
<span class="tip">按百分比填,95 就是 95 折,100 是不打折</span>
</el-form-item>
测的时候按这几种情况过一遍
改完别只测一个账号。我一般会拿几个不同等级的账号,各下一单看结算价对不对;再测个带券的,确认是先打折后用券;最后故意凑一单让折扣加券快到 0,看兜底校验拦不拦得住。
会员等级在官网标 Pro,是因为完整那套还包含成长值升级、等级专享券、专属客服这些,不是一个折扣的事。但如果客户只要「不同等级不同价」,按上面这几处改完就能跑,改动都压在 Service 层,以后跟着官方升级版本,冲突也还算好处理。要是客户后面想要完整的权益体系,那就还是老老实实上 Pro,别硬在开源版上堆。
- 点赞
- 收藏
- 关注作者
评论(0)