当前位置: 首页 > 后端技术 > Node.js

从零开始搭建Mock平台——功能模块

时间:2023-04-03 22:52:43 Node.js

前言2月初的时候,想着这个月要搞一篇文章,也没有什么好的想法,记录一下毕设的一些想法。重要特性扩展的一些重要特性将从头开始考虑。项目导入导出功能项目导入导出的思路是在设置feature功能的时候想到的,主要针对不同的服务器。平台部署的话,如果想私下部署测试,那就重新建立项目,然后接口一一配置。路径,配置返回数据是一件很麻烦的事情。为了以后能顺利使用(自己)用户(人),一键想到了各个版本的功能,包括项目的导入导出。因此,在设计数据结构时,我将最终返回的结果数据设计成如下形式{project:{projectId:,...interfaceList:[]}},即前端本地缓存中的数据即可直接导出。当然导出可以先过滤处理一些信息,比如projectId,interfaceId。所以导出可以是纯Json文本。然后在导入的时候需要校验Json格式。其实需要遍历一下,看看有没有应该有的属性。否则会报错,不会导入数据。而在导入的时候,需要做一个分类,是导入到项目示例中,还是导入到个人/团队项目中。最常见的是打包下载zip。这个大概需要后端处理,所以先看看有没有前端处理。搜索解决方案后,结合github上一些项目的源码,可以简单的写一个导出模块导出函数exportFile(data:string,filename:string,type:string){vartypeList={json:'application/JSON;charset=utf-8',markdown:'text/markdown;charset=utf-8',doc:'application/mswordcharset=utf-8',}//创建一个隐藏的可下载链接vareleLink=document.createElement('a');eleLink.download=文件名;eleLink.style.display='无';//字符内容被转换为blob地址varblob;blob=newBlob(['\uFEFF'+data],{type:typeList[type]});eleLink.href=URL.createObjectURL(blob);document.body.appendChild(eleLink);eleLink.click();document.body.removeChild(eleLink);}调用方式很简单,就是传入三个A参数:exportFile(JSON.stringify(this.state.currentProjectData),'default.md','markdwon')的当然,我们现在得到的数据并没有经过处理,我们需要对数据进行过滤,去掉一些关键信息和不需要的数据。假设当前数据是这样的","version":"v1.0","transferUrl":"http://haoqiao.me/api/project","status":"transfer","type":"demo","teamMember":[{"_id":"user001","username":"2333","role":"前端工程师","avatar":"https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"},{"_id":"user002","username":"宋庆书","role":"后端工程师","avatar":"https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"}],"interfaceList":[{"_id":"interface001","interfaceName":"Get","url":"/getAll","method":"get","desc":"接口说明","mode":"{data:1||2}"},{"_id":"interface002","interfaceName":"Add","url":"/add","method":"post","desc":"接口描述","mode":"{data:1||2}"},{"_id":"interface003","interfaceName":"delete","url":"/delete","method":"delete","desc":"接口描述","mode":"{data:1||2}"},{"_id":"interface004","interfaceName":"更新","url":"/update","method":"put","desc":"接口说明","模式“:“{数据:1||2}"}]}我们需要把里面的_id(字段),teamMember(数组)全部去掉。想想如果是自己的怎么办?其实很简单,只要看懂原来的JSON.stringify就可以了比较熟悉,你知道它完整的定义如下JSON.stringify(value,replacer?,space?)replacer是一个过滤函数或者是一个包含要字符串化的属性名的数组,如果没有定义,所有的属性都是默认的全部字符串化,可以做一个遍历器,遍历Json中的属性名,然后进行内消。例如filterData=(json:any)=>{console.log(json)letexpectArr=['_id','teamMember']letfilterArr=[]letresult=''for(letkeyinjson){if(expectArr.indexOf(key)===-1){filterArr.push(key)}result=JSON.stringify(this.state.currentProjectData,filterArr)console.log(result)returnresult}但是这样只能得到第一个如何获取嵌套数组中Json的属性名?我们只需要再做一个判断filterData=(json:any)=>{console.log(json)letexpectArr=['_id','teamMember']letfilterArr=[]letresult=''for(letkeyinjson){if(expectArr.indexOf(key)===-1){filterArr.push(key)//如果是嵌套数组且数组中有数据if(Object.prototype.toString.call(json[key])=="[objectArray]"&&json[key].length>0){for(letiteminjson[key][0]){//同样对里面的json数据进行属性字段过滤if(expectArr.indexOf(item)===-1){filterArr.推(项目)}}}}}result=JSON.stringify(this.state.currentProjectData,filterArr)console.log(result)returnresult}这将过滤掉应该存在的属性,然后进行转换以仅过滤所需的数据。数据清洗后变成如下格式:"项目描述","version":"v1.0","transferUrl":"http://haoqiao.me/api/project","status":"transfer","type":"demo","teamMember":[{"_id":"user001","username":"2333","role":"前端工程师","avatar":"https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"},{"_id":"user002","用户名":"宋庆书","role":"后端工程师","avatar":"https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"}],"interfaceList":[{"_id":"interface001","interfaceName":"Get","url":"/getAll","method":"get","desc":"Interface说明”,“模式”:“{data:1||2}”},{“_id”:“interface002”,“interfaceName”:“添加”,“url”:“/添加”,“方法”:“post","desc":"接口描述","mode":"{data:1||2}"},{"_id":"interface003","interfaceName":"delete","url":"/delete","method":"delete","desc":"接口描述","mode":"{data:1||2}"},{"_id":"interface004","interfaceName":"更新","url":"/update","method":"put","desc":"接口说明","模式“:“{数据:1||2}"}]}之后就要考虑导入项目了,我首先想到的是点击上传json格式文件然后读取里面的内容,然后校验数据让后台导入到指定的表中.这里发生了一些事情,比如我肯定不希望用户上传json文件到服务器,我觉得这个东西前端肯定可以解析解决,但是我们还是需要一个上传按钮来交互UI,这里我直接使用了antd的upload组件有一个方法叫beforeUpload,只要我们在这个函数中直接返回false,就不会触发上传动作,但是我们可以拿到本地的File,我们可以使用新的HTML5方法FileReader可以帮助读取内容。我们的组件可以这样修改constuploadProps={name:'file',action:'',showUploadList:false,beforeUpload:(file:any)=>{constisJSON=file.type==='application/json';if(!isJSON){Message.error('只允许上传JSON格式的文件!');}constisLt2M=file.size/1024/1024<2;if(!isLt2M){Message.error('JSON文件大小必须小于2MB!');}varreader=newFileReader();//读取操作全部由FileReader完成varthat=thisreader.readAsText(file);reader.onload=function(){//读取完成getvaluefromconstjson=this.resultif(isJson(json)&&that.state.uploadJsonData.length===0){that.setState({uploadProject:true,uploadJsonData:json})Message.success('Json文件上传识别成功!');}}返回假;}},onChange:(info:any)=>{},};<拖动器{...uploadProps}>

点击上传JSON文件或拖拽到上传JSON文件

下面看一下实际的交互效果,这样我们就可以打通项目的导入导出功能的交互,然后连接接口就行了。项目克隆/界面克隆这两个功能其实很相似,主要是用来帮助用户复制已有的界面或项目。比如我之前建立了一套系统接口,包括增删改查。我的下一个系统和这个系统很像,可能只要改几个字段就可以用了。当然我们可以使用import和import功能,但是在系统内部我们最好有一个一键迁移的方式,那就是clone。克隆需要注意,首先是接口克隆,假设我们接口定义的格式如下:_id(pin):"interface005"interfaceName(pin):"register"url(pin):"/reg"method(pin):"post"desc(pin):"Interfacedescription"mode(pin):"{data:1||2}"然后我为了方便把接口Model包含在我定义的项目Model中。即我只需要将接口Id和项目Id传递给后台,让后台做一个查询接口内容,然后新建一个接口将查询到的内容插入到指定的Id中即可。这很简单。主要部分是UI,但是数据流的管理是可以用时间解决的。如下图所示:之后就是克隆工程。我们首先知道项目Model包含接口Model,所以我们在clone整个项目的时候,其实需要把整个接口都提取出来,需要去掉团队成员,因为新的clone项目应该只有创建者,所以我们需要从前端传递用户ID,当然不是必须传递,也可以通过Jwt解析token来识别用户信息。主要是后台拿到信息后,其思路应该是先查询本项目的信息,然后提取部分信息创建新项目,然后遍历原项目的接口列表创建接口分批。个人信息变更基本上中后台应用都会有个人信息管理,有的使用形式,有的拆分。其余的数据很容易处理。无非是传递参数的问题,前后端约定的问题。当然,比较麻烦的其实是换头像。假设你在注册时默认分配了一个用户头像,然后用户想在个人信息中更改。问题来了。换头像其实是一个交互问题,一定不能让用户一步步操作。相反,它一步完成。上传符合要求的图片后,即可获取上传图片的地址。然后更改本地数据。数据也需要在后台自动更新。这里用redux维护了一个本地的前端数据层,所有的显示变化显示操作都需要更新。然后在reducer中监听action,使用定义为UPDATE_LOCALXXX的action提交数据等待后台处理完成后返回成功,然后更新本地数据。显然这个动作是一个异步操作。如果需要管理很多类似的操作,我们的代码写起来肯定会很乱。所以在技术测评阶段引入了rxjs。通过其特色的时间线管理,非常容易。下面是一个简单的示例代码loginValidation.map((response:any)=>{console.log(response);if(response.state.code===1){updateUserSuccess(response.state.msg);returnupdateLocalUser(action.data);}else{console.log('tokenerror')updateUserError(response.state.msg);returnnothing();}})//仅在服务器崩溃时捕获错误.catch((e:any):any=>{//console.log(e)returnObservable.of(({type:USER_LOGINERROR})).startWith(loadingError())})});最后还有一些功能需要等后端开发记录思路,所以这部分先放在这里。