从 0 到 1 node 项目管理系统:搭建基础平台(下)
前言
基础平台搭建上篇 介绍项目流程设计、数据库搭建、jwt 登录等模块
基础平台搭建中篇 介绍分支管理设计、webSocket 基础模块
本篇下将介绍流程管理与提测相关基础模块
后端模块
- DevOps - Gitlab Api使用(已完成,点击跳转)
- DevOps - 搭建 DevOps 基础平台(已完成 70%)
- DevOps - Gitlab CI 流水线构建
- DevOps - Jenkins 流水线构建
- DevOps - Docker 使用
- DevOps - 发布任务流程设计
- DevOps - 代码审查卡点
- DevOps - Node 服务质量监控
前端模块
- DevOps - H5 基础脚手架
- DevOps - React 项目开发
后期可能会根据 DevOps 项目的实际开发进度对上述系列进行调整
流程与提测管理
流程管理
在基础平台搭建上篇已经介绍过流程的设计,这里再简单解释下
- 开发同学创建对应的工程以及分支,进行功能开发
- 项目负责人创建流程时,关联多个开发分支,附加需求(需求模块简化成 desc 字段描述,没有单独抽出去)
- 流程的状态由关联的分支状态组合,当所关联所有的开发分支状态全部转变为已完成的时候,才会进入下一个状态
整个项目管理,应该拆解成项目->需求->工程,预留字段,将需求跟流程直接合并在一起,先完成主要功能,后期再进一步的拓展
提测管理
- 开发人员在开发完对应功能进行项目提测
- 未关联流程的分支不能进行提测
- 提测之后,测试同学介入测试,根据 desc (需求)进行测试
- 开发内容再提测之后,才能发布到预发或生产,否则只能在测试环境发布(禁止未测试的需求直接上线)
不要嫌麻烦,现实中,产品随便提个需求就上,出现问题到处甩锅的情况还少吗?严格卡关也是减轻工作量的一个小助力
DevOps 开发下篇
创建流程模块
import { Post, Prefix, Get } from "egg-shell-decorators";
import BaseController from "./base";
@Prefix("process")
export default class ProcessController extends BaseController {
/**
* @author: Cookie
* @description: 创建 devOps 任务流
*/
@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 任务流
*/
@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),
});
// 联表查询分支信息
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: 创建提测记录
*/
@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: "存在未关联流程的分支",
});
const status = await ctx.service.testRecord.createTestRecord({
desc,
name,
branchIds,
submitUserId,
testUserId,
testStatus: 0,
});
this.success(status);
}
}
提测消息推送采用邮件(正式)与机器人(即时),提测内容、次数、质量等写入数据库,系统本身也能追踪,作为后期效能评估的辅助
邮件推送
提测模块的具体实现代码,我们分为 3 块
- 发送邮件使用 nodemailer
- 邮件模板使用 nunjucks 模板引擎,配置邮件模板
- 邮件前端自定义内容使用 marked 插件解析 markdown 语法
import { MAIL_CONFIG } from "../../config/default.config";
const marked = require("marked"); // marked 转换
const nodemailer = require("nodemailer"); // 发送邮件
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, // 授权码
},
});
const htmlModel = ({ storyMail, exitInfo, summitUser, iterationMail }) => {
const html = nunjucks.render(path.join(__dirname, "./emailTpl/email.njk"), {
storyMail,
exitInfo,
summitUser,
iterationMail,
});
return html;
};
/*
* toEmail: String 接收者,可以同时发送多个,以逗号隔开
* subject: String 标题
* cc: String 抄送
* text: String 文本
* html: Object titleList表头 conterFontList内容
* 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, // 发送者,与上面的 user 一致
to: toEmail,
subject,
cc,
text,
html,
attachments,
};
try {
transporter.sendMail(mailOpts);
return true;
} catch (err) {
console.log(err);
return false;
}
};
export default { sendMail };
钉钉群机器人
具体参考钉钉机器人文档下面附带具体的实现代码(为了安全且简单,采用加签的安全验证)
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 等其他消息类型,照着例子直接上手改就行了
建议
从第一篇看到目前这篇博客的同学,如果团队缺少合适的项目管理或者想练习 node 的情况下,可以上手试试看,一般关键的代码,我有直接贴在博客上(大部分复制就能用啊)。
后面的内容就是贴合业务直接 curd 代码,基础篇到此结束。
下一篇就会出构建篇,团队可以结合自己项目实际情况增减功能,完善团队基础管理流程。
不明白的地方可以留言
尾声
此项目是从零开发,后续此系列博客会根据实际开发进度推出(真 TMD 累),项目完成之后,会开放部分源码供各位同学参考。
为什么是开放部分源码,因为有些业务是需要贴合实际项目针对性开发的,开放出去的公共模块我写的认真点
为了写个系列博客,结果要写完一整个系统(不是一般的累),觉得不错的同学麻烦顺手三连(点赞,关注,转发)。
如对文章内容有任何疑问、见解可添加微信 沟通。
另外关注公众号 Cookieboty1024,欢迎加入前端小兵成长营
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android 获取屏幕的多种宽高信息的示例代码
- Android编程实现禁止StatusBar下拉的方法
- Android自定义view圆并随手指移动
- Android仿微信发送语音消息的功能及示例代码
- 详解Android studio ndk配置cmake开发native C
- Android编程实现禁止状态栏下拉的方法详解
- Android进度条ProgressBar的实现代码
- Android画画板的制作方法
- Android实现bitmap指定区域滑动截取功能
- Android开发实现应用层面屏蔽状态栏的方法小结
- Android实现实时搜索框功能
- 浅谈Android轻量级的数据缓存框架RxCache
- Android开发实现消除屏幕锁的方法
- Android中js和原生交互的示例代码
- 浅谈android获取设备唯一标识完美解决方案