Files
smart_grid_iot/server/src/service/worker.service.ts
T
andyesfly 6b00f61550 feat: 新增现场勘察派单全流程业务能力
1.  新增共享电工管理模块:包含实体、DTO、控制器、服务以及前后端API
2.  重构勘察工单实体与查询逻辑,完善工单全状态流程与字段
3.  新增勘察SOP模板与工单回执业务体系
4.  更新数据库字典与初始化脚本,补充业务数据
5.  新增勘察派单增删改查、指派、复核、催办等全流程接口
6.  修复用户服务角色关联SQL生成问题
7.  新增勘察派单前后端完整交互链路
2026-09-15 08:50:55 +08:00

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 };
}
}