feat(aftersales): 工单客户/联系人/物料/批次改为文本 + 问题库/回访 SQL

- 工单:客户/联系人 改存文本(customer_name/contact_name),外键列改可空;明细 物料/批次 改文本,明细外键列改可空
- 新增 SQL:问题库/回访表(after_sale_problem/after_sale_visit)、工单回访/沉淀列、问题库菜单(602710)
已添加2个文件
已修改2个文件
165 ■■■■■ 文件已修改
sql/mysql/aftersales_problem_visit_init.sql 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/aftersales_ticket_text_fields.sql 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/dataobject/ticket/AfterSaleTicketDO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/aftersales_problem_visit_init.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
-- =============================================
-- å”®åŽï¼šé—®é¢˜åº“ + å›žè®¿ï¼ˆå¯é‡å¤æ‰§è¡Œï¼‰
-- å«ï¼šafter_sale_ticket åŠ  6 åˆ—、after_sale_problem / after_sale_visit å»ºè¡¨ã€é—®é¢˜åº“菜单(空闲 id 602710-602716)
-- =============================================
-- 1. after_sale_ticket åŠ å›žè®¿/沉淀相关列
SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS
           WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'after_sale_ticket' AND COLUMN_NAME = 'visit_status');
SET @ddl := IF(@c = 0,
    'ALTER TABLE `after_sale_ticket`
        ADD COLUMN `visit_status` tinyint NULL COMMENT ''回访状态: 0-待回访 1-已回访通过'' AFTER `closed_time`,
        ADD COLUMN `last_visit_time` datetime NULL COMMENT ''最近一次回访时间'' AFTER `visit_status`,
        ADD COLUMN `last_visit_satisfied` bit(1) NULL COMMENT ''最近一次回访是否满意'' AFTER `last_visit_time`,
        ADD COLUMN `last_visit_recurred` bit(1) NULL COMMENT ''最近一次回访问题是否复发'' AFTER `last_visit_satisfied`,
        ADD COLUMN `problem_id` bigint NULL COMMENT ''沉淀问题编号'' AFTER `last_visit_recurred`,
        ADD COLUMN `problem_no` varchar(64) NULL COMMENT ''沉淀问题单号'' AFTER `problem_id`',
    'SELECT 1');
PREPARE s FROM @ddl; EXECUTE s; DEALLOCATE PREPARE s;
-- 2. é—®é¢˜åº“表
CREATE TABLE IF NOT EXISTS `after_sale_problem` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '问题编号',
    `no` varchar(64) DEFAULT NULL COMMENT '问题单号 AP+yyyyMMdd+6位',
    `title` varchar(255) DEFAULT NULL COMMENT '问题标题',
    `source_ticket_id` bigint DEFAULT NULL COMMENT '来源工单编号',
    `source_ticket_no` varchar(64) DEFAULT NULL COMMENT '来源工单单号',
    `customer_id` bigint DEFAULT NULL COMMENT '客户编号',
    `item_id` bigint DEFAULT NULL COMMENT '物料编号',
    `item_code` varchar(64) DEFAULT NULL COMMENT '物料编码',
    `item_name` varchar(255) DEFAULT NULL COMMENT '物料名称',
    `batch_id` bigint DEFAULT NULL COMMENT '生产批次编号',
    `batch_code` varchar(64) DEFAULT NULL COMMENT '生产批次号',
    `issue_type` tinyint DEFAULT NULL COMMENT '问题类型',
    `issue_category` varchar(64) DEFAULT NULL COMMENT '问题分类',
    `severity_level` tinyint DEFAULT NULL COMMENT '严重程度',
    `description` varchar(1000) DEFAULT NULL COMMENT '问题描述',
    `root_cause_category` tinyint DEFAULT NULL COMMENT '根因分类',
    `root_cause` text COMMENT '根因分析',
    `solution` text COMMENT '解决方案',
    `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态',
    `owner_user_id` bigint DEFAULT NULL COMMENT '沉淀人用户编号',
    `publish_time` datetime DEFAULT NULL COMMENT '发布时间',
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    PRIMARY KEY (`id`), KEY `idx_source_ticket` (`source_ticket_id`), KEY `idx_item` (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='售后问题库表';
-- 3. å›žè®¿è®°å½•表
CREATE TABLE IF NOT EXISTS `after_sale_visit` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '回访记录编号',
    `ticket_id` bigint DEFAULT NULL COMMENT '工单编号',
    `visit_type` tinyint DEFAULT NULL COMMENT '回访方式: 0-电话 1-上门 2-远程 3-其他',
    `visitor_user_id` bigint DEFAULT NULL COMMENT '回访人用户编号',
    `content` varchar(1000) DEFAULT NULL COMMENT '回访内容',
    `is_satisfied` bit(1) DEFAULT NULL COMMENT '是否满意',
    `is_recurred` bit(1) DEFAULT NULL COMMENT '问题是否复发',
    `visit_time` datetime DEFAULT NULL COMMENT '回访时间',
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    PRIMARY KEY (`id`), KEY `idx_ticket` (`ticket_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='售后回访记录表';
-- 4. é—®é¢˜åº“菜单(挂 602587 å”®åŽç®¡ç† ä¸‹ï¼Œç©ºé—² id 602710-602716)
INSERT INTO `system_menu`
(`id`,`name`,`permission`,`type`,`sort`,`parent_id`,`path`,`icon`,`component`,`component_name`,`status`,`visible`,`keep_alive`,`always_show`,`creator`,`create_time`,`updater`,`update_time`,`deleted`)
VALUES
(602710,'问题库','',2,3,602587,'problem','ep:collection','/aftersales/problem/index','AfterSaleProblem',0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'),
(602711,'问题库查询','aftersales:problem:query',3,1,602710,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'),
(602712,'问题库创建','aftersales:problem:create',3,2,602710,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'),
(602713,'问题库更新','aftersales:problem:update',3,3,602710,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'),
(602714,'问题库删除','aftersales:problem:delete',3,4,602710,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'),
(602715,'问题库发布','aftersales:problem:publish',3,5,602710,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'),
(602716,'问题库停用','aftersales:problem:disable',3,6,602710,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0')
ON DUPLICATE KEY UPDATE `name`=VALUES(`name`),`permission`=VALUES(`permission`),`type`=VALUES(`type`),`parent_id`=VALUES(`parent_id`),`path`=VALUES(`path`),`component`=VALUES(`component`),`component_name`=VALUES(`component_name`),`status`=VALUES(`status`),`deleted`=b'0';
INSERT INTO `system_role_menu` (`role_id`,`menu_id`,`creator`,`create_time`,`updater`,`update_time`,`deleted`)
SELECT 1, m.menu_id, '1', NOW(), '1', NOW(), b'0'
FROM (SELECT 602710 AS menu_id UNION ALL SELECT 602711 UNION ALL SELECT 602712 UNION ALL SELECT 602713 UNION ALL SELECT 602714 UNION ALL SELECT 602715 UNION ALL SELECT 602716) m
WHERE NOT EXISTS (SELECT 1 FROM `system_role_menu` rm WHERE rm.`role_id`=1 AND rm.`menu_id`=m.menu_id AND rm.`deleted`=b'0');
SELECT '售后问题库/回访初始化完成' AS result;
sql/mysql/aftersales_ticket_text_fields.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
-- =============================================
-- å”®åŽå·¥å•:客户/联系人/物料/批次 æ”¹ä¸ºæ–‡æœ¬å­˜å‚¨ + å…œåº•补齐工单按钮权限(可重复执行)
-- =============================================
SET @c1 := (SELECT COUNT(*) FROM information_schema.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'after_sale_ticket' AND COLUMN_NAME = 'customer_name');
SET @ddl1 := IF(@c1 = 0,
    'ALTER TABLE `after_sale_ticket` ADD COLUMN `customer_name` varchar(255) NULL COMMENT ''客户名称(文本)'' AFTER `customer_id`',
    'SELECT 1');
PREPARE s1 FROM @ddl1; EXECUTE s1; DEALLOCATE PREPARE s1;
SET @c2 := (SELECT COUNT(*) FROM information_schema.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'after_sale_ticket' AND COLUMN_NAME = 'contact_name');
SET @ddl2 := IF(@c2 = 0,
    'ALTER TABLE `after_sale_ticket` ADD COLUMN `contact_name` varchar(255) NULL COMMENT ''联系人(文本)'' AFTER `contact_id`',
    'SELECT 1');
PREPARE s2 FROM @ddl2; EXECUTE s2; DEALLOCATE PREPARE s2;
-- å®¢æˆ·/联系人改为文本后,原外键列允许为空
ALTER TABLE `after_sale_ticket` MODIFY COLUMN `customer_id` bigint NULL COMMENT '客户编号(可选)';
ALTER TABLE `after_sale_ticket` MODIFY COLUMN `contact_id` bigint NULL COMMENT '联系人编号(可选)';
-- ç‰©æ–™/批次改为文本后,明细原外键列允许为空
ALTER TABLE `after_sale_ticket_item` MODIFY COLUMN `sale_order_item_id` bigint NULL COMMENT '销售订单行(可选)';
ALTER TABLE `after_sale_ticket_item` MODIFY COLUMN `item_id` bigint NULL COMMENT 'MDM物料编号(可选)';
ALTER TABLE `after_sale_ticket_item` MODIFY COLUMN `batch_id` bigint NULL COMMENT '批次编号(可选)';
-- å…œåº•:工单按钮权限(仅当该权限不存在时才插入,避免与已有菜单重复/撞 id)
INSERT INTO `system_menu`
(`id`,`name`,`permission`,`type`,`sort`,`parent_id`,`path`,`icon`,`component`,`component_name`,`status`,`visible`,`keep_alive`,`always_show`,`creator`,`create_time`,`updater`,`update_time`,`deleted`)
SELECT 602700,'售后工单新增','aftersales:ticket:create',3,1,602588,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `system_menu` WHERE `permission`='aftersales:ticket:create' AND `deleted`=b'0');
INSERT INTO `system_menu`
(`id`,`name`,`permission`,`type`,`sort`,`parent_id`,`path`,`icon`,`component`,`component_name`,`status`,`visible`,`keep_alive`,`always_show`,`creator`,`create_time`,`updater`,`update_time`,`deleted`)
SELECT 602701,'售后工单编辑','aftersales:ticket:update',3,2,602588,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `system_menu` WHERE `permission`='aftersales:ticket:update' AND `deleted`=b'0');
INSERT INTO `system_menu`
(`id`,`name`,`permission`,`type`,`sort`,`parent_id`,`path`,`icon`,`component`,`component_name`,`status`,`visible`,`keep_alive`,`always_show`,`creator`,`create_time`,`updater`,`update_time`,`deleted`)
SELECT 602702,'售后工单删除','aftersales:ticket:delete',3,3,602588,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `system_menu` WHERE `permission`='aftersales:ticket:delete' AND `deleted`=b'0');
INSERT INTO `system_menu`
(`id`,`name`,`permission`,`type`,`sort`,`parent_id`,`path`,`icon`,`component`,`component_name`,`status`,`visible`,`keep_alive`,`always_show`,`creator`,`create_time`,`updater`,`update_time`,`deleted`)
SELECT 602703,'售后工单查询','aftersales:ticket:query',3,4,602588,'','','',NULL,0,b'1',b'1',b'1','1',NOW(),'1',NOW(),b'0'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `system_menu` WHERE `permission`='aftersales:ticket:query' AND `deleted`=b'0');
-- å…œåº•按钮 æŽˆæƒç»™è¶…管
INSERT INTO `system_role_menu` (`role_id`,`menu_id`,`creator`,`create_time`,`updater`,`update_time`,`deleted`)
SELECT 1, m.menu_id, '1', NOW(), '1', NOW(), b'0'
FROM (SELECT 602700 AS menu_id UNION ALL SELECT 602701 UNION ALL SELECT 602702 UNION ALL SELECT 602703) m
WHERE EXISTS (SELECT 1 FROM `system_menu` sm WHERE sm.`id`=m.menu_id)
  AND NOT EXISTS (SELECT 1 FROM `system_role_menu` rm WHERE rm.`role_id`=1 AND rm.`menu_id`=m.menu_id AND rm.`deleted`=b'0');
SELECT '售后工单文本字段初始化完成' AS result;
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java
@@ -22,12 +22,18 @@
    @NotEmpty(message = "工单主题不能为空")
    private String name;
    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "客户不能为空")
    @Schema(description = "客户编号", example = "1")
    private Long customerId;
    @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "某某公司")
    @NotEmpty(message = "客户不能为空")
    private String customerName;
    @Schema(description = "联系人编号", example = "1")
    private Long contactId;
    @Schema(description = "联系人", example = "李四")
    private String contactName;
    @Schema(description = "销售订单编号", example = "1")
    private Long saleOrderId;
@@ -69,14 +75,14 @@
        @Schema(description = "销售订单行编号", example = "1")
        private Long saleOrderItemId;
        @Schema(description = "MDM物料编号", requiredMode = Schema.RequiredMode.REQUIRED)
        @NotNull(message = "物料不能为空")
        @Schema(description = "MDM物料编号", example = "1")
        private Long itemId;
        @Schema(description = "物料编码", example = "MAT-001")
        private String itemCode;
        @Schema(description = "物料名称", example = "电子元器件")
        @Schema(description = "物料名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "电子元器件")
        @NotEmpty(message = "物料不能为空")
        private String itemName;
        @Schema(description = "生产批次编号", example = "1")
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/dataobject/ticket/AfterSaleTicketDO.java
@@ -41,10 +41,18 @@
     */
    private Long customerId;
    /**
     * å®¢æˆ·åç§°(文本,可自由填写)
     */
    private String customerName;
    /**
     * è”系人编号,关联 crm_contact.id
     */
    private Long contactId;
    /**
     * è”系人(文本,可自由填写)
     */
    private String contactName;
    /**
     * é”€å”®è®¢å•编号,关联 erp_sale_order.id
     */
    private Long saleOrderId;