规则:
示例:
<?php
// ✅ 正确
class UserServices
class StoreOrderController
class SystemConfigDao
// ❌ 错误
class userServices
class store_order_controller
class DAO_User
命名约定:
UserController, ProductControllerUserServices, StoreOrderServicesUserDao, StoreProductDaoUser, StoreOrderUserValidateAuthTokenMiddlewareOrderPaySuccessListenerValidateException规则:
示例:
<?php
// 服务层/DAO层/模型层 - 小驼峰
public function getUserList()
public function saveOrder()
public function deleteById()
private function _formatData() // 私有方法以下划线开头
// 控制器层 - 小写+下划线
public function get_list()
public function save_data()
public function delete()
public function update_status()
规则:
示例:
<?php
// ✅ 正确
$userName = '张三';
$orderId = 12345;
$userList = []; // 数组使用复数
$isPaid = true; // 布尔值使用is/has/can开头
$hasStock = true;
$canRefund = false;
// 常量
const STATUS_ACTIVE = 1;
const ORDER_TYPE_NORMAL = 0;
// ❌ 错误
$username = '张三'; // 不清晰
$a = 12345; // 无意义
$list = []; // 不明确
$paid = true; // 不清晰
表命名:
eb_ 前缀示例:
-- ✅ 正确
eb_user
eb_store_product
eb_store_order
eb_system_config
-- ❌ 错误
user -- 无前缀
StoreProduct -- 大写字母
systemconfig -- 无下划线
字段命名:
id表名_idcreate_time, update_timestatusis_del示例:
-- ✅ 正确
id INT PRIMARY KEY,
user_id INT,
order_id INT,
create_time INT,
update_time INT,
status TINYINT(1),
is_del TINYINT(1)
-- ❌ 错误
ID INT PRIMARY KEY, -- 大写
userId INT, -- 驼峰
orderId INT, -- 驼峰
createTime INT, -- 驼峰
Status TINYINT(1), -- 大写
isDelete TINYINT(1), -- 驼峰
请求 → 路由 → 中间件 → 控制器 → 服务层 → DAO层 → 模型层 → 数据库
↓ ↑
└────────→ 异常处理 ←───────── 事务管理 ←───────────┘
职责:
规范示例:
<?php
namespace app\adminapi\controller\system;
use think\facade\App;
use app\services\system\SystemAdminServices;
class SystemAdminController
{
protected $services;
public function __construct(App $app, SystemAdminServices $services)
{
$this->services = $services;
}
/**
* 获取管理员列表
* @return \think\response\Json
*/
public function get_list()
{
// 1. 获取并验证请求参数
$where = $this->request->getMore([
['keywords', ''],
['status', ''],
['role_id', '']
]);
// 2. 调用服务层
$list = $this->services->getAdminList($where);
// 3. 返回响应
return app('json')->success($list);
}
/**
* 保存管理员
* @return \think\response\Json
*/
public function save()
{
// 1. 获取并验证POST参数
$data = $this->request->postMore([
['id', 0],
['account', ''],
['real_name', ''],
['pwd', ''],
['roles', []],
['status', 1]
]);
// 2. 调用服务层保存
$this->services->saveAdmin($data);
// 3. 返回成功响应
return app('json')->success('保存成功');
}
/**
* 删除管理员
* @param int $id
* @return \think\response\Json
*/
public function delete($id)
{
// 1. 参数验证
if (!$id) {
return app('json')->fail('参数错误');
}
// 2. 调用服务层删除
$this->services->deleteAdmin($id);
// 3. 返回成功响应
return app('json')->success('删除成功');
}
}
职责:
规范示例:
<?php
namespace app\services\system;
use crmeb\basic\BaseServices;
use crmeb\exceptions\AdminException;
use app\dao\system\SystemAdminDao;
class SystemAdminServices extends BaseServices
{
protected $dao;
public function __construct(SystemAdminDao $dao)
{
$this->dao = $dao;
}
/**
* 获取管理员列表
* @param array $where
* @param int $page
* @param int $limit
* @return array
*/
public function getAdminList(array $where, int $page = 1, int $limit = 10): array
{
// 1. 构建查询条件
$query = $this->dao->search($where);
// 2. 统计总数
$count = $query->count();
// 3. 获取列表数据
$list = $query->page($page, $limit)->select()->toArray();
// 4. 处理数据
foreach ($list as &$item) {
$item['role_names'] = $this->getRoleNames($item['roles']);
$item['status_text'] = $item['status'] ? '启用' : '禁用';
$item['last_login_time_text'] = $item['last_login_time']
? date('Y-m-d H:i:s', $item['last_login_time'])
: '-';
}
// 5. 返回分页数据
return [
'list' => $list,
'count' => $count,
'page' => $page,
'limit' => $limit
];
}
/**
* 保存管理员
* @param array $data
* @return int
* @throws AdminException
*/
public function saveAdmin(array $data): int
{
// 1. 数据验证
if (empty($data['account'])) {
throw new AdminException('账号不能为空');
}
// 2. 验证账号唯一性
$exists = $this->dao->getAdminByAccount($data['account'], $data['id'] ?? 0);
if ($exists) {
throw new AdminException('账号已存在');
}
// 3. 密码处理
if (!empty($data['pwd'])) {
$data['pwd'] = password_hash($data['pwd'], PASSWORD_DEFAULT);
} else {
unset($data['pwd']);
}
// 4. 开启事务
return $this->transaction(function () use ($data) {
if (isset($data['id']) && $data['id']) {
// 更新
$id = $data['id'];
unset($data['id']);
$this->dao->update($id, $data);
return $id;
} else {
// 新增
$data['create_time'] = time();
return $this->dao->save($data);
}
});
}
/**
* 删除管理员
* @param int $id
* @return bool
* @throws AdminException
*/
public function deleteAdmin(int $id): bool
{
// 1. 检查是否可删除
$count = $this->dao->count();
if ($count <= 1) {
throw new AdminException('至少需要保留一个管理员');
}
// 2. 软删除
return $this->dao->update($id, ['is_del' => 1]);
}
/**
* 根据角色ID获取角色名称
* @param array $roleIds
* @return string
*/
private function getRoleNames(array $roleIds): string
{
if (empty($roleIds)) {
return '';
}
$roleNames = $this->dao->getRoleNames($roleIds);
return implode(',', $roleNames);
}
}
职责:
规范示例:
<?php
namespace app\dao\system;
use crmeb\basic\BaseDao;
use app\model\system\SystemAdmin;
class SystemAdminDao extends BaseDao
{
/**
* 设置模型
* @return string
*/
protected function setModel(): string
{
return SystemAdmin::class;
}
/**
* 搜索条件构建
* @param array $where
* @return \think\Model
*/
public function search(array $where = [])
{
// 基础查询(排除已删除)
$query = $this->getModel()
->where('is_del', 0);
// 关键词搜索
if (!empty($where['keywords'])) {
$query = $query->whereLike('account|real_name', "%{$where['keywords']}%");
}
// 状态筛选
if ($where['status'] !== '') {
$query = $query->where('status', $where['status']);
}
// 角色筛选
if (!empty($where['role_id'])) {
$query = $query->whereFindInSet('roles', $where['role_id']);
}
// 时间范围筛选
if (!empty($where['start_time'])) {
$query = $query->where('create_time', '>=', strtotime($where['start_time']));
}
if (!empty($where['end_time'])) {
$query = $query->where('create_time', '<', strtotime($where['end_time']));
}
// 排序(默认按创建时间倒序)
$query = $query->order('create_time DESC, id DESC');
return $query;
}
/**
* 根据账号获取管理员
* @param string $account
* @param int $exceptId
* @return array|null
*/
public function getAdminByAccount(string $account, int $exceptId = 0): ?array
{
$query = $this->getModel()
->where('account', $account)
->where('is_del', 0);
if ($exceptId > 0) {
$query = $query->where('id', '<>', $exceptId);
}
$admin = $query->find();
return $admin ? $admin->toArray() : null;
}
/**
* 统计管理员数量
* @return int
*/
public function count(): int
{
return $this->getModel()
->where('is_del', 0)
->count();
}
}
职责:
规范示例:
<?php
namespace app\model\system;
use crmeb\basic\BaseModel;
use think\model\relation\BelongsToMany;
class SystemAdmin extends BaseModel
{
// 数据表名
protected $name = 'system_admin';
// 主键
protected $pk = 'id';
// 自动时间戳(使用int类型存储时间戳)
protected $autoWriteTimestamp = 'int';
// 创建时间字段
protected $createTime = 'create_time';
// 更新时间字段
protected $updateTime = 'update_time';
// 软删除字段
protected $deleteTime = 'delete_time';
/**
* 关联角色(多对多)
* @return BelongsToMany
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(
SystemRole::class,
'system_admin_role',
'role_id',
'admin_id'
);
}
/**
* 密码修改器(自动加密)
* @param string $value
* @return string
*/
public function setPwdAttr(string $value): string
{
return password_hash($value, PASSWORD_DEFAULT);
}
/**
* 状态获取器
* @param int $value
* @return string
*/
public function getStatusTextAttr(int $value, array $data): string
{
$status = [
0 => '禁用',
1 => '启用'
];
return $status[$data['status']] ?? '未知';
}
/**
* 最后登录时间获取器
* @param int $value
* @return string
*/
public function getLastLoginTimeTextAttr(int $value): string
{
return $value ? date('Y-m-d H:i:s', $value) : '-';
}
}
<?php
/**
* CRMEB电商系统
*
* PHP版本: 7.1-7.4
*
* 文件描述: 管理员服务类
* 主要功能: 处理管理员相关的业务逻辑
*
* @package app\services\system
* @author CRMEB团队
* @license Apache-2.0
* @link https://www.crmeb.com
* @since 2025-03-17
*/
namespace app\services\system;
<?php
/**
* 管理员服务类
*
* 该类负责处理管理员相关的所有业务逻辑,包括:
* - 管理员列表查询
* - 管理员添加/编辑
* - 管理员删除
* - 管理员权限验证
*
* @package app\services\system
*/
class SystemAdminServices extends BaseServices
{
// ...
}
<?php
/**
* 获取管理员列表
*
* 根据筛选条件查询管理员列表,支持分页、关键词搜索、状态筛选等
*
* @param array $where 筛选条件
* - keywords: 关键词搜索(账号、姓名)
* - status: 状态筛选(0禁用,1启用)
* - role_id: 角色ID筛选
* @param int $page 页码,默认为1
* @param int $limit 每页数量,默认为10
* @return array 返回包含列表数据和分页信息的数组
* - list: 管理员列表
* - count: 总数量
* - page: 当前页码
* - limit: 每页数量
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\db\exception\DbException
*
* @example
* ```php
* $services = new SystemAdminServices();
* $result = $services->getAdminList([
* 'keywords' => 'admin',
* 'status' => 1
* ], 1, 10);
* ```
*/
public function getAdminList(array $where, int $page = 1, int $limit = 10): array
{
// ...
}
<?php
public function calculateOrderPrice($carts, $coupon = null, $useIntegral = false)
{
// 1. 计算商品总价
$totalPrice = array_sum(array_column($carts, 'price'));
// 2. 应用优惠券优惠
if ($coupon) {
// 优惠券使用条件验证
if ($totalPrice >= $coupon['use_min_price']) {
$totalPrice -= $coupon['coupon_price']; // 减去优惠券金额
$totalPrice = max(0, $totalPrice); // 确保不小于0
}
}
// 3. 积分抵扣
if ($useIntegral) {
$integralPrice = $this->convertIntegralToPrice($userIntegral);
$totalPrice -= $integralPrice;
$totalPrice = max(0, $totalPrice);
}
// 4. 运费计算(满额包邮)
if ($totalPrice < $freeShippingPrice) {
$totalPrice += $shippingPrice;
}
return $totalPrice;
}
字段类型选择:
-- 整数类型
TINYINT(1) -- 状态字段(0/1)
SMALLINT -- 小范围整数(如分类ID)
INT -- 主键、用户ID、订单ID
BIGINT -- 大数值(如雪花算法ID)
-- 小数类型
DECIMAL(10,2) -- 金额、价格(精确计算)
FLOAT -- 非精确小数(少用)
DOUBLE -- 非精确大数值(少用)
-- 字符串类型
VARCHAR(255) -- 短字符串(账号、名称、标题)
VARCHAR(500) -- 中等长度(描述、简介)
TEXT -- 长文本(商品详情、文章内容)
-- 时间类型
INT(11) -- 时间戳(推荐,时区灵活)
DATETIME -- 日期时间(可视性好)
DATE -- 日期(仅日期)
TIME -- 时间(仅时间)
必须添加索引的字段:
-- 主键
PRIMARY KEY (id)
-- 外键
INDEX idx_user_id (user_id)
INDEX idx_order_id (order_id)
-- 查询条件字段
INDEX idx_status (status)
INDEX idx_is_del (is_del)
INDEX idx_create_time (create_time)
-- 组合索引(根据查询场景)
INDEX idx_user_status (user_id, status)
INDEX idx_create_type (create_time, type)
索引使用原则:
查询规范:
<?php
// ✅ 正确 - 使用查询构造器
$user = Db::name('user')
->where('id', $userId)
->where('status', 1)
->find();
// ✅ 正确 - 参数绑定(防SQL注入)
$users = Db::name('user')
->where('create_time', '>=', $startTime)
->where('create_time', '<', $endTime)
->where('status', $status)
->select();
// ❌ 错误 - 直接拼接SQL(SQL注入风险)
$sql = "SELECT * FROM eb_user WHERE id = $userId";
Db::query($sql);
// ❌ 错误 - 不使用参数绑定
Db::name('user')->where("id = $userId").find();
避免SELECT *:
<?php
// ❌ 错误 - 查询所有字段
$users = Db::name('user')->select();
// ✅ 正确 - 只查询需要的字段
$users = Db::name('user')
->field('id, account, real_name, status, create_time')
->select();
路由文件:
route/app.phproute/adminapi.phproute/api.php路由规范:
<?php
use think\facade\Route;
// ✅ 正确 - RESTful风格
Route::get('user/:id', 'User/read'); // 查询用户
Route::post('user', 'User/save'); // 创建用户
Route::put('user/:id', 'User/update'); // 更新用户
Route::delete('user/:id', 'User/delete'); // 删除用户
// ✅ 正确 - 路由分组
Route::group('user', function () {
Route::get('list', 'User/get_list'); // 用户列表
Route::get('info/:id', 'User/get_info'); // 用户信息
Route::post('save', 'User/save'); // 保存用户
Route::post('update', 'User/update'); // 更新用户
Route::post('delete/:id', 'User/delete'); // 删除用户
})->middleware([AuthTokenMiddleware::class]);
// ❌ 避免 - 不规范的路由
Route::get('getUserList', 'User/getUserList'); // 无需get前缀
Route::post('doSaveUser', 'User/doSave'); // 无需do前缀
HTTP方法使用规范:
| 方法 | 用途 | 示例 |
|---|---|---|
| GET | 查询数据 | GET /api/user/list |
| POST | 创建数据 | POST /api/user |
| PUT | 更新数据(完整更新) | PUT /api/user/:id |
| PATCH | 更新数据(部分更新) | PATCH /api/user/:id |
| DELETE | 删除数据 | DELETE /api/user/:id |
统一响应格式:
{
"code": 200,
"msg": "操作成功",
"data": {}
}
成功响应示例:
<?php
// 返回成功,无数据
return app('json')->success();
// 返回成功,带数据
return app('json')->success($data);
// 返回成功,自定义消息
return app('json')->success('登录成功');
// 返回成功,带数据和消息
return app('json')->success($data, '获取成功');
分页响应格式:
{
"code": 200,
"msg": "操作成功",
"data": {
"list": [],
"count": 100,
"page": 1,
"limit": 10
}
}
错误响应格式:
{
"code": 400,
"msg": "错误信息",
"data": null
}
<?php
// 返回错误
return app('json')->fail('参数错误');
// 返回错误,带错误码
return app('json')->fail('用户不存在', 404);
// 抛出异常(框架自动处理)
throw new ValidateException('手机号格式错误');
throw new AuthException('请先登录', 401);
系统状态码:
| 状态码 | 说明 |
|---|---|
| 200 | 操作成功 |
| 400 | 参数错误 |
| 401 | 未授权(未登录或token过期) |
| 403 | 无权限访问 |
| 404 | 资源不存在 |
| 429 | 请求过于频繁 |
| 500 | 服务器内部错误 |
| 502 | 服务不可用 |
业务状态码:
| 状态码 | 说明 |
|---|---|
| 1000 | 用户已存在 |
| 1001 | 账号或密码错误 |
| 1002 | 验证码错误 |
| 1003 | 用户已被禁用 |
| 2000 | 库存不足 |
| 2001 | 商品已下架 |
| 3000 | 订单不存在 |
| 3001 | 订单状态错误 |
| 4000 | 余额不足 |
| 5000 | 优惠券已过期 |
<?php
use think\facade\Cache;
// 设置缓存(1小时)
Cache::set('user_' . $uid, $user, 3600);
// 获取缓存
$user = Cache::get('user_' . $uid);
if (!$user) {
$user = User::find($uid);
Cache::set('user_' . $uid, $user, 3600);
}
// 删除缓存
Cache::delete('user_' . $uid);
// 清空缓存
Cache::clear();
// 使用Redis缓存
Cache::store('redis')->set('key', $value, 3600);
使用索引:
-- 为常用查询字段添加索引
CREATE INDEX idx_user_id ON eb_order(user_id);
CREATE INDEX idx_create_time ON eb_order(create_time);
CREATE INDEX idx_status ON eb_order(status);
-- 组合索引
CREATE INDEX idx_user_status ON eb_order(user_id, status);
避免N+1查询:
<?php
// 错误:循环中查询
foreach ($orders as $order) {
$user = User::find($order['user_id']); // N次查询
}
// 正确:预加载
$orders = Order::with('user')->select(); // 2次查询
<?php
// 将耗时任务放入队列
\think\facade\Queue::push(Job::class, $data, 'queue_name');
// 示例:发送短信通知
class SmsJob
{
public function fire($job, $data)
{
try {
// 发送短信
\app\services\sms\SmsServices::send($data['phone'], $data['template'], $data['params']);
// 成功删除任务
$job->delete();
} catch (\Exception $e) {
// 失败记录日志
\think\facade\Log::error('短信发送失败', [
'phone' => $data['phone'],
'error' => $e->getMessage()
]);
// 根据失败次数决定重试或删除
if ($job->attempts() > 3) {
$job->delete();
} else {
$job->release(60); // 60秒后重试
}
}
}
}
<?php
// ✅ 正确 - 使用参数绑定
$user = Db::name('user')
->where('account', $account)
->where('status', 1)
->find();
// ❌ 错误 - SQL注入风险
$sql = "SELECT * FROM eb_user WHERE account = '$account'";
Db::query($sql);
<?php
// 输入过滤
$input = htmlspecialchars($_GET['keyword'], ENT_QUOTES, 'UTF-8');
// 输出编码
echo htmlspecialchars($user['nickname'], ENT_QUOTES, 'UTF-8');
<?php
// 在表单中添加token
<input type="hidden" name="__token__" value="{:token()}">
// 验证token
if (!token_verify($request->post('__token__'))) {
throw new ValidateException('非法请求');
}
<?php
// 密码加密
$password = password_hash($plainPassword, PASSWORD_DEFAULT);
// 验证密码
if (password_verify($plainPassword, $hashedPassword)) {
// 密码正确
}
// 敏感数据脱敏
$phone = substr_replace($phone, '****', 3, 4);
$idCard = substr_replace($idCard, '********', 6, 8);
文档版本: v1.0.0 最后更新: 2025-03-17