给 Tigshop开源商城 单商户加会员等级折扣,只改一张表是不够的(附代码)

举报
电商系统程序猿 发表于 2026/07/31 17:18:21 2026/07/31
【摘要】 需求本身很简单:普通会员原价、银卡 95 折、金卡 9 折,等级不同,买东西的价格就不一样。看了下 tigshop 开源版,user_rank 这张表是有的,字段里也能塞折扣。我一开始想得挺美,以为在表里填个 discount 就完事了。结果填完测,列表价没变、结算价也没变,就个人中心那个等级图标跟着换了下。价格纹丝不动。后来把代码翻了一遍才明白,等级权益是 Pro 的完整能力,开源版只留了...

需求本身很简单:普通会员原价、银卡 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,别硬在开源版上堆。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。