guoziyun před 1 rokem
revize
bf840b8bec

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+# Add any directories, files, or patterns you don't want to be tracked by version control
+
+node_modules/
+public/bower/
+public/data
+#public/app
+build
+data/
+.DS_Store
+*.log
+*.gzip
+*.zip
+._*
+test/*.png
+test/*.svg
+core.*
+report.*.json
+.vscode/
+zorro/.vscode/
+.eslintrc.js

+ 8 - 0
bin/monitor

@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+
+require('../service/monitor')

+ 3 - 0
libs/utils/index.js

@@ -0,0 +1,3 @@
+module.exports = {
+  mail: require('./mail'),
+};

+ 57 - 0
libs/utils/mail.js

@@ -0,0 +1,57 @@
+"use strict";
+const nodemailer = require("nodemailer");
+
+
+
+let DEFAULT_DATA = {
+  //from: '通知<webmaster@ikamaza.com>',
+  from: 'PCOLORING<pcoloring@ikamaza.com>',
+  to: "chengen@jccy-tech.com", // list of receivers
+  subject: '无标题',
+  text: "无内容", // plain text body
+  //html: "<b>Hello world?</b>", // html body
+}
+
+async function send(mail) {
+  let transporter = nodemailer.createTransport({
+    host: "mail.ikamaza.com",
+    port: 587,
+    secure: false, // true for 465, false for other ports
+    auth: {
+      user: 'mailer', // generated ethereal user
+      pass: 'Mailer@Jccy2017.', // generated ethereal password
+    },
+  });
+
+
+  let data = JSON.parse(JSON.stringify(DEFAULT_DATA));
+  data = Object.assign(data, mail);
+  try {
+    let info = await transporter.sendMail(data);
+    console.log('Mail', data);
+    console.log("Message sent: %s", info.messageId);
+    // Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>
+  
+    // Preview only available when sending through an Ethereal account
+    console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
+    return info;
+  } catch(err) {
+    console.error(err);
+  }
+  
+}
+
+
+module.exports = {
+  send,
+}
+
+if (require.main == module) {
+  send({
+    subject: 'hello:' + new Date(),
+    text: 'hello',
+    //attachments: [{
+    //  path: './mail.js',
+    //}]
+  }).then(console.log).catch(console.error);
+}

+ 472 - 0
package-lock.json

@@ -0,0 +1,472 @@
+{
+  "name": "monitor",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "monitor",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "axios": "^1.7.9",
+        "date-fns": "^4.1.0",
+        "ejs": "^3.1.10",
+        "node-cron": "^3.0.3",
+        "nodemailer": "^6.9.16",
+        "tencentcloud-sdk-nodejs": "^4.0.1003"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dependencies": {
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/async": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.7.9",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
+      "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "node_modules/bignumber.js": {
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
+      "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+    },
+    "node_modules/date-fns": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+      "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/kossnocorp"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+      "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/ejs": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+      "dependencies": {
+        "jake": "^10.8.5"
+      },
+      "bin": {
+        "ejs": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/filelist": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
+      "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
+      "dependencies": {
+        "minimatch": "^5.0.1"
+      }
+    },
+    "node_modules/filelist/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/filelist/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+      "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+      "dependencies": {
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/jake": {
+      "version": "10.9.2",
+      "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
+      "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
+      "dependencies": {
+        "async": "^3.2.3",
+        "chalk": "^4.0.2",
+        "filelist": "^1.0.4",
+        "minimatch": "^3.1.2"
+      },
+      "bin": {
+        "jake": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/json-bigint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+      "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+      "dependencies": {
+        "bignumber.js": "^9.0.0"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+    },
+    "node_modules/node-cron": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz",
+      "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==",
+      "dependencies": {
+        "uuid": "8.3.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/node-cron/node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/nodemailer": {
+      "version": "6.9.16",
+      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz",
+      "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/tencentcloud-sdk-nodejs": {
+      "version": "4.0.1003",
+      "resolved": "https://registry.npmjs.org/tencentcloud-sdk-nodejs/-/tencentcloud-sdk-nodejs-4.0.1003.tgz",
+      "integrity": "sha512-w7/qTGJtpmKOCiwPDbcTrU5jAbEx1DEuGVFADws3zGHQTdjpnn+q1ItzO833kwVWi42PmZTC6rhXeYSJV3j8pw==",
+      "dependencies": {
+        "form-data": "^3.0.0",
+        "get-stream": "^6.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "is-stream": "^2.0.0",
+        "json-bigint": "^1.0.0",
+        "node-fetch": "^2.2.0",
+        "tslib": "1.13.0",
+        "uuid": "^9.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/tencentcloud-sdk-nodejs/node_modules/form-data": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz",
+      "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+    },
+    "node_modules/tslib": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+      "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
+    },
+    "node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    }
+  }
+}

+ 23 - 0
package.json

@@ -0,0 +1,23 @@
+{
+  "name": "monitor",
+  "version": "1.0.0",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git@git.jccytech.cn:guoziyun/monitor.git"
+  },
+  "author": "guoziyun",
+  "license": "ISC",
+  "description": "",
+  "dependencies": {
+    "axios": "^1.7.9",
+    "date-fns": "^4.1.0",
+    "ejs": "^3.1.10",
+    "node-cron": "^3.0.3",
+    "nodemailer": "^6.9.16",
+    "tencentcloud-sdk-nodejs": "^4.0.1003"
+  }
+}

+ 183 - 0
service/monitor/check-api.js

@@ -0,0 +1,183 @@
+/// 每个小时检查下api接口及数据一致性
+const axios = require('axios');
+
+const { sendEmail } = require('./email')
+const { sendSms } = require('./sms')
+const SmsTemplate = require('./sms-templates');
+
+const onlineHost = 'app2.pcoloring.com';
+const offlineHost = 'color.jccytech.cn';
+const artLatestUri = 'napi/number/v8/list/art/content?column=latest';
+const jigsawLatestUri = 'napi/jigsaw/mobi/list/latest';
+const artPuzzleLatestUri = 'napi/puzzle/mobi/list/latest';
+
+async function checkArtLatest() {
+  let onlineUrl = `https://${onlineHost}/${artLatestUri}`;
+  let offlineUrl = `https://${offlineHost}/${artLatestUri}`;
+  let onlineData, offlineData;
+  try {
+    let resp = await axios.get(onlineUrl);
+    onlineData = resp.data;
+  } catch (e) {
+    console.error('Art线上latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('Art线上latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  try {
+    let resp = await axios.get(offlineUrl);
+    offlineData = resp.data;
+  } catch (e) {
+    console.error('Art 线下latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('Art 线下latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  // 检查数据是否一致: 看第一条即可
+  if (!onlineData.data || onlineData.data.length <= 0) {
+    sendEmail('Art 线上latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+  if (!offlineData.data || offlineData.data.length <= 0) {
+    sendEmail('Art 线下latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  if (onlineData.data[0]._id != offlineData.data[0]._id) {
+    sendEmail('Art latest 线上线下数据不一致, 请及时检查', `${onlineUrl}<br>${offlineUrl}`);
+    sendSms(SmsTemplate.SMS_SYNC_ERROR);
+    return false;
+  }
+
+  return true;
+}
+
+async function checkJigsawLatest() {
+  let onlineUrl = `https://${onlineHost}/${jigsawLatestUri}`;
+  let offlineUrl = `https://${offlineHost}/${jigsawLatestUri}`;
+  let onlineData, offlineData;
+  try {
+    let resp = await axios.get(onlineUrl);
+    onlineData = resp.data;
+  } catch (e) {
+    console.error('Jigsaw 线上latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('Jigsaw 线上latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  try {
+    let resp = await axios.get(offlineUrl);
+    offlineData = resp.data;
+  } catch (e) {
+    console.error('Jigsaw 线下latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('Jigsaw 线下latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  // 检查数据是否一致: 看第一条即可
+  if (!onlineData.data || onlineData.data.length <= 0) {
+    sendEmail('Jigsaw 线上latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+  if (!offlineData.data || offlineData.data.length <= 0) {
+    sendEmail('Jigsaw 线下latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  if (onlineData.data[0]._id != offlineData.data[0]._id) {
+    sendEmail('Jigsaw latest 线上线下数据不一致, 请及时检查', `${onlineUrl}<br>${offlineUrl}`);
+    sendSms(SmsTemplate.SMS_SYNC_ERROR);
+    return false;
+  }
+
+  return true;
+}
+
+async function checkArtPuzzleLatest() {
+  let onlineUrl = `https://${onlineHost}/${artPuzzleLatestUri}`;
+  let offlineUrl = `https://${offlineHost}/${artPuzzleLatestUri}`;
+  let onlineData, offlineData;
+  try {
+    let resp = await axios.get(onlineUrl);
+    onlineData = resp.data;
+  } catch (e) {
+    console.error('ArtPuzzle 线上latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('ArtPuzzle 线上latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  try {
+    let resp = await axios.get(offlineUrl);
+    offlineData = resp.data;
+  } catch (e) {
+    console.error('ArtPuzzle 线下latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('ArtPuzzle 线下latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  if (!onlineData.data || onlineData.data.length <= 0) {
+    sendEmail('ArtPuzzle 线上latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+  if (!offlineData.data || offlineData.data.length <= 0) {
+    sendEmail('ArtPuzzle 线下latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  // 检查数据是否一致: 看第一条即可
+  if (onlineData.data[0]._id != offlineData.data[0]._id) {
+    sendEmail('ArtPuzzle latest 线上线下数据不一致, 请及时检查', `\r\n${onlineUrl}<br>${offlineUrl}`);
+    sendSms(SmsTemplate.SMS_SYNC_ERROR);
+    return false;
+  }
+
+  return true;
+}
+
+async function run() {
+  console.log("check api...", new Date());
+  try {
+    if (await checkArtLatest()) {
+      console.log("check art latest OK! ");
+    } else {
+      console.error("check art latest error! ");
+      return;
+    }
+
+    if (await checkJigsawLatest()) {
+      console.log("check jigsaw latest OK! ");
+    } else {
+      console.error("check jigsaw latest error! ");
+      return;
+    }
+
+    if (await checkArtPuzzleLatest()) {
+      console.log("check art puzzle latest OK! ");
+    } else {
+      console.error("check art puzzle latest error! ");
+      return;
+    }
+  } catch (err) {
+    console.error(err.stack);
+  }
+}
+
+
+module.exports = { run }
+
+
+if (require.main == module) {
+  run();
+}

+ 182 - 0
service/monitor/check-new.js

@@ -0,0 +1,182 @@
+/// 每天下午18点检查是否上新
+const axios = require('axios');
+const datefns = require('date-fns');
+
+const { sendEmail } = require('./email')
+const { sendSms } = require('./sms')
+const SmsTemplate = require('./sms-templates');
+
+// const latestUrl = 'http://localhost:6888/napi/number/v8/list/art/content?column=latest';
+
+const artLatestUrl = 'https://app2.pcoloring.com/napi/number/v8/list/art/content?column=latest';
+const holaLatestUrl = 'https://app2.pcoloring.com/napi/number/v8/list/hola/content?column=latest';
+const casualLatestUrl = 'https://app2.pcoloring.com/napi/number/v8/list/casual/content?column=latest';
+const artDailyUrl = 'https://app2.pcoloring.com/napi/coloring/mobi/art/daily';
+
+async function checkArtLatest() {
+  let data;
+  try {
+    let resp = await axios.get(artLatestUrl);
+    data = resp.data;
+  } catch (e) {
+    console.error('Art线上latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('Art线上latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+
+  if (!data.data || data.data.length <= 0) {
+    sendEmail('Art 线上latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  // 检查今日是否有新的内容
+  let today = new Date(new Date().setHours(0, 0, 0, 0));
+  let onshelfTime = new Date(data.data[0].onshelfTime);
+  if (datefns.isBefore(onshelfTime, today)) {
+    sendEmail('art 未及时上新,请检查', `${artLatestUrl}`);
+    sendSms(SmsTemplate.SMS_NO_NEW_CONTENT);
+    return false;
+  }
+
+  return true;
+}
+
+async function checkHolaLatest() {
+  let data;
+  try {
+    let resp = await axios.get(holaLatestUrl);
+    data = resp.data;
+  } catch (e) {
+    console.error('Art线上latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('Art线上latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+
+  if (!data.data || data.data.length <= 0) {
+    sendEmail('Art 线上latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  // 检查今日是否有新的内容
+  let today = new Date(new Date().setHours(0, 0, 0, 0));
+  let onshelfTime = new Date(data.data[0].onshelfTime);
+  if (datefns.isBefore(onshelfTime, today)) {
+    sendEmail('hola 未及时上新,请检查', `${holaLatestUrl}`);
+    sendSms(SmsTemplate.SMS_NO_NEW_CONTENT);
+    return false;
+  }
+
+  return true;
+}
+
+async function checkCasualLatest() {
+  let data;
+  try {
+    let resp = await axios.get(casualLatestUrl);
+    data = resp.data;
+  } catch (e) {
+    console.error('Art线上latest数据请求出错, 请及时检查:', e.message);
+    sendEmail('Art线上latest数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+
+  if (!data.data || data.data.length <= 0) {
+    sendEmail('Art 线上latest数据有误, 请及时检查', 'latest数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  // 检查今日是否有新的内容
+  let today = new Date(new Date().setHours(0, 0, 0, 0));
+  let onshelfTime = new Date(data.data[0].onshelfTime);
+  if (datefns.isBefore(onshelfTime, today)) {
+    sendEmail('casual 未及时上新,请检查', `${casualLatestUrl}`);
+    sendSms(SmsTemplate.SMS_NO_NEW_CONTENT);
+    return false;
+  }
+
+  return true;
+}
+
+async function checkArtDaily() {
+  let data;
+  try {
+    let resp = await axios.get(artDailyUrl);
+    data = resp.data;
+  } catch (e) {
+    console.error('Art线上daily数据请求出错, 请及时检查:', e.message);
+    sendEmail('Art线上daily数据请求出错, 请及时检查', e.message);
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+
+  if (!data.data || data.data.length <= 0) {
+    sendEmail('Art 线上daily数据有误, 请及时检查', 'daily数据为空');
+    sendSms(SmsTemplate.SMS_API_ERROR);
+    return false;
+  }
+
+  // 检查今日是否有新的内容
+  let today = new Date(new Date().setHours(0, 0, 0, 0));
+  let dailyDate = new Date(data.data[0].dailyDate);
+  if (datefns.isBefore(dailyDate, today)) {
+    sendEmail('art daily 未及时上新,请检查', `${artDailyUrl}`);
+    sendSms(SmsTemplate.SMS_NO_NEW_CONTENT);
+    return false;
+  }
+
+  return true;
+}
+
+
+async function run() {
+  console.log("check new...", new Date());
+  try {
+    if (await checkArtLatest()) {
+      console.log("check art latest new OK! ");
+    } else {
+      console.error("check art latest new error! ");
+      return;
+    }
+
+    if (await checkHolaLatest()) {
+      console.log("check hola latest new OK! ");
+    } else {
+      console.error("check hola latest new error! ");
+      return;
+    }
+
+    if (await checkCasualLatest()) {
+      console.log("check casual latest new OK! ");
+    } else {
+      console.error("check casual latest new error! ");
+      return;
+    }
+
+    if (await checkArtDaily()) {
+      console.log("check art daily new OK! ");
+    } else {
+      console.error("check art daily new error! ");
+      return;
+    }
+  } catch (err) {
+    console.error(err.stack);
+  }
+}
+
+
+module.exports = { run }
+
+
+if (require.main == module) {
+  run();
+}

+ 49 - 0
service/monitor/email.js

@@ -0,0 +1,49 @@
+/// 邮件发送接口封装
+
+const { format } = require('date-fns');
+const utils = require('../../libs/utils');
+const ejs = require('ejs');
+
+
+async function sendEmail(title, data) {
+  await reportEmail(title, data);
+}
+
+async function reportEmail(title, data) {
+  let html = await renderFile(__dirname + '/monitor.ejs', {
+    title,
+    data,
+    now: new Date(),
+    format: (str, fmt) => {
+      fmt = 'yyyy/M/d HH:mm'
+      return format(new Date(str), fmt);
+    }
+  });
+
+
+  let res = await utils.mail.send({
+    // to: 'chengen@jccy-tech.com,yangshuai@jccy-tech.com,chenxinmiao@jccy-tech.com,wangmeng@jccy-tech.com;guoziyun@jccy-tech.com',
+    to: 'guoziyun@jccy-tech.com',
+    subject: title,
+    html,
+  })
+
+  return res;
+}
+
+async function renderFile(filename, data, options) {
+  return new Promise((done, reject) => {
+    ejs.renderFile(filename, data, options, function (err, str) {
+      if (err) reject(err);
+      else done(str);
+    });
+  })
+}
+
+
+module.exports = { sendEmail }
+
+
+if (require.main == module) {
+  sendEmail("Monitor Heartbeat", "heartbeat");
+}

+ 23 - 0
service/monitor/heartbeat.js

@@ -0,0 +1,23 @@
+/// 每天发一条,表征monitor服务器状态正常
+
+const { sendEmail } = require('./email')
+const { sendSms } = require('./sms')
+const SmsTemplate = require('./sms-templates');
+
+async function run() {
+  console.log("heartbeat...", new Date());
+  try {
+    sendEmail("Monitor Hearbeat", "heartbeat");
+    sendSms(SmsTemplate.SMS_HEARTBEAT);
+  } catch (err) {
+    console.error(err.stack);
+  }
+}
+
+
+module.exports = { run }
+
+
+if (require.main == module) {
+  run();
+}

+ 24 - 0
service/monitor/index.js

@@ -0,0 +1,24 @@
+/// monitor
+
+const cron = require('node-cron');
+
+const settings = [
+  ['heartbeat', '0 10 * * *', require('./heartbeat')],  // 每天早上10点monitor服务器心跳
+  ['check-api', '15 * * * *', require('./check-api')],  // 每个小时(的第15分钟)检查下api接口及数据一致性
+  ['check-new', '0 18 * * *', require('./check-new')],  // 每天下午18点检查是否上新
+]
+
+
+
+settings.forEach(setting => {
+  let [name, schedule, job] = setting;
+  if (!job.run) throw new Error(`Job [${name}] has no run() function`);
+  console.log(`[${name}] run@'${schedule}' installed!`);
+  cron.schedule(schedule, () => {
+    let date = new Date().toLocaleString();
+    console.log(`[${name}]@'${schedule}' running @ ${date}`);
+    if (job.run) {
+      job.run().then(console.log).catch(console.error);
+    }
+  })
+})

+ 24 - 0
service/monitor/monitor.ejs

@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+
+<head>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+  <title><%= title %></title>
+  <style>
+
+  </style>
+</head>
+
+<body
+  style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
+
+
+  <p>
+    <%= format(now)%>: <%= data %>
+  </p>
+
+
+</body>
+
+</html>

+ 6 - 0
service/monitor/sms-templates.js

@@ -0,0 +1,6 @@
+module.exports = {
+  SMS_API_ERROR: '2341138',   // API接口访问异常
+  SMS_NO_NEW_CONTENT: '2341062',  // 没有新的发布内容
+  SMS_SYNC_ERROR: '2341060', // 线上数据不同步
+  SMS_HEARTBEAT: '2341055',  // monitor心跳提醒
+}

+ 87 - 0
service/monitor/sms.js

@@ -0,0 +1,87 @@
+/// 短信发送(使用腾讯云短信服务)
+
+const tencentcloud = require("tencentcloud-sdk-nodejs");
+// 导入对应产品模块的client models。
+const smsClient = tencentcloud.sms.v20210111.Client;
+
+function sendSms(templateId) {
+  /* 实例化要请求产品(以sms为例)的client对象 */
+  const client = new smsClient({
+    credential: {
+      /* 为了保护密钥安全,建议将密钥设置在环境变量中或者配置文件中。
+       * 硬编码密钥到代码中有可能随代码泄露而暴露,有安全隐患,并不推荐。
+       * SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */
+      // secretId: process.env.TENCENTCLOUD_SECRET_ID,
+      // secretKey: process.env.TENCENTCLOUD_SECRET_KEY,
+      secretId: 'AKIDxnPm12ohMmxvhKvISSFoDPN9bsvkLiut',
+      secretKey: 'rVv74EDzWt5AB0fFdu3zkGevHQM6oMgI',
+    },
+    /* 必填:地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
+    region: "ap-guangzhou",
+    /* 非必填:
+     * 客户端配置对象,可以指定超时时间等配置 */
+    profile: {
+      /* SDK默认用TC3-HMAC-SHA256进行签名,非必要请不要修改这个字段 */
+      signMethod: "HmacSHA256",
+      httpProfile: {
+        reqMethod: "POST", // 请求方法
+        reqTimeout: 10, // 请求超时时间,默认60s
+        /**
+         * 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com
+         */
+        endpoint: "sms.tencentcloudapi.com"
+      },
+    },
+  })
+
+  /* 请求参数,根据调用的接口和实际情况,可以进一步设置请求参数
+   * 属性可能是基本类型,也可能引用了另一个数据结构
+   * 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */
+  /* 帮助链接:
+   * 短信控制台: https://console.cloud.tencent.com/smsv2
+   * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
+  let params = {
+    /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */
+    // 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
+    SmsSdkAppId: "1400217872",
+    /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
+    // 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
+    SignName: "北京杰诚创业科技有限公司",
+    /* 模板 ID: 必须填写已审核通过的模板 ID */
+    // 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
+    TemplateId: templateId,
+    /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */
+    TemplateParamSet: null,
+    /* 下发手机号码,采用 e.164 标准,+[国家或地区码][手机号]
+     * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号*/
+    // PhoneNumberSet: ["+8613530139503", "+8618210309391", "+8618612452398", "+8618600915226"],
+    PhoneNumberSet: ["+8613530139503"],
+    /* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 */
+    SessionContext: "",
+    /* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */
+    ExtendCode: "",
+    /* 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId,无需填写该字段。注:月度使用量达到指定量级可申请独立 SenderId 使用,详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 */
+    SenderId: "",
+  }
+  // 通过client对象调用想要访问的接口,需要传入请求对象以及响应回调函数
+  client.SendSms(params, function (err, response) {
+    // 请求异常返回,打印异常信息
+    if (err) {
+      console.error("短信发送失败");
+      console.log(err)
+      return
+    }
+    // 请求正常返回,打印response对象
+    console.log("短信发送成功");
+    console.log(response)
+  })
+
+}
+
+
+module.exports = { sendSms }
+
+
+if (require.main == module) {
+  sendSms("2341055");
+}