前言
搭建基礎平臺搭建上篇的時候的時候,已經介紹過了項目流程設計、數據庫搭建、jwt 登錄等模塊。
此篇我們介紹分支管理設計及其他的基礎模塊。
后端模塊
- DevOps - Gitlab Api使用(已完成,點擊跳轉)
- DevOps - 搭建 DevOps 基礎平臺(已完成 50%)基礎平臺搭建上,點擊跳轉
- DevOps - Gitlab CI 流水線構建
- DevOps - Jenkins 流水線構建
- DevOps - Docker 使用
- DevOps - 發布任務流程設計
- DevOps - 代碼審查卡點
- DevOps - Node 服務質量監控
后期可能會根據 DevOps 項目的實際開發進度對上述系列進行調整
Git 分支管理流程
Git Flow 流程

Production 分支
就是常用的 Master 分支,這個分支包含最近發布到生產環境的代碼,最近發布的 Release, 這個分支只能從其他分支合并,不能在這個分支直接修改
Develop 分支
這個分支是的主開發分支,包含所有要發布到下一個Release的代碼,這個主要合并于其他分支,比如 Feature 分支
Feature 分支
這個分支主要是用來開發一個新的功能,一旦開發完成,我們合并回 Develop 分支,并進入下一個 Release
Release 分支
當需要發布一個新 Release 的時候,基于 Develop 分支創建一個 Release 分支,完成 Release 后,合并到 Master 和 Develop 分支
Hotfix 分支
當在 Production 發現新的 Bu g時候,需要創建一個 Hotfix, 完成 Hotfix 后,合并回 Master 和 Develop 分支,所以 Hotfix 的改動會進入下一個 Release
整體的分支管理流程如下圖所示

項目自建流程
上述的 Git Flow 流程使用可以規范約束開發質量跟流程,我們稍微修改一下部分流程,融入到項目中進行使用。


如圖每個工程都共享一個 version 版本號,分支創建分為版本升級、特性更新、修訂補丁三種模式,強制項目所有分支創建的命名規則都會升級,不會出現重復跟降級。

上述流程的優點:
- 工程使用固定的版本鎖死,版本對應需求流程,上線質量得到保障
- 每個開發分支都只能部署到測試環境,必須合并到合并到對應的版本分支之后才能上生產
- 所有合并到 master 或者 relase 分支會被刪除,防止一條分支處理過多業務,后期 review、回滾難度提升
- realse 版本分支上線之后,生成對應 tag
- hotfix 版本可以從對應的 tag 拉出,可以明確的知道 hotfix 具體修復的是哪個版本的問題
上述流程的缺點:
- 固化版本流程導致創建命名規則固定,且版本號不能升級只能降級
- 流程限制,降低開發靈活性
沒有完美的解決方法,所有 devops 流程都要結合真實項目需求來設計,上述只是一種解決方案,有更通用的方案設計請加我微信 Cookieboty 探討
DevOps 開發中篇
添加全局報錯回調
沒有絕對安全的程序,所有程序在運行中因各種情況會出現 error,全局錯誤回調是基礎模塊必要的。
export default class HttpExceptions extends Error { // 繼承修改 error 類型 code: number; msg: string; httpCode: number; constructor({ msg = "服務器異常", code = 1, httpCode = 400 }) { super(); this.msg = msg; this.code = code; this.httpCode = httpCode; }}import HttpExceptions from "../exceptions/http_exceptions"; // 全局攔截錯誤處理export default () => { return async function errorHandler(ctx, next) { try { await next(); } catch (err) { // 所有的異常都在 app 上觸發一個 error 事件,框架會記錄一條錯誤日志 ctx.app.emit("error", err, ctx); let status = err.status || 500; let error: any = {}; if (err instanceof HttpExceptions) { status = err.httpCode; error.requestUrl = `${ctx.method} : ${ctx.path}`; error.msg = err.msg; error.code = err.code; error.httpCode = err.httpCode; } else { // 未知異常,系統異常,線上不顯示堆棧信息 // 生產環境時 500 錯誤的詳細錯誤內容不返回給客戶端,因為可能包含敏感信息 error.code = 500; error.errsInfo = status === 500 && ctx.app.config.env === "prod" ? "Internal Server Error" : err.message; } // 從 error 對象上讀出各個屬性,設置到響應中 ctx.body = error; if (status === 422) { ctx.body.detail = err.errors; } ctx.status = status; } };};
如上,我們拓展默認錯誤類,添加錯誤中間件攔截全局異常,如果出現自定義異常拋出的時候,則處理全局異常,否則統一拋出 500 錯誤,去除敏感信息。
webSocket 使用
為什么要使用 webSocket
項目管理中,會涉及到同一個項目多人協作操作,而 ajax 輪訓既消耗性能,實時性也不能完全保證,也會推送大量無效信息。所以項目采用 websocket 來推送多人協作信息以及后期構建流程的狀態推送。
egg-socket
框架提供了 egg-socket.io 插件,增加了以下開發規約:
- namespace: 通過配置的方式定義 namespace(命名空間)
- middleware: 對每一次 socket 連接的建立/斷開、每一次消息/數據傳遞進行預處理
- controller: 響應 socket.io 的 event 事件
- router: 統一了 socket.io 的 event 與 框架路由的處理配置方式。
具體的使用方式請參考:egg-socket.io 使用,下面簡單說下 ts 的配置
import { Application } from "egg"; // io路由使用方式import { EggShell } from "egg-shell-decorators";export default (app: Application) => { const { router, controller, io } = app; EggShell(app); // socket.io io.of('/').route('server', io.controller.nsp.ping);};
ts 使用中 io.controller.nsp 會報類型未定義,所以需要修改一下 typings/index.d.ts 文件。
import "egg";declare module "egg" { interface Application { } interface CustomController { nsp: any; } interface EggSocketNameSpace { emit: any }}
socket.io-client
window.onload = function () { // init const socket = io('http://127.0.0.1:7001', { // 實際使用中可以在這里傳遞參數 query: { room: 'nsp', userId: `client_${Math.random()}`, }, transports: ['websocket'], }); socket.on('connect', () => { const id = socket.id; log('#connect,', id, socket); // 監聽自身 id 以實現 p2p 通訊 socket.on(id, (msg: any) => { log('#receive,', msg); }); }); // 接收在線用戶信息 socket.on('online', (msg: any) => { log('#online,', msg); }); // 系統事件 socket.on('disconnect', (msg: any) => { log('#disconnect', msg); }); socket.on('disconnecting', () => { log('#disconnecting'); }); socket.on('error', () => { log('#error'); }); window.socket = socket;};
客服端采用 socket.io-client 去鏈接 websocket。上述是基礎鏈接部分,具體的實現要根據業務需求開發。
客服端實現
為了保障項目開發速度,客戶端選擇了 ANT DESIGN PRO。具體安裝步驟請參考教程,這邊展示一下部分業務端的代碼。

JWT 前端使用
/** * 異常處理程序 */const errorHandler = (error: { response: Response }): Response => { const { response } = error; if (response && response.status) { const errorText = codeMessage[response.status] || response.statusText; const { status, url } = response; if (response.status === 401) { window.location.href = '/user/login'; } notification.error({ message: `請求錯誤 ${status}: ${url}`, description: errorText, }); } else if (!response) { notification.error({ description: '您的網絡發生異常,無法連接服務器', message: '網絡異常', }); } return response;};/** * 配置request請求時的默認參數 */const request = extend({ prefix: '/api', errorHandler, // 默認錯誤處理 credentials: 'include', // 默認請求是否帶上cookie headers: { authorization: localStorage.getItem('authorization'), // 讀取本地保存的 authorization token },});export default request;
改造 request 模塊
import request from '@/utils/request';export interface LoginParamsType { username: string; password: string; mobile: string; captcha: string;}export async function fakeAccountLogin(params: LoginParamsType) { return request('/user/getUserToken', { getResponse: true, // 開啟可以拿到返回 header 參數,將對應的 authorization token 存入本地使用 method: 'POST', data: { params }, });}
如上,拿到 response header 里面的 token,后續可以正常請求接口。
尾聲
此項目是從零開發,后續此系列博客會根據實際開發進度推出(真 TMD 累),項目完成之后,會開放部分源碼供各位同學參考。
如對文章內容有任何疑問、見解可添加微信 Cookieboty 溝通。
另外關注公眾號 Cookieboty1024,歡迎加入前端小兵成長營