1636 lines
46 KiB
Markdown
1636 lines
46 KiB
Markdown
# Ciyon后台管理系统 - PHP开发框架 Skill文档
|
||
|
||
## 系统概述
|
||
|
||
Ciyon后台管理系统是一个基于PHP8的SaaS平台后台管理框架,采用现代化的Web技术栈,提供完整的用户管理、权限控制、数据管理等核心功能。
|
||
|
||
### 技术特点
|
||
- **PHP命名空间**:采用现代化的命名空间组织代码
|
||
- **MVC架构**:控制器与视图分离,便于维护和扩展
|
||
- **API**:统一POST AJAX接口调用,返回JSON格式数据
|
||
- **安全机制**:完整的用户认证、权限控制、数据加密
|
||
- **数据库抽象**:基于PDO的数据库操作层,支持事务处理
|
||
- **模块化设计**:可插拔的功能模块,便于功能扩展
|
||
|
||
---
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
/web/
|
||
├── ambap/ # 某个移动端的API目录
|
||
├── admin/ # 后台管理主目录
|
||
│ ├── common.php # 后台管理公共函数库
|
||
│ ├── common.js # 前端公共JavaScript
|
||
│ ├── index.php # 后台管理主控制器
|
||
│ ├── index.html # 后台管理主页面
|
||
│ ├── login.php # 登录控制器
|
||
│ ├── login.html # 登录页面
|
||
│ ├── welcome.php # 欢迎页控制器
|
||
│ ├── welcome.html # 欢迎页面
|
||
│ ├── upload.php # 文件上传处理
|
||
│ ├── ap/ # 平台用户中心
|
||
│ │ ├── user.html # 平台用户管理前端
|
||
│ │ ├── user.php # 平台用户管理后端
|
||
│ │ └── ... # 其他平台功能模块
|
||
│ ├── saas1/ # SaaS租户管理,可能有多种机构角色,例如卖家/买家/代理等
|
||
│ │ ├── content.html # 内容管理前端
|
||
│ │ ├── content.php # 内容管理后端
|
||
│ │ └── ... # 其他模块
|
||
│ ├── autotask/ # 定时任务目录
|
||
│ │ ├── task.php # crond系统调用入口控制器
|
||
│ │ ├── run.php # 手动运行入口
|
||
│ │ ├── base.php # 定时任务函数库
|
||
│ │ └── ... # 其他定时任务函数库
|
||
│ └── [其他功能目录] # 如:cemap、datasse等
|
||
├── jscss/ # 前端资源
|
||
│ ├── ciy.js # 核心JS库
|
||
│ ├── ciycmp.js # 组件库
|
||
│ ├── ciycmp2.js # 组件库扩展
|
||
│ ├── ciytable.js # 列表组件库(表格、卡片)
|
||
│ └── style.css # 样式文件
|
||
└── cwebcomon.php # 公共函数定义类库
|
||
|
||
/zciyphp/ # PHP核心类库
|
||
├── comm.php # 通用公共函数库
|
||
├── db.php # 数据库操作类
|
||
├── sql.php # SQL构建类
|
||
├── post.php # POST参数处理类
|
||
├── web.php # Web工具类
|
||
├── html.php # HTML处理类
|
||
├── http.php # HTTP工具类
|
||
├── upload.php # 文件上传类
|
||
├── excel.php # Excel处理类
|
||
├── openai.php # AI接口类
|
||
├── smtp.php # 邮件发送类
|
||
└── ... # 其他工具类
|
||
```
|
||
|
||
---
|
||
|
||
## 核心架构
|
||
|
||
### 命名空间组织
|
||
系统采用命名空间进行代码组织:
|
||
- `web\admin` - 后台管理控制器命名空间
|
||
- `web\cwebcomon` - 公共函数定义类命名空间
|
||
- `ciy` - 核心工具类命名空间
|
||
|
||
### 控制器结构
|
||
控制器类采用静态方法模式,每个功能对应一个静态方法:
|
||
|
||
```php
|
||
namespace web\admin;
|
||
|
||
class index {
|
||
// 初始化页面数据
|
||
public static function json_init() { }
|
||
|
||
// 添加收藏菜单
|
||
public static function json_favadd() { }
|
||
|
||
// 删除收藏菜单
|
||
public static function json_favdel() { }
|
||
}
|
||
```
|
||
|
||
### 路由机制
|
||
系统通过路由自动调用对应的控制器方法:
|
||
- API URL格式:`/admin/index.init`
|
||
- 系统自动调用 `web\admin\index::json_init()`
|
||
- 所有AJAX接口方法名以 `json_` 开头
|
||
|
||
### 响应格式
|
||
统一使用JSON格式响应:
|
||
|
||
```php
|
||
// 成功响应
|
||
$ret['data'] = array();
|
||
return succjson($ret);
|
||
// 返回:{"code":1, "data":{...}}
|
||
|
||
// 错误响应
|
||
return errjson('错误信息', 错误码);
|
||
// 返回:{"code":0, "errmsg":"错误信息"}
|
||
```
|
||
|
||
---
|
||
|
||
## 开发规范
|
||
|
||
### 文件命名规范
|
||
- 控制器文件:`{功能名}.php`
|
||
- 视图文件:`{功能名}.html`
|
||
- 类名与文件名保持一致
|
||
- 方法命名:`json_{功能名}`(AJAX接口)
|
||
- API路径:/admin/{功能名}.{方法名}
|
||
- 所有代码,均被nginx引导至route.php路由。所有函数均为类静态函数。
|
||
|
||
### 代码组织规范
|
||
```php
|
||
<?php
|
||
namespace web\admin;
|
||
|
||
class {功能名} {
|
||
public static function json_init() {
|
||
// 1. 验证用户登录
|
||
$rsuser = verifyfast();
|
||
|
||
// 2. 业务逻辑处理
|
||
// ...
|
||
|
||
// 3. 返回数据
|
||
return succjson($ret);
|
||
}
|
||
|
||
// 其他功能方法
|
||
public static function json_{方法名}() {
|
||
// ...
|
||
}
|
||
}
|
||
```
|
||
|
||
### 数据验证规范
|
||
```php
|
||
// 获取POST参数
|
||
$post = new \ciy\post();
|
||
|
||
// 参数获取和验证
|
||
$id = $post->getint('id'); // 获取整数
|
||
$name = $post->get('name'); // 获取字符串(自动过滤HTML标签)
|
||
$html = $post->get('html', '', 'html'); // 获取字符串(自动过滤,但保留HTML标签)
|
||
$content = $post->get('content', '', 'all'); // 获取字符串,不做任何过滤
|
||
$price = $post->getfloat('price'); // 获取浮点数
|
||
$date = $post->getdate('date'); // 获取日期(转换为时间戳)
|
||
$isenable = $post->getbool('enable');// 获取布尔值
|
||
```
|
||
|
||
### 错误处理规范
|
||
```php
|
||
try {
|
||
// 业务逻辑
|
||
$db->begin();
|
||
// ... 数据库操作
|
||
$db->commit();
|
||
} catch (\Exception $ex) {
|
||
$db->rollback();
|
||
return errjson('操作失败:' . $ex->getMessage());
|
||
}
|
||
```
|
||
|
||
|
||
## 移动端API接口
|
||
|
||
### 移动端接口目录定义
|
||
- 每个移动端均指定统一目录实现API接口
|
||
例如: /web/ambap/
|
||
一般在前端jsnurl中设置。
|
||
|
||
### 前端函数调用方法
|
||
```
|
||
await this.callfunc({
|
||
func: 'main.index_init', // API函数名,格式:模块.方法
|
||
data: { // 要发送的数据
|
||
id: 123
|
||
},
|
||
});
|
||
```
|
||
|
||
### 后端API接口开发
|
||
- 在/web/ambap/建立 main.php文件
|
||
- 在main.php文件中建立 json_index_init() 静态函数
|
||
- 示例代码:/web/ambap/main.php 文件
|
||
```
|
||
<?php
|
||
namespace web\ambap;
|
||
|
||
class main {
|
||
public static function json_index_init() {
|
||
global $db;
|
||
$rsuser = verifyfast(); //如果用户未授权,直接返回JSON,后面代码不执行
|
||
$rsuser = verifyuser(); //如果用户未授权,$rsuser = null,不返回
|
||
$post = new \ciy\post();
|
||
$id = $post->getint('id');
|
||
$csql = new \ciy\sql('ap_art_post');
|
||
$csql->where('id', $id);
|
||
$artrow = $db->getone($csql);
|
||
if (!is_array($artrow))
|
||
return errjson('文章不存在' . $id);
|
||
$ret['data'] = $artrow;
|
||
return succjson($ret);
|
||
}
|
||
}
|
||
```
|
||
|
||
## PC端 增删改查完整示例
|
||
|
||
本章节通过一个完整的示例演示如何开发增删改查功能。
|
||
|
||
前端页面参考:/web/admin/demo/normal.html
|
||
后端页面参考:/web/admin/demo/normal.php
|
||
后端代码如下:
|
||
|
||
#### 查询列表接口
|
||
```php
|
||
namespace web\admin\demo;
|
||
|
||
class normal {
|
||
/**
|
||
* 查询条件构建(私有方法)
|
||
*/
|
||
static function setwhere($db, $post, $rsuser) {
|
||
$query = $post->get('query', array());
|
||
$csql = new \ciy\sql('demo_normal');
|
||
|
||
// 下拉筛选
|
||
$liid = objint($query, 'liid');
|
||
if ($liid > 0)
|
||
$csql->where('auditstatus', $liid);
|
||
|
||
// 文本模糊查询(关联字典表)
|
||
$val = objstr($query, 'audituser');
|
||
if (!empty($val)) {
|
||
$csqlt = new \ciy\sql('zc_cata');
|
||
$csqlt->where('cbid in (select id from zc_cata where cbid=0 and codeid=\'audituser\')');
|
||
$csqlt->where('name like', $val);
|
||
$trow = $db->getone($csqlt);
|
||
if (is_array($trow)) {
|
||
$csql->where('audituser', $trow['codeid']);
|
||
$query['audituser'] = $trow['name'];
|
||
} else {
|
||
$csql->where('audituser=0');
|
||
}
|
||
}
|
||
|
||
// 日期范围查询。无需判断,objstr返回空字符串则where自动跳过。
|
||
$csql->wheredaterange('audittimes', objstr($query, 'audittimes'));
|
||
|
||
// 文本模糊查询。无需判断,objstr返回空字符串则where自动跳过。
|
||
$csql->where('auditmsg like', objstr($query, 'auditmsg'));
|
||
$csql->where('name like', objstr($query, 'name'));
|
||
|
||
// 关联表查询
|
||
$val = objstr($query, 'menuid');
|
||
if (!empty($val)) {
|
||
$csqlt = new \ciy\sql('zc_menu');
|
||
$csqlt->where('name like', $val);
|
||
$trow = $db->getone($csqlt);
|
||
if (is_array($trow)) {
|
||
$csql->where('menuid', $trow['id']);
|
||
$query['menuid'] = $trow['name'];
|
||
} else {
|
||
$csql->where('menuid=0');
|
||
}
|
||
}
|
||
|
||
// 单选筛选
|
||
$csql->where('isopen', objstr($query, 'isopen'));
|
||
|
||
// 多选筛选(存储格式:,1,2,)
|
||
$csql->where('mauditstatus like', ',' . objstr($query, 'mauditstatus') . ',');
|
||
|
||
// 数值范围查询(带倍数转换)
|
||
$csql->wherenumrange('ton', objstr($query, 'ton_1'), objstr($query, 'ton_2'), 1000000);
|
||
|
||
// 排序
|
||
$order = objstr($query, 'order', 'id desc');
|
||
$csql->order($order);
|
||
$query['order'] = $order;
|
||
|
||
return [$query, $csql];
|
||
}
|
||
|
||
/**
|
||
* 列表查询接口
|
||
*/
|
||
public static function json_list() {
|
||
global $db;
|
||
$rsuser = verifyfast();
|
||
$post = new \ciy\post();
|
||
|
||
// 构建查询条件
|
||
list($where, $csql) = self::setwhere($db, $post, $rsuser);
|
||
|
||
// 排除大字段内容字段
|
||
$csql->column('!content,md', $db->getraw('show full fields from demo_normal'));
|
||
|
||
// 分页
|
||
$pageno = $post->getint('pageno', 1);
|
||
$pagecount = $post->getint('pagecount', 10);
|
||
$csql->limit($pageno, $pagecount);
|
||
|
||
// 查询数据
|
||
$mainrowcount = $post->getint('count');
|
||
$mrows = $db->get($csql, $mainrowcount);
|
||
if ($mrows === false)
|
||
return errjson($db->error);
|
||
|
||
$ret['searchwhere'] = $where;
|
||
$ret['pageno'] = $pageno;
|
||
$ret['pagecount'] = $pagecount;
|
||
$ret['count'] = $mainrowcount;
|
||
$ret['list'] = $mrows;
|
||
|
||
// 获取字段信息(用于显示配置,字段备注[c]前边有小写逗号,则代表前端不显示该字段)
|
||
if ($post->getbool('field')) {
|
||
$field = array();
|
||
$fshow = $db->getfield($field, 'demo_normal');
|
||
foreach ($field as $fr => $v) {
|
||
if (get('_' . $fr))
|
||
$field[$fr]['c'] = ',' . $field[$fr]['c'];
|
||
}
|
||
$fshow = fieldadd($fshow, $field, 0, '_btn', '操作');
|
||
$ret['field'] = $field;
|
||
$ret['fshow'] = $fshow;
|
||
}
|
||
|
||
// 初始化数据(本页面只执行一次)
|
||
if ($post->getbool('once')) {
|
||
$ret['once'] = true;
|
||
$input = array();
|
||
$input[] = array('type' => 'input', 'form' => 'audituser', 'name' => '审核人', 'prop' => ' style="width:8em;"');
|
||
$input[] = array('type' => 'daterange', 'form' => 'audittimes', 'name' => '审核时间');
|
||
$input[] = array('type' => 'input', 'form' => 'auditmsg', 'name' => '审核理由', 'prop' => ' style="width:8em;"');
|
||
$input[] = array('type' => 'input', 'form' => 'name', 'name' => '默认标题', 'prop' => ' style="width:8em;"');
|
||
$input[] = array('type' => 'input', 'form' => 'menuid', 'name' => '所属菜单', 'prop' => ' style="width:8em;"');
|
||
$input[] = array('type' => 'select', 'form' => 'isopen', 'name' => '是否开启', 'all' => '全部', 'select' => '开启.关闭');
|
||
$input[] = array('type' => 'select', 'form' => 'mauditstatus', 'name' => '多选状态', 'all' => '全部', 'select' => 'auditstatus');
|
||
$input[] = array('type' => 'num', 'form' => 'ton', 'name' => '吨位', 'prop' => ' style="width:4em;"');
|
||
$ret['searchinput'] = $input;
|
||
|
||
// 加载全量关联数据
|
||
$csql = (new \ciy\sql('zc_depart'))->column('id,name,upid');
|
||
$ret['zc_depart'] = $db->get($csql);
|
||
}
|
||
|
||
// 每页增量关联数据(用于表格显示)
|
||
$ret['zc_menu'] = getrelation($db, $mrows, 'zc_menu', 'menuid');
|
||
|
||
return succjson($ret);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 获取数据接口
|
||
```php
|
||
/**
|
||
* 获取单条数据(用于编辑/查看)
|
||
*/
|
||
public static function json_getdata() {
|
||
global $db;
|
||
$rsuser = verifyfast();
|
||
$post = new \ciy\post();
|
||
$id = $post->getint('id');
|
||
$act = $post->get('act');
|
||
|
||
$mrow = array();
|
||
if ($id > 0) {
|
||
$csql = new \ciy\sql('demo_normal');
|
||
$csql->where('id', $id);
|
||
$mrow = $db->getone($csql);
|
||
if (!is_array($mrow))
|
||
return errjson('数据不存在');
|
||
|
||
// 根据操作类型加载该条关联数据
|
||
if ($act == 'view' || $act == 'review') {
|
||
$csql = (new \ciy\sql('zc_menu'))->column('id,name');
|
||
$csql->where('id', $mrow['menuid']);
|
||
$ret['zc_menu'] = $db->get($csql);
|
||
}
|
||
}
|
||
|
||
$ret['data'] = $mrow;
|
||
|
||
// 编辑时加载所有关联数据
|
||
if ($act == 'edit') {
|
||
$csql = (new \ciy\sql('zc_menu'))->column('id,name');
|
||
$ret['zc_menu'] = $db->get($csql);
|
||
}
|
||
|
||
return succjson($ret);
|
||
}
|
||
```
|
||
|
||
#### 新增/更新接口
|
||
```php
|
||
/**
|
||
* 新增/更新数据
|
||
*/
|
||
public static function json_update() {
|
||
global $db;
|
||
$rsuser = verifyfast();
|
||
|
||
// 权限检查。权限定义在菜单表zc_menu中
|
||
// if (nopower($db, $rsuser['id'], 'pxxxu'))
|
||
// return errjson('您未被授权操作');
|
||
|
||
$post = new \ciy\post();
|
||
|
||
// 获取参数
|
||
$id = $post->getint('id');
|
||
$name = $post->get('name');
|
||
$menuid = $post->getint('menuid');
|
||
$filesize = $post->getint('filesize');
|
||
$content = $post->get('content');
|
||
// ... 其他字段
|
||
|
||
// 参数验证
|
||
if (empty($name))
|
||
return errjson('请填写默认标题');
|
||
|
||
$datarow = null;
|
||
if ($id > 0) {
|
||
// 更新模式:获取原数据
|
||
$csql = new \ciy\sql('demo_normal');
|
||
$csql->where('id', $id);
|
||
$datarow = $db->getone($csql);
|
||
if (!is_array($datarow))
|
||
return errjson('数据不存在');
|
||
}
|
||
|
||
try {
|
||
$db->begin();
|
||
|
||
// 构建更新数据
|
||
$updata = array();
|
||
$updata['name'] = $name;
|
||
$updata['menuid'] = $menuid;
|
||
$updata['filesize'] = $filesize;
|
||
$updata['content'] = $content;
|
||
// ... 其他字段
|
||
|
||
$csql = new \ciy\sql('demo_normal');
|
||
|
||
if ($id > 0) {
|
||
// 更新
|
||
$csql->where('id', $id);
|
||
if ($db->update($csql, $updata) === false)
|
||
throw new \Exception('更新失败:' . $db->error);
|
||
} else {
|
||
// 新增
|
||
$updata['auditstatus'] = 0;
|
||
$updata['audituser'] = 0;
|
||
$updata['audittimes'] = 0;
|
||
$updata['auditmsg'] = '';
|
||
$updata['addtimes'] = tostamp();
|
||
if ($db->insert($csql, $updata) === false)
|
||
throw new \Exception('新增失败:' . $db->error);
|
||
$id = $db->insert_id();
|
||
}
|
||
|
||
$updata['id'] = $id;
|
||
|
||
// 记录日志(可选)
|
||
// savelogdb($db, $rsuser['id'], 'demo_normal', $datarow, $updata);
|
||
|
||
$db->commit();
|
||
} catch (\Exception $ex) {
|
||
$db->rollback();
|
||
savelogfile('err_db', $ex->getMessage());
|
||
return errjson($ex->getMessage());
|
||
}
|
||
|
||
// 返回更新后的数据
|
||
$ret['data'] = $updata;
|
||
$ret['zc_menu'] = getrelation($db, [$updata], 'zc_menu', 'menuid');
|
||
return succjson($ret);
|
||
}
|
||
```
|
||
|
||
#### 删除接口
|
||
```php
|
||
/**
|
||
* 删除数据
|
||
*/
|
||
public static function json_del() {
|
||
global $db;
|
||
$rsuser = verifyfast();
|
||
|
||
// 权限检查(可选)
|
||
// if (nopower($db, $rsuser['id'], 'pxxxd'))
|
||
// return errjson('您未被授权操作');
|
||
|
||
$post = new \ciy\post();
|
||
$ids = $post->get('ids');
|
||
|
||
if (empty($ids))
|
||
return errjson('请选择至少一条');
|
||
|
||
// 查询要删除的数据
|
||
$csql = new \ciy\sql('demo_normal');
|
||
$csql->where('id in', $ids);
|
||
$mrows = $db->get($csql);
|
||
|
||
$vids = array();
|
||
try {
|
||
$db->begin();
|
||
|
||
foreach ($mrows as $mrow) {
|
||
//可针对$mrow做是否可删除判断,不可删除用continue跳过
|
||
$delid = $mrow['id'];
|
||
|
||
// 检查提示有关联数据(可选)
|
||
// delcheck($db, $delid, 'tablexx', 'xxid', '关联数据');
|
||
|
||
// 直接删除关联数据(可选)
|
||
// delall($db, $delid, 'tablexx', 'xxid', '关联数据');
|
||
|
||
// 删除主数据
|
||
delme($db, $delid, 'demo_normal');
|
||
|
||
// 记录日志
|
||
savelogdb($db, $rsuser['id'], 'demo_normal', $mrow, null);
|
||
|
||
$vids[] = $delid;
|
||
}
|
||
|
||
$db->commit();
|
||
} catch (\Exception $ex) {
|
||
$db->rollback();
|
||
savelogfile('err_db', $ex->getMessage());
|
||
return errjson($ex->getMessage());
|
||
}
|
||
|
||
$ret['ids'] = $vids;
|
||
return succjson($ret);
|
||
}
|
||
```
|
||
|
||
#### 审核接口(自定义业务)
|
||
```php
|
||
/**
|
||
* 审核接口
|
||
*/
|
||
public static function json_audit() {
|
||
global $db;
|
||
$rsuser = verifyfast();
|
||
|
||
// 权限检查(可选)
|
||
// if (nopower($db, $rsuser['id'], 'p a'))
|
||
// return errjson('您未被授权操作');
|
||
|
||
$post = new \ciy\post();
|
||
$ids = $post->get('ids');
|
||
|
||
if (empty($ids))
|
||
return errjson('请选择至少一条');
|
||
|
||
$auditstatus = $post->getint('auditstatus');
|
||
$auditmsg = $post->get('auditmsg');
|
||
|
||
// 驳回时必须填写原因
|
||
if ($auditstatus == 90 && empty($auditmsg))
|
||
return errjson('请填写驳回原因');
|
||
|
||
// 查询数据
|
||
$csql = new \ciy\sql('demo_normal');
|
||
$csql->where('id in', $ids);
|
||
$mrows = $db->get($csql);
|
||
|
||
$ids = array();
|
||
try {
|
||
$db->begin();
|
||
|
||
foreach ($mrows as $mrow) {
|
||
if ($auditstatus == 100) {
|
||
// 审核通过时的额外处理
|
||
}
|
||
|
||
// 更新审核状态
|
||
$updata = array();
|
||
$updata['auditstatus'] = $auditstatus;
|
||
$updata['audituser'] = $rsuser['id'];
|
||
$updata['audittimes'] = tostamp();
|
||
$updata['auditmsg'] = $auditmsg;
|
||
|
||
$csql = new \ciy\sql('demo_normal');
|
||
$csql->where('id', $mrow['id']);
|
||
if ($db->update($csql, $updata) === false)
|
||
throw new \Exception('审核失败:' . $db->error);
|
||
|
||
$ids[] = $mrow['id'];
|
||
}
|
||
|
||
$db->commit();
|
||
} catch (\Exception $ex) {
|
||
$db->rollback();
|
||
savelogfile('err_db', $ex->getMessage());
|
||
return errjson($ex->getMessage());
|
||
}
|
||
|
||
$ret['data'] = $updata;
|
||
$ret['ids'] = $ids;
|
||
return succjson($ret);
|
||
}
|
||
```
|
||
|
||
## 核心类库详解
|
||
|
||
### 1. 用户认证系统 (common.php)
|
||
|
||
#### verifyfast() - 快速用户验证
|
||
```php
|
||
$rsuser = verifyfast();
|
||
```
|
||
- 自动验证用户登录状态
|
||
- 未登录时自动返回JSON错误响应,中断后面代码执行
|
||
- 超时自动续期
|
||
|
||
#### verifyuser() - 用户验证
|
||
```php
|
||
$rsuser = verifyuser();
|
||
```
|
||
- 验证用户登录状态
|
||
- 返回用户信息数组或null
|
||
- 需要手动处理未登录情况
|
||
|
||
#### nopower() - 权限验证
|
||
```php
|
||
if (nopower($db, $userid, 'x1ssh'))
|
||
return errjson('您未被授权操作');
|
||
```
|
||
- 检查用户是否有指定权限
|
||
- 权限格式:`{模块}{操作}`,如 `x1ssh` 表示SSH模块访问权限
|
||
- 超级管理员权限:`.*.`
|
||
- 权限是字符串存储,多个权限两边都有句点。例如 .x1ssh.v4del.
|
||
|
||
### 2. 配置管理 (common.php)
|
||
|
||
#### getconfig() - 获取配置
|
||
```php
|
||
$apiid = getconfig($db, 'apiid', '');
|
||
```
|
||
- 从 `zc_config` 表读取配置
|
||
- 支持默认值
|
||
|
||
#### setconfig() - 设置配置
|
||
```php
|
||
setconfig($db, 'apiid', 'newvalue');
|
||
```
|
||
- 更新配置到数据库
|
||
- 配置不存在时自动创建
|
||
|
||
### 3. 日志系统 (common.php)
|
||
|
||
#### savelog() - 保存日志
|
||
```php
|
||
savelog($db, $userid, 'LOGIN', '用户登录', false);
|
||
```
|
||
- 记录操作日志到 `zc_log` 表
|
||
- 支持记录完整的请求信息
|
||
|
||
#### savelogdb() - 数据库变更日志
|
||
```php
|
||
savelogdb($db, $userid, 'UPDATE', $oldrow, $newrow);
|
||
```
|
||
- 自动记录数据变更
|
||
- 对比新旧数据,只记录变更字段
|
||
- 添加、修改、删除
|
||
|
||
---
|
||
|
||
## 数据库操作
|
||
|
||
### 1. 数据库连接
|
||
```php
|
||
global $db;
|
||
// $db 自动从配置文件中读取数据库配置并连接
|
||
```
|
||
|
||
### 2. SQL构建类 (sql.php)
|
||
|
||
#### 基本查询
|
||
```php
|
||
$csql = new \ciy\sql('table_name');
|
||
$csql->column('id,name,addtimes');
|
||
$csql->where('status', 1);
|
||
$csql->order('addtimes desc');
|
||
$rows = $db->get($csql);
|
||
```
|
||
|
||
#### 分页查询
|
||
```php
|
||
$csql = new \ciy\sql('table_name');
|
||
$rowcount = 0;
|
||
$rows = $db->get($csql, $rowcount); // 返回总数据条数
|
||
```
|
||
|
||
#### 单条查询
|
||
```php
|
||
$csql = new \ciy\sql('table_name');
|
||
$csql->where('id', $id);
|
||
$mrow = $db->getone($csql); //可能返回Array有数据、Null无数据、false数据库错误
|
||
if(!is_array($mrow))
|
||
return errjson('没找到数据');
|
||
```
|
||
|
||
#### 查询单个字段
|
||
```php
|
||
$csql = new \ciy\sql('table_name');
|
||
$csql->column('name');
|
||
$csql->where('id', $id);
|
||
$name = $db->get1($csql);
|
||
```
|
||
|
||
#### 统计数据
|
||
```php
|
||
$csql = new \ciy\sql('table_name');
|
||
$csql->column('count(*)');
|
||
$csql->where('status', 1);
|
||
$count = $db->get1($csql);
|
||
```
|
||
|
||
|
||
#### 条件查询
|
||
```php
|
||
// LIKE查询
|
||
$csql->where('name like', 'keyword');
|
||
|
||
// IN查询
|
||
$csql->where('id in', '1,2,3');
|
||
|
||
// 范围查询
|
||
$csql->where('price>=', 100);
|
||
$csql->where('price<=', 500);
|
||
|
||
// 日期范围
|
||
$csql->wheredaterange('addtimes', '2024-01-01~2024-12-31', 'date');
|
||
$csql->wheredaterange('addtimes', '-7~7', 'day'); //查询上周至下周的近两周数据
|
||
$csql->wheredaterange('addtimes', '2024-01', 'month'); //查询2024年1月,整月数据
|
||
|
||
// 数值范围
|
||
$csql->wherenumrange('price', '', '20', 100); //查询小于20元的数据,数据表实际按分存储,20元=2000,100为倍数,默认1。
|
||
$csql->wherenumber('tempc', '38~', 1000); //查询大于38度的数据,原始数据,例如38.23°,数据库实际存储整数:38230
|
||
$csql->wherenumber('status', '>=10'); //查询大于等于10的数据。
|
||
|
||
// 多参数符合查询
|
||
$csql->where('(name like ? or prodname like ?) and status=?', ['名字', '产品', 10]);
|
||
|
||
// 自由查询(非必要不使用)
|
||
$csql->where('id=3');
|
||
|
||
// 添加sql末尾数据
|
||
$csql->selecttail('group by xxx');
|
||
|
||
// 选项卡筛选(状态类型筛选)
|
||
$liid = objint($query, 'liid');
|
||
if ($liid === 1)
|
||
$csql->where('xxx>', 0); // liid=1选项卡
|
||
else if ($liid === 2)
|
||
$csql->where('xxstatus', 10);// liid=2选项卡
|
||
else
|
||
$csql->where('exptimes>', tostamp());// 默认选项卡
|
||
|
||
if ($liid > 0)
|
||
$csql->where('xxxtype', $liid); // 字典项选项卡
|
||
|
||
// 时间范围筛选(未到期/已到期)
|
||
if ($liid === 1)
|
||
$csql->where('expiretimes>', tostamp()); // 未到期
|
||
else if ($liid === 2)
|
||
$csql->where('expiretimes<=', tostamp()); // 已到期
|
||
|
||
// 按关联表名称查询
|
||
$topicid = objstr($query, 'topicid');
|
||
if (!empty($topicid)) {
|
||
$csqlt = new \ciy\sql('am_topicbase');
|
||
$csqlt->where('name like', $topicid);
|
||
$trow = $db->getone($csqlt);
|
||
if (is_array($trow)) {
|
||
$csql->where('topicid', $trow['id']);
|
||
$query['topicid'] = $trow['name'];
|
||
} else {
|
||
$csql->where('topicid=0');
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 数据操作
|
||
|
||
#### 插入数据
|
||
```php
|
||
$updata = array(
|
||
'name' => '测试',
|
||
'status' => 1,
|
||
'addtimes' => tostamp()
|
||
);
|
||
$csql = new \ciy\sql('table_name');
|
||
$db->insert($csql, $updata);
|
||
$newid = $db->insert_id();
|
||
```
|
||
|
||
#### 更新数据
|
||
```php
|
||
$updata = array(
|
||
'name' => '更新后的名称',
|
||
'status' => 2
|
||
);
|
||
$csql = new \ciy\sql('table_name');
|
||
$csql->where('id', $id);
|
||
$db->update($csql, $updata);
|
||
```
|
||
|
||
#### 删除数据
|
||
```php
|
||
$csql = new \ciy\sql('table_name');
|
||
$csql->where('id', $id);
|
||
$db->delete($csql); // 直接删除
|
||
$db->delete($csql, true); // 删除前备份到table_name_bak表
|
||
```
|
||
|
||
#### 删除数据(批量)
|
||
```php
|
||
public static function json_del() {
|
||
global $db;
|
||
$rsuser = verifyfast();
|
||
$post = new \ciy\post();
|
||
$ids = $post->get('ids');
|
||
if (empty($ids))
|
||
return errjson('请选择至少一条');
|
||
$csql = new \ciy\sql('table_name');
|
||
$csql->where('id in', $ids);
|
||
$mrows = $db->get($csql);
|
||
$vids = array();
|
||
try {
|
||
$db->begin();
|
||
foreach ($mrows as $mrow) {
|
||
$delid = $mrow['id'];
|
||
//delcheck($db, $delid, 'tablexx', 'xxid', '管理员'); //发现该表有相关数据不允许删除
|
||
//delall($db, $delid, 'tablexx', 'xxid', '运动员'); //该表有相关数据一起直接删除
|
||
delme($db, $delid, 'table_name');
|
||
savelogdb($db, $rsuser['id'], 'table_name', $mrow, null);
|
||
$vids[] = $delid;
|
||
}
|
||
$db->commit();
|
||
} catch (\Exception $ex) {
|
||
$db->rollback();
|
||
savelogfile('err_db', $ex->getMessage());
|
||
return errjson($ex->getMessage());
|
||
}
|
||
$ret['ids'] = $vids;
|
||
return succjson($ret);
|
||
}
|
||
```
|
||
|
||
**前端调用**:
|
||
```javascript
|
||
<a class="btn dag" onclick="ciyfn.select_callfunc(table, this, 'del','已选{n}条,是否批量删除?', {},function(json){table.delline(json)})">批量删除</a>
|
||
```
|
||
|
||
**要点**:
|
||
- 使用 `ciyfn.select_callfunc()` 框架方法处理批量选择和确认
|
||
- 后端返回删除的ID数组 `$ret['ids']`
|
||
- 前端通过 `table.delline(json)` 自动删除表格中的对应行
|
||
- 批量删除按钮使用 `dag` class(红色警告样式)
|
||
|
||
#### 统一的事务处理
|
||
```php
|
||
try {
|
||
$db->begin();
|
||
// 多个数据库操作
|
||
$db->insert($csql1, $data1);
|
||
$db->update($csql2, $data2);
|
||
$db->commit();
|
||
} catch (\Exception $ex) {
|
||
$db->rollback();
|
||
return errjson('操作失败:' . $ex->getMessage());
|
||
}
|
||
return succjson();
|
||
```
|
||
|
||
---
|
||
|
||
## 用户认证与权限
|
||
|
||
### 登录流程 (login.php)
|
||
框架已实现,不定制新需求,无需生成新代码。
|
||
|
||
|
||
**数据字典缓存机制**
|
||
登录时会一次性返回所有字典数据到前端
|
||
|
||
**前端使用字典数据**
|
||
|
||
```javascript
|
||
// 使用字典数据
|
||
var statusList = ciyfn.getstorage('cata_auditstatus');
|
||
```
|
||
|
||
### Token机制
|
||
|
||
#### Token配置 (common.php)
|
||
```php
|
||
$_token['type'] = 'cookie'; // 存储方式:cookie/localstorage。移动端为兼容微信小程序,需使用localstorage
|
||
$_token['swapsec'] = 3600; // 更换Token时间(秒)
|
||
$_token['expsec'] = 86400 * 30; // 过期退出时间(秒)
|
||
$_token['field'] = 'ciyadm'; // Cookie字段名
|
||
$_token['salt'] = 'bka02$59gG'; // 加密盐值
|
||
```
|
||
|
||
#### Token验证流程
|
||
1. 客户端发送请求时携带Token
|
||
2. 服务端解密Token获取用户ID
|
||
3. 从 `zc_online` 表验证会话有效性
|
||
4. 超时自动续期,返回新Token
|
||
|
||
### 权限控制
|
||
|
||
#### 权限格式
|
||
- 格式:`{模块}{操作}`
|
||
- 示例:`x1ssh` 表示SSH模块访问权限
|
||
- 超级管理员:`.*.`
|
||
|
||
#### 权限验证
|
||
```php
|
||
// 检查用户是否有权限
|
||
if (nopower($db, $rsuser['id'], 'x1ssh'))
|
||
return errjson('您未被授权操作');
|
||
|
||
// 前端权限检查
|
||
if (ciyfn.nopower(me.power, 'x1ssh'))
|
||
continue; // 无权限
|
||
```
|
||
|
||
|
||
## API开发规范
|
||
|
||
### PC端接口命名规范
|
||
- 所有AJAX接口方法名以 `json_` 开头
|
||
- 方法名采用小写+下划线格式
|
||
- 示例:`json_init`, `json_update`, `json_del`
|
||
|
||
### 移动端接口命名规范
|
||
- API方法名应与前端页面文件名对应,便于识别和维护。格式建议:`json_{vue页面名}_{操作}`
|
||
- 示例:`aimap.php` 文件中的函数 `json_index_init` 对应 `/pages/aimap/index.vue`,前端调用 `func: 'aimap.index_init'`
|
||
- 示例:`json_aimap_msglst_init` 对应 `/pages/aimap/msglst.vue`,前端调用 `func: 'aimap.msglst_init'`
|
||
|
||
- **用户认证接口复用**:注册、登录、找回密码等通用认证接口已内置,无需重复开发,直接复用框架提供的 `ciy-auth` 组件接口
|
||
- **首次请求优化**:页面首次加载使用 `init` 接口,通过 `once` 标志位一次性返回所有初始化数据,减少请求次数
|
||
|
||
### 接口参数处理
|
||
```php
|
||
public static function json_userlist() {
|
||
// 1. 验证用户登录
|
||
$rsuser = verifyfast();
|
||
|
||
// 2. 获取参数
|
||
$post = new \ciy\post();
|
||
$pageno = $post->getint('pageno', 1);
|
||
$pagecount = $post->getint('pagecount', 20);
|
||
$keyword = $post->get('keyword');
|
||
|
||
// 3. 业务逻辑处理
|
||
$csql = new \ciy\sql('zc_admin');
|
||
if (!empty($keyword)) //通常无需判断,where子句会判断$keyword是否为空
|
||
$csql->where('name like', $keyword);
|
||
$csql->order('addtimes desc');
|
||
$rowcount = -1; //rowcount=-1 则需要count(*)获得数据统计,>-1则跳过统计查询,避免翻页时频繁查询统计数据。
|
||
$rows = $db->get($csql, $rowcount);
|
||
|
||
// 4. 返回数据
|
||
$ret['rows'] = $rows;
|
||
$ret['rowcount'] = $rowcount;
|
||
return succjson($ret);
|
||
}
|
||
```
|
||
|
||
### 首次请求优化
|
||
|
||
```php
|
||
/**
|
||
* 页面初始化接口
|
||
* 通过once标志位一次性返回所有初始化数据
|
||
*/
|
||
public static function json_aimap_index_init() {
|
||
global $db;
|
||
$rsuser = verifyfast();
|
||
$post = new \ciy\post();
|
||
if ($post->getbool('once')) { //仅页面首次加载,翻页请求不再执行
|
||
$ret['once'] = true;
|
||
$csql = new \ciy\sql('table_name');
|
||
$ret['tabxxx'] = $db->get($csql);
|
||
}
|
||
$csql = new \ciy\sql('table_xxx');
|
||
$csql->order('addtimes desc');
|
||
$csql->limit($post->getint('pageno', 1), 20);
|
||
$ret['list'] = $db->get($csql);
|
||
return succjson($ret);
|
||
}
|
||
```
|
||
|
||
### 错误处理
|
||
```php
|
||
// 参数验证
|
||
if (empty($id))
|
||
return errjson('ID不能为空');
|
||
|
||
// 数据验证
|
||
if ($rsuser === null)
|
||
return errjson($db->error);
|
||
|
||
// 数据是否存在
|
||
$mrow = $db->getone(xxx);
|
||
if (!is_array($mrow)) //mrow有可能返回null或false,因此需使用is_array()
|
||
return errjson($db->error);
|
||
|
||
// 权限验证
|
||
if (nopower($db, $rsuser['id'], 'x1user'))
|
||
return errjson('您未被授权操作');
|
||
|
||
```
|
||
|
||
## 最佳实践
|
||
|
||
### 数据库操作优化
|
||
|
||
#### 批量操作
|
||
```php
|
||
// 使用事务进行批量操作
|
||
$db->begin();
|
||
try {
|
||
foreach ($datalist as $data) {
|
||
$db->insert($csql, $data);
|
||
}
|
||
$db->commit();
|
||
} catch (\Exception $ex) {
|
||
$db->rollback();
|
||
}
|
||
```
|
||
|
||
#### 避免N+1查询
|
||
```php
|
||
// 使用关联查询,按需查询可视化需要的数据
|
||
// 将$rows中userid数据去重后,查询 select id,name from table_name where id in (userid1,userid2,...),返回{id,name}数组
|
||
$ret['xxtable'] = getrelation($db, $rows, 'table_name', 'userid', 'id,name');
|
||
```
|
||
|
||
### 安全最佳实践
|
||
|
||
#### SQL注入防护
|
||
```php
|
||
// ✅ 使用参数化查询
|
||
$csql->where('id', $id);
|
||
$db->get($csql);
|
||
|
||
// ❌ 避免字符串拼接
|
||
$sql = "select * from table where id = " . $id;
|
||
```
|
||
|
||
#### XSS防护
|
||
```php
|
||
// 使用post类自动过滤
|
||
$post = new \ciy\post();
|
||
$name = $post->get('name'); // 自动strip_tags
|
||
|
||
// 需要HTML内容时
|
||
$html = $post->get('content', '', 'html'); // 安全过滤HTML
|
||
```
|
||
|
||
#### CSRF防护
|
||
```php
|
||
// 验证请求来源
|
||
if (!isset($_SERVER['HTTP_REFERER']))
|
||
return errjson('非法请求');
|
||
```
|
||
|
||
### 性能优化
|
||
|
||
#### 分页优化
|
||
```php
|
||
// 大数据量分页
|
||
$rowcount = -1; // 只有赋值-1时,才查询总数,其他数值则不查询总数
|
||
$csql->column('*');
|
||
$csql->limit($pageno, $pagecount);
|
||
$rows = $db->get($csql, $rowcount);
|
||
```
|
||
|
||
### 代码复用
|
||
|
||
#### 公共函数封装
|
||
```php
|
||
// 在cwebcomon.php中定义公共函数,只有1处调用时,不要定义到公共函数。
|
||
namespace web;
|
||
|
||
class cwebcomon {
|
||
function getuserlist($db, $keyword = '') {
|
||
$csql = new \ciy\sql('zc_admin');
|
||
if (!empty($keyword))
|
||
$csql->where('name like', $keyword);
|
||
$csql->order('addtimes desc');
|
||
return $db->get($csql);
|
||
}
|
||
}
|
||
|
||
```
|
||
|
||
#### 公共类库使用
|
||
```php
|
||
$rows = \web\cwebcomon::getuserlist($db, $keyword);
|
||
```
|
||
|
||
#### 日期工具
|
||
```
|
||
\ciy\sql::daterange('2024-01-01~2024-12-31', 'date'); //返回时间戳,与sql的wheredaterange函数机制一致
|
||
```
|
||
|
||
|
||
## 数据字典设计规范
|
||
|
||
参考 ciyon-数据字典设计.md
|
||
|
||
|
||
## 附录
|
||
|
||
### 数据库表结构
|
||
|
||
#### 已存在的核心表
|
||
- `zc_admin` - 管理员表
|
||
- `zc_role` - 角色表
|
||
- `zc_depart` - 组织机构表
|
||
- `zc_online` - 在线会话表
|
||
- `zc_cata` - 数据字典表
|
||
- `zc_menu` - 菜单表
|
||
- `zc_mnufav` - 收藏菜单表
|
||
- `zc_log` - 操作日志表
|
||
- `zc_lug` - 登录日志表
|
||
- `zc_config` - 配置表
|
||
- `zc_autotask` - 自动任务表
|
||
- `zc_mq` - 消息队列表 **需DB事务级一致,可用此队列过渡**
|
||
|
||
### comm.php 公共函数库详解
|
||
|
||
comm.php是Ciyon框架的核心公共函数库,提供了丰富的工具函数,涵盖日期处理、加密解密、字符串处理、数据库操作、文件操作等多个方面。
|
||
|
||
#### 日期时间函数
|
||
|
||
**tostamp() - 转换为时间戳**
|
||
```php
|
||
// 字符串转时间戳
|
||
$timestamp = tostamp('2024-03-14 10:30:00');
|
||
|
||
// 数字转时间戳
|
||
$timestamp = tostamp(1710385800);
|
||
|
||
// 当前时间
|
||
$timestamp = tostamp('now');
|
||
```
|
||
|
||
**todate() - 时间戳转字符串**
|
||
```php
|
||
// 默认格式:Y-m-d H:i
|
||
$date = todate(1710385800); // '2024-03-14 10:30'
|
||
|
||
// 指定格式
|
||
$date = todate(1710385800, 'd'); // '2024-03-14'
|
||
$date = todate(1710385800, 's'); // '2024-03-14 10:30:00'
|
||
$date = todate(1710385800, 'm'); // '2024-03'
|
||
$date = todate(1710385800, 'H'); // '2024-03-14 10'
|
||
$date = todate(1710385800, 'hi'); // '10:30'
|
||
```
|
||
|
||
**fixmonth() - 月份加减修正**
|
||
```php
|
||
// 修正PHP月份加减的错误(如1月31日加1个月应为2月28/29日)
|
||
$newdate = fixmonth(1, strtotime('2024-01-31')); // 返回2月最后一天
|
||
```
|
||
|
||
**totimepoint() - 数字时钟转字符串**
|
||
```php
|
||
// 将秒数转换为 HH:MM:SS 格式 范围为1-3600,0代表未设置
|
||
$time = totimepoint(1801); // '12:00'
|
||
$time = totimepoint(1, true); // '00:00:00'
|
||
|
||
```
|
||
|
||
**tostamp() - 字符串转时间戳**
|
||
```php
|
||
// 多种格式支持
|
||
$timestamp = tostamp('2024-03-14');
|
||
$timestamp = tostamp('2024-03-14 10:30:00');
|
||
$timestamp = tostamp('1710385800');
|
||
$timestamp = tostamp('now');
|
||
```
|
||
|
||
#### 数据类型转换
|
||
|
||
**tostr() - 转字符串**
|
||
```php
|
||
$str = tostr($value, ''); // 默认返回空字符串
|
||
```
|
||
|
||
**toint() - 转整数**
|
||
```php
|
||
$int = toint($value); // 默认返回0
|
||
$int = toint($value, -1); // 默认返回-1
|
||
```
|
||
|
||
**tofloat() - 转浮点数**
|
||
```php
|
||
$float = tofloat($value); // 默认返回0.0
|
||
$float = tofloat($value, 0.0); // 默认返回0.0
|
||
```
|
||
|
||
#### 加密解密函数
|
||
|
||
**encrypt() - 字符串加解密**
|
||
```php
|
||
// 加密
|
||
$encrypted = encrypt('原始数据', 'E', '密钥');
|
||
|
||
// 解密
|
||
$decrypted = encrypt($encrypted, 'D', '密钥');
|
||
|
||
// 示例
|
||
$key = 'bka02$59gG'; // 每个项目应该使用不同的密钥
|
||
$encrypted = encrypt('Hello World', 'E', $key);
|
||
$decrypted = encrypt($encrypted, 'D', $key); // 返回 'Hello World'
|
||
```
|
||
|
||
**enid()/deid() - ID数字加解密**
|
||
```php
|
||
// 加密ID(防爬虫)
|
||
$encrypted_id = enid(12345); // 返回如 '872435'
|
||
|
||
// 解密ID
|
||
$decrypted_id = deid(872435); // 返回 12345
|
||
|
||
// 注意:加密后的ID看起来像普通数字,但包含校验位
|
||
```
|
||
|
||
**sha256()/sha512() - 哈希函数**
|
||
```php
|
||
// SHA256哈希
|
||
$hash = sha256('password'); // 返回64位十六进制字符串
|
||
|
||
// SHA512哈希
|
||
$hash = sha512('password'); // 返回128位十六进制字符串
|
||
```
|
||
|
||
**conv33_10()/conv10_33() - 33进制转换**
|
||
```php
|
||
// 33进制转10进制
|
||
$decimal = conv33_10('abc123');
|
||
|
||
// 10进制转33进制
|
||
$base33 = conv10_33(123456);
|
||
```
|
||
|
||
#### 字符串处理
|
||
|
||
**gb_substr() - 中文字符串截取**
|
||
```php
|
||
// 截取中文字符串(中文字符算2个位置)
|
||
$text = gb_substr('中华人民共和国', 10); // '中华...'
|
||
$text = gb_substr('中华人民共和国', 9); // '中华...'
|
||
```
|
||
|
||
**gb_strlen() - 中文字符串长度**
|
||
```php
|
||
// 计算中文字符串长度(中文字符算2个)
|
||
$len = gb_strlen('中华人民共和国'); // 返回 28
|
||
```
|
||
|
||
**gb_haschinese() - 检测中文字符**
|
||
```php
|
||
// 检测字符串是否包含中文
|
||
$has = gb_haschinese('Hello世界'); // 返回 true
|
||
```
|
||
|
||
**getstrparam()/setstrparam() - 简化数据保存**
|
||
```php
|
||
// 解析简化格式数据
|
||
$data = getstrparam('name=张三|age=18|city=北京');
|
||
$data = getstrparam('name=张三&age=18&city=北京', '&');
|
||
// 返回:['name' => '张三', 'age' => '18', 'city' => '北京']
|
||
|
||
// 生成简化格式数据
|
||
$str = setstrparam(['name' => '李四', 'age' => 20, 'city' => '上海']);
|
||
// 返回:'name=李四|age=20|city=上海'
|
||
$str = setstrparam(['name' => '李四', 'age' => 20, 'city' => '上海'], '&');
|
||
// 返回:'name=李四&age=20&city=上海'
|
||
```
|
||
|
||
**startwith()/endwith() - 首尾匹配**
|
||
```php
|
||
// 检查字符串开头
|
||
$starts = startwith('Hello World', 'Hello'); // 返回 true
|
||
|
||
// 检查字符串结尾
|
||
$ends = endwith('Hello World', 'World'); // 返回 true
|
||
```
|
||
|
||
#### 数据验证
|
||
|
||
**ismobile() - 手机号验证**
|
||
```php
|
||
// 验证中国大陆手机号
|
||
$valid = ismobile('13800138000'); // 返回 true
|
||
$valid = ismobile('12345'); // 返回 false
|
||
```
|
||
|
||
**idcard() - 身份证验证**
|
||
```php
|
||
// 验证身份证号并提取信息
|
||
$info = idcard('330106199001011234');
|
||
// 返回:[
|
||
// 'code' => '330106199001011234',
|
||
// 'birth' => '1990-01-01',
|
||
// 'sex' => 1, // 1=男, 2=女
|
||
// 'area' => '330106'
|
||
// ]
|
||
|
||
// 验证失败返回错误信息
|
||
$info = idcard('123456'); // 返回 '身份证长度错误'
|
||
```
|
||
|
||
**iduscc() - 统一社会信用代码验证**
|
||
```php
|
||
// 验证统一社会信用代码
|
||
$info = iduscc('91330100MA2XXX001X');
|
||
// 返回:['code' => '91330100MA2XXX001X', 'area' => '33', 'pn' => '91']
|
||
```
|
||
|
||
**locinzone() - 经纬度围栏检测**
|
||
```php
|
||
// 检测经纬度是否在围栏内
|
||
$fence = '30.434272 120.274938,30.433977 120.277270,30.432403 120.277957';
|
||
$inside = locinzone($fence, 30.433, 120.275); // 返回 true
|
||
```
|
||
|
||
**locdistance() - 计算距离**
|
||
```php
|
||
// 计算两个经纬度之间的距离(毫米)
|
||
$distance = locdistance(30.25, 120.15, 30.26, 120.16);
|
||
// 返回:距离(毫米)
|
||
```
|
||
|
||
#### 字典数据函数
|
||
|
||
**ccode() - 代码转名称**
|
||
```php
|
||
// 单个代码转名称
|
||
$arr = [
|
||
['id' => 1, 'name' => '启用'],
|
||
['id' => 2, 'name' => '禁用']
|
||
];
|
||
$name = ccode($arr, 1, 'name', '--'); // 返回 '启用'
|
||
|
||
// 返回对象
|
||
$obj = ccode($arr, 1, '_obj'); // 返回 ['id' => 1, 'name' => '启用']
|
||
```
|
||
|
||
**mcode() - 多级代码转名称**
|
||
```php
|
||
// 多级代码转名称数组(如地区、部门)
|
||
$arr = [
|
||
['id' => 1, 'name' => '浙江', 'upid' => 0],
|
||
['id' => 2, 'name' => '杭州', 'upid' => 1],
|
||
['id' => 3, 'name' => '西湖', 'upid' => 2]
|
||
];
|
||
$names = mcode($arr, 3, 'name'); // 返回 ['浙江', '杭州', '西湖']
|
||
```
|
||
|
||
**scode() - 多个代码转名称数组**
|
||
```php
|
||
// 多个代码转名称数组
|
||
$arr = [['id' => 1, 'name' => 'A'], ['id' => 2, 'name' => 'B']];
|
||
$names = scode($arr, '1,2', 'name'); // 返回 ['A', 'B']
|
||
```
|
||
|
||
**dcode() - 名称转代码**
|
||
```php
|
||
// 名称转代码
|
||
$arr = [['id' => 1, 'name' => '启用'], ['id' => 2, 'name' => '禁用']];
|
||
$id = dcode($arr, '启用', 'id'); // 返回 1
|
||
```
|
||
|
||
**bcode() - 位标志转名称**
|
||
```php
|
||
// 位标志转名称数组(用于多选)
|
||
$arr = [
|
||
['id' => 1, 'name' => 'A'],
|
||
['id' => 2, 'name' => 'B'],
|
||
['id' => 4, 'name' => 'C']
|
||
];
|
||
$names = bcode($arr, 5); // 5 = 1+4,返回 ['A', 'C']
|
||
```
|
||
|
||
#### 文件操作
|
||
|
||
**dirmake() - 创建目录**
|
||
```php
|
||
// 创建多层目录
|
||
$result = dirmake('/path/to/dir', 0777);
|
||
```
|
||
|
||
**filedel() - 删除文件**
|
||
```php
|
||
// 静默删除文件(不报错)
|
||
filedel('/path/to/file.txt');
|
||
```
|
||
|
||
**filecopy() - 复制文件**
|
||
```php
|
||
// 静默复制文件
|
||
filecopy('/source/file.txt', '/dest/file.txt');
|
||
```
|
||
|
||
**filesave() - 保存文件**
|
||
```php
|
||
// 保存文本到文件
|
||
$result = filesave('/path/to/file.txt', '文件内容');
|
||
```
|
||
|
||
**fileload() - 读取文件**
|
||
```php
|
||
// 读取文本文件
|
||
$content = fileload('/path/to/file.txt');
|
||
```
|
||
|
||
**storsave() - 保存到存储目录**
|
||
```php
|
||
// 保存到存储目录(支持日期变量)
|
||
storsave('{Y}/{m}{d}/data.txt', '内容');
|
||
// 保存到:/web/ud/2024/0314/data.txt
|
||
```
|
||
|
||
**file_down() - 下载文件**
|
||
```php
|
||
// 下载远程文件到本地
|
||
$path = file_down(
|
||
'https://example.com/image.jpg', // 远程URL
|
||
'/ud/images/', // 保存路径
|
||
'', // 文件名(空则自动生成)
|
||
60, // 超时时间(秒)
|
||
'.jpg' // 默认扩展名
|
||
);
|
||
```
|
||
|
||
**file_ext() - 获取文件扩展名**
|
||
```php
|
||
// 获取文件扩展名
|
||
$ext = file_ext('/path/to/file.txt'); // 返回 'txt'
|
||
```
|
||
|
||
**file_stor() - 获取文件的云存储URL**
|
||
```php
|
||
// 获取文件访问URL(支持云存储)
|
||
$url = file_stor('/2024/03/14/image.jpg');
|
||
// 返回完整的访问URL
|
||
```
|
||
|
||
**注意**:前端显示附件时,必须使用 `ciyfn.file_stor()` 将数据库存储的相对路径转换为云存储绝对URL。前端调用时:
|
||
```javascript
|
||
var imgurl = ciyfn.file_stor('/2024/03/14/image.jpg');
|
||
// 返回完整的云存储URL(原始文件)
|
||
var thumburl = ciyfn.file_stor('/2024/03/14/image.jpg', 'thumb');
|
||
// 返回缩略图云存储URL(图片资源用)
|
||
```
|
||
|
||
|
||
#### 配置和输入
|
||
|
||
**webini() - 读取配置文件**
|
||
```php
|
||
// 读取web.ini配置
|
||
$config = webini('db');
|
||
// 返回:['host' => 'localhost', 'port' => '3306', ...]
|
||
|
||
$config = webini('s3');
|
||
// 返回:['url' => 'https://...', ...]
|
||
```
|
||
|
||
**getstr()/getint()/post()/request()/cookie() - 获取输入**
|
||
```php
|
||
// 获取GET参数
|
||
$name = getstr('name', '', 'text'); // 第三个参数:text(过滤HTML)、all(不过滤)
|
||
$age = getint('age', 0);
|
||
|
||
// 获取POST参数
|
||
$name = post('name', '', 'text');
|
||
|
||
// 获取REQUEST参数
|
||
$name = request('name', '', 'text');
|
||
|
||
// 获取Cookie
|
||
$name = cookie('name', '', 'text');
|
||
```
|
||
|
||
**getip() - 获取真实IP**
|
||
```php
|
||
// 获取客户端真实IP(支持CDN、反向代理)
|
||
$ip = getip(); // 返回'80.15.128.210'
|
||
```
|
||
|
||
#### 日志和调试
|
||
|
||
**savelogfile() - 保存日志到文件**
|
||
```php
|
||
// 保存日志到文件
|
||
savelogfile('error', '错误信息');
|
||
// 保存到:/web/log/error.log
|
||
|
||
// 包含请求信息
|
||
savelogfile('api', 'API调用', true);
|
||
```
|
||
|
||
**logdbstr() - 格式化数据变更**
|
||
```php
|
||
// 格式化数据变更日志
|
||
$oldrow = ['id' => 1, 'name' => '旧名称', 'status' => 1];
|
||
$newrow = ['id' => 1, 'name' => '新名称', 'status' => 2];
|
||
$msg = logdbstr($oldrow, $newrow);
|
||
// 返回:'Upd=1_|@|_name=旧名称→新名称_|@|_status=1→2'
|
||
```
|
||
|
||
**clog() - 调试输出**
|
||
```php
|
||
// 调试输出变量
|
||
clog($variable);
|
||
clog('Title', $variable);
|
||
clog($var1, $var2, $var3);
|
||
```
|
||
|
||
#### 杂项工具
|
||
|
||
**randstr() - 生成随机字符串**
|
||
```php
|
||
// 生成随机字符串
|
||
$str = randstr(10); // 生成10位随机字符串
|
||
$str = randstr(10, 'abcdef123456'); // 使用指定字符集
|
||
```
|
||
|
||
**arrayrand() - 随机抽取数组元素**
|
||
```php
|
||
// 随机抽取数组元素并删除
|
||
$arr = ['A', 'B', 'C', 'D', 'E'];
|
||
$selected = arrayrand($arr, 2); // 返回 ['A', 'C'],原数组变为 ['B', 'D', 'E']
|
||
```
|
||
|
||
**timems() - 获取微秒时间**
|
||
```php
|
||
// 获取当前微秒数
|
||
$ms = timems(); // 返回如 1710385800123
|
||
```
|
||
|
||
---
|
||
|
||
### 常用函数索引
|
||
|
||
#### 用户相关
|
||
- `verifyfast()` - 快速用户验证
|
||
- `verifyuser()` - 用户验证
|
||
- `nopower()` - 权限验证
|
||
|
||
#### 配置相关
|
||
- `getconfig()` - 获取配置
|
||
- `setconfig()` - 设置配置
|
||
|
||
#### 日志相关
|
||
- `savelog()` - 保存日志
|
||
- `savelogdb()` - 数据库变更日志
|
||
|
||
#### 工具函数
|
||
- `tostamp()` - 转换为时间戳
|
||
- `todate()` - 时间戳转字符串
|
||
- `randstr()` - 生成随机字符串
|
||
- `getip()` - 获取IP地址
|
||
|
||
### api json错误代码
|
||
|
||
| 代码 | 说明 |
|
||
|------|------|
|
||
| 1 | 成功 |
|
||
| 0 | 失败 |
|
||
| 2 | 未登录 |
|
||
| 3 | 未授权 |
|
||
| 9 | 函数未加载 |
|
||
|
||
### 开发环境配置
|
||
|
||
#### 数据库配置 (web.ini)
|
||
```ini
|
||
[db]
|
||
host = localhost
|
||
port = 3306
|
||
database = c5_ciyon
|
||
username = root
|
||
password = wkxroot
|
||
charset = utf8mb4
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
Ciyon后台管理系统是一个功能完善、架构清晰的PHP开发框架。通过本文档,开发者可以快速理解系统的核心架构和开发规范,高效地进行二次开发和功能扩展。
|
||
|
||
**核心优势:**
|
||
- 完整的用户认证和权限控制系统
|
||
- 安全的数据库操作层
|
||
- 灵活的模块化设计
|
||
- 完善的错误处理机制
|
||
- 丰富的工具类库
|
||
|
||
*文档版本:1.0*
|
||
*技术支持:众产® https://ciy.cn/code* |