feat: 新增客户管理全模块功能
实现客户信息、客户部门、客户账号三个子模块的完整CRUD,包含: 1. 数据库实体、DTO、控制器、服务层代码 2. RBAC权限映射与菜单种子数据扩展 3. 前端路由与API封装 4. 数据权限隔离与鉴权逻辑 5. 配套工具脚本与测试验证清单
This commit is contained in:
1 parent
026ec34471
commit
1c80a24d1d
31 files changed
+26321
-653
No files matched your search
@@ -0,0 +1,343 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op } from "sequelize";
|
||||
import CustomerAccount from "../entity/customer_account.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
import { hashPassword } from "../util/crypto";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
|
||||
function getDefaultPassword(phone: string): string {
|
||||
return phone.length >= 6 ? phone.slice(-6) : "123456";
|
||||
}
|
||||
|
||||
@Provide()
|
||||
export class CustomerAccountService {
|
||||
@InjectRepository(CustomerAccount)
|
||||
accountModel!: Repository<CustomerAccount>;
|
||||
|
||||
@InjectRepository(Customer)
|
||||
customerModel!: Repository<Customer>;
|
||||
|
||||
@InjectRepository(SysUserRole)
|
||||
userRoleModel!: Repository<SysUserRole>;
|
||||
|
||||
@InjectRepository(SysRole)
|
||||
roleModel!: Repository<SysRole>;
|
||||
|
||||
private async isAdmin(userId: number): Promise<boolean> {
|
||||
const userRoles = await SysUserRole.findAll({
|
||||
where: { userId },
|
||||
include: [SysRole]
|
||||
});
|
||||
return userRoles.some((ur) => (ur as any).role?.roleCode === "ADMIN");
|
||||
}
|
||||
|
||||
private async isCustomerOwner(userId: number, customerId: number): Promise<boolean> {
|
||||
if (await this.isAdmin(userId)) {
|
||||
return true;
|
||||
}
|
||||
const customer = await Customer.findByPk(customerId);
|
||||
if (!customer) {
|
||||
return false;
|
||||
}
|
||||
return customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
private cleanSensitiveFields(obj: any): any {
|
||||
const result = obj.toJSON ? obj.toJSON() : { ...obj };
|
||||
delete result.passwordHash;
|
||||
delete result.wxOpenid;
|
||||
return result;
|
||||
}
|
||||
|
||||
async list(query: { page?: any; pageSize?: any; keyword?: string; customerId?: number; status?: number }, ctx: Context): Promise<any> {
|
||||
const page = Number(query.page) || 1;
|
||||
const pageSize = Number(query.pageSize) || 10;
|
||||
const userId = ctx.state.user.userId;
|
||||
|
||||
const where: any = {};
|
||||
|
||||
if (query.keyword) {
|
||||
where.phone = { [Op.like]: `%${query.keyword}%` };
|
||||
}
|
||||
|
||||
if (query.customerId !== undefined) {
|
||||
where.customerId = query.customerId;
|
||||
}
|
||||
|
||||
if (query.status !== undefined) {
|
||||
where.status = query.status;
|
||||
}
|
||||
|
||||
if (!(await this.isAdmin(userId))) {
|
||||
const myCustomers = await this.customerModel.findAll({
|
||||
where: { salesmanId: userId },
|
||||
attributes: ["id"]
|
||||
});
|
||||
const myCustomerIds = myCustomers.map((c) => c.id);
|
||||
if (myCustomerIds.length === 0) {
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户账号列表成功",
|
||||
data: {
|
||||
list: [],
|
||||
total: 0,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize)
|
||||
}
|
||||
};
|
||||
}
|
||||
if (where.customerId !== undefined) {
|
||||
if (!myCustomerIds.includes(where.customerId)) {
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户账号列表成功",
|
||||
data: {
|
||||
list: [],
|
||||
total: 0,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize)
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
where.customerId = { [Op.in]: myCustomerIds };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.accountModel.findAndCountAll({
|
||||
where,
|
||||
limit: pageSize,
|
||||
offset: (page - 1) * pageSize,
|
||||
order: [["created_at", "DESC"]]
|
||||
});
|
||||
|
||||
const list = rows.map((row) => this.cleanSensitiveFields(row));
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户账号列表成功",
|
||||
data: {
|
||||
list,
|
||||
total: count,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async detail(id: number, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.accountModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户账号不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限访问该账号",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户账号详情成功",
|
||||
data: this.cleanSensitiveFields(record)
|
||||
};
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, data.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限为该客户创建账号",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
const existing = await this.accountModel.findOne({
|
||||
where: { phone: data.phone }
|
||||
});
|
||||
if (existing) {
|
||||
return {
|
||||
code: 400,
|
||||
message: "手机号已存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
const password = getDefaultPassword(data.phone);
|
||||
const passwordHash = await hashPassword(password);
|
||||
|
||||
const record = await this.accountModel.create({
|
||||
customerId: data.customerId,
|
||||
phone: data.phone,
|
||||
passwordHash,
|
||||
status: data.status ?? 1
|
||||
});
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "创建客户账号成功",
|
||||
data: this.cleanSensitiveFields(record)
|
||||
};
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.accountModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户账号不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限修改该账号",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (data.resetPassword === true) {
|
||||
const password = getDefaultPassword(record.phone);
|
||||
record.passwordHash = await hashPassword(password);
|
||||
await record.save();
|
||||
}
|
||||
|
||||
if (data.phone !== undefined && data.phone !== record.phone) {
|
||||
const conflict = await this.accountModel.findOne({
|
||||
where: { phone: data.phone }
|
||||
});
|
||||
if (conflict) {
|
||||
return {
|
||||
code: 400,
|
||||
message: "手机号已存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const cleanedData: any = {};
|
||||
if (data.phone !== undefined) cleanedData.phone = data.phone;
|
||||
if (data.status !== undefined) cleanedData.status = data.status;
|
||||
|
||||
const isAdmin = await this.isAdmin(userId);
|
||||
if (isAdmin && data.customerId !== undefined) {
|
||||
cleanedData.customerId = data.customerId;
|
||||
}
|
||||
|
||||
await record.update(cleanedData);
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "更新客户账号成功",
|
||||
data: this.cleanSensitiveFields(record)
|
||||
};
|
||||
}
|
||||
|
||||
async updateStatus(id: number, status: number, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.accountModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户账号不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限修改该账号状态",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
await record.update({ status });
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "状态更新成功",
|
||||
data: this.cleanSensitiveFields(record)
|
||||
};
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.accountModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户账号不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限删除该账号",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
await record.destroy();
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "删除客户账号成功",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
async resetPassword(id: number, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.accountModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户账号不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限重置该账号密码",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
const password = getDefaultPassword(record.phone);
|
||||
record.passwordHash = await hashPassword(password);
|
||||
await record.save();
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "重置密码成功",
|
||||
data: this.cleanSensitiveFields(record)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op } from "sequelize";
|
||||
import CustomerDepartment from "../entity/customer_department.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
|
||||
@Provide()
|
||||
export class CustomerDepartmentService {
|
||||
@InjectRepository(CustomerDepartment)
|
||||
depModel!: Repository<CustomerDepartment>;
|
||||
|
||||
@InjectRepository(Customer)
|
||||
customerModel!: Repository<Customer>;
|
||||
|
||||
@InjectRepository(SysUserRole)
|
||||
userRoleModel!: Repository<SysUserRole>;
|
||||
|
||||
@InjectRepository(SysRole)
|
||||
roleModel!: Repository<SysRole>;
|
||||
|
||||
private async isAdmin(userId: number): Promise<boolean> {
|
||||
const userRoles = await SysUserRole.findAll({
|
||||
where: { userId },
|
||||
include: [SysRole]
|
||||
});
|
||||
return userRoles.some((ur) => (ur as any).role?.roleCode === "ADMIN");
|
||||
}
|
||||
|
||||
private async isCustomerOwner(userId: number, customerId: number): Promise<boolean> {
|
||||
if (await this.isAdmin(userId)) {
|
||||
return true;
|
||||
}
|
||||
const customer = await Customer.findByPk(customerId);
|
||||
if (!customer) {
|
||||
return false;
|
||||
}
|
||||
return customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
async list(query: { page?: any; pageSize?: any; keyword?: string; customerId?: number }, ctx: Context): Promise<any> {
|
||||
const page = Number(query.page) || 1;
|
||||
const pageSize = Number(query.pageSize) || 10;
|
||||
const userId = ctx.state.user.userId;
|
||||
|
||||
const where: any = {};
|
||||
|
||||
if (query.keyword) {
|
||||
where[Op.or] = [
|
||||
{ deptName: { [Op.like]: `%${query.keyword}%` } },
|
||||
{ contactPerson: { [Op.like]: `%${query.keyword}%` } },
|
||||
{ contactPhone: { [Op.like]: `%${query.keyword}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
if (query.customerId !== undefined) {
|
||||
where.customerId = query.customerId;
|
||||
}
|
||||
|
||||
if (!(await this.isAdmin(userId))) {
|
||||
const myCustomers = await this.customerModel.findAll({
|
||||
where: { salesmanId: userId },
|
||||
attributes: ["id"]
|
||||
});
|
||||
const myCustomerIds = myCustomers.map((c) => c.id);
|
||||
if (myCustomerIds.length === 0) {
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户部门列表成功",
|
||||
data: {
|
||||
list: [],
|
||||
total: 0,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize)
|
||||
}
|
||||
};
|
||||
}
|
||||
if (where.customerId !== undefined) {
|
||||
if (!myCustomerIds.includes(where.customerId)) {
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户部门列表成功",
|
||||
data: {
|
||||
list: [],
|
||||
total: 0,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize)
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
where.customerId = { [Op.in]: myCustomerIds };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.depModel.findAndCountAll({
|
||||
where,
|
||||
limit: pageSize,
|
||||
offset: (page - 1) * pageSize,
|
||||
order: [["created_at", "DESC"]]
|
||||
});
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户部门列表成功",
|
||||
data: {
|
||||
list: rows.map((r) => r.toJSON()),
|
||||
total: count,
|
||||
page: Number(page),
|
||||
pageSize: Number(pageSize)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async detail(id: number, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.depModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户部门不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限访问该部门",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户部门详情成功",
|
||||
data: record.toJSON()
|
||||
};
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, data.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限为该客户创建部门",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
const record = await this.depModel.create(data);
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "创建客户部门成功",
|
||||
data: record.toJSON()
|
||||
};
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.depModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户部门不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限修改该部门",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
delete data.customerId;
|
||||
|
||||
await record.update(data);
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "更新客户部门成功",
|
||||
data: record.toJSON()
|
||||
};
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const userId = ctx.state.user.userId;
|
||||
const record = await this.depModel.findByPk(id);
|
||||
|
||||
if (!record) {
|
||||
return {
|
||||
code: 404,
|
||||
message: "客户部门不存在",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
if (!(await this.isCustomerOwner(userId, record.customerId))) {
|
||||
return {
|
||||
code: 403,
|
||||
message: "无权限删除该部门",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
await record.destroy();
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "删除客户部门成功",
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
async findByCustomerId(customerId: number): Promise<any> {
|
||||
const records = await this.depModel.findAll({
|
||||
where: { customerId },
|
||||
order: [["created_at", "DESC"]]
|
||||
});
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: "获取客户部门列表成功",
|
||||
data: records.map((r) => r.toJSON())
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
import { Provide, Inject } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op, UniqueConstraintError } 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";
|
||||
});
|
||||
}
|
||||
|
||||
private 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())
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user