管理100个小程序-很难吗

news2025/4/26 3:46:16

20公里的徒步-真难


群里的伙伴发起了一场天目山20公里徒步的活动,想着14公里都轻松拿捏了,思考了30秒后,就借着春风带着老婆孩子就出发了。一开始溪流清澈见底,小桥流水没有人家;青山郁郁葱葱,枯藤老树没有乌鸦,微风习习,鸟语花香,好不惬意。大有我看青山多妩媚,料青山见我应如是的舒坦,但是,但是没多一会儿画风突变了,爬过一座山还有数不尽的山,关键还一山更比一山高。响午温度升高,加之水资源极度匮乏(因为没有人家,划重点: 徒步一定要多带水),小宝先哭为敬了。好在睡意过去后,又坚强的跑了起来,一直用他的格言激励自己:放弃很多简单,坚持很难,我要坚持。大宝一直在在前面跟着大队伍,想想也是克服了极大的困难,他一直在山顶殷切的期盼着我们,当我们出现视野里时,又高兴的喊着爸爸,妈妈,小宝加油。或许大宝还是蛮优秀的,只是有时对大宝可能过于严厉了些。最后,大家都是笑着走过了最难的路。
在这里插入图片描述

走到16公里的地方,天已经黑了,已经到大路了,是不是20公里也不重要了,大家开心的找了家饭店,酣畅淋漓的吃喝了一顿,途中的跌倒与艰难全都成了豪爽的谈资,第二天大家都还可以自豪的说全身酸痛不已。
在这里插入图片描述

流量来了-心动了


领略了天目山的秀丽风景,回归正题。书接上文,之前捣鼓了一个小程序,没有想到日活居然过1000了,日新增200+,活跃用户次日留存超40%…
在这里插入图片描述

看着这些数据,陷入了沉思,思绪竟然来到了明朝末年(估计最近读《明朝那些事儿》魔怔了吧),农民起义纷争的年代,自己化身高迎祥、李自成、张献忠,手握数万雄兵,但不知所措…思绪一阵乱飞后得到这样一个结论:一个小程序1000,100个小程序就是10万(10万日活广告费真是不得了)- 构建小程序矩阵,构建100个程序,小程序就是雄兵,去攻城略地。

这事儿只有开头简单


有了目标,一口气又注册了5个小程序,备案,各种配置,上传,提交审核,发布…一套动作下来,虽是幸苦,总算是5个小程序都上架了,但是心中总有点不得劲儿的感觉,又说不出是哪里出了问题。还没等回过劲儿,发现程序有bug, 又吭哧吭哧一个个修改,上传,提交审核,发布…这会儿明白问题在哪里了:机械重复。光明白还没用,因为又有bug了,又是全套流程要做完。更多严重的问题是:这个过程又中注册了5个新小程序…应了那句老话:万事开头难,开头后更难。看着10个小程序要机械的重复发布,我没有崩溃,也没有去重复了,去捣鼓自动化了,解放双手才是正确的路。虽然只是解决了代码上传的问题,已是一个巨大的进步。

提前在 key目录下添加小程序代码上传密钥文件格式 private.wx0d8d56e152eb16xx.key

const fileExists = require('file-exists');

const del = require('del');
const child_process = require('child_process');
const ci = require('miniprogram-ci');

const gulp = require('gulp');


const less = require('gulp-less');
const uglify = require('gulp-uglify');
const cleanCSS = require('gulp-clean-css');
const rename = require('gulp-rename');
const gulpif = require('gulp-if');
const replace = require('gulp-replace');
const alias = require('gulp-path-alias');
const autoprefixer = require('gulp-autoprefixer');

const pkg = require('./package.json');
let projectConfig = require('./project.config.json');
const buildPath = path.join(__dirname, 'dist/');

const argv = require('minimist')(process.argv.slice(1));
const appId = argv["appId"];
if (appId){
  console.log('set appId  = ',appId);
  projectConfig['appid'] = appId;
}


const env = process.env.NODE_ENV
console.log("evn=", env)
const isPro = env === 'production';
console.log("isPro=", isPro)
const branchName = child_process.execSync('git symbolic-ref --short HEAD', {
  encoding: 'utf8',
});

const paths = {
  styles: {
    src: ['src/**/*.less'],
    dest: buildPath,
  },
  images: {
    src: 'src/images/**/*.{png,jpg,jpeg,svg,gif}',
    dest: buildPath,
  },
  scripts: {
    src: 'src/**/*.js',
    dest: buildPath,
  },
  copy: {
    src: [
      'src/**',
      '!src/**/*.less',
      '!src/**/*.js',
      'package.json',
    ],
    dest: buildPath,
  },
};

// 删除构建
function clean() {
  return del([buildPath]);
}

function log() {
  const data = Array.prototype.slice.call(arguments);
  console.log(data);
}

// 任务处理函数
function styles() {
  return gulp
    .src(paths.styles.src, { base: 'src' })
    .pipe(
      alias({
        paths: {
          '@': path.resolve(__dirname, './src/'),
        },
      })
    )
    .pipe(less())
    .pipe(autoprefixer())
    .pipe(gulpif(isPro, cleanCSS()))
    .pipe(rename((path) => (path.extname = '.wxss')))
    .pipe(gulp.dest(paths.styles.dest));
}

function scripts() {
  return (
    gulp
      .src(paths.scripts.src, { base: 'src' })
      .pipe(
        alias({
          paths: {
            '@': path.resolve(__dirname, './src/'), // src 目录
          },
        })
      )
      // .pipe(babel({ presets: ['@babel/env'], 'plugins': [] }))
      .pipe(replace('%ENV%', process.env.NODE_ENV)) // 环境变量静态替换
      .pipe(replace('%VERSION%', pkg.version))
      .pipe(gulpif(isPro, uglify()))
      .pipe(gulp.dest(paths.scripts.dest))
  );
}

// 不需要处理的文件直接复制过去
function copy() {
  return gulp
    .src(paths.copy.src)
    .pipe(gulp.dest(paths.copy.dest));
}

function watchFiles() {
  const w1 = gulp.watch(paths.styles.src, styles).on('unlink', function (file) {
    log(file + ' is deleted');
    const filePath = file.replace(/src\\/, 'dist\\');
    del([filePath]);
  });

  const w2 = gulp
    .watch(paths.scripts.src, scripts)
    .on('unlink', function (file) {
      log(file + ' is deleted');
      const filePath = file.replace(/src\\/, 'dist\\');
      del([filePath]);
    });

  const w3 = gulp.watch(paths.copy.src, copy).on('unlink', function (file) {
    log(file + ' is deleted');
    const filePath = file.replace(/src\\/, 'dist\\');
    del([filePath]);
  });

  return Promise.all([w1, w2, w3]);
}

/**
 * 小程序ci相关函数
 */
let project = {};

const keyFile = fileExists.sync(`./key/private.${appId}.key`);
if (keyFile) {
  project = new ci.Project({
    appid: appId,
    type: 'miniProgram',
    projectPath: './dist',
    privateKeyPath: `./key/private.${appId}.key`,
  });
}
async function npmBuild() {

  await ci.packNpmManually({
    packageJsonPath: './package.json',
    miniprogramNpmDistDir: './src/',
  });
}
const envLabels = {
  'production': '正式环境',
  'development': '测试环境',
  'pre': '预发环境',
};

// 机器人代号,有效范围[1-30]
const robotMap = {
  'development': 1,
  'production': 2,
  'pre': 3,
}

async function mpUpload() {
  log('mpUpload appid',appId);

   if (!appId) {
     console.log('\x1b[35m%s\x1b[0m', `
════════════════════════════════════════════════════════════════════════
⚡【${envLabels[env]}】小程序打包失败,请先执行 export APPID=你的appid 命令,设置appid
════════════════════════════════════════════════════════════════════════
  `);
    return false;
  }
  projectConfig['appid'] = appId;

  log('projectConfig appid',projectConfig.appid);

  const uploadResult = await ci.upload({
    project,
    version: pkg.version,
    desc: `【${envLabels[env]}】${pkg.description}`,
    setting: {
      es7: true,
      es6: true,
      minifyJS: true,
      minifyWXML: true,
      minifyWXSS: true,
      minify: true,
      autoPrefixWXSS: true,
    },
    robot: robotMap[env],
    onProgressUpdate: console.log,
  });
  console.log('[uploadResult:]', uploadResult);
  console.log('\x1b[35m%s\x1b[0m', `
════════════════════════════════════════════════════════════════════════
🚀【${envLabels[env]}】小程序打包已完成,可以去发布了https://mp.weixin.qq.com/
════════════════════════════════════════════════════════════════════════
  `);
}

async function preview() {
  const previewResult = await ci.preview({
    project,
    desc: `【${envLabels[env]}】${pkg.description}`, // 此备注将显示在“小程序助手”开发版列表中
    qrcodeFormat: 'image',
    qrcodeOutputDest: './preview.jpg',
    setting: {
      es7: true,
      es6: true,
      minifyJS: true,
      minifyWXML: true,
      minifyWXSS: true,
      minify: true,
      autoPrefixWXSS: true,
    },
    robot: robotMap[env],
    onProgressUpdate: console.log,
    // pagePath: 'pages/index/index', // 预览页面
    // searchQuery: 'a=1&b=2',  // 预览参数 [注意!]这里的`&`字符在命令行中应写成转义字符`\&`
  });
  console.log('[previewResult:]', previewResult);
  console.log('\x1b[35m%s\x1b[0m', `
════════════════════════════════════════════════════════════════════════
🚀【${envLabels[env] || '测试环境'}】小程序预览已完成,可以去小程序助手中查看了
════════════════════════════════════════════════════════════════════════
  `);
}
exports.watch = watchFiles;
exports.preview = preview;
// ci 自动构建npm
exports.npm = npmBuild;
exports.upload = mpUpload;

exports.default = gulp.series(styles, scripts, copy, watchFiles);

exports.build = gulp.series(clean, styles, scripts, copy);

再写个python 处理批量的问题

import subprocess
# 指定的目录
directory = '/Users/jijunjian/wealth'

commandList = [
    'npm run deploy:pro -- --appId=wx7f4984150494f817',
    'npm run deploy:pro -- --appId=wx1e8e9dc2e337b821',
    'npm run deploy:pro -- --appId=wx7493b6cfe63e360e',
    'npm run deploy:pro -- --appId=wx7c7c8a0e9e242133',
    'npm run deploy:pro -- --appId=wxfc6898107cb428a7',
    'npm run deploy:pro -- --appId=wx4fc01c82126749bc',
    'npm run deploy:pro -- --appId=wx842b1d5e54ddff47'
]
index = 0;
# 要执行的shell命令
for i in commandList:
    # 使用subprocess.run来执行命令,cwd参数指定工作目录
    result = subprocess.run(i, cwd=directory, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    # 打印命令的输出和错误信息
    print(result.stdout)  # 命令的输出
    print(result.stderr)  # 命令的错误信息
    index += 1
print("一共:%s" % index)


虽然没有完全解决重复的问题,10万日活还在远远的招手,小程序还得继续注册,但这事儿还在心里萌芽着。

日活上来了,重复还在继续


为了解决若干小程序界面一样的问题,可能会被下线,又风风火火的做了多个模板。维护变得愈发难了,难是发一次版本都会成为一次浩大的工程。如果更新20个程序,必须要登陆20次mp后台,选账号都会成为一个难点,见图可知。

在这里插入图片描述

苦不堪言时,终于想起了之前参加微信生态线下交流时,一个同学提到的服务商模式,之前觉得接入成本也挺高,就放下了,现在已是非常时期,抽出一个周末开始了摸索。

拨云见日,终觅良方


注册开放平台,创建第三方平台应用,绑定小程序,上传草稿箱,设置普通模板,提交审核,上线… 2天时间终于摸索得7788了。几乎所有操作都可以通过接口完成,比如设置域名,设置隐私,提交审核,甚至上线…有了接口就可以配置自动化了,直接使用apifox的编排能力,60分钟搞定配置。这一套下来,直接节省了90%的工作。

在这里插入图片描述

管理100小程序真不难了

有了上面的一套配置,配合模板库,对应不同的版本。发布变得非常轻松,根本不用登陆MP后台,轻松管理100个小程序,甚至可以说多多益善。这个过程大概经历了一个月,回头来看,也许正是困难让我们更强大。恰巧最近在读老舍先生的《骆驼祥子》,连在地府都可以当个好鬼儿的祥子,却没能够从苦难中强大起来,着实可惜了。最后来一张效果图,接口自动化提交审核,发布;统一的平台管理所有的小程序,管理100个小程序就是这么简单。有兴趣的朋友可以体验下
官方不让放二维码,只能放一个链接了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342941.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在Linux用libevent写一个聊天服务器

废话少说,先看看思路 因为libevent的回调机制,我们可以借助这个机制来创建bufferevent来实现用户和用户进行通信 如果成功连接后我们可以直接在listener回调函数里创建一个bufferevent缓冲区,并为每个缓冲区设置相应的读回调和事件回调&…

马浩棋:产通链CT-Chain 破局不动产 RWA,引领数智金融新变革

全球不动产 RWA 数智金融高峰论坛上马浩棋先生致辞 在全球不动产 RWA 数智金融高峰论坛暨产通链 CT-Chain 上链首发会的现场,犀牛世纪集团(香港)有限公司董事会主席马浩棋成为众人瞩目的焦点。此次盛会汇聚了全球金融、区块链及不动产领域的…

学习整理在centos7上安装mysql8.0版本教程

学习整理在centos7上安装mysql8.0版本教程 查看linux系统版本下载mysql数据库安装环境检查解压mysql安装包创建MySQL需要的目录及授权新增用户组新增组用户配置mysql环境变量编写MySQL配置文件初始化数据库初始化msyql服务启动mysql修改初始化密码配置Linux 系统服务工具,使My…

SIEMENS PLC程序解读 -BLKMOV (指定长度数据批量传输)

1、程序代码 2、程序解读 这段西门子 PLC 程序&#xff08;程序段 10&#xff09;实现了基于条件的数据块移动功能&#xff0c;具体解释如下&#xff1a; 条件触点&#xff1a; %M0.1 Always<>(TRUE)&#xff08;注释为 AT<>1&#xff09;&#xff1a;当 M0.1 的值…

初识HashMap

HashMap&#xff1a;无序&#xff0c;不重复&#xff0c;无索引 HashMap小练习&#xff1a; import java.text.ParseException; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer;import static java.lang.Math.abs;public cla…

隧道高清晰广播如何提升行车安全体验?

在隧道中行驶时&#xff0c;驾驶员常面临回声干扰、语音模糊、信息过载等问题&#xff0c;传统广播系统可能不仅未能提供有效信息&#xff0c;反而因噪音增加驾驶压力。高清晰广播通过数字降噪、动态音效优化等技术&#xff0c;显著改善驾驶员的听觉体验&#xff0c;进而提升行…

从0开始搭建一套工具函数库,发布npm,支持commonjs模块es模块和script引入使用

文章目录 文章目标技术选型工程搭建1. 初始化项目2. 安装开发依赖3. 项目结构4. 配置文件tsconfig.json.eslintrc.jseslint.config.prettierrc.jsrollup.config.cjs创建 .gitignore文件 设置 Git 钩子创建示例工具函数8. 版本管理和发布9 工具函数测试方案1. 安装测试依赖2. 配…

Cadence学习笔记之---原理图设计基本操作

目录 01 | 引 言 02 | 环境描述 03 | 原理图工具介绍 04 | 原理图设计基本操作 05 | 生成页间引用 06 | 元件自动编号 07 | 结 尾 01 | 引 言 书接上回&#xff0c;在前文中讲述了怎样制作常用的库元件&#xff0c;如电阻、二极管&#xff0c;IC器件&#xff0c;以及怎…

进行性核上性麻痹饮食指南:科学膳食助力对抗疾病

进行性核上性麻痹是一种进展性神经退行性疾病&#xff0c;常导致患者出现吞咽困难、运动障碍等症状。科学合理的饮食不仅能为患者提供必要的营养支持&#xff0c;还能降低并发症风险&#xff0c;改善生活质量。 蛋白质是维持身体机能的关键&#xff0c;患者应注重优质蛋白的摄取…

opencv函数展示4

一、形态学操作函数 1.基本形态学操作 &#xff08;1&#xff09;cv2.getStructuringElement() &#xff08;2&#xff09;cv2.erode() &#xff08;3&#xff09;cv2.dilate() 2.高级形态学操作 &#xff08;1&#xff09;cv2.morphologyEx() 二、直方图处理函数 1.直方图…

附赠二张图,阐述我对大模型的生态发展、技术架构认识。

文章精炼&#xff0c;用两张图说明大模型发展业态方向&#xff0c;以及大模型主体技术架构。&#xff08;目前还需要进一步验证我的Thought && ideas&#xff0c;等待机会吧.........&#xff09; 图一&#xff1a;探究大模型三个层次应用方向&#xff0c;浅层次入门简…

OpenCv高阶(九)——背景建模

目录 一、背景建模的核心目标与核心挑战 1. 核心目标 2. 核心挑战 ​二、背景建模模型 1、帧差法原理 2. 概率模型&#xff08;Parametric Models&#xff09; &#xff08;1&#xff09;高斯混合模型&#xff08;Gaussian Mixture Model, GMM&#xff09; &#xff08;…

leetcode - 字符串

字符串 466. 统计重复个数 题目 定义 str [s, n] 表示 str 由 n 个字符串 s 连接构成。 例如&#xff0c;str ["abc", 3] "abcabcabc" 。 如果可以从 s2( )中删除某些字符使其变为 s1&#xff0c;则称字符串 s1( )可以从字符串 s2 获得。 例如&#xf…

【基础IO上】复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux系统下,一切皆文件 | 重定向原理

1.关于文件的预备知识 1.1 文件的宏观理解 广义上理解&#xff0c;键盘、显示器等都是文件&#xff0c;因为我们说过“Linux下&#xff0c;一切皆文件”&#xff0c;当然我们现在对于这句话的理解是片面的&#xff1b;狭义上理解&#xff0c;文件在磁盘上&#xff0c;磁盘是一…

Freertos--统计所有任务栈信息以及CPU占比和钩子函数

一、概念 在 FreeRTOS 中统计任务栈信息和 CPU 占比是为了分析栈使用情况防止溢出、优化性能识别高负载任务、合理分配资源避免内存浪费、调试系统排查阻塞或优先级问题&#xff0c;有助于提升效率、确保稳定性、快速定位问题并防止崩溃&#xff0c;比如在你的蜂鸣器任务中可以…

京东商品详情API接口调用技术指南‌

本文基于京东宙斯开放平台&#xff08;JD Open API&#xff09;的 jingdong.ware.product.detail.search.get 接口&#xff0c;提供商品详情数据获取的完整技术方案&#xff0c;包含参数说明、代码实现及实战避坑指南。 一、接口功能与权限‌ 核心能力‌ 获取商品SKU的完整信…

基于Java(JSP)+MySQL实现深度学习的音乐推荐系统

基于深度学习的音乐推荐系统简述 本文简要介绍我做的基于深度学习的音乐推荐系统。主要从需求分析与设计实现的角度来进行介绍。 需求分析 基于深度学习的音乐推荐系统旨在以个性化音乐推荐模型为基础&#xff0c;使用B/S架构的形式实现。个性化推荐模型使用了 随机梯度下降…

Linux:进程间通信---匿名管道

文章目录 1. 进程间通信1.1 什么是进程间通信&#xff1f;1.2 为什么进程要进行进程间通信&#xff1f;1.3 怎么实现进程间通信&#xff1f; 2. 匿名管道2.1 匿名管道的原理2.2 匿名管道的系统接口2.3 匿名管道的使用2.4 匿名管道的运用场景 序&#xff1a;在上一篇文章中我们知…

深度学习小记(包括pytorch 还有一些神经网络架构)

这个是用来增加深度学习的知识面或者就是记录一些常用的命令,会不断的更新 import torchvision.transforms as transforms toPIL transforms.ToPILImage()#可以把tensor转换为Image类型的 imgtoPIL(img) #利用save就可以保存下来 img.save("/opt/data/private/stable_si…

【数据可视化-32】全球住房市场分析(2015-2024 年)数据集可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…