# 第一步：安装
```
npm i zion-mdapi

// 如果要使用【mdapi框架自定义行为】需要先在行为流中按格式配置好
// [mdapi配置教程和使用文档链接](https://functorz.feishu.cn/wiki/HnjcwGPs8i5i8RkloJZccp6UnVa)

```

# 第二步：定义配置文件`mdapi.config.js`
```js
// mdapi.config.js,放在项目根目录（node_modules同级目录下）
export default {
	// 回调链接调用框架自定义行为
	"callback_url": "https://zion-app.functorz.com/Y5GGXyqOA5K/zero/L6ARjmeyKwy/callback/8c6be54d-2123-412c-b90b-303f1a24ca8d",

	//zion项目信息配置,配置url、actionflow_id、actionflow_version后会走前端直连方式
	//"url": "https://zion-app.functorz.com/Y5GGXyqOA5K/zero/L6ARjmeyKwy/api/graphql-v2",

	// 配置权限管理后可传入headers信息来支持调用制定接口
	"headers": {
		"authorization": ""
	},

	// mdapi框架自定义行为actionflowId
	//"actionflow_id": "5e5e155d-7b3c-4771-bf94-ceb597cbb666",

	// mdapi框架自定义行为行为版本号,未配置则调用最新部署的版本号，一般在system?.actionflow_version中配置
	//"actionflow_version": 0,

	//会自动追加到payload中的客户端数据
	"client_data": {
		"secret":"",//开发者密钥
		//内置框架自定义行为执行密码，mdapi执行内置的框架自定义行为时会与system.actionflow_pwd进行比对验证
		"actionflow_pwd": "",
		// 统一token,mdapi在dataToToken中会取payload?.client_data.token为默认值
		"token": ""

		// ...支持更多自定义配置
	},

	// 可以不配置，不配置时默认会根据环境执行环境检测判断
	//"env": "H5", //1.MP-WEIXIN 2.H5 3.NODE

	"isClog":true,//请求时是否在控制台打印日志
	// server配置参数,用于调试，一般无需变动
	"server_port": 3020,
	"server_root": "http://localhost"
}
```

# 第三步：开始使用（客户端/前端）
## 项目内引入
1. 方式一：web项目或小程序项目ES模块化引入
```js
import zionMdapi from "zion-mdapi"
const mdapiConfig = {
	url:"XXXXXXX",
	env:"H5"// 1.H5（web项目）2.MP-WEIXIN（小程序项目）
}
const mdapi = zionMdapi.init(mdapiConfig)
// 查看mdapi支持的所有方法
console.log(mdapi)
```

2. 方式二：node环境中使用
```js

// 支持commonjs和ES模块两种引入方式
import zionMdapi from "zion-mdapi";//ES模块化引入
// const zionMdapi = require("zion-mdapi");//commonjs方式
const mdapiConfig = {
	url:"XXXXXXX",
	env:"NODE"
}
const mdapi = zionMdapi.init(mdapiConfig)
console.log(mdapi)
```

3. 方式三：script标签模块化引入
```html
<script type="module">
// 可以使用cdn方式，也可以将文件下载到本地
import zionMdapi from "https://cdn.jsdelivr.net/npm/zion-mdapi/dist/client/index-browser-esm.js"
const mdapiConfig = {
	url:"XXXXXXX",
	env:"H5"
}
const mdapi = zionMdapi.init(mdapiConfig)
console.log(mdapi)
</script>
```

4. 方式四：浏览器script标签直接引入
```html
<script src="https://cdn.jsdelivr.net/npm/zion-mdapi/dist/client/index-browser-iife.js">
</script>

<script>
window.onload = function () {
	const mdapiConfig = {
		url:"XXXXXXX",
		env:"H5"
	}
	const mdapi = zionMdapi.init(mdapiConfig)
	console.log(mdapi)
}

</script>
```

5. 方式五：mdapi框架自定义行为通过http请求调用，示例curl如下
```
curl --location --request POST 'https://zion-app.functorz.com/Y5GGXyqOA5K/zero/L6ARjmeyKwy/api/graphql-v2' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--data-raw '{
    "variables": {
        "args": {
            "actionflow_name": "query",
            "payload": {
                "client_data": {},
                "data": {
                    "model":"account"
                }
            }
        },
        "versionId": 60,
        "actionFlowId": "5e5e155d-7b3c-4771-bf94-ceb597cbb666"
    },
    "query": "mutation fz_invoke_action_flow(\n\t\t\t  $args: Json!\n\t\t\t  $versionId: Int!\n\t\t\t  $actionFlowId: String!\n\t\t\t) {\n\t\t\t  response: fz_invoke_action_flow(\n\t\t\t    args: $args\n\t\t\t    versionId: $versionId\n\t\t\t    actionFlowId: $actionFlowId\n\t\t\t  )\n\t\t\t}"
}'
```


## 1.查询（query）
```js
// 前端直接查询
mdapi.nativeQuery({
	model:"actionflow"
})

// 前端直接批量查询
mdapi.nativeBatchQuery([{
	response_key:"actionflow_1" //批量查询必须设置response_key，且response_key需要唯一
	model:"actionflow"
}])

// 前端带聚合数据的模型查询
mdapi.nativeResponseQuery({
	model:"user",
	aggregate_fields:"count"	//当前表的数据条数
})

// 框架方式查询，需要配置actionflow_id
mdapi.query({
	model:"actionflow",
	where:{},								//查询条件，同graphql接口文档
	order_by:{},							// 排序方式，同graphql接口文档
	limit:0,								// 0表示不限额
	offset:0,								//第几条开始
	fields:`id name actionflow_id` 			//要返回哪些字段信息，参考graphql接口文档
}).then(res=>{
	console.log(res)
}).catch(err=>{
	console.error(err)
})
// 框架方式批量查询
mdapi.batch_query([{
	response_key:"actionflow_1" //批量查询必须设置response_key，且response_key需要唯一
	model:"actionflow"
}])

// 框架方式带聚合数据的模型查询
mdapi.responseQuery({
	model:"user",
	aggregate_fields:"count"	//当前表的数据条数
})


```

## 2.变更（mutation）
```js
// mdapi框架方式变更，需要配置callback_url或项目url
mdapi.mutation({
	operation:"insert_errlog",
	objects:[{
		title:"测试"
	}]
})

// 前端直接操作变更,需要配置项目url
mdapi.nativeMutation({
	operation:"insert_errlog",	// 操作前缀：1.insert(插入) 2.update(更新) 3.delete(删除)
	objects:[{					// 格式为：操作前缀_操作表
		title:"测试"			
	}],
})

// 与查询一样,变更也支持：batch_mutation、nativeBatchMutation
```

## 3.nativeCallActionflow（原生执行自定义行为）
```js
mdapi.nativeCallActionflow({
	actionflowId:"",	//行为流id
	versionId:null,//行为流版本号,
	args:{
		
	}
})
```

## 4.nativeRunJscode（原生执行自定义行为代码）
```js
// 依赖调试密码
mdapi.nativeRunJscode({
	jscode:"",				// 代码内容
	args:{}					// 参数
})
```

## 5.媒体资源上传
```js
// 依赖mdapi框架，公网资源直传，media_url必须为资源格式的公网链接
mdapi.outer_umedia(media_url) 	

// 本地上传资源的方法，上传后可以获得对应的资源id
mdapi.local_uimage(fileObj)   	//fileObj为文件对象或者{path:"blob:xxxx",name:"xxx.png"}格式的对象
mdapi.local_uvideo(fileObj) 
mdapi.local_ufile(fileObj) 
```


## 6.callActionflow（框架执行自定义行为）
```js
// 调用mdapi框架自定义行为
mdapi.callActionflow({
	actionflow_id:"",// 一般无需传入，会自动从配置文件读取
	actionflow_version:null,// 一般无需传入，会自动从配置文件读取
	
	actionflow_dir:"",//指定执行目录
	actionflow_name:"test",
	payload:{
		// 客户端自定义配置数据,未传入时默认从配置文件中读取
		client_data:{}
		
		//...其他业务参数
	}
})
```

## 7.runJscode（框架执行自定义行为代码）
```js
// mdapi框架 依赖调试token,生产环境慎用
mdapi.runJscode({
	jscode:"",				// mdapi框架的actionflow代码内容
	attach_data:{},			// 模拟配置数据
	payload:{}				// 参数
})

```


## 8.调试自定义行为debugActionflow（需要启动文件服务器）
```js
// 依赖调试token,生产环境慎用
mdapi.debugActionflow({
	actionflow_type:"native", 	// 1.native(原生自定义行为)  2.custom(框架自定义行为)
	actionflow_name:"test", 	// 调试代码文件名
	actionflow_dir:"/", 		// 调试代码文件目录
	attach_data:{},				// 仅custom时传入
	payload:{},					// 仅custom时传入
	args:{}						// 仅native时传入
})

// 新增mdapi框架代码
mdapi.insertActionflow("test","/",{
	attach_data,//配置数据
	describe:"",//接口描述
	jscode:"", //为传入时从本地文件服务器自动读取
	parameters:{},//入参jsonSchema
	returns:{}//返回参数jsonSchema
})

// 更新mdapi框架代码
mdapi.updateActionflow("test","/",{
	attach_data,//配置数据
	describe:"",//接口描述
	jscode:"", //为传入时从本地文件服务器自动读取
	parameters:{},//入参jsonSchema
	returns:{}//返回参数jsonSchema
})

// 批量上传mdapi框架代码
mdapi.uploadActionflow("test","/",false) //参数1：行为流名称 参数2：行为流目录 参数3：是否覆盖远端

// 批量下载mdapi框架代码
mdapi.uploadActionflow("test","/",false) //参数1：行为流名称 参数2：行为流目录 参数3：是否覆盖本地

// 格式化actionflow表的actionflow_dir字段，自动检测出不符合文件夹目录规范的字段并格式化，主要是兼容解决旧版调试工具的问题
mdapi.actionflowDirFormat()

```

## 9.本地代码保存和获取（需要启动文件服务器）
```js
//保存自定义代码到本地
mdapi.setActionflowCode([{
	actionflow_name:"test",		// 文件名称
	actionflow_dir:"/",  		// 保存到哪个路径
	actionflow_type:"custom"	// 类型：1.custom（框架自定义行为）2.native（原生自定义行为）
	jscode:"",					// 代码内容
}])


//从本地获取自定义代码
mdapi.getActionflowCode(actionflow_name, actionflow_dir = "/", actionflow_type = "custom")
```

## 10.生成graphql并执行
```js
// mdapi框架执行
mdapi.action({
	response_key:"response", // 节点重命名
	action_name:"user",      // 节点
	inputs:{
		where:{
			id:{
				_gt:0
			}
		},
		order_by:{
			__enum_keys:{// 遇到枚举字段时用放入__enum_keys字段下即可
				id:"desc"
			}
		}
	},				// 节点入参
	fields:["id",{
		action_name:"user_logs",
		fields:['id',"created_at"]
	}]				// 返回内容,支持无限往下嵌套
},{
	type:"query",
	mode:3, // 1.返回生成的gql 2.返回生成的gql内容体 3.生成gql并执行
	variables:{},//执行gql变量
	variables_def:{}// 如果gql语句中包含变量需要提前定义
})

// 前端直接执行，需要配置项目url
mdapi.nativeAction({
	// 参数同上
})

```

## 11.执行gql
```js
// madpi框架执行gql
mdapi.runGql(`query{user{id}}`,{}) //第一个参数：gql字符串   第二个参数：variables变量

// 前端直接执行gql,需要配置项目url
mdapi.nativeRunGql(`query{user{id}}`,{}) 
```

## 12.调用第三方api
```js
// 前端直接调用第三方api,需要配置项目url
mdapi.nativeCallThirdapi({
	thirdapi_name:"",//可选参数，从表中配置第三方api
	action_name:"operation_xxx",//名称
	params:{
		// 参数列表
	},
	fields:"" //需要返回的字段
})

```

## 12.更多内置方法
```js
// 浏览器中为fetch、微信小程序中为wx.request、node环境中为axios
mdapi.request({
	url:"",				//请求地址
	method:"POST",		//请求方法
	headers:{},			//请求头
	data:{}				//数据
}),

//mdapi.generateJWT--------------JWT：参数1：payload，参数2：secret，参数3：header
//mdapi.verifyJWT----------------JWT：参数1：token，参数2：secret

//mdapi.ksort--------------------将对象键值按ascll码表排序：参数1：obj
//mdapi.dateFormat---------------日期格式化函数：参数1：date，参数2：fmt
//mdapi.deepMerge----------------对象深度合并：参数1：obj_result,参数2：obj_provide
```


## 14.AI流式数据处理使用示例
```js
// mdapi.aiUtil中提供了chatgpt、zhipu、azure的流式响应数据转换
import zionMdapi from "zion-mdapi";
let mdapi = zionMdapi.init({env:"H5"});
const OPENAI_API_KEY="";
let resBody = await mdapi.request({
	url: `https://api.openai.com/v1/chat/completions`,
	header: {
		'Authorization': `Bearer ${OPENAI_API_KEY}`,
		'Content-Type': 'application/json'
	},
	data:{
		model:"gpt-3.5-turbo",
		// 消息内容
		messages: [
		      {
		        role: "system",
		        content: "你是个有用的助手。"
		      },
		      {
		        role: "user",
		        content: "你好!"
		      }
		],
		// 流式返回结果
		stream: true
	}
})

// 流数据转化为js对象
async function streamDeal(resBody,callBack){
	const reader = resBody.getReader();
	while (true) {
		const {
			value,
			done
		} = await reader.read();
		if (done) {
			break; // 读取完毕
		} else {
			let messageStr = new TextDecoder().decode(value);
			let messageRes = mdapi.aiUtil.chatgpt.dealStreamStr(messageStr) ;
			callback && callback(messageRes)
		}
	}
} 
let content=""
await streamDeal(resBody,result=>{
	// 这里会逐条收到回调信息
	content += result.content;
	if(result.process=="finish"){
		// 已获取到完整的输出内容
		console.log(content)
	}
})


```


# 第四步：调试功能配置说明（可以调试zion的行为流自定义代码块）
1. 运行服务器（package.json添加scripts,第一个终端执行`npm start`启动文件服务器，第二个终端执行`npm test`启动调试服务器）
```json
	// 文件服务器默认服务位置：http://localhost:3020（端口号默认为server_port+1）
	// 调试服务器默认服务位置：http://localhost:3021（端口号默认为server_port+1）
	"scripts": {
		"start": "cd node_modules/zion-mdapi && npm start",
		"test": "cd node_modules/zion-mdapi && npm test"
	}
```

2. 在md_actionflow文件夹对应的自定义行为目录（custom或native）下新建js文件编写代码即可。

3. 目录`/node_modules/zion-mdapi/dist/actionflow/`下放置了`mdapi框架自定义行为.umd-all.js`为该框架的actionflow入口文件，复制内容粘贴到zion的actionflow代码块中部署后即可生效。

4. 代码打包文件cdn目录[dist目录](https://cdn.jsdelivr.net/npm/zion-mdapi/dist/)。

# 更多文档及教学课程参考
1. [技术文档](https://functorz.feishu.cn/wiki/Dcl7w3rfViSfa6kl11ScSqirngc?from=from_copylink)
2. [视频教学文档](https://t4auvosy3y.feishu.cn/wiki/Wr5WwSqfUiCLTMkWxFtcsznDnLc?from=from_copylink)

有问题可在文档评论~

# 更新日志
-【2.1.7】【mdapi】
1. setReturn报错捕捉、增加console.log日志记录到阿里云

-【2.1.6】【zion-mdapi、mdapi】
1. 修复action生成graphql代码时fields嵌套传入时的bug问题
2. mdapi框架配置回调时会尝试自动读取actionflow_name、actionflow_dir、payload入参

-【2.1.5】【zion-mdapi】
1. 修复上传文件无法上传压缩包的问题

-【2.1.4】【调试工具】
1. 修复npm run test后报错的问题

-【2.1.3】【zion-mdapi】
1. mdapi.config.json改为mdapi.config.js模块导出方式
2. mdapi.config.js增加配置项isClog，控制是否在控制台打印请求日志，默认会进行打印，isClog设置为false时会关掉打印日志。不再使用process变量来判断生产还是开发环境

-【2.1.2】【zion-mdapi】
1. callActionflow第二个参数默认会自动处理status!=="成功"的错误

-【2.1.1】【mdapi框架、zion-mdapi】
1. 修复action生成gql时出现inputs中数组参数不符合期待的问题，如：_in:["zach","yach"]
2. 新增developer数据模型，可以独立配置开发者的接口调用权限
3. `decimalToBase62`和`base62ToDecimal`方法支持bigInt入参，长度不足默认填充0变为默认填充a
4. 取消`dataToToken`方法的必传`id`条件限制
5. 调试工具方法`updateActionflow`、`insertActionflow`、`uploadActionflow`、`downloadActionflow`、`actionflowDirFormat`更改为后端操作
6. `callNativeThirdapi`改名为`nativeCallThirdapi`、`runNativeJscode`改名为`nativeRunJscode`、`callNativeActionflow`改名为`nativeCallActionflow`
7. 相关入参名全局调整,涉及变动有`nativeRunJscode`,`nativeCallActionflow`,`callActionflow`
8. 调试工具强依赖madpi框架，配置文件兼容两种模式，1.前端直接控制【url+actionflow_id+actionflow_version】 2.统一回调入口【callback_url】。需求满足时推荐使用callback_url模式
9. mdapi框架新增接口`callActionflow`、`outer_umedia`、`nativeRunJscode`

-【2.0.8】【mdapi框架】
1. 修改errlog和binlog数据模型，报错机制更新
2. `query`、`mutation`、`responseQuery`、`callThirdapi`方法的参数优先级调整

-【2.0.7】【mdapi框架】
1. 客户端的responseQuery兼容aggregate_fields入参
2. query和mutation兼容inputs节点输入

-【2.0.6】【zion-mdapi】
1. 运行npm start脚本会在项目根目录自动生成带演示项目的配置文件`mdapi.config.js`
2. responseQuery兼容aggregate_fields入参
3. 使用原生自定义行为时必须配置actionflow_vertion

-【2.0.5】【mdapi框架】
1. 修复中间件不支持使用setReturn和错误抛出的bug

-【2.0.1】【mdapi框架】
1. `middleware`指定在`actionflow`表中的`actionflow_dir`名必须为`/middleware/`

-【2.0.0】【客户端、mdapi框架】
1. 配置文件新增`client_data`配置项，并将`token`和`actionflow_pwd`配置项移动到`client_data`配置项下
2. 新增入参`actionflow_dir`可用于`actionflow_name`指定文件夹目录
3. `middleware`分解为`start_middleware`和`end_middleware`,可在`/md_actionflow/custom/middleware/`目录下编写中间件代码后上传
4. mdapi新增挂载项`response`、`actionflow`、`actionflow_dir`