update docs

Signed-off-by: 唐永川 <tangyongchuan@shopex.cn>
This commit is contained in:
唐永川
2026-06-26 13:44:58 +00:00
committed by Gitee
parent 7092cf4d81
commit bef1e8fd50
18 changed files with 2987 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
---
url: deploy_prepare_appoint
---
# 约定
- 部署文档以源源客官方平台作为例子, 域名为: yuanyuanke.cn
-

View File

@@ -0,0 +1,44 @@
---
url: deploy_prepare_domain
---
# 域名
<a name="ff8ccd26"></a>
## SSL证书
> ssl证书, 对网站传输的数据可以进行SSL加密包括用户名密码等等防止窃取和篡改。并且网站安装SSL证书后在浏览器地址栏可以显示一个安全锁告诉用户你的网站是安全的。SSL证书是数字证书的一种类似于驾驶证、护照和营业执照的电子副本。因为配置在服务器上也称为SSL服务器证书。
> **ECShopX需要购买通配型ssl证书(泛域名型ssl证书).
**
<a name="f9439f4a"></a>
## 域名规划域名规划
为了表述方便, 以域名_b.yuanyuanke.cn_做例子.
- https://b.yuanyuanke.cn               api/商家后台域名
- https://h5.yuanyuanke.cn              h5访问域名
- https://pc.yuanyuanke.cn              pc访问域名
- https://b-import-cdn.yuanyuanke.cn    上传文件cdn域名
- https://b-cdn.yuanyuanke.cn           后台前端的相关 css js 资源cdn域名
- https://b-img-cdn.yuanyuanke.cn       图片cdn域名
<a name="FAQ"></a>
## FAQ
- 为什么必须要使用SSL证书
<br />其一SSL证书作为电商领域保证客户信息安全的重要手段. 其二微信小程序 公众号 苹果商店等对于安全性的要求越来越高, 微信小程序是必须SSL才可以, 其它场景也陆续增加限制. 其三, C端用户对于安全的意识也越来越强, 有些客户对于没有小锁头的电商网站有抵触.
- 是否可以选择免费的SSL?
<br />尽量避免使用免费的SSL, 免费的SSL通常不支持**通配符型SSL证书**/**泛域名SSL证书**, 本文档相关部署以支持_通配符型SSL证书_为准.
- 对于收费的SSL证书应该如何选择?
- 云盾证书服务
- SSL去哪里买?
<br />主流的云供应商都有, 阿里的云盾
<a name="bKVjD"></a>
##

View File

@@ -0,0 +1,35 @@
---
url: deploy_prepare_domain_record
---
# 域名备案
根据 《互联网信息服务管理办法》 以及 《非经营性互联网信息服务备案管理办法》 ,国家对非经营性互联网信息服务实行备案制度,对经营性互联网信息服务实行许可制度。未取得许可或者未履行备案手续的,不得从事互联网信息服务。即所有对中国大陆提供服务的网站都必须先进行 ICP 备案才可开通服务。阿里云ICP代备案系统为您提供申请备案、修改注销备案信息、认领备案等功能。
> **如果由商派提供采购阿里云服务器, 可以选用我们的备案服务.**<br />
<a name="61fe3c73"></a>
## 阿里云备案
[阿里云备案](https://beian.aliyun.com)
<a name="eebf8b79"></a>
## 腾讯云备案
[腾讯云备案](https://cloud.tencent.com/product/ba)
<a name="a2e173ea"></a>
## 微软云(中国)
[微软云备案](https://www.azure.cn/zh-cn/support/icp/icp-new/)
<a name="50cca03c"></a>
## 华为云备案
[华为云备案](https://support.huaweicloud.com/pi-icp/zh-cn_topic_0115820080.html)
<a name="faf6cf75"></a>
## 网易云备案
[网易云备案](https://www.163yun.com/help/documents/15588222030344192)

View File

@@ -0,0 +1,256 @@
---
url: deploy_prepare_implementation
---
# ECShopX初始化
> **本文档引自ECShopX操作手册. 前半部分为数据初始化及上线前准备工作, 因此罗列在此处.**
>
> **目前本文档仅有Word版, 如需要可向售后人员索要.**
<a name="OobJ2"></a>
### 开通EcshopX
<a name="7t47E"></a>
### 注册开放平台以及创建第三方平台
<a name="ee3c7775"></a>
### 申请服务号并且认证
> 不能是订阅号
<a name="C0hNt"></a>
### 申请小程序并且认证
> 可以通过服务号快速申请小程序,这样可以避免主体不一致带来的麻烦
<a name="JgdDj"></a>
### 申请微信支付
> 小程序支付、JSAPI支付、h5支付
> 按您的业务需求申请,如果只有小程序则只需要开通小程序支付即可
> 在接入微信支付过程中会出现APPID、MCH_ID、公众平台、开放平台、商户平台等概念下面仅从微信支付的角度来做简单分析
>
> ![](../assets/1599795958725-a6092948-c05e-43af-adac-56b41a67bae4.png)
>
> **● 公众平台([mp.weixin.qq.com](https://mp.weixin.qq.com/)**注册、配置服务号、订阅号、小程序的入口注册成功后系统就会下发一个与之一一对应的APPID其中订阅号的APPID不支持申请和使用微信支付
>
> **● 商户平台([ pay.weixin.qq.com](https://pay.weixin.qq.com/)**微信支付业务管理中心商户可以在商户平台进行所有支付业务相关操作例如退款、下载对账单、查询订单、提现、账号绑定、API证书下载、API密钥设置、查看证书序列号等操作。
>
> **● 开放平台([open.weixin.qq.com](https://open.weixin.qq.com/)**注册、配置APP移动应用、网站应用的入口注册成功后系统就会下发一个与之一一对应的APPID。
>
> **● APPID**在公众平台或开放平台申请注册之后由平台下发,在支付接口中通常作为配置参数,必须上传。
>
> **● MCH_ID**在公众平台、开放平台申请微信支付成功后由微信支付下发或者直接在商户平台注册也可获得MCH_ID在支付接口中通常作为配置参数必须上传。
>
> 注意: 支付接口要求APPID与MCH_ID必须有绑定关系在商户平台注册获得的MCH_ID需要在【商户平台—>产品中心—>APPID授权管理】菜单下与APPID进行绑定后方可使用。
<a name="f88sS"></a>
###
<a name="UlRPA"></a>
### ECShopX授权绑定服务号
> 管理员才能授权
<a name="MhZNP"></a>
### ECShopX授权绑定小程序
> 管理员才能授权
<br />
<a name="rX3lg"></a>
### 小程序模板开发
> 与开发普通小程序一致,开发者在开发工具上开发好相关的业务逻辑之后,在项目页卡中提交预览即可以在微信中查看小程序的真实表现。
> 有所不同的是,第三方平台小程序的提交上传是上传至该第三方平台的 open 帐号下的模板草稿箱中,该平台的管理员需要自行对该模板进行相应的设置,更多请参考 [开放平台的文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489144594_DhNoV&token=&lang=zh_CN) 。
<a name="dv3WC"></a>
### 上架小程序须知
> 第三方平台帮助旗下已授权的小程序进行代码管理时,需先开发完成小程序模板,再将小程序模板部署到旗下小程序帐号中,具体流程如下:
> **第一步:绑定开发小程序**
> 1第三方平台的开发人员需先到微信公众平台mp.weixin.qq.com申请一个普通的小程序并完善小程序的头像、昵称、简介、服务类目等信息。
> 2进入微信开放平台在第三方平台详情中将该小程序添加为**开发小程序**。
> **注意:** 绑定为开发小程序后,该小程序的在开发工具中上传,代码会直接上传到开放平台,不会上传到公众平台。
> **第二步:小程序模板的开发和上传**
> 使用开发小程序的开发者微信号登录[微信开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html),开发者工具中按照正常的小程序开发流程进行代码开发和调试。开发完成后,在开发工具中点击上传。使用详见:[**第三方平台代开发小程序**](https://developers.weixin.qq.com/miniprogram/dev/devtools/ext.html)
> **第三步:添加到小程序模板库,获得模板 ID**
> 从开发者工具中上传的代码会先存在草稿箱中每个开发小程序只保留最新一份上传记录。开发者可将草稿箱中的代码添加到小程序模板库中小程序模板库中的模板不会被覆盖。最多可以有200个代码模板添加后可以获得模板 IDTemplateID
> **第四步:调用接口,为旗下授权的小程序部署代码**
> 具体接口详见“代码管理”文档中的接口。
> **重点提示:**
> 小程序授权托管之后,只能使用第三方平台的在微信开放平台登记的服务器地址。所以第三方平台在帮助旗下公众号发布代码之前,需先把服务器地址设置到小程序的服务器地址中,设置接口详见“修改服务器地址”文档中的接口。
<!--
<a name="ccf3d27d"></a>
## 微信支付配置
<a name="0aeca07a"></a>
## 基础设置
<a name="818c8a91"></a>
### 新增运费模版
<a name="5782b6ea"></a>
### 商品管理
<a name="b56f8d93"></a>
#### 新增商品品牌
<a name="8dca0e3f"></a>
#### 新增商品规格
<a name="8686bbbf"></a>
#### 商品参数
<a name="7b40f744"></a>
#### 商品主目录
<a name="aa3736f4"></a>
##### 商品类目关联规格、参数
<a name="ea4da67d"></a>
##### 关联规格
<a name="804a1f6b"></a>
##### 关联参数
<a name="41be0e36"></a>
#### 新增商品分类
<a name="23b8e6c3"></a>
#### 商品录入
<a name="b944dbc4"></a>
#### 商品会员价
<a name="ba7290ac"></a>
#### 批量处理
<a name="f2827c2c"></a>
## 会员卡
<a name="7b1bd4f7"></a>
### 普通等级
<a name="27cbae87"></a>
### 付费等级
<a name="21352a1f"></a>
### 付费等级会员卡延期
<a name="e9cc0a19"></a>
### 会员导出
<a name="2f36359e"></a>
## 优惠券
<a name="9268f91e"></a>
### 折扣券
<a name="607c87ab"></a>
### 代金券
<a name="8bc752db"></a>
### 兑换券
<a name="b017e964"></a>
## 会员营销
<a name="3cc026e1"></a>
### 会员标签
<a name="dfa3625e"></a>
### 群发优惠券
<a name="43e10b7a"></a>
### 群发短信
<a name="66833906"></a>
### 新客营销
<a name="91fa9bc1"></a>
#### 赠送优惠券
<a name="811077e4"></a>
#### 赠送付费会员卡
<a name="1f53fac2"></a>
#### 赠送积分
<a name="57605093"></a>
## 商城小程序模版装修
<a name="95456b09"></a>
## 客服设置 -->

View File

@@ -0,0 +1,7 @@
---
url: deploy_prepare_lbs
---
# 腾讯位置服务
[接入步骤](https://lbs.qq.com/guides/startup.html)

View File

@@ -0,0 +1,17 @@
---
url: deploy_prepare_license
---
# 域名备案
如果由商派提供采购阿里云服务器, 可以选用我们的备案服务.
<a name="61fe3c73"></a>
## 阿里云备案
[阿里云备案](https://beian.aliyun.com)
<a name="eebf8b79"></a>
## 腾讯云备案
[腾讯云备案](https://cloud.tencent.com/product/ba)

View File

@@ -0,0 +1,168 @@
---
url: deploy_prepare_open_weixin
---
# 微信开放平台第三方平台申请
<a name="b66b8eaa"></a>
## 注册微信开放平台账号
<br />[注册微信平台注册](https://open.weixin.qq.com/cgi-bin/readtemplate?t=regist/regist_tmpl&lang=zh_CN)<br />
<a name="d7b8487d"></a>
## 开发者资质认证
<br />[第三方平台申请流程](https://open.weixin.qq.com/cgi-bin/frame?t=home/wx_plugin_tmpl&lang=zh_CN)<br />
<br />![open_weixin_flow.png](../assets/1584343626626-7d4018ea-489a-4240-b918-e59057c13c7b.png)<br />
<br />点击_`开发者资质认证`_, 并进行认证.<br />
> 开发者资质认证介绍
> 微信开放平台帐号的开发者资质认证提供更安全、更严格的真实性认证、也能够更好的保护企业及用户的合法权益<br />开发者资质认证通过后,微信开放平台帐号下的应用,将获得微信登录、智能接口、第三方平台开发等高级能力<br />审核费用中国大陆地区300元非中国大陆地区99美元
<br />认证时间大概2-3天, 已微信实际认证时间为准<br />
<a name="bcce70b7"></a>
## 创建第三方平台
<br />管理中心->第三方平台<br />
<br />![open_third_platform.png](../assets/1584343649169-0b36319a-545c-4150-855b-7fd915ecd521.png)<br />
<br />点击_`创建第三方平台`_<br />
<a name="a2890803"></a>
## 输入基本信息
<br />平台类型选择_**`平台型服务商`**_<br />
<br />![open_third_platform_create_1.png](../assets/1584343669051-3bc5c06e-af75-4353-941f-0dd327ba05ce.png)<br />
<a name="b17be281"></a>
## 选择权限
<br />公众号权限<br />
<br />![open_third_platform_create_2_1.png](../assets/1584343692332-dd5950e1-5f56-46b7-ba67-1413dd763bb1.png)<br />![open_third_platform_create_2_2.png](../assets/1584343704222-2b3df860-3813-4fc5-9c5c-3d300b33bbf5.png)<br />
<br />小程序权限<br />
<br />![image.png](../assets/1599635273983-376e0426-6436-4e17-a429-89fceca249b5.png)<br />
<a name="eeac4e56"></a>
## 填写开发资料
<br />为了表述方便, 下文将以主域名_yuanyuanke.cn_为例。实际部署时根据您的域名进行修改并且您的域名是要https的。<br />
<a name="A3eVR"></a>
#### 授权流程相关
- 登录授权的发起页域名:_ b.yuanyuanke.cn_
> 必须从本域名内网页跳转到登录授权页,才可完成登录授权。无需填写`https://等域名协议前缀`。
- 授权测试公众号列表: gh_************
> 在全网发布之前,仅该列表内公众号才可进行授权(包含测试小程序)以便测试。请填写公众号的原始ID可在公众平台网站的公众号设置页找到最多10个以英文“;”隔开。
- 授权事件接收URL: _b.yuanyuanke.cn/wechatAuth/events_
> 用于接收取消授权通知、授权成功通知、授权更新通知也用于接收ticketticket是验证平台方的重要凭据。
> 并且选择https协议头。
<a name="QGqHD"></a>
#### 授权后实现业务
- 消息校验Token: qwn2891ktj024
> 开发者在代替公众号或小程序接收到消息时用此Token来校验消息。可以自定义。
> **消息校验Token前后, 绝不能有空格**
- 消息加解密Key: sklfjkfgeiawwwqvn65997wlj01ndnfoqoe8x2k0nck
> 在代替公众号或小程序收发消息过程中使用。必须是长度为43位的字符串只能是字母和数字。可以自定义。
> **消息加解密Key前后, 绝不能有空格. 长度必须43位<br />**
- 消息与事件接收URL: _b.yuanyuanke.cn/wechatAuth/callback/$APPID$_
> 通过该URL接收公众号或小程序消息和事件推送该参数按规则填写需包含/$APPID$如www.abc.com/$APPID$/callback实际接收消息时$APPID$将被替换为公众号或小程序AppId。
- 公众号开发域名: _b.yuanyuanke.cn_
> 第三方平台在代公众号做网页授权、调用JS SDK等网页开发工作时所用的域名以;隔开。为了满足开发者管理需要,符合以下要求的下级域名也将生效:$APPID$.wx.abc.com$APPID$为公众号的AppID的替换符<br />请**下载校验文件**并将文件放置在域名根目录下例如wx.qq.com并确保可以访问该文件。<br />每月可提交修改申请3次本月有3次机会。不需要填写https协议头。
- 小程序服务器域名: _b.yuanyuanke.cn;b-websocket.yuanyuanke.cn;mmbiz.qpic.cn;wx.qlogo.cn;b-img-cdn.yuanyuanke.cn;up.qiniup.com;up-z1.qiniup.com;up-z2.qiniup.com;up-na0.qiniup.com;up-as0.qiniup.com;thirdwx.qlogo.cn_
> 第三方平台旗下授权的小程序,只可配置本平台服务器域名列表中的域名,以;隔开。<br />每月可提交修改申请3次本月有2次机会。
> **需要访问的小程序服务器域名, 同时需要后端接口b.yuanyuanke.cn 和 websocket服务: b-websocket.yuanyuanke.cn。这里主要就是您的网站域名其他还有图片相关域名(七牛阿里OSS的相关域名原始域名或者cdn域名)websocket域名(如果用到),微信官方素材域名**
- 小程序业务域名: **无需填写**
> 请**下载校验文件**并将文件放置在域名根目录下例如wx.qq.com并确保可以访问该文件。<br />每月可提交修改申请3次本月有3次机会
<a name="VVMkM"></a>
#### 其他
- 白名单IP地址列表: 47.115.75.92;
> 仅当开发者IP地址在该列表中时才被允许调用相关接口。最多填写100个IP地址以英文“;”隔开。
> **服务器出口ip地址, 如果是vpc. 否则访问微信接口会被拒**
**
<a name="z2igl"></a>
#### 开发资料综合截图
![image.png](../assets/1599634362838-5ff39c5c-ea0a-418a-b73d-0a0c62c7ce03.png)<br />
<a name="19401cf2"></a>
## 获取APPSECRET
<br />获取appsecret(微信开放平台管理中心的第三方平台详情页中的AppID和AppSecret)。<br />示例:<br />![image.png](../assets/1599635790626-92d05a28-49a6-475a-9e69-fc003032e51b.png)
<a name="7e548143"></a>
## 绑定开发者小程序
<br />微信开放平台管理中心的**第三方平台详情页**的**开发配置**里添加开发小程序。<br />同时在对应的小程序**添加开发者**。<br />**建议开发小程序和您的实际授权小程序用同一个**,避免一些异常问题。<br />示例:<br />![image.png](../assets/1599634524957-1d5f3f64-c7b8-4d03-af9c-5b9c10729448.png)
<a name="c07f7d53"></a>
## 开放平台绑定小程序
<br />微信开放平台管理中心的小程序中去绑定小程序。用于保证unionid一致。<br />![image.png](../assets/1599634689598-ab320e8e-d646-4371-adbb-f1725d5d146b.png)

View File

@@ -0,0 +1,64 @@
---
url: deploy_prepare_qiniu
---
# 七牛
<a name="b59c9e0f"></a>
## 概念
<a name="cdn"></a>
### cdn
> CDN的全称是Content Delivery Network即内容分发网络。CDN是构建在网络之上的内容分发网络依靠部署在各地的边缘服务器通过中心平台的负载均衡、内容分发、调度等功能模块使用户就近获取所需内容降低网络拥塞提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
<a name="9e236b4b"></a>
### 开放存储服务 OSS
> 开放存储服务OpenStorageService简称OSS是云对外提供的海量安全低成本高可靠的云存储服务。用户可以通过简单的APIREST方式的接口在任何时间、任何地点、任何互联网设备上进行数据上传和下载 保存js, ccs, 图片等静态资源
<a name="3deda530"></a>
### 七牛云
[七牛云](https://www.qiniu.com)
> 七牛云是国内领先的企业级公有云服务商致力于打造以数据为核心的场景化PaaS服务。围绕富媒体场景七牛先后推出了对象存储融合CDN加速数据通用处理内容反垃圾服务以及直播云服务等。目前七牛云已经在为 50多万家企业提供服务, 亲历互联网创新创业发展的同时,也深入理解传统企业转型过程中的云服务需求场景,推出了有针对性的一系列行业解决方案
<a name="f947a646"></a>
## 创建存储空间
<a name="espier-vue"></a>
### espier-vue
用来存储前端的 css js 及静态图片等
- 存储空间(bucket): espier-vue
- 融合 CDN 加速域名: [https://b-cdn.yuanyuanke.cn](https://b-cdn.yuanyuanke.cn)
- 访问控制:  公开空间
<a name="espier-import-files"></a>
### espier-import-files
用来存储系统导入 导出的相关数据
- 存储空间(bucket): espier-import-files
- 融合 CDN 加速域名: [https://b-import-cdn.yuanyuanke.cn](https://b-import-cdn.yuanyuanke.cn)
- 访问控制:  私有空间
<a name="espier-images"></a>
### espier-images
用来存储商品图片, video等资源
- 存储空间(bucket): espier-images
- 融合 CDN 加速域名: [https://b-img-cdn.yuanyuanke.cn](https://b-img-cdn.yuanyuanke.cn)
- 访问控制:  公开空间
<a name="FAQ"></a>
## FAQ
- 怎么获取或者找到 Access Key 和 Secret Key
<br />[怎么获取或者找到 Access Key 和 Secret Key](https://developer.qiniu.com/af/kb/1479/how-to-access-or-locate-the-access-key-and-secret-key)

View File

@@ -0,0 +1,14 @@
---
url: deploy_prepare_readme
---
# README.md
---
<a name="fb3745ce"></a>
## description: Shortcut keys allows an easy and quick method for navigating or editing con
<a name="88210852"></a>
# 准备工作

View File

@@ -0,0 +1,120 @@
---
url: deploy_prepare_solution
---
# 服务器方案
<a name="4c5c2fdb"></a>
## 独立部署服务器方案
![deploy.jpg](../assets/1584344156612-d48bb04b-5f44-49e7-8cd4-941abc03a9be.jpeg)
- 角色定义
- 负载均衡: Load Balance
- Web服务:<br />
API服务
- 队列:<br />
可使用redis/rabbitmq
- scheduler:<br />
定时任务, 例如凌晨1点产生统计任务, 加入队列.
- job:<br />
处理队列中的任务
- neo4j:<br />
图数据库
- redis:<br />
Key-Value数据库
- OSS:<br />
对象存储, 存储文件/图片/视频
- CDN:<br />
CDN的全称是Content Delivery Network即内容分发网络
<a name="86e79fca"></a>
### 声明
- 本文方案所指皆为以阿里云, 其它云方方案可以此为基准.
- 数据库建议使用RDS. 如果不使用RDS, 请做好备份方案, 以免误操作造成数据丢失.
<a name="03b54f13"></a>
### 入门级方案
- ECS1台 4核8g 100g高速硬盘<br />
部署 redis/neo4j/ecshopX(web/Job/scheduler)
- RDS 2核4g
- 七牛云 cdn 及 图片
方案简描述:
```
入门级方案, 不考虑高可用, 仅作为业务刚刚开始, 初始量级不大的情况下.
```
<a name="70c4464b"></a>
### 标准方案
![deploy_3.jpg](../assets/1584344192904-4878b26e-b21c-46e0-9d55-bbfcc50106e4.jpeg)
- 负载均衡
- ECS3台 4核8g 100g高速硬盘
- Web服务: ecs*2
- 定时任务/job/redis/neo4j ecs*1
- RDS: 2核4g
方案简描述:
```
优点
Web端提供两台服务器做负载均衡, 保证高可用. 队列任务分开部署, 将可异步处理的业务从前端业务中剥离, 后端的任务处理不影响API业务正常的相应. 一旦业务发生瓶颈, 可以方便增加Web机的数量, 提高RDS的配置. 以快速相应业务.
缺点
任务机上部署了过多的服务, 当任务服务器负载过重时, 快速扩展不易.
```
<a name="72c5a326"></a>
### 进阶方案(根据业务侧重点调整方案配置)
- 负载均衡
- ecs 4核8g 40g ssd硬盘 * 6
- Web: ecs * 3
- Job: ecs * 2
- Scheduler/redis/neo4j: ecs * 1
- RDS 2核4g 100g * 1 集群版 便于扩展主从
- 七牛云 cdn 及 图片
- 日志服务
<a name="422d97da"></a>
### Kubernetes方案
![kubernetes_deploy.jpg](../assets/1584344218270-4d439a3a-4baa-48c9-957c-7c3df981f546.jpeg)
- kubernetes 主节点: ecs 2核4g * 3
- kubernetes node 工作节点: ecs 4核8g * 3
- RDS 2核4g 100g * 1 集群版 便于扩展主从
<a name="fc9f6ae1"></a>
## 私有云方案(kubernetes)
服务器托管在商派集群. 使用kubernetes集群方案.
<a name="f33c8e41"></a>
### 入门方案(默认)
- 共享负载均衡
- 5核5g 容器资源
- RDS 2核4g 100g * 1
- 七牛云 cdn 及 图片
<a name="70c4464b-1"></a>
### 标准方案
- 独享负载均衡
- ECS 4核8g 100g高速硬盘 * 2 (独享)
- RDS 2核4g
- 七牛云 cdn 及 图片
<a name="72c5a326-1"></a>
### 进阶方案(根据业务侧重点调整方案配置)
- 独享负载均衡
- ECS 4核8g 100g高速硬盘 * 6 (独享)
- RDS 2核4g 100g * 1 集群版 便于扩展主从
- 七牛云 cdn 及 图片

View File

@@ -0,0 +1,77 @@
---
url: deploy_prepare_wxa
---
# 小程序
> **本文档引自ECShopX操作手册. 前半部分为数据初始化及上线前准备工作, 因此罗列在此处.**<br />
> **需确认好小程序的**_**管理员**_**。****后续很多其它操作员的操作都需要管理员微信扫码。例如添加开发者,小程序授权等**
<a name="Gz6C1"></a>
#### 申请小程序
> [https://kf.qq.com/faq/170109iQBJ3Q170109JbQfiu.html](https://kf.qq.com/faq/170109iQBJ3Q170109JbQfiu.html)
>
> 如果您有服务号,建议直接从服务号后台走快速注册并认证小程序,避免主体搞错的情况发生
> ![image.png](../assets/1599637547874-89d12ef5-e2a2-45c1-8cb1-057cf523d291.png)
<a name="PWa57"></a>
####
<a name="DDDy1"></a>
#### 小程序认证
> [https://kf.qq.com/faq/170109F7ZVzq170109MnQRNN.html](https://kf.qq.com/faq/170109F7ZVzq170109MnQRNN.html)
<a name="UYJzk"></a>
####
<a name="hqkyd"></a>
#### 添加开发者或者体验者
> 登录小程序后。开发的时候都是在小程序开发者工具中进行开发,所以必须要开发者权限才能运行。体验成员则可以看小程序体验版。
> ![image.png](../assets/1599636099208-c2abcc21-fd52-4706-a6b6-4a567cf50da5.png)
<a name="R0WSY"></a>
#### 获取小程序的APPID(小程序ID)原始ID
> - 登录小程序后,点 _设置->基本设置_
>
_![image.png](../assets/1599636771216-5703d789-921f-46d3-9479-d64efd3817df.png)_
> - 拉到页面底部
>
![image.png](../assets/1599636892509-a7648d02-e44e-47b0-a056-8690548be4fd.png)
<a name="rX3lg"></a>
### 小程序模板开发
> 与开发普通小程序一致,开发者在开发工具上开发好相关的业务逻辑之后,在项目页卡中提交预览即可以在微信中查看小程序的真实表现。
> 有所不同的是,第三方平台小程序的提交上传是上传至该第三方平台的 open 帐号下的模板草稿箱中,该平台的管理员需要自行对该模板进行相应的设置,更多请参考 [开放平台的文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489144594_DhNoV&token=&lang=zh_CN) 。
<a name="dv3WC"></a>
### 上架小程序须知
> 第三方平台帮助旗下已授权的小程序进行代码管理时,需先开发完成小程序模板,再将小程序模板部署到旗下小程序帐号中,具体流程如下:
> **第一步:绑定开发小程序**
> 1第三方平台的开发人员需先到微信公众平台mp.weixin.qq.com申请一个普通的小程序并完善小程序的头像、昵称、简介、服务类目等信息。
> 2进入微信开放平台在第三方平台详情中将该小程序添加为**开发小程序**。
> **注意:** 绑定为开发小程序后,该小程序的在开发工具中上传,代码会直接上传到开放平台,不会上传到公众平台。
> **第二步:小程序模板的开发和上传**
> 使用开发小程序的开发者微信号登录[微信开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html),开发者工具中按照正常的小程序开发流程进行代码开发和调试。开发完成后,在开发工具中点击上传。使用详见:[**第三方平台代开发小程序**](https://developers.weixin.qq.com/miniprogram/dev/devtools/ext.html)
> **第三步:添加到小程序模板库,获得模板 ID**
> 从开发者工具中上传的代码会先存在草稿箱中每个开发小程序只保留最新一份上传记录。开发者可将草稿箱中的代码添加到小程序模板库中小程序模板库中的模板不会被覆盖。最多可以有200个代码模板添加后可以获得模板 IDTemplateID
> **第四步:调用接口,为旗下授权的小程序部署代码**
> 具体接口详见“代码管理”文档中的接口。
> **重点提示:**
> 小程序授权托管之后,只能使用第三方平台的在微信开放平台登记的服务器地址。所以第三方平台在帮助旗下公众号发布代码之前,需先把服务器地址设置到小程序的服务器地址中,设置接口详见“修改服务器地址”文档中的接口。

6
docs/docs/dicucx.md Normal file
View File

@@ -0,0 +1,6 @@
---
url: dicucx
---
# php的readme

1343
docs/docs/el5sfv.md Normal file

File diff suppressed because it is too large Load Diff

21
docs/docs/fe4lti.md Normal file
View File

@@ -0,0 +1,21 @@
---
url: fe4lti
---
# 架构说明
<a name="cTwYb"></a>
## 软件架构
后端开发语言PHP 8.2<br />后端技术框架:采用 Lumen 开源框架开发引入dingo作为接口管理采用更符合大型项目的 Doctrine ORM 替换系统自带 ORM<br />前端开发语言HTML5/CSS3/ES6/小程序开发框架<br />前端开发框架VueJS,Taro<br />数据库:支持 MySQL/MariaDB/SqlServer等数据库<br />队列:支持 Redis/RabbitMQ/数据库 等方案,默认采用 Redis<br />缓存:支持 Redis/filesystem/memcached默认采用 Redis<br />CDN/OSS支持阿里云/七牛云<br />图形数据库Neo4j可选<br />日志搜集ELK/Sentry
<a name="SoaeN"></a>
## 数据处理能力
支持 MySQL 读写分离模式支持阿里云PolarDB。<br />在 5 机集群下支持 2000 每秒并发,其中订单占比 1%,且根据实际业务需求扩容<br />采用多级缓存机制可以有效保证API响应时间在 2S 以内<br />系统具备按照终端,店铺,门店等权限控制<br />
<a name="igNAZ"></a>
## 稳定性
系统支持集群模式部署,可按照 web/schedule/worker 等不同角色部署,可有效保证系统高可用<br />支持阿里云 K8S 部署可以通过HPA实现 POD 自动扩容,可有效保证在突发流量下,系统的高可用<br />采用事件机制,可以异步处理耗时业务逻辑,保证实时业务稳定性<br />采用多级缓存机制nginx+memcache/php+redis 方式在redis数据库宕机是可由 nginx 缓存来替代,保证系统高可用。
<a name="Qk1if"></a>
## 持续集成
系统采用 PHP 开发,支持二次开发<br />具备完善的开发环境docker-compose和测试环境构建脚本可以快速搭建环境<br />采用基于 GitLab-CI/K8S/Helm/Helmfile 的持续集成方案

51
docs/docs/fiytfx.md Normal file
View File

@@ -0,0 +1,51 @@
---
url: fiytfx
---
# 阿里云
进入 [https://oss.console.aliyun.com/bucket](https://oss.console.aliyun.com/bucket) 创建bucket。<br />
<a name="h29pS"></a>
## 创建 espier-image 和 espier-vue公共读
<a name="UTQO7"></a>
### 一、创建 Bucket
![image.png](../assets/1584441196616-99d22e5b-b0a0-4571-aa7e-45ee632284cd.png)<br />
<a name="tNULn"></a>
### 二、填写详细信息
<br />![image.png](../assets/1584441319048-6c6a3e75-bba8-495a-aed9-3d14aaed031e.png)<br />
<a name="Zv52U"></a>
### 三、进入详情页面配置
<br />![image.png](../assets/1584441452079-64e680e1-a70a-4e52-b715-ac5d78a8f08f.png)<br />
<a name="jUZQ5"></a>
### 四、创建跨域规则
<br />![image.png](../assets/1584441540025-4d88ba3c-d65c-43d0-8997-170fdb7e18fc.png)<br />
<br />创建完成如下:<br />
<br />![image.png](../assets/1584441567872-a8004b67-9ef7-43d1-b155-291656df2a5e.png)<br />
<a name="Ytp9Z"></a>
### 五、配置 CDN
进入 cdn 管理界面 [https://cdn.console.aliyun.com/domain/list](https://cdn.console.aliyun.com/domain/list)<br />
<br />![image.png](../assets/1584441906864-3f87ef16-b4c2-40ee-a941-229fc417bd72.png)
<a name="k69o2"></a>
### 六、域名解析
CDN添加完成后会在域名管理界面生成相应的CNAME然后在域名解析后台添加相应的解析<br />![图片.png](../assets/1590482695375-bb8fce09-dd9b-4702-8f84-1c93a926fdc8.png)<br />
<a name="ZHeb0"></a>
## 创建 espier-import-files (私有)
<a name="2nG6V"></a>
### 一、创建
![image.png](../assets/1584442028777-2124d276-52a2-4b24-9a92-2afd4081c3e0.png)<br />
> 私有存储不需要配置 cdn 和跨域

View File

@@ -0,0 +1,339 @@
---
url: framework_architecture-repository
---
# Repository
> **使用 Repository 辅助 Model**<br />
若将数据库逻辑都写在 model会造成 model 的肥大而难以维护,基于 SOLID 原则,我们应该使用 Repository 模式辅助 model将相关的数据库逻辑封装在不同的 repository方便中大型项目的维护。
<a name="c101406b"></a>
## 数据库逻辑
在 CRUD 中CUD 比较稳定,但 R 的部分则千变万化大部分的数据库逻辑都在描述R 的部分,若将数据库逻辑写在 controller 或 model 都不适当,会造成 controller 与 model 肥大,造成日后难以维护。
<a name="Model"></a>
## Model
如果使用 Eloquent ORM 来实现 Repository 模式,需要修改 model 不要包含数据库逻辑,仅保留以下部分:
- Property : 如$table$fillable…等。
- Mutator: 包括 mutator 與 accessor。
- Method : relation 類的 method如使用 hasMany() 與 belongsTo()。
简化后的Eloquent 的 model 类我们可以称之为`Eloquent Class`,由于继承了`Illuminate\Database\Eloquent\Model` 类,所以`Eloquent Class`仍然有很多方法可以操作数据库。
Eloquent Class 代码结构如下:
```php
namespace MyBlog;
use Illuminate\Database\Eloquent\Model;
/**
* MyBlog\User
*
*/
class User extends Model {
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name', 'email', 'password'];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = ['password', 'remember_token'];
}
```
而在 ECShopX 中,由于我们采用了 Doctrine ORM其自带Repository模式。在 Doctrine ORM 中也有简化的 model 类: `Entity Class`
相比 `Eloquent Class` `Entity Class` 更简单,其只包含 `protected``private`的属性和`getter``setter` 方法(在 JAVA 中,这种对象成为值对象)。
Entity Class 代码结构如下:
```php
<?php
namespace MyBlog;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="users")
*/
class User
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
/**
* @ORM\Column(type="string")
*/
protected $name;
/**
* @ORM\Column(type="string")
*/
protected $email;
/**
* @ORM\Column(type="string")
*/
protected $password;
/**
* @ORM\Column(type="string")
*/
protected $remember_token;
// .. (other code getter setter )
}
```
<a name="Repository"></a>
## Repository
初学者常会在 controller 直接调用 model 写数据库逻辑:
```php
public function index()
{
$users = User::where('age', '>', 20)
->orderBy('age')
->get();
return view('users.index', compact('users'));
}
```
数据库逻辑是`获取 20 岁以上的用户`
在中大型项目中,会有几个问题 :
- 将数据库逻辑写在 controller造成 controller 的肥大难以维护。
- 违反 SOLID 的单一职责原则 : 数据库逻辑不应该写在 controller。
- controller 直接相依于model使得我们无法对 controller 做单元测试。
比较好的方式是使用 repository :
- 将 model 依赖注入到 repository。
- 将数据库逻辑写在 repository。
- 将 repository 依赖注入到 service。
以此原则,我们看下,采用 Eloquent ORM 如何使用 Repository 模式。
UserRepository.php
```php
namespace MyBlog\Repositories;
use Doctrine\Common\Collections\Collection;
use MyBlog\User;
class UserRepository
{
/** @var User 注入的User model */
protected $user;
/**
* UserRepository constructor.
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* 返回年龄大于$age的用户
* @param integer $age
* @return Collection
*/
public function getAgeLargerThan($age)
{
return $this->user
->where('age', '>', $age)
->orderBy('age')
->get();
}
}
```
第 8 行
```php
/** @var User 注入的User model */
protected $user;
/**
* UserRepository constructor.
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
```
将依赖的 User model 依赖注入到 UserRepository。
UserController.php
```php
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use MyBlog\Repositories\UserRepository;
class UserController extends Controller
{
/** @var UserRepository 注入的UserRepository */
protected $userRepository;
/**
* UserController constructor.
*
* @param UserRepository $userRepository
*/
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$users = $this->userRepository
->getAgeLargerThan(20);
return view('users.index', compact('users'));
}
}
```
将依赖的 UserRepository 依赖注入到 UserController。
26行
```php
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$users = $this->userRepository
->getAgeLargerThan(20);
return view('users.index', compact('users'));
}
```
从原本直接依赖 User model改成依赖注入的 UserRepository。
改用这种写法,有几个优点:
- 将数据库逻辑写在存储库中,解决控制器肥大问题。
- 符合SOLID的单一职责原则数据库逻辑写在repository没写在controller。
- 符合SOLID的依赖转换原则controller 并非直接相依于存储库而是将存储库依赖注入进controller。
>
**实际上建议repository仅依赖注入于service而不要直接注入在controller本范例因为还没介绍到servie模式以便简化起见所以直接注入于controller**。<br />
<br />
>
**是否该建立存储库接口?**<br />
理论上使用依赖注入时应该使用接口不过接口目的在于通过抽象化方便在后期更换接口的具体实现以便让程序达到开放封闭的要求但是实际上要更换存储库的机会不高除非你有更换资料库的需求如从MySQL抽换到MongoDB此时就该建立存储库接口。
不过由于我们使用了依赖注入将来要从类改成interface也很方便只要在构造方法的类型提示改成interface即可维护成本很低所以在此大可使用的存储库类用接口反而会造成设计过度等真正需求来时再重构成接口即可。
采用 Doctrine ORM 如何使用 Repository 模式。
由于 Doctrine ORM 自带 Repository 模式,所以直接使用即可。
UserRepository.php
```php
<?php
namespace App\Repositories;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository {
/**
* 返回年龄大于$age的用户
* @param integer $age
* @return Collection
*/
public function getAgeLargerThan($age)
{
return $this->user
->where('age', '>', $age)
->orderBy('age')
->get();
}
}
```
UserController.php
```php
namespace App\Http\Controllers;
use App\Http\Requests;
use MyBlog\Repositories\UserRepository;
class UserController extends Controller
{
/** @var UserRepository 注入的UserRepository */
protected $userRepository;
/**
* UserController constructor.
*
* @param UserRepository $userRepository
*/
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$users = $this->userRepository
->getAgeLargerThan(20);
return view('users.index', compact('users'));
}
}
```

View File

@@ -0,0 +1,308 @@
---
url: framework_architecture-service
---
# Service
>
**使用 Service 辅助 Controller**<br />
若将商业逻辑都写在 controller会造成 controller 肥大而难以维护基于SOLID原则我们应该使用 Service 模式辅助 controller将相关的商业逻辑封装在不同的 service方便中大型项目的维护。
<a name="f86fc3d0"></a>
## 商业逻辑
商业逻辑中,常见的如 :
- 牵涉到外部行为 : 如发送Email使用外部API…。
- 使用PHP写的逻辑 : 如促销规则计算、订单创建等。
若将商业逻辑写在 controller会造成 controller 肥大,日后难以维护。
<a name="Service"></a>
## Service
牵涉到外部行为
如发`送Email`,初学者常会在 controller 直接调用 `Mail::queue()`:
```php
public function store(Request $request)
{
Mail::queue('email.index', $request->all(), function (Message $message) {
$message->sender(env('MAIL_USERNAME'));
$message->subject(env('MAIL_SUBJECT'));
$message->to(env('MAIL_TO_ADDR'));
});
}
```
> Mail::queue()只有一行可能无感,但很多外部服务需要一连串 API甚至还要有 try/catch 处理。
在中大型项目,会有几个问题 :
- 将牵涉到外部行为的商业逻辑写在 controller造成 controller 的肥大难以维护。
- 违反 SOLID 的单一职责原则 : 外部行为不应该写在 controller。
- controller 直接相依于外部行为,使得我们无法对 controller 做单元测试。
比较好的方式是使用 service :
- 将外部行为注入到 service。
- 在 service 使用外部行为。
- 将 service 注入到 controller。
EmailService.php
```php
namespace App\Services;
use Illuminate\Mail\Mailer;
use Illuminate\Mail\Message;
class EmailService
{
/** @var Mailer */
private $mail;
/**
* EmailService constructor.
* 将依赖的 Mailer 注入到 EmailService。
* @param Mailer $mail
*/
public function __construct(Mailer $mail)
{
$this->mail = $mail;
}
/**
* 发送Email
* 将发送 Email 的商业逻辑写在 send()。
* 不是使用 Mail facade而是使用注入的 $this->mail。
* @param array $request
*/
public function send(array $request)
{
$this->mail->queue('email.index', $request, function (Message $message) {
$message->sender(env('MAIL_USERNAME'));
$message->subject(env('MAIL_SUBJECT'));
$message->to(env('MAIL_TO_ADDR'));
});
}
}
```
UserController.php
```php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use MyBlog\Services\EmailService;
class UserController extends Controller
{
/** @var EmailService */
protected $emailService;
/**
* UserController constructor.
* @param EmailService $emailService
*/
public function __construct(EmailService $emailService)
{
//将依赖的 EmailService 注入到 UserController。
$this->emailService = $emailService;
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//从原本直接依赖于 Mail facade改成相依于注入的 EmailService。
$this->emailService->send($request->all());
}
}
```
>
**改用这种写法,有几个优点 :**<br />
- 将外部行为写在 service解决 controller 肥大问题。
- 符合 SOLID 的单一职责原则 : 外部行为写在 service没写在 controller。
- 符合 SOLID 的依赖反转原则 : controller 并非直接相依于 service而是将 service 依赖注入进 controller。
使用 PHP 写的逻辑:
如根据购买的件数,有不同的折扣,初学者常会在 controller 直接写 if...else 逻辑。
```php
public function store(Request $request)
{
$qty = $request->input('qty');
$price = 500;
if ($qty == 1) {
$discount = 1.0;
} elseif ($qty == 2) {
$discount = 0.9;
} elseif ($qty == 3) {
$discount = 0.8;
} else {
$discount = 0.7;
}
$total = $price * $qty * $discount;
echo($total);
}
```
在中大型项目,会有几个问题 :
- 将 PHP 写的商业逻辑直接写在 controller造成 controller 的肥大难以维护。
- 违反 SOLID的 单一职责原则 : 商业逻辑不应该写在 controller。
- 违反SOLID的单一职责原则: 若未来想要改变折扣与加总的算法都需要改到此method也就是说此method 同时包含了计算折扣与计算加总的职责因此违反SOLID 的单一职责原则。
- 直接写在 controller 的逻辑无法被其他 controller 使用。
比较好的方式是使用 service。
- 将依赖的类注入到 service。
- 在 service 写 PHP逻辑使用相依物件。
- 将 service 注入到 controller。
OrderService.php
```php
namespace App\Services;
class OrderService
{
/**
* 計算折扣
* @param int $qty
* @return float
*/
//为了符合 SOLID 的单一职责原则,将计算折扣独立成 getDiscount()将PHP写的判断逻辑写在里面。
public function getDiscount($qty)
{
if ($qty == 1) {
return 1.0;
} elseif ($qty == 2) {
return 0.9;
} elseif ($qty == 3) {
return 0.8;
} else {
return 0.7;
}
}
/**
* 计算最后价格
* @param integer $qty
* @param float $discount
* @return float
*/
//为了符合 SOLID 的单一职责原则,将计算加总独立成 getTotal()将PHP写的计算逻辑写在里面。
public function getTotal($qty, $discount)
{
return 500 * $qty * $discount;
}
}
```
OrderController.php
```php
namespace App\Http\Controllers;
use App\Http\Requests;
use App\MyBlog\Services\OrderService;
use Illuminate\Http\Request;
class OrderController extends Controller
{
/** @var OrderService */
protected $orderService;
/**
* OrderController constructor.
* @param OrderService $orderService
*/
public function __construct(OrderService $orderService)
{
//将依赖的 OrderService 注入到 UserController。
$this->orderService = $orderService;
}
/**
* Store a newly created resource in storage.
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$qty = $request->input('qty');
//将原本的 if...else 逻辑改成调用 OrderService
//controller 变得非常干净也达到了controller 接收 HTTP request调用其他 class 的责任。
$discount = $this->orderService->getDiscount($qty);
$total = $this->orderService->getTotal($qty, $discount);
echo($total);
}
}
```
::: info
改用这种写法,有几个优点 :
:::
- 将PHP写的商业逻辑写在 service解决 controller 肥大问题。
- 符合 SOLID 的单一职责原则 : 商业逻辑写在 service没写在 controller。
- 符合 SOLID 的单一职责原则 : 计算折扣与计算加总价分开在不同 method且归属于 OrderService而非 OrderController。
- 符合 SOLID 的依赖反转原则 : controller 并非直接相依于 service而是将 service依赖注入进 controller。
- 其他 controller 也可以重复使用此段商业逻辑。
<a name="Controller"></a>
## Controller
涉及到外部行为
```php
public function store(Request $request)
{
$this->emailService->send($request->all());
}
```
使用 PHP 写的逻辑
```php
public function store(Request $request)
{
$qty = $request->input('qty');
$discount = $this->orderService->getDiscount($qty);
$total = $this->orderService->getTotal($qty, $discount);
echo($total);
}
```
若使用了 service 辅助 controller再搭配依赖注入与 service container则 controller 就非常干净,能专心处理`接收HTTP request调用其他class`的职责了。
<a name="Conclusion"></a>
## Conclusion
- 实际上会有很多 service须自行依照 SOLID 原则去判断是否该建立 service。
- Service 使得商业逻辑从 controller 中解放,不仅更容易维护、更容易扩展、更容易重复使用,且更容易测试。

View File

@@ -0,0 +1,109 @@
---
url: framework_architecture
---
# 中大型项目架构
Laravel 的初学者分为两种:
- 一种是乖乖的将程序在写 MVC 架构内,这将导致 Controller 和 Model 非常臃肿,日后代码很难维护。
- 一种是不知道将程序写在哪一个Class内而犹豫不决。
本文整理出最适合 Laravel 的中大型项目架构,兼具易维护、易扩展、易复用和易测试的特点。
<a name="910dfdc0"></a>
## Controller 过于肥大
受RoR的影响初学者常认为 MVC 架构就是 ModelViewController
- Model 就是数据库。
- Controller 负责与 HTTP 通信,调用 Model 与 View。
- View 就是 HTML。
假如按照此定义,以下需求应该写在哪里呢?
1.发送 Email使用外部 API。
2.使用 PHP 写的逻辑。
3.依需求将显示格式作转换。
4.依需求是否显示某些资料。
5.依需求显示不同资料。
其中1, 2 属于商业逻辑而3, 4, 5 属于显示逻辑,若依照一般人对 MVC 的定义Model 是数据库,而 View 是HTML以上这些需求都不能写在Model 与View只能勉强写在Controller。
因此初学者开始将大量程序写在 Controller造成 Controller 的肥大难以维护。
<a name="d0cd5997"></a>
## Model 过于肥大
>
**既然逻辑写在 Controller 不方便维护,那我将逻辑都写在 Model 就好了?**<br />
<br />
当你将逻辑从 Controller 搬到 Model 后,虽然 Controller 变瘦了,但却肥了 ModelModel 从原本只处理数据库逻辑,现在变成还要负担商业逻辑与显示逻辑,结果更惨。
Model 代表数据库吗?把它想成是 Eloquent class就好数据库逻辑应该写在 repository 里,这也是为什么 LaravelLumen 已经没有 Models目录Eloquent class 仅仅是放在 app 根目录下而已。
<a name="4b968317"></a>
## 中大型项目架构
那我们该怎么写呢?别将我们的思维局限在 MVC 内 :
- Model : 仅当成 Eloquent class。
- Repository : 辅助 Model处理数据库逻辑然后注入到 service。
- Service : 辅助 Controller处理商业逻辑然后注入到 Controller。
- Controller : 接收 HTTP request调用其他 service。
- Presenter : 处理显示逻辑,然后注入到 View。
- View : 使用 blade 将资料 binding 到 HTML。
其中蓝色为原本的 MVC而紫色为本文要介绍的的重点 : Repository 模式Service 模式与 Presenter 模式。
![arch002.svg](../assets/1584351898106-8e924cd5-0904-4bf5-a50b-d48b44bbd0c4.svg)
> 箭头表示物件依赖注入的方向。
我们可以发现 MVC 架构还在,由于 SOLID 的单一职责原则与依赖反转原则 :
- 我们将数据库逻辑从 Model 分离出来,由 repository 辅助 Model将 Model 依赖注入进 repository。
- 我们将商业逻辑从 Controller 分离出来,由 service 辅助 Controller将 service 依赖注入进 Controller。
- 我们将显示逻辑从 View 分离出来,由 presenter 辅助 View将 presenter 依赖注入进 View。
建立目录在 src 的 Bundle 目录下建立 RepositoriesServices 与 Presenters 目录。
别害怕在 Laravel 预设目录以外建立的其他目录,根据 SOLID 的单一职责原则class 功能越多,责任也越多,因此越违反单一职责原则。
所以你应该将你的程序分割成更小的部分,每个部分都有它专属的功能,而不是一个 class 功能包山包海,也就是所谓的`万能类`
所以整个项目不应该只有 MVC 三个部分,放手根据你的需求建立适当的目录,并将适当的 class 放到该目录下只要我们的class 有namespace 帮我们分类即可。
<a name="Repository"></a>
## Repository
由于篇幅的关系,将 repository 独立成专文讨论,请参考[如何使用 Repository 模式](framework_architecture-repository) ?
<a name="Service"></a>
## Service
由于篇幅的关系,将 service 独立成专文讨论,请参考如何使用 Service 模式?
<a name="93b824b5"></a>
## 单元测试
由于现在Model、View、Controller 的依赖都已经拆开也都使用依赖注入因此每个部分都可以单独的做单元测试如要测试service就将repository 加以 mock也可以将其他 service 加以 mock。
<a name="54bbba80"></a>
## 结论
本文谈到的架构只是开始,你可以依照实际需求增加更多的目录与 class当你发现你的 MVC 违反 SOLID原则时就大胆的将 class 从 MVC 拆开重构,然后依照以下手法 :
- 建立新的 class 或 interface。
- 将依赖类依赖注入到 class。
- 在 class 内处理他的职责。
- 将 class 或 interface 注入到 Controller 或 View。
最后搭配单元测试,测试重构后的架构是否与原来的需求结果相同。