前言
基礎(chǔ)平臺搭建上篇 介紹項目流程設(shè)計、數(shù)據(jù)庫搭建、jwt 登錄等模塊
基礎(chǔ)平臺搭建中篇 介紹分支管理設(shè)計、webSocket 基礎(chǔ)模塊
本篇下將介紹流程管理與提測相關(guān)基礎(chǔ)模塊
后端模塊
- DevOps - Gitlab Api使用(已完成,點擊跳轉(zhuǎn))
- DevOps - 搭建 DevOps 基礎(chǔ)平臺(已完成 70%)
- DevOps - Gitlab CI 流水線構(gòu)建
- DevOps - Jenkins 流水線構(gòu)建
- DevOps - Docker 使用
- DevOps - 發(fā)布任務(wù)流程設(shè)計
- DevOps - 代碼審查卡點
- DevOps - Node 服務(wù)質(zhì)量監(jiān)控
前端模塊
- DevOps - H5 基礎(chǔ)腳手架
- DevOps - React 項目開發(fā)
后期可能會根據(jù) DevOps 項目的實際開發(fā)進度對上述系列進行調(diào)整
流程與提測管理
流程管理
在基礎(chǔ)平臺搭建上篇已經(jīng)介紹過流程的設(shè)計,這里再簡單解釋下
- 開發(fā)同學(xué)創(chuàng)建對應(yīng)的工程以及分支,進行功能開發(fā)
- 項目負責(zé)人創(chuàng)建流程時,關(guān)聯(lián)多個開發(fā)分支,附加需求(需求模塊簡化成 desc 字段描述,沒有單獨抽出去)
- 流程的狀態(tài)由關(guān)聯(lián)的分支狀態(tài)組合,當(dāng)所關(guān)聯(lián)所有的開發(fā)分支狀態(tài)全部轉(zhuǎn)變?yōu)橐淹瓿傻臅r候,才會進入下一個狀態(tài)
整個項目管理,應(yīng)該拆解成項目->需求->工程,預(yù)留字段,將需求跟流程直接合并在一起,先完成主要功能,后期再進一步的拓展
提測管理
- 開發(fā)人員在開發(fā)完對應(yīng)功能進行項目提測
- 未關(guān)聯(lián)流程的分支不能進行提測
- 提測之后,測試同學(xué)介入測試,根據(jù) desc (需求)進行測試
- 開發(fā)內(nèi)容再提測之后,才能發(fā)布到預(yù)發(fā)或生產(chǎn),否則只能在測試環(huán)境發(fā)布(禁止未測試的需求直接上線)
不要嫌麻煩,現(xiàn)實中,產(chǎn)品隨便提個需求就上,出現(xiàn)問題到處甩鍋的情況還少嗎?嚴(yán)格卡關(guān)也是減輕工作量的一個小助力
DevOps 開發(fā)下篇
創(chuàng)建流程模塊
import { Post, Prefix, Get } from "egg-shell-decorators";
import BaseController from "./base";
@Prefix("process")
export default class ProcessController extends BaseController {
/**
* @author: Cookie
* @description: 創(chuàng)建 devOps 任務(wù)流
*/
@Post("/create")
public async createProcess({
request: {
body: { params },
},
}) {
const { ctx } = this;
const { username } = this.userInfo;
const { name, branchIds, workflowTplId, desc } = params;
const branchStatus = await ctx.service.branch.checkProcess({ branchIds });
if (!branchStatus)
this.error({
msg: "已有分支在流程中",
});
const status = await ctx.service.process.createProcess({
desc,
name,
branchIds,
workflowTplId,
createdUser: username,
updateUser: username,
});
await ctx.service.branch.updateBranch({
branchIds,
opt: {
processId: status.id,
},
});
this.success(status);
}
/**
* @author: Cookie
* @description: 查詢 devOps 任務(wù)流
*/
@Get("/getList")
public async getProcessList({ request: { query } }) {
const { ctx } = this;
const { pageSize = 10, pageNum = 1 } = query;
const processList = await ctx.service.process.getProcessList({
pageNum: parseInt(pageNum),
pageSize: parseInt(pageSize),
});
// 聯(lián)表查詢分支信息
for (let process of processList.rows) {
const { branchIds } = process;
process.branches = await ctx.service.branch.getSelfBranchList({
branchIds,
});
}
this.success(processList);
}
}
提測模塊
import { Post, Prefix, Get } from "egg-shell-decorators";
import BaseController from "./base";
@Prefix("testRecord")
export default class TestRecord extends BaseController {
/**
* @author: Cookie
* @description: 創(chuàng)建提測記錄
*/
@Post("/create")
public async createTestRecord({
request: {
body: { params },
},
}) {
const { ctx } = this;
const { id: submitUserId } = this.userInfo;
const { desc, name, branchIds, testUserId } = params;
const branchStatus = await ctx.service.branch.checkProcess({
branchIds,
status: "every",
});
if (branchStatus)
this.error({
msg: "存在未關(guān)聯(lián)流程的分支",
});
const status = await ctx.service.testRecord.createTestRecord({
desc,
name,
branchIds,
submitUserId,
testUserId,
testStatus: 0,
});
this.success(status);
}
}
提測消息推送采用郵件(正式)與機器人(即時),提測內(nèi)容、次數(shù)、質(zhì)量等寫入數(shù)據(jù)庫,系統(tǒng)本身也能追蹤,作為后期效能評估的輔助
郵件推送
提測模塊的具體實現(xiàn)代碼,我們分為 3 塊
- 發(fā)送郵件使用 nodemailer
- 郵件模板使用 nunjucks 模板引擎,配置郵件模板
- 郵件前端自定義內(nèi)容使用 marked 插件解析 markdown 語法
import { MAIL_CONFIG } from "../../config/default.config";
const marked = require("marked"); // marked 轉(zhuǎn)換
const nodemailer = require("nodemailer"); // 發(fā)送郵件
const nunjucks = require("nunjucks"); // 模板引擎
const path = require("path");
// 郵箱配置初始化
const transporter = nodemailer.createTransport({
host: MAIL_CONFIG.service,
secureConnection: true, // 使用 SSL 方式(安全方式,防止被竊取信息)
port: MAIL_CONFIG.port,
auth: {
user: MAIL_CONFIG.user_email, // 賬號
pass: MAIL_CONFIG.auth_code, // 授權(quán)碼
},
});
const htmlModel = ({ storyMail, exitInfo, summitUser, iterationMail }) => {
const html = nunjucks.render(path.join(__dirname, "./emailTpl/email.njk"), {
storyMail,
exitInfo,
summitUser,
iterationMail,
});
return html;
};
/*
* toEmail: String 接收者,可以同時發(fā)送多個,以逗號隔開
* subject: String 標(biāo)題
* cc: String 抄送
* text: String 文本
* html: Object titleList表頭 conterFontList內(nèi)容
* attachments: any 附件
* [
* {
filename: 'img1.png', // 改成你的附件名
path: 'public/images/img1.png', // 改成你的附件路徑
cid : '00000001' // cid可被郵件使用
}
* ]
*/
interface mailInterface {
toEmail: string;
subject: string;
cc?: string;
text?: string;
html?: any;
attachments?: any;
storyMail?: any;
exitInfo?: any;
summitUser?: String;
iterationMail?: any;
}
const sendMail = async (mailOptions: mailInterface) => {
const {
toEmail,
subject,
cc,
text,
attachments,
storyMail,
exitInfo,
summitUser,
iterationMail,
} = mailOptions;
Object.keys(exitInfo).forEach((key) => {
exitInfo[key] = marked(exitInfo[key]);
});
const html = htmlModel({ storyMail, exitInfo, summitUser, iterationMail });
const mailOpts = {
from: MAIL_CONFIG.user_email, // 發(fā)送者,與上面的 user 一致
to: toEmail,
subject,
cc,
text,
html,
attachments,
};
try {
transporter.sendMail(mailOpts);
return true;
} catch (err) {
console.log(err);
return false;
}
};
export default { sendMail };
釘釘群機器人
具體參考釘釘機器人文檔下面附帶具體的實現(xiàn)代碼(為了安全且簡單,采用加簽的安全驗證)
const crypto = require("crypto");
const secret ="";
const sendUrl =""; // 替換成自己的
export default (app) => {
return {
async send(content) {
const timestamp = Date.now();
const str = crypto
.createHmac("sha256", secret)
.update(timestamp + "\n" + secret)
.digest()
.toString("base64", "UTF-8");
try {
const { res, data } = await app.curl(
`${sendUrl}×tamp=${timestamp}&sign=${encodeURIComponent(str)}`,
{
headers: {
"Content-Type": "application/json; charset=utf-8",
},
method: "POST",
data: JSON.stringify(content),
}
);
return res;
} catch (error) {
return error;
}
},
text({ content = {}, at }) {
console.log("content===>", content);
at = at || {};
this.send({
msgtype: "text",
text: {
content,
},
at,
});
},
};
};
// 測試機器人 Controller
import { Post, Prefix, Get } from "egg-shell-decorators";
import BaseController from "./base";
@Prefix("robot")
export default class ProjectController extends BaseController {
@Post("/ding")
public async getProjectList({
request: {
body: { params },
},
}) {
const { ctx } = this;
const { content } = params;
await ctx.helper.robot.ding.text({ content });
this.success({});
}
}
上述只附帶了 text 文本消息推送,markdown、link、FeedCard 等其他消息類型,照著例子直接上手改就行了
建議
從第一篇看到目前這篇博客的同學(xué),如果團隊缺少合適的項目管理或者想練習(xí) node 的情況下,可以上手試試看,一般關(guān)鍵的代碼,我有直接貼在博客上(大部分復(fù)制就能用?。?。
后面的內(nèi)容就是貼合業(yè)務(wù)直接 curd 代碼,基礎(chǔ)篇到此結(jié)束。
下一篇就會出構(gòu)建篇,團隊可以結(jié)合自己項目實際情況增減功能,完善團隊基礎(chǔ)管理流程。
不明白的地方可以留言
尾聲
此項目是從零開發(fā),后續(xù)此系列博客會根據(jù)實際開發(fā)進度推出(真 TMD 累),項目完成之后,會開放部分源碼供各位同學(xué)參考。
為什么是開放部分源碼,因為有些業(yè)務(wù)是需要貼合實際項目針對性開發(fā)的,開放出去的公共模塊我寫的認真點
為了寫個系列博客,結(jié)果要寫完一整個系統(tǒng)(不是一般的累),覺得不錯的同學(xué)麻煩順手三連(點贊,關(guān)注,轉(zhuǎn)發(fā))。