feat: add process knowledge and after-sales workflows
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- å®åè·¨é¨é¨ååä»»å¡ï¼ä¸ç´æ¥å建财å¡åè¯æä¾åºåç´¢èµåã |
| | | CREATE TABLE IF NOT EXISTS after_sale_collaboration ( |
| | | id BIGINT NOT NULL AUTO_INCREMENT, |
| | | ticket_id BIGINT NOT NULL COMMENT 'å®åå·¥åç¼å·', |
| | | category VARCHAR(32) NOT NULL COMMENT 'QUALITYãFINANCEãSUPPLIERãWARRANTYãRESEARCHãPROCESS', |
| | | title VARCHAR(200) NOT NULL COMMENT 'ååäºé¡¹', |
| | | content VARCHAR(2000) NOT NULL COMMENT 'å¾
å¤çå
容', |
| | | assignee_user_id BIGINT NOT NULL COMMENT 'æ¥æ¶äºº', |
| | | requested_by BIGINT NOT NULL COMMENT 'å起人', |
| | | status TINYINT NOT NULL DEFAULT 0 COMMENT '0 å¾
å¤çï¼1 已宿ï¼2 已忶', |
| | | result VARCHAR(2000) DEFAULT NULL COMMENT 'å¤çç»æ', |
| | | completed_time DATETIME DEFAULT NULL, |
| | | creator VARCHAR(64) DEFAULT '', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | updater VARCHAR(64) DEFAULT '', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | deleted BIT(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (id), KEY idx_collaboration_ticket (ticket_id, deleted), |
| | | KEY idx_collaboration_assignee (assignee_user_id, status, deleted), |
| | | KEY idx_collaboration_requester (requested_by, deleted) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='å®åååä»»å¡'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ä¾èµæ¢æå®å管çç®å½ï¼ææ ID ç±ç®æ åºçæï¼è§è²æééå¨ç®æ ç³»ç»æ¾å¼åé
ã |
| | | SET @after_sale_parent := (SELECT id FROM system_menu WHERE path = '/aftersales' AND parent_id = 0 AND deleted = b'0' LIMIT 1); |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, updater) |
| | | SELECT 'å®ååå', '', 2, 20, @after_sale_parent, 'collaboration', 'lucide:users', 'aftersales/collaboration/index', 'AftersalesCollaboration', 0, b'1', b'1', b'1', '1', '1' |
| | | WHERE @after_sale_parent IS NOT NULL AND NOT EXISTS (SELECT 1 FROM system_menu WHERE component = 'aftersales/collaboration/index' AND deleted = b'0'); |
| | | SET @collaboration_page := (SELECT id FROM system_menu WHERE component = 'aftersales/collaboration/index' AND deleted = b'0' LIMIT 1); |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, status, visible, keep_alive, always_show, creator, updater) |
| | | SELECT p.name, p.permission, 3, p.sort_no, @collaboration_page, '', '', 0, b'1', b'1', b'1', '1', '1' |
| | | FROM (SELECT 'ååæ¥è¯¢' name, 'aftersales:collaboration:query' permission, 1 sort_no UNION ALL |
| | | SELECT 'åååèµ·', 'aftersales:collaboration:create', 2 UNION ALL |
| | | SELECT 'ååå¤ç', 'aftersales:collaboration:update', 3 UNION ALL |
| | | SELECT 'ä¸å¡å®¡æ ¸', 'aftersales:collaboration:approve', 4) p |
| | | WHERE @collaboration_page IS NOT NULL AND NOT EXISTS |
| | | (SELECT 1 FROM system_menu m WHERE m.permission = p.permission AND m.deleted = b'0'); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- å¨ after_sale_collaboration_20261105.sql åæ§è¡ãä»
ä¿åè´¢å¡å¾
å®¡æ ¸è稿ï¼ä¸äº§ç仿¬¾æåè¯ã |
| | | SET @sql_amount := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='amount')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN amount DECIMAL(18,2) NULL COMMENT ''éé¢ï¼å
ï¼''', 'SELECT 1'); |
| | | PREPARE stmt_amount FROM @sql_amount; EXECUTE stmt_amount; DEALLOCATE PREPARE stmt_amount; |
| | | SET @sql_direction := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='direction')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN direction VARCHAR(16) NULL COMMENT ''RECEIVABLEãPAYABLEãINTERNAL''', 'SELECT 1'); |
| | | PREPARE stmt_direction FROM @sql_direction; EXECUTE stmt_direction; DEALLOCATE PREPARE stmt_direction; |
| | | SET @sql_party := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='party_name')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN party_name VARCHAR(200) NULL COMMENT ''徿¥æ¹åç§°''', 'SELECT 1'); |
| | | PREPARE stmt_party FROM @sql_party; EXECUTE stmt_party; DEALLOCATE PREPARE stmt_party; |
| | | SET @sql_finance_id := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='finance_draft_id')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN finance_draft_id BIGINT NULL COMMENT ''è´¢å¡è稿ç¼å·''', 'SELECT 1'); |
| | | PREPARE stmt_finance_id FROM @sql_finance_id; EXECUTE stmt_finance_id; DEALLOCATE PREPARE stmt_finance_id; |
| | | SET @sql_business_actor := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='business_approver_id')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN business_approver_id BIGINT NULL COMMENT ''ä¸å¡å®¡æ ¸äºº''', 'SELECT 1'); |
| | | PREPARE stmt_business_actor FROM @sql_business_actor; EXECUTE stmt_business_actor; DEALLOCATE PREPARE stmt_business_actor; |
| | | SET @sql_business_time := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='business_approval_time')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN business_approval_time DATETIME NULL COMMENT ''ä¸å¡å®¡æ ¸æ¶é´''', 'SELECT 1'); |
| | | PREPARE stmt_business_time FROM @sql_business_time; EXECUTE stmt_business_time; DEALLOCATE PREPARE stmt_business_time; |
| | | SET @sql_business_remark := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='business_remark')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN business_remark VARCHAR(1000) NULL COMMENT ''ä¸å¡å®¡æ ¸æè§''', 'SELECT 1'); |
| | | PREPARE stmt_business_remark FROM @sql_business_remark; EXECUTE stmt_business_remark; DEALLOCATE PREPARE stmt_business_remark; |
| | | SET @sql_previous := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='previous_id')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN previous_id BIGINT NULL COMMENT ''ä¸ä¸æ¬¡éåçååäºé¡¹''', 'SELECT 1'); |
| | | PREPARE stmt_previous FROM @sql_previous; EXECUTE stmt_previous; DEALLOCATE PREPARE stmt_previous; |
| | | SET @sql_previous_index := IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND index_name='uq_collaboration_previous')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD UNIQUE KEY uq_collaboration_previous (previous_id)', 'SELECT 1'); |
| | | PREPARE stmt_previous_index FROM @sql_previous_index; EXECUTE stmt_previous_index; DEALLOCATE PREPARE stmt_previous_index; |
| | | |
| | | CREATE TABLE IF NOT EXISTS erp_after_sale_finance_draft ( |
| | | id BIGINT NOT NULL AUTO_INCREMENT, |
| | | collaboration_id BIGINT NOT NULL COMMENT 'å®åååäºé¡¹', |
| | | ticket_id BIGINT NOT NULL COMMENT 'å®åå·¥å', |
| | | category VARCHAR(32) NOT NULL COMMENT 'ååç±»å', |
| | | direction VARCHAR(16) NOT NULL COMMENT 'RECEIVABLEãPAYABLEãINTERNAL', |
| | | amount DECIMAL(18,2) NOT NULL COMMENT 'éé¢ï¼å
ï¼', |
| | | party_name VARCHAR(200) DEFAULT NULL COMMENT '徿¥æ¹åç§°', |
| | | description VARCHAR(2000) NOT NULL COMMENT 'ä¸å¡è¯´æ', |
| | | business_approver_id BIGINT NOT NULL COMMENT 'ä¸å¡ç¡®è®¤äºº', |
| | | status TINYINT NOT NULL DEFAULT 0 COMMENT '0 å¾
è´¢å¡å®¡æ ¸ï¼1 å®¡æ ¸éè¿ï¼2 éå', |
| | | reviewer_id BIGINT DEFAULT NULL, |
| | | review_remark VARCHAR(1000) DEFAULT NULL, |
| | | review_time DATETIME DEFAULT NULL, |
| | | creator VARCHAR(64) DEFAULT '', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | updater VARCHAR(64) DEFAULT '', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | deleted BIT(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (id), UNIQUE KEY uq_as_finance_collaboration (collaboration_id), |
| | | KEY idx_as_finance_status (status, deleted), KEY idx_as_finance_ticket (ticket_id, deleted) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='å®åè´¢å¡å®¡æ ¸è稿'; |
| | | |
| | | -- å
³èä¸»æ°æ®ï¼åæ¶ä¿çåç§°å¿«ç
§ï¼åå²è®°å½å
许为空ã |
| | | SET @sql_party_type := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='party_type')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN party_type VARCHAR(16) NULL', 'SELECT 1'); |
| | | PREPARE stmt_party_type FROM @sql_party_type; EXECUTE stmt_party_type; DEALLOCATE PREPARE stmt_party_type; |
| | | SET @sql_party_id := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='after_sale_collaboration' AND column_name='party_id')=0, |
| | | 'ALTER TABLE after_sale_collaboration ADD COLUMN party_id BIGINT NULL', 'SELECT 1'); |
| | | PREPARE stmt_party_id FROM @sql_party_id; EXECUTE stmt_party_id; DEALLOCATE PREPARE stmt_party_id; |
| | | SET @sql_draft_party_type := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='erp_after_sale_finance_draft' AND column_name='party_type')=0, |
| | | 'ALTER TABLE erp_after_sale_finance_draft ADD COLUMN party_type VARCHAR(16) NULL', 'SELECT 1'); |
| | | PREPARE stmt_draft_party_type FROM @sql_draft_party_type; EXECUTE stmt_draft_party_type; DEALLOCATE PREPARE stmt_draft_party_type; |
| | | SET @sql_draft_party_id := IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='erp_after_sale_finance_draft' AND column_name='party_id')=0, |
| | | 'ALTER TABLE erp_after_sale_finance_draft ADD COLUMN party_id BIGINT NULL', 'SELECT 1'); |
| | | PREPARE stmt_draft_party_id FROM @sql_draft_party_id; EXECUTE stmt_draft_party_id; DEALLOCATE PREPARE stmt_draft_party_id; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- å¨è´¢å¡ç®¡çä¸å¢å ç¬ç«å®¡æ ¸è稿å
¥å£ï¼è§è²æéå¨ç®æ ç³»ç»ä¸æ¾å¼åé
ã |
| | | SET @finance_parent := (SELECT id FROM system_menu WHERE path='finance' AND type=1 AND deleted=b'0' AND name='è´¢å¡ç®¡ç' LIMIT 1); |
| | | INSERT INTO system_menu (name,permission,type,sort,parent_id,path,icon,component,component_name,status,visible,keep_alive,always_show,creator,updater) |
| | | SELECT 'å®åè´¢å¡è稿','',2,30,@finance_parent,'after-sale-draft','lucide:file-check','erp/finance/after-sale-draft/index','ErpAfterSaleFinanceDraft',0,b'1',b'1',b'1','1','1' |
| | | WHERE @finance_parent IS NOT NULL AND NOT EXISTS(SELECT 1 FROM system_menu WHERE component='erp/finance/after-sale-draft/index' AND deleted=b'0'); |
| | | SET @finance_page := (SELECT id FROM system_menu WHERE component='erp/finance/after-sale-draft/index' AND deleted=b'0' LIMIT 1); |
| | | INSERT INTO system_menu (name,permission,type,sort,parent_id,path,icon,status,visible,keep_alive,always_show,creator,updater) |
| | | SELECT p.name,p.permission,3,p.sort_no,@finance_page,'','',0,b'1',b'1',b'1','1','1' |
| | | FROM (SELECT 'è稿æ¥è¯¢' name,'erp:after-sale-finance:query' permission,1 sort_no UNION ALL SELECT 'èç¨¿å®¡æ ¸','erp:after-sale-finance:review',2) p |
| | | WHERE @finance_page IS NOT NULL AND NOT EXISTS(SELECT 1 FROM system_menu m WHERE m.permission=p.permission AND m.deleted=b'0'); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ä¾èµ process_knowledge_menu_20261105.sqlï¼èå ID ç±æ°æ®åºçæã |
| | | SET @process_parent := (SELECT id FROM system_menu WHERE path = '/process-design' AND parent_id = 0 AND deleted = b'0' LIMIT 1); |
| | | SET @knowledge_menu := (SELECT id FROM system_menu WHERE parent_id = @process_parent AND path = 'knowledge' AND type = 1 AND deleted = b'0' LIMIT 1); |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, updater) |
| | | SELECT 'ç¥è¯ç»´æ¤', '', 2, 0, @knowledge_menu, 'catalog', 'lucide:book-open', 'mes/process-design/knowledge/index', 'MesPdKnowledge', 0, b'1', b'1', b'1', '1', '1' |
| | | WHERE @knowledge_menu IS NOT NULL AND NOT EXISTS (SELECT 1 FROM system_menu WHERE component = 'mes/process-design/knowledge/index' AND deleted = b'0'); |
| | | SET @knowledge_page := (SELECT id FROM system_menu WHERE component = 'mes/process-design/knowledge/index' AND deleted = b'0' LIMIT 1); |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, status, visible, keep_alive, always_show, creator, updater) |
| | | SELECT p.name, p.permission, 3, p.sort_no, @knowledge_page, '', '', 0, b'1', b'1', b'1', '1', '1' |
| | | FROM (SELECT 'ç¥è¯æ¥è¯¢' name, 'mes:pd-knowledge:query' permission, 1 sort_no UNION ALL |
| | | SELECT 'ç¥è¯å建', 'mes:pd-knowledge:create', 2 UNION ALL |
| | | SELECT 'ç¥è¯ä¿®æ¹', 'mes:pd-knowledge:update', 3 UNION ALL |
| | | SELECT 'ç¥è¯åå¸', 'mes:pd-knowledge:publish', 4 UNION ALL |
| | | SELECT 'ç¥è¯åè', 'mes:pd-knowledge:reference', 5) p |
| | | WHERE @knowledge_page IS NOT NULL AND NOT EXISTS |
| | | (SELECT 1 FROM system_menu m WHERE m.permission = p.permission AND m.deleted = b'0'); |
| | | |
| | | -- 页é¢ä¸æä½æééè¿ç³»ç»è§è²ç®¡çæ¾å¼åé
ï¼ä¸èªå¨æ©å¤§å·²æè§è²æéã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- å·¥èºç¥è¯åºèåæ´çï¼ä¿çå页é¢ãæéåæé®ï¼ä»
æ°å¢ç®å½å¹¶è°æ´ç¶çº§ã |
| | | -- å¨ç®æ åºæ§è¡å请å
å¤ä»½ system_menu å system_role_menuã |
| | | -- éè¿ç»ä»¶è·¯å¾å®ä½å·²æèåï¼ä¸ä¾èµè·¨é¡¹ç®çåºå®èå IDã |
| | | SET @process_parent := (SELECT id FROM system_menu WHERE path = '/process-design' AND parent_id = 0 AND deleted = b'0' LIMIT 1); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, status, visible, keep_alive, always_show, creator, updater) |
| | | SELECT 'å·¥èºç¥è¯åº', '', 1, 1, @process_parent, 'knowledge', 'lucide:library-big', 0, b'1', b'1', b'1', '1', '1' |
| | | WHERE @process_parent IS NOT NULL |
| | | AND NOT EXISTS (SELECT 1 FROM system_menu WHERE parent_id = @process_parent AND path = 'knowledge' AND deleted = b'0'); |
| | | |
| | | SET @knowledge_menu := (SELECT id FROM system_menu WHERE parent_id = @process_parent AND path = 'knowledge' AND type = 1 AND deleted = b'0' LIMIT 1); |
| | | |
| | | -- åªåå·²æç¸å
³é¡µé¢æéçè§è²å¼æ¾ç®å½ï¼ä¸æ©å¤§ä¸å¡é¡µé¢æéã |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, updater, tenant_id) |
| | | SELECT DISTINCT rm.role_id, @knowledge_menu, '1', '1', rm.tenant_id |
| | | FROM system_role_menu rm JOIN system_menu m ON m.id = rm.menu_id |
| | | WHERE @knowledge_menu IS NOT NULL AND rm.deleted = b'0' AND m.deleted = b'0' |
| | | AND m.component IN ('mes/process-design/parameter/index', 'mes/process-design/template/index', 'mes/process-design/process/index') |
| | | AND NOT EXISTS (SELECT 1 FROM system_role_menu existing WHERE existing.role_id = rm.role_id AND existing.tenant_id = rm.tenant_id AND existing.menu_id = @knowledge_menu AND existing.deleted = b'0'); |
| | | |
| | | UPDATE system_menu SET parent_id = @knowledge_menu, updater = '1', update_time = NOW() |
| | | WHERE @knowledge_menu IS NOT NULL AND parent_id = @process_parent AND deleted = b'0' |
| | | AND component IN ('mes/process-design/parameter/index', 'mes/process-design/template/index', 'mes/process-design/process/index'); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- å·¥èºç¥è¯åºç¬ç«æ°æ®ç»æï¼ä¸ä¿®æ¹æ¢æè·¯çº¿ãåæ°åå·¥åºæ°æ®ã |
| | | -- 使ç¨ä¸å¡ ID å
³èç°æä¸»æ°æ®ï¼ä¸è·¨æ¨¡å设置ç©çå¤é®ï¼ä¾¿äºé¡¹ç®é´è¿ç§»ã |
| | | CREATE TABLE IF NOT EXISTS mes_pd_knowledge ( |
| | | id BIGINT NOT NULL AUTO_INCREMENT, |
| | | code VARCHAR(64) NOT NULL COMMENT 'ç¥è¯ç¼ç ', |
| | | name VARCHAR(200) NOT NULL COMMENT 'ç¥è¯åç§°', |
| | | version INT NOT NULL DEFAULT 1 COMMENT 'ç¥è¯çæ¬', |
| | | product_item_id BIGINT DEFAULT NULL COMMENT 'éç¨æåç©æ', |
| | | bom_id BIGINT DEFAULT NULL COMMENT 'éç¨ BOM åç©æç¼å·', |
| | | voltage_min DECIMAL(16,4) DEFAULT NULL COMMENT 'æä½çµåï¼åä½ kV', |
| | | voltage_max DECIMAL(16,4) DEFAULT NULL COMMENT 'æé«çµåï¼åä½ kV', |
| | | market VARCHAR(16) NOT NULL DEFAULT 'ALL' COMMENT 'ALLãDOMESTIC æ EXPORT', |
| | | route_id BIGINT DEFAULT NULL COMMENT 'åèå·¥èºè·¯çº¿', |
| | | template_id BIGINT DEFAULT NULL COMMENT 'åèåæ°æ¨¡æ¿', |
| | | description TEXT COMMENT 'éç¨è¯´æ', |
| | | status TINYINT NOT NULL DEFAULT 0 COMMENT '0 è稿ã1 å·²åå¸ã2 åç¨', |
| | | creator VARCHAR(64) DEFAULT '', |
| | | create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | updater VARCHAR(64) DEFAULT '', |
| | | update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | deleted BIT(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (id), |
| | | UNIQUE KEY uq_knowledge_code_version (code, version), |
| | | KEY idx_knowledge_match (status, market, product_item_id, deleted) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='å·¥èºç¥è¯çæ¬'; |
| | | |
| | | -- åæ¥ä¸ºå
åå·²å建ç表补建å¯ä¸ç´¢å¼ï¼å¦å·²æé夿°æ®ååºå
人工å¤çï¼ä¸è½éé»è·³è¿ã |
| | | SET @knowledge_unique_sql := IF( |
| | | (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() |
| | | AND table_name = 'mes_pd_knowledge' AND index_name = 'uq_knowledge_code_version') = 0, |
| | | 'ALTER TABLE mes_pd_knowledge ADD UNIQUE KEY uq_knowledge_code_version (code, version)', |
| | | 'SELECT 1'); |
| | | PREPARE knowledge_unique_stmt FROM @knowledge_unique_sql; |
| | | EXECUTE knowledge_unique_stmt; |
| | | DEALLOCATE PREPARE knowledge_unique_stmt; |
| | | |
| | | CREATE TABLE IF NOT EXISTS mes_pd_knowledge_standard ( |
| | | id BIGINT NOT NULL AUTO_INCREMENT, |
| | | knowledge_id BIGINT NOT NULL, |
| | | standard_code VARCHAR(128) NOT NULL COMMENT 'éç¨æ åç¼ç ', |
| | | standard_name VARCHAR(200) DEFAULT NULL COMMENT 'éç¨æ ååç§°', |
| | | creator VARCHAR(64) DEFAULT '', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | updater VARCHAR(64) DEFAULT '', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | deleted BIT(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (id), KEY idx_knowledge_standard_parent (knowledge_id, deleted), |
| | | KEY idx_knowledge_standard_code (standard_code, deleted) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='å·¥èºç¥è¯éç¨æ å'; |
| | | |
| | | CREATE TABLE IF NOT EXISTS mes_pd_knowledge_resource ( |
| | | id BIGINT NOT NULL AUTO_INCREMENT, |
| | | knowledge_id BIGINT NOT NULL, |
| | | process_id BIGINT DEFAULT NULL COMMENT 'ç°æå·¥åº', |
| | | resource_type VARCHAR(32) NOT NULL COMMENT 'èµæºç±»å', |
| | | resource_id BIGINT DEFAULT NULL COMMENT 'ç°æèµæºç¼å·', |
| | | capability_description VARCHAR(500) DEFAULT NULL COMMENT 'è½åå约ææè¿°', |
| | | creator VARCHAR(64) DEFAULT '', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | updater VARCHAR(64) DEFAULT '', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | deleted BIT(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (id), KEY idx_knowledge_resource_parent (knowledge_id, deleted) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='å·¥èºç¥è¯èµæºè½å'; |
| | | |
| | | CREATE TABLE IF NOT EXISTS mes_pd_knowledge_reference ( |
| | | id BIGINT NOT NULL AUTO_INCREMENT, |
| | | knowledge_id BIGINT NOT NULL, |
| | | knowledge_version INT NOT NULL COMMENT 'å¼ç¨æ¶çæ¬', |
| | | target_type VARCHAR(32) NOT NULL COMMENT 'å¼ç¨ç®æ ç±»å', |
| | | target_id BIGINT NOT NULL COMMENT 'å¼ç¨ç®æ ç¼å·', |
| | | creator VARCHAR(64) DEFAULT '', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | updater VARCHAR(64) DEFAULT '', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | deleted BIT(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (id), UNIQUE KEY uq_knowledge_reference (knowledge_id, target_type, target_id), |
| | | KEY idx_knowledge_reference_source (knowledge_id, knowledge_version, deleted), |
| | | KEY idx_knowledge_reference_target (target_type, target_id, deleted) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='å·¥èºç¥è¯å¼ç¨è®°å½'; |
| | | |
| | | SET @knowledge_reference_sql := IF( |
| | | (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() |
| | | AND table_name = 'mes_pd_knowledge_reference' AND index_name = 'uq_knowledge_reference') = 0, |
| | | 'ALTER TABLE mes_pd_knowledge_reference ADD UNIQUE KEY uq_knowledge_reference (knowledge_id, target_type, target_id)', |
| | | 'SELECT 1'); |
| | | PREPARE knowledge_reference_stmt FROM @knowledge_reference_sql; |
| | | EXECUTE knowledge_reference_stmt; |
| | | DEALLOCATE PREPARE knowledge_reference_stmt; |
| | |
| | | </description> |
| | | |
| | | <dependencies> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-srm-api</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | <!-- å®å API 模å --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.aftersales.controller.admin.collaboration; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.aftersales.dal.dataobject.collaboration.AfterSaleCollaborationDO; |
| | | import cn.iocoder.yudao.module.aftersales.service.collaboration.AfterSaleCollaborationService; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import java.util.List; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | |
| | | @RestController |
| | | @RequestMapping("/aftersales/collaboration") |
| | | public class AfterSaleCollaborationController { |
| | | @Resource private AfterSaleCollaborationService service; |
| | | |
| | | @PostMapping("/create") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:collaboration:create')") |
| | | public CommonResult<Long> create(@RequestBody AfterSaleCollaborationService.CreateRequest input) { |
| | | return success(service.create(input, getLoginUserId())); |
| | | } |
| | | |
| | | @GetMapping("/my-page") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:collaboration:query')") |
| | | public CommonResult<PageResult<AfterSaleCollaborationDO>> myPage(@Valid PageParam page) { |
| | | return success(service.myTasks(page, getLoginUserId())); |
| | | } |
| | | |
| | | @GetMapping("/ticket-list") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:collaboration:query')") |
| | | public CommonResult<List<AfterSaleCollaborationDO>> ticketList(@RequestParam("ticketId") Long ticketId) { |
| | | return success(service.ticketTasks(ticketId, getLoginUserId())); |
| | | } |
| | | |
| | | @PutMapping("/complete") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:collaboration:update')") |
| | | public CommonResult<Boolean> complete(@RequestParam("id") Long id, @RequestBody CompleteRequest input) { |
| | | service.complete(id, getLoginUserId(), input == null ? null : input.result()); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/cancel") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:collaboration:update')") |
| | | public CommonResult<Boolean> cancel(@RequestParam("id") Long id) { |
| | | service.cancel(id, getLoginUserId()); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/review-financial") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:collaboration:approve')") |
| | | public CommonResult<Boolean> reviewFinancial(@RequestParam("id") Long id, @RequestBody ReviewRequest input) { |
| | | if (input == null || input.approved() == null) throw invalidParamException("è¯·éæ©å®¡æ ¸ç»æ"); |
| | | service.reviewFinancial(id, getLoginUserId(), input.approved(), input.remark()); |
| | | return success(true); |
| | | } |
| | | |
| | | public record CompleteRequest(String result) {} |
| | | public record ReviewRequest(Boolean approved, String remark) {} |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.aftersales.dal.dataobject.ticket.AfterSaleTicketDO; |
| | | import cn.iocoder.yudao.module.aftersales.dal.dataobject.ticket.AfterSaleTicketItemDO; |
| | | import cn.iocoder.yudao.module.aftersales.service.ticket.AfterSaleTicketService; |
| | | import cn.iocoder.yudao.module.aftersales.service.ticket.AfterSaleLedgerService; |
| | | import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi; |
| | | import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | |
| | | |
| | | @Resource |
| | | private AfterSaleTicketService ticketService; |
| | | @Resource |
| | | private AfterSaleLedgerService ledgerService; |
| | | |
| | | @Resource |
| | | private CrmCustomerApi customerApi; |
| | |
| | | return success(new PageResult<>(list, pageResult.getTotal())); |
| | | } |
| | | |
| | | @GetMapping("/overview") |
| | | @Operation(summary = "ç»è®¡å½åç¨æ·å¯è§çå®åå·¥åç¶æ") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:ticket:query')") |
| | | public CommonResult<Map<Integer, Long>> getOverview(@RequestParam("sceneType") Integer sceneType) { |
| | | if (sceneType == null || sceneType < 1 || sceneType > 4) { |
| | | throw cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException("å·¥ååºæ¯ä¸æ£ç¡®"); |
| | | } |
| | | return success(ledgerService.overview(getLoginUserId(), sceneType)); |
| | | } |
| | | |
| | | @PostMapping("/assign") |
| | | @Operation(summary = "åé
å¤ç人") |
| | | @PreAuthorize("@ss.hasPermission('aftersales:ticket:update')") |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.aftersales.dal.dataobject.collaboration; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import cn.iocoder.yudao.module.erp.api.finance.dto.AfterSaleFinanceReviewDTO; |
| | | import tools.jackson.databind.annotation.JsonSerialize; |
| | | import tools.jackson.databind.ser.std.ToStringSerializer; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import java.time.LocalDateTime; |
| | | import java.math.BigDecimal; |
| | | |
| | | @TableName("after_sale_collaboration") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class AfterSaleCollaborationDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long ticketId; |
| | | private String category; |
| | | private String title; |
| | | private String content; |
| | | private Long assigneeUserId; |
| | | private Long requestedBy; |
| | | private Integer status; |
| | | private String result; |
| | | private LocalDateTime completedTime; |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private BigDecimal amount; |
| | | private String direction; |
| | | private String partyName; |
| | | private String partyType; |
| | | private Long partyId; |
| | | private Long financeDraftId; |
| | | private Long previousId; |
| | | private Long businessApproverId; |
| | | private LocalDateTime businessApprovalTime; |
| | | private String businessRemark; |
| | | @TableField(exist = false) |
| | | private AfterSaleFinanceReviewDTO financeReview; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.aftersales.dal.mysql.collaboration; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.aftersales.dal.dataobject.collaboration.AfterSaleCollaborationDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface AfterSaleCollaborationMapper extends BaseMapperX<AfterSaleCollaborationDO> {} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.aftersales.service.collaboration; |
| | | |
| | | import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.aftersales.dal.dataobject.collaboration.AfterSaleCollaborationDO; |
| | | import cn.iocoder.yudao.module.aftersales.dal.dataobject.ticket.AfterSaleTicketDO; |
| | | import cn.iocoder.yudao.module.aftersales.dal.mysql.collaboration.AfterSaleCollaborationMapper; |
| | | import cn.iocoder.yudao.module.aftersales.enums.AfterSaleBizTypeEnum; |
| | | import cn.iocoder.yudao.module.aftersales.enums.permission.AfterSalePermissionLevelEnum; |
| | | import cn.iocoder.yudao.module.aftersales.service.permission.AfterSalePermissionService; |
| | | import cn.iocoder.yudao.module.aftersales.service.ticket.AfterSaleTicketService; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi; |
| | | import cn.iocoder.yudao.module.erp.api.finance.AfterSaleFinanceDraftApi; |
| | | import cn.iocoder.yudao.module.erp.api.finance.dto.AfterSaleFinanceDraftCreateDTO; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.Data; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.dao.DuplicateKeyException; |
| | | import java.time.LocalDateTime; |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | |
| | | @Service |
| | | public class AfterSaleCollaborationService { |
| | | @Resource private AfterSaleCollaborationMapper mapper; |
| | | @Resource private AfterSaleTicketService ticketService; |
| | | @Resource private AfterSalePermissionService permissionService; |
| | | @Resource private PermissionCommonApi permissionApi; |
| | | @Resource private AdminUserApi userApi; |
| | | @Resource private AfterSaleFinanceDraftApi financeDraftApi; |
| | | @Resource private SrmSupplierApi supplierApi; |
| | | @Resource private CrmCustomerApi customerApi; |
| | | |
| | | @Data |
| | | public static class CreateRequest { |
| | | private Long ticketId; |
| | | private String category; |
| | | private String title; |
| | | private String content; |
| | | private Long assigneeUserId; |
| | | private BigDecimal amount; |
| | | private String direction; |
| | | private String partyName; |
| | | private Long partyId; |
| | | private Long previousId; |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long create(CreateRequest input, Long userId) { |
| | | if (userId == null || input == null || input.getTicketId() == null || input.getAssigneeUserId() == null |
| | | || input.getCategory() == null || !Set.of("QUALITY", "FINANCE", "SUPPLIER", "WARRANTY", "RESEARCH", "PROCESS").contains(input.getCategory()) |
| | | || input.getTitle() == null || input.getTitle().isBlank() || input.getTitle().length() > 200 |
| | | || input.getContent() == null || input.getContent().isBlank() || input.getContent().length() > 2000) |
| | | throw invalidParamException("è¯·å®æ´å¡«åååäºé¡¹åæ¥æ¶äºº"); |
| | | boolean financialCategory = Set.of("FINANCE", "SUPPLIER", "WARRANTY").contains(input.getCategory()); |
| | | if (financialCategory && input.getAmount() == null) throw invalidParamException("该ååç±»åå¿
须填åéé¢"); |
| | | if (input.getAmount() != null) { |
| | | if (!Set.of("QUALITY", "FINANCE", "SUPPLIER", "WARRANTY").contains(input.getCategory()) |
| | | || input.getAmount().signum() <= 0 || input.getAmount().stripTrailingZeros().scale() > 2 |
| | | || input.getAmount().compareTo(new BigDecimal("9999999999999999.99")) > 0 |
| | | || input.getDirection() == null || !Set.of("RECEIVABLE", "PAYABLE", "INTERNAL").contains(input.getDirection()) |
| | | || (input.getPartyName() != null && input.getPartyName().length() > 200) |
| | | || (!Set.of("SUPPLIER", "WARRANTY").contains(input.getCategory()) && !"INTERNAL".equals(input.getDirection()) && (input.getPartyName() == null || input.getPartyName().isBlank()))) |
| | | throw invalidParamException("éé¢ãèµéæ¹åæå¾æ¥æ¹ä¸æ£ç¡®"); |
| | | } else if (input.getDirection() != null || input.getPartyId() != null || (input.getPartyName() != null && !input.getPartyName().isBlank())) { |
| | | throw invalidParamException("å¡«åèµéæ¹åæå¾æ¥æ¹æ¶å¿
须填åéé¢"); |
| | | } |
| | | AfterSaleTicketDO ticket = ticketService.getTicket(input.getTicketId()); |
| | | if (ticket == null) throw invalidParamException("å®åå·¥åä¸åå¨"); |
| | | if (Objects.equals(ticket.getStatus(), 60) || Objects.equals(ticket.getStatus(), 99)) |
| | | throw invalidParamException("å·²å
³éæåæ¶çå·¥åä¸è½åèµ·åå"); |
| | | if (!canWrite(ticket, userId)) throw invalidParamException("æ æå起该工åçååäºé¡¹"); |
| | | String partyType = null, partyName = null; |
| | | Long partyId = null; |
| | | if (input.getAmount() != null) { |
| | | if ("SUPPLIER".equals(input.getCategory())) { |
| | | if (input.getPartyId() == null) throw invalidParamException("è¯·éæ©ä¾åºå"); |
| | | var supplier = supplierApi.getSupplier(input.getPartyId()).getCheckedData(); |
| | | if (supplier == null || !Objects.equals(supplier.getStatus(), 0) || supplier.getName() == null || supplier.getName().isBlank()) |
| | | throw invalidParamException("ä¾åºåä¸å卿已åç¨"); |
| | | partyType = "SUPPLIER"; partyId = supplier.getId(); partyName = supplier.getName(); |
| | | } else if ("WARRANTY".equals(input.getCategory())) { |
| | | if (ticket.getCustomerId() == null) throw invalidParamException("å·¥åæªå
³è客æ·ï¼è¯·å
å®åå·¥å"); |
| | | var customer = customerApi.getCustomer(ticket.getCustomerId()); |
| | | if (customer == null || customer.getName() == null || customer.getName().isBlank()) |
| | | throw invalidParamException("å·¥åå
³èç客æ·ä¸åå¨"); |
| | | partyType = "CUSTOMER"; partyId = customer.getId(); partyName = customer.getName(); |
| | | } else if ("INTERNAL".equals(input.getDirection())) { |
| | | partyType = "INTERNAL"; |
| | | } else { |
| | | partyType = "OTHER"; partyName = input.getPartyName().trim(); |
| | | } |
| | | } |
| | | userApi.validateUser(input.getAssigneeUserId()); |
| | | if (input.getPreviousId() != null) { |
| | | AfterSaleCollaborationDO previous = mapper.selectById(input.getPreviousId()); |
| | | if (previous == null || !Objects.equals(previous.getRequestedBy(), userId) |
| | | || !Objects.equals(previous.getTicketId(), input.getTicketId()) || !Objects.equals(previous.getCategory(), input.getCategory())) |
| | | throw invalidParamException("åååäºé¡¹ä¸å¹é
ææ æéæ°åèµ·"); |
| | | boolean allowed = Objects.equals(previous.getStatus(), 4) |
| | | || (Objects.equals(previous.getStatus(), 2) && previous.getPreviousId() != null); |
| | | if (Objects.equals(previous.getStatus(), 3) && previous.getFinanceDraftId() != null) { |
| | | var review = financeDraftApi.getReviewResults(List.of(previous.getFinanceDraftId())).get(previous.getFinanceDraftId()); |
| | | allowed = review != null && Objects.equals(review.getStatus(), 2); |
| | | } |
| | | if (!allowed) throw invalidParamException("åªæä¸å¡éåãè´¢å¡éåæå·²åæ¶çéåäºé¡¹å¯ä»¥éæ°åèµ·"); |
| | | } |
| | | var task = new AfterSaleCollaborationDO(); |
| | | task.setTicketId(input.getTicketId()); task.setCategory(input.getCategory()); task.setTitle(input.getTitle().trim()); |
| | | task.setContent(input.getContent().trim()); task.setAssigneeUserId(input.getAssigneeUserId()); |
| | | task.setRequestedBy(userId); task.setStatus(0); task.setAmount(input.getAmount()); |
| | | task.setDirection(input.getDirection()); task.setPartyName(partyName); task.setPartyType(partyType); task.setPartyId(partyId); task.setPreviousId(input.getPreviousId()); |
| | | try { mapper.insert(task); } |
| | | catch (DuplicateKeyException e) { throw invalidParamException("该äºé¡¹å·²éæ°åèµ·ï¼è¯·å·æ°åæ¥ç"); } |
| | | return task.getId(); |
| | | } |
| | | |
| | | public PageResult<AfterSaleCollaborationDO> myTasks(PageParam page, Long userId) { |
| | | if (userId == null) throw invalidParamException("ç¨æ·æªç»å½"); |
| | | boolean canApprove = permissionApi.hasAnyPermissions(userId, "aftersales:collaboration:approve"); |
| | | boolean isAdmin = canApprove && permissionApi.hasAnyRoles(userId, "aftersales_admin"); |
| | | var query = new LambdaQueryWrapperX<AfterSaleCollaborationDO>(); |
| | | query.and(w -> { |
| | | w.eq(AfterSaleCollaborationDO::getAssigneeUserId, userId).or().eq(AfterSaleCollaborationDO::getRequestedBy, userId) |
| | | .or().eq(AfterSaleCollaborationDO::getBusinessApproverId, userId); |
| | | if (canApprove) w.or(approval -> { |
| | | approval.eq(AfterSaleCollaborationDO::getStatus, 1).isNotNull(AfterSaleCollaborationDO::getAmount); |
| | | if (!isAdmin) approval.exists("SELECT 1 FROM after_sale_ticket t WHERE t.id = after_sale_collaboration.ticket_id AND t.deleted = 0 " |
| | | + "AND (t.owner_user_id = {0} OR EXISTS (SELECT 1 FROM after_sale_permission p WHERE p.biz_type = 1 " |
| | | + "AND p.biz_id = t.id AND p.user_id = {1} AND p.level IN (1, 3) AND p.deleted = 0))", userId, userId); |
| | | }); |
| | | }); |
| | | PageResult<AfterSaleCollaborationDO> result = mapper.selectPage(page, query.orderByDesc(AfterSaleCollaborationDO::getId)); |
| | | attachFinanceReviews(result.getList()); |
| | | return result; |
| | | } |
| | | |
| | | public List<AfterSaleCollaborationDO> ticketTasks(Long ticketId, Long userId) { |
| | | if (userId == null || ticketId == null) throw invalidParamException("忰䏿£ç¡®"); |
| | | AfterSaleTicketDO ticket = ticketService.getTicket(ticketId); |
| | | if (ticket == null) throw invalidParamException("å®åå·¥åä¸åå¨"); |
| | | var query = new LambdaQueryWrapperX<AfterSaleCollaborationDO>().eq(AfterSaleCollaborationDO::getTicketId, ticketId); |
| | | if (!canRead(ticket, userId)) query.and(w -> w.eq(AfterSaleCollaborationDO::getAssigneeUserId, userId).or().eq(AfterSaleCollaborationDO::getRequestedBy, userId)); |
| | | List<AfterSaleCollaborationDO> result = mapper.selectList(query.orderByDesc(AfterSaleCollaborationDO::getId)); |
| | | attachFinanceReviews(result); |
| | | return result; |
| | | } |
| | | |
| | | private void attachFinanceReviews(List<AfterSaleCollaborationDO> tasks) { |
| | | List<Long> ids = tasks.stream().map(AfterSaleCollaborationDO::getFinanceDraftId).filter(Objects::nonNull).distinct().toList(); |
| | | if (ids.isEmpty()) return; |
| | | var reviews = financeDraftApi.getReviewResults(ids); |
| | | tasks.forEach(task -> task.setFinanceReview(reviews.get(task.getFinanceDraftId()))); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void complete(Long id, Long userId, String result) { |
| | | if (id == null || userId == null || result == null || result.isBlank() || result.length() > 2000) |
| | | throw invalidParamException("请填åä¸è¶
è¿ 2000 åçå¤çç»æ"); |
| | | AfterSaleCollaborationDO task = mapper.selectById(id); |
| | | if (task == null || !Objects.equals(task.getAssigneeUserId(), userId) || !Objects.equals(task.getStatus(), 0)) |
| | | throw invalidParamException("ååäºé¡¹ä¸åå¨ãå·²å¤çæå½åç¨æ·ä¸æ¯æ¥æ¶äºº"); |
| | | if (mapper.update(null, new LambdaUpdateWrapper<AfterSaleCollaborationDO>() |
| | | .eq(AfterSaleCollaborationDO::getId, id).eq(AfterSaleCollaborationDO::getStatus, 0) |
| | | .eq(AfterSaleCollaborationDO::getAssigneeUserId, userId).set(AfterSaleCollaborationDO::getStatus, 1) |
| | | .set(AfterSaleCollaborationDO::getResult, result.trim()).set(AfterSaleCollaborationDO::getCompletedTime, LocalDateTime.now())) != 1) |
| | | throw invalidParamException("ååäºé¡¹ç¶æå·²ååï¼è¯·å·æ°åéè¯"); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void cancel(Long id, Long userId) { |
| | | if (id == null || userId == null) throw invalidParamException("忰䏿£ç¡®"); |
| | | AfterSaleCollaborationDO task = mapper.selectById(id); |
| | | if (task == null || !Objects.equals(task.getRequestedBy(), userId) || !Objects.equals(task.getStatus(), 0)) |
| | | throw invalidParamException("åªæå起人å¯ä»¥åæ¶å¾
å¤çäºé¡¹"); |
| | | if (mapper.update(null, new LambdaUpdateWrapper<AfterSaleCollaborationDO>() |
| | | .eq(AfterSaleCollaborationDO::getId, id).eq(AfterSaleCollaborationDO::getStatus, 0) |
| | | .eq(AfterSaleCollaborationDO::getRequestedBy, userId).set(AfterSaleCollaborationDO::getStatus, 2)) != 1) |
| | | throw invalidParamException("ååäºé¡¹ç¶æå·²ååï¼è¯·å·æ°åéè¯"); |
| | | } |
| | | |
| | | /** ä¸å¡å®¡æ¹éè¿åå建财å¡å®¡æ ¸è稿ï¼è稿åå»ºå¤±è´¥æ¶æ¬æ¬¡å®¡æ¹æ´ä½åæ»ã */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void reviewFinancial(Long id, Long userId, boolean approved, String remark) { |
| | | if (id == null || userId == null || (remark != null && remark.length() > 1000) |
| | | || (!approved && (remark == null || remark.isBlank()))) throw invalidParamException("éåæ¶è¯·å¡«åä¸å¡å®¡æ ¸æè§"); |
| | | AfterSaleCollaborationDO task = mapper.selectById(id); |
| | | if (task == null || !Objects.equals(task.getStatus(), 1) || task.getAmount() == null) |
| | | throw invalidParamException("äºé¡¹ä¸å卿æªå¤äºå¾
ä¸å¡å®¡æ ¸ç¶æ"); |
| | | AfterSaleTicketDO ticket = ticketService.getTicket(task.getTicketId()); |
| | | if (ticket == null || !canWrite(ticket, userId)) throw invalidParamException("æ æå®¡æ ¸è¯¥å·¥åçååäºé¡¹"); |
| | | int changed = mapper.update(null, new LambdaUpdateWrapper<AfterSaleCollaborationDO>() |
| | | .eq(AfterSaleCollaborationDO::getId, id).eq(AfterSaleCollaborationDO::getStatus, 1) |
| | | .isNotNull(AfterSaleCollaborationDO::getAmount) |
| | | .set(AfterSaleCollaborationDO::getStatus, approved ? 3 : 4) |
| | | .set(AfterSaleCollaborationDO::getBusinessApproverId, userId) |
| | | .set(AfterSaleCollaborationDO::getBusinessApprovalTime, LocalDateTime.now()) |
| | | .set(AfterSaleCollaborationDO::getBusinessRemark, remark)); |
| | | if (changed != 1) throw invalidParamException("äºé¡¹ç¶æå·²ååï¼è¯·å·æ°åéè¯"); |
| | | if (approved) { |
| | | var input = new AfterSaleFinanceDraftCreateDTO(); |
| | | input.setCollaborationId(id); input.setTicketId(task.getTicketId()); input.setCategory(task.getCategory()); |
| | | input.setDirection(task.getDirection()); input.setAmount(task.getAmount()); input.setPartyName(task.getPartyName()); |
| | | input.setPartyType(task.getPartyType()); input.setPartyId(task.getPartyId()); |
| | | input.setDescription(task.getResult()); input.setBusinessApproverId(userId); |
| | | Long draftId = financeDraftApi.createDraft(input); |
| | | if (draftId == null) throw invalidParamException("è´¢å¡è稿å建失败"); |
| | | var update = new AfterSaleCollaborationDO(); update.setId(id); update.setFinanceDraftId(draftId); |
| | | if (mapper.updateById(update) != 1) throw invalidParamException("å
³èè´¢å¡è稿失败ï¼è¯·éè¯"); |
| | | } |
| | | } |
| | | |
| | | private boolean canWrite(AfterSaleTicketDO ticket, Long userId) { |
| | | return Objects.equals(ticket.getOwnerUserId(), userId) || permissionApi.hasAnyRoles(userId, "aftersales_admin") |
| | | || permissionService.hasPermission(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType(), ticket.getId(), userId, AfterSalePermissionLevelEnum.WRITE) |
| | | || permissionService.hasPermission(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType(), ticket.getId(), userId, AfterSalePermissionLevelEnum.OWNER); |
| | | } |
| | | |
| | | private boolean canRead(AfterSaleTicketDO ticket, Long userId) { |
| | | return canWrite(ticket, userId) || permissionService.hasPermission(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType(), ticket.getId(), userId, AfterSalePermissionLevelEnum.READ); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.aftersales.service.ticket; |
| | | |
| | | import cn.iocoder.yudao.module.aftersales.controller.admin.ticket.vo.ticket.AfterSaleTicketPageReqVO; |
| | | import cn.iocoder.yudao.module.aftersales.enums.AfterSaleTicketStatusEnum; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | |
| | | /** å¤ç¨å·¥åå表æéååºæ¯ï¼ç»è®¡å½åç¨æ·å¯è§å·¥åã */ |
| | | @Service |
| | | public class AfterSaleLedgerService { |
| | | @Resource private AfterSaleTicketService ticketService; |
| | | |
| | | public Map<Integer, Long> overview(Long userId, Integer sceneType) { |
| | | Map<Integer, Long> counts = new LinkedHashMap<>(); |
| | | for (AfterSaleTicketStatusEnum status : AfterSaleTicketStatusEnum.values()) { |
| | | var request = new AfterSaleTicketPageReqVO(); |
| | | request.setPageNo(1); request.setPageSize(1); request.setSceneType(sceneType); request.setStatus(status.getStatus()); |
| | | counts.put(status.getStatus(), ticketService.getTicketPage(request, userId).getTotal()); |
| | | } |
| | | return counts; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.api.finance; |
| | | |
| | | import cn.iocoder.yudao.module.erp.api.finance.dto.AfterSaleFinanceDraftCreateDTO; |
| | | import cn.iocoder.yudao.module.erp.api.finance.dto.AfterSaleFinanceReviewDTO; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** ä¾å®åä¸å¡ç¡®è®¤åå建财å¡å®¡æ ¸è稿ï¼ä¸æ§è¡ä»æ¬¾æè®°è´¦ã */ |
| | | public interface AfterSaleFinanceDraftApi { |
| | | Long createDraft(AfterSaleFinanceDraftCreateDTO input); |
| | | Map<Long, AfterSaleFinanceReviewDTO> getReviewResults(List<Long> ids); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.api.finance.dto; |
| | | |
| | | import lombok.Data; |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class AfterSaleFinanceDraftCreateDTO { |
| | | private Long collaborationId; |
| | | private Long ticketId; |
| | | private String category; |
| | | private String direction; |
| | | private BigDecimal amount; |
| | | private String partyName; |
| | | private String partyType; |
| | | private Long partyId; |
| | | private String description; |
| | | private Long businessApproverId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.api.finance.dto; |
| | | |
| | | import lombok.Data; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | public class AfterSaleFinanceReviewDTO { |
| | | private Integer status; |
| | | private String remark; |
| | | private LocalDateTime reviewTime; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.finance; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAfterSaleFinanceDraftDO; |
| | | import cn.iocoder.yudao.module.erp.service.finance.ErpAfterSaleFinanceDraftService; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | |
| | | @RestController |
| | | @RequestMapping("/erp/finance/after-sale-draft") |
| | | public class ErpAfterSaleFinanceDraftController { |
| | | @Resource private ErpAfterSaleFinanceDraftService service; |
| | | |
| | | @GetMapping("/page") |
| | | @PreAuthorize("@ss.hasPermission('erp:after-sale-finance:query')") |
| | | public CommonResult<PageResult<ErpAfterSaleFinanceDraftDO>> page(@Valid PageParam page, |
| | | @RequestParam(value = "status", required = false) Integer status) { |
| | | return success(service.page(page, status)); |
| | | } |
| | | |
| | | @PutMapping("/review") |
| | | @PreAuthorize("@ss.hasPermission('erp:after-sale-finance:review')") |
| | | public CommonResult<Boolean> review(@RequestParam("id") Long id, @RequestBody ReviewRequest request) { |
| | | if (request == null || request.approved() == null) throw invalidParamException("è¯·éæ©å®¡æ ¸ç»æ"); |
| | | service.review(id, getLoginUserId(), request.approved(), request.remark()); |
| | | return success(true); |
| | | } |
| | | |
| | | public record ReviewRequest(Boolean approved, String remark) {} |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.dal.dataobject.finance; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import tools.jackson.databind.annotation.JsonSerialize; |
| | | import tools.jackson.databind.ser.std.ToStringSerializer; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @TableName("erp_after_sale_finance_draft") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class ErpAfterSaleFinanceDraftDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long collaborationId; |
| | | private Long ticketId; |
| | | private String category; |
| | | private String direction; |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private BigDecimal amount; |
| | | private String partyName; |
| | | private String partyType; |
| | | private Long partyId; |
| | | private String description; |
| | | private Long businessApproverId; |
| | | private Integer status; |
| | | private Long reviewerId; |
| | | private String reviewRemark; |
| | | private LocalDateTime reviewTime; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.dal.mysql.finance; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAfterSaleFinanceDraftDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface ErpAfterSaleFinanceDraftMapper extends BaseMapperX<ErpAfterSaleFinanceDraftDO> {} |
| | |
| | | ErrorCode SALE_ORDER_SUBMIT_FAIL_ITEM_EMPTY = new ErrorCode(1_020_201_018, "æäº¤å®¡æ¹å¤±è´¥ï¼éå®è®¢å({})没æè®¢åæç»ï¼æ æ³æäº¤"); |
| | | ErrorCode SALE_ORDER_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_201_019, "éå®è®¢åå®¡æ ¸åç±»ä¸åå¨ï¼è¯·å
å¨BPM模åå建éå®è®¢åå®¡æ ¸æµç¨åç±»"); |
| | | ErrorCode SALE_ORDER_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_020_201_020, "éå®è®¢åå®¡æ ¸åç±»ä¸æ²¡æå¯ç¨çæµç¨æ¨¡åï¼è¯·å
å建并å叿µç¨"); |
| | | ErrorCode SALE_ORDER_CANCEL_FAIL_ACTIVE_MPS = new ErrorCode(1_020_201_021, "ä½åºå¤±è´¥ï¼è®¢å仿å
³èç产计åï¼è¯·å
å¤çå¹¶åæ¶ç产计å"); |
| | | ErrorCode SALE_ORDER_CANCEL_FAIL_MPS_UNKNOWN = new ErrorCode(1_020_201_022, "æ æ³ç¡®è®¤å
³èç产计åç¶æï¼è¯·ç¨åéè¯"); |
| | | |
| | | // ========== ERP éå®åºåºï¼1-030-202-000ï¼ ========== |
| | | ErrorCode SALE_OUT_NOT_EXISTS = new ErrorCode(1_020_202_000, "éå®åºåºåä¸åå¨"); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.service.finance; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.erp.api.finance.AfterSaleFinanceDraftApi; |
| | | import cn.iocoder.yudao.module.erp.api.finance.dto.AfterSaleFinanceDraftCreateDTO; |
| | | import cn.iocoder.yudao.module.erp.api.finance.dto.AfterSaleFinanceReviewDTO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAfterSaleFinanceDraftDO; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpAfterSaleFinanceDraftMapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.dao.DuplicateKeyException; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | |
| | | @Service |
| | | public class ErpAfterSaleFinanceDraftService implements AfterSaleFinanceDraftApi { |
| | | @Resource private ErpAfterSaleFinanceDraftMapper mapper; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createDraft(AfterSaleFinanceDraftCreateDTO input) { |
| | | if (input == null || input.getCollaborationId() == null || input.getTicketId() == null || input.getBusinessApproverId() == null |
| | | || input.getCategory() == null || !Set.of("QUALITY", "FINANCE", "SUPPLIER", "WARRANTY").contains(input.getCategory()) |
| | | || input.getDirection() == null || !Set.of("RECEIVABLE", "PAYABLE", "INTERNAL").contains(input.getDirection()) |
| | | || input.getAmount() == null || input.getAmount().signum() <= 0 || input.getAmount().stripTrailingZeros().scale() > 2 |
| | | || input.getAmount().compareTo(new BigDecimal("9999999999999999.99")) > 0 |
| | | || input.getDescription() == null || input.getDescription().isBlank() || input.getDescription().length() > 2000 |
| | | || (input.getPartyName() != null && input.getPartyName().length() > 200) |
| | | || (!"INTERNAL".equals(input.getDirection()) && (input.getPartyName() == null || input.getPartyName().isBlank()))) |
| | | throw invalidParamException("è´¢å¡è稿çéé¢ãæ¹åã徿¥æ¹ææ¥æºä¸æ£ç¡®"); |
| | | var draft = new ErpAfterSaleFinanceDraftDO(); |
| | | draft.setCollaborationId(input.getCollaborationId()); draft.setTicketId(input.getTicketId()); |
| | | draft.setCategory(input.getCategory()); draft.setDirection(input.getDirection()); draft.setAmount(input.getAmount()); |
| | | draft.setPartyName(input.getPartyName()); draft.setDescription(input.getDescription()); |
| | | draft.setPartyType(input.getPartyType()); draft.setPartyId(input.getPartyId()); |
| | | draft.setBusinessApproverId(input.getBusinessApproverId()); draft.setStatus(0); |
| | | try { mapper.insert(draft); } |
| | | catch (DuplicateKeyException e) { throw invalidParamException("该ååäºé¡¹å·²çæè´¢å¡è稿"); } |
| | | return draft.getId(); |
| | | } |
| | | |
| | | public PageResult<ErpAfterSaleFinanceDraftDO> page(PageParam page, Integer status) { |
| | | if (status != null && !Set.of(0, 1, 2).contains(status)) throw invalidParamException("å®¡æ ¸ç¶æä¸æ£ç¡®"); |
| | | return mapper.selectPage(page, new LambdaQueryWrapperX<ErpAfterSaleFinanceDraftDO>() |
| | | .eqIfPresent(ErpAfterSaleFinanceDraftDO::getStatus, status).orderByDesc(ErpAfterSaleFinanceDraftDO::getId)); |
| | | } |
| | | |
| | | @Override |
| | | public Map<Long, AfterSaleFinanceReviewDTO> getReviewResults(List<Long> ids) { |
| | | if (ids == null || ids.isEmpty()) return Map.of(); |
| | | return mapper.selectBatchIds(ids).stream().collect(Collectors.toMap(ErpAfterSaleFinanceDraftDO::getId, draft -> { |
| | | var result = new AfterSaleFinanceReviewDTO(); result.setStatus(draft.getStatus()); |
| | | result.setRemark(draft.getReviewRemark()); result.setReviewTime(draft.getReviewTime()); return result; |
| | | })); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void review(Long id, Long reviewerId, boolean approved, String remark) { |
| | | if (id == null || reviewerId == null || (remark != null && remark.length() > 1000) |
| | | || (!approved && (remark == null || remark.isBlank()))) throw invalidParamException("éåæ¶è¯·å¡«åå®¡æ ¸æè§"); |
| | | var draft = mapper.selectById(id); |
| | | if (draft == null || !Objects.equals(draft.getStatus(), 0)) throw invalidParamException("è稿ä¸åå¨æå·²å®¡æ ¸"); |
| | | if (mapper.update(null, new LambdaUpdateWrapper<ErpAfterSaleFinanceDraftDO>() |
| | | .eq(ErpAfterSaleFinanceDraftDO::getId, id).eq(ErpAfterSaleFinanceDraftDO::getStatus, 0) |
| | | .set(ErpAfterSaleFinanceDraftDO::getStatus, approved ? 1 : 2).set(ErpAfterSaleFinanceDraftDO::getReviewerId, reviewerId) |
| | | .set(ErpAfterSaleFinanceDraftDO::getReviewRemark, remark).set(ErpAfterSaleFinanceDraftDO::getReviewTime, LocalDateTime.now())) != 1) |
| | | throw invalidParamException("èç¨¿ç¶æå·²ååï¼è¯·å·æ°åéè¯"); |
| | | } |
| | | } |
| | |
| | | throw exception(SALE_ORDER_CANCEL_FAIL_EXISTS_OUT); |
| | | } |
| | | |
| | | // éå®è®¢åå®¡æ ¸åå¯è½å·²çæ MPSãæªåæ¶ç计åä¸è½éç订åä½åºç»§ç»æ§è¡ã |
| | | List<ErpSaleOrderItemDO> orderItems = saleOrderItemMapper.selectListByOrderId(id); |
| | | if (CollUtil.isNotEmpty(orderItems)) { |
| | | List<Long> itemIds = convertList(orderItems, ErpSaleOrderItemDO::getId); |
| | | List<MesProMpsRespDTO> plans = mpsApi.getMpsListBySaleOrderItemIds(itemIds); |
| | | if (plans == null) throw exception(SALE_ORDER_CANCEL_FAIL_MPS_UNKNOWN); |
| | | if (plans.stream().anyMatch(plan -> plan == null || ObjectUtil.notEqual(plan.getStatus(), MPS_STATUS_CANCELED))) { |
| | | throw exception(SALE_ORDER_CANCEL_FAIL_ACTIVE_MPS); |
| | | } |
| | | } |
| | | |
| | | // 2. æ´æ°ç¶æä¸ºå·²ä½åº |
| | | int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(), |
| | | new ErpSaleOrderDO().setStatus(ErpAuditStatus.CANCEL.getStatus())); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeDO; |
| | | import cn.iocoder.yudao.module.mes.service.pd.knowledge.KnowledgeMatch; |
| | | import cn.iocoder.yudao.module.mes.service.pd.knowledge.KnowledgeSearchService; |
| | | import cn.iocoder.yudao.module.mes.service.pd.knowledge.KnowledgeWriteService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | |
| | | @RestController |
| | | @RequestMapping("/mes/pd/knowledge") |
| | | public class KnowledgeController { |
| | | @Resource private KnowledgeSearchService searchService; |
| | | @Resource private KnowledgeWriteService writeService; |
| | | |
| | | @GetMapping("/page") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:query')") |
| | | public CommonResult<PageResult<KnowledgeDO>> page(PageParam page, |
| | | @RequestParam(value = "name", required = false) String name) { |
| | | return success(searchService.page(page, name)); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:query')") |
| | | public CommonResult<KnowledgeSearchService.Detail> get(@RequestParam("id") Long id) { |
| | | return success(searchService.get(id)); |
| | | } |
| | | |
| | | @PostMapping("/create") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:create')") |
| | | public CommonResult<Long> create(@RequestBody KnowledgeWriteService.SaveRequest input) { |
| | | return success(writeService.create(input)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:update')") |
| | | public CommonResult<Boolean> update(@RequestBody KnowledgeWriteService.SaveRequest input) { |
| | | writeService.update(input); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/publish") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:publish')") |
| | | public CommonResult<Boolean> publish(@RequestParam("id") Long id) { |
| | | writeService.publish(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @PostMapping("/revise") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:create')") |
| | | public CommonResult<Long> revise(@RequestParam("id") Long id) { |
| | | return success(writeService.revise(id)); |
| | | } |
| | | |
| | | @PostMapping("/reference-route") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:reference')") |
| | | public CommonResult<Boolean> referenceRoute(@RequestParam("id") Long id, @RequestParam("routeId") Long routeId) { |
| | | writeService.referenceRoute(id, routeId); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/match") |
| | | @PreAuthorize("@ss.hasPermission('mes:pd-knowledge:query')") |
| | | public CommonResult<List<KnowledgeDO>> match( |
| | | @RequestParam(value = "productId", required = false) Long productId, |
| | | @RequestParam(value = "bomId", required = false) Long bomId, |
| | | @RequestParam(value = "voltage", required = false) BigDecimal voltage, |
| | | @RequestParam(value = "market", required = false) String market, |
| | | @RequestParam(value = "standards", required = false) Set<String> standards) { |
| | | if (market != null && !Set.of("DOMESTIC", "EXPORT").contains(market)) { |
| | | throw invalidParamException("å
å¤éç±»å䏿£ç¡®"); |
| | | } |
| | | if (voltage != null && voltage.signum() < 0) { |
| | | throw invalidParamException("çµåä¸è½å°äºé¶"); |
| | | } |
| | | return success(searchService.match(new KnowledgeMatch.Request(productId, bomId, voltage, market, standards))); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import java.math.BigDecimal; |
| | | |
| | | @TableName("mes_pd_knowledge") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class KnowledgeDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private String code; |
| | | private String name; |
| | | private Integer version; |
| | | private Long productItemId; |
| | | private Long bomId; |
| | | private BigDecimal voltageMin; |
| | | private BigDecimal voltageMax; |
| | | private String market; |
| | | private Long routeId; |
| | | private Long templateId; |
| | | private String description; |
| | | private Integer status; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @TableName("mes_pd_knowledge_reference") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class KnowledgeReferenceDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long knowledgeId; |
| | | private Integer knowledgeVersion; |
| | | private String targetType; |
| | | private Long targetId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @TableName("mes_pd_knowledge_resource") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class KnowledgeResourceDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long knowledgeId; |
| | | private Long processId; |
| | | private String resourceType; |
| | | private Long resourceId; |
| | | private String capabilityDescription; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @TableName("mes_pd_knowledge_standard") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class KnowledgeStandardDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long knowledgeId; |
| | | private String standardCode; |
| | | private String standardName; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface KnowledgeMapper extends BaseMapperX<KnowledgeDO> {} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeReferenceDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface KnowledgeReferenceMapper extends BaseMapperX<KnowledgeReferenceDO> {} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeResourceDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface KnowledgeResourceMapper extends BaseMapperX<KnowledgeResourceDO> {} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeStandardDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface KnowledgeStandardMapper extends BaseMapperX<KnowledgeStandardDO> {} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pd.knowledge; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | |
| | | /** å¹é
å·²åå¸å·¥èºç¥è¯çéç¨èå´ï¼æªç¥æ¡ä»¶ä¸è½æ»¡è¶³æçº¦æçç¥è¯ã */ |
| | | public final class KnowledgeMatch { |
| | | private KnowledgeMatch() {} |
| | | |
| | | public record Criteria(Long productId, Long bomId, BigDecimal minVoltage, |
| | | BigDecimal maxVoltage, String market, Set<String> standards) {} |
| | | public record Request(Long productId, Long bomId, BigDecimal voltage, |
| | | String market, Set<String> standards) {} |
| | | |
| | | public static boolean matches(Criteria criteria, Request request) { |
| | | Objects.requireNonNull(criteria, "criteria"); |
| | | Objects.requireNonNull(request, "request"); |
| | | if (criteria.productId() != null && !criteria.productId().equals(request.productId())) return false; |
| | | if (criteria.bomId() != null && !criteria.bomId().equals(request.bomId())) return false; |
| | | if (criteria.minVoltage() != null || criteria.maxVoltage() != null) { |
| | | if (request.voltage() == null) return false; |
| | | if (criteria.minVoltage() != null && request.voltage().compareTo(criteria.minVoltage()) < 0) return false; |
| | | if (criteria.maxVoltage() != null && request.voltage().compareTo(criteria.maxVoltage()) > 0) return false; |
| | | } |
| | | if (!"ALL".equals(criteria.market()) && !Objects.equals(criteria.market(), request.market())) return false; |
| | | Set<String> available = criteria.standards() == null ? Set.of() : criteria.standards(); |
| | | Set<String> required = request.standards() == null ? Set.of() : request.standards(); |
| | | return available.containsAll(required); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeStandardDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeResourceDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeReferenceDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeStandardMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeResourceMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeReferenceMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | |
| | | /** å·²åå¸ç¥è¯çæ¡ä»¶å¹é
ï¼è稿ååç¨çæ¬ä¸å䏿¨èã */ |
| | | @Service |
| | | public class KnowledgeSearchService { |
| | | @Resource private KnowledgeMapper knowledgeMapper; |
| | | @Resource private KnowledgeStandardMapper standardMapper; |
| | | @Resource private KnowledgeResourceMapper resourceMapper; |
| | | @Resource private KnowledgeReferenceMapper referenceMapper; |
| | | @Resource private MesMdItemService itemService; |
| | | @Resource private MesMdProductBomService bomService; |
| | | |
| | | public PageResult<KnowledgeDO> page(PageParam page, String name) { |
| | | return knowledgeMapper.selectPage(page, new LambdaQueryWrapperX<KnowledgeDO>() |
| | | .likeIfPresent(KnowledgeDO::getName, name).orderByDesc(KnowledgeDO::getId)); |
| | | } |
| | | |
| | | public Detail get(Long id) { |
| | | KnowledgeDO record = knowledgeMapper.selectById(id); |
| | | if (record == null) return null; |
| | | List<String> standards = standardMapper.selectList(new LambdaQueryWrapperX<KnowledgeStandardDO>() |
| | | .eq(KnowledgeStandardDO::getKnowledgeId, id)).stream() |
| | | .map(KnowledgeStandardDO::getStandardCode).toList(); |
| | | List<KnowledgeResourceDO> resources = resourceMapper.selectList(new LambdaQueryWrapperX<KnowledgeResourceDO>() |
| | | .eq(KnowledgeResourceDO::getKnowledgeId, id)); |
| | | List<KnowledgeReferenceDO> references = referenceMapper.selectList(new LambdaQueryWrapperX<KnowledgeReferenceDO>() |
| | | .eq(KnowledgeReferenceDO::getKnowledgeId, id)); |
| | | return new Detail(record, standards, resources, references); |
| | | } |
| | | |
| | | public record Detail(KnowledgeDO knowledge, List<String> standards, List<KnowledgeResourceDO> resources, List<KnowledgeReferenceDO> references) {} |
| | | |
| | | public List<KnowledgeDO> match(KnowledgeMatch.Request request) { |
| | | if (request.bomId() != null && request.productId() == null) throw invalidParamException("è¯·éæ© BOM æç»æå±çç©æ"); |
| | | if (request.productId() != null) itemService.validateItemExists(request.productId()); |
| | | Set<Long> productBomItems; |
| | | if (request.productId() != null) { |
| | | productBomItems = bomService.getProductBomListByItemId(request.productId()).stream() |
| | | .filter(bom -> Objects.equals(bom.getItemId(), request.productId()) && Objects.equals(bom.getStatus(), 0)).map(MesMdProductBomDO::getBomItemId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | } else productBomItems = Set.of(); |
| | | if (request.bomId() != null && !productBomItems.contains(request.bomId())) |
| | | throw invalidParamException("æé BOM åç©æä¸åå¨ãæªå¯ç¨æä¸å±äºè¯¥ç©æ"); |
| | | Set<Long> candidateBomItems = request.bomId() == null ? productBomItems : Set.of(request.bomId()); |
| | | List<KnowledgeDO> records = knowledgeMapper.selectList(new LambdaQueryWrapperX<KnowledgeDO>() |
| | | .eq(KnowledgeDO::getStatus, 1).orderByDesc(KnowledgeDO::getId)); |
| | | if (records.isEmpty()) return List.of(); |
| | | List<Long> ids = records.stream().map(KnowledgeDO::getId).toList(); |
| | | Map<Long, Set<String>> standards = standardMapper.selectList(new LambdaQueryWrapperX<KnowledgeStandardDO>() |
| | | .in(KnowledgeStandardDO::getKnowledgeId, ids)).stream() |
| | | .collect(Collectors.groupingBy(KnowledgeStandardDO::getKnowledgeId, |
| | | Collectors.mapping(KnowledgeStandardDO::getStandardCode, Collectors.toSet()))); |
| | | return records.stream().filter(record -> { |
| | | if (record.getBomId() != null && !candidateBomItems.contains(record.getBomId())) return false; |
| | | // å·²æ BOM åç©ææ¯å¯¹ï¼å纯è§åä¼ éå½åç¥è¯å¯¹åºç约æã |
| | | var resolvedRequest = new KnowledgeMatch.Request(request.productId(), record.getBomId(), request.voltage(), request.market(), request.standards()); |
| | | return KnowledgeMatch.matches(new KnowledgeMatch.Criteria(record.getProductItemId(), record.getBomId(), |
| | | record.getVoltageMin(), record.getVoltageMax(), record.getMarket(), |
| | | standards.getOrDefault(record.getId(), Set.of())), resolvedRequest); |
| | | }).toList(); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pd.knowledge; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeStandardDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeResourceDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeReferenceDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeStandardMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeResourceMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeReferenceMapper; |
| | | import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryService; |
| | | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService; |
| | | import cn.iocoder.yudao.module.mes.service.pd.processparamtemplate.MesPdProcessParamTemplateService; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.Data; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.dao.DuplicateKeyException; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | | |
| | | @Service |
| | | public class KnowledgeWriteService { |
| | | @Resource private KnowledgeMapper knowledgeMapper; |
| | | @Resource private KnowledgeStandardMapper standardMapper; |
| | | @Resource private KnowledgeResourceMapper resourceMapper; |
| | | @Resource private KnowledgeReferenceMapper referenceMapper; |
| | | @Resource private MesDvMachineryService machineryService; |
| | | @Resource private MesMdWorkstationService workstationService; |
| | | @Resource private MesProProcessService processService; |
| | | @Resource private MesMdItemService itemService; |
| | | @Resource private MesMdProductBomService bomService; |
| | | @Resource private MesProRouteService routeService; |
| | | @Resource private MesPdProcessParamTemplateService templateService; |
| | | |
| | | @Data |
| | | public static class SaveRequest { |
| | | private Long id; |
| | | private String code; |
| | | private String name; |
| | | private Long productItemId; |
| | | private Long bomId; |
| | | private BigDecimal voltageMin; |
| | | private BigDecimal voltageMax; |
| | | private String market; |
| | | private Long routeId; |
| | | private Long templateId; |
| | | private String description; |
| | | private List<String> standards; |
| | | private List<ResourceInput> resources; |
| | | } |
| | | |
| | | @Data |
| | | public static class ResourceInput { |
| | | private Long processId; |
| | | private String resourceType; |
| | | private Long resourceId; |
| | | private String capabilityDescription; |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long create(SaveRequest input) { |
| | | validate(input); |
| | | String code = input.getCode().trim(); |
| | | if (!knowledgeMapper.selectList(new LambdaQueryWrapperX<KnowledgeDO>() |
| | | .eq(KnowledgeDO::getCode, code).eq(KnowledgeDO::getVersion, 1)).isEmpty()) { |
| | | throw invalidParamException("ç¥è¯ç¼ç å·²åå¨"); |
| | | } |
| | | var record = new KnowledgeDO(); |
| | | record.setCode(code); record.setVersion(1); record.setStatus(0); |
| | | copyFields(record, input); |
| | | try { |
| | | knowledgeMapper.insert(record); |
| | | } catch (DuplicateKeyException e) { |
| | | throw invalidParamException("ç¥è¯ç¼ç å·²åå¨"); |
| | | } |
| | | saveStandards(record.getId(), input.getStandards()); |
| | | saveResources(record.getId(), input.getResources()); |
| | | return record.getId(); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(SaveRequest input) { |
| | | KnowledgeDO existing = input == null || input.getId() == null ? null : knowledgeMapper.selectById(input.getId()); |
| | | if (existing == null || !Integer.valueOf(0).equals(existing.getStatus())) { |
| | | throw invalidParamException("ç¥è¯ä¸å卿䏿¯å¯ç¼è¾çè稿"); |
| | | } |
| | | validate(input); |
| | | if (!existing.getCode().equals(input.getCode().trim())) throw invalidParamException("ç¥è¯ç¼ç ä¸å¯ä¿®æ¹"); |
| | | var update = new LambdaUpdateWrapper<KnowledgeDO>() |
| | | .eq(KnowledgeDO::getId, existing.getId()).eq(KnowledgeDO::getStatus, 0) |
| | | .set(KnowledgeDO::getName, input.getName().trim()) |
| | | .set(KnowledgeDO::getProductItemId, input.getProductItemId()).set(KnowledgeDO::getBomId, input.getBomId()) |
| | | .set(KnowledgeDO::getVoltageMin, input.getVoltageMin()).set(KnowledgeDO::getVoltageMax, input.getVoltageMax()) |
| | | .set(KnowledgeDO::getMarket, input.getMarket()).set(KnowledgeDO::getRouteId, input.getRouteId()) |
| | | .set(KnowledgeDO::getTemplateId, input.getTemplateId()).set(KnowledgeDO::getDescription, input.getDescription()); |
| | | if (knowledgeMapper.update(null, update) != 1) throw invalidParamException("ç¥è¯ç¶æå·²ååï¼è¯·å·æ°åéè¯"); |
| | | if (input.getStandards() != null) { |
| | | standardMapper.delete(new LambdaQueryWrapperX<KnowledgeStandardDO>().eq(KnowledgeStandardDO::getKnowledgeId, existing.getId())); |
| | | saveStandards(existing.getId(), input.getStandards()); |
| | | } |
| | | if (input.getResources() != null) { |
| | | resourceMapper.delete(new LambdaQueryWrapperX<KnowledgeResourceDO>().eq(KnowledgeResourceDO::getKnowledgeId, existing.getId())); |
| | | saveResources(existing.getId(), input.getResources()); |
| | | } |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void publish(Long id) { |
| | | KnowledgeDO record = id == null ? null : knowledgeMapper.selectById(id); |
| | | if (record == null || !Integer.valueOf(0).equals(record.getStatus())) |
| | | throw invalidParamException("ç¥è¯ä¸å卿䏿¯è稿"); |
| | | if (record.getRouteId() == null) throw invalidParamException("åå¸åè¯·éæ©å·¥èºè·¯çº¿"); |
| | | routeService.validateRouteExists(record.getRouteId()); |
| | | if (record.getTemplateId() != null) templateService.validateProcessParamTemplateExists(record.getTemplateId()); |
| | | var update = new LambdaUpdateWrapper<KnowledgeDO>().eq(KnowledgeDO::getId, id) |
| | | .eq(KnowledgeDO::getStatus, 0).eq(KnowledgeDO::getRouteId, record.getRouteId()); |
| | | if (record.getTemplateId() == null) update.isNull(KnowledgeDO::getTemplateId); |
| | | else update.eq(KnowledgeDO::getTemplateId, record.getTemplateId()); |
| | | if (knowledgeMapper.update(null, update.set(KnowledgeDO::getStatus, 1)) != 1) |
| | | throw invalidParamException("ç¥è¯ç¶æå·²ååï¼è¯·å·æ°åéè¯"); |
| | | // åä¸ç¼ç åªä¿çå½ååå¸çæ¬ï¼æ§çä»å¯ä½ä¸ºå¼ç¨å¿«ç
§è¿½æº¯ã |
| | | knowledgeMapper.update(null, new LambdaUpdateWrapper<KnowledgeDO>() |
| | | .eq(KnowledgeDO::getCode, record.getCode()).eq(KnowledgeDO::getStatus, 1) |
| | | .lt(KnowledgeDO::getVersion, record.getVersion()).set(KnowledgeDO::getStatus, 2)); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long revise(Long id) { |
| | | KnowledgeDO source = id == null ? null : knowledgeMapper.selectById(id); |
| | | if (source == null || !Integer.valueOf(1).equals(source.getStatus())) |
| | | throw invalidParamException("åªè½ä»å·²åå¸çç¥è¯å建æ°çæ¬"); |
| | | List<KnowledgeDO> versions = knowledgeMapper.selectList(new LambdaQueryWrapperX<KnowledgeDO>() |
| | | .eq(KnowledgeDO::getCode, source.getCode()).orderByDesc(KnowledgeDO::getVersion)); |
| | | if (versions.stream().anyMatch(version -> Integer.valueOf(0).equals(version.getStatus()))) |
| | | throw invalidParamException("该ç¥è¯å·²ææªåå¸çèç¨¿çæ¬"); |
| | | int nextVersion = versions.stream().map(KnowledgeDO::getVersion).max(Integer::compareTo).orElse(0) + 1; |
| | | var draft = new KnowledgeDO(); |
| | | draft.setCode(source.getCode()); draft.setVersion(nextVersion); draft.setStatus(0); draft.setName(source.getName()); |
| | | draft.setProductItemId(source.getProductItemId()); draft.setBomId(source.getBomId()); |
| | | draft.setVoltageMin(source.getVoltageMin()); draft.setVoltageMax(source.getVoltageMax()); draft.setMarket(source.getMarket()); |
| | | draft.setRouteId(source.getRouteId()); draft.setTemplateId(source.getTemplateId()); draft.setDescription(source.getDescription()); |
| | | try { knowledgeMapper.insert(draft); } |
| | | catch (DuplicateKeyException e) { throw invalidParamException("çæ¬å·²ååï¼è¯·å·æ°åéè¯"); } |
| | | List<String> standards = standardMapper.selectList(new LambdaQueryWrapperX<KnowledgeStandardDO>() |
| | | .eq(KnowledgeStandardDO::getKnowledgeId, source.getId())).stream().map(KnowledgeStandardDO::getStandardCode).toList(); |
| | | saveStandards(draft.getId(), standards); |
| | | List<KnowledgeResourceDO> resources = resourceMapper.selectList(new LambdaQueryWrapperX<KnowledgeResourceDO>() |
| | | .eq(KnowledgeResourceDO::getKnowledgeId, source.getId())); |
| | | for (KnowledgeResourceDO resource : resources) { |
| | | var copy = new KnowledgeResourceDO(); copy.setKnowledgeId(draft.getId()); |
| | | copy.setProcessId(resource.getProcessId()); copy.setResourceType(resource.getResourceType()); |
| | | copy.setResourceId(resource.getResourceId()); copy.setCapabilityDescription(resource.getCapabilityDescription()); |
| | | resourceMapper.insert(copy); |
| | | } |
| | | return draft.getId(); |
| | | } |
| | | |
| | | /** è®°å½åèå
³ç³»ï¼ä¸ä¿®æ¹ç®æ 路线æå¤å¶è·¯çº¿å
容ã */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void referenceRoute(Long knowledgeId, Long targetRouteId) { |
| | | if (knowledgeId == null || targetRouteId == null) throw invalidParamException("è¯·éæ©ç¥è¯åç®æ å·¥èºè·¯çº¿"); |
| | | KnowledgeDO source = knowledgeMapper.selectOne(new LambdaQueryWrapperX<KnowledgeDO>() |
| | | .eq(KnowledgeDO::getId, knowledgeId).last("FOR UPDATE")); |
| | | if (source == null || !Integer.valueOf(1).equals(source.getStatus())) |
| | | throw invalidParamException("åªè½åèå½åå·²åå¸çç¥è¯"); |
| | | routeService.validateRouteExists(targetRouteId); |
| | | var reference = new KnowledgeReferenceDO(); reference.setKnowledgeId(source.getId()); |
| | | reference.setKnowledgeVersion(source.getVersion()); reference.setTargetType("ROUTE"); reference.setTargetId(targetRouteId); |
| | | try { referenceMapper.insert(reference); } |
| | | catch (DuplicateKeyException e) { throw invalidParamException("该路线已åèæ¤ç¥è¯çæ¬"); } |
| | | } |
| | | |
| | | private void validate(SaveRequest input) { |
| | | if (input == null || input.getCode() == null || input.getCode().isBlank() || input.getCode().length() > 64 |
| | | || input.getName() == null || input.getName().isBlank() || input.getName().length() > 200) |
| | | throw invalidParamException("è¯·å¡«åææçç¥è¯ç¼ç ååç§°"); |
| | | if (input.getMarket() == null || !Set.of("ALL", "DOMESTIC", "EXPORT").contains(input.getMarket())) throw invalidParamException("å
å¤éç±»å䏿£ç¡®"); |
| | | if ((input.getVoltageMin() != null && input.getVoltageMin().signum() < 0) |
| | | || (input.getVoltageMax() != null && input.getVoltageMax().signum() < 0) |
| | | || (input.getVoltageMin() != null && input.getVoltageMax() != null && input.getVoltageMin().compareTo(input.getVoltageMax()) > 0)) |
| | | throw invalidParamException("çµåèå´ä¸æ£ç¡®"); |
| | | if (input.getProductItemId() != null) itemService.validateItemExists(input.getProductItemId()); |
| | | if (input.getBomId() != null) { |
| | | if (input.getProductItemId() == null) throw invalidParamException("éæ© BOM æç»å请å
éæ©éç¨ç©æ"); |
| | | boolean found = bomService.getProductBomListByItemId(input.getProductItemId()).stream() |
| | | .anyMatch(bom -> Objects.equals(bom.getItemId(), input.getProductItemId()) |
| | | && Objects.equals(bom.getBomItemId(), input.getBomId()) && Objects.equals(bom.getStatus(), 0)); |
| | | if (!found) throw invalidParamException("æé BOM åç©æä¸åå¨ãæªå¯ç¨æä¸å±äºè¯¥ç©æ"); |
| | | } |
| | | if (input.getRouteId() != null) routeService.validateRouteExists(input.getRouteId()); |
| | | if (input.getTemplateId() != null) templateService.validateProcessParamTemplateExists(input.getTemplateId()); |
| | | if (input.getStandards() != null && input.getStandards().stream().anyMatch(s -> s == null || s.isBlank() || s.trim().length() > 128)) |
| | | throw invalidParamException("æ åç¼ç ä¸è½ä¸ºç©ºæè¶
è¿ 128 å符"); |
| | | if (input.getResources() != null) for (ResourceInput resource : input.getResources()) { |
| | | if (resource == null || resource.getResourceType() == null || !Set.of("MACHINE", "WORKSTATION", "OTHER").contains(resource.getResourceType())) |
| | | throw invalidParamException("èµæºç±»å䏿£ç¡®"); |
| | | if (resource.getCapabilityDescription() == null || resource.getCapabilityDescription().isBlank() |
| | | || resource.getCapabilityDescription().length() > 500) throw invalidParamException("请填åä¸è¶
è¿ 500 åçèµæºè½å说æ"); |
| | | if (resource.getProcessId() != null) processService.validateProcessExists(resource.getProcessId()); |
| | | if ("MACHINE".equals(resource.getResourceType())) { |
| | | if (resource.getResourceId() == null) throw invalidParamException("è¯·éæ©è®¾å¤"); |
| | | machineryService.validateMachineryExists(resource.getResourceId()); |
| | | } else if ("WORKSTATION".equals(resource.getResourceType())) { |
| | | if (resource.getResourceId() == null) throw invalidParamException("è¯·éæ©å·¥ä½ç«"); |
| | | workstationService.validateWorkstationExists(resource.getResourceId()); |
| | | } else if (resource.getResourceId() != null) throw invalidParamException("å
¶ä»èµæºä¸è½æå®è®¾å¤æå·¥ä½ç«ç¼å·"); |
| | | } |
| | | } |
| | | |
| | | private void copyFields(KnowledgeDO record, SaveRequest input) { |
| | | record.setName(input.getName().trim()); record.setProductItemId(input.getProductItemId()); record.setBomId(input.getBomId()); |
| | | record.setVoltageMin(input.getVoltageMin()); record.setVoltageMax(input.getVoltageMax()); record.setMarket(input.getMarket()); |
| | | record.setRouteId(input.getRouteId()); record.setTemplateId(input.getTemplateId()); record.setDescription(input.getDescription()); |
| | | } |
| | | |
| | | private void saveStandards(Long id, List<String> standards) { |
| | | if (standards == null) return; |
| | | standards.stream().map(String::trim).distinct().forEach(code -> { |
| | | var standard = new KnowledgeStandardDO(); standard.setKnowledgeId(id); standard.setStandardCode(code); |
| | | standardMapper.insert(standard); |
| | | }); |
| | | } |
| | | |
| | | private void saveResources(Long id, List<ResourceInput> resources) { |
| | | if (resources == null) return; |
| | | for (ResourceInput resource : resources) { |
| | | var item = new KnowledgeResourceDO(); item.setKnowledgeId(id); item.setProcessId(resource.getProcessId()); |
| | | item.setResourceType(resource.getResourceType()); item.setResourceId(resource.getResourceId()); |
| | | item.setCapabilityDescription(resource.getCapabilityDescription().trim()); resourceMapper.insert(item); |
| | | } |
| | | } |
| | | } |