\n \u003C/ClientOnly>\n\u003C/template>\n\n\u003Cscript setup>\nconst model = ref()\nconst toolbarsConfig = {\n // ... toolbars config\n}\n\u003C/script>\n\n4. 上传图片\n//增加两个参数: md、@imgAdd\n\u003Cmavon-editor v-model=\"content\" ref=\"md\" @imgAdd=\"upImage\" /> \u003Cbr>\n\u003Cscript setup>\n let md = ref()\n const readFile = (file) => {\n return new Promise(resolve => {\n let reader = new FileReader()\n reader.readAsArrayBuffer(file)\n reader.onload = (event) => {\n resolve(Buffer.from(event.target.result))\n }\n })\n\n}\nconst upImage = async(pos, file) => {\n //换成自己的ipfs主机,或是使用自己的上传逻辑\n const ipfs = create({ host: 'ipfs.example.io', port: '2885', protocol: 'https' })\n const ipfs_host = \"https://ipfs.example.io/ipfs/\" \n let content = await readFile(file)\n let res = await ipfs.add(content)\n md.value.$img2Url(pos, ipfs_host+res.path)\n}\n\u003C/script>\n```\n\n好了,集成好了。`manon-editor`是我用了很长的编辑器,都有感情了,舍不得换其它的。再说它也很好嵌入和易用,还是多想办法集成为是。\n\n",[15,16,18,20,41,42],"manoneditor","editor","2025-08-02T02:28:33.000Z",{"_id":45,"user_id":8,"username":9,"title":46,"author":9,"category":11,"permlink":47,"body":48,"tags":49,"created":53,"__v":24},"68a4297e9c0586aeab990978","Supervisor管理python web服务 / 网络研习社#88","hp1qg1vb","\n\nSupervisor就是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。\n\n因为是python开发的一个库,可以直接用pip来安装,很方便。supervisor安装完成后会生成三个执行程序:supervisord、supervisorctl、echo_supervisord_conf,分别是supervisor的守护进程服务(用于接收进程管理命令)、客户端(用于和守护进程通信,发送管理进程的指令)、生成初始配置文件程序。\n\n```py\npip install supervisor #supervisor-4.2.5\n\necho_supervisord_conf #查看基本配置\necho_supervisord_conf > /home/wsgi.ini #生成初始配置文件\n\n# 配置范例\n[program:wsgiX] ;program:名称\n;工作目录(脚本启动目录的全路径)\ndirectory=/home/knowqa \n;启动命令,当然你可以直接 python api.py,此处使用gunicorn启动\ncommand = /home/knowqa/know_env/bin/python /home/knowqa/know_env/bin/gunicorn -c config.py api:app\nstartsecs=0\nstopwaitsecs=0\nautostart=true ;supervisord守护程序启动时自动启动tornado\nautorestart=true ;supervisord守护程序重启时自动重启tornado\nredirect_stderr=true ;将stderr重定向到stdout\n;日志标准输出路径,同时脚本print打印信息也会在改文件显示\nstdout_logfile=./stdout.log\nstderr_logfile=./error.log\n\n;守护进程,可在 web 上访问\n[inet_http_server] ; inet (TCP) server disabled by default\nport=0.0.0.0:9001 ; (ip_address:port specifier, *:port for all iface) 127.0.0.1\nusername=user ; (default is no username (open server))\npassword=123 ; (default is no password (open server))\n\n;supervisord日志配置\n[supervisord]\nlogfile=/tmp/supervisord.log ; main log file; default $CWD/supervisord.log\nlogfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB\nlogfile_backups=1\n\n# 启动\nsupervisord -c ./wsgi.ini\n//supervisord -c /home/wsgi.ini\n```\n\n### 查看运行状态\n`ps -ef | grep supervisord`\n\n\n看到如上所示,即运行正常。如果要停止,则直接kill, 比如: `kill 197320` 。命令如下:\nkill pid #停止运行\n\n在Supervisor的使用中,**其中“工作目录”和 “启动命令”是最关键的两处设置,务必正确!** 在“启动命令”时可以看到是使用`gunicorn`来启动服务的,就来补下`gunicorn`的设置。\n\n## Gunicorn\nGunicorn是一个unix上被广泛使用的高性能的Python WSGI UNIX HTTP Server,和大多数的web框架兼容,并具有实现简单,轻量级,高性能等特点。\n\n```py\npip install gunicorn\n\n# 快速启动run.py\ngunicorn --workers=4 --bind=0.0.0.0:8000 run:app\n\n# run.py\nfrom flask import Flask\napp = Flask(__name__)\n\n# 配置文件启动\n命令行中定义的参数,都可以放在配置文件中。\n# 配置文件范例 config.py\nimport multiprocessing\n\nbind = \"0.0.0.0:8000\"\nworkers = multiprocessing.cpu_count() * 2 + 1\n\nbacklog = 2048\nworker_class = \"eventlet\"\nworker_connections = 1000\ndaemon = True\npidfile = 'log/gunicorn.pid'\naccesslog = 'log/access.log'\nerrorlog = 'log/gunicorn.log'\n\n# 启动\ngunicorn --config=config.py run:app\n\n# 服务重启、退出等\n获取Gunicorn进程树,用下面的命令获取gunicorn的Master PID\n#方法1\npstree -ap|grep gunicorn\n#方法2\nps -ef|grep gunicorn \n# 重启Gunicorn进程\nHUP(终端断线)信号发出之后,worker进程会进行被杀掉,并启动新的进程,保证源代码的修改会反映进来。master进程不会变。\nkill -HUP master_pid\n# 优雅停止Gunicorn进程\npkill gunicorn\nkill -9 master_pid\n```\n\n\n\n\n",[15,18,16,50,51,52],"gunicorn","supervisor","python","2024-04-28T06:35:51.000Z",{"_id":55,"user_id":8,"username":9,"title":56,"author":9,"category":11,"permlink":57,"body":58,"tags":59,"created":64,"__v":24},"68a4297e9c0586aeab990926","sharp分割图片 / 网络研习社#87","i045jyle","最近在开发AI绘图时,Midjourney生成图像时是一次就是四张,并且这四张是长一起的!分析了一下这张图片,其实这并不是一张小图,而是一张大图(7M以上)!它其实就是成品,并不需要额外地再去放大生成一次!discord中要再次生成,估计也是想要你多消费吧。获取的数据如下:\n```js\n{\n id: '1186583581955469334',\n flags: 0,\n content: '**futuristic motorcycle concept inspired by Egyptian mythology** - \u003C@901742107700633620> (fast)',\n hash: 'cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189',\n progress: 'done',\n uri: 'https://cdn.discordapp.com/attachments/1073862082413473814/1186583581678653511/lemooljiang_futuristic_motorcycle_concept_inspired_by_Egyptian__cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189.png?ex=6593c713&is=65815213&hm=0eb95c361d276f887d9b7e59876a2cd9d6b2edcda15653005e2786df09f40b09&',\n proxy_url: 'https://media.discordapp.net/attachments/1073862082413473814/1186583581678653511/lemooljiang_futuristic_motorcycle_concept_inspired_by_Egyptian__cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189.png?ex=6593c713&is=65815213&hm=0eb95c361d276f887d9b7e59876a2cd9d6b2edcda15653005e2786df09f40b09&',\n options: [\n {\n type: 2,\n style: 2,\n label: 'U1',\n custom: 'MJ::JOB::upsample::1::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n },\n {\n type: 2,\n style: 2,\n label: 'U2',\n custom: 'MJ::JOB::upsample::2::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n },\n {\n type: 2,\n style: 2,\n label: 'U3',\n custom: 'MJ::JOB::upsample::3::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n },\n {\n type: 2,\n style: 2,\n label: 'U4',\n custom: 'MJ::JOB::upsample::4::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n },\n {\n type: 2,\n style: 2,\n label: '',\n custom: 'MJ::JOB::reroll::0::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189::SOLO'\n },\n {\n type: 2,\n style: 2,\n label: 'V1',\n custom: 'MJ::JOB::variation::1::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n },\n {\n type: 2,\n style: 2,\n label: 'V2',\n custom: 'MJ::JOB::variation::2::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n },\n {\n type: 2,\n style: 2,\n label: 'V3',\n custom: 'MJ::JOB::variation::3::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n },\n {\n type: 2,\n style: 2,\n label: 'V4',\n custom: 'MJ::JOB::variation::4::cb0ebbd9-ab8f-472d-b9a6-8d42d9bc9189'\n }\n ],\n width: 2048,\n height: 2048\n}\n```\n\n\n\n\nMidjourney一次生成的图像\n\n从图中可以看到,图片的精度已经足够,没有心要再去单独生成放大图。至于变异图,再生成一次就好啦。好了,那摆在眼前的就是将这个拼图平均拆成四份,再上传到服务器,得到需要的url地址.\n\n这里用到的拆分的包是`sharp`, 拆分的图传到IPFS网络, 就这么个思路。\n\n## sharp使用\n```js\nnpm install sharp --save\n\nimport sharp from 'sharp'\n\nasync function splitImageIntoFour(inputImagePath) {\n try {\n // 读取图片信息\n const metadata = await sharp(inputImagePath).metadata()\n console.log(566, metadata)\n /*\n {\n format: 'png',\n width: 2048,\n height: 2048,\n space: 'srgb',\n channels: 3,\n depth: 'uchar',\n density: 72,\n isProgressive: false,\n hasProfile: false,\n hasAlpha: false\n }\n */\n\n // 计算每个部分的尺寸\n const width = metadata.width / 2\n const height = metadata.height / 2\n console.log(233, \"width height\", width, height)\n \n\n // 分别裁剪四个部分\n const topLeft = sharp(inputImagePath).extract({ left: 0, top: 0, width, height })\n await topLeft.toFile('./top-left.png') \n const topRight = sharp(inputImagePath).extract({ left: width, top: 0, width, height })\n await topRight.toFile('./top-right.png')\n const bottomLeft = sharp(inputImagePath).extract({ left: 0, top: height, width, height })\n await bottomLeft.toFile('./bottom-left.png')\n const bottomRight = sharp(inputImagePath).extract({ left: width, top: height, width, height })\n await bottomRight.toFile('./bottom-right.png')\n\n console.log('图片已成功四等分')\n\n } catch (error) {\n console.error('发生错误:', error)\n }\n}\n\n// 调用函数,传入图片路径\nsplitImageIntoFour('./image/test.png')\n\n//也可以直接传入网络图片,以buffer传入\nconst url = \"https://cdn.discordapp.com/attachments/107386208241ebb513ea501934aabf0a&\"\nconst response = await fetch(url)\nconst bufferX = await response.arrayBuffer()\nsplitImageIntoFour(bufferX)\n```\n\n## 上传网络图片到IPFS\n```js\nimport fetch from 'node-fetch'\n\nlet url = \"https://pic.ntimg.cn/file/20160921/4562496_103844736000_2.jpg\"\nconst response = await fetch(url)\nconst bufferX = await response.arrayBuffer()\nconst content = Buffer.from(bufferX)\nlet resX = await ipfs.add(content)\nlet imgHash = resX.path\nconsole.log(88, resX, 456, imgHash)\n```\n\n[AI·Joe](https://ai.ilark.io/image) 已新增了Midjourney模型,欢迎大家使用!\n\n\n",[15,16,18,60,61,62,63],"midjourney","sharp","ipfs","aigc","2023-12-28T03:22:03.000Z",{"_id":66,"user_id":8,"username":9,"title":67,"author":9,"category":11,"permlink":68,"body":69,"tags":70,"created":73,"__v":24},"68a4297e9c0586aeab9908ec","linux文件权限梳理 / 网络研习社#86","0ut1qme3","linux的文件权限颇为复杂,简单地读下资料是很难弄明白的,非得实践多次才能完全整明白。以前我是对权限不太重视,不管在哪,都是一个root通行天下。现在有多人协作,以及安全性方面的考虑,需要适当地避免root而使用普通用户的角色。\n\n文件权限是个系统工程,对于linux来说“一切皆文件”。,要梳理这方面的知识,也确实花了近两天的时间。整理和测试后,归纳如下:\n\n\n\n```sh\nll 查看当前所有目录的权限 \nll -d /home/test 查看对test目录的权限\n\ndrwxr-xr-x 4 root root 4096 May 31 09:52 test/\ndrwxr-xr-x 2 lemool lemool 4096 Sep 28 02:31 lemool/\ndrwxr-xr-x 2 root root 4096 Apr 6 13:27 mongodb/\n-rw-r--r-- 1 root root 68 Aug 28 07:01 ser.txt\n前十位表示文件或文件夹的权限,\n第一个数字表示文件类型,2-4 属主, 5-7 属组,8-10 其他人\n\n权限代号:\nr:读取权限,数字代号为 “4” \nw:写入权限,数字代号为 “2” \nx:执行权限,数字代号为 “1” \n-:不具备任何权限,数字代号为 “0”\n\n权限操作: \nchmod + - = 改变权限 u g o a\nchmod [ u / g / o / a ] [ + / - / = ] [ r / w / x ] file\n[ u / g / o / a ] 为权限范围,其中 \nu:User,即文件或目录的拥有者 \ng:Group,即文件或目录的所属群组 \no:Other,除了文件或目录拥有者和所属群组外,其他用户都属于这个范围 \na:All,即全部用户\n+表示增加权限 \n-表示取消权限 \n=表示取消之前的权限,并给予唯一的权限\neg:\n chmod u=rwx a.txt //对a.txt的属主增加所有权限\n chmod u+w a.txt //对a.txt的属主增加写权限\n chmod u=- a.txt //对a.txt的属主减去所有权限\n chmod o+w /home/test //对目录test增加其他人的可写权限\n chmod u+rw /code/readme.txt //给User用户增加了对”/code/readme.txt”文件 “w” 和 “x” 的权限\n chown lem.lem test.txt //更改test.txt的属主和属组为lem\n\n chmod +x 的意思就是给执行权限\n chmod +x start.sh\n\n# 也可以数字的形式来代表权限\n# r-4 w-2 x-1 --0 几个数字相加得到权限,比如7就是所有权限\nchmod o+w /home/test //增加其他人对test文件夹的权限,其他人就可以在此自由的操作了\nchmod -R 774 /code/ //修改这个目录,以及子目录下文件的所有权限, -R表示递归\nUser : 7 = 111 表示具有 ” r , w , x” 权限 \nGroup : 7 = 111 表示具有 ” r , w , x” 权限 \nOther : 4 = 100 表示只具有 ” r ” 权限,而没有 “w,x” 权限\n```\n\n总结下来看似简单,但要理解每个符号的意义却要花不少时间和心思。行动起来吧!\n",[15,16,18,71,72],"linux","chmod","2023-10-10T03:27:48.000Z",{"_id":75,"user_id":8,"username":9,"title":76,"author":9,"category":11,"permlink":77,"body":78,"tags":79,"created":81,"__v":24},"68a4297e9c0586aeab9908de","Mongodb数据库遍历 / 网络研习社#85","cfw6j6hj","\n\nhttps://www.mongodb.com/\n\nMongodb是个简单易用、拓展性极强的数据库。对它的查询和遍历说不上难,但也有些技巧在的。\n\n## 条件查询\n```js\n//增加查找条件\nor() :添加限制条件\nlimit(1) :查看几个\nsort() :排序 \nskip():跳过几个\nUser.find()//查询全部\n .or([{gender:2}])//查询gender为2的数据\n .sort({_id:'desc'})//倒序排列数据, 或sort({_id: -1}),asc正序 \n .limit(2)//展示两条\n .skip(2)//跳过前两个\n\neg:\nlet images = await User\n .find({user_id: req.user_id})\n .sort({_id:'desc'})\n .limit(50)\n```\n\n## 对数据库遍历\n```js\n// nodejs端\n//查询用户\napp.get('/getusers/:skip', async (req, res) => {\n try {\n let limit = 5\n let skip = req.params.skip\n // 主要是通过limit和skip来进行遍历\n // limit 和 skip 要保持一致\n let users= await User\n .find()\n .sort({_id:'desc'})\n .limit(limit)\n .skip(skip)\n res.status(200).send({\n users\n })\n\n } catch (err) {\n console.error(556, err)\n res.status(500).send('Something went wrong');\n }\n})\n\n// nuxt前端\nlet skip = 5\nlet skipNum = ref(0)\n//获取用户\nconst getHistory = async () => { \n const getOption = {\n method: \"GET\",\n baseURL\n }\n let { data, error } = await useFetch('/getusers/0', getOption)\n console.log(668, data.value.users)\n}\n\nif(process.client){\n getHistory()\n}\n\nconst more = async () => {\n console.log(563, \"skipNum\", skipNum.value)\n const getOption = {\n method: \"GET\",\n baseURL\n }\n skipNum.value += skip\n let { data, error } = await useFetch('/getusers/'+skipNum.value, getOption)\n if(error.value) {\n message.error(\"失败!\\n\"+error.value.data, { duration: 5e3 })\n return\n }\n if(data.value.users.length \u003C skip){\n console.log(\"没有更多数据了!\")\n moreFlag.value = false\n }\n}\n```\n\n这个遍历有点像接力赛,查完一段再查下一段。通过limit和skip来完成一段段的查询,效果还是很完美的。",[15,18,16,80],"mongodb","2023-09-12T03:58:03.000Z",{"_id":83,"user_id":8,"username":9,"title":84,"author":9,"category":11,"permlink":85,"body":86,"tags":87,"created":91,"__v":24},"68a4297e9c0586aeab99088a","HTTP 响应状态码 / 网络研习社#84","fxosvvq1","如果有朋友找你借钱,你会如何回复呢?你肯定会说:那得看情况啊。如果只是借个几百块应个急,那就二话不说转过去了。如果要借个几万,你估计得说你得想想。如果要借几百刀,你估计就得直接回绝了:没那么多钱啰!如果把这些回应放在服务器上,道理也差不多。\n\n\n\n在express做服务器时,有时响应的状态还蛮迷惑的,什么时候发200, 什么时候发404,什么时候发500呢?这些其实都是标准,找文件来参考下。\n\n[HTTP 响应状态码](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status) 这个文件基本都涉及了,好像还挺长的,找几个常用的。\n```js\nres.sendStatus(200); // 'OK'\nres.sendStatus(403); // 'Forbidden'\nres.sendStatus(404); // 'Not Found'\nres.sendStatus(422); // Unprocessable Entity 请求格式正确,但由于语义错误而无法遵循。\nres.sendStatus(500); // 'Internal Server Error'\n```\n\n日常中就是上面这五种情况,灵活组合就基本够用了。\n",[15,18,88,89,90],"express","status","http","2023-05-17T03:56:51.000Z",{"_id":93,"user_id":8,"username":9,"title":94,"author":9,"category":11,"permlink":95,"body":96,"tags":97,"created":99,"__v":24},"68a4297e9c0586aeab990878","MongoDB的权限和角色 / 网络研习社#83","3k8ap9r1","\n\n## 用户权限管理\n```js\nmongosh \nuser admin // 进入admin\n\n//创建一个超级用户 用户名admin 密码5615 权限root\ndb.createUser( { user:\"root\", pwd:\"pwd\", roles:[\"root\"] }) \neg: db.createUser({user:\"admin\",pwd:\"5615 \",roles:[\"root\"]}) //{ ok: 1 }\n\n//账号授权:用户名 密码。 回车,返回1,认证成功。\ndb.auth(\"admin\",\"5615 \") //{ ok: 1 } 对数据库授权,即登录数据库,否则将不能正常操作。\ndb.system.users.find().pretty() //查看\n\n//创建一个业务数据库管理员用户\ndb.createUser({\n user:\"jiang001\",\n pwd:\"123456\",\n roles:[\n {role:\"readWrite\",db:\"users\"}\n ]\n})\ndb.createUser({\n user:\"user001\",\n pwd:\"123456\",\n customData:{\n name:'jim',\n email:'jim@qq.com',\n age:18,\n },\n roles:[\n {role:\"readWrite\",db:\"db001\"},\n {role:\"readWrite\",db:\"db002\"},\n 'read'// 对其他数据库有只读权限,对db001、db002是读写权限\n ]\n})\n\n//创建admin管理员用户 \n//并不能直接操作数据库 not authorized on users to execute command\ndb.createUser({\n user:\"jiang002\",\n pwd:\"1234567\",\n roles:[\n {role:\"dbAdmin\",db:\"users\"}\n ]\n})\n\n//创建admin超级管理员用户\ndb.createUser( \n { user: \"admin\", \n customData:{description:\"superuser\"},\n pwd: \"admin\", \n roles: [ { role: \"userAdminAnyDatabase\", db: \"admin\" } ] \n } \n) \n\n说明:\nuser字段,为新用户的名字;\npwd字段,用户的密码;\ncusomData字段,为任意内容,例如可以为用户全名介绍,可省略;\nroles字段,指定用户的角色,可以用一个空数组给新用户设定空角色。在roles字段,可以指定内置角色和用户定义的角色。\n超级用户的role有两种,userAdmin或者userAdminAnyDatabase(比前一种多加了对所有数据库的访问,仅仅是访问而已)。\ndb是指定数据库的名字,admin是管理数据库。\n不能用admin数据库中的用户登录其他数据库。注:只能查看当前数据库中的用户,哪怕当前数据库admin数据库,也只能查看admin数据库中创建的用户。 \n```\n\n## 数据库用户角色\n数据库用户角色:read、readWrite;\n数据库管理角色:dbAdmin、dbOwner、userAdmin;\n集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManage;\n备份恢复角色:backup、restore;\n所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase\n超级用户角色:root\n内部角色:__system\n\nRead:允许用户读取指定数据库\nreadWrite:允许用户读写指定数据库\ndbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile\nuserAdmin:允许用户向system.users集合写入,可以在指定数据库里创建、删除和管理用户\nclusterAdmin:必须在admin数据库中定义,赋予用户所有分片和复制集相关函数的管理权限\nreadAnyDatabase:必须在admin数据库中定义,赋予用户所有数据库的读权限\nreadWriteAnyDatabase:必须在admin数据库中定义,赋予用户所有数据库的读写权限\nuserAdminAnyDatabase:必须在admin数据库中定义,赋予用户所有数据库的userAdmin权限\ndbAdminAnyDatabase:必须在admin数据库中定义,赋予用户所有数据库的dbAdmin权限\nroot:必须在admin数据库中定义,超级账号,超级权限\n\n和用户管理相关的操作基本都要在admin数据库下运行,要先use admin;\n如果在某个单一的数据库下,那只能对当前数据库的权限进行操作;\n\n//查看创建的用户\nshow users 或 db.system.users.find() 或 db.runCommand({usersInfo:\"userName\"})\n\n//修改密码\ndb.changeUserPassword(\"username\", \"xxx\")\n\n//删除数据库用户\ndb.dropUser('user001')\n\n\n## 数据库最大化安全和最小化用户权限\n1. 数据库运行以权限方式打开, `mongod --config /etc/mongod.conf --auth`\n2. mongod.conf中设置IP限制, 只允许本机访问,禁止其它远程终端的可能性:\n `bindIp: 127.0.0.1`\n3. 只创建一个`root`用户以管理整个数据库,以后台使用。\n4. 用`root`用户创建一个普通用户,用于数据库的读写等业务操作。\n `db.createUser({\n user:\"jiang001\",\n pwd:\"123456\",\n roles:[\n {role:\"readWrite\",db:\"users\"}\n ]\n})`\n\n## 系列文章\n[MongoDB的安装与运行](https://blog.larkneer.com/trend/@lemooljiang/kvufmdsr)\n[Mongoose操作数据库](https://blog.larkneer.com/trend/@lemooljiang/nojnacnk)\n[Bcrypt给用户系统加密](https://blog.larkneer.com/trend/@lemooljiang/5g79xdve)\n[MongoDB的数据结构和字段](https://blog.larkneer.com/trend/@lemooljiang/9kym3j37)\n\n\n",[15,16,18,80,98],"auth","2023-04-25T03:49:00.000Z",{"_id":101,"user_id":8,"username":9,"title":102,"author":9,"category":11,"permlink":103,"body":104,"tags":105,"created":107,"__v":24},"68a4297e9c0586aeab990876","MongoDB的数据结构和字段 / 网络研习社#82","9kym3j37","MongoDB非常灵活,不需要像MySQL一样创建数据库、表、设计表结构。它只需要向指定的数据库集合插入即可(数据库不存在也没关系,可以随时自动创建)。\n\nMongoDB可以有多个数据库,一个数据库可以有多个集合(表),一个集合可以有多个文档(表记录)。可以有相同的表结构,也可以不同。\n\n\n\nMongoDB的数据结构类似上图中的JSON样式\n\n## 常用字段类型\n[参考 ](https://mongoosejs.com/docs/schematypes.html#dates)\n```js\nString Number Date Buffer Boolean Mixed ObjectId Array Decimal128 Map UUID\n//其它:\n 其它属性:\n index: 布尔值 是否对这个属性创建索引\n unique: 布尔值 是否对这个属性创建唯一索引\n sparse: 布尔值 是否对这个属性创建稀疏索引\n required: 布尔值或函数 如果值为真,为此属性添加 required 验证器\n default: 任何值或函数 设置此路径默认值。如果是函数,函数返回值为默认值\n get: 函数 使用 Object.defineProperty() 定义自定义 getter\n set: 函数 使用 Object.defineProperty() 定义自定义 setter\n Mixed: 等同于:{} Object Schema.Types.Mixed, 可以存入任何对象\n\neg:\nconst schema = new Schema({\n name: String,\n binary: Buffer,\n living: Boolean,\n updated: { type: Date, default: Date.now },\n age: { type: Number, min: 18, max: 65 },\n mixed: Schema.Types.Mixed,\n _someId: Schema.Types.ObjectId, //mongoose.ObjectId\n decimal: Schema.Types.Decimal128,\n array: [],\n ofString: [String],\n ofNumber: [Number],\n ofDates: [Date],\n ofBuffer: [Buffer],\n ofBoolean: [Boolean],\n ofMixed: [Schema.Types.Mixed],\n ofObjectId: [Schema.Types.ObjectId],\n ofArrays: [[]],\n ofArrayOfNumbers: [[Number]],\n nested: {\n stuff: { type: String, lowercase: true, trim: true }\n },\n map: Map,\n mapOfString: {\n type: Map,\n of: String\n },\n uuid: Schema.Types.UUID, // Can also do `_id: 'UUID'`\n})\n\nconst Thing = mongoose.model('Thing', schema)\n\nconst m = new Thing\nm.name = 'Statue of Liberty'\nm.age = 125\n\nm.updated = new Date\n//m.markModified('updated')\n\nm.binary = Buffer.alloc(0)\nm.living = false\n\nm.mixed = { any: { thing: 'i want' } }\nm.markModified('mixed')\n\nm._someId = new mongoose.Types.ObjectId\nm.array.push(1)\nm.ofString.push('strings!')\nm.ofNumber.unshift(1, 2, 3, 4)\nm.ofDates.addToSet(new Date)\nm.ofBuffer.pop()\nm.ofMixed = [1, [], 'three', { four: 5 }]\nm.nested.stuff = 'good'\nm.map = new Map([['key', 'value']])\nm.uuid = require('crypto').randomUUID()\n\nm.save(callback)\n```\n\n## Schemas\n```js\nimport mongoose from 'mongoose'\n\nmongoose.connect('mongodb://localhost/test')\nconst { Schema } = mongoose\n\nconst kittySchema = new Schema({\n name: String\n})\n//在这之后你还想添加 keys 的话, 请使用 Schema#add 方法\n// 当然,也可以直接在原Schema上添加字段,只是以前的数据就没有这些字段,可以通过更新方法添加\n//Schema.prototype.add()\nkittySchema.add({ \n color: String, \n price: Number \n})\n\n// method 是给 document 用的\n// 要在mongoose.model之前添加方法\n// 加在 schema methods 属性的函数会编译到 Model 的 prototype, 也会暴露到每个 document 实例\nkittySchema.methods.speak = function () {\n let greeting = this.name\n ? \"Meow name is \" + this.name\n : \"I don't have a name\"\n console.log(greeting)\n}\n\n//model 是我们构造 document 的 Class,它同样会有变量和方法。\nconst Kitten = mongoose.model('Kitten', kittySchema)\n\n//新增数据\nconst huahua = new Kitten({\n name: \"huahua\"\n})\nhuahua.save()\n.then((res) => console.log('新增成功', res))\n.catch(error => console.log(44, error))\n\n//查找\nlet kitty = await Kitten.findOne()\nkitty.speak()\n```\n\n从上可以看出,MongoDB的数据和交互和现在流行的JSON格式几乎一致,而且它对格式的要求也比较宽,有些出入也不会报错。数据可以很灵活地变动和拓展,非常适合现有的网格需求。如果你现在想创建一个中小型的网络项目,那么,MongoDB几乎是最佳选择!\n\n\n",[15,16,18,80,106],"mysql","2023-04-21T03:07:03.000Z",{"_id":109,"user_id":8,"username":9,"title":110,"author":9,"category":11,"permlink":111,"body":112,"tags":113,"created":117,"__v":24},"68a4297e9c0586aeab990872","Bcrypt给用户系统加密 / 网络研习社#81","5g79xdve","\n\nMongoDB开发还蛮顺利,操作什么的基本也都摸熟了。现在开始设计数据库的格式和用户系统。在用户系统中的用户密码是需要加密保存的,用什么加密比较好呢?\n\n**Bcrypt是不错的方案!** 在网上查了不少方法, Bcrypt被推荐得比较多。从各方面来比较, Bcrypt也算是比较适宜的方案。\n\n## 基本使用:\n```js\nimport bcrypt from 'bcrypt'\n\nconst saltRounds = 10\nconst myPlaintextPassword = 's0/\\/\\P4$$w0rD'\nconst someOtherPlaintextPassword = 'not_bacon'\n\nconst salt = bcrypt.genSaltSync(saltRounds)\nconst hash = bcrypt.hashSync(myPlaintextPassword, salt) //获取加密后的哈希值\n\n// 密码校验\nbcrypt.compareSync(myPlaintextPassword, hash) // true \nbcrypt.compareSync(someOtherPlaintextPassword, hash) // false\n```\n\n## MongoDB Model\n```js\nimport mongoose from 'mongoose'\n\nmongoose.connect('mongodb://jixxx:pwd@localhost:27017/users?authSource=admin') \n.then(() => console.log('Connected!'))\n.catch(error => console.log(44, error))\n\nconst { Schema } = mongoose\n\n// 定义一个用户模型,username是唯一的索引,表示不能被重复\nconst UserSchema = new Schema({\n username: { type: String, unique: true },\n password: { \n type: String, \n set(val) {\n let salt = bcrypt.genSaltSync(saltRounds)\n let hash = bcrypt.hashSync(val, salt)\n return hash\n }\n },\n})\n\nconst User = mongoose.model('User', UserSchema)\n```\n\n在定义模型时,使用`set(val) ` 对用户密码加密保存,感觉还是蛮顺利的。在用户登录时则使用`bcrypt.compareSync(pwd, hash)`来进行校验,为`true` 则是真实用户,反之则不通过。\n",[15,16,18,80,114,115,116],"mongoose","bcrypto","user","2023-04-16T04:03:24.000Z",{"_id":119,"user_id":8,"username":9,"title":120,"author":9,"category":11,"permlink":121,"body":122,"tags":123,"created":124,"__v":24},"68a4297e9c0586aeab99086c","MongoDB的安装与运行 / 网络研习社#79","kvufmdsr","\n\nhttps://www.mongodb.com/\n\n自研究区块链以来,就没再碰数据库了!博客就用Hive,代码就用github, 一般都够用,就不去折腾MySQL、MongoDB这些了。\n\n但区块链的使用门槛比较高,要是服务国内的用户,那用区块链当数据库就不太合适,还是得用回MySQL、MongoDB。在选型上,MySQL、MongoDB都各有优势,但现阶段,MongoDB更适合些,那就它啰。\n\n## Ubuntu安装\nUbuntu 20.04.5 LTS \nmongodb-org-server_6.0.5 \nmongosh_1.8.0 \n[安装](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/)\n```js\n//ubuntu\napt update\n//服务器 直接下载安装包后安装\nwget https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/6.0/multiverse/binary-amd64/mongodb-org-server_6.0.5_amd64.deb\ndpkg -i ./mongodb-org-server_6.0.5_amd64.deb\n//dpkg -i --force-overwrite ./mongodb-org-server_6.0.5_amd64.deb 强制重装\n\n\n//客户端 mongosh\nwget https://downloads.mongodb.com/compass/mongodb-mongosh_1.8.0_amd64.deb \ndpkg -i ./mongodb-mongosh_1.8.0_amd64.deb\n```\n\n## Windows安装\n```js\n1、安装好后添加环境变量\n2、在默认的C盘下创建数据目录 c:/data/db\n3、启动\ncd C:\\Program Files\\MongoDB\\Server\\4.0\\bin\nmongod\n//在默认的C盘下创建数据目录 c:/data/db\n\n//使用另外的存储路径\n mongod.exe --dbpath D:\\MongoDB\\Server\\4.0\\data\n\n4、mongo 连接即可 exit退出\n5、基本命令\n show dbs //查看所有数据库\n db //当前数据库\n use dbname //切换指定数据库(没有则新建)\n\n //直接可以放集合students(当前没有students则新建)中插入数据\n db.students.insertOne({\"name\":\"lemool\"})\n\n show collections //查看集合\n db.students.find() //查看集合中的所有数据\n``` \n\n## 运行服务端\n```js\n/usr/bin/mongod --config /etc/mongod.conf //无权限\n/usr/bin/mongod --config /etc/mongod.conf --auth //打开权限\n\n//查看运行\nps -ef | grep mongo\n\n//配置 /etc/mongod.conf\nbindIp: 127.0.0.1 //只允许本机 \nbindIp: 0.0.0.0 //允许所有IP\n```\n\n## 后台运行\n```js\n//mongod.service port: 27017\n# cat /lib/systemd/system/mongod.service\nvim /lib/systemd/system/mongod.service\n//震要修改的地方\nExecStart=/usr/bin/mongod --config /etc/mongod.conf --auth //以权限的方式运行\nUser=root //User=mongodb\nGroup=root\n\nsystemctl daemon-reload\n\nsystemctl start mongod\nsystemctl stop mongod\nsystemctl restart mongod\n\nsystemctl status mongod //查看状态\nps -ef | grep mongo\n```\n\n## 基本操作\n```js \nmongosh //服务端无权限时\nmongosh \"mongodb://localhost:27017\" --username admin --password 589485 --authenticationDatabase admin\n\nshow dbs //显示所有数据库, 默认是test\nshow collections //显示所有集合\n\ndb //显示当前数据库名\nuse \u003Cdatabase name> //更改数据库\nuser admin // 进入admin\n//创建一个新账号 用户名admin 密码589485 权限root\ndb.createUser({user:\"admin\",pwd:\"589485\",roles:[\"root\"]}) //{ ok: 1 }\n//db.createUser({user:\"admin\",pwd:\"589485\",roles: [ {role:\"dbOwner\", db:\"mydb\"} ]}) \n//账号授权:用户名 密码。 回车,返回1,认证成功。\ndb.auth(\"admin\",\"589485\") //{ ok: 1 }\ndb.system.users.find().pretty() //查看\n\n//创建集合\ndb.createCollection(name, options), eg: db.createCollection(\"runoob\")\n//删除集合\ndb.collection.drop(), eg: db.runoob.drop()\n//插入记录\ndb.COLLECTION_NAME.insertOne(document)\neg: db.collection.insertOne({\"a\": 3})\n db.collection.insertMany([{\"b\": 3}, {'c': 4}])\n >db.test1.insertOne({title: 'MongoDB 教程', \n description: 'MongoDB 是一个 Nosql 数据库',\n tags: ['mongodb', 'database', 'NoSQL'],\n likes: 100\n })\n\ndb.test1.find().pretty()\n```\n\n## 几个重点\n1. 运行的用户配置\nMongoDB的默认用户和组是`mongodb`, 虽然安装时也创建了,但就是运行不起来,就全部改成`root`吧。\n2. IP限制\nmongod.conf中设置IP限制:\nbindIp: 127.0.0.1 //只允许本机 \nbindIp: 0.0.0.0 //允许所有IP\n3. 用户权限\n需要进`user admin`中设置用户名和密码,MongoDB运行时也需要以权限的形式运行,这样安全系数最高。\n",[15,18,80,106],"2023-04-06T06:29:09.000Z",{"_id":126,"user_id":8,"username":9,"title":127,"author":9,"category":11,"permlink":128,"body":129,"tags":130,"created":135,"__v":24},"68a4297e9c0586aeab99084a","OpenAI关键词技巧 / 网络研习社#78","41psg6ia","\n\nhttps://ai.ilark.io\n\n和AI的交流就好比是两个人之间的交流一样,言语要通。和AI的沟通也要说些它能听懂的\"语言\",这就是关键词(prompt)技巧了。\n\nOpenAI在文档中展示了不少的使用场景和提词技巧,自己可以总结总结,如下:\n\n1. 翻译\n在段落前加上`Translate this into English: \\n\\n`,后面再加上你要翻译的段落。你要翻译成哪国语言,就把‘English’换成其它语种,比如'Japanese'、'Chinese'这些。不过因为最近chatgpt爆火,服务器繁忙,翻译过程太费时间,用户体验不是很理想。\n\n2. 编程\n在段首加上`用JavaScript实现以下需求:\\n\\n`,后面接上需求即可。\n\n3. 解释代码\n在代码片段后加上`\\n\\n\\\"\\\"\\\"\\n 用中文解释以上代码功能:`即可帮你解读代码。\n\n4. 模拟面试\n`为面试\"job\"创建一张10个问题的清单`, 把job替换成你想面试的职位即可。\n\n5. 续写文本\n在需要续写的文本后加上`\\n\\n 请按照以上风格续写`即可。\n\n6. 摘要功能\n在需要生成摘要的文本后加上`\\n\\nTl;dr`即可。\n\n先来这些最常用最基本的,后面的有时间再补上。\n\n\n",[15,16,18,131,132,133,134],"st","openai","ai","ilark","2023-02-15T04:18:51.000Z",{"_id":137,"user_id":8,"username":9,"title":138,"author":9,"category":11,"permlink":139,"body":140,"tags":141,"created":142,"__v":24},"68a4297e9c0586aeab990848","OpenAI开发指南 / 网络研习社#77","qamjsytu","\n\n\nhttps://ai.ilark.io\n\n这是OpenAI js版开发指南,打开即用。\n\n```js\nlet express = require('express')\nlet cors = require('cors')\nlet { Configuration, OpenAIApi } = require('openai')\n\n\nconst configuration = new Configuration({\n apiKey: \"your openai api key\",\n})\n\nconst openai = new OpenAIApi(configuration)\n\nconst app = express()\napp.use(cors())\napp.use(express.json())\n\napp.get('/', async (req, res) => {\n res.status(200).send({\n message: 'Hello ilark AI!'\n })\n})\n\n//AI的文本功能,模型是text-davinci-003\n//temperature 取0-1之间,值越高,相关度越低\napp.post('/word', async (req, res) => {\n try {\n const prompt = req.body.prompt\n const temperature = req.body.temperature\n\n const response = await openai.createCompletion({\n model: \"text-davinci-003\",\n prompt: `${prompt}`,\n temperature: temperature, // Higher values means the model will take more risks.\n max_tokens: 1500, // The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 4096).\n top_p: 1, // alternative to sampling with temperature, called nucleus sampling\n frequency_penalty: 0, // Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n presence_penalty: 0, // Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n });\n\n res.status(200).send({\n bot: response.data.choices[0].text\n });\n\n } catch (error) {\n console.error(error)\n res.status(500).send(error || 'Something went wrong');\n }\n})\n\n//AI的代码功能,模型是code-davinci-002\napp.post('/code', async (req, res) => {\n try {\n const prompt = req.body.prompt;\n const response = await openai.createCompletion({\n model: \"code-davinci-002\",\n prompt: `${prompt}`,\n temperature: 0,\n max_tokens: 1200,\n top_p: 1.0,\n frequency_penalty: 0.0,\n presence_penalty: 0.0,\n stop: [\"\\\"\\\"\\\"\"],\n });\n res.status(200).send({\n bot: response.data.choices[0].text\n });\n\n } catch (error) {\n console.error(error)\n res.status(500).send(error || 'Something went wrong');\n }\n})\n\n//AI的图片功能,模型是dall-e 2\napp.post('/image', async (req, res) => {\n try {\n const prompt = req.body.prompt;\n let response = await openai.createImage({\n prompt: `${prompt}`,\n n: 1,\n size: \"512x512\",\n });\n res.status(200).send({\n image_url: response.data.data[0].url\n });\n\n } catch (error) {\n console.error(error)\n res.status(500).send(error || 'Something went wrong');\n }\n})\n\napp.listen(6200, () => console.log('AI server started on http://localhost:6200'))\n```\n\n简单地`npm install express cors openai`就可以访问了, [AI-JOE github地址](https://github.com/ilarkdao/AI-JOE)。可以使用前端,或是postman这样的工具进行测试啰,还是蛮简单的。",[15,16,18,131,132,133,134],"2023-02-14T04:30:36.000Z",{"_id":144,"user_id":8,"username":9,"title":145,"author":9,"category":11,"permlink":146,"body":147,"tags":148,"created":151,"__v":24},"68a4297e9c0586aeab99078e","Go语言学习笔记","rzwpd5fe","## 目录\n\n- [下载与资源](#下载与资源)\n- [安装和配置](#安装和配置)\n- [快速启动goland的方法](#快速启动goland的方法)\n- [liteide](#liteide)\n- [基础](#基础)\n- [工程管理](#工程管理)\n- [变量](#变量)\n- [常量const](#常量const)\n- [类型转换](#类型转换)\n- [iota枚举](#iota枚举)\n- [输出格式](#输出格式)\n- [接收输入](#接收输入)\n- [枚举iota](#枚举iota)\n- [字符串处理](#字符串处理)\n- [if条件](#if条件)\n- [switch分支](#switch分支)\n- [for循环](#for循环)\n- [函数的不定参数](#函数的不定参数)\n- [函数类型](#函数类型)\n- [函数返回值](#函数返回值)\n- [匿名函数](#匿名函数)\n- [闭包](#闭包)\n- [递归函数](#递归函数)\n- [数组](#数组)\n- [遍历数组](#遍历数组)\n- [切片](#切片)\n- [map](#map)\n- [结构体](#结构体)\n- [指针变量](#指针变量)\n- [数组指针](#数组指针)\n- [切片指针](#切片指针)\n- [匿名字段](#匿名字段)\n- [方法](#方法)\n- [接口](#接口)\n- [接口继承](#接口继承)\n- [空接口](#空接口)\n- [多态](#多态)\n- [错误处理](#错误处理)\n- [defer](#defer)\n- [文件](#文件)\n- [并行和并发](#并行和并发)\n- [goroutine](#goroutine)\n- [channel](#channel)\n\n\n\n## 下载与资源\n[官网 |](https://golang.org/dl) \n[中文站 |](https://studygolang.com/)\n[liteide](http://liteide.org/cn/)\n\n\n## 安装和配置\n```\nsudo tar -xzf go1.12.7.linux-amd64.tar.gz -C /usr/local\n\nsudo vim /etc/profile\n\nexport GOPATH=/home/jiangzhibin/golang\nexport GOROOT=/usr/local/go\nexport PATH=$PATH:$GOROOT/bin:$GOPATH/bin\n\nsource /etc/profile 使得其配置文件有效\n\n//gopath\necho $GOPATH \n\ngppm下载包\n```\n\n\n## 快速启动goland的方法\n```\n1. 创建一个根链接,goland\nsudo ln -s /home/jiangzhibin/soft/goland/bin/goland.sh /usr/bin/goland\n\n2. 开机自动启动\n 终端: gnome-session-properties \n 添加命令: goland\n```\n\n## liteide\n```\n/home/jiangzhibin/soft/liteidex37.4.linux64-qt5.5.1/liteide/bin/liteide\nsudo ln -s /home/jiangzhibin/soft/liteidex37.4.linux64-qt5.5.1/liteide/bin/liteide /usr/bin/liteide\n```\n\n## 基础\n```go\n//导入主函数的包\npackage main\n\nimport \"fmt\"\n\n// 单行注释\n\n/*\n块注释\n */\n\nfunc main() {\n\t//程序有且只有一个主入口\n\tfmt.Println(\"hello world go!\")\n\tfmt.Println(\"tiantian!\")\n\n}\n```\n\n## 工程管理\nsrc工作目录 main包 主函数\n函数是全局的,还有全局变量,都可全局使用。 \n\n不同级目录,包名用目录名,函数首字母大写。\n主函数如果调用不同级目录中的函数要导入这个包:\n\n如下图所示:\n\n\n```go\nimport \"userinfo\"\nfunc main(){\n\tuserinfo.Login()\n}\n\npkg目录,系统生成的归档文件\n\nbin目录,源码生成的可执行文件\n```\n\n## 变量 \n```go\n//内存地址的别名\nvar 变量名 数据类型 声明\nvar 变量名 数据类型 值\n变量名 := 值 //自动推导类型\n\nvar a int = 10\nvar r float64 = 2.5\n\n//自动推导类型, 注意只能在函数内使用\n//会自动为变量选择类型\nr := 3.256\na,b,c,d := 10,20,30,60\n\n//匿名变量 _ ,占位但不接受值\na,_ := 1,20\n\n不同的数据类型不能计算\n\n// * 指针运算符,取出地址中的值\nvar a int = 10\np := &a\n*p //p是指针变量\n```\n\n## 常量const\n常量的值不允许修改,一般大写字母表示,存储在数据区,变量存储在栈区。\n常量不能用&来引用\n```\nconst A int = 20\n\n//字面常量,程序中的硬编码常量 \n直接数字 或 字符串\n```\n\n## 类型转换\n```\nvar a int = 10\nvar b float64 = 2.44\nc := float64(a) * b\n```\n\n## iota枚举\n常量声明可以使用常量生成器初始化。\n```\nconst (\n a = iota\n b = iota\n c = iota\n)\n```\n\n## 输出格式\n```\nfmt.Println(\"hello world\") //输出并换行\n\na := 10\nb := \"zhansan\"\nfmt.Printf(\"数据是:%d ,名字是:%s \", a, b) //格式化输出\n%d 整形 %f 浮点型 %t 布尔型 %s 字符串 %c 字符类型 %p 内存地址 \\n 换行\n```\n\n## 接收输入\n```go\nvar a int\nfmt.Scan(&a)\n\nfmt.Scanf(\"%d%s\", &a,&b) //格式化输入\n```\n\n## 枚举iota\n声明一组相似的常量,值会递增\n```go\nconst (\n\ta = iota //0\n\tb = iota //1\n\tc = iota //2\n)\n```\n\n## 字符串处理\n```go\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tstr := \"hello world\"\n\tres := strings.Contains(str, \"he\")\n\tfmt.Println(res)\n}\n```\n\n## if条件 \n```go\nvar a = 10\nvar b = 20\nif a \u003C b {\n fmt.Println(\"true!\")\n}\n\nif a \u003C b {\n\n}else if a = b{\n\n}else {\n\n}\n```\n\n## switch分支\n```go\nvar a int\nfmt.Scan(&a)\nswitch a {\ncase 1:\n fmt.Println(\"monday\")\ncase 2:\n fmt.Println(\"tuesday\")\n}\n```\n\n## for循环\n```go\nvar a int\nfor a = 1; a \u003C 20; a++ {\n fmt.Println(\"tongtong\")\n}\n//break 跳出本层循环\n//continue 结束本次循环,继续下次循环\ngoto\n```\n\n## 函数的不定参数\n可以接受多个参数\n```go\nfunc test(args ...int){\n\tfmt.Printf(args)\n}\ntest(1,2,3)\n\nlen()计算不定参的长度,len(args)\n\n```\n\n## 函数类型\n其实是一个指针,在代码区存储\n\n函数是全局的,可以被项目中的所有文件调用,所以函数名是唯一的。\n```go\ntype funcdemo func()\ntype functest func(int int) /带参数 \ntype functt func(int int)int /带返回值\n```\n\n## 函数返回值\n```go\n//单个返回值\nfunc add(a int, b int) int {\n\treturn a + b\n}\n\n//多个返回值\nfunc add(a int, b int) (int, int) {\n\treturn a + b, a - b\n}\n```\n\n## 匿名函数\n```go\nfunc() {\n fmt.Println(\"hello\")\n}()\n\n```\n\n## 闭包\n函数的返回值是一个函数,实现函数在栈区的本地化。\n```go\nfunc test2() func() int{\n\tvar a int\n\treturn func() int{\n\t\ta++\n\t\treturn a\n\t}\n}\n```\n\n## 递归函数\n本身调用自己的函数\n```go\nfunc test3(){\n\tif i \u003C 3{\n\t\ttest3()\n\t}\n}\n```\n\n## 数组\n```\nvar 数组名 [元素个数]数据类型\nvar arr1 [10]int\n```\n\n## 遍历数组\nfor range 可以遍历集合、数组、切片、map中的数据, for的另一种写法\nfor i, data = range array1\n```go\nfor _,data = range arr{\n\tsum += data\n}\n```\n\n## 切片\n一般不直接使用数组,使用切片slice\n不写元素个数是切片,必须写元素个数的是数组。\n切片是视图类型view\n```go\narr := [...]int{0, 1, 3, 10, 52, 30, 3}\ns := arr[2:6]\n\nvar s []int = []int{1,2,3,4}\ns = append(s,5,6) //追加数据\nslice := s[1:] //切片取值,从下标是1开始到结尾\n\n\ns := make([]int, 5)\ns[0] = 1\n```\n\n## map\n字典结构类型,键值对存储:key->value\n```go\nvar m map[int]string\nm := make(map[string]int)\n\nm := make(map[int]string)\nm[12] = \"hello\"\nm[1] = \"wowu\"\n\nm := map[int]string{\n 1:\"zhangsan\",\n 2:\"lisi\",\n 3:\"wangwu\",\n}\n\nm := map[string]string {\n\t\"name\": \"zhangsan\",\n\t\"site\": \"baidu.com\"\n}\n\n// for... range遍历\nfor k, v := range m {\n\tfmt.PrintLn(k, v)\n}\n\n//删除元素\ndelete(m, \"name\")\n```\n\n## 结构体\ngo仅支持封装,没有class, 不支持继承和多态\n类似于类的概念,于函数外定义,全局作用域\n```go\ntype treeNode struct {\n\tvalue int\n\tleft, right *treeNode\n}\n\ntype Student struct {\n\tid int\n\tname string\n\tsex string\n\tage int\n\taddr string\n}\n\nvar s Student\ns.id = 1\ns.name = \"zhangsan\"\ns.age = 20\n\n//或者这样定义\nvar s Student = Student{id: 1, name: \"lisi\", sex: \"male\", age: 20}\ns := Student{id:1, name:\"zhangsan\", age:18}\n\n//结构体数组\nvar arr [5]Student\narr[0].id = 1\narr[0].name = \"hello\"\n\narr[1] = Student{id: 1, name: \"lisi\", sex: \"male\", age: 20}\n\n//\ntype treeNode struct {\n\tvalue int\n\tleft, right *treeNode\n}\nfunc main(){\n\tvar root treeNode\n\troot = treeNode{value: 3}\n\troot.left = &treeNode{}\n\troot.right = {5, nil, nil}\n\troot.right.left = new(treeNode)\n}\n```\n\n## 指针变量\n基本类型前加 * ,可以通过指针访问变量和赋值\n指针不能运算, go只有值传递\n值传递就是直接拷贝数值, 引用传递是地址拷贝\n```go\nvar a = 10\nvar p *int //空指针\n//p = oxf589 野指针, 指针定义后不能直接赋值,直接赋值为野指针\np = &a\n*p / == a\n\n//指针空间 在堆区创建空间\nvar p *int\np = new(int)\n\n//两个变量互换值\n//以下的方法不会改变\nfunc swap(a, b int){\n\ta, b = b, a //结果不会改变\n}\n\n//需要使用指针的办法\nfunc swap(a, b *int){\n\t*a, *b = *b, *a \n}\n//或者将数据返回\nfunc swap(a, b int) (int, int){\n\ta, b = b, a\n\treturn a, b\n}\n```\n\n## 数组指针\n可以直接使用指针操作数组元素\n```go\nvar arr [5]int = [5]int{1,2,3,4,5}\nvar p *[5]int\np = &arr\n\nfmt.Printf(\"%T\", p) // *[5]int\n\np[0] //arr[0] 1\nlen(p) //也可以获取数组的长度\n```\n\n## 切片指针\n和数组指针类似,但有区别\n```go\nvar slice []int = []int{4, 12, 12, 6}\nvar p *[]int\np = &slice\n\n(*p)[0] //slice[0] 4\n```\n\n## 匿名字段\n实现继承操作,结构体嵌套结构体\n```go\ntype person struct {\n\tname string\n\tsex string\n\tage int\n}\ntype Student struct {\n\tperson\n\tid int\n\tscore int\n}\nfunc main() {\n\tvar stu1 Student\n\tstu1.id = 1\n\tstu1.name = \"zhangsan\"\n\tfmt.Println(stu1)\n}\n\n//或者这样初始化\nvar stu1 Student = Student{person{\"zhangsan\", \"male\", 18}, 1, 60}\n\n//如果有同名字段,则用子类(就近原则),或者使用父类名初始化。\nstu1.person.name = \"zhangsan\"\n```\n\n## 方法 \n为对象绑定方法,和函数类似,但也有不同。\n```go\ntype Int int //为基本类型取别名,因为基本类型不能绑定方法 \n//func (方法授受者) 方法名(参数列表) 返回值类型\nfunc (a Int) add(b Int) Int {\n\treturn a + b\n}\nfunc main() {\n\tvar a Int = 10 //根据数据类型,绑定方法 \n\tvalue := a.add(15)\n\tfmt.Println(value)\n}\n\n//结构体绑定方法 \ntype Student struct {\n\tname string\n\tage int\n}\nfunc (s Student) PrintInfo() { //结构体绑定方法 PrintInfo\n\tfmt.Println(s.name)\n}\nfunc main() {\n\tvar s Student = Student{\"zhangsan\", 18}\n\ts.PrintInfo()\n}\n```\n\n## 接口\n```go\n//接口方法只定义不实现\ntype Human interface {\n\tsayHello()\n}\n\ntype Person struct {\n\tname string\n\tage int\n}\n\nfunc (alice *Person) sayHello() {\n\tfmt.Println(\"hello world\", alice.name)\n}\n\nfunc main() {\n\tvar bob Person = Person{\"alice\", 18}\n\tvar h Human = &bob\n\th.sayHello()\n}\n```\n\n## 接口继承\n```go\ntype Human interface { //子集\n\tsayHello()\n}\n\ntype Person interface { //超集\n\tHuman //一组子集的集合\n\tdance()\n}\n```\n\n## 空接口\n空接口可以接受任意类型数据\n```go\nvar i interface {}\ni = 10\ni = \"hello world\"\n```\n\n## 多态\n多态是将接口类型作为函数参数,多态实现了接口的统一处理\n```go\ntype Human interface {\n\tsayHello()\n}\n\ntype Person struct {\n\tname string\n\tage int\n}\n\nfunc (alice *Person) sayHello() {\n\tfmt.Println(\"hello world\", alice.name)\n}\n\nfunc sayHello(h Human) {\n\th.sayHello()\n}\n\nfunc main() {\n\tvar bob Person = Person{\"alice\", 18}\n\tvar h Human = &bob\n\t// h.sayHello()\n\tsayHello(h)\n}\n```\n\n## 错误处理\nerrors panic recover\n```go\n//可以从panic中获取控制权\ndefer func(){\n\trecover()\n}() \n```\n\n## defer\n延迟调用\ndefer func\n\n## 文件\n```go\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nfp, _ := os.Create(\"./test.txt\")\nfp.WriteString(\"hello golang!\")\ndefer fp.Close()\n```\n\n## 并行和并发\n并行:有两队同时使用两台咖啡机。 在同一时刻,有多个任务在多个处理器上同时进行。\n并发:有两队同时轮流使用一台咖啡机。\n\n## goroutine\n协程goroutime, 轻量级线程,\ngo关键字,开一个子协程,\n主协程退出了,其它子协程也会退出,\n非抢占式多任务处理,由协程主动交出任务权\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc newTask() {\n\tfor {\n\t\tfmt.Println(\"new task\")\n\t\ttime.Sleep(time.Second)\n\t}\n\n}\n\nfunc main() {\n\tgo newTask()\n\tfor {\n\t\tfmt.Println(\"hello main\")\n\t\ttime.Sleep(time.Second)\n\t}\n\n}\n\n//\nfunc main() {\n\tfor i := 0; i \u003C 1000; i++ {\n\t\tgo func(i int) { //并发\n\t\t\tfor {\n\t\t\t\tfmt.Printf(\"hello2\\n\", i)\n\t\t\t}\n\t\t}(i)\n\t}\n\ttime.Sleep(time.Millisecond)\n}\n\n//让别的协程先执行\nruntime.Gosched()\n```\n\n## channel\n```go\nmake(chan type) //无缓存的通道\nmake(chan type, capacity) //有缓存的通道\nclose(channel) //关闭通道\neg:\nchannel \u003C- value //发送value到channnel\n\u003C- channel //接受并弃用\nx := \u003C- channel //从channnel接受并赋值给x\nx, ok := \u003C- channel //功能同上,同时检查通道是否关闭或为空\n\nfunc chanDemo() {\n\tc := make(chan int)\n\tgo func() {\n\t\tfor {\n\t\t\tn := \u003C-c\n\t\t\tfmt.Println(n)\n\t\t}\n\t}()\n\n\tc \u003C- 1\n\tc \u003C- 2\n\ttime.Sleep(time.Millisecond)\n}\n\n//bufferedChannel 构建缓冲区\nc := make(chan int, 3)\n```",[15,16,18,149,150],"golang","dev","2022-04-23T03:16:57.000Z",{"_id":153,"user_id":8,"username":9,"title":154,"author":9,"category":11,"permlink":155,"body":156,"tags":157,"created":159,"__v":24},"68a4297d9c0586aeab99077c","前端跨域失败的非常见原因 / 网络研习社#76","kieodri8","昨天在开发一个项目时被跨域失败的问题折腾了一天!几乎所有的解决方案都轮个来了一遍,也还是没有解决!\n\n\n\n如上所示(仅举例),跨域失败是个很常见的问题,一个网站向另一个网站(或是不同的端口)请求数据时都会有跨域的问题,也就是说,必须得到对方的许可才可访问。举个现实中的例子:你去朋友家拜访是不是要提前打个招呼,人家允许了你才去,对吧。网站的请求也是类拟。做为玩了前端数年的老玩家,却被这个小问题坑了快一天,还查不出问题,真是活久见了!\n\n最后只能是一个个排查,从Vue, axios, Nginx, Nodejs, Express...... 一圈下来,问题竞然还是存在!试了半天,所有能考虑到的方案都试了个遍!实在是想不出还有什么可以试试的方法了,都整得要放弃了。\n\n但是最后,眼尖时突然发现:**上传到服务器的文件少了一个json的配置!** 我去,就这么个原因,前端竞然报跨域失败!真想把浏览器的开发人员暴打一顿!把文件上传好后,一切正常。喝水也香了。",[15,18,150,158],"cros","2022-01-26T03:49:18.000Z",{"_id":161,"user_id":8,"username":9,"title":162,"author":9,"category":11,"permlink":163,"body":164,"tags":165,"created":168,"__v":24},"68a4297d9c0586aeab99062c","多国语言组件vue-i18n / 网络研习社#75","vue-i18n-75","\n\nvue-i18n是个非常实用的多国语言组件!当你的网站要配置多种语言的界面时,用它是首选。用起来倒也容易,翻下手册,看几个实用的案例,就基本上八九不离十啰。可以说,上手的成本挺小的。\n\n[参考手册](https://kazupon.github.io/vue-i18n/zh/)\n\n```js\nnpm install vue-i18n --save\n\n//main.js\nimport VueI18n from 'vue-i18n'\n\nVue.use(VueI18n)\nconst DEFAULT_LANG = navigator.language\n// console.log(888, 'DEFAULT_LANG', DEFAULT_LANG) //888 \"DEFAULT_LANG\" \"zh-CN\" \"DEFAULT_LANG\" \"en\"\nconst LOCALE_KEY = 'localeLanguage'\nconst i18n = new VueI18n({\n locale:'zh-CN',\n messages: {\n 'zh-CN': require('./assets/lang/zh_CN'),\n 'en': require('./assets/lang/EN')\n },\n fallbackLocale: 'zh-CN'\n})\nconst setup = lang => {\n if (lang === undefined){\n lang = window.localStorage.getItem(LOCALE_KEY)\n if (i18n.messages[lang] === undefined){\n lang = DEFAULT_LANG\n }\n }\n window.localStorage.setItem(LOCALE_KEY, lang)\n Object.keys(i18n.messages).forEach(lang => {\n document.body.classList.remove(`lang-${lang}`)\n })\n document.body.classList.add(`lang-${lang}`)\n document.body.setAttribute('lang', lang)\n\n Vue.config.lang = lang\n i18n.locale = lang\n}\nsetup()\n\n//切换\nsetzhlang(){\n let LOCALE_KEY = 'localeLanguage'\n let lang = 'zh-CN'\n localStorage.setItem(LOCALE_KEY, lang)\n this.$i18n.locale = 'zh-CN'\n},\nsetenlang(){\n let LOCALE_KEY = 'localeLanguage'\n let lang = 'en'\n localStorage.setItem(LOCALE_KEY, lang)\n this.$i18n.locale = 'en'\n}\n\n//html\n{{ $t('message.orderNumber') }}\n {{ $t('message.paymentMethod') }}\n```\n\n在`./assets/lang/zh_CN`这个文件夹下配置中文或是其它的语言包就要可以,然后配上前端的语言切换就可以了。还是相当易用的。",[15,18,33,166,167],"vue-i18n","language","2020-12-22T06:16:00.000Z",{"_id":170,"user_id":8,"username":9,"title":171,"author":9,"category":11,"permlink":172,"body":173,"tags":174,"created":178,"__v":24},"68a4297d9c0586aeab9905fe","在前端中查询币价 / 网络研习社#74","74","\n\nhttps://pro-api.coinmarketcap.com/v1\n\n在前端中有一个显示币价的功能,那么就要实时查询到这个币价。因为平时也是用coinmarketcap查看行情的,所以,第一时间也是想到用它来实现这个功能。\n\nhttps://coinmarketcap.com/api/documentation/v1/#section/Standards-and-Conventions\n\n先找来它的开发文档,注册帐号,获得API KEY,然后是测试查询!\n```\nthis.axios.request({\n method:\"get\",\n url:'https://pro-api.coinmarketcap.com/v1/global-metrics/quotes/latest',\n headers: {\n 'X-CMC_PRO_API_KEY': 'd8002bdd-7a6d-4a2b-xxxxx,\n \"Accept\": \"application/json\",\n \"Access-Control-Allow-Origin\":\"*\",\n \"Content-Type\":\"application/json\",\n \"Accept-Encoding\": \"gzip\",\n },\n params: {\n 'CMC_PRO_API_KEY': 'd8002bdd-7a6d-4a2b-xxxxx',\n }\n}).then( (arg) => {\n console.log(111,arg.data)\n}).catch( (error) => {\n console.log(333,error)\n})\n```\n\n按照文档写了函数,但测试起来总是有个跨域的问题存在,如上图所示!\n\n以前做前端的时候也碰到过跨域的问题,调下参数一般都能解决!但这次的问题很怪:**直接用postman都可以访问到数据,但用前端就是一直报错!** 查了无数文档,看了数个视频,自信对跨域的问题是有了一定的了解,但是测试起来问题总是存在!\n\n无奈下,只能求助于其它的API工具了,希望它能给我受伤的小心灵一点安慰吧!\n\n\n\nhttps://api.coingecko.com/api\n\n```\nasync testApi(){\n let res = await this.axios.request({\n method:\"get\",\n url:'https://api.coingecko.com/api/v3/coins/bitcoin',\n headers: {\n \"accept\": \"application/json\",\n }\n })\n console.log(111,res)\n}\n```\n\n这次换了coingecko的API,想不到一次就成功了,而且不用注册,也不用什么API KEY之类的,简短两行代码就搞定了,真是幸福感满满啊!\n\n有时候还真不能钻牛角尖,特别是死牛角!换个思路可能会是更好的办法!",[15,18,175,176,177,158],"coingecko","coinmarketcap","api","2020-11-01T05:08:00.000Z",{"_id":180,"user_id":8,"username":9,"title":181,"author":9,"category":11,"permlink":182,"body":183,"tags":184,"created":186,"__v":24},"68a4297d9c0586aeab9905fc","Vue中定时刷新数据 / 网络研习社#73","vue-73","\n\n定时刷新数据好像是一个蛮普遍的需求,比如每秒刷新交易价格、每秒刷新挖矿产出...... 每少刷新肯定是要用到定时器的功能,刷新数据是要用到查询接口,这两者结合就可以实现定时刷新数据的目的。\n\n## 实现定时功能\n```\n //得到当前时间\nlet getTimestamp = function(){\n that.timestamp = Date.parse(new Date()) * 1e-3\n}\n//设置定时器以更新当前时间\nlet timer = setInterval(getTimestamp, 3000)\n// 通过$once来监听定时器,在beforeDestroy钩子时被清除。\nthis.$once('hook:beforeDestroy', () => {\n clearInterval(timer)\n})\n```\n\n设置每3秒更新一次的定时器,切换页面则关闭定时器。\n\n## 查询数据\n```\ncomputed: {\n orderCreatedInTime(){\n //订单剩余时间\n let tsteemOtcLists = this.orderCreated()\n tsteemOtcLists.forEach(item => {\n item.leftTime = (item.submitTime + this.orderValidity) - this.timestamp\n })\n return tsteemOtcLists\n }\n}\n```\n\n实时更新数据一般使用computed的功能,timestamp每更新一次,它就会重新计算一次,以实现实时更新的目的。它可以返回几乎任何的数据,唯一有点不足的是不能像函数一样传入参数。\n\n**定时器和computed结合就可以实现几乎所有的实时刷新的功能啰!**",[15,18,33,185],"time","2020-10-31T04:19:21.000Z",{"_id":188,"user_id":8,"username":9,"title":189,"author":9,"category":11,"permlink":190,"body":191,"tags":192,"created":196,"__v":24},"68a4297d9c0586aeab9905f4","智能合约中判读字符串相等 / 网络研习社#71","2vh5j","\n\nSolidity中很多方法都很底层,比如开发中很常见的迭代和判断字符串相等的方法都没有,必须要自己写个方法来实现。可能已有些库已实现类似的方法,不过总归是要麻烦不少。相比起来,JavaScript自己就带了很多原生的方法,用起来也很顺手。\n\n判读字符串相等不能直接使用`==`这样的方法,我也搞不清为什么,很多语言都是直接支持的,只有Solidity不支持。没办法,只能自己设计个方法来实现。\n\n```\nfunction compareStr (string _str1, string _str2) public returns(bool) {\n if(keccak256(abi.encodePacked(_str1)) == keccak256(abi.encodePacked(_str2))) {\n // 如果二者相等,使checkResult为true\n checkResult = true;\n }else {\n checkResult = false;\n }\n // 返回checkResult\n return checkResult;\n}\n```\n\n对两个字符串进行哈希运算,结果一致就证明两个字符串相等。这个哈希运算有点费时,可以在这之前做个长度判断以节约点时间,达到一个平衡。改进后的方法:\n\n```\nfunction compareStr (string _str1, string _str2) public returns(bool) {\n if(bytes(_str1).length == bytes(_str2).length){\n if(keccak256(abi.encodePacked(_str1)) == keccak256(abi.encodePacked(_str2))) {\n retrun true;\n }\n }\n return false;\n}\n```\n\n如果是频繁地运用这个方法,或者是对很多的字符串组进行比较,则改进后的方法会优良不少。\n\n\n",[15,18,193,194,195],"solidity","eth","contract","2020-10-12T04:38:33.000Z",["Reactive",198],{},["Set"],["ShallowReactive",201],{"$fN2Cqt7vtpFvxeluTRYH5Ax6ilHAtbS4aXvFHwOxaXAg":-1},true,"/category/network-institute"]