chore: 更新lint-staged和commitlint配置
All checks were successful
CI Monitor CI/CD / build-image (push) Successful in 33s
CI Monitor CI/CD / deploy (push) Successful in 37s

This commit is contained in:
zhaoyingbo 2024-07-25 01:09:24 +00:00
parent a4555ac862
commit 18a95387ee
40 changed files with 499 additions and 491 deletions

1
.husky/commit-msg Normal file
View File

@ -0,0 +1 @@
npx --no -- commitlint --edit $1

1
.husky/pre-commit Normal file
View File

@ -0,0 +1 @@
lint-staged

BIN
bun.lockb

Binary file not shown.

1
commitlint.config.js Normal file
View File

@ -0,0 +1 @@
export default { extends: ["@commitlint/config-conventional"] }

View File

@ -1,63 +1,63 @@
import moment from "moment";
import moment from "moment"
import db from "../../db";
import service from "../../service";
import { DB } from "../../types/db";
import { Gitlab } from "../../types/gitlab";
import db from "../../db"
import service from "../../service"
import { DB } from "../../types/db"
import { Gitlab } from "../../types/gitlab"
/**
* pipeline列表
*/
const getFullPipelineList = async (project: DB.Project) => {
// 先获取最新的pipelineID
const latestOne = await db.pipeline.getLatestOne(project.id);
const latestOne = await db.pipeline.getLatestOne(project.id)
// 获取本次数据获取的截止时间如果没有则获取从20240101到现在所有pipeline信息
const latestTime = moment(
latestOne?.created_at || "2024-01-01T00:00:00.000+08:00"
);
)
// 获取pipeline列表并保存
const fullPipelineList: Gitlab.Pipeline[] = [];
let page = 1;
let hasBeforeLatestTime = false;
const fullPipelineList: Gitlab.Pipeline[] = []
let page = 1
let hasBeforeLatestTime = false
while (!hasBeforeLatestTime) {
const pipelines = await service.gitlab.pipeline.getList(
project.project_id,
page++
);
)
// 如果当前页没有数据,则直接跳出
if (pipelines.length === 0) break;
if (pipelines.length === 0) break
pipelines.forEach((pipeline) => {
// 如果已经有了比最新的pipeline还要早的pipeline则跳出
if (hasBeforeLatestTime) return;
if (hasBeforeLatestTime) return
if (moment(pipeline.created_at).isSameOrBefore(latestTime)) {
hasBeforeLatestTime = true;
hasBeforeLatestTime = true
} else {
fullPipelineList.push(pipeline);
fullPipelineList.push(pipeline)
}
});
})
}
const fullPipelineDetailList = await Promise.all(
fullPipelineList.map(({ project_id, id, created_at }) =>
service.gitlab.pipeline.getDetail(project_id, id, created_at)
)
);
)
return fullPipelineDetailList.filter((v) => v) as (Gitlab.PipelineDetail & {
created_at: string;
})[];
};
created_at: string
})[]
}
const insertFullPipelineList = async (
fullPipelineList: (Gitlab.PipelineDetail & { created_at: string })[][],
fullUserMap: Record<string, string>,
fullProjectMap: Record<string, string>
) => {
const dbPipelineList: Partial<DB.Pipeline> = [];
const dbPipelineList: Partial<DB.Pipeline> = []
fullPipelineList.forEach((pipelineList) => {
pipelineList.forEach((pipeline) => {
// 如果没有时间信息,则跳过
if (!pipeline.created_at || !pipeline.started_at || !pipeline.finished_at)
return;
return
dbPipelineList.push({
project_id: fullProjectMap[pipeline.project_id],
user_id: fullUserMap[pipeline.user.id],
@ -70,17 +70,17 @@ const insertFullPipelineList = async (
finished_at: pipeline.finished_at,
duration: pipeline.duration,
queued_duration: pipeline.queued_duration,
});
});
});
})
})
})
await Promise.all(
dbPipelineList.map((v: Partial<DB.Pipeline>) => db.pipeline.create(v))
);
};
)
}
const managePipeline = {
getFullPipelineList,
insertFullPipelineList,
};
}
export default managePipeline;
export default managePipeline

View File

@ -1,14 +1,14 @@
import db from "../../db";
import service from "../../service";
import { DB } from "../../types/db";
import db from "../../db"
import service from "../../service"
import { DB } from "../../types/db"
/**
*
*/
const fillProj = async (project: DB.Project) => {
const projDetail = await service.gitlab.project.getDetail(project.project_id);
const projDetail = await service.gitlab.project.getDetail(project.project_id)
if (!projDetail) {
return project;
return project
}
const useFulParams: Partial<DB.Project> = {
...project,
@ -17,38 +17,38 @@ const fillProj = async (project: DB.Project) => {
name: projDetail.name,
path_with_namespace: projDetail.path_with_namespace,
web_url: projDetail.web_url,
};
return await db.project.update(project.id, useFulParams);
};
}
return await db.project.update(project.id, useFulParams)
}
/**
*
* fillProj填充内容
*/
const getFullProjList = async () => {
const fullList = await db.project.getFullList();
const fullList = await db.project.getFullList()
// 把信息不全的项目送过去填充
const filledProjList = await Promise.all(
fullList.filter((v) => !v.name).map((item) => fillProj(item))
);
)
// 合并成完整数据
const filledFullProjList = fullList
.filter((v) => v.name)
.concat(filledProjList);
return filledFullProjList;
};
.concat(filledProjList)
return filledFullProjList
}
const getFullProjectMap = (fullProjList: DB.Project[]) => {
const fullProjectMap: Record<string, string> = {};
const fullProjectMap: Record<string, string> = {}
fullProjList.forEach((item) => {
fullProjectMap[item.project_id] = item.id;
});
return fullProjectMap;
};
fullProjectMap[item.project_id] = item.id
})
return fullProjectMap
}
const manageProject = {
getFullProjList,
getFullProjectMap,
};
}
export default manageProject;
export default manageProject

View File

@ -1,37 +1,34 @@
import db from "../../db";
import service from "../../service";
import { calculateWeeklyRate } from "../../utils/robotTools";
import {
getPrevWeekWithYear,
getWeekTimeWithYear,
} from "../../utils/timeTools";
import db from "../../db"
import service from "../../service"
import { calculateWeeklyRate } from "../../utils/robotTools"
import { getPrevWeekWithYear, getWeekTimeWithYear } from "../../utils/timeTools"
const getNewCicdStatus = async () => {
const fullProjList = await db.project.getFullList();
const fullProjList = await db.project.getFullList()
const has_new_cicd_count = String(
fullProjList.filter((item) => {
return item.has_new_cicd === true;
return item.has_new_cicd === true
}).length
);
)
const without_new_cicd_count = String(
fullProjList.filter((item) => {
return item.has_new_cicd === false;
return item.has_new_cicd === false
}).length
);
)
return {
has_new_cicd_count,
without_new_cicd_count,
};
};
}
}
const getStatisticsInfo = async () => {
const curWeekInfo = await db.view.getFullStatisticsByWeek(
getWeekTimeWithYear()
);
)
const prevWeekInfo = await db.view.getFullStatisticsByWeek(
getPrevWeekWithYear()
);
if (!curWeekInfo || !prevWeekInfo) return {};
)
if (!curWeekInfo || !prevWeekInfo) return {}
return {
total_count: String(curWeekInfo?.total_count ?? 0),
weekly_count_rate: calculateWeeklyRate(
@ -48,37 +45,37 @@ const getStatisticsInfo = async () => {
curWeekInfo?.success_rate,
prevWeekInfo?.success_rate
).text,
};
};
}
}
const getProjDiffInfo = async () => {
const curWeekInfo =
(await db.view.getProjStatisticsByWeek(getWeekTimeWithYear())) || [];
(await db.view.getProjStatisticsByWeek(getWeekTimeWithYear())) || []
const prevWeekInfo =
(await db.view.getProjStatisticsByWeek(getPrevWeekWithYear())) || [];
(await db.view.getProjStatisticsByWeek(getPrevWeekWithYear())) || []
const group: {
project_name: string;
project_ref: string;
project_duration: string;
project_duration_rate: string;
percentage: string;
}[] = [];
project_name: string
project_ref: string
project_duration: string
project_duration_rate: string
percentage: string
}[] = []
curWeekInfo.forEach((curWeekProjInfo) => {
const prevWeekProjInfo = prevWeekInfo.find(
(info) =>
info.ref === curWeekProjInfo.ref && info.name === curWeekProjInfo.name
);
if (!prevWeekProjInfo) return;
)
if (!prevWeekProjInfo) return
const { text: project_duration_rate, percentage } = calculateWeeklyRate(
curWeekProjInfo.duration,
prevWeekProjInfo.duration,
false
);
)
if (percentage === "0") return;
if (percentage === "0") return
group.push({
project_name: curWeekProjInfo.name,
@ -86,15 +83,15 @@ const getProjDiffInfo = async () => {
project_duration: String(curWeekProjInfo.duration),
project_duration_rate,
percentage,
});
});
})
})
// 排序
group.sort((a, b) => Number(b.percentage) - Number(a.percentage));
group.sort((a, b) => Number(b.percentage) - Number(a.percentage))
// 取前五个
return group.slice(0, 5);
};
return group.slice(0, 5)
}
/**
*
@ -114,26 +111,26 @@ const getRobotMsg = async () =>
group_table: await getProjDiffInfo(),
},
},
});
})
/**
* ChatID发送CI报告
* @param chat_id
*/
const sendCIReportByChatId = async (chat_id: string) => {
await service.message.byChatId(chat_id, await getRobotMsg());
};
await service.message.byChatId(chat_id, await getRobotMsg())
}
/**
* CI报告
* @returns
*/
const sendCIReportByCron = async () =>
await service.message.byGroupId("52usf3w8l6z4vs1", await getRobotMsg());
await service.message.byGroupId("52usf3w8l6z4vs1", await getRobotMsg())
const manageRobot = {
sendCIReportByChatId,
sendCIReportByCron,
};
}
export default manageRobot;
export default manageRobot

View File

@ -1,15 +1,15 @@
import db from "../../db";
import { Gitlab } from "../../types/gitlab";
import db from "../../db"
import { Gitlab } from "../../types/gitlab"
const getFullUserMap = async (fullPipelineList: Gitlab.PipelineDetail[][]) => {
const userList: Gitlab.User[] = [];
const userList: Gitlab.User[] = []
fullPipelineList.forEach((fullPipeline) => {
fullPipeline.forEach((item) => {
if (item.user && !userList.find((v) => v.id === item.user?.id)) {
userList.push(item.user);
userList.push(item.user)
}
});
});
})
})
const dbUserInfo = await Promise.all(
userList
@ -23,17 +23,17 @@ const getFullUserMap = async (fullPipelineList: Gitlab.PipelineDetail[][]) => {
web_url: user.web_url,
})
)
);
const userMap: Record<string, string> = {};
)
const userMap: Record<string, string> = {}
dbUserInfo.forEach((item) => {
if (!item) return;
userMap[item.user_id] = item.id;
});
return userMap;
};
if (!item) return
userMap[item.user_id] = item.id
})
return userMap
}
const manageUser = {
getFullUserMap,
};
}
export default manageUser;
export default manageUser

View File

@ -1,13 +1,13 @@
import pipeline from "./pipeline";
import project from "./project";
import user from "./user";
import view from "./view";
import pipeline from "./pipeline"
import project from "./project"
import user from "./user"
import view from "./view"
const db = {
project,
pipeline,
user,
view,
};
}
export default db;
export default db

View File

@ -1,7 +1,7 @@
import PocketBase from "pocketbase";
import PocketBase from "pocketbase"
const pbClient = new PocketBase("https://ci-pb.xiaomiwh.cn");
const pbClient = new PocketBase("https://ci-pb.xiaomiwh.cn")
pbClient.autoCancellation(false);
pbClient.autoCancellation(false)
export default pbClient;
export default pbClient

View File

@ -1,6 +1,6 @@
import { DB } from "../../types/db";
import { managePb404 } from "../../utils/pbTools";
import pbClient from "../pbClient";
import { DB } from "../../types/db"
import { managePb404 } from "../../utils/pbTools"
import pbClient from "../pbClient"
/**
* ID
@ -10,7 +10,7 @@ import pbClient from "../pbClient";
const getOne = (id: string) =>
managePb404<DB.Pipeline>(
async () => await pbClient.collection("pipeline").getOne(id)
);
)
/**
*
@ -25,8 +25,8 @@ const getLatestOne = (project_id: string) => {
.getFirstListItem(`project_id="${project_id}"`, {
sort: "-created_at",
})
);
};
)
}
/**
*
@ -34,12 +34,12 @@ const getLatestOne = (project_id: string) => {
* @returns promise
*/
const create = async (data: Partial<DB.Pipeline>) =>
await pbClient.collection("pipeline").create<DB.Pipeline>(data);
await pbClient.collection("pipeline").create<DB.Pipeline>(data)
const pipeline = {
create,
getOne,
getLatestOne,
};
}
export default pipeline;
export default pipeline

View File

@ -1,6 +1,6 @@
import { DB } from "../../types/db";
import { managePb404 } from "../../utils/pbTools";
import pbClient from "../pbClient";
import { DB } from "../../types/db"
import { managePb404 } from "../../utils/pbTools"
import pbClient from "../pbClient"
/**
* ID
@ -10,14 +10,14 @@ import pbClient from "../pbClient";
const getOne = (id: string) =>
managePb404<DB.Project>(
async () => await pbClient.collection("project").getOne(id)
);
)
/**
*
* @returns promise
*/
const getFullList = async () =>
await pbClient.collection("project").getFullList<DB.Project>();
await pbClient.collection("project").getFullList<DB.Project>()
/**
* 使
@ -26,7 +26,7 @@ const getFullList = async () =>
* @returns promise
*/
const update = async (id: string, data: Partial<DB.Project>) =>
await pbClient.collection("project").update<DB.Project>(id, data);
await pbClient.collection("project").update<DB.Project>(id, data)
/**
*
@ -35,6 +35,6 @@ const project = {
getFullList,
getOne,
update,
};
}
export default project;
export default project

View File

@ -1,6 +1,6 @@
import { DB } from "../../types/db";
import { managePb404 } from "../../utils/pbTools";
import pbClient from "../pbClient";
import { DB } from "../../types/db"
import { managePb404 } from "../../utils/pbTools"
import pbClient from "../pbClient"
/**
* ID从数据库检索单个用户
@ -8,9 +8,7 @@ import pbClient from "../pbClient";
* @returns promise404
*/
const getOne = (id: string) =>
managePb404<DB.User>(
async () => await pbClient.collection("user").getOne(id)
);
managePb404<DB.User>(async () => await pbClient.collection("user").getOne(id))
/**
* ID从数据库检索单个用户
@ -25,8 +23,8 @@ const getOneByUserId = (user_id: number) => {
.getFirstListItem(`user_id="${user_id}"`, {
sort: "-created",
})
);
};
)
}
/**
*
@ -34,7 +32,7 @@ const getOneByUserId = (user_id: number) => {
* @returns promise
*/
const create = async (data: Partial<DB.User>) =>
await pbClient.collection("user").create<DB.User>(data);
await pbClient.collection("user").create<DB.User>(data)
/**
*
@ -45,11 +43,11 @@ const create = async (data: Partial<DB.User>) =>
* IDnull
*/
const upsert = async (data: Partial<DB.User>) => {
if (!data.user_id) return null;
const userInfo = await getOneByUserId(data.user_id);
if (userInfo) return userInfo;
return await create(data);
};
if (!data.user_id) return null
const userInfo = await getOneByUserId(data.user_id)
if (userInfo) return userInfo
return await create(data)
}
/**
*
@ -59,6 +57,6 @@ const user = {
upsert,
getOne,
getOneByUserId,
};
}
export default user;
export default user

View File

@ -1,6 +1,6 @@
import { DB } from "../../types/db";
import { managePb404 } from "../../utils/pbTools";
import pbClient from "../pbClient";
import { DB } from "../../types/db"
import { managePb404 } from "../../utils/pbTools"
import pbClient from "../pbClient"
/**
*
@ -13,8 +13,8 @@ const getFullStatisticsByWeek = (week: string) => {
await pbClient
.collection("statisticsPerWeek")
.getFirstListItem(`week="${week}"`)
);
};
)
}
/**
*
@ -27,12 +27,12 @@ const getProjStatisticsByWeek = (week: string) => {
await pbClient
.collection("statisticsPerProj")
.getFullList({ filter: `week="${week}"` })
);
};
)
}
const view = {
getFullStatisticsByWeek,
getProjStatisticsByWeek,
};
}
export default view;
export default view

View File

@ -1,7 +1,7 @@
import pluginJs from "@eslint/js";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import globals from "globals";
import tseslint from "typescript-eslint";
import pluginJs from "@eslint/js"
import simpleImportSort from "eslint-plugin-simple-import-sort"
import globals from "globals"
import tseslint from "typescript-eslint"
export default [
{ files: ["**/*.{js,mjs,cjs,ts}"] },
@ -19,4 +19,4 @@ export default [
"simple-import-sort/exports": "error",
},
},
];
]

View File

@ -1,27 +1,27 @@
import { manageCIMonitorReq } from "./routes/ci";
import initSchedule from "./schedule";
import netTool from "./service/netTool";
import { manageCIMonitorReq } from "./routes/ci"
import initSchedule from "./schedule"
import netTool from "./service/netTool"
// 启动定时任务
initSchedule();
initSchedule()
const server = Bun.serve({
async fetch(req) {
try {
const url = new URL(req.url);
const url = new URL(req.url)
// 根路由
if (url.pathname === "/") return netTool.ok("hello, glade to see you!");
if (url.pathname === "/") return netTool.ok("hello, glade to see you!")
// CI 监控
if (url.pathname === "/ci") return manageCIMonitorReq(req);
if (url.pathname === "/ci") return manageCIMonitorReq(req)
// 其他
return netTool.ok("hello, glade to see you!");
return netTool.ok("hello, glade to see you!")
} catch (error: any) {
// 错误处理
console.error("🚀 ~ serve ~ error", error);
return netTool.serverError(error.message || "server error");
console.error("🚀 ~ serve ~ error", error)
return netTool.serverError(error.message || "server error")
}
},
port: 3000,
});
})
console.log(`Listening on ${server.hostname}:${server.port}`);
console.log(`Listening on ${server.hostname}:${server.port}`)

View File

@ -4,9 +4,20 @@
"type": "module",
"scripts": {
"start": "bun run index.ts",
"lint": "eslint --fix ."
"lint": "eslint --fix .",
"prepare": "husky",
"prettier": "prettier --write ."
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write",
"git add"
]
},
"devDependencies": {
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@eslint/js": "^9.7.0",
"@types/lodash": "^4.14.202",
"@types/node-schedule": "^2.1.6",
@ -14,6 +25,9 @@
"eslint": "9.x",
"eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^15.8.0",
"husky": "^9.1.1",
"lint-staged": "^15.2.7",
"prettier": "^3.3.3",
"typescript-eslint": "^7.17.0"
},
"peerDependencies": {

8
prettier.config.js Normal file
View File

@ -0,0 +1,8 @@
const config = {
trailingComma: "es5",
tabWidth: 2,
semi: false,
singleQuote: false,
}
export default config

View File

@ -1,5 +1,5 @@
import manageRobot from "../../controllers/manageRobot";
import netTool from "../../service/netTool";
import manageRobot from "../../controllers/manageRobot"
import netTool from "../../service/netTool"
/**
* CI监视器的请求
@ -7,16 +7,16 @@ import netTool from "../../service/netTool";
* @returns
*/
export const manageCIMonitorReq = (req: Request) => {
const url = new URL(req.url);
const url = new URL(req.url)
if (url.pathname === "/ci") {
const chat_id = url.searchParams.get("chat_id");
if (!chat_id) return netTool.badRequest("chat_id is required!");
manageRobot.sendCIReportByChatId(chat_id);
const chat_id = url.searchParams.get("chat_id")
if (!chat_id) return netTool.badRequest("chat_id is required!")
manageRobot.sendCIReportByChatId(chat_id)
return Response.json({
code: 0,
msg: "success",
data: "reporting...",
});
})
}
return netTool.ok();
};
return netTool.ok()
}

View File

@ -1,15 +1,15 @@
import { scheduleJob } from "node-schedule";
import { scheduleJob } from "node-schedule"
import manageRobot from "../controllers/manageRobot";
import syncPipLine from "./syncPipLine";
import manageRobot from "../controllers/manageRobot"
import syncPipLine from "./syncPipLine"
const initSchedule = async () => {
// 每周五早上10点发送CI报告
scheduleJob("0 10 * * 5", manageRobot.sendCIReportByCron);
scheduleJob("0 10 * * 5", manageRobot.sendCIReportByCron)
// 每15分钟同步一次CI数据
scheduleJob("*/15 * * * *", syncPipLine);
scheduleJob("*/15 * * * *", syncPipLine)
// 立即同步一次
syncPipLine();
syncPipLine()
}
export default initSchedule;
export default initSchedule

View File

@ -1,19 +1,19 @@
import managePipeline from "../controllers/managePipeLine";
import manageProject from "../controllers/manageProject";
import manageUser from "../controllers/manageUser";
import managePipeline from "../controllers/managePipeLine"
import manageProject from "../controllers/manageProject"
import manageUser from "../controllers/manageUser"
const syncPipLine = async () => {
const fullProjList = await manageProject.getFullProjList();
const fullProjList = await manageProject.getFullProjList()
const fullPipelineList = await Promise.all(
fullProjList.map((v) => managePipeline.getFullPipelineList(v))
);
const fullUserMap = await manageUser.getFullUserMap(fullPipelineList);
const fullProjectMap = await manageProject.getFullProjectMap(fullProjList);
)
const fullUserMap = await manageUser.getFullUserMap(fullPipelineList)
const fullProjectMap = await manageProject.getFullProjectMap(fullProjList)
await managePipeline.insertFullPipelineList(
fullPipelineList,
fullUserMap,
fullProjectMap
);
};
)
}
export default syncPipLine;
export default syncPipLine

View File

@ -1,6 +1,6 @@
import service from "../../service";
import { BadgeSetParams } from "../../service/gitlab/badge";
import { Gitlab } from "../../types/gitlab";
import service from "../../service"
import { BadgeSetParams } from "../../service/gitlab/badge"
import { Gitlab } from "../../types/gitlab"
const projectList = [
// "cloud-ml/cloudml-maas",
@ -22,39 +22,39 @@ const projectList = [
// "cloud-ml/knowledge-base",
// "cloud-ml/ai-proxy",
"cloud-ml/lark_auth",
];
]
const getNewProjectBadge = async (
projectDetail: Gitlab.ProjDetail
): Promise<BadgeSetParams[]> => {
// 项目路径 cloud-ml/cloudml-dev
const projectPath = projectDetail.path_with_namespace;
const projectPath = projectDetail.path_with_namespace
// 根据项目路径获取sonarqubeId 类似于 cloud-ml/cloudml-dev -> cloud-ml:cloudml-dev
const sonarqubeId = projectPath.replace("/", ":");
const sonarqubeId = projectPath.replace("/", ":")
// 获取项目的badges
const badges: Gitlab.Badge[] = await service.gitlab.badge.get(
projectDetail.id
);
)
// 对badges进行补全可能只有 name: "sonarqube_coverage" 的情况,把剩下的补全
const badgeNames = badges.map((badge) => badge.name);
const badgeNameSet = new Set(badgeNames);
const badgeNames = badges.map((badge) => badge.name)
const badgeNameSet = new Set(badgeNames)
const badgeNameList = [
"sonarqube_bugs",
"sonarqube_vulnerabilities",
"sonarqube_security_hotspots",
"sonarqube_coverage",
"sonarqube_quality_gate",
];
const diff = [...badgeNameList].filter((x) => !badgeNameSet.has(x));
]
const diff = [...badgeNameList].filter((x) => !badgeNameSet.has(x))
const newBadges: BadgeSetParams[] = diff.map((name) => {
const link_url = encodeURI(
`https://sonarqube.mioffice.cn/dashboard?id=${sonarqubeId}`
);
const metric = name.replace("sonarqube_", "");
)
const metric = name.replace("sonarqube_", "")
const image_url =
name !== "sonarqube_quality_gate"
? `https://sonarqube.mioffice.cn/api/badges/measure?key=${sonarqubeId}&metric=${metric}`
: `https://sonarqube.mioffice.cn/api/badges/gate?key=${sonarqubeId}`;
: `https://sonarqube.mioffice.cn/api/badges/gate?key=${sonarqubeId}`
return {
id: projectDetail.id,
link_url,
@ -62,39 +62,39 @@ const getNewProjectBadge = async (
rendered_image_url: image_url,
rendered_link_url: link_url,
name,
};
});
return newBadges;
};
}
})
return newBadges
}
const addNewProjectBadge = async (badgeSetParamsList: BadgeSetParams[]) => {
const chunkSize = 5; // 每次并发请求的数量
const chunkSize = 5 // 每次并发请求的数量
for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) {
const chunk = badgeSetParamsList.slice(i, i + chunkSize);
const chunk = badgeSetParamsList.slice(i, i + chunkSize)
const res = await Promise.all(
chunk.map((badgeSetParams) => service.gitlab.badge.add(badgeSetParams))
);
console.log(res);
)
console.log(res)
}
};
}
const main = async () => {
const errorList: string[] = [];
const errorList: string[] = []
const badgeAddParamsList = await Promise.all(
projectList.map(async (projectName) => {
const projectDetail = await service.gitlab.project.getDetail(projectName);
const projectDetail = await service.gitlab.project.getDetail(projectName)
if (!projectDetail) {
errorList.push(projectName);
return [];
errorList.push(projectName)
return []
}
return await getNewProjectBadge(projectDetail);
return await getNewProjectBadge(projectDetail)
})
);
)
await addNewProjectBadge(badgeAddParamsList.flat());
};
await addNewProjectBadge(badgeAddParamsList.flat())
}
main();
main()
// [
// {

View File

@ -1,6 +1,6 @@
import service from "../../service";
import { BadgeSetParams } from "../../service/gitlab/badge";
import { Gitlab } from "../../types/gitlab";
import service from "../../service"
import { BadgeSetParams } from "../../service/gitlab/badge"
import { Gitlab } from "../../types/gitlab"
const projectList = [
"miai-fe/fe/ai-admin-fe",
@ -53,23 +53,23 @@ const projectList = [
"miai-fe/fe/ai-schedule-manager-fe",
"miai-fe/fe/ai-voiceprint-fe",
"miai-fe/fe/ai-shortcut-fe",
];
]
const getProjectId = async (projectName: string) => {
const res = await service.gitlab.project.getDetail(projectName);
return res?.id;
};
const res = await service.gitlab.project.getDetail(projectName)
return res?.id
}
const getNewProjectBadge = async (
projectId: number
): Promise<BadgeSetParams[]> => {
const badges: Gitlab.Badge[] = await service.gitlab.badge.get(projectId);
const badges: Gitlab.Badge[] = await service.gitlab.badge.get(projectId)
const replacePath = (value: string) =>
value.replace(
"https://sonarqube.mioffice.cn",
"http://scan.sonarqube.xiaomi.srv"
);
)
return badges.map((badge: Gitlab.Badge) => ({
id: projectId,
@ -78,36 +78,36 @@ const getNewProjectBadge = async (
image_url: badge.image_url,
rendered_image_url: badge.rendered_image_url,
rendered_link_url: replacePath(badge.rendered_link_url),
}));
};
}))
}
const setNewProjectBadge = async (badgeSetParamsList: BadgeSetParams[]) => {
const chunkSize = 5; // 每次并发请求的数量
const chunkSize = 5 // 每次并发请求的数量
for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) {
const chunk = badgeSetParamsList.slice(i, i + chunkSize);
const chunk = badgeSetParamsList.slice(i, i + chunkSize)
const res = await Promise.all(
chunk.map((badgeSetParams) => service.gitlab.badge.set(badgeSetParams))
);
console.log(res);
)
console.log(res)
}
};
}
const main = async () => {
const errorList: string[] = [];
const errorList: string[] = []
const badgeSetParamsList = await Promise.all(
projectList.map(async (projectName) => {
const projectId = await getProjectId(projectName);
const projectId = await getProjectId(projectName)
if (!projectId) {
errorList.push(projectName);
return [];
errorList.push(projectName)
return []
}
return await getNewProjectBadge(projectId);
return await getNewProjectBadge(projectId)
})
);
)
await setNewProjectBadge(badgeSetParamsList.flat());
await setNewProjectBadge(badgeSetParamsList.flat())
console.log("errorList", errorList);
};
console.log("errorList", errorList)
}
main();
main()

View File

@ -9,9 +9,7 @@
"source": "push",
"status": "pending",
"detailed_status": "pending",
"stages": [
"print"
],
"stages": ["print"],
"created_at": "2024-07-23 09:28:44 +0800",
"finished_at": null,
"duration": null,
@ -80,4 +78,4 @@
"environment": null
}
]
}
}

View File

@ -9,9 +9,7 @@
"source": "push",
"status": "running",
"detailed_status": "running",
"stages": [
"print"
],
"stages": ["print"],
"created_at": "2024-07-23 08:57:14 +0800",
"finished_at": null,
"duration": null,
@ -78,9 +76,7 @@
"runner_type": "group_type",
"active": true,
"is_shared": false,
"tags": [
"cloudml-fe-bj"
]
"tags": ["cloudml-fe-bj"]
},
"artifacts_file": {
"filename": null,
@ -89,4 +85,4 @@
"environment": null
}
]
}
}

View File

@ -9,9 +9,7 @@
"source": "push",
"status": "success",
"detailed_status": "passed",
"stages": [
"print"
],
"stages": ["print"],
"created_at": "2024-07-23 08:57:14 +0800",
"finished_at": "2024-07-23 08:57:24 +0800",
"duration": 5,
@ -78,9 +76,7 @@
"runner_type": "group_type",
"active": true,
"is_shared": false,
"tags": [
"cloudml-fe-bj"
]
"tags": ["cloudml-fe-bj"]
},
"artifacts_file": {
"filename": null,
@ -89,4 +85,4 @@
"environment": null
}
]
}
}

View File

@ -1,13 +1,13 @@
import { Gitlab } from "../../types/gitlab";
import netTool from "../netTool";
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
import { Gitlab } from "../../types/gitlab"
import netTool from "../netTool"
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"
/**
* GitLab
*/
export type BadgeSetParams = Omit<Gitlab.Badge, "kind" | "name"> & {
badge_id?: number;
};
badge_id?: number
}
/**
* GitLab
@ -15,12 +15,12 @@ export type BadgeSetParams = Omit<Gitlab.Badge, "kind" | "name"> & {
* @returns GitLab
*/
const get = async (project_id: number) => {
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/badges`;
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/badges`
return gitlabReqWarp<Gitlab.Badge[]>(
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
[]
);
};
)
}
/**
* GitLab
@ -28,12 +28,12 @@ const get = async (project_id: number) => {
* @returns
*/
const set = async (badge: BadgeSetParams) => {
const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges/${badge.badge_id}`;
const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges/${badge.badge_id}`
return gitlabReqWarp<Gitlab.Badge>(
() => netTool.put(URL, badge, {}, GITLAB_AUTH_HEADER),
null
);
};
)
}
/**
* GitLab
@ -41,12 +41,12 @@ const set = async (badge: BadgeSetParams) => {
* @returns
*/
const add = async (badge: BadgeSetParams) => {
const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges`;
const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges`
return gitlabReqWarp<Gitlab.Badge>(
() => netTool.post(URL, badge, {}, GITLAB_AUTH_HEADER),
null
);
};
)
}
/**
*
@ -55,6 +55,6 @@ const badge = {
get,
set,
add,
};
}
export default badge;
export default badge

View File

@ -1,5 +1,5 @@
import netTool from "../netTool";
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
import netTool from "../netTool"
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"
/**
*
@ -8,15 +8,15 @@ import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
* @returns promise
*/
const getMr = async (project_id: number, sha: string) => {
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/repository/commits/${sha}/merge_requests`;
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/repository/commits/${sha}/merge_requests`
return gitlabReqWarp<any[]>(
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
[]
);
};
)
}
const commit = {
getMr,
};
}
export default commit;
export default commit

View File

@ -1,14 +1,13 @@
import badge from "./badge";
import commit from "./commit";
import pipeline from "./pipeline";
import project from "./project";
import badge from "./badge"
import commit from "./commit"
import pipeline from "./pipeline"
import project from "./project"
const gitlab = {
project,
badge,
commit,
pipeline,
};
}
export default gitlab;
export default gitlab

View File

@ -1,6 +1,6 @@
import { Gitlab } from "../../types/gitlab";
import netTool from "../netTool";
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
import { Gitlab } from "../../types/gitlab"
import netTool from "../netTool"
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"
/**
* GitLab流水线的详细信息
@ -14,14 +14,14 @@ const getDetail = async (
pipeline_id: number,
created_at: string
) => {
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines/${pipeline_id}`;
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines/${pipeline_id}`
const res = await gitlabReqWarp<Gitlab.PipelineDetail>(
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
null
);
if (res === null) return null;
return { ...res, created_at };
};
)
if (res === null) return null
return { ...res, created_at }
}
/**
* GitLab流水线列表
@ -30,17 +30,17 @@ const getDetail = async (
* @returns 线promise
*/
const getList = async (project_id: number, page = 1) => {
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines`;
const params = { scope: "finished", per_page: 100, page };
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines`
const params = { scope: "finished", per_page: 100, page }
return gitlabReqWarp<Gitlab.Pipeline[]>(
() => netTool.get(URL, params, GITLAB_AUTH_HEADER),
[]
);
};
)
}
const pipeline = {
getDetail,
getList,
};
}
export default pipeline;
export default pipeline

View File

@ -1,6 +1,6 @@
import { Gitlab } from "../../types/gitlab";
import netTool from "../netTool";
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
import { Gitlab } from "../../types/gitlab"
import netTool from "../netTool"
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"
/**
* GitLab
@ -9,16 +9,16 @@ import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
*/
const getDetail = async (project_id: number | string) => {
if (typeof project_id === "string")
project_id = encodeURIComponent(project_id);
const URL = `${GITLAB_BASE_URL}/projects/${project_id}`;
project_id = encodeURIComponent(project_id)
const URL = `${GITLAB_BASE_URL}/projects/${project_id}`
return gitlabReqWarp<Gitlab.ProjDetail>(
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
null
);
};
)
}
const project = {
getDetail,
};
}
export default project;
export default project

View File

@ -1,19 +1,19 @@
import { Gitlab } from "../../types/gitlab";
import { Gitlab } from "../../types/gitlab"
export const gitlabReqWarp = async <T = any>(
func: () => Promise<T>,
default_value: any
): Promise<T> => {
try {
let response = {} as T & Gitlab.Error;
response = (await func()) as T & Gitlab.Error;
if (response.message === "404 Project Not Found") return default_value;
return response;
let response = {} as T & Gitlab.Error
response = (await func()) as T & Gitlab.Error
if (response.message === "404 Project Not Found") return default_value
return response
} catch {
return default_value;
return default_value
}
};
}
export const GITLAB_BASE_URL = "https://git.n.xiaomi.com/api/v4";
export const GITLAB_BASE_URL = "https://git.n.xiaomi.com/api/v4"
export const GITLAB_AUTH_HEADER = { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" };
export const GITLAB_AUTH_HEADER = { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" }

View File

@ -1,9 +1,9 @@
import gitlab from "./gitlab";
import message from "./message";
import gitlab from "./gitlab"
import message from "./message"
const service = {
gitlab,
message,
};
}
export default service;
export default service

View File

@ -1,27 +1,27 @@
import netTool from "../netTool";
import netTool from "../netTool"
const API_KEY = "1dfz4wlpbbgiky0";
const URL = "https://egg.imoaix.cn/message";
const API_KEY = "1dfz4wlpbbgiky0"
const URL = "https://egg.imoaix.cn/message"
const message = async (body: any) => {
try {
const res = await netTool.post(URL, {
api_key: API_KEY,
...body,
});
return res;
})
return res
} catch {
return null;
return null
}
};
}
message.byGroupId = async (group_id: string, content: string) => {
return message({
group_id,
msg_type: "interactive",
content,
});
};
})
}
message.byChatId = async (chat_id: string, content: string) => {
return message({
@ -29,8 +29,8 @@ message.byChatId = async (chat_id: string, content: string) => {
receive_id_type: "chat_id",
msg_type: "interactive",
content,
});
};
})
}
message.byUserId = async (user_id: string, content: string) => {
return message({
@ -38,7 +38,7 @@ message.byUserId = async (user_id: string, content: string) => {
receive_id_type: "user_id",
msg_type: "interactive",
content,
});
};
})
}
export default message;
export default message

View File

@ -1,9 +1,9 @@
interface NetRequestParams {
url: string;
method: string;
queryParams?: any;
payload?: any;
additionalHeaders?: any;
url: string
method: string
queryParams?: any
payload?: any
additionalHeaders?: any
}
/**
@ -32,10 +32,10 @@ const logResponse = (
responseHeaders: response.headers,
requestBody,
responseBody,
};
console.log("🚀 ~ responseLog:", JSON.stringify(responseLog, null, 2));
return responseLog;
};
}
console.log("🚀 ~ responseLog:", JSON.stringify(responseLog, null, 2))
return responseLog
}
/**
* Promise
@ -55,13 +55,13 @@ const netTool = async <T = any>({
additionalHeaders,
}: NetRequestParams): Promise<T> => {
// 拼接完整的URL
let fullUrl = url;
let fullUrl = url
if (queryParams) {
if (typeof queryParams === "string") {
fullUrl = `${url}?${queryParams}`;
fullUrl = `${url}?${queryParams}`
} else {
const queryString = new URLSearchParams(queryParams).toString();
if (queryString) fullUrl = `${url}?${queryString}`;
const queryString = new URLSearchParams(queryParams).toString()
if (queryString) fullUrl = `${url}?${queryString}`
}
}
@ -69,42 +69,42 @@ const netTool = async <T = any>({
const headers = {
"Content-Type": "application/json",
...additionalHeaders,
};
}
// 发送请求
const res = await fetch(fullUrl, {
method,
body: JSON.stringify(payload),
headers,
});
})
// 获取响应数据
let resData: any = null;
let resText: string = "";
let resData: any = null
let resText: string = ""
try {
resText = await res.text();
resData = JSON.parse(resText);
resText = await res.text()
resData = JSON.parse(resText)
} catch {
/* empty */
}
// 记录响应
logResponse(res, method, headers, payload, resData || resText);
logResponse(res, method, headers, payload, resData || resText)
if (!res.ok) {
if (resData?.msg) {
throw new Error(resData.msg);
throw new Error(resData.msg)
}
if (resText) {
throw new Error(resText);
throw new Error(resText)
}
throw new Error("网络响应异常");
throw new Error("网络响应异常")
}
// http 错误码正常,但解析异常
if (!resData) {
throw new Error("解析响应数据异常");
throw new Error("解析响应数据异常")
}
return resData as T;
};
return resData as T
}
/**
* GET请求并返回一个解析为响应数据的Promise
@ -118,8 +118,7 @@ netTool.get = <T = any>(
url: string,
queryParams?: any,
additionalHeaders?: any
): Promise<T> =>
netTool({ url, method: "get", queryParams, additionalHeaders });
): Promise<T> => netTool({ url, method: "get", queryParams, additionalHeaders })
/**
* POST请求并返回一个解析为响应数据的Promise
@ -136,7 +135,7 @@ netTool.post = <T = any>(
queryParams?: any,
additionalHeaders?: any
): Promise<T> =>
netTool({ url, method: "post", payload, queryParams, additionalHeaders });
netTool({ url, method: "post", payload, queryParams, additionalHeaders })
/**
* PUT请求并返回一个解析为响应数据的Promise
@ -153,7 +152,7 @@ netTool.put = <T = any>(
queryParams?: any,
additionalHeaders?: any
): Promise<T> =>
netTool({ url, method: "put", payload, queryParams, additionalHeaders });
netTool({ url, method: "put", payload, queryParams, additionalHeaders })
/**
* DELETE请求并返回一个解析为响应数据的Promise
@ -170,7 +169,7 @@ netTool.del = <T = any>(
queryParams?: any,
additionalHeaders?: any
): Promise<T> =>
netTool({ url, method: "delete", payload, queryParams, additionalHeaders });
netTool({ url, method: "delete", payload, queryParams, additionalHeaders })
/**
* PATCH请求并返回一个解析为响应数据的Promise
@ -187,7 +186,7 @@ netTool.patch = <T = any>(
queryParams?: any,
additionalHeaders?: any
): Promise<T> =>
netTool({ url, method: "patch", payload, queryParams, additionalHeaders });
netTool({ url, method: "patch", payload, queryParams, additionalHeaders })
/**
* 400 Bad Request的响应对象
@ -197,7 +196,7 @@ netTool.patch = <T = any>(
* @returns 400 Bad Request的响应对象
*/
netTool.badRequest = (msg: string, requestId?: string) =>
Response.json({ code: 400, msg, requestId }, { status: 400 });
Response.json({ code: 400, msg, requestId }, { status: 400 })
/**
* 404 Not Found的响应对象
@ -207,7 +206,7 @@ netTool.badRequest = (msg: string, requestId?: string) =>
* @returns 404 Not Found的响应对象
*/
netTool.notFound = (msg: string, requestId?: string) =>
Response.json({ code: 404, msg, requestId }, { status: 404 });
Response.json({ code: 404, msg, requestId }, { status: 404 })
/**
* 500 Internal Server Error的响应对象
@ -218,7 +217,7 @@ netTool.notFound = (msg: string, requestId?: string) =>
* @returns 500 Internal Server Error的响应对象
*/
netTool.serverError = (msg: string, data?: any, requestId?: string) =>
Response.json({ code: 500, msg, data, requestId }, { status: 500 });
Response.json({ code: 500, msg, data, requestId }, { status: 500 })
/**
* 200 OK的响应对象
@ -228,6 +227,6 @@ netTool.serverError = (msg: string, data?: any, requestId?: string) =>
* @returns 200 OK的响应对象
*/
netTool.ok = (data?: any, requestId?: string) =>
Response.json({ code: 0, msg: "success", data, requestId });
Response.json({ code: 0, msg: "success", data, requestId })
export default netTool;
export default netTool

View File

@ -1,52 +1,52 @@
import { RecordModel } from "pocketbase";
import { RecordModel } from "pocketbase"
export namespace DB {
export interface Pipeline extends RecordModel {
project_id: string;
user_id: string;
pipeline_id: number;
ref: string;
status: string;
web_url: string;
project_id: string
user_id: string
pipeline_id: number
ref: string
status: string
web_url: string
// 2024-03-06 02:53:59.509Z
created_at: string;
started_at: string;
finished_at: string;
duration: number;
queued_duration: number;
created_at: string
started_at: string
finished_at: string
duration: number
queued_duration: number
}
export interface Project extends RecordModel {
project_id: number;
description: string;
name: string;
path_with_namespace: string;
web_url: string;
avatar_url: string;
has_new_cicd: boolean;
project_id: number
description: string
name: string
path_with_namespace: string
web_url: string
avatar_url: string
has_new_cicd: boolean
}
export interface User extends RecordModel {
user_id: number;
username: string;
name: string;
avatar_url: string;
web_url: string;
user_id: number
username: string
name: string
avatar_url: string
web_url: string
}
export interface StatisticsPerWeek extends RecordModel {
week: string;
total_count: number;
failed_count: number;
success_count: number;
success_rate: number;
duration: number;
week: string
total_count: number
failed_count: number
success_count: number
success_rate: number
duration: number
}
export interface StatisticsPerProj extends RecordModel {
week: string;
name: string;
duration: number;
ref: string;
week: string
name: string
duration: number
ref: string
}
}

View File

@ -1,59 +1,59 @@
export namespace Gitlab {
export interface Error {
message: string;
message: string
}
export interface User {
id: number;
username: string;
name: string;
state: string;
avatar_url: string;
web_url: string;
id: number
username: string
name: string
state: string
avatar_url: string
web_url: string
}
export interface ProjDetail {
id: number;
description: string;
name: string;
path_with_namespace: string;
web_url: string;
avatar_url?: any;
message?: string;
id: number
description: string
name: string
path_with_namespace: string
web_url: string
avatar_url?: any
message?: string
}
export interface PipelineDetail {
id: number;
project_id: number;
ref: string;
status: string;
web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046";
user: User;
started_at: string;
finished_at: string;
duration: number;
queued_duration: number;
message?: string;
id: number
project_id: number
ref: string
status: string
web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046"
user: User
started_at: string
finished_at: string
duration: number
queued_duration: number
message?: string
}
export interface Pipeline {
id: number;
project_id: number;
sha: string;
ref: string;
status: string;
source: string;
created_at: string;
updated_at: string;
web_url: string;
id: number
project_id: number
sha: string
ref: string
status: string
source: string
created_at: string
updated_at: string
web_url: string
}
export interface Badge {
id: number;
name: string;
link_url: string;
image_url: string;
rendered_link_url: string;
rendered_image_url: string;
kind: "project" | "group";
id: number
name: string
link_url: string
image_url: string
rendered_link_url: string
rendered_image_url: string
kind: "project" | "group"
}
}

View File

@ -2,10 +2,10 @@ export const managePb404 = async <T>(
dbFunc: () => Promise<T>
): Promise<T | null> => {
try {
return await dbFunc();
return await dbFunc()
} catch (err: any) {
if (err?.message === "The requested resource wasn't found.") {
return null;
} else throw err;
return null
} else throw err
}
};
}

View File

@ -5,22 +5,22 @@
*/
export const calculatePercentageChange = (cur: number, prev: number) => {
// 计算差值
const diff = cur - prev;
const diff = cur - prev
if (diff === 0)
return {
diff,
percentage: "0",
};
}
// 计算百分比
const percentage = Math.abs((diff / prev) * 100).toFixed(1);
const percentage = Math.abs((diff / prev) * 100).toFixed(1)
return {
diff,
percentage,
};
};
}
}
/**
*
@ -36,7 +36,7 @@ export const calculateWeeklyRate = (
const { diff, percentage } = calculatePercentageChange(
Number(cur),
Number(prev)
);
)
if (diff > 0)
return {
text: `<font color='red'>${
@ -44,7 +44,7 @@ export const calculateWeeklyRate = (
}${percentage}%</font>`,
diff,
percentage,
};
}
if (diff < 0)
return {
text: `<font color='green'>${
@ -52,10 +52,10 @@ export const calculateWeeklyRate = (
}${percentage}%</font>`,
diff,
percentage,
};
}
return {
text: `<font color='gray'>${needCN ? "较上周 " : ""}0%</font>`,
diff,
percentage,
};
};
}
}

View File

@ -1,22 +1,22 @@
import moment from "moment";
import moment from "moment"
/**
* like 2024-05
*/
export const getWeekTimeWithYear = () => {
return moment().format("YYYY-WW");
};
return moment().format("YYYY-WW")
}
/**
* like 2024-04
*/
export const getPrevWeekWithYear = () => {
return moment().subtract(1, "weeks").format("YYYY-WW");
};
return moment().subtract(1, "weeks").format("YYYY-WW")
}
/**
*
*/
export const sec2min = (sec: number) => {
return (sec / 60).toFixed(1);
};
return (sec / 60).toFixed(1)
}