一、前言
在之前的系列文章中,我写了 LikeShop 多商户版的安全加固清单,从后台权限、接口鉴权、支付回调防护和数据隔离四个层面给出了加固方案。这一篇换个角度,聊的是二开过程中如何“安全地加功能”。
先说一个我见过的真实场景。有个团队接了 LikeShop 多商户版的定制项目,需求是“订单支付成功后,除了发短信,还要推送一条企业微信通知”。开发同学很直接,在OrderLogic::paySuccess()方法末尾加了三行代码,调用了一个自建的推送方法。
功能上线没问题。三个月后官方发布了新版本,修复了一个订单状态机的安全问题。团队想升级,发现paySuccess()已经被改得面目全非,核心逻辑和推送逻辑缠在一起,根本没法直接合并官方更新。最后只能人工比对代码,花了三天时间做一次“手术式”的合并。
问题的根源不是“不该加功能”,而是“加错了地方”。这篇文章就围绕这个核心问题,把 LikeShop 的二开规范拆开讲清楚。
二、四条核心原则
LikeShop 的二次开发方法论中,定义了四条核心原则。理解这四条原则,后面所有具体的操作规范就都能推导出来。
2.1 稳定内核原则
核心业务链路(订单、支付、用户)保持稳定,避免直接修改底层实现,所有扩展优先基于外围能力实现。目标是保证系统可升级性。
这条原则的含义是:OrderLogic、PayLogic、UserLogic这些核心文件,应该被视为“只读”的。你可以调用它们的方法,但不要修改它们的方法体。
2.2 分层解耦原则
系统按职责划分为控制层(Controller)、业务层(Service / Logic)、数据层(Model)。扩展逻辑主要集中在业务层,避免跨层耦合。
这条原则在之前的《LikeShop 分层架构源码导读》中已经详细讲过。这里补充一个二开视角的关键点:扩展逻辑写在业务层,但不要写在核心业务的 Logic 文件中。新建一个独立的 Logic 或 Service,让核心 Logic 去调用它,而不是把扩展代码塞进核心 Logic。
2.3 扩展优先原则
新增能力 > 修改原逻辑,组合扩展 > 覆盖替换。目标是降低系统侵入性。
这条原则给出了具体的策略排序。举个例子:要在下单流程中增加“风控审核”步骤,修改原逻辑的做法是在OrderLogic::create()中插入风控判断;扩展优先的做法是新建一个RiskLogic,让OrderLogic在合适的时机调用RiskLogic::check()。
2.4 数据驱动原则
通过数据结构扩展支持业务变化,减少硬编码逻辑,提升系统适应能力。
如果业务需求是“支持三种不同的订单拆分策略”,不要写三个if/else分支,而是设计一个策略配置字段,把策略标识存在数据库中,代码中只保留一个“根据策略标识执行对应逻辑”的入口。
三、可扩展点:在哪里加功能是安全的
LikeShop 在四个核心域中预留了扩展点。理解这些扩展点的位置和实现方式,就能在不触碰核心链路的前提下完成功能扩展。
3.1 商品域扩展
支持扩展:商品属性模型、多规格组合、动态定价规则。实现方式是扩展字段(JSON / 结构化字段)和业务层计算逻辑注入。
多商户版特别注意:商品和商户的关联关系(shop_id)是核心字段,扩展商品属性时不要修改关联逻辑。如果要增加“按商户分组的商品属性”,应该新建关联表,而不是在ls_goods中加字段。
3.2 订单域扩展
订单生命周期为:创建 → 支付 → 履约 → 完成 → 售后。扩展能力包括拆单策略、多仓发货、自定义履约逻辑。建议通过业务层注入规则,而非修改核心流程。
多商户版的特殊约束:订单采用“统一下单 + 订单拆分”模式,购物车跨店结算时系统按店铺自动拆分订单。如果要修改拆单策略(比如“按仓库拆分”而不是“按店铺拆分”),正确做法是在拆单逻辑前增加一个策略判断入口,而不是直接改OrderLogic中的拆单循环。
3.3 营销域扩展
营销域的特点是高复杂度、强规则驱动。支持扩展:优惠规则引擎、用户分组策略、活动叠加机制。推荐采用“规则配置 + 计算引擎”模式。
营销扩展的核心原则是“统一计算入口”。所有价格计算必须进入统一的 Price Engine,保证输入一致、输出一致、可追踪。很多系统的价格问题都源于“商品页算一套、购物车算一套、下单再算一套”。
3.4 用户域扩展
支持用户标签体系、会员等级、分层定价。实现方式是扩展用户属性,引入策略控制层。
多商户版的用户隔离:用户是整个平台共用的,但用户的会员等级、标签可能因商户而异。扩展用户属性时,要明确这个属性是“平台级”还是“商户级”。平台级的属性放ls_user,商户级的属性需要新建用户-商户关联表。
四、操作规范:具体怎么改
4.1 分层开发规范
| 层级 | 职责 | 二开约束 |
|---|---|---|
| Controller | 请求处理 | 不写业务逻辑 |
| Service / Logic | 核心业务 | 二开主要区域 |
| Model | 数据访问 | 不包含业务逻辑 |
Controller 只做参数接收和响应返回。Model 只做数据映射和基础查询。二开的所有业务逻辑都应该写在 Service / Logic 层,但要注意区分“核心 Logic”和“扩展 Logic”。
4.2 扩展实现方式
推荐模式:新建 Service 扩展逻辑,使用组合调用原能力,保持接口兼容。
避免:修改原方法逻辑,直接覆盖核心类。
具体来说,如果你需要在下单成功后增加一个操作:
// ❌ 不推荐:直接修改 OrderLogic 核心方法publicstaticfunctionpaySuccess($orderId){// ... 原有逻辑// 新增的推送逻辑直接插在这里WecomPush::send($orderId);}// ✅ 推荐:新建扩展 Logic,在业务层组合调用classOrderExtendLogic{publicstaticfunctionafterPaySuccess($orderId){// 扩展逻辑独立成方法WecomPush::send($orderId);}}然后在 Controller 层或事件监听中调用OrderExtendLogic::afterPaySuccess(),而不是修改OrderLogic本身。
4.3 数据库扩展策略
表结构扩展原则:优先扩展字段,必要时新增业务表,避免破坏原有结构。
优先扩展字段:如果新功能只需要存储一两个额外的值,在现有表中加字段是可以接受的。但要确认这个字段不会影响原有查询和索引。
必要时新增业务表:如果新功能是一个独立的业务实体(比如“商户结算周期配置”),应该新建一张表,通过外键关联到ls_shop,而不是在ls_shop中堆砌字段。
索引与性能:核心查询必须走索引,控制 Join 数量,避免大事务。
五、多商户版的特殊约束
多商户版相比单商户版,在二开时需要额外注意几条约束。
5.1 商户数据隔离是底线
多商户系统的数据隔离是核心安全要求。所有二开新增的查询接口,必须显式加上商户维度的过滤条件。不要依赖“前端传的 shop_id 是可信的”这种假设。
官方在安全加固中已经明确:订单、售后、分销、会员数据均增加了数据归属校验。二开时如果新增了查询接口,需要自己补上同样的归属校验逻辑。
5.2 订单拆分的核心逻辑不要动
多商户版的订单拆分是多商户架构的核心能力。购物车跨店铺下单时,系统按店铺自动拆分订单,运费模板根据不同店铺商品独立核算。
如果业务要求“同一店铺的商品再按仓库拆分成多个子订单”,正确做法是在拆单策略层增加一个仓库维度的判断,而不是修改原有的店铺拆分逻辑。拆单的核心约束是“订单必须归属到唯一商户”,这个约束不能破坏。
5.3 结算归属不能错
多商户版支持平台抽佣 + 商家独立结算,平台统一结算订单后由商家提现。二开新增的订单类型或营销活动,必须明确结算归属规则——这笔订单的收入归哪个商户,平台抽佣比例是多少。
六、实战案例:两个常见的二开场景
场景一:新增一个支付渠道
错误做法:在PayLogic的支付回调处理方法中增加if ($payType == 'unionpay')分支,把银联的验签和业务处理都塞进去。
正确做法:
第一步:在PayServer中封装银联 SDK 的验签方法,保持和微信验签相同的调用接口。
第二步:在PayLogic中新增渠道分支,处理银联回调的验签结果,然后统一调用OrderLogic::paySuccess()。
第三步:确保所有支付渠道最终都汇入同一个paySuccess()方法,不要为银联单独写一套订单状态流转逻辑。
这样做的好处是:订单状态机的逻辑完全不受影响,新增渠道只是增加了一个“验签入口”。
场景二:订单支付成功后推送企业微信
错误做法:直接在OrderLogic::paySuccess()方法末尾加推送代码。
正确做法:
方案 A(事件驱动):在paySuccess()执行完成后,触发一个“订单支付成功”事件。新建一个事件监听类处理推送逻辑。核心 Logic 只负责触发事件,不关心谁监听。
方案 B(业务层组合):在调用OrderLogic::paySuccess()的 Controller 或上层 Logic 中,在调用成功后追加OrderExtendLogic::afterPaySuccess()。
两种方案都做到了核心逻辑不变,扩展逻辑独立。
七、避坑清单
坑一:一开始就大改结构。直接改核心代码的结果是后面升级很麻烦、代码越来越难维护。正确的做法是尽量做“扩展”,而不是“替换”。
坑二:把营销逻辑当成简单 if-else。营销逻辑复杂度容易被低估。当规则数量超过 5 个时,if-else 必然失控。建议先设计规则引擎,再写代码。
坑三:在核心 Logic 中直接操作状态字段。订单状态、支付状态的变更必须走OrderLogic中定义的状态迁移方法。状态机校验和日志记录都在这些方法中,绕过它们会导致状态追溯失效。
坑四:忽略多商户的数据隔离。二开新增的查询接口如果没有加shop_id过滤条件,会导致商户之间的数据泄露,这是多商户系统最严重的业务风险。
坑五:改动没有版本管理。LikeShop 官方会持续更新源码。如果不做 Git 管理,后续同步官方更新时很难处理冲突。建议以官方源码为上游仓库,自己的改动在独立分支上开发。
八、总结
LikeShop 二开规范的核心可以概括为四条原则的实践:
稳定内核——OrderLogic、PayLogic、UserLogic只调用不修改。
分层解耦——扩展逻辑写在业务层,不跨层耦合。
扩展优先——新增能力 > 修改原逻辑,组合扩展 > 覆盖替换。
数据驱动——用字段和配置支持变化,减少硬编码。
多商户版需要额外守住三条底线:商户数据隔离不能破、订单拆分核心逻辑不能动、结算归属不能错。
把这四条原则和三条底线记清楚,二开时就能做到“加功能而不破坏系统”——既满足业务需求,又保持系统的可升级性和可维护性。
本文基于 LikeShop 二次开发扩展能力白皮书及多商户版 整理,不同版本的扩展点和代码路径可能略有差异,请以实际源码和官方开发文档为准。