毕设中封装的一些工具方法

主要是封装的一些koa后端工具方法,都是简单的封装,并未考虑复杂情况。

snow_flake.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { env } from 'process';

export class SnowFlake {
// 系统上线的时间戳,此处设置为 2023-06-22 00:00:00 的时间戳
epoch = BigInt(1687392000000);

// 数据中心的位数
dataCenterIdBits = 5;
// 机器id的位数
workerIdBits = 5;
// 自增序列号的位数
sequenceBits = 12;

// 最大的数据中心id 这段位运算可以理解为2^5-1 = 31
maxDataCenterId = (BigInt(1) << BigInt(this.workerIdBits)) - BigInt(1);
// 最大的机器id 这段位运算可以理解为2^5-1 = 31
maxWorkerId = (BigInt(1) << BigInt(this.workerIdBits)) - BigInt(1);

// 时间戳偏移位数
timestampShift = BigInt(
this.dataCenterIdBits + this.workerIdBits + this.sequenceBits,
);

// 数据中心偏移位数
dataCenterIdShift = BigInt(this.workerIdBits + this.sequenceBits);
// 机器id偏移位数
workerIdShift = BigInt(this.sequenceBits);
// 自增序列号的掩码
sequenceMask = (BigInt(1) << BigInt(this.sequenceBits)) - BigInt(1);
// 记录上次生成id的时间戳
lastTimestamp = BigInt(-1);
// 数据中心id
dataCenterId = BigInt(0);
// 机器id
workerId = BigInt(0);
// 自增序列号
sequence = BigInt(0);
constructor(dataCenterId: number, workerId: number) {
// 校验数据中心 ID 和工作节点 ID 的范围
if (dataCenterId > this.maxDataCenterId || dataCenterId < 0) {
throw new Error(
`Data center ID must be between 0 and ${this.maxDataCenterId}`,
);
}

if (workerId > this.maxWorkerId || workerId < 0) {
throw new Error(`Worker ID must be between 0 and ${this.maxWorkerId}`);
}

this.dataCenterId = BigInt(dataCenterId);
this.workerId = BigInt(workerId);
}

nextId() {
let timestamp = BigInt(Date.now());
// 如果上一次生成id的时间戳比下一次生成的还大,说明服务器时间有问题,出现了回退,这时候再生成id,可能会生成重复的id,所以直接抛出异常。
if (timestamp < this.lastTimestamp) {
// 时钟回拨,抛出异常并拒绝生成 ID
throw new Error('Clock moved backwards. Refusing to generate ID.');
}

// 如果当前时间戳和上一次的时间戳相等,序列号加一
if (timestamp === this.lastTimestamp) {
// 同一毫秒内生成多个 ID,递增序列号,防止冲突
this.sequence = (this.sequence + BigInt(1)) & this.sequenceMask;
if (this.sequence === BigInt(0)) {
// 序列号溢出,等待下一毫秒
timestamp = this.waitNextMillis(this.lastTimestamp);
}
} else {
// 不同毫秒,重置序列号
this.sequence = BigInt(0);
}

this.lastTimestamp = timestamp;

// 组合各部分生成最终的 ID,可以理解为把64位二进制转换位十进制数字
const id =
((timestamp - this.epoch) << this.timestampShift) |
(this.dataCenterId << this.dataCenterIdShift) |
(this.workerId << this.workerIdShift) |
this.sequence;

return id.toString();
}

waitNextMillis(lastTimestamp) {
let timestamp = BigInt(Date.now());
while (timestamp <= lastTimestamp) {
// 主动等待,直到当前时间超过上次记录的时间戳
timestamp = BigInt(Date.now());
}
return timestamp;
}
}

// 如果有pm_id,把pm_id当机器id传进去
export const snowFlake = new SnowFlake(0, +env.pm_id || 0);

python_runner.js

在nodejs中调用python程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fs = require('fs');
const child_process = require('child_process');
const path = require('path')

const pythonAbsolutePath = path.resolve(__dirname, '../py/index.py');
console.log('pythonAbsolutePath', pythonAbsolutePath);

let workerProcess = child_process.exec(
`python3 ${pythonAbsolutePath}`,
function (error, stdout, stderr) {
if (error) {
console.log(error.stack);
console.log('Error code: '+error.code);
console.log('Signal received: '+error.signal);
}
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});

workerProcess.on('exit', function (code) {
console.log('子进程已退出,退出码 '+code);
});


ip.js

获取用户ip。

1
2
3
4
5
6
7
8
9
10
11
12
13
function getClientIP(req) {
let ip =
req.headers["x-forwarded-for"] || // 判断是否有反向代理 IP
req.ip ||
req.connection.remoteAddress || // 判断 connection 的远程 IP
req.socket.remoteAddress || // 判断后端的 socket 的 IP
req.connection.socket.remoteAddress ||
"";
if (ip) {
ip = ip.replace("::ffff:", "");
}
return ip;
}

file_util.js

读取文件的工具方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const fs = require('fs')
module.exports.getFileJsonData = (filePath) => {
// 根据文件的路径, 读取文件的内容
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf-8', (error, data) => {
if(error) {
// 读取文件失败
reject(error)
} else {
// 读取文件成功
resolve(data)
}
})
})
}

sendEmail.js

此处我使用的是网易邮箱进行发邮件,将配置写在config/index.js中然后获取wyAuth的参数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const nodemailer = require("nodemailer");
const config = require("../config/index");

let transporter = nodemailer.createTransport({
//node_modules/nodemailer/lib/well-known/services.json 查看相关的配置,如果使用qq邮箱,就查看qq邮箱的相关配置
service: "smtp.163.com",
host: "smtp.163.com",
secureConnection: true,
// service: 'qq', //类型qq邮箱
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: config.wyAuth.user, // 发送方的邮箱
pass: config.wyAuth.pass, // smtp 的授权码
},
});
//pass 不是邮箱账户的密码而是stmp的授权码(必须是相应邮箱的stmp授权码)
//邮箱---设置--账户--POP3/SMTP服务---开启---获取stmp授权码

function sendMail(mail, code, call) {
// 发送的配置项
let mailOptions = {
from: `"weather" <${config.wyAuth.user}>`, // 发送方
to: mail, //接收者邮箱,多个邮箱用逗号间隔
subject: '欢迎来到"weather"', // 标题
text: "Here is a weather demo.", // 文本内容
html: `<p>感谢您使用weather-demo!,您的验证码是${code}</p>, 详情请点击:</p><a href="https://weather.zyha.cn">点击跳转</a>`, //页面内容
// attachments: [{//发送文件
// filename: 'index.html', //文件名字
// path: './index.html' //文件路径
// },
// {
// filename: 'sendEmail.js', //文件名字
// content: 'sendEmail.js' //文件路径
// }
// ]
};

//发送函数
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
call(false);
} else {
call(true); //因为是异步 所有需要回调函数通知成功结果
}
});
}

module.exports = {
sendMail,
};

sendSms.js

发送短信,使用的是腾讯云的短信api,相关参数需要在腾讯云获取,付费使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Depends on tencentcloud-sdk-nodejs version 4.0.3 or higher
const tencentcloud = require("tencentcloud-sdk-nodejs");

const SmsClient = tencentcloud.sms.v20210111.Client;

const clientConfig = {
credential: {
secretId: "SecretId",
secretKey: "SecretKey",
},
region: "ap-nanjing",
profile: {
httpProfile: {
endpoint: "sms.tencentcloudapi.com",
},
},
};

const client = new SmsClient(clientConfig);
const params = {
"PhoneNumberSet": [
"+xxxxxxxxx"
],
"SmsSdkAppId": "xxxx",
"SignName": "xxxx",
"TemplateId": "xxxxx",
"TemplateParamSet": [
"xxxxx"
]
};
client.SendSms(params).then(
(data) => {
console.log(data);
},
(err) => {
console.error("error", err);
}
);