管理100个小程序-很难吗

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.mzph.cn/diannao/80376.shtml

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

相关文章

大模型工业化元年:GPT-5开启通用AI新纪元,中国技术如何破局?

过去一周,AI领域的焦点无疑是OpenAI发布的GPT-5预览版,以及全球大模型技术从实验室迈向工业化的关键转折。这场变革不仅标志着通用人工智能(AGI)的进一步逼近,更掀起了全球产业链的竞争与反思。本文将从技术突破、产业…

软考【网络工程师】2023年5月上午题答案解析

1、固态硬盘的存储介质是()。 A 光盘 B 闪存 C 软盘 D 磁盘 答案是 B。 固态硬盘(Solid State Drive),简称 SSD,是用固态电子存储芯片阵列制成的硬盘,其存储介质是闪存(Flash Memory)。闪存具有非易失性,即在断电后仍能保留存储的数据,且读写速度快、抗震性强、能…

【速写】钩子与计算图

文章目录 前向钩子反向钩子的输入反向钩子的输出 前向钩子 下面是一个测试用的计算图的网络,这里因为模型是自定义的缘故,可以直接把前向钩子注册在模型类里面,这样会更加方便一些。其实像以前BERT之类的last_hidden_state以及pool_output之…

高级电影感户外街拍人像摄影后期Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色介绍 高级电影感户外街拍人像摄影后期 Lr 调色,是运用 Adobe Lightroom 软件,对户外街拍的人像照片进行后期处理,以塑造出具有电影质感的独特视觉效果。此调色过程借助 Lr 丰富的工具与功能,从色彩、光影、对比度等多维度着手…

16.QT-Qt窗口-菜单栏|创建菜单栏|添加菜单|创建菜单项|添加分割线|添加快捷键|子菜单|图标|内存泄漏(C++)

Qt窗⼝是通过QMainWindow类来实现的。 QMainWindow是⼀个为⽤⼾提供主窗⼝程序的类,继承⾃QWidget类,并且提供了⼀个预定义的布局。QMainWindow包含⼀个菜单栏(menu bar)、多个⼯具栏(tool bars)、多个浮动窗⼝(铆接部…

【kafka初学】启动执行命令

接上篇,启动:开两个cdm窗口 注意放的文件不要太深或者中文,会报命令行太长的错误 启动zookeeper bin\windows\zookeeper-server-start.bat config\zookeeper.properties2. 启动kafka-serve bin\windows\kafka-server-start.bat config\serv…

利用 Claw Cloud Run 免费应用部署前端网页

一、注册 使用注册180天的github账户注册Claw Cloud账户,可获得每月5$的免费配额官网链接 - https://run.claw.cloud/ (ps:直接github账号登录应该就不用写了吧) 二、创建应用 打开App Launchpad 点击Create AppCPU选0.1即可&a…

豆瓣图书数据采集与可视化分析(三)- 豆瓣图书数据统计分析

文章目录 前言一、数据读取与保存1. 读取清洗后数据2. 保存数据到CSV文件3. 保存数据到MySQL数据库 二、不同分类统计分析1. 不同分类的图书数量统计分析2. 不同分类的平均评分统计分析3. 不同分类的平均评价人数统计分析4. 不同分类的平均价格统计分析5. 分类综合分析 三、不同…

网络原理 - 3(UDP 协议)

目录 协议 应用层 xml json protobuffer 传输层 端口号(Port) UDP 协议 UDP 协议端格式 完! 协议 网络通信中,协议是一个非常重要的概念。我们前面在网络原理中,就已经介绍了,为了统一各方网络&…

Java Agent 注入 WebSocket 篇

Agent 如果要对其进行Agent注入的编写,需要先理解三个名字premain,agentmain,Instrumentation premain方法在 JVM 启动阶段调用,一般维持权限的时候不会使用 agentmain方法在 JVM 运行时调用 常用的 Instrumentation实例为代理…

【深度强化学习 DRL 快速实践】近端策略优化 (PPO)

PPO(2017,OpenAI)核心改进点 Proximal Policy Optimization (PPO):一种基于信赖域优化的强化学习算法,旨在克服传统策略梯度方法在更新时不稳定的问题,采用简单易实现的目标函数来保证学习过程的稳定性 解决…

笔试强训:Day2

一、字符串中找出连续最长的数字串(双指针) 字符串中找出连续最长的数字串_牛客题霸_牛客网 #include <iostream> #include <string> #include <cctype> using namespace std;int main() {//双指针string str;cin>>str;int nstr.size();int begin-1,l…

MySQL 详解之 InnoDB:核心特性深度剖析 (ACID, 事务, 锁, 外键, 崩溃恢复)

在 MySQL 的世界里,存储引擎是数据库管理系统的核心组成部分,它负责数据的存储和提取。MySQL 支持多种存储引擎,如 MyISAM, Memory, CSV 等,但自 MySQL 5.5 版本以来,InnoDB 成为了默认的存储引擎,也是绝大多数应用场景的首选。 为什么 InnoDB 如此重要并被广泛采用?因…

Java中正则表达式使用方法

1. 正则表达式概述 正则表达式&#xff08;Regular Expression&#xff0c;简称 Regex&#xff09;是一种用于匹配字符串的模式工具。在 Java 中&#xff0c;正则表达式通过 java.util.regex 包实现&#xff0c;主要涉及以下两个类&#xff1a; Pattern&#xff1a;表示一个编…

使用浏览器的Clipboard API实现前端复制copy功能

在前端开发中&#xff0c;复制文本到剪贴板的功能通常使用浏览器的 Clipboard API 实现。比如 navigator.clipboard.writeText 方法。以下是一个简单的案例&#xff0c;展示如何使用 Clipboard API 实现复制文本的功能。 基本用法 首先&#xff0c;你需要创建一个按钮&#x…

【因果推断】(二)CV中的应用

文章目录 因果表征学习因果图 (Causal Diagram)“后门准则”&#xff08;backdoor criterion&#xff09;和“前门准则”&#xff08;frontdoor criterion&#xff09;后门调整Visual Commonsense R-CNNCausal Intervention for Weakly-Supervised Semantic SegmentationCausal…

【iOS】alloc init new底层原理

目录 前言 alloc alloc核心操作 cls->instanceSize(extraBytes) calloc obj->initInstanceIsa init 类方法&#xff1a; 实例方法&#xff1a; new 前言 笔者最近在进行对OC语言源码的学习&#xff0c;学习源码的过程中经常会出现一些从来没有遇见过的函数&…

QT窗口相关控件及其属性

widget&#xff0c;PushButton&#xff0c;lineEdit等都是基于QWidget延展出来的 并不是完整的窗口&#xff0c;而是作为窗口的一部分 真正的窗口是QMainWindow 菜单栏 Qt中的菜单栏是通过QMenuBar这个类来实现的&#xff0c;一个主窗口最多只有一个菜单栏&#xff0c;位于主…

day47—双指针-平方数之和(LeetCode-633)

题目描述 给定一个非负整数 c &#xff0c;你要判断是否存在两个整数 a 和 b&#xff0c;使得 a^2 b^2 c 。 示例 1&#xff1a; 输入&#xff1a;c 5 输出&#xff1a;true 解释&#xff1a;1 * 1 2 * 2 5示例 2&#xff1a; 输入&#xff1a;c 3 输出&#xff1a;f…

蓝桥杯 20. 压缩变换

压缩变换 原题目链接 题目描述 小明最近在研究压缩算法。他知道&#xff0c;压缩时如果能够使数值很小&#xff0c;就能通过熵编码得到较高的压缩比。然而&#xff0c;要使数值变小是一个挑战。 最近&#xff0c;小明需要压缩一些正整数序列&#xff0c;这些序列的特点是&a…