This action will force synchronization from 铅笔刀/sc, which will overwrite any changes that you have made since you forked the repository, and can not be recovered!!!
Synchronous operation will process in the background and will refresh the page when finishing processing. Please be patient.
[TOC]
##前置条件
###开发者需要掌握的
Linux基本命令使用、文件、进程管理、Nginx+PHP+MySQL+Redis环境配置
PHP开发
MySQL数据库
Redis数据库
Linux+Nginx+PHP7.2+MySQL(5.6|5.7)+Redis(4|5)
clone https://gitee.com/zjhj/zjhj_mall_v4.git
cd zjhj_mall_v4
cp config/db.example.php config db.php
,并配置相关数据库信息cd web
(可选)php -S localhost:8000
(可选)http://localhost:8000
(可选)/www/wwwroot/zjhj_bd/
复制db.example.php
到db.php
,按相关参数配置。
复制.env.example.php
到.env
按需配置相关选项。
在YII_DEBUG = true
的情况下,所有错误结果将由Yii框架处理,YII_DEBUG = false
或未配置YII_DEBUG
的情况下,所有错误结果将统一处理,HTTP不再直接返回相关错误码,错误码在ajax下返回在code
字段中。
复制local.example.php
到local.php
按需配置相关选项。
通过案例了解框架处理流程
创建文件/controllers/mall/DemoController.php
<?php
namespace app\controllers\mall;
use app\core\response\ApiCode;
class DemoController extends MallController
{
public function actionIndex()
{
if (\Yii::$app->request->isAjax) { // ajax请求返回json数据,此处返回数组将自动转换成json
return [
'code' => ApiCode::CODE_SUCCESS,
'data' => [
'content' => 'hello!',
],
'msg' => 'any msg'
];
} else { // 其他请求返回界面视图
return $this->render('index');
}
}
}
创建文件/views/mall/demo/index.php
<div id="app" v-cloak>
<div>{{content}}</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
content: ''
};
},
created() {
this.$request({
params: {
r: 'mall/demo/index'
}
}).then(e => {
this.content = e.data.data.content;
}).catch(e => {
});
},
});
</script>
http://网站目录/web/index.php?r=mall/demo/index
即可访问到。##代码说明
###目录说明
/condif #配置文件
/controllers #控制器
/events #事件定义类
/forms #表单处理
/handlers #事件处理
/jobs #队列任务
/models #数据库表模型
/plugins #插件
/validators #自定义验证器
/views #视图文件
/web #入口文件、资源文件
###开发调试模式
要开启开发调试模式,可在项目根目录下创建.env
配置文件,写入内容
YII_DEBUG=true
YII_ENV=dev
###插件开发
插件与系统交互部分:
后台 菜单、权限
小程序 导航菜单、底部导航、用户中心
每个插件代码放在/plugins目录下,插件格式可参照下面的demo插件,插件代码结构:
├── Plugin.php
├── assets
│ └── css
│ └── style.css
├── controllers
│ └── IndexController.php
├── models
│ └── DemoPost.php
├── tree.txt
└── views
└── index
└── index.php
Plugin.php: 插件配置文件,必须,需要继承\app\plugins\Plugin。
assets: 插件静态资源文件,如css、js、图片,插件安装时将自动复制到/web/assets/plugins/插件名
目录下,可使用\app\helpers\PluginHelper::getPluginBaseAssetsUrl()
和\app\helpers\PluginHelper::getPluginAssetsPath()
获取。
controllers: 插件控制器目录。
后台控制器需要继承\app\plugins\Controller
小程序端控制器需要继承\app\controllers\api\ApiController
models: 插件model文件,注意插件数据表对应的model也放在此文件夹下。
views: 插件视图文件。
小程序端配置:详见\app\plugins\Plugin()->getAppConfig();
PSR12
PHP规范要求符合PSR12的代码规范https://laravel-china.org/docs/psr/。
要求每个开发者给自己的代码编辑器安装规范检查工具:
PHPStorm: https://www.jianshu.com/p/b5697eb5f401
Sublime: https://blog.csdn.net/he426100/article/details/76573038
命名规范
数据表、字段、常量、变量、类、方法函数命名应该明确,尽量能从命名能知道其作用。
驼峰or下划线: 数据表、接口传递字段使用下划线,其它位置尽量使用驼峰。
数据表前缀:
核心数据表前缀使用zjhj_bd_core_,如日志、定时任务.
基础业务数据表使用zjhj_bd_xxx,如用户、商品、设置。
插件数据表使用zjhj_bd_插件_xxx。
注释规范
函数、方法应当编写对应的注释,要求写明函数说明、传入参数类型、返回参数类型。
可参考内容[https://www.cnblogs.com/hellohell/p/5733712.html]
错误处理
客户提交数据验证错误: 返回错误信息。
系统错误:抛出异常。
抛出异常将通过日志系统记录进日志。
数据库规范
明确字段是否是NOT NULL的,如果是NOT NULL,就不用设置默认值了,除非真的需要,如果是可以为NULL,请设置成NOT NULL并添加默认值。
表、字段 Charset 统一 utf8mb4
,Collation 统一 utf8mb4_general_ci
,存储引擎统一 InnoDB
。
除非情况特殊,严禁使用 TEXT
/ LONGTEXT
/ BLOB
/ LONGBLOB
等类型。
每张表必须加入 created_at(创建时间)
/ updated_at(更新时间)
字段, deleted_at(软删除时间)
字段可按需添加。 字段类型统一为TIMESTAMP
,
is_delete(是否删除,TINYINT(1)类型)
,
类似 is_delete
store_id
等常用查询的字段,且必须建 Index 索引。
对于存储 URL 的字段,必须采用 VARCHAR
类型,建议长度:2048
- 8192
,参见:https://stackoverflow.com/questions/2659952/maximum-length-of-http-get-request
GIT规范
commit备注禁止出现1
、提交
等不明其意的备注!
目前基础功能开发提交至dev分支,后期开发各个模块、插件的功能使用各自分支,要求分支命名能明其意,保持将dev分支合并到自己分支的习惯(每天至少一次)。
不提交的文件应该加入.gitignore,避免误提交。
代码安全: SQL注入 | XSS | CSRF
逻辑安全: 支付
.
├── config // 配置文件
├── controllers // 系统控制器
│ ├── admin // 管理员(独立版管理员)
│ ├── mall // 商城管理
│ └── api // 小程序接口
├── core // 系统文件
├── helpers // 公共函数、助手类
├── models // 系统模型
├── plugins // 插件
│ └── demo // 示例插件
│ ├── assets // 插件静态文件(css、js)
│ ├── controllers // 插件控制器
│ ├── models // 插件模型
│ └── views // 插件视图
├── runtime // 运行临时目录
│ ├── HTML
│ ├── URI
│ ├── cache
│ ├── debug
│ ├── gii-2.0.15.1
│ └── logs
├── validators // 公共验证器
├── vendor
├── views // 系统视图
│ ├── error
│ ├── layouts
│ └── site
└── web
└── assets
└── plugins
公共函数放在/helpers/functions.php, 有公共方法可以补充到这里,或是在helpers下创建自己的助手类。
/.env
主要存储简单的环境配置,如调试模式开关,不应放敏感信息的配置。
/config/local.php
存储系统敏感配置信息,如缓存配置、session配置。
统一由/core/ErrorHandler处理,要求区分json和html的返回结果,错误信息保存进日志。
日志级别
Error: 系统出错无法继续运行,如抛出Exception。
Warning: 警告,系统继续运行,只是某些结果可能跟预期不符合;场景举例:订单支付模板消息通知,模板消息没法发送成功,可记录警告信息。
Info: 普通信息,不影响系统运行;场景举例:管理员操作日志,如商品删除、订单删除、订单价格修改等操作。
统一系统日志接口
api::info(); api::warning(); api::error(); api::add( ,level);
api::delete(id); api::list(page, pageSize); api::detail(id);
JSON数据返回结果统一以下结构
{
"code": 0,
"msg": "操作结果。",
"data": 1,
"error": null
}
参数 | 值 | 类型 | 说明 |
---|---|---|---|
code | -1 | integer | 用户未登录 |
code | 0 | integer | 成功 |
code | 1 | integer | 失败 |
code | 500 | integer | 系统错误 |
msg | - | string | - |
data | - | - | 返回的数据 |
error | - | - | - |
前端开发的成员请先熟悉ES6新特性http://es6.ruanyifeng.com/, 优先阅读:
24.编程风格
2.let 和 const命令
7.函数的扩展-5.箭头函数
14.Promise 对象
前后端数据分离, 管理后台前端使用Vue | Element-ui | Axios, 使用示例见/controllers/DemoController。
用于下单单线程处理、定时任务、异步任务。
详细用法见:https://github.com/yiisoft/yii2-queue/blob/master/docs/guide/usage.md#usage
队列服务启动
【方法一】一次运行,关闭控制台会自动结束。
运行代码目录/yii queue/listen 1
即可。
【方法二】自动后台运行并创建检测服务。
运行chmod +x 代码目录/queue.sh && 代码目录/queue.sh
即可。
此脚本会在系统crontab创建任务每分钟检测服务是否运行,不运行会自动启动。
支付组件封装了微信支付和余额支付的功能。
调用组件\Yii::$app->payment
组件接口
创建待支付订单
$order = new \app\core\payment\PaymentOrder([
'title' => '商品名称(128)',
'amount' => 100.00, //订单金额(0.01~100000000.00)
'orderNo' => '订单号(32)',
'notifyClass' => MyNotifyClass::class, //支付结果通知类,需继承\app\core\payment\PaymentNotify并实现notify方法
'supportPayTypes' => [ //选填,支持的支付方式,若不填将支持所有支付方式。
\app\core\payment\Payment::PAY_TYPE_HUODAO, // 货到付款
\app\core\payment\Payment::PAY_TYPE_BALANCE, // 余额
\app\core\payment\Payment::PAY_TYPE_WECHAT, // 微信支付
\app\core\payment\Payment::PAY_TYPE_ALIPAY, // 支付宝支付
\app\core\payment\Payment::PAY_TYPE_BAIDU, // 百度支付
\app\core\payment\Payment::PAY_TYPE_TOUTIAO, // 抖音头条支付
],
]);
$id = \Yii::$app->payment->createOrder($order);
获取待支付订单id后可将id返回给小程序端,由小程序端的支付组件完成后续付款操作。
目前收录了 指定用户余额、积分(添加、减少、查询、退还) 指定用户添加余额
\Yii::$app->currency->setUser(\app\models\User)->balance->add($price, $desc, $customDesc);
指定用户减少余额
\Yii::$app->currency->setUser(\app\models\User)->balance->sub($price, $desc, $customDesc);
指定用户查询余额
\Yii::$app->currency->setUser(\app\models\User)->balance->select();
指定用户退还余额
\Yii::$app->currency->setUser(\app\models\User)->balance->refund($price, $desc, $customDesc);
指定用户添加积分
\Yii::$app->currency->setUser(\app\models\User)->integral->add($integral, $desc, $customDesc);
指定用户减少积分
\Yii::$app->currency->setUser(\app\models\User)->integral->sub($integral, $desc, $customDesc);
指定用户查询可用积分
\Yii::$app->currency->setUser(\app\models\User)->integral->select();
指定用户查询总积分
\Yii::$app->currency->setUser(\app\models\User)->integral->selectTotal();
指定用户退还积分
\Yii::$app->currency->setUser(\app\models\User)->integral->refund($price, $desc, $customDesc);
指定用户添加佣金
\Yii::$app->currency->setUser(\app\models\User)->brokerage->add($price, $desc, $customDesc);
指定用户减少佣金
\Yii::$app->currency->setUser(\app\models\User)->brokerage->sub($price, $desc, $customDesc);
指定用户查询可用佣金
\Yii::$app->currency->setUser(\app\models\User)->brokerage->select();
指定用户查询总佣金
\Yii::$app->currency->setUser(\app\models\User)->brokerage->selectTotal();
指定用户退还佣金
\Yii::$app->currency->setUser(\app\models\User)->brokerage->refund($price, $desc, $customDesc);
系统中一些关键步骤会触发事件,可在应用启动时挂载一些事件处理器处理相关的事件。
创建处理器handlers\\MyHandler
:
<?php
namespace app\handlers;
class MyHandler extends HandlerBase
{
/**
* 事件处理注册
*/
public function register()
{
\Yii::$app->on(\app\models\User::EVENT_REGISTER, function ($event) {
// todo 事件相应处理
});
}
}
挂载处理器,编辑文件handlers/HandlerRegister.php
, 在getHandlers
加入自己的处理器类即可:
public function getHandlers()
{
return [
OrderCreatedHandler::class,
MyHandler::class,
];
}
除了框架的事件外,商城运行过程也会触发事件。
事件 | 事件类 | 事件说明 |
---|---|---|
app\models\User::EVENT_REGISTER | app\events\UserEvent | 小程序新用户加入后触发 |
app\models\User::EVENT_LOGIN | app\events\UserEvent | 小程序用户登录获取access_token时触发 |
事件 | 事件类 | 事件说明 |
---|---|---|
app\models\Order::EVENT_CREATED | app\events\OrderEvent | 创建新订单后触发 |
app\models\Order::EVENT_PAYED | app\events\OrderEvent | 订单支付后触发 |
app\models\Order::EVENT_SENT | app\events\OrderEvent | 订单发货后触发 |
app\models\Order::EVENT_CONFIRMED | app\events\OrderEvent | 订单确认收货后触发 |
app\models\Order::EVENT_SALES | app\events\OrderEvent | 订单过售后触发 |
app\models\OrderRefund::EVENT_REFUND | app\events\OrderRefundEvent | 订单商品售后处理完成后触发 |
注:售后订单处理完成后需要添加售后队列
// 判断queue队列中的售后是否已经触发
$queueId = app\models\CoreQueueData:select(app\models\Order $order->token);
if (!\Yii::$app->queue->isDone($queueId)) {
// 若未触发
return ;
} else {
// 若已触发,则重新添加
$id = \Yii::$app->queue->delay(0)->push(new OrderSalesJob([
'orderId' => app\models\Order $order->id
]));
CoreQueueData::add($id, $event->order->token);
}
商城订单支付完成事件需要执行的操作都收录在\app\handlers\orderHandler\OrderPayedHandlerClass中
插件可以通过Plugin下面的getOrderHandler方法进行重载商城订单支付完成事件
订单支付完成事件处理需要继承\app\handles\orderHandler\BaseOrderPayedHandler
详见示例r=mall/demo/index
使用el-card组件,头部左侧标题,右侧添加按钮(或其他操作)。
列表上边搜索表单(如果有),从左到右摆列。
列表列数数据项不宜超过6列,数据项过多尽量放在同一个单元格内,用换行方式显示。
对于可预知长度的列,尽量设定合适的宽度。
列表数据加载过程请使用element-ui的loading动画。
列表底部左侧放批量操作按钮(如果有)。
列表底部右侧放分页组件。
el-card组件不要显示阴影。
按钮的大小、颜色、样式请参照demo。
详见示例r=mall/demo/edit
####新增分页获取用法
示例:
\app\models\Goods::find()->page($pagination, $limit, $page)->all();
使用page()方法可以直接获取分页列表;其中$pagination为null|\app\core\Pagination对象
公共布局文件已经定义了全局的request实例(Axios),直接调用即可。
在vue下建议使用this.$request
调用
this.$request({
method: 'get', // 默认
params: { // GET请求参数
r: 'demo/index',
id: 100,
},
}).then(response => {
// 请求成功
// 返回的数据
console.log(response.data);
}).catch(error => {
// 请求出错
});
this.$request({
method: 'post', // 默认
params: { // GET请求参数
r: 'demo/index',
id: 100,
},
data: { // POST提交内容,数据默认统一使用Qs组件转换成QueryString
title: 'hello',
content: 'longtext content',
},
}).then(response => {
// 请求成功
// 返回的数据
console.log(response.data);
}).catch(error => {
// 请求出错
});
在Vue内可使用this.$navigate(params, newWindow)
跳转网址:
<el-button @click="$navigate({r: 'demo/index'})">在当前页面打开</el-button>
<el-button @click="$navigate({r: 'demo/index'}, true)">在新页面打开</el-button>
// 在当前页面打开
this.$navigate({
r: 'demo/index',
id: 1,
});
// 在新页面打开
this.$navigate({
r: 'demo/index',
id: 1,
}, true);
其它可使用navigateTo跳转链接
navigateTo({
r: 'demo/index',
id: 123
});
链接跳转js内可使用navigateTo跳转链接
getQuery('name');
基础的选择器组件,用法:
<app-picker :multiple="true" :max="2" :list="[{}, {}, {}]" @change="pickerChange"></app-picker>
参数:
事件:
可选择图片、视频等附件,或是上传新附件,用法:
<app-attachment :multiple="true" :max="2" @selected="attachmentSelected">选择文件</app-attachment>
参数:
事件:
用于展示单张或多张图片,用法
<app-gallery :show-delete="true" @deleted="picDeleted" :list="[]"></app-gallery>
参数:
show-delete: true|false, 是否显示删除按钮
list: Array, 图片列表,要求格式[ {url: 'http://xxx', ...}, {url: 'http://xxx', ...} ]
url: String 单图图片地址,(当传入url参数时,list参数失效) 事件:
deleted: 点击删除操作,deleted(item, index); // item: 被删除元素, index: 被删元素位置
可选择跳转链接、底部导航链接,用法:
<app-pick-link @selected="selectAdvertUrl"><el-button size="mini">选择链接</el-button></app-pick-link>
事件:
selected: 链接选择完成,点击确认,selected(list);
type single(默认)|单选, multiple|多选
<app-image mode="" width="50px" height="50px" src="http://aaa.com/test.jpg"></app-image>
参数:
mode: string, 图片显示方式,支持aspectFill(默认,图片自动裁剪),scaleToFill(不裁剪,自动拉伸)。
width: string, 图片宽度,默认50px。
radius: string, 图片圆角,默认0%
height: string, 图片高度,默认50px。
src: string, 图片地址。
<app-ellipsis :line="1">文本内容</app-ellipsis>
参数:
可搜索位置、获取经纬度,用法:
<app-map @map-submit="mapEvent"><el-button size="mini">展开地图</el-button></app-map>
事件:
<app-district :level="3" :detail="[]" @selected="selectDistrict" :edit="[]"></app-district>
参数:
事件:
<app-goods></app-goods>
参数:
form: {extra:{first:{name:'积分',value:''}}}
内部方法:
文本编辑器支持数据双向绑定(v-model)。
// 加载组件
Yii::$app->loadViewComponent('app-rich-text')
<app-rich-text v-model="content"></app-rich-text>
// 加载组件
Yii::$app->loadViewComponent('app-hotspot')
<app-hotspot @confirm></app-hotspot>
参数:
// 加载组件
Yii::$app->loadViewComponent('app-dialog-select')
<app-dialog-select @selected></app-dialog-select>
参数:
发送统一调用\app\forms\common\template\TemplateSend; 参数说明:
在后台管理代码 /models/Mall.php 有具体说明
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。