前言
介紹過了專案流程設計、資料庫搭建、jwt 登入等模組。
此篇我們介紹分支管理設計及其他的基礎模組。
後端模組
DevOps - Gitlab Api使用(已完成,點選跳轉)DevOps - 搭建 DevOps 基礎平臺(已完成 50%)基礎平臺搭建上,點選跳轉DevOps - Gitlab CI 流水線構建DevOps - Jenkins 流水線構建DevOps - Docker 使用DevOps - 釋出任務流程設計DevOps - 程式碼審查卡點DevOps - Node 服務品質監控Git 分支管理流程Git Flow 流程後期可能會根據 DevOps 專案的實際開發進度對上述系列進行調整
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 版本分支上線之後,生成對應 taghotfix 版本可以從對應的 tag 拉出,可以明確的知道 hotfix 具體修復的是哪個版本的問題上述流程的缺點:
固化版本流程導致建立命名規則固定,且版本號不能升級只能降級流程限制,降低開發靈活性沒有完美的解決方法,所有 devops 流程都要結合真實專案需求來設計,上述只是一種解決方案
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-clientwindow.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,後續可以正常請求介面。
尾聲此專案是從零開發,後續此係列部落格會根據實際開發進度推出,專案完成之後,會開放部分原始碼供各位同學參考。
最後,咱給小編: