• 给老婆写的,每日自动推送暖心消息



    一、起因

    在稀土掘金上刷到一篇文章【给女友写的,每日自动推送暖心消息】,每天自动定时发送消息,感觉很有趣,刚好最近在周会上听同事讲用 nodejs框架实现前后端考试系统,自己也通过百度了解到nodejs后端框架排名如图,于是决定尝试一把 nestjs(nodejs框架排第二名) 实现。

    在这里插入图片描述

    二、环境准备

    操作系统:windows、Linux、MacOs
    运行环境:Nodejs>=12 v13版本除外

    三、创建nestjs项目

    使用 Nest CLI 建立新项目非常简单。 在安装好 npm 后,您可以使用下面命令在您的 OS 终端中创建 Nest 项目:

    $ npm i -g @nestjs/cli
    $ nest new project-name
    
    • 1
    • 2

    将会创建 project-name 目录, 安装 node_modules 和一些其他样板文件,并将创建一个 src 目录,目录中包含几个核心文件。

    src
     ├── app.controller.spec.ts
     ├── app.controller.ts
     ├── app.module.ts
     ├── app.service.ts
     └── main.ts
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    以下是这些核心文件的简要概述:

    在这里插入图片描述
    main.ts 包含一个异步函数,它负责引导我们的应用程序:

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      await app.listen(3000);
    }
    bootstrap();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    要创建一个 Nest 应用实例,我们使用了 NestFactory 核心类。NestFactory 暴露了一些静态方法用于创建应用实例。 create() 方法返回一个实现 INestApplication 接口的对象。该对象提供了一组可用的方法。 在上面的 main.ts 示例中,我们只是启动 HTTP 服务,让应用程序等待 HTTP 请求。

    四、控制器

    @Controller('ymn')
    export class YmnController {
      constructor(private ymnService: YmnService) {}
    
      @Get()
      async send(@Res() response: Response) {
        const result = await this.ymnService.sendOut();
        response.status(HttpStatus.OK).send('ok');
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    五、service服务层

    // 时间处理
    const moment = require('moment');
    
    @Injectable()
    export class YmnService {
      constructor(
        private configService: ConfigService,
        private readonly httpService: HttpService,
      ) {}
    
      /**
       * @method 发送模板消息给媳妇儿
       * @returns any[]
       */
      async sendOut(): Promise<any[]> {
        const token = await this.getToken();
        const data = await this.getTemplateData();
        console.log(data);
        // 模板消息接口文档
        const users = this.configService.get('weixin.users');
        const promise = users.map((id) => {
          data.touser = id;
          return this.toWechart(token, data);
        });
        const results = await Promise.all(promise);
        return results;
      }
    
      /**
       * @method 获取token
       * @returns string token
       */
      async getToken(): Promise<string> {
        ...
      }
    
      /**
       * @method 组装模板消息数据
       * @returns dataType 组装完的数据
       */
      async getTemplateData(): Promise<dataType> {
        ...
      }
    
      /**
       * @method 获取距离下次发工资还有多少天
       * @returns number
       */
      getWageDay(): number {
        ...
      }
    
      /**
       * @method 获取距离下次结婚纪念日还有多少天
       * @returns number
       */
      getWeddingDay(): number {
        ...
      }
    
      /**
       * @method 获取距离下次生日还有多少天
       * @returns number
       */
      getbirthday(): number {
        ...
      }
    
      /**
       * @method 获取时间日期
       * @returns string
       */
      getDatetime(): string {
        ...
      }
    
      /**
       * @method 获取是第几个结婚纪念日
       * @returns number
       */
      getMarryYear(): number {
        ...
      }
    
      /**
       * @method 获取相恋几年了
       * @returns number
       */
      getLoveYear(): number {
        ...
      }
    
      /**
       * @method 获取是第几个生日
       * @returns number
       */
      getBirthYear(): number {
        ...
      }
    
      /**
       * @method 获取天气
       * @returns
       */
      async getWeather(): Promise<weatherType> {
        ...
      }
    
      /**
       * @method 获取每日一句
       * @returns string
       */
      async getOneSentence(): Promise<string> {
        ...
      }
    
      /**
       * @method 获取相恋天数
       * @returns number
       */
      getLoveDay(): number {
        ...
      }
    
      /**
       * @method 通知微信接口
       * @param token
       * @param data
       * @returns
       */
      async toWechart(token: string, data: dataType): Promise<any> {
        ...
      }
    }
    
    • 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
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134

    1、获取Access token

      /**
       * @method 获取token
       * @returns string token
       */
      async getToken(): Promise<string> {
        const grantType = this.configService.get('weixin.grant_type');
        const appid = this.configService.get('weixin.appid');
        const secret = this.configService.get('weixin.secret');
        // 模板消息接口文档
        const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=${grantType}&appid=${appid}&secret=${secret}`;
        const result = await firstValueFrom(this.httpService.get(url));
        if (result.status === 200) return result.data.access_token as string;
        else return '';
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、组装模板消息数据

      /**
       * @method 组装模板消息数据
       * @returns dataType 组装完的数据
       */
      async getTemplateData(): Promise<dataType> {
        // 判断所需模板
        // 发工资模板 getWageDay === 0       wageDay
        // 结婚纪念日模板 getWeddingDay === 0  weddingDay
        // 生日模板 getbirthday === 0       birthday
        // 正常模板                         daily
        const wageDay = this.getWageDay();
        const weddingDay = this.getWeddingDay();
        const birthday = this.getbirthday();
        const data = {
          topcolor: '#FF0000',
          template_id: '',
          data: {},
          touser: '',
        };
        // 发工资模板
        if (!wageDay) {
          data.template_id = this.configService.get('weixin.wage_day');
          data.data = {
            dateTime: {
              value: this.getDatetime(),
              color: '#cc33cc',
            },
          };
        } else if (!weddingDay) {
          // 结婚纪念日模板
          data.template_id = this.configService.get('weixin.wedding_day');
          data.data = {
            dateTime: {
              value: this.getDatetime(),
              color: '#cc33cc',
            },
            anniversary: {
              value: this.getMarryYear(),
              color: '#ff3399',
            },
            year: {
              value: this.getLoveYear(),
              color: '#ff3399',
            },
          };
        } else if (!birthday) {
          // 生日模板
          data.template_id = this.configService.get('weixin.birthdate');
          data.data = {
            dateTime: {
              value: this.getDatetime(),
              color: '#cc33cc',
            },
            individual: {
              value: this.getBirthYear(),
              color: '#ff3399',
            },
          };
        } else {
          // 正常模板
          data.template_id = this.configService.get('weixin.daily');
          // 获取天气
          const getWeather = await this.getWeather();
          // 获取每日一句
          const message = await this.getOneSentence();
          data.data = {
            dateTime: {
              value: this.getDatetime(),
              color: '#cc33cc',
            },
            love: {
              value: this.getLoveDay(),
              color: '#ff3399',
            },
            wage: {
              value: wageDay,
              color: '#66ff00',
            },
            birthday: {
              value: birthday,
              color: '#ff0033',
            },
            marry: {
              value: weddingDay,
              color: '#ff0033',
            },
            wea: {
              value: getWeather.wea,
              color: '#33ff33',
            },
            tem: {
              value: getWeather.tem,
              color: '#0066ff',
            },
            wind_class: {
              value: getWeather.wind_class,
              color: '#ff0033',
            },
            tem1: {
              value: getWeather.tem1,
              color: '#ff0000',
            },
            tem2: {
              value: getWeather.tem2,
              color: '#33ff33',
            },
            win: {
              value: getWeather.win,
              color: '#3399ff',
            },
            message: {
              value: message,
              color: '#8C8C8C',
            },
          };
        }
        return data;
      }
    
    • 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
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    3、获取下次发工资还有多少天

      /**
       * @method 获取距离下次发工资还有多少天
       * @returns number
       */
      getWageDay(): number {
        const wageDay = this.configService.get('time.wage_day');
        // 获取日期 day
        // 如果在wage号之前或等于wage时,那么就用wage-day
        // 如果在wage号之后,那么就用wage +(当前月总天数 - day)
        // 当日日期day
        const day = moment().date();
        // 当月总天数
        const nowDayTotal = moment().daysInMonth();
        // // 下个月总天数
        let resultDay = 0;
        if (day <= wageDay) resultDay = wageDay - day;
        else resultDay = wageDay + (nowDayTotal - day);
        return resultDay;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4、获取距离下次结婚纪念日还有多少天

      /**
       * @method 获取距离下次结婚纪念日还有多少天
       * @returns number
       */
      getWeddingDay(): number {
        const weddingDay = this.configService.get('time.wedding_day');
        // 获取当前时间戳
        const now = moment(moment().format('YYYY-MM-DD')).valueOf();
        // 获取纪念日 月-日
        const mmdd = moment(weddingDay).format('-MM-DD');
        // 获取当年
        const y = moment().year();
        // 获取今年结婚纪念日时间戳
        const nowTimeNumber = moment(y + mmdd).valueOf();
        // 判断今天的结婚纪念日有没有过,如果已经过去(now>nowTimeNumber),resultMarry日期为明年的结婚纪念日
        // 如果还没到,则结束日期为今年的结婚纪念日
        let resultMarry = nowTimeNumber;
        if (now > nowTimeNumber) {
          // 获取明年纪念日
          resultMarry = moment(y + 1 + mmdd).valueOf();
        }
        return moment(moment(resultMarry).format()).diff(
          moment(now).format(),
          'day',
        );
      }
    
    • 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

    5、获取距离下次生日还有多少天

      /**
       * @method 获取距离下次生日还有多少天
       * @returns number
       */
      getbirthday(): number {
        const birthdate = this.configService.get('time.birthdate');
        // 获取当前时间戳
        const now = moment(moment().format('YYYY-MM-DD')).valueOf();
        // 获取当年
        const y = moment().year();
        // 获取纪念日 月
        const m = moment(birthdate).month();
        // 获取纪念日 日
        const d = moment(birthdate).date();
        // 获取今年生日阳历
        const nowBirthday = lunisolar
          .fromLunar({
            year: y,
            month: m + 1,
            day: d,
          })
          .format('YYYY-MM-DD');
        // 获取今年生日时间戳
        const nowTimeNumber = moment(nowBirthday).valueOf();
        // 判断生日有没有过,如果已经过去(now>nowTimeNumber),resultBirthday日期为明年的生日日期
        // 如果还没到,则结束日期为今年的目标日期
        let resultBirthday = nowTimeNumber;
        if (now > nowTimeNumber) {
          // 获取明年生日阳历
          const nextBirthday = lunisolar
            .fromLunar({
              year: y + 1,
              month: m + 1,
              day: d,
            })
            .format('YYYY-MM-DD');
          // 获取明年目标日期
          resultBirthday = moment(nextBirthday).valueOf();
        }
        return moment(moment(resultBirthday).format()).diff(
          moment(now).format(),
          'day',
        );
      }
    
    • 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

    6、获取时间日期

      /**
       * @method 获取时间日期
       * @returns string
       */
      getDatetime(): string {
        const week = {
          1: '星期一',
          2: '星期二',
          3: '星期三',
          4: '星期四',
          5: '星期五',
          6: '星期六',
          0: '星期日',
        };
        return moment().format('YYYY年MM月DD日 ') + week[moment().weekday()];
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    7、获取是第几个结婚纪念日

      /**
       * @method 获取是第几个结婚纪念日
       * @returns number
       */
      getMarryYear(): number {
        const weddingDay = this.configService.get('time.wedding_day');
        return moment().year() - moment(weddingDay).year();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    8、获取相恋几年了

      /**
       * @method 获取相恋几年了
       * @returns number
       */
      getLoveYear(): number {
        const loveDay = this.configService.get('time.love');
        return moment().year() - moment(loveDay).year();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    9、获取是第几个生日

      /**
       * @method 获取是第几个生日
       * @returns number
       */
      getBirthYear(): number {
        const birthdate = this.configService.get('time.birthdate');
        return moment().year() - moment(birthdate).year();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    10、获取天气

      /**
       * @method 获取天气
       * @returns
       */
      async getWeather(): Promise<weatherType> {
        try {
          const dataType = this.configService.get('weather.data_type');
          const ak = this.configService.get('weather.ak');
          const districtId = this.configService.get('weather.district_id');
          // https://api.map.baidu.com/weather/v1/?district_id=130128&data_type=all&ak=bGjmaBLLzlBZXTiAkOwSqiVjftZlg17O
          const url = `https://api.map.baidu.com/weather/v1/?district_id=${districtId}&data_type=${dataType}&ak=${ak}`;
          const result: any = await firstValueFrom(this.httpService.get(url));
          // "wea": "多云",
          // "tem": "27", 实时温度
          // "tem1": "27", 高温
          // "tem2": "17", 低温
          // "win": "西风",
          // "air_level": "优",
          if (result && result.data && result.data.status === 0) {
            const now = result.data.result.now;
            const forecasts = result.data.result.forecasts[0];
            return {
              wea: now.text,
              tem: now.temp,
              tem1: forecasts.high,
              tem2: forecasts.low,
              win: now.wind_dir,
              wind_class: now.wind_class,
            };
          }
        } catch (error) {
          return {
            wea: '未知',
            tem: '未知',
            tem1: '未知',
            tem2: '未知',
            win: '未知',
            wind_class: '未知',
          };
        }
      }
    
    • 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

    11、获取每日一句

      /**
       * @method 获取每日一句
       * @returns string
       */
      async getOneSentence(): Promise<string> {
        const url = 'https://v1.hitokoto.cn/';
        const result = await firstValueFrom(this.httpService.get(url));
        if (result && result.status === 200) return result.data.hitokoto;
        else return '今日只有我爱你!';
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    12、获取相恋天数

      /**
       * @method 获取相恋天数
       * @returns number
       */
      getLoveDay(): number {
        const loveDay = this.configService.get('time.love');
        return moment(moment().format('YYYY-MM-DD')).diff(loveDay, 'day');
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    13、发送模板消息

      /**
       * @method 通知微信接口
       * @param token
       * @param data
       * @returns
       */
      async toWechart(token: string, data: dataType): Promise<any> {
        // 模板消息接口文档
        const url = `https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=${token}`;
        const result = await firstValueFrom(this.httpService.post(url, data));
        return result;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    六、定义的数据类型

    interface dataItemType {
      value: string;
      color: string;
    }
    
    export interface dataType {
      topcolor: string;
      template_id: string;
      touser: string;
      data: Record<any, dataItemType>;
    }
    
    export interface weatherType {
      wea: string;
      tem: string;
      tem1: string;
      tem2: string;
      win: string;
      wind_class: string;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    七、配置文件

    配置方法参考配置

    1、微信公众号配置

    因个人只能申请订阅号,而订阅号不支持发送模板消息,所以在此使用的测试的微信公众号,有微信号都可以申请,免注册,扫码登录

    无需公众帐号、快速申请接口测试号

    直接体验和测试公众平台所有高级接口

    申请地址

    import { registerAs } from '@nestjs/config';
    
    export default registerAs('weixin', () => ({
      grant_type: '*',
      appid: '*',
      secret: '*',
      users: ['*'],// 用户的openid
      wage_day: '*',// 工资日模板
      wedding_day: '*',// 结婚纪念日模板
      birthdate: '*',// 生日模板
      daily: '*',// 普通模板
    }));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2、特殊时间点设置

    import { registerAs } from '@nestjs/config';
    
    // 时间
    export default registerAs('time', () => ({
      wage_day: 10, // 工资日
      wedding_day: '*',// 结婚纪念日
      birthdate: '*',// 生日阴历
      love: '*',// 相爱日期
    }));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、天气配置

    import { registerAs } from '@nestjs/config';
    
    export default registerAs('weather', () => ({
      data_type: 'all',
      ak: '*',
      district_id: '110100',
    }));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    八、微信消息模板

    这个需要在上文提到的 微信公众平台测试账号 单独设置

    以下是我用的模板

    1、正常模板

    {{dateTime.DATA}} 
    今天是 我们相恋的第{{love.DATA}}天 
    距离上交工资还有{{wage.DATA}}天 
    距离你的生日还有{{birthday.DATA}}天 
    距离我们结婚纪念日还有{{marry.DATA}}天 
    今日天气 {{wea.DATA}} 
    当前温度 {{tem.DATA}}度 
    最高温度 {{tem1.DATA}}度 
    最低温度 {{tem2.DATA}}度 
    风向 {{win.DATA}} 
    风力等级 {{wind_class.DATA}} 
    每日一句 
    {{message.DATA}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2、发工资模板

    {{dateTime.DATA}}
    老婆大人,今天要发工资了,预计晚九点前会准时上交,记得查收!
    
    • 1
    • 2

    3、生日模板

    {{dateTime.DATA}}
    听说今天是你人生当中第 {{individual.DATA}} 个生日?天呐,
    我差点忘记!因为岁月没有在你脸上留下任何痕迹。
    尽管,日历告诉我:你又涨了一岁,但你还是那个天真可爱的小妖女,生日快乐!
    
    • 1
    • 2
    • 3
    • 4

    4、结婚纪念日

    {{dateTime.DATA}}
    今天是结婚{{anniversary.DATA}}周年纪念日,在一起{{year.DATA}}年了,
    经历了风风雨雨,最终依然走在一起,很幸运,很幸福!我们的小家庭要一直幸福下去。
    
    • 1
    • 2
    • 3

    九、本地开发

    $ yarn
    $ yarn start:dev
    
    • 1
    • 2

    十、展示效果

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    参考

    1. node.js 后端框架star 排名 2023年2月更新
    2. 给女友写的,每日自动推送暖心消息

    写在最后

    如果你感觉文章不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
    如果你觉得该文章有一点点用处,可以给作者点个赞;\\*^o^*//
    如果你想要和作者一起进步,可以微信扫描二维码,关注前端老L~~~///(^v^)\\\~~~
    谢谢各位读者们啦(^_^)∠※!!!

  • 相关阅读:
    【Java并发编程】AQS和CAS
    Vue的计算属性/侦听器/组件用法详解
    算法-复杂度
    unity 使用声网(Agora)实现语音通话
    Mybiosource基因编辑技术HIV及研究工具
    用CSS实现一个扇形
    DataTable扩展 列转行方法(2*2矩阵转换)
    一文读懂:testcafe框架和页面元素交互
    雪花算法改造: 兼容JS截短位数的53bit分布式ID生成器
    FTXUI基础笔记(checkbox复选框组件)
  • 原文地址:https://blog.csdn.net/weixin_62277266/article/details/132724488