Skip to content

Commit

Permalink
feat(mis): 管理系统平台数据统计横坐标优化 (#1300)
Browse files Browse the repository at this point in the history
做了什么:
1.平台数据统计,消费充值账户TOP10横坐标改为userName:

![image](https://github.com/PKUHPC/SCOW/assets/72734623/e38afc05-b121-4ada-8fb7-39f166699b87)

对比之前

![image](https://github.com/PKUHPC/SCOW/assets/72734623/b35e2249-64f6-48ce-9640-d69ebbeb2e23)

2.将 mis-server 中 getTopChargeAccount、getTopPayAccount 返回值新增 userName

3.门户系统使用功能次数interval改为全部显示,并且文字方向改为横向:

![540f39bf433d2c1b1f8dd349e590c34](https://github.com/PKUHPC/SCOW/assets/72734623/71ec3ba6-399f-4999-8564-0eacc40cdb06)

对比之前

![image](https://github.com/PKUHPC/SCOW/assets/72734623/2306e456-f338-4d6d-80e9-ccbc0feb7e88)
  • Loading branch information
usaveh authored Jun 18, 2024
1 parent 0fddd5b commit 5b6af87
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .changeset/late-days-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@scow/grpc-api": minor
---

将 mis-server 中 getTopChargeAccount、getTopPayAccount 返回值新增 userName
5 changes: 5 additions & 0 deletions .changeset/plenty-meals-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@scow/mis-web": patch
---

修改了 mis 系统下充值、消费账户前十的统计图的横坐标为 userName;修复了 mis 系统下系统使用量横坐标显示不全的问题。
87 changes: 54 additions & 33 deletions apps/mis-server/src/services/charging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,28 +286,35 @@ export const chargingServiceServer = plugin((server) => {
getTopChargeAccount: async ({ request, em }) => {
const { startTime, endTime, topRank = 10 } = ensureNotUndefined(request, ["startTime", "endTime"]);

const qb = em.createQueryBuilder(ChargeRecord, "cr");
qb
.select("cr.accountName")
.addSelect([raw("SUM(cr.amount) as `totalAmount`")])
.where({ time: { $gte: startTime } })
.andWhere({ time: { $lte: endTime } })
.andWhere({ accountName: { $ne: null } })
.groupBy("accountName")
.orderBy({ [raw("SUM(cr.amount)")]: QueryOrder.DESC })
// 直接使用Knex查询构建器
const knex = em.getKnex();

// 查询消费记录
const results: {account_name: string, user_name: string, chargedAmount: number}[] =
// 从pay_record表中查询
await knex("charge_record as cr")
// 选择account_name字段
// 选择user表中的name字段,并将其命名为user_name
// 计算amount字段的总和,并将其命名为totalAmount
.select(["cr.account_name", "u.name as user_name", knex.raw("SUM(amount) as chargedAmount")])
.join("user as u", "u.user_id", "=", "cr.user_id")
.where("cr.time", "<=", endTime)
.andWhere("cr.time", ">=", startTime)
// 过滤为空的情况
.whereNotNull("cr.account_name")
// 按account_name和user_name分组
.groupBy(["cr.account_name", "u.name"])
// 按totalAmount降序排序
.orderBy("chargedAmount", "desc")
// 限制结果的数量为topRank
.limit(topRank);

const results: {accountName: string, totalAmount: number}[] = await queryWithCache({
em,
queryKeys: ["get_top_charge_account", `${startTime}`, `${endTime}`, `${topRank}`],
queryQb: qb,
});

return [
{
results: results.map((x) => ({
accountName: x.accountName,
chargedAmount: numberToMoney(x.totalAmount),
accountName: x.account_name,
userName:x.user_name,
chargedAmount: numberToMoney(x.chargedAmount),
})),
},
];
Expand Down Expand Up @@ -346,30 +353,44 @@ export const chargingServiceServer = plugin((server) => {
}];
},

// 获取指定时间段内支付金额最高的账户信息
getTopPayAccount: async ({ request, em }) => {
// 从请求中获取开始时间、结束时间和前N名的数量,如果未提供topRank则默认为10
const { startTime, endTime, topRank = 10 } = ensureNotUndefined(request, ["startTime", "endTime"]);

const qb = em.createQueryBuilder(PayRecord, "p");
qb
.select("p.accountName")
.addSelect(raw("SUM(p.amount) as `totalAmount`"))
.where({ time: { $gte: startTime } })
.andWhere({ time: { $lte: endTime } })
.andWhere({ accountName: { $ne: null } })
.groupBy("accountName")
.orderBy({ [raw("SUM(p.amount)")]: QueryOrder.DESC })
// 直接使用Knex查询构建器
const knex = em.getKnex();

// 查询支付记录
const results: {account_name: string, user_name: string, totalAmount: number}[] =
// 从pay_record表中查询
await knex("pay_record as pr")
// 选择account_name字段
// 选择user表中的name字段,并将其命名为user_name
// 计算amount字段的总和,并将其命名为totalAmount
.select(["pr.account_name", "u.name as user_name", knex.raw("SUM(amount) as totalAmount")])
// 通过accountName字段与account表连接
.join("account as a", "a.account_name ", "=", "pr.account_name")
// 通过account_id字段与user_account表连接
.join("user_account as ua", "ua.account_id", "=", "a.id")
.where("role", "=", "OWNER")
.join("user as u", "u.id", "=", "ua.user_id")
.where("pr.time", "<=", endTime)
.andWhere("pr.time", ">=", startTime)
// 过滤为空的情况
.whereNotNull("pr.account_name")
// 按account_name和user_name分组
.groupBy(["pr.account_name", "u.name"])
// 按totalAmount降序排序
.orderBy("totalAmount", "desc")
// 限制结果的数量为topRank
.limit(topRank);

const results: {accountName: string, totalAmount: number}[] = await queryWithCache({
em,
queryKeys: ["get_top_pay_account", `${startTime}`, `${endTime}`, `${topRank}`],
queryQb: qb,
});

return [
{
results: results.map((x) => ({
accountName: x.accountName,
accountName: x.account_name,
userName:x.user_name,
payAmount: numberToMoney(x.totalAmount),
})),
},
Expand Down
26 changes: 24 additions & 2 deletions apps/mis-server/tests/charging/statistics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { Account } from "src/entities/Account";
import { ChargeRecord } from "src/entities/ChargeRecord";
import { PayRecord } from "src/entities/PayRecord";
import { Tenant } from "src/entities/Tenant";
import { User } from "src/entities/User";
import { UserAccount, UserRole, UserStatus } from "src/entities/UserAccount";
import { dropDatabase } from "tests/data/helpers";

dayjs.extend(utc);
Expand All @@ -48,11 +50,28 @@ beforeEach(async () => {

const accounts = Array.from({ length: 10 }, (_, i) => createAccount(i + 1));

// 创建关联的USER
const createUser = (index: number) => new User({
userId:`${index}`,
name:`top${index}UserName`,
email:`user${index}@foxmail.com`,
tenant:tenant,
});

// 创建UserAccount并插入数据库
const users = accounts.map((_, index) => createUser(index + 1));
const userAccounts = users.map((user, index) => new UserAccount({
account: accounts[index],
user: user,
role: UserRole.OWNER,
blockedInCluster: UserStatus.UNBLOCKED,
}));

const chargeRecords: ChargeRecord[] = [];
const payRecords: PayRecord[] = [];
const date = dayjs().startOf("day");

accounts.forEach((account) => {
accounts.forEach((account, index) => {
const topNumber = +account.accountName.replace("top", "");
const curDate = date.clone().subtract((topNumber - 1), "day");
chargeRecords.push(
Expand All @@ -62,6 +81,7 @@ beforeEach(async () => {
type: "test",
comment: "test",
amount: new Decimal(100 * (11 - topNumber)),
userId: `${index + 1}`, // 确保 userId 正确设置
}),
);
payRecords.push(
Expand All @@ -78,7 +98,7 @@ beforeEach(async () => {
});


await em.persistAndFlush([tenant, ...accounts, ...chargeRecords, ...payRecords]);
await em.persistAndFlush([tenant, ...accounts, ...users, ...userAccounts, ...chargeRecords, ...payRecords]);

await server.start();

Expand All @@ -102,6 +122,7 @@ it("correct get Top 10 Charge Account", async () => {

const results = Array.from({ length: 10 }, (_, i) => ({
accountName: `top${i + 1}`,
userName:`top${i + 1}UserName`,
chargedAmount: decimalToMoney(new Decimal(100 * (11 - (i + 1)))),
}));

Expand Down Expand Up @@ -150,6 +171,7 @@ it("correct get Top 10 Pay Account", async () => {

const results = Array.from({ length: 10 }, (_, i) => ({
accountName: `top${i + 1}`,
userName:`top${i + 1}UserName`,
payAmount: decimalToMoney(new Decimal(100 * (11 - (i + 1)))),
}));

Expand Down
5 changes: 3 additions & 2 deletions apps/mis-web/src/apis/api.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,9 +400,10 @@ export const mockApi: MockApi<typeof api> = {
setDefaultAccountBlockThreshold: async () => ({ executed: true }),
getNewUserCount: async () => ({ results: [{ date: { year: 2023, month: 12, day: 21 }, count: 10 }]}),
getActiveUserCount: async () => ({ results: [{ date: { year: 2023, month: 12, day: 21 }, count: 10 }]}),
getTopChargeAccount: async () => ({ results: [{ accountName: "test", chargedAmount: numberToMoney(10) }]}),
getTopChargeAccount: async () => ({ results: [{ accountName: "test",
userName:"user1", chargedAmount: numberToMoney(10) }]}),
getDailyCharge: async () => ({ results: [{ date: { year: 2023, month: 12, day: 21 }, amount: numberToMoney(10) }]}),
getTopPayAccount: async () => ({ results: [{ accountName: "test", payAmount: numberToMoney(10) }]}),
getTopPayAccount: async () => ({ results: [{ accountName: "test", userName:"user1", payAmount: numberToMoney(10) }]}),
getDailyPay: async () => ({ results: [{ date: { year: 2023, month: 12, day: 21 }, amount: numberToMoney(10) }]}),
getPortalUsageCount: async () => ({ results: [{ operationType: "submitJob", count: 10 }]}),
getMisUsageCount: async () => ({ results: [{ operationType: "createAccount", count: 10 }]}),
Expand Down
20 changes: 19 additions & 1 deletion apps/mis-web/src/pageComponents/admin/DataBarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ export const StatisticTitle = styled.div<{ justify?: string }>`
margin: 8px 0;
`;


const CustomizedAxisTick = (props) => {
const { x, y, payload } = props;
return (
<g transform={`translate(${x},${y})`}>
<text x={0} y={0} dy={16} textAnchor="end" fill="#666" transform="rotate(-35)">
{payload.value}
</text>
</g>
);
};

export const DataBarChart: React.FC<Props> = ({
title,
data,
Expand All @@ -49,6 +61,9 @@ export const DataBarChart: React.FC<Props> = ({
const roundedValue = Number.isInteger(value) ? value : parseFloat(value.toFixed(2));
return roundedValue.toString();
};

console.log(title);

return (
<StatisticContainer>
{isLoading ? <Spin /> : (
Expand All @@ -65,7 +80,10 @@ export const DataBarChart: React.FC<Props> = ({
dataKey="x"
padding={{ left: 20, right: 20 }}
label={{ value: xLabel, position: "insideBottom", offset: 0 }}
height={40}
interval={0}
height={(title.includes("系统使用功能次数") || title.includes("Feature Usage Count")) ? 80 : 40}
tick={(title.includes("系统使用功能次数") || title.includes("Feature Usage Count")) ?
<CustomizedAxisTick /> : true}
/>
<YAxis padding={{ top: 20 }} tickFormatter={tickFormatter} />
<Tooltip
Expand Down
8 changes: 4 additions & 4 deletions apps/mis-web/src/pages/admin/statistic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,15 @@ requireAuth((u) => u.platformRoles.includes(PlatformRole.PLATFORM_ADMIN))
const topChargeAccountData = useMemo(() => {

return topChargeAccount?.results.map((r) => ({
x: r.accountName,
x: r.userName,
y: moneyToNumber(r.chargedAmount).toFixed(2),
})) || [];
}, [query, topChargeAccount]);

const topPayAccountData = useMemo(() => {

return topPayAccount?.results.map((r) => ({
x: r.accountName,
x: r.userName,
y: moneyToNumber(r.payAmount).toFixed(2),
})) || [];
}, [query, topPayAccount]);
Expand Down Expand Up @@ -439,7 +439,7 @@ requireAuth((u) => u.platformRoles.includes(PlatformRole.PLATFORM_ADMIN))
data={topChargeAccountData}
title={t(p("topTenChargedAccount"))}
isLoading={topChargeAccountLoading}
xLabel={t(p("accountName"))}
xLabel={t(p("userName"))}
toolTipFormatter={amountToolTipFormatter}
/>
</Col>
Expand All @@ -461,7 +461,7 @@ requireAuth((u) => u.platformRoles.includes(PlatformRole.PLATFORM_ADMIN))
data={topPayAccountData}
title={t(p("topTenPayAccount"))}
isLoading={topPayAccountLoading}
xLabel={t(p("accountName"))}
xLabel={t(p("userName"))}
toolTipFormatter={amountToolTipFormatter}
/>
</Col>
Expand Down
1 change: 1 addition & 0 deletions apps/mis-web/src/pages/api/admin/getTopChargeAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getClient } from "src/utils/client";
export const GetTopChargeAccountResponse = Type.Object({
results: Type.Array(Type.Object({
accountName: Type.String(),
userName: Type.String(),
chargedAmount: Money,
})),
});
Expand Down
1 change: 1 addition & 0 deletions apps/mis-web/src/pages/api/admin/getTopPayAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getClient } from "src/utils/client";
export const GetTopPayAccountResponse = Type.Object({
results: Type.Array(Type.Object({
accountName: Type.String(),
userName:Type.String(),
payAmount: Money,
})),
});
Expand Down
2 changes: 2 additions & 0 deletions protos/server/charging.proto
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ message GetTopChargeAccountResponse {
message ChargeAccount {
string account_name = 1;
common.Money charged_amount = 2;
string user_name = 3;
}
repeated ChargeAccount results = 1;
}
Expand Down Expand Up @@ -277,6 +278,7 @@ message GetTopPayAccountResponse {
message PayAccount {
string account_name = 1;
common.Money pay_amount = 2;
string user_name = 3;
}
repeated PayAccount results = 1;
}
Expand Down

0 comments on commit 5b6af87

Please sign in to comment.