Files
shz-backend/sql/migration_external_job_import.sql

222 lines
11 KiB
MySQL
Raw Normal View History

-- 外部 Excel 岗位全量快照同步。
-- 执行目标HighGo/PostgreSQL 的 shz schema。
-- 本脚本为增量 migration不删除现有 job、company、row_work 或监测数据。
BEGIN;
-- 串行化本 migration 的并发执行,避免两个发布进程同时计算 MAX(id)+1。
-- 锁仅持续到本事务提交;重复执行不会改变已存在的业务记录或 Quartz 状态。
SELECT pg_advisory_xact_lock(hashtext('shz.migration_external_job_import'));
ALTER TABLE "shz"."job"
ADD COLUMN IF NOT EXISTS "external_import_flag" CHAR(1) NOT NULL DEFAULT '0',
ADD COLUMN IF NOT EXISTS "external_source_code" VARCHAR(64),
ADD COLUMN IF NOT EXISTS "external_source_name" VARCHAR(200),
ADD COLUMN IF NOT EXISTS "job_fingerprint" VARCHAR(64),
ADD COLUMN IF NOT EXISTS "job_content_fingerprint" VARCHAR(64),
ADD COLUMN IF NOT EXISTS "external_batch_id" VARCHAR(64),
ADD COLUMN IF NOT EXISTS "external_first_seen_at" TIMESTAMP,
ADD COLUMN IF NOT EXISTS "external_last_seen_at" TIMESTAMP;
COMMENT ON COLUMN "shz"."job"."external_import_flag" IS '是否外部 Excel 导入岗位0否1是';
COMMENT ON COLUMN "shz"."job"."job_fingerprint" IS '外部岗位身份指纹FP_V1 + 来源编码 + 标准化岗位链接';
COMMENT ON COLUMN "shz"."job"."job_content_fingerprint" IS '外部岗位内容指纹:用于同一身份岗位的原地更新';
CREATE TABLE IF NOT EXISTS "shz"."job_import_batch" (
"batch_id" VARCHAR(64) PRIMARY KEY,
"file_id" VARCHAR(64) NOT NULL,
"file_name" VARCHAR(500) NOT NULL,
"file_sha256" VARCHAR(64) NOT NULL,
"snapshot_scope" TEXT,
"fingerprint_version" VARCHAR(16) NOT NULL,
"status" VARCHAR(20) NOT NULL,
"total_rows" INTEGER NOT NULL DEFAULT 0,
"valid_rows" INTEGER NOT NULL DEFAULT 0,
"invalid_rows" INTEGER NOT NULL DEFAULT 0,
"duplicate_rows" INTEGER NOT NULL DEFAULT 0,
"insert_count" INTEGER NOT NULL DEFAULT 0,
"update_count" INTEGER NOT NULL DEFAULT 0,
"keep_count" INTEGER NOT NULL DEFAULT 0,
"restore_count" INTEGER NOT NULL DEFAULT 0,
"delete_count" INTEGER NOT NULL DEFAULT 0,
"error_summary" TEXT,
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completed_at" TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "shz"."job_import_row" (
"row_id" BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
"batch_id" VARCHAR(64) NOT NULL,
"excel_row_number" INTEGER NOT NULL,
"source_name" VARCHAR(200),
"source_code" VARCHAR(64),
"job_fingerprint" VARCHAR(64),
"content_fingerprint" VARCHAR(64),
"validation_status" VARCHAR(20) NOT NULL,
"result_status" VARCHAR(20) NOT NULL,
"error_message" TEXT,
"raw_payload" TEXT,
"job_id" BIGINT,
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "shz"."job_import_source_alias" (
"alias_name" VARCHAR(200) PRIMARY KEY,
"source_code" VARCHAR(64) NOT NULL,
"website_id" BIGINT,
"is_active" CHAR(1) NOT NULL DEFAULT '1',
"snapshot_required" CHAR(1) NOT NULL DEFAULT '1',
"remark" VARCHAR(500),
"create_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_time" TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS "uk_job_external_active_fingerprint"
ON "shz"."job" ("job_fingerprint")
WHERE "external_import_flag" = '1' AND "del_flag" = '0';
CREATE INDEX IF NOT EXISTS "idx_job_import_batch_sha_status"
ON "shz"."job_import_batch" ("file_sha256", "status");
CREATE INDEX IF NOT EXISTS "idx_job_import_row_batch"
ON "shz"."job_import_row" ("batch_id", "excel_row_number");
CREATE INDEX IF NOT EXISTS "idx_job_external_source_active"
ON "shz"."job" ("external_source_code", "external_import_flag", "del_flag");
-- Quartz 任务默认暂停,防止尚未配置 external-job-import URL/token 时发生无效拉取。
-- 完成环境配置后,在“定时任务”中启用即可;计划为每周三、周六 03:00。
INSERT INTO "shz"."sys_job" (
"job_id", "job_name", "job_group", "invoke_target", "cron_expression",
"misfire_policy", "concurrent", "status", "create_by", "create_time",
"update_by", "update_time", "remark"
)
SELECT
(SELECT COALESCE(MAX("job_id"), 0) + 1 FROM "shz"."sys_job"),
'外部岗位全量同步',
'DEFAULT',
'jobCron.importLatestExternalSnapshot()',
'0 0 3 ? * WED,SAT',
'3',
'1',
'1',
'system',
CURRENT_TIMESTAMP,
'',
NULL,
'外部 Excel 全量快照同步;配置 external-job-import 后由管理员启用'
WHERE NOT EXISTS (
SELECT 1 FROM "shz"."sys_job" WHERE "invoke_target" = 'jobCron.importLatestExternalSnapshot()'
);
-- 为样例及当前采集范围初始化来源管理记录。名称相近的来源不自动合并。
INSERT INTO "shz"."website_management" ("website_name", "website_url", "is_active", "data_type", "del_flag", "create_by", "create_time", "remark")
SELECT seed.website_name, '', '1', '2', '0', 'system', CURRENT_TIMESTAMP, '外部 Excel 岗位采集来源'
FROM (VALUES
('石河子招聘网'), ('智联招聘'), ('天互招聘网'), ('猎聘网'), ('乌鲁木齐快聘网'), ('前程无忧')
) AS seed(website_name)
WHERE NOT EXISTS (
SELECT 1 FROM "shz"."website_management" existing
WHERE existing."website_name" = seed.website_name AND existing."del_flag" = '0'
);
-- 以 alias_name 为幂等键;若此前仅完成了部分迁移,重跑时会补齐网站关联。
INSERT INTO "shz"."job_import_source_alias" AS target ("alias_name", "source_code", "website_id", "is_active", "snapshot_required", "remark")
SELECT seed.alias_name, seed.source_code, website."website_id", '1', '1', '外部 Excel ORG 来源映射'
FROM (VALUES
('石河子招聘网', 'shz_job_net'),
('智联招聘', 'zhaopin'),
('天互招聘网', 'tianhu_job'),
('猎聘网', 'liepin'),
('乌鲁木齐快聘网', 'wlmq_fast_job'),
('前程无忧', '51job')
) AS seed(alias_name, source_code)
LEFT JOIN "shz"."website_management" website ON website."website_name" = seed.alias_name AND website."del_flag" = '0'
ON CONFLICT ("alias_name") DO UPDATE
SET "source_code" = EXCLUDED."source_code",
"website_id" = EXCLUDED."website_id",
"is_active" = EXCLUDED."is_active",
"snapshot_required" = EXCLUDED."snapshot_required",
"remark" = EXCLUDED."remark",
"update_time" = CURRENT_TIMESTAMP
WHERE target."source_code" IS DISTINCT FROM EXCLUDED."source_code"
OR target."website_id" IS DISTINCT FROM EXCLUDED."website_id"
OR target."is_active" IS DISTINCT FROM EXCLUDED."is_active"
OR target."snapshot_required" IS DISTINCT FROM EXCLUDED."snapshot_required"
OR target."remark" IS DISTINCT FROM EXCLUDED."remark";
-- 四字菜单:来源管理、入库监测、采集趋势、统计分析。
INSERT INTO "shz"."sys_menu" (
"menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "route_name",
"is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "remark"
)
SELECT (SELECT COALESCE(MAX("menu_id"), 0) + 1 FROM "shz"."sys_menu"),
'招聘信息采集监测分析', 0, 99, 'recruit-monitor', '', '', 'RecruitMonitor',
1, 0, 'M', '0', '0', 'cms:recruit:root', 'chart', 'system', CURRENT_TIMESTAMP, '外部岗位采集监测分析根菜单'
WHERE NOT EXISTS (SELECT 1 FROM "shz"."sys_menu" WHERE "perms" = 'cms:recruit:root');
INSERT INTO "shz"."sys_menu" (
"menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "route_name",
"is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "remark"
)
SELECT ids."base_id" + ROW_NUMBER() OVER (ORDER BY seed.order_num),
seed.menu_name, parent."menu_id", seed.order_num, seed.path, seed.component, '', seed.route_name,
1, 0, 'C', '0', '0', seed.perms, 'table', 'system', CURRENT_TIMESTAMP, seed.remark
FROM (VALUES
('来源管理', 1, 'recruit/source', 'Recruit/Source/index', 'RecruitSource', 'cms:recruit:source:list', '外部岗位来源与别名管理'),
('入库监测', 2, 'recruit/monitor', 'Recruit/Monitor/index', 'RecruitMonitor', 'cms:recruit:monitor:list', '外部岗位导入批次监测'),
('采集趋势', 3, 'recruit/trend', 'Recruit/Trend/index', 'RecruitTrend', 'cms:recruit:trend:list', '外部岗位采集趋势'),
('统计分析', 4, 'recruit/analysis', 'Recruit/Analysis/index', 'RecruitAnalysis', 'cms:recruit:analysis:list', '外部岗位统计分析')
) AS seed(menu_name, order_num, path, component, route_name, perms, remark)
JOIN "shz"."sys_menu" parent ON parent."perms" = 'cms:recruit:root'
CROSS JOIN (SELECT COALESCE(MAX("menu_id"), 0) AS "base_id" FROM "shz"."sys_menu") ids
WHERE NOT EXISTS (SELECT 1 FROM "shz"."sys_menu" existing WHERE existing."perms" = seed.perms);
INSERT INTO "shz"."sys_menu" (
"menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "route_name",
"is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "remark"
)
SELECT (SELECT COALESCE(MAX("menu_id"), 0) + 1 FROM "shz"."sys_menu"),
'手动导入', parent."menu_id", 1, '', '', '', '', 1, 0, 'F', '0', '0',
'cms:recruit:import:manual', '#', 'system', CURRENT_TIMESTAMP, '手动拉取并导入最新外部 Excel'
FROM "shz"."sys_menu" parent
WHERE parent."perms" = 'cms:recruit:monitor:list'
AND NOT EXISTS (SELECT 1 FROM "shz"."sys_menu" WHERE "perms" = 'cms:recruit:import:manual');
INSERT INTO "shz"."sys_menu" (
"menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "route_name",
"is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "remark"
)
SELECT (SELECT COALESCE(MAX("menu_id"), 0) + 1 FROM "shz"."sys_menu"),
'来源维护', parent."menu_id", 1, '', '', '', '', 1, 0, 'F', '0', '0',
'cms:recruit:source:edit', '#', 'system', CURRENT_TIMESTAMP, '维护外部来源和来源别名'
FROM "shz"."sys_menu" parent
WHERE parent."perms" = 'cms:recruit:source:list'
AND NOT EXISTS (SELECT 1 FROM "shz"."sys_menu" WHERE "perms" = 'cms:recruit:source:edit');
INSERT INTO "shz"."sys_menu" (
"menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "route_name",
"is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "remark"
)
SELECT (SELECT COALESCE(MAX("menu_id"), 0) + 1 FROM "shz"."sys_menu"),
'监测维护', parent."menu_id", 2, '', '', '', '', 1, 0, 'F', '0', '0',
'cms:recruit:monitor:edit', '#', 'system', CURRENT_TIMESTAMP, '维护导入监测记录'
FROM "shz"."sys_menu" parent
WHERE parent."perms" = 'cms:recruit:monitor:list'
AND NOT EXISTS (SELECT 1 FROM "shz"."sys_menu" WHERE "perms" = 'cms:recruit:monitor:edit');
INSERT INTO "shz"."sys_role_menu" ("role_id", "menu_id")
SELECT role."role_id", menu."menu_id"
FROM "shz"."sys_role" role
JOIN "shz"."sys_menu" menu ON menu."perms" IN (
'cms:recruit:root', 'cms:recruit:source:list', 'cms:recruit:monitor:list',
'cms:recruit:trend:list', 'cms:recruit:analysis:list', 'cms:recruit:import:manual',
'cms:recruit:source:edit', 'cms:recruit:monitor:edit'
)
WHERE role."role_key" = 'admin'
AND NOT EXISTS (
SELECT 1 FROM "shz"."sys_role_menu" existing
WHERE existing."role_id" = role."role_id" AND existing."menu_id" = menu."menu_id"
);
COMMIT;