管理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.pswp.cn/diannao/80376.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/80376.shtml
英文地址,請注明出處:http://en.pswp.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…