
简介WSTMart多商户商城系统是一套基于PHP开发的开源电商解决方案面向需要搭建多商家入驻平台的开发者、企业及电商创业者提供从商家入驻、商品发布到订单处理、支付集成等完整闭环。压缩包共包含1665个文件以529个php源码、198个html页面模板、135个js脚本、47个css样式以及125个sql数据库脚本为主另含图片、配置文件等压缩后仅4.77MB轻量易部署适合学习二次开发或快速搭建原型已有123人下载学习。解压后可直接获得源代码、数据库脚本、配置文件和安装指引开发者可基于系统提供的API与钩子机制扩展第三方支付、物流查询、营销活动等功能模板引擎也便于定制界面风格。多商户独立后台管理机制是核心亮点平台运营者可快速吸引商家入驻商家则能独立管理店铺同时保持统一的前端购物体验。整体而言这是一套成熟度高、扩展灵活的多商户商城基础工程能有效降低企业自研成本加速电商平台落地。1. 多商户商城选型为什么WSTMart值得你花两天跑通做多商户商城最怕的不是下单链路复杂而是选了一套从里到外都封闭的源码后续每改一个按钮都要靠作者施舍。WSTMart能成为PHP多商户商城里的常青树原因是它在“能用”和“能改”之间踩到了平衡点后台覆盖平台、商家、门店、运营四大角色前台带上营销、分销、结算的常见玩法而代码基于ThinkPHP组织目录清楚、钩子机制明确二次开发基本靠读源码就能推进。不用看完本文你也会发现围绕这套系统折腾一轮等于把“商户入驻、商品管理、订单结算”这条电商主干在本地完整走了一遍对新手是练手项目对团队是拿来即用的业务底座。下面我以部署视角把它按顺序拆开每一步都会给出命令、参数和失败时的判断方法。2. 本地跑通WSTMart环境准备、部署命令与目录结构三个卡点2.1 运行环境不要盲上最新版PHP先看资料文件里的版本约定WSTMart多数公开包基于ThinkPHP 5.x开发这意味着PHP版本不是越新越好。ThinkPHP 5.1官方对PHP 7.1支持友好但实际跑WSTMart时PHP 7.3到7.4最稳妥PHP 8.0以上容易出现类库兼容问题比如Mcrypt扩展移除、字符串函数行为变化导致验证码组件翻车。你手里如果是PHPStudy集成环境注意把它自带的PHP版本管理当成第一道关卡不要图省事用默认的PHP 8.x硬跑否则登录后台时可能连验证码图片都是黑匣子状态。提示先在php -v确认当前版本。若你已装有多个PHP版本推荐为WSTMart单独建一个PHP 7.4站点避免污染其他项目。数据库方面MySQL 5.7是常见基线MariaDB 10.2以上也大多能兼容。系统安装时会自动检测PHP环境是否满足要求但检测项偏宽松真正卡人的是fileinfo、curl、openssl这几个PHP扩展。部署命令、扩展检查手段和目录权限设置如下方代码块所示。# 进入站点目录假设你的Web根目录是 /www/wwwroot/wstmart cd /www/wwwroot/wstmart # 检查关键PHP扩展是否启用 php -m | grep -E fileinfo|curl|openssl|pdo|mbstring # 若fileinfo缺失Windows的PHPStudy在“设置-扩展”里勾选Linux用 apt install php7.4-fileinfo # 设置运行目录和runtime目录写权限 chmod -R 755 public chmod -R 755 runtime chmod -R 775 upload chmod -R 775 public/upload上面这段命令的关键在于chmod -R 755 public保证Nginx或Apache能读取静态资源runtime目录如果不可写ThinkPHP会直接抛“目录没有写入权限”的致命错误这个是部署WSTMart时出现频率最高的前置故障。upload和public/upload用于商家上传商品图、用户上传头像权限不足时表现是图片无法写入、上传返回-1或根本没有报错但目录为空。执行后可在浏览器访问http://你的域名/install进入安装引导。2.2 伪静态配置URL模式与Web服务器的三组规则WSTMart默认URL带index.php入口虽然能用但伪静态能带来两个实际收益一是后台菜单的跳转链接更简短二是在申请微信支付、对接H5时回调地址不用处理乱七八糟的index.php前缀。伪静态配置属于“不做也能跑、做了少踩坑”的典型项。Nginx环境常见写法是把下面的规则放进server块Apache环境则使用.htaccess并确认mod_rewrite已加载location / { index index.php index.html; if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } }这段规则的含义是当请求的路径在磁盘上不存在时!-e $request_filename把整条路径交给index.php?s$1处理由ThinkPHP的URL解析器按PATHINFO模式路由到控制器。这里容易翻车的是s参数的形式与系统配置不匹配。WSTMart后台的“设置-系统设置”里有一条URL模式配置常见选“伪静态”或“兼容模式”选完必须保证Nginx的rewrite写法与系统要求一致否则会出现前台能开、后台链接全部404的怪异局面。Apache环境请在站点根目录放入.htaccessOptions FollowSymlinks -Multiviews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?s$1 [QSA,PT,L]参数说明QSA表示保留原有查询参数PT把重写后的路径交给下一个处理器L停止本轮重写。若你的服务器在负载均衡后面别忘了在Nginx层配置proxy_set_header SCRIPT_NAME /index.php;否则后台登录状态经常掉线这也是很多团队把WSTMart从本地迁到云服务器时被折磨半天的原因。伪静态配置完成后记得重启Nginx再访问后台不要只用nginx -t通过就以为结束。2.3 看WSTMart目录结构从application到addons分清“核心”和“皮肤”部署完成后别急着进去配商城先把目录扫一遍。WSTMart的根目录下application是业务代码的主战场里面按模块拆分为admin平台后台、shop商家后台、home商城前台、api接口端、common公共模型。你后续改结算规则、增加商家等级权益绝大多数操作都落在application/common/model和application/shop/controller这两个层级里。public下主要是静态资源和上传目录addons是插件扩展位WSTMart的微信登录、分销、门店自提等功能有不少以插件形态驻留在这里。判断某个功能是核心内建还是插件扩展就看它代码放在application还是addons这个区分能避免你改错文件。thinkphp目录是框架本体除非升级框架需求否则不建议动。runtime是缓存与日志目录排错时优先看runtime/log下的日志文件很多神秘报错在日志里都有明确的行号。另外注意install目录在正式环境应删除或改名否则别人访问/install可能重装系统这是WSTMart部署后必须处理的第一个安全隐患。3. WSTMart商城核心配置从商品分类到订单结算的数据流转3.1 平台、商家、门店三层权限账号体系与数据隔离的落地路径WSTMart的权限设计可以概括为“平台管商家、商家管门店、门店管店员”。平台管理员在建站脚本里生成登录后台地址是/admin商家账号由平台在“商家管理”里添加或由商家自助入驻登录后台是/shop门店与店员是商家后台的内部数据店员登录后只能看到本门店的订单和待核销商品。这套三层结构的直接好处是订单数据天然带shop_id和store_id两个维度后续做结算单、对账单、员工业绩核算都不需要额外打标。我在给客户做二次开发时经常被问到“能不能让商家自己注册后直接开店”。WSTMart的入驻流程默认是商家提交资料、平台审核、审核通过后开通店铺这个动作在后台“商家管理-入驻审核”里处理。你可以控制是否开启“商家自助申请”路径在设置-商城设置-商家入驻设置。如果团队想让一部分内部商家绕过审核直接开店常见做法是把该商家的审核状态字段直接置为通过并手动初始化一个门店而不是改代码写死这样后期调整更灵活。3.2 商品分类体系平台分类与店铺分类的从属关系WSTMart的商品分类分为两层平台分类商城后台统一维护和店铺分类商家给自己的商品归堆。商品发布时商家先选平台分类再选店铺分类。这个设计的真实原因是平台端做首页聚合、搜索筛选时要按统一分类走而店铺端希望自己店内能按“上衣/裤子/配饰”再细分。二开时最容易踩的坑是去改goods表的goodsCatId字段含义结果平台分类和店铺分类混在一个ID空间里导致前台筛选结果凭空消失。正确的数据关系是goods_cats表中用catType区分分类层级平台分类作为根节点店铺分类挂在某个平台分类下。添加店铺分类时接口会同时写入parentId和shopId所以在二次开发中判断“某商品属于哪个店铺分类”需要用shopCatId判断“某商品属于哪个平台分类”才用goodsCatId。这套双字段设计是理解WSTMart商品数据结构的钥匙想改分类导航时不要只盯着一个字段。3.3 订单结算从下单支付到货款分账的完整链路订单结算是多商户系统比单商户复杂得多的核心原因。WSTMart的下单链路大致为用户在前台提交订单系统按店铺维度拆单同一个购物车如果包含多个商家的商品会生成多个店铺订单每个订单有自己的order_no、shop_id和支付状态。用户一次性合并支付后系统按订单维度分别标记已支付再进入商家结算池。结算规则涉及两个关键参数结算周期和结算比例。平台后台“结算管理”里可以设置商家货款是按周结、月结还是T1自动结算同时可以按商品类目设置平台抽佣比例。这个比例在商家后台的订单详情里会显示为“平台佣金”商家看到的“可提现金额”已经扣掉佣金。做报表二开时请认准settlement相关数据表和order_settlement字段不要自己去订单表里把pay_amount累加因为退款订单、部分退款订单和已结算订单都要做状态过滤直接累加出来的金额一定会和后台对不上。-- 查询指定商家已结算订单的实收总额含退款前数据仅作示意 SELECT shop_id, SUM(pay_amount) AS total_pay, SUM(platform_commission) AS total_commission, SUM(settlement_amount) AS total_settlement FROM order_settlement WHERE shop_id 15 AND settlement_status 1 GROUP BY shop_id;这段SQL里settlement_status1表示已进入结算流程platform_commission是平台佣金settlement_amount是商家实际获得的货款。三者的关系是settlement_amount pay_amount - platform_commission但要注意如果存在售后期内退款退款订单会把原结算单标记为异常所以报表统计时还要把refund_status加入条件。参数如何设取决于你统计的是“商家可提现余额”还是“平台GMV”两个指标的口径完全不同。3.4 营销与分销优惠券、拼团和分销等级的常见配置WSTMart内置的营销工具包括优惠券、满减、拼团、秒杀、分销等。上线前需要先弄明白这些工具的生效优先级否则会出现“优惠券和满减同时叠加导致商家利润为负”的事故。一般来说WSTMart的优惠计算优先级是商品促销价 会员等级折扣 优惠券 满减活动。修改这个顺序的代码集中在application/common/model/Orders.php的金额计算部分二开时建议先在本地写单元测试覆盖“同时参与多活动”的场景再上正式环境。分销体系方面WSTMart支持一级、二级分销佣金按订单实付金额的百分比计算。配置路径是后台“分销管理-分销设置”。这里最容易被忽略的是“分销关系绑定时机”用户第一次点击分销链接后才绑定上下级关系之前注册的用户不会被追溯。电商团队如果用这套系统做分销活动运营建议在活动页明确引导用户“先点链接再注册”否则大量自然流量涌入后分销关系混乱客诉处理成本极高。另外分销佣金的结算状态与订单结算绑定订单退款后已发放佣金要能自动追回——检查代码时会发现这逻辑在application/common/model/OrderRefund.php里如果自行改过退款流程记得连带测试这条链路。4. 二次开发与API对接行为钩子、路由改写与支付回调的通用改法4.1 用行为钩子向下单流程注入逻辑适合新手的第一处二开点WSTMart基于ThinkPHP的钩子机制保留了多处hook调用常见位置在订单生成后、支付成功后、发货后。这个机制让二开者不必直接改下单核心控制器减少和原版代码的冲突。以支付成功后给用户增加积分、给上级返佣场景为例你可以注册一个自定义行为类// application/common/behavior/AfterPay.php namespace app\common\behavior; class AfterPay { public function run($params) { // $params 里通常包含订单信息对象或订单ID $order $params[order]; // 给购买用户增加积分 $reward intval($order[pay_amount] * 0.01); // 按 1% 返积分 model(users)-where(userId, $order[user_id]) -setInc(points, $reward); // 记录一行积分日志方便前台展示和后台对账 model(user_points_log)-save([ user_id $order[user_id], points $reward, data_src order_pay, order_no $order[order_no], add_time time(), ]); return true; } }代码含义拆开看$params[order]是钩子传递的数据具体字段以系统基类传入参数为准model(users)是ThinkPHP的模型快捷方法setInc是自增操作避免“先查再改”的并发覆盖问题。user_points_log是常见的积分流水表写入日志是为了防止用户质疑“我明明买了东西为什么积分没变”时无法追溯。注册行为的位置在application/tags.php里追加return [ // 其他标签... order_pay_after [ app\\common\\behavior\\AfterPay, ], ];这里需要核实你的系统版本里支付成功标签的实际名称常见命名是order_pay_after拿到源码包后先全局搜一下hook(order_pay_after)确认标签存在再注册。注册完成后清空runtime/cache否则行为类不生效这个坑几乎每个第一次做WSTMart二开的人都会撞上。4.2 路由改写把商家店铺页变成不落index.php的静态路径WSTMart的店铺首页默认地址类似/index.php/shop/index.html?id2搜索引擎收录不友好。通过ThinkPHP的路由配置你可以把店铺页改写成/shop/2这样更短的路径。修改文件在application/route.php追加use think\Route; Route::get(shop/:id, shop/index/index); Route::get(shop/:id/articlegc/:gcid, shop/index/articlegc);第一个规则把/shop/2路由到店铺控制器id参数会被解析进请求第二个规则适合店铺内商品分类跳转。配置后必须同步检查模板里的跳转链接是否还是老格式否则SEO链接变了但站内所有入口还指向旧地址等于白改。常见做法是如果模板里大量使用url(shop/index/index, [id$shopId])你需要去application/common.php里搜索是否有统一的URL生成函数有则全局替换没有则考虑保留原样只在页面底部输出路由后的链接给爬虫。// 在模板里输出路由后的店铺链接时绕过默认url函数直接拼接 a href/shop/?php echo $shop[shopId]; ??php echo $shop[shopName]; ?/a这里注意伪静态规则是否与shop/:id路由冲突。Nginx将不存在的路径都交给index.php?s$1而ThinkPHP会对shop/2按路由表解析所以两者不冲突。但Apache的.htaccess如果是旧拷贝可能缺少QSA标记导致查询参数丢失这里最容易出的故障是店铺页能打开但店铺内点击“下一页”时分页参数丢失整页商品不加载。4.3 支付接口对接以微信支付为参照绕开回调验签的坑WSTMart后台的支付方式配置里通常已内置微信支付和支付宝支付的参数位但很多人对接时只填了appid和mch_id就以为完事。微信支付最容易被忽略的四个参数是API密钥、证书路径、回调地址和支付结果通知的URL白名单。如果你的回调地址带index.php在微信支付平台配置回调域名时务必包含完整格式否则微信服务器通知时会被签名校验拦截表现为用户付了款但订单状态不更新——这个是电商系统里最让人头疼的“钱到了账、订单还是待付款”事故。排查支付回调问题时不要只盯着前台页面先检查服务器日志里是否收到微信POST通知。参考排错脚本# 查看Nginx访问日志里来自微信支付回调的请求通常按微信官方IP段过滤 tail -f /www/wwwlogs/wstmart.log | grep -i weixin\|pay # 若日志有请求但应用没反应查看ThinkPHP运行日志 tail -f /www/wwwroot/wstmart/runtime/log/$(date \%Y\%m)/$(date \%d).log | grep -i pay日志能看到请求进来说明网络链路通看不到说明微信支付平台后台配置的回调地址与代码里notify_url不一致。如果能看到请求但验签失败最常出问题的概率点是API密钥填错、签名算法用了旧版MD5而非HMAC-SHA256、或者回调里XML解析时大小写不匹配。把这三个点逐项核对基本能解决九成支付回调问题。WSTMart这套支付逻辑是基于扩展包实现的升级PHP版本可能导致证书读取函数变化所以再次强调不要让支付相关环境里的PHP版本随意升级。5. WSTMart部署与二开的5条高发踩坑记录5.1 后台登录跳转死循环缓存与Session配置打架现象后台账号密码输对后页面一直弹回登录页甚至出现redirect次数过多。排查后发现是runtime目录残留缓存里的配置和当前会话存储方式不一致导致。解决先清空runtime/cache、runtime/temp同时检查application/config.php里session的type设置WSTMart在部分集成环境里推荐使用redis做会话保存若你本地只开了文件缓存却配置成redis就会出现登录状态存不住的情况。代码层面可以在config.php中把type改为File并把path指到runtime/session然后重启PHP服务即可。5.2 商家上传图片返回“上传失败”目录权限之外还可能是文件校验规则现象平台后台能传图商家后台一传就报错或者小图能传大图失败。原因通常有两层一是upload目录权限不足这个上面已经解决过二是PHP的upload_max_filesize和post_max_size限制默认2M的配置装不下商品主图。解决打开PHP配置文件修改upload_max_filesize 20M、post_max_size 50M并重启PHP-FPM。另外WSTMart后台“图片上传设置”里还有一套按角色限制大小的参数如果改完PHP配置仍报错去系统设置里把商家上传的图片大小调大。5.3 伪静态后访问商品详情404PATHINFO模式与Nginx rewrite规则冲突现象首页能开、店铺页能开点进商品详情就404而且静态资源CSS/JS全部加载失败。原因Nginx配置里没有正确加载PATHINFO支持或者站点配置文件开启了try_files把真实存在的静态文件路径也当路由解析。解决确认当前站点配置是否还有另一层location ~ \.php规则如果是把伪静态规则放在该规则之前再检查fastcgi_param PATH_INFO是否显式传入。调整后执行nginx -t service nginx reload。这个坑在LNMP一键包里尤其常见因为一键包默认内置了一套更严格的rewrite规则。5.4 商品列表页分页参数丢失路由配置里没有保留原有查询参数现象列表页第一页正常点击第二页时筛选条件全部丢失甚至跳到首页。原因上面提到的路由规则在生成链接时分页参数没有拼进去。解决在分页链接生成处手动拼接当前筛选参数比如/goods/lists/p/2.html?price100或者在路由规则中添加merge参数让系统自动合并当前请求的query。WSTMart的模板中使用分页函数时会自动带上query但如果你二开写了自定义的URL生成分页参数就得自己接住。5.5 后台菜单空白/接口返回500PHP版本对类库的兼容性差异现象同一套源码在A服务器完全正常移到B服务器后部分菜单点开是空白页接口返回500。原因B服务器PHP版本更高代码里用了在旧版本正常但在新版本废弃的语法比如PHP 8里把each()函数移除某些老插件还依赖它。解决尽量不要升级PHP版本如果团队统一用高版本可以先在本地开启display_errors查看具体报错再针对报错做兼容改写。这一类问题在引入第三方扩展插件时更明显比如某些物流查询插件依赖curl_file_create在老版本PHP里不存在。建议在需求评审时就把“能否支持当前PHP版本”列入插件选型的硬性门槛。6. 进阶技巧用行为扩展注入下单校验并验证你的改动不会拖垮高并发上了生产环境后最怕的是运营临时要加一条规则——比如“单笔订单超过5000元需要平台人工审核”。直接改下单控制器代码能实现但下次升级WSTMart补丁时你的改动可能在合并中被覆盖。正确做法是把这类校验放到行为钩子里既可以快速生效又能保持核心控制器与官方一致。在application/tags.php注册下单前校验行为然后在InitCheck.php里实现规则namespace app\common\behavior; class InitCheck { public function run($params) { // $params 是下单时的请求数据具体结构以实际传入为准 $orderAmount $params[total_money]; // 设定阈值超过则返回错误码让前端拦截订单 if ($orderAmount 5000) { return json([status -1, msg 该订单金额需平台审核请稍后再试]); } return true; } }这个写法的价值在于阈值可以随时随地改不需要重新发布代码行为类失败时不会中断整个流程而是由上游控制器决定是否终止下单。你可能会问直接在Orders.php里加个if判断不也一样差别在于行为钩子属于框架级的扩展点未来官方更新合并代码时冲突面最小而且多个业务规则可以并存按标签顺序依次执行。上线前别忘了验证钩子的执行顺序和异常返回格式比如这里返回json如果有APP端也在下单需要保证APP的接口层也能识别这个状态码。验证手段建议分两层。第一层是功能验证构造一笔5100元订单断点确认下单接口返回业务码第二层是压力测试用ab命令模拟并发下单重点观察数据库连接池和Redis会话在订单写入时的表现# 模拟100个并发每个并发10个请求仅示例正式环境建议用压测工具做更精细模型 ab -n 1000 -c 100 -T application/x-www-form-urlencoded -p postdata.txt http://你的域名/index.php/api/order/create-n是总请求数-c是并发数-p postdata.txt指向一个包含下单参数的表单数据文件。压测时要盯两个指标失败率错误请求数/总请求数和平均响应时间。如果发现大量“数据库连接数超限”报错就去检查MySQL的max_connections和ThinkPHP的数据库连接池配置如果响应时间陡增但数据库负载不高多半是Redis会话或缓存拖慢了流程。WSTMart这类系统在并发200左右时瓶颈通常在PHP-FPM进程数和MySQL连接数上先把这两处调优再考虑更复杂的队列削峰。最后说一个我的血泪教训每次做完行为扩展我都习惯把runtime缓存清空再逐个点一遍后台菜单否则测试时改了代码却总在跑旧缓存容易误判成“这次改动引入了故障”。把“清缓存看日志”养成肌肉记忆WSTMart二开的很多玄学问题都会变成可控的常规业务问题。希望这篇部署和改造笔记能帮你把这套多商户商城跑顺少走几段弯路。本文还有配套的精品资源点击获取