本文档为CansCode API管理系统开发手册,适用于系统二次开发人员、模板开发者及插件开发工程师。文档系统梳理了开发过程中核心的技术要点,涵盖内置钩子、数据库操作函数及模板开发规范三大核心模块。其中,内置钩子部分详细说明支付、上传、系统相关钩子的类型、权重及使用场景,并提供注册实例;数据库函数部分包含函数引入方式与查询、新增、修改、删除等常用操作示例;模板规范部分则针对Typecho模板(兼容Joe主题),明确目录结构、核心文件功能、配置页面开发及关键注意事项。通过本文档,开发者可快速掌握系统开发的基础规范与核心能力,提升开发效率并保障开发质量。
内置钩子文档
支付相关
- Pay_Create_qq类型:执行钩子权重:1-100说明:创建支付订单时,QQ支付,可执行钩子,返回订单所有数据。
- Pay_Create_wx类型:执行钩子说明:创建支付订单时,微信支付,可执行钩子,返回订单所有数据。
- Pay_Create_ali类型:执行钩子说明:创建支付订单时,支付宝,可执行钩子,返回订单所有数据。
- Pay_Create_balance类型:执行钩子(非必要请勿使用!)说明:创建支付订单时,余额支付,可执行钩子,返回订单所有数据。
- Pay_Create类型:数据钩子说明:创建支付订单时,可返回数据钩子,用于创建后对订单数据进行编辑和返回。
- Pay_CallBack类型:执行钩子说明:支付成功回调钩子,返回所有回调数据。注意:发起支付若需要跳转链接而不是html,请确保code = 522 并且链接在 data中。
上传相关
- Update_File_Start类型:执行钩子权重:1-100说明:上传文件开始,返回$_FILES["file"]。
- Update_Image_Start类型:执行钩子说明:上传图片开始,返回$_FILES["file"]。
系统相关
- WebNotification类型:数据钩子权重:1-100说明:后台系统通知。
- AdminNav_Left类型:数据钩子说明:后台导航栏左侧。
- AdminNav_Chen类型:数据钩子说明:后台导航栏子栏目。
- 注意:内置钩子请勿重复注册,否则可能出现插件异常,权重越小,越先执行。
注册实例
提供两种类型的钩子注册示例:执行类钩子和数据处理类钩子。
执行类钩子注册实例:
use Logic\Important\Hooks;
// 注册支付创建后,QQ支付处理钩子
Hooks::Register("Pay_Create_qq", 1, function($parameter) {
echo "Pay_Create_qq";
var_dump($parameter);
exit();
}, 10);
数据处理类钩子注册实例:use Logic\Important\Hooks;
// 支付创建后,处理数据,并将数据返回
Hooks::Register("Pay_Create", 2, function($parameter) {
$parameter['payname'] = "我爱你";
return $parameter;
}, 10);内置函数
返回数据函数:
use Logic\Important\Tos;
// 返回状态
Tos::sendmsg(200);
// 返回单个数据
Tos::sendmsg(200, "", $data);
// 返回列表数据
Tos::sendmsg(200, "", $data, $total);数据库内置函数
一、引入函数
使用SqlFun库来处理数据库操作。
use Logic\Important\SqlFun;
public function __construct()
{
// 获取数据库实例
$this->db = SqlFun::getInstance();
}二、函数使用示例
1. 普通查询数据
执行一个简单的查询操作以获取数据。
$result = $this->db->query("SELECT * FROM xxx WHERE id = ?", [$id]);
2. 修改数据
更新表中的特定记录。$this->db->update("xxxxx", [
"xu_fans" => $merinfo[0]['xu_fans'] - 1,
], 'id = ?', [$merinfo[0]['id']]);
3. 删除数据
从指定表中删除一条记录。$this->db->delete("xu_merfans", "id = ?", [$res[0]['id']]);
4. 写入数据
向指定表中插入新数据。$this->db->insert("xu_keyapi", [
"xu_aid" => $aidlist[$a],
"xu_kid" => $ids
]);
5. 数据库语句拼接
使用SqlFun提供的方法构建复杂的SQL查询。$sql = SqlFun::SqlBuilderAdd("xu_name", $pre['search'], 'like')
->SqlBuilderAdd("xu_mid", $pre['mid'])
->SqlBuilderAdd("xu_status", 1)
->SqlBuilderBuild();
6. 数据库查询语句(分页查询)
执行分页查询,并返回结果。$result = $this->db->getPaginatedData("xu_api", $sql, [$pre['sortname'], $pre['sorttype']], $pagination, [
"xu_queryper", "xu_bodyper", "xu_headerper", "xu_cookies", "xu_errlist", "xu_bodyjson", "xu_words", "xu_apisource"
]);模板规范
Typecho 模板开发指南(Joe 主题兼容)
一、目录结构
一个标准的 Typecho 模板目录结构如下:
mytemplate/
├── user/ # 用户中心页面(自动判断登录)
├── Main.php # 主逻辑文件
├── package.json # 模板描述信息文件
├── ConfigurationPage.php # 模板配置页面二、Main.php —— 主逻辑文件
该文件定义模板的核心行为和生命周期钩子。
示例代码:
<?php
namespace Resource\Template\default;
use Logic\Important\Rote;
use Logic\Important\Tos;
use Resource\Template\default\cp;
class Main
{
public function CallBack($parameter)
{
if ($parameter['status'] == 1) {
cp::setteP($data);
Tos::sendmsg(200);
}
if ($parameter['status'] == 2) {
cp::checkUpdate(3);
}
}
public function init(): void
{
$this->getAuthCert();
}
public function Userinit(): void
{
$this->getAuthCert();
}
public function Admininit(): void
{
// 检查模板是否存在新版本
cp::checkUpdate();
}
public function getAuthCert()
{
// 授权验证示例(可选)
// $auth = new Rote();
// $res = $auth->appCertificate('5a6e619c-29d1-2890-1776-14078b36b4c3');
// if ($res['code'] !== '5200') {
// error_log('未授权');
// } else {
// error_log('已授权');
// }
}
}三、package.json —— 模板描述文件
用于存储模板的基本信息,便于管理与更新。
示例内容:
{
"tep_name": "默认模板",
"tep_version": "0.0.1",
"tep_image": "https://imgsbad.semoun.com/uploads/2025/03/19/67da24298292e.jpg",
"tep_brief": "自带的默认模板",
"tep_cansid": "default",
"tep_page": true,
"tep_PagePath": "ConfigurationPage.php",
"tep_code": "5a6e619c-29d1-2890-1776-14078b36b4c3",
"author_name": "CansCode"
}四、ConfigurationPage.php —— 模板配置页面
用于提供后台可视化配置界面,支持用户自定义设置。
示例代码片段(HTML + Vue.js):
<?php
use Logic\Function\MainControl\WebInfo;
use Resource\Template\default\cp;
$config = cp::gettep();
$pack = cp::getpack();
$info = new WebInfo();
$webinfo = $info->getWebInfo(2);
// 获取版本
$type = $_GET['type'] ?? 1;
$upinfo = cp::checkUpdate(2);
?>
<link rel="stylesheet" href="/Resource/Template/default/assets/css/page.min.css">
<link rel="stylesheet" href="/Resource/Template/default/assets/css/XuZhiGlobal.min.css">
<style>
.WebPagesPg{
background-color: #fff;
min-height: 100vh;
}
.tab-header {
display: flex;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
.tab-header div {
padding: 10px 20px;
cursor: pointer;
font-size: 16px;
border-bottom: 2px solid transparent;
}
.tab-header .active {
border-color: #1890ff;
font-weight: bold;
color: #1890ff;
}
.tabpanel {
display: none;
}
.tabpanel.active {
display: block;
}
.update-box {
border: 1px solid #ddd;
padding: 16px;
background: #f9f9f9;
border-radius: 8px;
}
.update-log {
margin-top: 16px;
}
.download-box {
margin-top: 20px;
}
.download-btn {
display: inline-block;
padding: 10px 16px;
background-color: #1466ff;
color: #fff;
border-radius: 6px;
text-decoration: none;
}
.download-btn:hover {
background-color: #0056d2;
}
</style>
<div class="WebPagesPg" id="WebPages">
<!-- 标签页 -->
<div class="tab-header">
<div :class="{tabbtn:true, active:showtag == 1}" data-tab="update" @click="showtag = 1">模板配置</div>
<div :class="{tabbtn:true, active:showtag == 2}" data-tab="config" @click="showtag = 2">模板更新</div>
</div>
<div class="x_from">
<!-- 模板配置 -->
<div :class="{tabpanel:true, active:showtag == 1}" id="tab-update">
<div class="x_from_item">
<p class="x_from_title">站点简介</p>
<input type="text" v-model="conform.desc" class="x_input" id="site_desc" placeholder="请输入站点简介">
<trg class="x_from_trg">Ps: 将显示在首页底部文字区域</trg>
</div>
<div class="x_from_item">
<p class="x_from_title">底部二维码</p>
<input type="text" class="x_input" v-model="conform.qr_urla" id="qr_urla" placeholder="请输入二维码图片地址">
</div>
<div class="x_from_item">
<p class="x_from_title">底部二维码</p>
<input type="text" class="x_input" v-model="conform.qr_urlb" id="qr_urlb" placeholder="请输入二维码图片地址">
</div>
<div class="x_from_item">
<button class="x_button" @click="upconfig()">确认修改配置</button>
</div>
</div>
<div :class="{tabpanel:true, active:showtag == 2}" id="tab-config">
<div class="x_from_item" style="display: flex; gap: 20px; align-items: flex-start;">
<div style="flex: 1;">
<p class="">模板名称:<span id="tep_name" style="color: #333;">{{ tepInfo.tep_name}}</span></p>
<p class="">模板标识:<span id="tep_cansid" style="color: #888;">{{ tepInfo.tep_cansid}}</span></p>
<p class="">模板版本:<span id="tep_version" style="color: #007bff;">v{{ tepInfo.tep_version}}</span> -- <span id="tep_version" style="color: #007bff;">v{{upinfo.NewVersion}}</span></p>
<p class="">作者:<span id="author_name" style="color: #555;">{{tepInfo.author_name}}</span></p>
</div>
</div>
<button class="x_button" @click="update()" style="margin-bottom: 20px;">更新至最新版</button>
<div v-if="upinfo && upinfo.data?.length">
<div class="update-box">
<h2 style="font-size: 16px">版本更新说明</h2>
<p style="margin-top: 10px"><strong>最新版本:</strong> {{ upinfo.NewVersion }}</p>
<div v-html="upinfo.data[0].x_word" class="update-log"></div>
</div>
</div>
</div>
</div>
</div>
<img style="display: none;" src="/Public/Assets/images/loadw.png" alt="">
<script src="/Public/Assets/js/axios.js"></script>
<script src="/Public/Assets/js/vue.js"></script>
<script>
const {
ref,
createApp
} = Vue;
const App = {
data() {
const showtag = ref(1);
const config = ref(JSON.parse('<?= json_encode($config, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>'));
const tepInfo = ref(JSON.parse('<?= json_encode($pack, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>'));
const upinfo = ref(<?= json_encode($upinfo, JSON_UNESCAPED_UNICODE) ?>);
const conform = ref({
type: 'TempCallBack',
status: 1,
desc: config.value.footer_text,
qr_urla: config.value.footer_qrlist?.[0]?.url || "",
qr_urlb: config.value.footer_qrlist?.[1]?.url || "",
})
const upconfig = () => {
window.parent.postMessage(JSON.parse(JSON.stringify(conform.value)))
}
const update = () => {
window.parent.postMessage({
type: 'TempCallBack',
status: 2
})
}
return {
config,
tepInfo,
conform,
showtag,
upinfo,
upconfig,
update
}
}
}
const app = Vue.createApp(App);
window.vm = app.mount("#WebPages");
</script>五、功能说明
| 功能模块 | 描述 |
|---|---|
| CallBack() | 处理来自系统的回调事件(如配置更新、版本升级) |
| init() | 初始化函数,在前台加载时执行 |
| Userinit() | 用户中心初始化函数 |
| Admininit() | 后台初始化函数,可用于检查模板更新 |
| getAuthCert() | 可选的授权验证逻辑 |
六、注意事项
- 所有模板页面需放置在 /usr/themes/ 目录下。
- 若模板包含用户中心页面,请放入 user/ 文件夹,并确保系统会自动进行登录验证。
- 使用 Vue.js 前端框架时,确保引入了对应的 JS 库(如 vue.js, axios.js)。
- 所有数据交互应使用 window.parent.postMessage() 发送至主窗口处理。



