-- ===================================================================== -- 2026-08-27 CRM 升级 - PostgreSQL 17 -- ===================================================================== -- 背景: -- 1) 旧版库(archive/init_pg17.sql + 各增量升级脚本)中以下三个表的 -- biz_type CHECK 约束缺少 'crm' 值,而 CRM 拓展功能会写入 -- biz_type = 'crm'(日报反写 CRM 拜访记录、CRM 拓展新增拜访记录、 -- 外勤打卡关联 CRM 拓展、日报消息关联 CRM 拓展),会被旧约束拒绝。 -- 2) CRM 拓展主表 crm_crm_expansion 与多联系人子表 -- crm_crm_expansion_contact 的建表脚本在项目 SQL 中缺失,本次补齐, -- 结构以当前库实际定义为准(含主键、外键、索引,无触发器/注释, -- 子表无外键约束,与现有库保持一致)。 -- 3) 新增 crm_oms_dict_mapping:CRM 与 OMS 字典码值映射表,用于商机 -- 推送 OMS / OMS 导入回传时的字典码值转换。 -- 说明:init_full_pg17.sql(全新初始化)已包含正确约束;本脚本用于 -- 旧库增量升级。对新库执行同样安全(幂等:create if not exists、 -- drop constraint if exists)。 begin; set search_path to public; -- 1) crm_expansion_followup:跟进/拜访记录表,需支持 sales/channel/crm alter table crm_expansion_followup drop constraint if exists crm_expansion_followup_biz_type_check; alter table crm_expansion_followup add constraint crm_expansion_followup_biz_type_check check (biz_type in ('sales', 'channel', 'crm')); -- 2) work_checkin:外勤打卡,可关联 sales/channel/opportunity/crm alter table work_checkin drop constraint if exists work_checkin_biz_type_check; alter table work_checkin add constraint work_checkin_biz_type_check check (biz_type is null or biz_type in ('sales', 'channel', 'opportunity', 'crm')); -- 3) work_report_message:日报消息,可关联 sales/channel/opportunity/crm alter table work_report_message drop constraint if exists work_report_message_biz_type_check; alter table work_report_message add constraint work_report_message_biz_type_check check (biz_type is null or biz_type in ('sales', 'channel', 'opportunity', 'crm')); -- 4) CRM 拓展主表(结构与当前库实际定义一致) create table if not exists crm_crm_expansion ( id bigint generated by default as identity primary key, end_user varchar(200) not null, office_name varchar(200) not null, industry_attr varchar(200), extension_type varchar(50) not null, purchase_date date, warranty_expiry date, online_status varchar(50) not null, contact_name varchar(100), contact_phone varchar(50), contact_title varchar(100), supplier_id bigint, h3c_contact_id bigint, has_expansion_opportunity varchar(10), owner_user_id bigint not null, remark text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint fk_crm_crm_expansion_supplier foreign key (supplier_id) references crm_channel_expansion(id), constraint fk_crm_crm_expansion_h3c_contact foreign key (h3c_contact_id) references crm_sales_expansion(id) ); create index if not exists idx_crm_crm_expansion_owner on crm_crm_expansion (owner_user_id); create index if not exists idx_crm_crm_expansion_supplier on crm_crm_expansion (supplier_id); -- 5) CRM 拓展多联系人子表(结构与当前库实际定义一致) create table if not exists crm_crm_expansion_contact ( id bigint generated by default as identity primary key, crm_expansion_id bigint not null, contact_name varchar(255), contact_mobile varchar(64), contact_title varchar(128), sort_order integer not null default 0, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create index if not exists idx_crm_expansion_contact_expansion on crm_crm_expansion_contact (crm_expansion_id); -- 5.5) CRM 拓展主表新增字段:软件点数 / 扩容时间 / 扩容规模 -- 软件点数:整数;扩容时间:日期(与采购时间/过保时间一致用 date); -- 扩容规模:文本。 alter table crm_crm_expansion add column if not exists software_points int; alter table crm_crm_expansion add column if not exists expansion_time date; alter table crm_crm_expansion add column if not exists expansion_scale varchar(100); comment on column crm_crm_expansion.software_points is '软件点数'; comment on column crm_crm_expansion.expansion_time is '扩容时间'; comment on column crm_crm_expansion.expansion_scale is '扩容规模'; -- 5.6) CRM 拓展主表新增字段:进货商名称 / 新华三对接人名称 -- 需求:进货商、新华三对接人在下拉里没有想选的人时,可直接手动输入文字保存。 -- 因此除保留 supplier_id / h3c_contact_id 关联外,新增名称冗余字段: -- - 下拉选中时:存 id,并冗余存名称(supplier_name / h3c_contact_name) -- - 手动输入时:supplier_id / h3c_contact_id 置空,名称存文本 -- 列表/详情展示以名称字段优先,历史仅有 id 的旧数据回退用 JOIN 关联名称。 alter table crm_crm_expansion add column if not exists supplier_name varchar(255); alter table crm_crm_expansion add column if not exists h3c_contact_name varchar(255); comment on column crm_crm_expansion.supplier_name is '进货商名称(下拉选中冗余存名;手动输入时存文字)'; comment on column crm_crm_expansion.h3c_contact_name is '新华三对接人名称(下拉选中冗余存名;手动输入时存文字)'; -- 5.7) CRM 拓展主表新增字段:是否有维保项目机会 -- 使用系统字典 sys_is:1是 / 0否 alter table crm_crm_expansion add column if not exists has_maintenance_opportunity varchar(10); comment on column crm_crm_expansion.has_maintenance_opportunity is '是否有维保项目机会(系统是否字典 sys_is:1是 / 0否)'; -- 6) CRM 与 OMS 字典码值映射表:用于商机推送 OMS / OMS 导入回传时做字典码值转换 create table if not exists crm_oms_dict_mapping ( id bigint generated by default as identity primary key, dict_type varchar(50) not null, crm_value varchar(100) not null, crm_label varchar(200), oms_value varchar(100) not null, oms_label varchar(200), remark varchar(500), status smallint not null default 1, is_default boolean not null default false, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uk_crm_oms_dict_mapping unique (dict_type, crm_value, oms_value) ); comment on table crm_oms_dict_mapping is 'CRM与OMS字典码值映射表'; comment on column crm_oms_dict_mapping.dict_type is '字典类型(如 opportunity_stage 商机阶段、opportunity_type 商机类型)'; comment on column crm_oms_dict_mapping.crm_value is 'CRM侧字典码值(item_value)'; comment on column crm_oms_dict_mapping.crm_label is 'CRM侧字典标签(冗余,便于核对)'; comment on column crm_oms_dict_mapping.oms_value is 'OMS侧码值'; comment on column crm_oms_dict_mapping.oms_label is 'OMS侧标签(冗余,便于核对)'; comment on column crm_oms_dict_mapping.remark is '备注'; comment on column crm_oms_dict_mapping.status is '状态:1 启用 0 停用'; comment on column crm_oms_dict_mapping.is_default is '是否为默认映射:true 是 false 否(用于CRM推OMS时的默认匹配)'; comment on column crm_oms_dict_mapping.created_at is '创建时间'; comment on column crm_oms_dict_mapping.updated_at is '更新时间'; -- 7) 初始化数据:项目阶段(sj_xmjd)映射 insert into crm_oms_dict_mapping (dict_type, crm_value, crm_label, oms_value, oms_label, is_default) values ('sj_xmjd', 'S0', 'S0-配置报价', 'S0', 'S0-配置报价', true), ('sj_xmjd', 'S1', 'S1-深化设计', 'S1', 'S1-标前引导', true), ('sj_xmjd', 'S1', 'S1-深化设计', 'S2', 'S2-招投标签', false), ('sj_xmjd', 'S2', 'S2-招投标签', 'S3', 'S3-招投标签期', true), ('sj_xmjd', 'S3', 'S3-中标未下单', 'S4A', 'S4A-中标未签(总包)', false), ('sj_xmjd', 'S3', 'S3-中标未下单', 'S4B', 'S4B-中标未签(省代)', false), ('sj_xmjd', 'S3', 'S3-中标未下单', 'S4C', 'S4C-中标未签(汇智)', true), ('sj_xmjd', 'S4', 'S4-下单未签单', 'S5', 'S5-订单合同签订期', true), ('sj_xmjd', 'S5', 'S5-已签单', 'S6', 'S6-订单合同已生效', true), ('sj_xmjd', 'S5', 'S5-已签单', 'S7A', 'S7A-订单部分发货', false), ('sj_xmjd', 'S5', 'S5-已签单', 'S7B', 'S7B-订单全部发货', false), ('sj_xmjd', 'S5', 'S5-已签单', 'S8', 'S8-已签收', false), ('sj_xmjd', 'L', 'L-已丢单', 'L', 'L-已丢单', true) on conflict (dict_type, crm_value, oms_value) do nothing; -- 8) 渠道拓展覆盖地市:改为从表存储(便于后期统计) -- 覆盖关系存 crm_channel_expansion_coverage,每行一个覆盖项(省/市)。 -- 主表原有的 coverage_province / coverage_city 两列已废弃, -- 历史数据迁移到从表后删除,查询/写入均改为读写从表,避免冗余与不一致。 create table if not exists crm_channel_expansion_coverage ( id bigserial primary key, channel_id bigint not null references crm_channel_expansion(id) on delete cascade, province varchar(100), city varchar(100) ); comment on table crm_channel_expansion_coverage is '渠道拓展覆盖地市明细(每行一个覆盖项)'; comment on column crm_channel_expansion_coverage.province is '覆盖省份(cnarea 一级区域名称,city 为空表示全省)'; comment on column crm_channel_expansion_coverage.city is '覆盖市/区/县(cnarea 二级区域名称)'; create index if not exists idx_cec_channel_id on crm_channel_expansion_coverage(channel_id); create index if not exists idx_cec_province on crm_channel_expansion_coverage(province); create index if not exists idx_cec_city on crm_channel_expansion_coverage(city); -- --------------------------------------------------------------------- -- 9.5) 销售人员拓展所属区域:改为从表存储(参考渠道拓展"覆盖地市"结构) -- 所属区域为多选下拉,每个选中项存为一行(省/市), -- 关联 crm_sales_expansion(id),on delete cascade。 -- --------------------------------------------------------------------- create table if not exists crm_sales_expansion_coverage ( id bigserial primary key, sales_expansion_id bigint not null references crm_sales_expansion(id) on delete cascade, province varchar(100), city varchar(100) ); comment on table crm_sales_expansion_coverage is '销售人员拓展所属区域明细(每行一个区域项)'; comment on column crm_sales_expansion_coverage.province is '所属省份(cnarea 一级区域名称,city 为空表示全省)'; comment on column crm_sales_expansion_coverage.city is '所属市/区/县(cnarea 二级区域名称)'; create index if not exists idx_csec_sales_expansion_id on crm_sales_expansion_coverage(sales_expansion_id); create index if not exists idx_csec_province on crm_sales_expansion_coverage(province); create index if not exists idx_csec_city on crm_sales_expansion_coverage(city); -- --------------------------------------------------------------------- -- 9) work_checkin.id 序列同步(打卡主键改为 identity 自动生成) -- 背景:历史版本 insertCheckIn 使用 max(id)+1 显式主键,identity 序列 -- 从未推进;且历史遗留存在 work_checkin_id_seq / _seq1 双序列。 -- 现 insertCheckIn 已改为由 identity 自动生成主键,需将 identity 实际 -- 绑定的序列同步到表内 max(id),避免后续插入主键冲突。 -- 使用 pg_get_serial_sequence 动态解析实际序列,避免对错序列。 -- 幂等:重复执行结果一致,可安全用于任意环境。 -- --------------------------------------------------------------------- do $$ declare seq_name text; begin select pg_get_serial_sequence('work_checkin', 'id') into seq_name; if seq_name is not null then execute format('select setval(%L, coalesce((select max(id) from work_checkin), 0) + 1, false)', seq_name); raise notice 'work_checkin.id 序列 % 已同步', seq_name; else raise notice '未找到 work_checkin.id 的 identity 序列'; end if; end $$; -- --------------------------------------------------------------------- -- 10) 渠道拓展新增字段:注册资金 -- 需求:渠道拓展页面新增「注册资金」字段,必填,前端位置在「人员规模」 -- 字段之后。单位与年度营业额一致(万元),numeric(18,2)。 -- 幂等:add column if not exists,可安全用于任意环境。 -- --------------------------------------------------------------------- alter table crm_channel_expansion add column if not exists registered_capital numeric(18, 2); comment on column crm_channel_expansion.registered_capital is '注册资金(万元)'; -- --------------------------------------------------------------------- -- 11) 渠道拓展联系人新增字段:工作职责 / 生日 / 是否加企业微信 / 特别说明 -- 需求:渠道联系人固定 6 行,工作职责不可编辑,固定为 -- 销售负责人、技术负责人、商务负责人、宣传负责人、 -- 订单通知接单人、汇智日常联络人。 -- 除生日选填外,其余联系人字段均必填(至少完整填写一行)。 -- 是否加企业微信使用系统「是否」字典(sys_is):是(1) / 否(0)。 -- 工作职责为固定 6 项:销售负责人 / 技术负责人 / 商务负责人 / -- 宣传负责人 / 订单通知接单人 / 汇智日常联络人。 -- 幂等:add column if not exists,可安全用于任意环境。 -- --------------------------------------------------------------------- alter table crm_channel_expansion_contact add column if not exists duty varchar(50); alter table crm_channel_expansion_contact add column if not exists birthday date; alter table crm_channel_expansion_contact add column if not exists wecom_added varchar(100); alter table crm_channel_expansion_contact add column if not exists special_note text; comment on column crm_channel_expansion_contact.duty is '工作职责(固定6项之一:销售负责人/技术负责人/商务负责人/宣传负责人/订单通知接单人/汇智日常联络人)'; comment on column crm_channel_expansion_contact.birthday is '生日'; comment on column crm_channel_expansion_contact.wecom_added is '是否加企业微信(系统是否字典 sys_is:1是 / 0否)'; comment on column crm_channel_expansion_contact.special_note is '特别说明'; commit;