feat: 新增客户拜访、建筑、房间及项目全流程业务模块
本次提交新增了客户拜访、客户建筑、客户房间的完整业务模块,同时补充了项目勘察、项目方案、合同管理、项目验收、回访记录的全项目流程相关功能,包括: 1. 新增对应业务的实体类、DTO、控制器、服务层代码 2. 完善RBAC权限映射配置 3. 添加前端API、路由及页面菜单配置 4. 更新数据库种子数据补充菜单和权限绑定
This commit is contained in:
1 parent
f61ade107b
commit
219091f864
53 files changed
+5990
-6
No files matched your search
@@ -0,0 +1,121 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op, UniqueConstraintError } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import Acceptance from "../entity/acceptance.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class AcceptanceService {
|
||||
@InjectRepository(Acceptance)
|
||||
acceptModel!: Repository<Acceptance>;
|
||||
|
||||
@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 this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
private async generateAcceptNo(retry: number = 0): Promise<string> {
|
||||
const now = new Date();
|
||||
const d = new Date(now.getTime() + 8 * 60 * 60 * 1000);
|
||||
const yyyymmdd = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, "0")}${String(d.getUTCDate()).padStart(2, "0")}`;
|
||||
const prefix = `YS${yyyymmdd}`;
|
||||
const last = await this.acceptModel.findOne({ where: { acceptNo: { [Op.like]: `${prefix}%` } }, order: [["accept_no", "DESC"]] });
|
||||
let seq = 1;
|
||||
if (last) seq = (parseInt(last.acceptNo.slice(-3), 10) || 0) + 1;
|
||||
const result = `${prefix}${String(seq).padStart(3, "0")}`;
|
||||
const exists = await this.acceptModel.findOne({ where: { acceptNo: result } });
|
||||
if (exists) {
|
||||
if (retry < 1) return this.generateAcceptNo(retry + 1);
|
||||
throw new Error("验收单号生成冲突,请重试");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [{ acceptor: { [Op.like]: `%${query.keyword}%` } }, { acceptOpinion: { [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 ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取项目验收列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取项目验收列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.acceptModel.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 record = await this.acceptModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目验收不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限访问该验收", data: null };
|
||||
return { code: 200, message: "获取项目验收详情成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, data.customerId))) return { code: 403, message: "无权限为该客户创建验收", data: null };
|
||||
try {
|
||||
data.acceptNo = await this.generateAcceptNo();
|
||||
const created = await this.acceptModel.create(data);
|
||||
return { code: 200, message: "创建项目验收成功", data: created.toJSON() };
|
||||
} catch (err: any) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
data.acceptNo = await this.generateAcceptNo();
|
||||
const created = await this.acceptModel.create(data);
|
||||
return { code: 200, message: "创建项目验收成功", data: created.toJSON() };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.acceptModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目验收不存在", data: null };
|
||||
if (!(await this.isAdmin(ctx.state.user.userId))) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限修改该验收", data: null };
|
||||
delete data.customerId;
|
||||
}
|
||||
delete data.acceptNo;
|
||||
await record.update(data);
|
||||
return { code: 200, message: "更新项目验收成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const record = await this.acceptModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目验收不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该验收", data: null };
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除项目验收成功", data: null };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op, UniqueConstraintError } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import Contract from "../entity/contract.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class ContractService {
|
||||
@InjectRepository(Contract)
|
||||
contractModel!: Repository<Contract>;
|
||||
|
||||
@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 this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
private async generateContractNo(retry: number = 0): Promise<string> {
|
||||
const now = new Date();
|
||||
const d = new Date(now.getTime() + 8 * 60 * 60 * 1000);
|
||||
const yyyy = d.getUTCFullYear();
|
||||
const yyyymmdd = `${yyyy}${String(d.getUTCMonth() + 1).padStart(2, "0")}${String(d.getUTCDate()).padStart(2, "0")}`;
|
||||
const prefix = `HT${yyyymmdd}`;
|
||||
const last = await this.contractModel.findOne({ where: { contractNo: { [Op.like]: `${prefix}%` } }, order: [["contract_no", "DESC"]] });
|
||||
let seq = 1;
|
||||
if (last) seq = (parseInt(last.contractNo.slice(-3), 10) || 0) + 1;
|
||||
const result = `${prefix}${String(seq).padStart(3, "0")}`;
|
||||
const exists = await this.contractModel.findOne({ where: { contractNo: result } });
|
||||
if (exists) {
|
||||
if (retry < 1) return this.generateContractNo(retry + 1);
|
||||
throw new Error("合同单号生成冲突,请重试");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async generateXtContractNo(): Promise<string> {
|
||||
const now = new Date();
|
||||
const d = new Date(now.getTime() + 8 * 60 * 60 * 1000);
|
||||
const prefix = `XT${d.getUTCFullYear()}`;
|
||||
const last = await this.contractModel.findOne({ where: { xtContractNo: { [Op.like]: `${prefix}%` } }, order: [["xt_contract_no", "DESC"]] });
|
||||
let seq = 1;
|
||||
if (last) {
|
||||
const lastNo = last.xtContractNo ?? "";
|
||||
const seqStr = lastNo.slice(-4);
|
||||
seq = (parseInt(seqStr, 10) || 0) + 1;
|
||||
}
|
||||
return `${prefix}${String(seq).padStart(4, "0")}`;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [{ xtContractNo: { [Op.like]: `%${query.keyword}%` } }, { contractDesc: { [Op.like]: `%${query.keyword}%` } }];
|
||||
}
|
||||
if (query.customerId !== undefined) where.customerId = query.customerId;
|
||||
if (query.salesmanId) where.salesmanId = query.salesmanId;
|
||||
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 ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取项目合同列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取项目合同列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
delete where.salesmanId;
|
||||
}
|
||||
|
||||
const { count, rows } = await this.contractModel.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 record = await this.contractModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目合同不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.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 };
|
||||
if (!(await this.isAdmin(userId))) data.salesmanId = userId;
|
||||
try {
|
||||
data.contractNo = await this.generateContractNo();
|
||||
if (!data.xtContractNo) data.xtContractNo = await this.generateXtContractNo();
|
||||
const created = await this.contractModel.create(data);
|
||||
return { code: 200, message: "创建项目合同成功", data: created.toJSON() };
|
||||
} catch (err: any) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
data.contractNo = await this.generateContractNo();
|
||||
const created = await this.contractModel.create(data);
|
||||
return { code: 200, message: "创建项目合同成功", data: created.toJSON() };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.contractModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目合同不存在", data: null };
|
||||
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
||||
if (!isAdmin) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限修改该合同", data: null };
|
||||
delete data.salesmanId;
|
||||
delete data.customerId;
|
||||
}
|
||||
delete data.contractNo;
|
||||
await record.update(data);
|
||||
return { code: 200, message: "更新项目合同成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const record = await this.contractModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目合同不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该合同", data: null };
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除项目合同成功", data: null };
|
||||
}
|
||||
|
||||
async findOptions(customerId: number): Promise<any> {
|
||||
const where: any = {};
|
||||
if (customerId) where.customerId = customerId;
|
||||
const records = await this.contractModel.findAll({ where, order: [["created_at", "DESC"]] });
|
||||
return { code: 200, message: "获取项目合同选项成功", data: records.map((r) => r.toJSON()) };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import CustomerBuilding from "../entity/customer_building.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class CustomerBuildingService {
|
||||
@InjectRepository(CustomerBuilding)
|
||||
buildingModel!: Repository<CustomerBuilding>;
|
||||
|
||||
@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 this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [{ buildingName: { [Op.like]: `%${query.keyword}%` } }, { purpose: { [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 ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取客户建筑列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取客户建筑列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.buildingModel.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 record = await this.buildingModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户建筑不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限访问该建筑", data: null };
|
||||
return { code: 200, message: "获取客户建筑详情成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, data.customerId))) return { code: 403, message: "无权限为该客户创建建筑", data: null };
|
||||
const created = await this.buildingModel.create(data);
|
||||
return { code: 200, message: "创建客户建筑成功", data: created.toJSON() };
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.buildingModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户建筑不存在", data: null };
|
||||
if (!(await this.isAdmin(ctx.state.user.userId))) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.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 record = await this.buildingModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户建筑不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该建筑", data: null };
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除客户建筑成功", data: null };
|
||||
}
|
||||
|
||||
async findOptions(customerId: number): Promise<any> {
|
||||
const records = await this.buildingModel.findAll({ where: { customerId }, order: [["created_at", "DESC"]] });
|
||||
return { code: 200, message: "获取客户建筑选项成功", data: records.map((r) => r.toJSON()) };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import CustomerRoom from "../entity/customer_room.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import CustomerBuilding from "../entity/customer_building.entity";
|
||||
import CustomerDepartment from "../entity/customer_department.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class CustomerRoomService {
|
||||
@InjectRepository(CustomerRoom)
|
||||
roomModel!: Repository<CustomerRoom>;
|
||||
|
||||
@InjectRepository(Customer)
|
||||
customerModel!: Repository<Customer>;
|
||||
|
||||
@InjectRepository(CustomerBuilding)
|
||||
buildingModel!: Repository<CustomerBuilding>;
|
||||
|
||||
@InjectRepository(CustomerDepartment)
|
||||
deptModel!: Repository<CustomerDepartment>;
|
||||
|
||||
@InjectRepository(SysUserRole)
|
||||
userRoleModel!: Repository<SysUserRole>;
|
||||
|
||||
@InjectRepository(SysRole)
|
||||
roleModel!: Repository<SysRole>;
|
||||
|
||||
private async isAdmin(userId: number): Promise<boolean> {
|
||||
const userRoles = await this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [{ roomName: { [Op.like]: `%${query.keyword}%` } }, { roomCode: { [Op.like]: `%${query.keyword}%` } }];
|
||||
}
|
||||
if (query.customerId !== undefined) where.customerId = query.customerId;
|
||||
if (query.buildingId !== undefined) where.buildingId = query.buildingId;
|
||||
|
||||
if (!(await this.isAdmin(userId))) {
|
||||
const myCustomers = await this.customerModel.findAll({ where: { salesmanId: userId }, attributes: ["id"] });
|
||||
const ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取客户房间列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取客户房间列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.roomModel.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 record = await this.roomModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户房间不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限访问该房间", data: null };
|
||||
return { code: 200, message: "获取客户房间详情成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, data.customerId))) return { code: 403, message: "无权限为该客户创建房间", data: null };
|
||||
const created = await this.roomModel.create(data);
|
||||
return { code: 200, message: "创建客户房间成功", data: created.toJSON() };
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.roomModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户房间不存在", data: null };
|
||||
if (!(await this.isAdmin(ctx.state.user.userId))) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.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 record = await this.roomModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户房间不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该房间", data: null };
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除客户房间成功", data: null };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op, UniqueConstraintError } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import CustomerVisit from "../entity/customer_visit.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class CustomerVisitService {
|
||||
@InjectRepository(CustomerVisit)
|
||||
visitModel!: Repository<CustomerVisit>;
|
||||
|
||||
@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 this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
private async generateVisitNo(retry: number = 0): Promise<string> {
|
||||
const now = new Date();
|
||||
const offsetMs = now.getTime() + 8 * 60 * 60 * 1000;
|
||||
const d = new Date(offsetMs);
|
||||
const yyyymmdd = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, "0")}${String(d.getUTCDate()).padStart(2, "0")}`;
|
||||
const prefix = `BF${yyyymmdd}`;
|
||||
const last = await this.visitModel.findOne({ where: { visitNo: { [Op.like]: `${prefix}%` } }, order: [["visit_no", "DESC"]] });
|
||||
let seq = 1;
|
||||
if (last) seq = (parseInt(last.visitNo.slice(-3), 10) || 0) + 1;
|
||||
const result = `${prefix}${String(seq).padStart(3, "0")}`;
|
||||
const exists = await this.visitModel.findOne({ where: { visitNo: result } });
|
||||
if (exists) {
|
||||
if (retry < 1) return this.generateVisitNo(retry + 1);
|
||||
throw new Error("拜访单号生成冲突,请重试");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [
|
||||
{ visitPerson: { [Op.like]: `%${query.keyword}%` } },
|
||||
{ visitPhone: { [Op.like]: `%${query.keyword}%` } }
|
||||
];
|
||||
}
|
||||
if (query.customerId !== undefined) where.customerId = query.customerId;
|
||||
if (query.salesmanId) where.salesmanId = query.salesmanId;
|
||||
|
||||
if (!(await this.isAdmin(userId))) {
|
||||
const myCustomers = await this.customerModel.findAll({ where: { salesmanId: userId }, attributes: ["id"] });
|
||||
const ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取客户拜访列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取客户拜访列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.visitModel.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 record = await this.visitModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户拜访不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.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 };
|
||||
if (!(await this.isAdmin(userId))) data.salesmanId = userId;
|
||||
try {
|
||||
data.visitNo = await this.generateVisitNo();
|
||||
const created = await this.visitModel.create(data);
|
||||
return { code: 200, message: "创建客户拜访成功", data: created.toJSON() };
|
||||
} catch (err: any) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
data.visitNo = await this.generateVisitNo();
|
||||
const created = await this.visitModel.create(data);
|
||||
return { code: 200, message: "创建客户拜访成功", data: created.toJSON() };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.visitModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户拜访不存在", data: null };
|
||||
const isAdmin = await this.isAdmin(ctx.state.user.userId);
|
||||
if (!isAdmin) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限修改该拜访记录", data: null };
|
||||
delete data.salesmanId;
|
||||
delete data.customerId;
|
||||
}
|
||||
delete data.visitNo;
|
||||
await record.update(data);
|
||||
return { code: 200, message: "更新客户拜访成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const record = await this.visitModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户拜访不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该拜访记录", data: null };
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除客户拜访成功", data: null };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op, UniqueConstraintError } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import ProjectPlan from "../entity/project_plan.entity";
|
||||
import ProjectPlanDetail from "../entity/project_plan_detail.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class ProjectPlanService {
|
||||
@InjectRepository(ProjectPlan)
|
||||
planModel!: Repository<ProjectPlan>;
|
||||
|
||||
@InjectRepository(ProjectPlanDetail)
|
||||
detailModel!: Repository<ProjectPlanDetail>;
|
||||
|
||||
@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 this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
private async generatePlanNo(retry: number = 0): Promise<string> {
|
||||
const now = new Date();
|
||||
const d = new Date(now.getTime() + 8 * 60 * 60 * 1000);
|
||||
const yyyymmdd = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, "0")}${String(d.getUTCDate()).padStart(2, "0")}`;
|
||||
const prefix = `FA${yyyymmdd}`;
|
||||
const last = await this.planModel.findOne({ where: { planNo: { [Op.like]: `${prefix}%` } }, order: [["plan_no", "DESC"]] });
|
||||
let seq = 1;
|
||||
if (last) seq = (parseInt(last.planNo.slice(-3), 10) || 0) + 1;
|
||||
const result = `${prefix}${String(seq).padStart(3, "0")}`;
|
||||
const exists = await this.planModel.findOne({ where: { planNo: result } });
|
||||
if (exists) {
|
||||
if (retry < 1) return this.generatePlanNo(retry + 1);
|
||||
throw new Error("方案单号生成冲突,请重试");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [{ registrar: { [Op.like]: `%${query.keyword}%` } }, { planDesc: { [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 ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取项目方案列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取项目方案列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.planModel.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 record = await this.planModel.findByPk(id, { include: [this.detailModel] });
|
||||
if (!record) return { code: 404, message: "项目方案不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限访问该方案", data: null };
|
||||
return { code: 200, message: "获取项目方案详情成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, data.customerId))) return { code: 403, message: "无权限为该客户创建方案", data: null };
|
||||
const details = data.details || [];
|
||||
delete data.details;
|
||||
try {
|
||||
data.planNo = await this.generatePlanNo();
|
||||
const created = await this.planModel.create(data);
|
||||
if (details.length > 0) {
|
||||
await this.detailModel.bulkCreate(details.map((d: any) => ({ ...d, planId: created.id, customerId: created.customerId })));
|
||||
}
|
||||
const result = await this.planModel.findByPk(created.id, { include: [this.detailModel] });
|
||||
return { code: 200, message: "创建项目方案成功", data: result?.toJSON() };
|
||||
} catch (err: any) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
data.planNo = await this.generatePlanNo();
|
||||
const created = await this.planModel.create(data);
|
||||
if (details.length > 0) {
|
||||
await this.detailModel.bulkCreate(details.map((d: any) => ({ ...d, planId: created.id, customerId: created.customerId })));
|
||||
}
|
||||
const result = await this.planModel.findByPk(created.id, { include: [this.detailModel] });
|
||||
return { code: 200, message: "创建项目方案成功", data: result?.toJSON() };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.planModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目方案不存在", data: null };
|
||||
if (!(await this.isAdmin(ctx.state.user.userId))) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限修改该方案", data: null };
|
||||
delete data.customerId;
|
||||
}
|
||||
const details = data.details;
|
||||
delete data.details;
|
||||
delete data.planNo;
|
||||
await record.update(data);
|
||||
|
||||
if (details && Array.isArray(details)) {
|
||||
await this.detailModel.destroy({ where: { planId: id } });
|
||||
if (details.length > 0) {
|
||||
await this.detailModel.bulkCreate(details.map((d: any) => ({ ...d, planId: id, customerId: record.customerId })));
|
||||
}
|
||||
}
|
||||
const result = await this.planModel.findByPk(id, { include: [this.detailModel] });
|
||||
return { code: 200, message: "更新项目方案成功", data: result?.toJSON() };
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const record = await this.planModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "项目方案不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该方案", data: null };
|
||||
await this.detailModel.destroy({ where: { planId: id } });
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除项目方案成功", data: null };
|
||||
}
|
||||
|
||||
async findOptions(customerId: number): Promise<any> {
|
||||
const where: any = {};
|
||||
if (customerId) where.customerId = customerId;
|
||||
const records = await this.planModel.findAll({ where, order: [["created_at", "DESC"]] });
|
||||
return { code: 200, message: "获取项目方案选项成功", data: records.map((r) => r.toJSON()) };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op, UniqueConstraintError } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import SurveyOrder from "../entity/survey_order.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class SurveyOrderService {
|
||||
@InjectRepository(SurveyOrder)
|
||||
surveyModel!: Repository<SurveyOrder>;
|
||||
|
||||
@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 this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
private async generateSurveyNo(retry: number = 0): Promise<string> {
|
||||
const now = new Date();
|
||||
const d = new Date(now.getTime() + 8 * 60 * 60 * 1000);
|
||||
const yyyymmdd = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, "0")}${String(d.getUTCDate()).padStart(2, "0")}`;
|
||||
const prefix = `KC${yyyymmdd}`;
|
||||
const last = await this.surveyModel.findOne({ where: { surveyNo: { [Op.like]: `${prefix}%` } }, order: [["survey_no", "DESC"]] });
|
||||
let seq = 1;
|
||||
if (last) seq = (parseInt(last.surveyNo.slice(-3), 10) || 0) + 1;
|
||||
const result = `${prefix}${String(seq).padStart(3, "0")}`;
|
||||
const exists = await this.surveyModel.findOne({ where: { surveyNo: result } });
|
||||
if (exists) {
|
||||
if (retry < 1) return this.generateSurveyNo(retry + 1);
|
||||
throw new Error("勘察单号生成冲突,请重试");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [{ surveyor: { [Op.like]: `%${query.keyword}%` } }, { remark: { [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 ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取勘察派单列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取勘察派单列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.surveyModel.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 record = await this.surveyModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "勘察派单不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限访问该勘察派单", data: null };
|
||||
return { code: 200, message: "获取勘察派单详情成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, data.customerId))) return { code: 403, message: "无权限为该客户创建勘察派单", data: null };
|
||||
try {
|
||||
data.surveyNo = await this.generateSurveyNo();
|
||||
const created = await this.surveyModel.create(data);
|
||||
return { code: 200, message: "创建勘察派单成功", data: created.toJSON() };
|
||||
} catch (err: any) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
data.surveyNo = await this.generateSurveyNo();
|
||||
const created = await this.surveyModel.create(data);
|
||||
return { code: 200, message: "创建勘察派单成功", data: created.toJSON() };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.surveyModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "勘察派单不存在", data: null };
|
||||
if (!(await this.isAdmin(ctx.state.user.userId))) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限修改该勘察派单", data: null };
|
||||
delete data.customerId;
|
||||
}
|
||||
delete data.surveyNo;
|
||||
await record.update(data);
|
||||
return { code: 200, message: "更新勘察派单成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const record = await this.surveyModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "勘察派单不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该勘察派单", data: null };
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除勘察派单成功", data: null };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { Provide } from "@midwayjs/core";
|
||||
import { InjectRepository } from "@midwayjs/sequelize";
|
||||
import { Repository } from "sequelize-typescript";
|
||||
import { Op, UniqueConstraintError } from "sequelize";
|
||||
import { Context } from "@midwayjs/koa";
|
||||
import VisitRecord from "../entity/visit_record.entity";
|
||||
import Customer from "../entity/customer.entity";
|
||||
import SysUserRole from "../entity/sysUserRole.entity";
|
||||
import SysRole from "../entity/sysRole.entity";
|
||||
|
||||
@Provide()
|
||||
export class VisitRecordService {
|
||||
@InjectRepository(VisitRecord)
|
||||
visitModel!: Repository<VisitRecord>;
|
||||
|
||||
@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 this.userRoleModel.findAll({ where: { userId }, include: [this.roleModel] });
|
||||
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 this.customerModel.findByPk(customerId);
|
||||
return !!customer && customer.salesmanId === userId;
|
||||
}
|
||||
|
||||
private async generateVisitNo(retry: number = 0): Promise<string> {
|
||||
const now = new Date();
|
||||
const d = new Date(now.getTime() + 8 * 60 * 60 * 1000);
|
||||
const yyyymmdd = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, "0")}${String(d.getUTCDate()).padStart(2, "0")}`;
|
||||
const prefix = `HF${yyyymmdd}`;
|
||||
const last = await this.visitModel.findOne({ where: { visitNo: { [Op.like]: `${prefix}%` } }, order: [["visit_no", "DESC"]] });
|
||||
let seq = 1;
|
||||
if (last) seq = (parseInt(last.visitNo.slice(-3), 10) || 0) + 1;
|
||||
const result = `${prefix}${String(seq).padStart(3, "0")}`;
|
||||
const exists = await this.visitModel.findOne({ where: { visitNo: result } });
|
||||
if (exists) {
|
||||
if (retry < 1) return this.generateVisitNo(retry + 1);
|
||||
throw new Error("回访单号生成冲突,请重试");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async list(query: any, 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] = [
|
||||
{ visitor: { [Op.like]: `%${query.keyword}%` } },
|
||||
{ contactPerson: { [Op.like]: `%${query.keyword}%` } },
|
||||
{ contactPhone: { [Op.like]: `%${query.keyword}%` } },
|
||||
{ customerOpinion: { [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 ids = myCustomers.map((c) => c.id);
|
||||
if (ids.length === 0) return { code: 200, message: "获取客户回访列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
if (where.customerId !== undefined) {
|
||||
if (!ids.includes(where.customerId)) return { code: 200, message: "获取客户回访列表成功", data: { list: [], total: 0, page: Number(page), pageSize: Number(pageSize) } };
|
||||
} else {
|
||||
where.customerId = { [Op.in]: ids };
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await this.visitModel.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 record = await this.visitModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户回访不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限访问该回访记录", data: null };
|
||||
return { code: 200, message: "获取客户回访详情成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async create(data: any, ctx: Context): Promise<any> {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, data.customerId))) return { code: 403, message: "无权限为该客户创建回访记录", data: null };
|
||||
try {
|
||||
data.visitNo = await this.generateVisitNo();
|
||||
const created = await this.visitModel.create(data);
|
||||
return { code: 200, message: "创建客户回访成功", data: created.toJSON() };
|
||||
} catch (err: any) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
data.visitNo = await this.generateVisitNo();
|
||||
const created = await this.visitModel.create(data);
|
||||
return { code: 200, message: "创建客户回访成功", data: created.toJSON() };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: number, data: any, ctx: Context): Promise<any> {
|
||||
const record = await this.visitModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户回访不存在", data: null };
|
||||
if (!(await this.isAdmin(ctx.state.user.userId))) {
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限修改该回访记录", data: null };
|
||||
delete data.customerId;
|
||||
}
|
||||
delete data.visitNo;
|
||||
await record.update(data);
|
||||
return { code: 200, message: "更新客户回访成功", data: record.toJSON() };
|
||||
}
|
||||
|
||||
async delete(id: number, ctx: Context): Promise<any> {
|
||||
const record = await this.visitModel.findByPk(id);
|
||||
if (!record) return { code: 404, message: "客户回访不存在", data: null };
|
||||
if (!(await this.isCustomerOwner(ctx.state.user.userId, record.customerId))) return { code: 403, message: "无权限删除该回访记录", data: null };
|
||||
await record.destroy();
|
||||
return { code: 200, message: "删除客户回访成功", data: null };
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user