368 lines
8.6 KiB
TypeScript
368 lines
8.6 KiB
TypeScript
import { Provide, Inject } from "@midwayjs/core";
|
|
import { InjectRepository } from "@midwayjs/sequelize";
|
|
import { Repository } from "sequelize-typescript";
|
|
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";
|
|
import CustomerAccount from "../entity/customer_account.entity";
|
|
import SysUserRole from "../entity/sysUserRole.entity";
|
|
import SysRole from "../entity/sysRole.entity";
|
|
|
|
@Provide()
|
|
export class CustomerService {
|
|
@InjectRepository(Customer)
|
|
customerModel!: Repository<Customer>;
|
|
|
|
@InjectRepository(CustomerDepartment)
|
|
customerDepModel!: Repository<CustomerDepartment>;
|
|
|
|
@InjectRepository(CustomerAccount)
|
|
customerAccModel!: Repository<CustomerAccount>;
|
|
|
|
@InjectRepository(SysUserRole)
|
|
userRoleModel!: Repository<SysUserRole>;
|
|
|
|
@InjectRepository(SysRole)
|
|
roleModel!: Repository<SysRole>;
|
|
|
|
async list(
|
|
query: { page?: any; pageSize?: any; keyword?: string; status?: number; salesmanId?: number },
|
|
ctx: Context
|
|
): Promise<any> {
|
|
const page = Number(query.page) || 1;
|
|
const pageSize = Number(query.pageSize) || 10;
|
|
|
|
const where: any = {};
|
|
|
|
if (query.keyword) {
|
|
where[Op.or] = [
|
|
{ customerName: { [Op.like]: `%${query.keyword}%` } },
|
|
{ contactPerson: { [Op.like]: `%${query.keyword}%` } },
|
|
{ contactPhone: { [Op.like]: `%${query.keyword}%` } }
|
|
];
|
|
}
|
|
|
|
if (query.status !== undefined) {
|
|
where.status = query.status;
|
|
}
|
|
|
|
if (query.salesmanId) {
|
|
where.salesmanId = query.salesmanId;
|
|
}
|
|
|
|
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
|
if (!isAdmin) {
|
|
where.salesmanId = ctx.state.user.userId;
|
|
}
|
|
|
|
const { count, rows } = await this.customerModel.findAndCountAll({
|
|
where,
|
|
limit: pageSize,
|
|
offset: (page - 1) * pageSize,
|
|
order: [["created_at", "DESC"]]
|
|
});
|
|
|
|
return {
|
|
code: 200,
|
|
message: "获取客户列表成功",
|
|
data: {
|
|
list: rows,
|
|
total: count,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize)
|
|
}
|
|
};
|
|
}
|
|
|
|
async detail(id: number, ctx: Context): Promise<any> {
|
|
const record = await this.customerModel.findByPk(id);
|
|
|
|
if (!record) {
|
|
return {
|
|
code: 404,
|
|
message: "客户不存在",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
|
if (!isAdmin && record.salesmanId !== ctx.state.user.userId) {
|
|
return {
|
|
code: 403,
|
|
message: "无权限访问该客户",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
return {
|
|
code: 200,
|
|
message: "获取客户详情成功",
|
|
data: record
|
|
};
|
|
}
|
|
|
|
private async isAdmin(userId: number): Promise<boolean> {
|
|
const userRoles = await this.userRoleModel.findAll({
|
|
where: { userId },
|
|
include: [SysRole]
|
|
});
|
|
|
|
return userRoles.some((ur) => {
|
|
const role = (ur as any).role as SysRole;
|
|
return role && role.roleCode === "ADMIN";
|
|
});
|
|
}
|
|
|
|
public async generateCustomerNo(retry: number = 0): Promise<string> {
|
|
const now = new Date();
|
|
const offsetMs = now.getTime() + 8 * 60 * 60 * 1000;
|
|
const dateLocal = new Date(offsetMs);
|
|
const yyyy = dateLocal.getUTCFullYear();
|
|
const mm = String(dateLocal.getUTCMonth() + 1).padStart(2, "0");
|
|
const dd = String(dateLocal.getUTCDate()).padStart(2, "0");
|
|
const yyyymmdd = `${yyyy}${mm}${dd}`;
|
|
|
|
const lastRecord = await this.customerModel.findOne({
|
|
where: { customerNo: { [Op.like]: `KH${yyyymmdd}%` } },
|
|
order: [["customer_no", "DESC"]]
|
|
});
|
|
|
|
let newSeq = 1;
|
|
if (lastRecord) {
|
|
const lastNo = lastRecord.customerNo;
|
|
const seqStr = lastNo.slice(-3);
|
|
const seq = parseInt(seqStr, 10) || 0;
|
|
newSeq = seq + 1;
|
|
}
|
|
|
|
const newSeqStr = String(newSeq).padStart(3, "0");
|
|
const result = `KH${yyyymmdd}${newSeqStr}`;
|
|
|
|
const exists = await this.customerModel.findOne({
|
|
where: { customerNo: result }
|
|
});
|
|
|
|
if (exists) {
|
|
if (retry < 1) {
|
|
return this.generateCustomerNo(retry + 1);
|
|
} else {
|
|
throw new Error("客户编号生成冲突,请重试");
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
async create(data: any, ctx: Context): Promise<any> {
|
|
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
|
|
|
if (!isAdmin) {
|
|
data.salesmanId = ctx.state.user.userId;
|
|
}
|
|
|
|
try {
|
|
data.customerNo = await this.generateCustomerNo();
|
|
const created = await this.customerModel.create(data);
|
|
|
|
return {
|
|
code: 200,
|
|
message: "创建客户成功",
|
|
data: created.toJSON()
|
|
};
|
|
} catch (err: any) {
|
|
if (err instanceof UniqueConstraintError && err.errors?.some((e) => e.path === "customer_no" || e.path === "uk_customer_no")) {
|
|
try {
|
|
data.customerNo = await this.generateCustomerNo();
|
|
const created = await this.customerModel.create(data);
|
|
return {
|
|
code: 200,
|
|
message: "创建客户成功",
|
|
data: created.toJSON()
|
|
};
|
|
} catch (_e) {
|
|
return {
|
|
code: 400,
|
|
message: "客户编号冲突,请重试",
|
|
data: null
|
|
};
|
|
}
|
|
}
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
async update(id: number, data: any, ctx: Context): Promise<any> {
|
|
const record = await this.customerModel.findByPk(id);
|
|
|
|
if (!record) {
|
|
return {
|
|
code: 404,
|
|
message: "客户不存在",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
|
if (!isAdmin && record.salesmanId !== ctx.state.user.userId) {
|
|
return {
|
|
code: 403,
|
|
message: "无权限操作该客户",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
delete data.customerNo;
|
|
delete data.createdAt;
|
|
delete data.updatedAt;
|
|
if (!isAdmin) {
|
|
delete data.salesmanId;
|
|
}
|
|
|
|
await record.update(data);
|
|
|
|
return {
|
|
code: 200,
|
|
message: "更新客户成功",
|
|
data: record
|
|
};
|
|
}
|
|
|
|
async delete(id: number, ctx: Context): Promise<any> {
|
|
const record = await this.customerModel.findByPk(id);
|
|
|
|
if (!record) {
|
|
return {
|
|
code: 404,
|
|
message: "客户不存在",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
|
if (!isAdmin && record.salesmanId !== ctx.state.user.userId) {
|
|
return {
|
|
code: 403,
|
|
message: "无权限操作该客户",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
const hasDep = await this.customerDepModel.findOne({ where: { customerId: id }, limit: 1 });
|
|
const hasAcc = await this.customerAccModel.findOne({ where: { customerId: id }, limit: 1 });
|
|
|
|
if (hasDep || hasAcc) {
|
|
return {
|
|
code: 400,
|
|
message: "该客户存在关联的部门或账号,无法删除,请先删除关联数据",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
await record.destroy();
|
|
|
|
return {
|
|
code: 200,
|
|
message: "删除客户成功",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
async updateStatus(id: number, status: number, ctx: Context): Promise<any> {
|
|
const record = await this.customerModel.findByPk(id);
|
|
|
|
if (!record) {
|
|
return {
|
|
code: 404,
|
|
message: "客户不存在",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
|
if (!isAdmin && record.salesmanId !== ctx.state.user.userId) {
|
|
return {
|
|
code: 403,
|
|
message: "无权限操作该客户",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
await record.update({ status });
|
|
|
|
return {
|
|
code: 200,
|
|
message: "更新客户状态成功",
|
|
data: null
|
|
};
|
|
}
|
|
|
|
async getSimpleList(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 rows = await this.customerModel.findAll({
|
|
where,
|
|
attributes: ["id", "customerNo", "customerName", "status"],
|
|
order: [["created_at", "DESC"]]
|
|
});
|
|
|
|
return {
|
|
code: 200,
|
|
message: "获取客户选项成功",
|
|
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 }
|
|
};
|
|
}
|
|
}
|