powder backend docs
user configs: https://docs.uninote.com.cn/book/1/2257#353472986F
开发环境搭建
初始安装项目到本地
git clone git@git.dajxyl.com:cyb/powder.git
cd powder
git checkout dajx
npm install
cd src\powder && npm install
运行服务
cd 项目根目录
npm run serve
# 示例访问地址(这些参数 dev 服有效):http://localhost:8080/?phone=13320817857&staff_id=62&phone_staff=13778707887&passwd_staff=123456
项目结构
- /api: 接口文档存放处
- /powder/generated: 根据 /api 自动生成的代码,不要手动修改
- /powder/user: 根据 /api 自动生成的模板代码,在此基础上编写生成函数、测试代码等
- /tests - 测试相关的公共模块存放处
开发流程
整个接口的开发流程就是不断的重复以下三个步骤。
API: code as doc
https://docs.uninote.com.cn/book/1/2374#48E64A965B
代码生成
- cd src\powder
- node generate.js
- 如果有冲突,会自动调起 beyond compare 解决冲突,完成后再执行 node generate -r(自动更新 base 文件,类似于 git add xx,并清理临时文件)**
- 需要将 beyond compare 加入 PATH 环境变量
- 当然也可以手动解决冲突,但强烈不建议!
- 注:可以使用 node generate.js -w,开启监控模式,文件更新后会自动重新生成代码,这一步不再需要每次手动执行。按 ctrl + c 退出监控模式。
- node generate 会进行时间戳比较,只有当 api 下的文件比 user 下对应的文件更新时,才会执行生成;如要强制执行,可以加 -f 参数
解决冲突
原理
tips
如果对冲突解决是否正确没有把握,可以进行多次比对确认:
- x.js.base -- x.js.new,必要时手动将变更添加到 x.js
- x.js.new -- x.js: 确认是否基于模板正确修改
- 执行 node generate -r
- old x.js -- x.js 确认之前的手动修改是否丢失
编写生成函数、测试代码等 (user/*):
user 规范详解:https://docs.uninote.com.cn/book/1/2257#353472986F
- 配置 default 参数
- 编写 gene args 生成逻辑
- 调通接口
- 编写单元测试
生成函数 - core of powder
对象组成结构
假设 api 下有一个模块 test.js,则生成代码后的对象组成结构为:
user/test.js -> generated/test.js -> base(base.js)
|--> api/test.js
- 尽可能的使用封装对象,当然必要时仍然可以直接调用原始 api
使用场景
当做 postman 使用时(前端开发人员)
查看接口时,一键生成参数,就能保证接口请求成功
当前接口开发时
编写/演示 必要的场景,eg:
- 创建多 sku 的订单:http://powder.dajxyl.com/?select-module=client%2Fshop%2Forder&select-api=create#/
- 朋友圈动态评论列表:http://powder.dajxyl.com/?select-module=client%2Ffriends%2Fcomment&select-api=comment#/
这里相当于将自测的用列固化为测试代码(场景),以备后用(共享)
- 自己
- review
- 测试
- 前端开发人员
- 批量测试
- 被依赖
接口开发完成的标志就是,对于每一个场景,接口返回值验证、单元测试都通过了(“请求-完整验证”不报错)。
被依赖时: base.make()
调试定位
这里 讲了两种线上定位方式,开发环境时,还可以通过 debugger 定位:
请求: base.request()
- GUI 时,最终的请求参数由 默认值、生成值、自定义值 共同决定 https://docs.uninote.com.cn/book/1/2943#2F8162F43D
- 其他情况下发送请求,仅由 默认值、生成值 共同决定
完整验证: base.fullValidate()
https://docs.uninote.com.cn/book/1/2257#DB817E0B31
生成函数的结果 - 生成值,会被常规流程使用一次,以及 testSuites 使用 0-n 次。
返回值验证
- 状态码验证
- 数据结构验证:验证当前的请求结果是否与接口的返回值注释匹配(仅比较结构)
单元测试
生成参数保存-还原:base.save() & base.restore()
便捷的生成参数保存-还原工具,用于保存一份有效的参数,后面每次在此基础上进行修改测试:
ctx.generated = {
id: 1,
name: 'lucy',
};
ctx.save();
ctx.generated.id = 2;
console.log({...ctx.generated}); // {id: 2, name: 'lucy'}
ctx.restore(); // 恢复上次保存的生成参数
console.log({...ctx.generated}); // {id: 1, name: 'lucy'}
按照当前参数显式
的调用接口:base.request2()
一般情况下,都是在生成函数中生成参数,再由框架负责调用接口,下面的实例中,则是在生成函数中循环
生成参数-调用接口(cartAdd)。这里生成函数类型设置为 GENE_ONLY, 是为了生成函数调用后框架不会再次调用接口(cartAdd)。
'cartAdd': {
'__proto__': base.cartAdd,
'api': apis.cartAdd,
'desc': " [商城模块]添加购物车",
'geneArgs': [
{
'scene': 'product_sku_list',
'desc': '添加商品SKU列表',
'type': GENE_ONLY,
'func': async (ctx) => {
let shop_sku_id = (await infos.productSkuList.make()).data.data.info.productSku;
for (let sku_id of shop_sku_id) {
ctx.generated.product_sku_id = sku_id.id;
await ctx.request2()
}
}
}
快速错误定位
-
ReqIndex:request index,会为每一个请求生成一个索引,每次请求加1,页面刷新后重置为0。
- network panel, request header 可以看到
- 报错时可以得到出错的 ReqIndex:
- 如果启用了 debug,则有如下输出:
-
TrackID:每个终端可以配置自己的 ID,当多终端并发测试时后台日志可以区分是哪个终端发送的请求
- 配置 tid=1,建议在 local storage 全局配置
-
ReqIndex,TrackID在后台的日志中也会相应存在,可以方便查找对应的日志
-
以上两点的前提是后台记录了请求参数信息
实例 & 技巧
// 缓存数据到 ctx 中以备后用:
ctx.__info = info;
// 直接调用依赖接口:
let data = (await infos.getExpressSelect.api(arg1, arg2)).data.data;
// 都通过 make 调用依赖接口,能够直接拿到返回值
let data = (await infos.getExpressSelect.make()).data.data;
// 祖先接口返回值获取:
// 1 模块内:
infos.create.lastResult.data
// 2 跨模块:
import * as shop_order from '../client/shop/order';
shop_order.infos.create.lastResult.data
- 表情要用这种表示方式:
配置 webpack.alias.js:
- 用 '@' 表示 'src',简化路径引用,eg:
import * as scms from '../../../tests/scms';
// 等价于上面的写法,但更简洁,同时文件移动位置后更易于维护
import * as scms from '@/tests/scms';