2025-06-26 18:25:08 +08:00

478 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\api\controller\mcp;
use app\common\model\school\classes\ClassesLib;
use app\common\model\school\classes\order\Order as OrderModel;
class School extends Base
{
// 工具定义常量
private const ADD_FUNCTION = [
[
'name' => 'list',
'description' => "查找多样青春夜校的所有课程列表信息
表结构sql为
`id` int(11) NOT NULL AUTO_INCREMENT,
`manystore_id` int(11) NOT NULL COMMENT '机构账号id',
`shop_id` int(11) NOT NULL COMMENT '机构店铺id',
`user_id` int(11) NOT NULL COMMENT '主讲师用户id',
`teacher_id` int(11) NOT NULL COMMENT '老师id',
`classes_cate_ids` varchar(255) NOT NULL COMMENT '平台课程分类ids',
`classes_label_ids` varchar(255) DEFAULT NULL COMMENT '平台课程类型ids',
`self_label_tag` varchar(1000) DEFAULT NULL COMMENT '机构特色标签',
`add_type` enum('1','2') NOT NULL DEFAULT '2' COMMENT '添加人类型:1=机构,2=总后台',
`add_id` int(11) NOT NULL DEFAULT '0' COMMENT '添加人id',
`title` varchar(255) NOT NULL COMMENT '标题',
`headimage` varchar(500) NOT NULL COMMENT '头图',
`images` varchar(1500) NOT NULL COMMENT '轮播图',
`type` enum('out','in') NOT NULL DEFAULT 'in' COMMENT '地点类型:out=户外,in=室内',
`classes_num` int(10) NOT NULL COMMENT '课时数',
`address_type` enum('1','2') NOT NULL DEFAULT '1' COMMENT '地址类型:1=按机构,2=独立位置',
`address_city` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '城市选择',
`province` int(11) DEFAULT NULL COMMENT '省编号',
`city` int(11) DEFAULT NULL COMMENT '市编号',
`district` int(11) DEFAULT NULL COMMENT '县区编号',
`address` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '店铺地址',
`address_detail` varchar(200) CHARACTER SET utf8 DEFAULT '' COMMENT '店铺详细地址',
`longitude` varchar(30) CHARACTER SET utf8 DEFAULT '' COMMENT '经度',
`latitude` varchar(30) CHARACTER SET utf8 DEFAULT '' COMMENT '纬度',
`classes_date_text` varchar(500) NOT NULL COMMENT '上课日期',
`classes_time_text` varchar(500) NOT NULL COMMENT '上课时间',
`content` text NOT NULL COMMENT '课程详情',
`notice` text NOT NULL COMMENT '课程须知',
`virtual_num` int(11) DEFAULT '0' COMMENT '虚拟报名人数',
`sale` int(11) DEFAULT '0' COMMENT '总销量',
`price` decimal(10,2) NOT NULL COMMENT '售价',
`underline_price` decimal(10,2) DEFAULT NULL COMMENT '划线价',
`virtual_collect` int(11) DEFAULT '0' COMMENT '虚拟收藏量',
`status` enum('1','2','3') DEFAULT '1' COMMENT '状态:1=上架,2=下架,3=平台下架',
`auth_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '审核状态:0=待审核,1=审核通过,2=审核失败',
`reason` varchar(200) CHARACTER SET utf8 DEFAULT '' COMMENT '审核不通过原因',
`auth_time` int(11) DEFAULT '0' COMMENT '审核时间',
`admin_id` int(11) DEFAULT '0' COMMENT '审核管理员id',
`weigh` int(11) DEFAULT '0' COMMENT '权重',
`recommend` enum('0','1') DEFAULT '0' COMMENT '平台推荐:0=否,1=是',
`hot` enum('0','1') DEFAULT '0' COMMENT '平台热门:0=否,1=是',
`new` enum('0','1') DEFAULT '0' COMMENT '平台最新:0=否,1=是',
`selfhot` enum('0','1') DEFAULT '0' COMMENT '机构热门:0=否,1=是',
`createtime` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`updatetime` bigint(11) unsigned DEFAULT '0' COMMENT '修改时间',
`deletetime` bigint(11) unsigned DEFAULT NULL COMMENT '删除时间',
`virtual_people` int(11) DEFAULT '0' COMMENT '虚拟参与人数',
`feel` enum('0','1') DEFAULT '0' COMMENT '是否免费:0=否,1=是',
`limit_num` int(11) DEFAULT '0' COMMENT '总限定人数0不限制',
`sign_num` int(11) DEFAULT '0' COMMENT '总已报名人数',
`verification_num` int(11) DEFAULT NULL COMMENT '总已核销人数',
`views` bigint(20) DEFAULT '0' COMMENT '浏览量',
`start_time` bigint(20) DEFAULT NULL COMMENT '开始时间',
`end_time` bigint(20) DEFAULT NULL COMMENT '结束时间',
`collect` bigint(20) DEFAULT NULL COMMENT '实际收藏量',
`classes_type` varchar(2000) DEFAULT NULL COMMENT '课程类型',
`classes_star` decimal(10,1) NOT NULL DEFAULT '5.0' COMMENT '课程评分',
`star_number` int(11) NOT NULL DEFAULT '0' COMMENT '评价数',
",
'inputSchema' => [
'type' => 'object',
'properties' => [
'keywords' => [
'description' => '搜索关键字',
'example' => "书法",
'type' => 'string',
],
'page' => [
'description' => '页数(必填)',
'example' => 1,
'type' => 'number',
],
'limit' => [
'description' => '每页数量(必填)',
'example' => 20,
'type' => 'number',
]
],
'required' => [ 'page', 'limit']
]
],
[
'name' => 'orderlist',
'description' => "
查找多样青春夜校当前课程的订单列表,想获取订单列表请先获取课程列表list工具信息中对应的课程里的id
表结构sql为
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_no` varchar(255) DEFAULT NULL COMMENT '订单号',
`pay_no` varchar(255) DEFAULT NULL COMMENT '微信支付单号',
`user_id` int(11) DEFAULT NULL COMMENT '下单人id',
`manystore_id` int(11) DEFAULT NULL COMMENT '机构账号id',
`shop_id` int(11) DEFAULT NULL COMMENT '机构店铺id',
`code` varchar(255) DEFAULT NULL COMMENT '核销码',
`codeimage` varchar(500) DEFAULT NULL COMMENT '核销二维码图片',
`codeoneimage` varchar(500) DEFAULT NULL COMMENT '核销一维码图片',
`classes_lib_id` int(11) DEFAULT NULL COMMENT '课程id',
`classes_order_detail_id` int(11) DEFAULT NULL COMMENT '订单课程id',
`beforeprice` decimal(10,2) DEFAULT NULL COMMENT '订单优惠前金额',
`totalprice` decimal(10,2) DEFAULT NULL COMMENT '订单应付金额',
`payprice` decimal(10,2) DEFAULT NULL COMMENT '订单实付金额',
`pay_type` enum('yue','wechat') DEFAULT 'wechat' COMMENT '支付方式:yue=余额,wechat=微信',
`status` enum('-3','0','3','4','6','9') DEFAULT '0' COMMENT '订单状态:-3=已取消,0=待支付,3=使用中,4=售后中,6=已退款,9=已完成',
`before_status` enum('-3','0','3','6','9') DEFAULT '0' COMMENT '售后前状态:-3=已取消,0=未售后,3=使用中,6=已退款,9=已完成',
`server_status` enum('0','3','6') DEFAULT '0' COMMENT '售后订单状态:0=正常,3=售后中,6=售后完成',
`reason` varchar(500) DEFAULT NULL COMMENT '售后申请原因',
`auth_manystore_id` int(11) DEFAULT NULL COMMENT '机构审核人id',
`auth_opinion` varchar(500) DEFAULT NULL COMMENT '机构审核意见',
`auth_file` varchar(500) DEFAULT NULL COMMENT '机构审核附件',
`admin_id` int(11) DEFAULT NULL COMMENT '平台审核人id',
`result_status` enum('0','3','6') DEFAULT '0' COMMENT '售后处理结果:0=未售后,3=退款,6=驳回',
`result_text` varchar(1000) DEFAULT NULL COMMENT '售后处理结果说明',
`result_file` varchar(500) DEFAULT NULL COMMENT '售后处理结果说明附件',
`canceltime` bigint(20) DEFAULT NULL COMMENT '取消时间',
`paytime` bigint(20) DEFAULT NULL COMMENT '支付时间',
`finishtime` bigint(20) DEFAULT NULL COMMENT '完成时间',
`refundtime` bigint(20) DEFAULT NULL COMMENT '退款时间',
`total_refundprice` decimal(10,2) DEFAULT NULL COMMENT '应退款金额',
`real_refundprice` decimal(10,2) DEFAULT NULL COMMENT '实际退款金额',
`sub_refundprice` decimal(10,2) DEFAULT NULL COMMENT '剩余未退金额',
`createtime` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`updatetime` bigint(11) unsigned DEFAULT '0' COMMENT '修改时间',
`deletetime` bigint(11) unsigned DEFAULT NULL COMMENT '删除时间',
`pay_json` varchar(2000) DEFAULT NULL COMMENT '三方支付信息json',
`platform` varchar(255) DEFAULT NULL COMMENT '支付平台',
`classes_service_order_id` int(11) DEFAULT '0' COMMENT '售后订单id',
`classes_lib_spec_id` int(11) DEFAULT '0' COMMENT '快捷下单课时id',
`classes_evaluate_id` int(11) DEFAULT '0' COMMENT '课程反馈id',
",
'inputSchema' => [
'type' => 'object',
'properties' => [
'classes_lib_id' => [
'description' => '课程id通过课程列表信息获取的id字段',
'example' => 1202,
'type' => 'int',
],
'page' => [
'description' => '页数(必填)',
'example' => 1,
'type' => 'int',
],
'limit' => [
'description' => '每页数量(必填)',
'example' => 20,
'type' => 'int',
]
],
'required' => [ 'page', 'limit','classes_lib_id']
]
],
];
public function sse()
{
// 设置 CORS 头
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
// 处理 OPTIONS 预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
// 设置 SSE 响应头
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no');
// 获取原始输入
$input = file_get_contents('php://input');
file_put_contents('mcp_debug.log', date('Y-m-d H:i:s').' Raw input: '.$input.PHP_EOL, FILE_APPEND);
// 解析 JSON-RPC 请求
$request = json_decode($input, true);
// 处理请求
if (isset($request['method'])) {
switch($request['method']) {
case 'initialize':
$this->handleInitializeRequest($request);
break;
case 'function':
case 'tools/call': // 新增支持tools/call方法
$this->handleFunctionRequest($request);
break;
case 'ping':
$this->sendJsonRpcResponse($request['id'] ?? null, ['result' => 'pong']);
break;
case 'notifications/initialized':
$this->sendJsonRpcResponse($request['id'] ?? null, ['result' => 'success']);
break;
case 'tools/list':
$this->sendJsonRpcResponse($request['id'] ?? null, ['tools' => self::ADD_FUNCTION]);
break;
default:
$this->handleOtherRequests($request);
}
} else {
$this->handleOtherRequests($request);
}
}
/**
* 处理初始化请求
*/
private function handleInitializeRequest($request)
{
$id = $request['id'] ?? null;
// 记录完整的初始化请求内容
file_put_contents('mcp_debug.log', "Received initialize request: " . json_encode($request) . PHP_EOL, FILE_APPEND);
// 发送初始化响应
$this->sendJsonRpcResponse($id, [
'capabilities' => [
'progress' => true,
'functions' => self::ADD_FUNCTION
],
'serverInfo' => [
'name' => 'Calculator Server',
'version' => '1.0.0',
'vendor' => 'MyCompany',
'license' => 'MIT'
],
'protocolVersion' => '2025-03-26'
]);
}
/**
* 处理函数调用请求
*/
public function handleFunctionRequest($request)
{
$id = $request['id'] ?? null;
$function = $request['params']['function'] ?? '';
$arguments = $request['params']['arguments'] ?? [];
$function = $request['params']['name'] ?? 'list';
switch ($function){
case 'list':
// 解构参数
$keywords = $arguments['keywords'] ?? 0;
$page = $arguments['page'] ?? 1;
$limit = $arguments['limit'] ?? 1;
$params = [
'keywords' => $keywords,
'page' => (int)$page,
'limit' => (int)$limit,
"order" => "normal",
"status" => "1,2,3",
"auth_status"=> "-1",
"is_expire" => "0"
];
// 发送进度通知
$this->sendNotification('progress', ['id' => $id, 'message' => '正在获取数据...']);
try {
$result = ClassesLib::getVaildList($params);
}catch ( \Exception $e){
$this->sendJsonRpcResponse($id, [
'error' => [
'code' => -32603,
'message' => 'Internal error',
'data' => $e->getMessage()
]
]);
}
$this->sendJsonRpcResponse($id, [
'content' => [
[
'type' => 'list',
'text' => $result // 返回格式化的时间和时区信息
]
]
]);
break;
case 'orderlist':
// 解构参数
$classes_lib_id = $arguments['classes_lib_id'] ?? 0;
$page = $arguments['page'] ?? 1;
$limit = $arguments['limit'] ?? 1;
// 发送进度通知
$this->sendNotification('progress', ['id' => $id, 'message' => '正在获取数据...']);
try {
$result = OrderModel::allList(0,(int)$page, (int)$limit,'',"0,3,4,6,9",(int)$classes_lib_id);
}catch ( \Exception $e){
$this->sendJsonRpcResponse($id, [
'error' => [
'code' => -32603,
'message' => 'Internal error',
'data' => $e->getMessage()
]
]);
}
$this->sendJsonRpcResponse($id, [
'content' => [
[
'type' => 'list',
'text' => $result // 返回格式化的时间和时区信息
]
]
]);
break;
}
}
/**
* 验证加法参数
* @param array $arguments
* @param mixed $id
* @return array|bool 返回true表示验证通过否则返回错误响应数组
*/
private function validateAddParameters(array $arguments, $id)
{
if (!isset($arguments['a'], $arguments['b'])) {
return [
'error' => [
'code' => -32602,
'message' => 'Invalid params',
'data' => 'Missing required parameters: a and b'
]
];
}
if (!is_numeric($arguments['a']) || !is_numeric($arguments['b'])) {
return [
'error' => [
'code' => -32602,
'message' => 'Invalid params',
'data' => 'Parameters must be numbers'
]
];
}
return true;
}
/**
* 处理其他类型的请求
*/
private function handleOtherRequests($request)
{
if (!$request || !isset($request['function'])) {
$this->sendJsonRpcResponse($request['id'] ?? null, ['error' => 'Invalid request format']);
return;
}
if ($request['function'] === 'add' || $request['function'] === 'tools/call') {
$a = $request['params']['a'] ?? 0;
$b = $request['params']['b'] ?? 0;
if (!is_numeric($a) || !is_numeric($b)) {
$this->sendEvent('error', 'Parameters must be numbers');
return;
}
// 模拟处理延迟
sleep(1);
$result = $this->calculateAdd($a, $b);
$this->sendEvent('result', [
'operation' => 'add',
'result' => $result,
'expression' => "$a + $b = $result"
]);
} else {
$this->sendEvent('error', 'Unsupported function: ' . $request['function']);
}
}
/**
* 执行加法计算
*/
private function calculateAdd(float $a, float $b): float
{
return $a + $b;
}
/**
* 发送 JSON-RPC 响应
*/
private function sendJsonRpcResponse($id, $resultOrError)
{
$response = [
'jsonrpc' => '2.0',
'id' => $id
];
if (isset($resultOrError['error'])) {
$response['error'] = $resultOrError['error'];
} else {
$response['result'] = $resultOrError;
}
$this->sendStreamData(json_encode($response, JSON_UNESCAPED_UNICODE));
}
/**
* 发送通知(用于进度更新等)
*/
private function sendNotification($method, $params)
{
$notification = [
'jsonrpc' => '2.0',
'method' => $method,
'params' => $params
];
$this->sendStreamData(json_encode($notification, JSON_UNESCAPED_UNICODE));
}
/**
* 发送标准 SSE 事件
*/
private function sendEvent($type, $data)
{
$jsonData = json_encode($data, JSON_UNESCAPED_UNICODE);
echo "event: $type\n";
echo "data: $jsonData\n\n";
$this->flushBuffers();
file_put_contents('mcp_debug.log', "Sent event: $type - $jsonData".PHP_EOL, FILE_APPEND);
}
/**
* 发送流数据
*/
private function sendStreamData(string $data)
{
echo "data: $data\n\n";
$this->flushBuffers();
file_put_contents('mcp_debug.log', "JSON-RPC Response: $data".PHP_EOL, FILE_APPEND);
}
/**
* 刷新输出缓冲区
*/
private function flushBuffers()
{
ob_flush();
flush();
}
}