1. 新增共享电工管理模块:包含实体、DTO、控制器、服务以及前后端API 2. 重构勘察工单实体与查询逻辑,完善工单全状态流程与字段 3. 新增勘察SOP模板与工单回执业务体系 4. 更新数据库字典与初始化脚本,补充业务数据 5. 新增勘察派单增删改查、指派、复核、催办等全流程接口 6. 修复用户服务角色关联SQL生成问题 7. 新增勘察派单前后端完整交互链路
99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
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 Worker from "../entity/worker.entity";
|
|
|
|
// 电工状态:1在岗 0离职
|
|
const STATUS_ON_DUTY = 1;
|
|
// 推荐位数量
|
|
const RECOMMEND_LIMIT = 5;
|
|
// 经验分基准场次(达到即满分)
|
|
const EXP_BASE_COUNT = 50;
|
|
|
|
@Provide()
|
|
export class WorkerService {
|
|
@InjectRepository(Worker)
|
|
workerModel!: Repository<Worker>;
|
|
|
|
// 列表:姓名/手机号/资质模糊检索 + 状态过滤 + 分页
|
|
async list(query: any, ctx: Context): Promise<any> {
|
|
const page = Number(query.page) || 1;
|
|
const pageSize = Number(query.pageSize || query.limit) || 10;
|
|
|
|
const where: any = {};
|
|
if (query.keyword) {
|
|
where[Op.or] = [
|
|
{ workerName: { [Op.like]: `%${query.keyword}%` } },
|
|
{ phone: { [Op.like]: `%${query.keyword}%` } },
|
|
{ certInfo: { [Op.like]: `%${query.keyword}%` } }
|
|
];
|
|
}
|
|
if (query.status !== undefined && query.status !== "") where.status = Number(query.status);
|
|
|
|
const { count, rows } = await this.workerModel.findAndCountAll({
|
|
where,
|
|
limit: pageSize,
|
|
offset: (page - 1) * pageSize,
|
|
order: [["rating", "DESC"]],
|
|
distinct: true
|
|
});
|
|
|
|
return { code: 200, message: "获取共享电工列表成功", data: { list: rows.map((r) => r.toJSON()), total: count, page, pageSize } };
|
|
}
|
|
|
|
// 下拉选项:仅在岗电工
|
|
async options(ctx: Context): Promise<any> {
|
|
const rows = await this.workerModel.findAll({
|
|
where: { status: STATUS_ON_DUTY },
|
|
attributes: ["id", "workerName", "workerTitle", "rating", "certInfo", "certVerified", "completedCount"],
|
|
order: [["rating", "DESC"]]
|
|
});
|
|
return { code: 200, message: "获取共享电工选项成功", data: rows.map((r) => r.toJSON()) };
|
|
}
|
|
|
|
// 智能推荐:评分 50% + 资质 30% + 经验 20% 加权,取前 5 名
|
|
async recommend(query: any, ctx: Context): Promise<any> {
|
|
const rows = await this.workerModel.findAll({ where: { status: STATUS_ON_DUTY } });
|
|
if (rows.length === 0) return { code: 200, message: "获取推荐电工成功", data: [] };
|
|
|
|
const certKeyword: string = query.certKeyword || "";
|
|
const list = rows.map((row) => {
|
|
const certInfo: string = row.certInfo || "";
|
|
const rating = Number(row.rating) || 0;
|
|
const completedCount = Number(row.completedCount) || 0;
|
|
|
|
const scoreRating = rating / 5;
|
|
let scoreCert = row.certVerified ? (certInfo ? 1 : 0.7) : 0.4;
|
|
// 关键词命中持证资质时资质分置满
|
|
if (certKeyword && certInfo.includes(certKeyword)) scoreCert = 1;
|
|
const scoreExp = Math.min(completedCount / EXP_BASE_COUNT, 1);
|
|
|
|
const score = 0.5 * scoreRating + 0.3 * scoreCert + 0.2 * scoreExp;
|
|
|
|
return {
|
|
id: row.id,
|
|
workerName: row.workerName,
|
|
workerTitle: row.workerTitle,
|
|
rating,
|
|
certInfo,
|
|
certVerified: row.certVerified,
|
|
completedCount,
|
|
matchRate: Number((score * 100).toFixed(1)),
|
|
rank: 0,
|
|
isTop: false
|
|
};
|
|
});
|
|
|
|
list.sort((a, b) => b.matchRate - a.matchRate || b.rating - a.rating || b.completedCount - a.completedCount);
|
|
|
|
const data = list.slice(0, RECOMMEND_LIMIT).map((item, index) => {
|
|
const rank = index + 1;
|
|
return { ...item, rank, isTop: rank === 1 };
|
|
});
|
|
|
|
return { code: 200, message: "获取推荐电工成功", data };
|
|
}
|
|
}
|