refactor(客户管理): 完成客户管理模块UI与架构全面调整

1.  调整菜单与路由:重命名客户信息/客户账号为客户档案管理/客户小程序授权管理,移除客户建筑/房间路由,新增空间拓扑路由
2.  后端新增客户统计接口`/customers/stats`,前端同步API与类型定义
3.  重构客户档案与小程序授权页面,仅保留数据库支持字段,移除无支撑功能
4.  新增空间拓扑页面,合并建筑与房间管理为树形拓扑视图
5.  更新数据库种子数据与权限配置,清理废弃菜单与绑定关系
6.  补充UI设计文档与规范说明
This commit is contained in:
andyesfly committed 2026-09-09 17:31:24 +08:00
1 parent 2a06cfea6d
commit c60caa4770
35 files changed
+7524 -1961

No files matched your search

+47 -1
View File
@@ -1,7 +1,7 @@
import { Provide, Inject } from "@midwayjs/core";
import { InjectRepository } from "@midwayjs/sequelize";
import { Repository } from "sequelize-typescript";
import { Op, UniqueConstraintError } from "sequelize";
import { Op, UniqueConstraintError, fn, col } from "sequelize";
import { Context } from "@midwayjs/koa";
import Customer from "../entity/customer.entity";
import CustomerDepartment from "../entity/customer_department.entity";
@@ -318,4 +318,50 @@ export class CustomerService {
data: rows.map((r) => r.toJSON())
};
}
async getStats(ctx: Context): Promise<any> {
const userId = ctx.state.user.userId;
const isAdmin = await this.isAdmin(userId);
const where: any = {};
if (!isAdmin) {
where.salesmanId = userId;
}
const total = await this.customerModel.count({ where });
const statusRows = await this.customerModel.findAll({
where,
attributes: ["status", [fn("COUNT", col("id")), "count"]],
group: ["status"],
raw: true
});
const levelRows = await this.customerModel.findAll({
where,
attributes: ["customer_level", [fn("COUNT", col("id")), "count"]],
group: ["customer_level"],
raw: true
});
const statusCounts: Record<number, number> = {};
for (const row of statusRows as any[]) {
if (row.status !== null && row.status !== undefined) {
statusCounts[row.status] = Number(row.count) || 0;
}
}
const levelCounts: Record<string, number> = {};
for (const row of levelRows as any[]) {
if (row.customer_level) {
levelCounts[row.customer_level] = Number(row.count) || 0;
}
}
return {
code: 200,
message: "获取客户统计成功",
data: { total, statusCounts, levelCounts }
};
}
}