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

227 lines
16 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 项目勘察(现场勘察派单)功能扩展
-- 背景:db_init.sql 中 survey_order 原为 8 字段的极简结构(0已派发/1已接单/2已完成),
-- 现按「现场勘察派单台账」「现场勘察派单向导」两份 UI 稿完整落地,并引入
-- SOP 模板检查项 + 工单回执两张表,同时补齐共享电工的评分/资质/履约数据。
-- 幂等:全部使用 IF NOT EXISTS / ON CONFLICT,可重复执行。
BEGIN;
-- ------------------------------------------------------------
-- 1) 状态机迁移(必须先于 updated_at 建列执行,用于判定是否已迁移过)
-- 旧:0已派发 1已接单 2已完成
-- 新:0待指派 1现场勘察中 2待技术复核 3已归档 4预留 5已取消
-- 映射:0→0、1→1、2→3
-- ------------------------------------------------------------
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'survey_order' AND column_name = 'updated_at'
) THEN
UPDATE survey_order SET status = 3 WHERE status = 2;
END IF;
END $$;
-- ------------------------------------------------------------
-- 2) survey_order 业务字段扩展
-- ------------------------------------------------------------
-- 派工属性
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS priority SMALLINT DEFAULT 3; -- 优先级:1特急P1 2加急P2 3常规P3
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS task_title VARCHAR(128); -- 勘察任务标题
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS site_location VARCHAR(255); -- 具体勘察物理区域
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS source VARCHAR(32) DEFAULT 'ledger'; -- 工单来源:ledger台账派工 visit拜访转勘察 customer客户发起
-- 现场测绘规格
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS loop_spec VARCHAR(128); -- 主进线规约与断路器容量
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS loop_count INTEGER; -- 预估分支测绘回路数
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS progress_done INTEGER DEFAULT 0; -- 已完成测绘回路数
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS progress_total INTEGER; -- 应测绘回路总数
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS drawing_count INTEGER DEFAULT 0; -- 待技术复核图纸数
-- 责任人与时效
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS salesman_id BIGINT; -- 责任业务员ID(→ sys_user)
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS tech_contact VARCHAR(128); -- 现场技术联系人
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS plan_start TIMESTAMPTZ; -- 计划到场窗口开始
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS plan_end TIMESTAMPTZ; -- 计划到场窗口结束
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS expect_minutes INTEGER; -- 要求接单后到场时限(分钟)
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS expect_arrival_at TIMESTAMPTZ; -- 期望到场时限(指派时按 expect_minutes 推算)
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS sla_level VARCHAR(32); -- 履约质保要求
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS urge_count INTEGER DEFAULT 0; -- 催办次数
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS last_urged_at TIMESTAMPTZ; -- 最近催办时间
-- 派单草稿(向导「保存为草稿」,台账与统计默认排除草稿)
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS is_draft SMALLINT NOT NULL DEFAULT 0;
-- 报酬
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS estimated_fee NUMERIC(10,2); -- 预估劳务费(基础出场费+回路补贴)
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS actual_fee NUMERIC(10,2); -- 实际结算劳务费
-- 成果与复核
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS photos VARCHAR(1024); -- 现场取证照片对象key(JSON数组字符串)
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS review_remark TEXT; -- 技术复核意见
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS reviewed_at TIMESTAMPTZ; -- 技术复核时间
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS cancel_reason VARCHAR(255); -- 取消原因
-- 更新时间(Sequelize 实体已开启 updated_at,缺列会导致写入失败)
ALTER TABLE survey_order ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
-- 3) 字段注释
COMMENT ON COLUMN survey_order.priority IS '优先级:1特急P1 2加急P2 3常规P3';
COMMENT ON COLUMN survey_order.task_title IS '勘察任务标题';
COMMENT ON COLUMN survey_order.site_location IS '具体勘察物理区域';
COMMENT ON COLUMN survey_order.source IS '工单来源:ledger台账派工 visit拜访转勘察 customer客户发起';
COMMENT ON COLUMN survey_order.loop_spec IS '主进线规约与断路器容量';
COMMENT ON COLUMN survey_order.loop_count IS '预估分支测绘回路数';
COMMENT ON COLUMN survey_order.progress_done IS '已完成测绘回路数';
COMMENT ON COLUMN survey_order.progress_total IS '应测绘回路总数';
COMMENT ON COLUMN survey_order.drawing_count IS '待技术复核图纸数';
COMMENT ON COLUMN survey_order.salesman_id IS '责任业务员ID(→ sys_user)';
COMMENT ON COLUMN survey_order.tech_contact IS '现场技术联系人';
COMMENT ON COLUMN survey_order.plan_start IS '计划到场窗口开始';
COMMENT ON COLUMN survey_order.plan_end IS '计划到场窗口结束';
COMMENT ON COLUMN survey_order.expect_minutes IS '要求接单后到场时限(分钟)';
COMMENT ON COLUMN survey_order.expect_arrival_at IS '期望到场时限(指派时按 expect_minutes 推算)';
COMMENT ON COLUMN survey_order.sla_level IS '履约质保要求';
COMMENT ON COLUMN survey_order.urge_count IS '催办次数';
COMMENT ON COLUMN survey_order.last_urged_at IS '最近催办时间';
COMMENT ON COLUMN survey_order.is_draft IS '是否草稿:0正式工单 1草稿';
COMMENT ON COLUMN survey_order.estimated_fee IS '预估劳务费(基础出场费+回路补贴)';
COMMENT ON COLUMN survey_order.actual_fee IS '实际结算劳务费';
COMMENT ON COLUMN survey_order.photos IS '现场取证照片对象key(JSON数组字符串)';
COMMENT ON COLUMN survey_order.review_remark IS '技术复核意见';
COMMENT ON COLUMN survey_order.reviewed_at IS '技术复核时间';
COMMENT ON COLUMN survey_order.cancel_reason IS '取消原因';
COMMENT ON COLUMN survey_order.updated_at IS '更新时间';
COMMENT ON COLUMN survey_order.status IS '状态:0待指派 1现场勘察中 2待技术复核 3已归档 4预留 5已取消';
-- 4) 查询索引(台账按状态/优先级/计划窗口/业务员过滤与排序)
CREATE INDEX IF NOT EXISTS idx_survey_status ON survey_order (status);
CREATE INDEX IF NOT EXISTS idx_survey_priority ON survey_order (priority);
CREATE INDEX IF NOT EXISTS idx_survey_salesman ON survey_order (salesman_id);
CREATE INDEX IF NOT EXISTS idx_survey_plan_start ON survey_order (plan_start);
-- 5) survey_order 更新时间触发器(与 customer 等表保持一致的库级约定)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_survey_order_updated_at') THEN
CREATE TRIGGER trg_survey_order_updated_at BEFORE UPDATE ON survey_order
FOR EACH ROW EXECUTE FUNCTION trigger_set_updated_at();
END IF;
END $$;
-- ------------------------------------------------------------
-- 6) 共享电工域:补齐评分/资质/履约数据(不做真实距离,无经纬度字段)
-- ------------------------------------------------------------
ALTER TABLE worker ADD COLUMN IF NOT EXISTS worker_title VARCHAR(32); -- 职衔:电工/资深班组长/特种高压电工
ALTER TABLE worker ADD COLUMN IF NOT EXISTS rating NUMERIC(3,2) DEFAULT 5.00; -- 服务评分(0-5)
ALTER TABLE worker ADD COLUMN IF NOT EXISTS cert_info VARCHAR(128); -- 持证资质
ALTER TABLE worker ADD COLUMN IF NOT EXISTS cert_verified BOOLEAN DEFAULT TRUE; -- 资质是否已核验
ALTER TABLE worker ADD COLUMN IF NOT EXISTS completed_count INTEGER DEFAULT 0; -- 历史累计勘察场次
COMMENT ON COLUMN worker.worker_title IS '职衔:电工/资深班组长/特种高压电工';
COMMENT ON COLUMN worker.rating IS '服务评分(0-5)';
COMMENT ON COLUMN worker.cert_info IS '持证资质';
COMMENT ON COLUMN worker.cert_verified IS '资质是否已核验';
COMMENT ON COLUMN worker.completed_count IS '历史累计勘察场次';
-- ------------------------------------------------------------
-- 7) 勘察 SOP 模板检查项表(向导第 2 步「标准配电改造模板」)
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS survey_sop_item (
id BIGSERIAL PRIMARY KEY,
template_code VARCHAR(64) NOT NULL DEFAULT 'standard_dist',
sort_order INTEGER NOT NULL DEFAULT 0,
item_name VARCHAR(128) NOT NULL,
item_desc VARCHAR(255),
receipt_type VARCHAR(16) NOT NULL DEFAULT 'photo',
receipt_require VARCHAR(64),
required BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
COMMENT ON TABLE survey_sop_item IS '勘察 SOP 模板检查项表';
COMMENT ON COLUMN survey_sop_item.id IS '自增主键';
COMMENT ON COLUMN survey_sop_item.template_code IS 'SOP模板编码:standard_dist标准配电改造模板';
COMMENT ON COLUMN survey_sop_item.sort_order IS '检查项序号';
COMMENT ON COLUMN survey_sop_item.item_name IS '检查项名称';
COMMENT ON COLUMN survey_sop_item.item_desc IS '检查项说明';
COMMENT ON COLUMN survey_sop_item.receipt_type IS '回执形式:photo现场照片 form清单表单 meter测仪截图';
COMMENT ON COLUMN survey_sop_item.receipt_require IS '回执指标要求,如「照片 ≥ 2 张」';
COMMENT ON COLUMN survey_sop_item.required IS '是否必填';
COMMENT ON COLUMN survey_sop_item.created_at IS '创建时间';
CREATE UNIQUE INDEX IF NOT EXISTS uk_sop_template_order ON survey_sop_item (template_code, sort_order);
-- ------------------------------------------------------------
-- 8) 勘察 SOP 工单回执表(每张勘察工单的执行回执)
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS survey_sop_result (
id BIGSERIAL PRIMARY KEY,
survey_order_id BIGINT NOT NULL REFERENCES survey_order(id) ON DELETE CASCADE,
sop_item_id BIGINT NOT NULL REFERENCES survey_sop_item(id),
checked BOOLEAN NOT NULL DEFAULT FALSE,
receipt_value VARCHAR(1024),
remark VARCHAR(255),
checked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
COMMENT ON TABLE survey_sop_result IS '勘察 SOP 工单回执表';
COMMENT ON COLUMN survey_sop_result.id IS '自增主键';
COMMENT ON COLUMN survey_sop_result.survey_order_id IS '勘察工单ID(→ survey_order)';
COMMENT ON COLUMN survey_sop_result.sop_item_id IS 'SOP检查项ID(→ survey_sop_item)';
COMMENT ON COLUMN survey_sop_result.checked IS '是否已完成回执';
COMMENT ON COLUMN survey_sop_result.receipt_value IS '回执内容(照片对象key数组/表单值/截图key)';
COMMENT ON COLUMN survey_sop_result.remark IS '回执备注';
COMMENT ON COLUMN survey_sop_result.checked_at IS '回执时间';
COMMENT ON COLUMN survey_sop_result.created_at IS '创建时间';
COMMENT ON COLUMN survey_sop_result.updated_at IS '更新时间';
CREATE UNIQUE INDEX IF NOT EXISTS uk_sop_result_order_item ON survey_sop_result (survey_order_id, sop_item_id);
CREATE INDEX IF NOT EXISTS idx_sop_result_order ON survey_sop_result (survey_order_id);
-- ------------------------------------------------------------
-- 9) SOP 模板检查项种子数据(标准配电改造模板 5 项)
-- ------------------------------------------------------------
INSERT INTO survey_sop_item (template_code, sort_order, item_name, item_desc, receipt_type, receipt_require, required) VALUES
('standard_dist', 1, '变压器铭牌与备用容量', '拍摄变压器铭牌并核对备用容量', 'photo', '现场照片 ≥ 2 张', TRUE),
('standard_dist', 2, '母排厚度与电缆线径测绘', '测绘母排厚度与电缆线径', 'photo', '现场照片 ≥ 3 张', TRUE),
('standard_dist', 3, '老旧断路器脱扣特性清查', '清查老旧断路器脱扣特性', 'form', '清单格式·表单直填', TRUE),
('standard_dist', 4, 'DIN导轨剩余安装空间实测','实测配电箱内 DIN 导轨剩余空间','photo', '照片回传·含卷尺 1 张', TRUE),
('standard_dist', 5, '竖井4G/LoRa物联网场强测试','测试竖井物联网信号场强', 'meter', '手持测仪·信号截图', TRUE)
ON CONFLICT (template_code, sort_order) DO UPDATE SET
item_name = EXCLUDED.item_name,
item_desc = EXCLUDED.item_desc,
receipt_type = EXCLUDED.receipt_type,
receipt_require = EXCLUDED.receipt_require,
required = EXCLUDED.required;
-- ------------------------------------------------------------
-- 10) 共享电工种子数据(评分/资质/履约,供向导候选推荐使用)
-- ------------------------------------------------------------
INSERT INTO worker (worker_name, phone, status, worker_title, rating, cert_info, cert_verified, completed_count)
SELECT '赵志强', '13900000001', 1, '特种高压电工', 4.98, '特种低压电工证', TRUE, 24
WHERE NOT EXISTS (SELECT 1 FROM worker WHERE phone = '13900000001');
INSERT INTO worker (worker_name, phone, status, worker_title, rating, cert_info, cert_verified, completed_count)
SELECT '孙明远', '13900000002', 1, '继电保护技师', 4.92, '继电保护作业证', TRUE, 31
WHERE NOT EXISTS (SELECT 1 FROM worker WHERE phone = '13900000002');
INSERT INTO worker (worker_name, phone, status, worker_title, rating, cert_info, cert_verified, completed_count)
SELECT '王建国', '13900000003', 1, '资深班组长', 4.92, '高低压带电作业资质', TRUE, 68
WHERE NOT EXISTS (SELECT 1 FROM worker WHERE phone = '13900000003');
INSERT INTO worker (worker_name, phone, status, worker_title, rating, cert_info, cert_verified, completed_count)
SELECT '周正海', '13900000004', 1, '电气试验工程师', 4.88, '电气试验资质', TRUE, 12
WHERE NOT EXISTS (SELECT 1 FROM worker WHERE phone = '13900000004');
-- ------------------------------------------------------------
-- 11) 勘察派单价格种子数据(向导第 3 步报酬拆分规则)
-- 应付勘察劳务总报酬 = 基础出场工时费 350.00 + 回路数 × 5.00
-- ------------------------------------------------------------
INSERT INTO dispatch_price (price_code, dispatch_type, price_type, unit_price) VALUES
('SURVEY_BASE_FEE', 'survey', '基础出场工时费', 350.00),
('SURVEY_LOOP_FEE', 'survey', '回路测点补贴(元/路)', 5.00)
ON CONFLICT (price_code) DO UPDATE SET
dispatch_type = EXCLUDED.dispatch_type,
price_type = EXCLUDED.price_type,
unit_price = EXCLUDED.unit_price;
COMMIT;