feat: 完成客户拜访模块全功能开发

本次提交实现了完整的客户拜访业务系统:
1.  新增对象存储上传服务,支持S3兼容协议的RustFS存储
2.  完善客户拜访实体与数据库表结构,补充全业务字段
3.  开发拜访台账与记录的前后端接口,包含增删改查、批量创建、状态流转等能力
4.  新增拜访表单页面与路由,支持父级菜单补全面包屑
5.  新增勘察列表页预填客户ID跳转逻辑
6.  补充UI设计文档与页面截图
7.  调整日志中间件跳过上传路由,优化RBAC权限配置
This commit is contained in:
andyesfly committed 2026-09-14 11:42:10 +08:00
1 parent 68af363ca9
commit 1c83e09fa1
33 files changed
+6920 -587

No files matched your search

+76 -20
View File
@@ -258,30 +258,86 @@ CREATE TRIGGER trg_customer_department_updated_at BEFORE UPDATE ON customer_depa
-- 10. 客户拜访信息表
DROP TABLE IF EXISTS customer_visit CASCADE;
CREATE TABLE customer_visit (
id BIGSERIAL PRIMARY KEY,
visit_no VARCHAR(64) NOT NULL UNIQUE,
customer_id BIGINT NOT NULL REFERENCES customer(id),
visit_person VARCHAR(64) NOT NULL,
visit_phone VARCHAR(20),
visit_content TEXT,
visit_time DATE,
salesman_id BIGINT,
will_plan SMALLINT DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
id BIGSERIAL PRIMARY KEY,
visit_no VARCHAR(64) NOT NULL UNIQUE,
customer_id BIGINT NOT NULL REFERENCES customer(id),
visit_person VARCHAR(64) NOT NULL,
visit_phone VARCHAR(20),
visit_content TEXT,
visit_time DATE,
salesman_id BIGINT,
will_plan SMALLINT DEFAULT 0,
visit_type SMALLINT DEFAULT 1,
visit_mode SMALLINT DEFAULT 1,
priority SMALLINT DEFAULT 3,
subject VARCHAR(128),
plan_time TIMESTAMPTZ,
plan_end_time TIMESTAMPTZ,
checkin_time TIMESTAMPTZ,
checkin_status SMALLINT DEFAULT 0,
checkin_lng DECIMAL(10,6),
checkin_lat DECIMAL(10,6),
space_desc VARCHAR(128),
intent_level SMALLINT,
intent_desc VARCHAR(64),
demands VARCHAR(512),
companion VARCHAR(128),
reception_person VARCHAR(64),
reception_title VARCHAR(64),
reception_phone VARCHAR(20),
result_rating VARCHAR(64),
status SMALLINT DEFAULT 0,
next_visit_time DATE,
next_action VARCHAR(128),
photos VARCHAR(1024),
survey_order_id BIGINT,
project_no VARCHAR(64),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
COMMENT ON TABLE customer_visit IS '客户拜访信息表';
COMMENT ON COLUMN customer_visit.id IS '自增主键';
COMMENT ON COLUMN customer_visit.visit_no IS '拜访单号';
COMMENT ON COLUMN customer_visit.customer_id IS '客户 ID(→ customer)';
COMMENT ON COLUMN customer_visit.visit_person IS '拜访人';
COMMENT ON COLUMN customer_visit.visit_phone IS '拜访人电话';
COMMENT ON COLUMN customer_visit.visit_content IS '拜访内容';
COMMENT ON COLUMN customer_visit.visit_time IS '拜访时间';
COMMENT ON COLUMN customer_visit.salesman_id IS '业务员';
COMMENT ON COLUMN customer_visit.will_plan IS '是否做方案:0不同意 1同意';
COMMENT ON COLUMN customer_visit.created_at IS '创建时间';
COMMENT ON COLUMN customer_visit.id IS '自增主键';
COMMENT ON COLUMN customer_visit.visit_no IS '拜访单号';
COMMENT ON COLUMN customer_visit.customer_id IS '客户 ID(→ customer)';
COMMENT ON COLUMN customer_visit.visit_person IS '拜访人';
COMMENT ON COLUMN customer_visit.visit_phone IS '拜访人电话';
COMMENT ON COLUMN customer_visit.visit_content IS '拜访内容/沟通纪要';
COMMENT ON COLUMN customer_visit.visit_time IS '拜访时间';
COMMENT ON COLUMN customer_visit.salesman_id IS '业务员';
COMMENT ON COLUMN customer_visit.will_plan IS '是否做方案:0不同意 1同意';
COMMENT ON COLUMN customer_visit.visit_type IS '拜访类型:1首访摸排 2方案沟通 3商务二访 4维保回访 5应急巡检';
COMMENT ON COLUMN customer_visit.visit_mode IS '拜访方式:1现场拜访 2商务拜访 3电话沟通 4线上会议';
COMMENT ON COLUMN customer_visit.priority IS '优先级:1特急P1 2加急P2 3常规P3';
COMMENT ON COLUMN customer_visit.subject IS '拜访主题/目的';
COMMENT ON COLUMN customer_visit.plan_time IS '计划拜访开始时间';
COMMENT ON COLUMN customer_visit.plan_end_time IS '计划拜访结束时间';
COMMENT ON COLUMN customer_visit.checkin_time IS '到场签到时间';
COMMENT ON COLUMN customer_visit.checkin_status IS '签到状态:0未签到 1已签到';
COMMENT ON COLUMN customer_visit.checkin_lng IS '签到经度';
COMMENT ON COLUMN customer_visit.checkin_lat IS '签到纬度';
COMMENT ON COLUMN customer_visit.space_desc IS '客户空间性质/场所描述';
COMMENT ON COLUMN customer_visit.intent_level IS '意向评级:1=A高意向 2=B中意向 3=C低意向';
COMMENT ON COLUMN customer_visit.intent_desc IS '现场意向说明';
COMMENT ON COLUMN customer_visit.demands IS '诉求痛点(逗号分隔)';
COMMENT ON COLUMN customer_visit.companion IS '陪同人员';
COMMENT ON COLUMN customer_visit.reception_person IS '客户接待人';
COMMENT ON COLUMN customer_visit.reception_title IS '接待人职务';
COMMENT ON COLUMN customer_visit.reception_phone IS '接待人联系方式';
COMMENT ON COLUMN customer_visit.result_rating IS '现场成果评级/结论';
COMMENT ON COLUMN customer_visit.status IS '状态:0待执行 1已完成待复核 2取证待技术复核 3已立项转勘察 4已归档 5已取消';
COMMENT ON COLUMN customer_visit.next_visit_time IS '预计下次回访日期';
COMMENT ON COLUMN customer_visit.next_action IS '下一步行动目标';
COMMENT ON COLUMN customer_visit.photos IS '现场照片对象key(JSON数组字符串)';
COMMENT ON COLUMN customer_visit.survey_order_id IS '关联勘察单ID';
COMMENT ON COLUMN customer_visit.project_no IS '关联工程编号';
COMMENT ON COLUMN customer_visit.created_at IS '创建时间';
COMMENT ON COLUMN customer_visit.updated_at IS '更新时间';
CREATE INDEX idx_visit_customer ON customer_visit (customer_id);
CREATE INDEX idx_visit_salesman ON customer_visit (salesman_id);
CREATE INDEX idx_visit_status ON customer_visit (status);
CREATE INDEX idx_visit_plan_time ON customer_visit (plan_time);
CREATE TRIGGER trg_customer_visit_updated_at BEFORE UPDATE ON customer_visit
FOR EACH ROW EXECUTE FUNCTION trigger_set_updated_at();
-- 11. 客户智电改造建筑信息表
DROP TABLE IF EXISTS customer_building CASCADE;
@@ -0,0 +1,88 @@
-- 客户拜访功能扩展:补齐台账/记录页所需的业务字段、更新时间与查询索引
-- 背景:db_init.sql 中 customer_visit 原为 9 字段的极简结构,
-- 现按「客户拜访台帐」「客户拜访记录」两份 UI 稿完整落地。
-- 幂等:全部使用 IF NOT EXISTS,可重复执行。
BEGIN;
-- 1) 拜访属性
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS visit_type SMALLINT DEFAULT 1; -- 拜访类型:1首访摸排 2方案沟通 3商务二访 4维保回访 5应急巡检
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS visit_mode SMALLINT DEFAULT 1; -- 拜访方式:1现场拜访 2商务拜访 3电话沟通 4线上会议
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS priority SMALLINT DEFAULT 3; -- 优先级:1特急P1 2加急P2 3常规P3
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS subject VARCHAR(128); -- 拜访主题/目的
-- 2) 计划与签到
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS plan_time TIMESTAMPTZ; -- 计划拜访开始时间
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS plan_end_time TIMESTAMPTZ; -- 计划拜访结束时间
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS checkin_time TIMESTAMPTZ; -- 到场签到时间
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS checkin_status SMALLINT DEFAULT 0; -- 签到状态:0未签到 1已签到
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS checkin_lng DECIMAL(10,6); -- 签到经度
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS checkin_lat DECIMAL(10,6); -- 签到纬度
-- 3) 现场信息与意向
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS space_desc VARCHAR(128); -- 客户空间性质/场所描述
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS intent_level SMALLINT; -- 意向评级:1=A高意向 2=B中意向 3=C低意向
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS intent_desc VARCHAR(64); -- 现场意向说明
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS demands VARCHAR(512); -- 诉求痛点(逗号分隔)
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS companion VARCHAR(128); -- 陪同人员
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS reception_person VARCHAR(64); -- 客户接待人
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS reception_title VARCHAR(64); -- 接待人职务
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS reception_phone VARCHAR(20); -- 接待人联系方式
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS result_rating VARCHAR(64); -- 现场成果评级/结论
-- 4) 状态与后续跟进
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS status SMALLINT DEFAULT 0; -- 状态:0待执行 1已完成待复核 2取证待技术复核 3已立项转勘察 4已归档 5已取消
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS next_visit_time DATE; -- 预计下次回访日期
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS next_action VARCHAR(128); -- 下一步行动目标
-- 5) 现场照片(对象存储 key 的 JSON 数组字符串)
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS photos VARCHAR(1024); -- 现场照片对象key(JSON数组字符串)
-- 6) 工程关联
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS survey_order_id BIGINT; -- 关联勘察单ID
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS project_no VARCHAR(64); -- 关联工程编号
-- 7) 更新时间(Sequelize 实体已开启 updated_at,缺列会导致写入失败)
ALTER TABLE customer_visit ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
-- 8) 字段注释
COMMENT ON COLUMN customer_visit.visit_type IS '拜访类型:1首访摸排 2方案沟通 3商务二访 4维保回访 5应急巡检';
COMMENT ON COLUMN customer_visit.visit_mode IS '拜访方式:1现场拜访 2商务拜访 3电话沟通 4线上会议';
COMMENT ON COLUMN customer_visit.priority IS '优先级:1特急P1 2加急P2 3常规P3';
COMMENT ON COLUMN customer_visit.subject IS '拜访主题/目的';
COMMENT ON COLUMN customer_visit.plan_time IS '计划拜访开始时间';
COMMENT ON COLUMN customer_visit.plan_end_time IS '计划拜访结束时间';
COMMENT ON COLUMN customer_visit.checkin_time IS '到场签到时间';
COMMENT ON COLUMN customer_visit.checkin_status IS '签到状态:0未签到 1已签到';
COMMENT ON COLUMN customer_visit.checkin_lng IS '签到经度';
COMMENT ON COLUMN customer_visit.checkin_lat IS '签到纬度';
COMMENT ON COLUMN customer_visit.space_desc IS '客户空间性质/场所描述';
COMMENT ON COLUMN customer_visit.intent_level IS '意向评级:1=A高意向 2=B中意向 3=C低意向';
COMMENT ON COLUMN customer_visit.intent_desc IS '现场意向说明';
COMMENT ON COLUMN customer_visit.demands IS '诉求痛点(逗号分隔)';
COMMENT ON COLUMN customer_visit.companion IS '陪同人员';
COMMENT ON COLUMN customer_visit.reception_person IS '客户接待人';
COMMENT ON COLUMN customer_visit.reception_title IS '接待人职务';
COMMENT ON COLUMN customer_visit.reception_phone IS '接待人联系方式';
COMMENT ON COLUMN customer_visit.result_rating IS '现场成果评级/结论';
COMMENT ON COLUMN customer_visit.status IS '状态:0待执行 1已完成待复核 2取证待技术复核 3已立项转勘察 4已归档 5已取消';
COMMENT ON COLUMN customer_visit.next_visit_time IS '预计下次回访日期';
COMMENT ON COLUMN customer_visit.next_action IS '下一步行动目标';
COMMENT ON COLUMN customer_visit.photos IS '现场照片对象key(JSON数组字符串)';
COMMENT ON COLUMN customer_visit.survey_order_id IS '关联勘察单ID';
COMMENT ON COLUMN customer_visit.project_no IS '关联工程编号';
COMMENT ON COLUMN customer_visit.updated_at IS '更新时间';
-- 9) 查询索引(台账按状态、计划时间过滤与排序)
CREATE INDEX IF NOT EXISTS idx_visit_status ON customer_visit (status);
CREATE INDEX IF NOT EXISTS idx_visit_plan_time ON customer_visit (plan_time);
-- 10) 更新时间触发器(与 customer 等表保持一致的库级约定)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_customer_visit_updated_at') THEN
CREATE TRIGGER trg_customer_visit_updated_at BEFORE UPDATE ON customer_visit
FOR EACH ROW EXECUTE FUNCTION trigger_set_updated_at();
END IF;
END $$;
COMMIT;