Merge branch 'main' of http://27.223.88.102:33000/chenx/qhmes
This commit is contained in:
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -1,6 +1,24 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "jeecgboot-vue3: 调试前端 (Chrome)",
|
||||
"url": "http://localhost:3100",
|
||||
"webRoot": "${workspaceFolder}/jeecgboot-vue3",
|
||||
"preLaunchTask": "jeecgboot-vue3: dev"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "jeecgboot-vue3: dev",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["run", "dev"],
|
||||
"cwd": "${workspaceFolder}/jeecgboot-vue3",
|
||||
"console": "integratedTerminal",
|
||||
"skipFiles": ["<node_internals>/**", "**/node_modules/**"]
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "JeecgSystemApplication (单体)",
|
||||
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -14,6 +14,10 @@
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"java.maven.downloadSources": true,
|
||||
"java.eclipse.downloadSources": true,
|
||||
"java.project.importOnFirstTimeStartup": "automatic",
|
||||
"java.configuration.checkProjectSettingsExclusions": false,
|
||||
"java.import.exclusions": [
|
||||
"**/jeecg-server-cloud/**",
|
||||
"**/jeecg-boot-platform/**",
|
||||
|
||||
18
.vscode/tasks.json
vendored
18
.vscode/tasks.json
vendored
@@ -54,6 +54,24 @@
|
||||
"dependsOn": "YY.Admin: build",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "jeecgboot-vue3: dev",
|
||||
"type": "shell",
|
||||
"command": "pnpm run dev",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/jeecgboot-vue3"
|
||||
},
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "vite",
|
||||
"pattern": { "regexp": "^$" },
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": ".",
|
||||
"endsPattern": "(Local:|ready in|http://localhost)"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "YY.Admin: run (script)",
|
||||
"type": "process",
|
||||
|
||||
4
jeecg-boot/.vscode/settings.json
vendored
4
jeecg-boot/.vscode/settings.json
vendored
@@ -14,6 +14,10 @@
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"java.maven.downloadSources": true,
|
||||
"java.eclipse.downloadSources": true,
|
||||
"java.project.importOnFirstTimeStartup": "automatic",
|
||||
"java.configuration.checkProjectSettingsExclusions": false,
|
||||
"java.import.exclusions": [
|
||||
"**/jeecg-server-cloud/**",
|
||||
"**/jeecg-boot-platform/**",
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
-- 胶料快检记录(主子表):字典 + 建表 + 菜单(质量管理下)+ 按钮 + 胶料信息「检验」按钮 + 租户 admin 授权
|
||||
-- 权限前缀:mes:mes_xsl_rubber_quick_test_record:*
|
||||
-- 菜单 ID 段 1860000000000000192
|
||||
-- SET @mes_tenant_id:多租户 admin 授权目标租户
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检记录检验结果', 'xslmes_rubber_quick_test_record_result', '1合格0不合格', 0, 'admin', NOW(), 0, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_record_result' AND `del_flag` = 0);
|
||||
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, '合格', '1', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_record_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, '不合格', '0', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_record_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||
|
||||
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检班次', 'xslmes_rubber_quick_test_work_shift', '班次', 0, 'admin', NOW(), 0, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_work_shift' AND `del_flag` = 0);
|
||||
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||
FROM `sys_dict` d
|
||||
CROSS JOIN (
|
||||
SELECT '早班' AS txt, '1' AS val, 1 AS ord UNION ALL
|
||||
SELECT '中班', '2', 2 UNION ALL
|
||||
SELECT '晚班', '3', 3
|
||||
) v
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_work_shift'
|
||||
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||
|
||||
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检班组', 'xslmes_rubber_quick_test_work_team', '班组', 0, 'admin', NOW(), 0, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_work_team' AND `del_flag` = 0);
|
||||
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||
FROM `sys_dict` d
|
||||
CROSS JOIN (
|
||||
SELECT '甲班' AS txt, '1' AS val, 1 AS ord UNION ALL
|
||||
SELECT '乙班', '2', 2 UNION ALL
|
||||
SELECT '丙班', '3', 3
|
||||
) v
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_work_team'
|
||||
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_record` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`record_no` varchar(32) DEFAULT NULL COMMENT '单号(JL+日期+4位流水,如JL202605280001)',
|
||||
`rubber_material_id` varchar(32) DEFAULT NULL COMMENT '胶料 mes_material.id',
|
||||
`rubber_material_name` varchar(128) DEFAULT NULL COMMENT '胶料名称冗余',
|
||||
`std_id` varchar(32) DEFAULT NULL COMMENT '引用的实验标准 mes_xsl_rubber_quick_test_std.id',
|
||||
`prod_equipment_ledger_id` varchar(32) DEFAULT NULL COMMENT '生产机台 mes_xsl_equipment_ledger.id',
|
||||
`prod_equipment_name` varchar(128) DEFAULT NULL COMMENT '生产机台名称冗余',
|
||||
`production_date` date DEFAULT NULL COMMENT '生产日期',
|
||||
`train_no` varchar(64) DEFAULT NULL COMMENT '车次编号',
|
||||
`work_shift` varchar(8) DEFAULT NULL COMMENT '班次(字典xslmes_rubber_quick_test_work_shift)',
|
||||
`work_team` varchar(8) DEFAULT NULL COMMENT '班组(字典xslmes_rubber_quick_test_work_team)',
|
||||
`inspect_times` int DEFAULT NULL COMMENT '检验次数',
|
||||
`inspect_time` datetime DEFAULT NULL COMMENT '检验时间',
|
||||
`inspector_user_id` varchar(32) DEFAULT NULL COMMENT '检验人用户ID',
|
||||
`inspector_username` varchar(64) DEFAULT NULL COMMENT '检验人账号冗余',
|
||||
`inspector_realname` varchar(64) DEFAULT NULL COMMENT '检验人姓名冗余',
|
||||
`quick_test_type_id` varchar(32) DEFAULT NULL COMMENT '检验类型 mes_xsl_rubber_quick_test_type.id',
|
||||
`quick_test_type_name` varchar(128) DEFAULT NULL COMMENT '检验类型名称冗余',
|
||||
`inspect_result` varchar(2) DEFAULT NULL COMMENT '检验结果(字典xslmes_rubber_quick_test_record_result:1合格0不合格)',
|
||||
`production_plan_no` varchar(100) DEFAULT NULL COMMENT '生产计划号',
|
||||
`inspect_equipment_ledger_id` varchar(32) DEFAULT NULL COMMENT '检验机台 mes_xsl_equipment_ledger.id',
|
||||
`inspect_equipment_name` varchar(128) DEFAULT NULL COMMENT '检验机台名称冗余',
|
||||
`rubber_card_no` varchar(100) DEFAULT NULL COMMENT '胶料卡片号',
|
||||
`rubber_batch_no` varchar(100) DEFAULT NULL COMMENT '胶料批次',
|
||||
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_mrqtr_record_no` (`record_no`),
|
||||
KEY `idx_mrqtr_material` (`rubber_material_id`),
|
||||
KEY `idx_mrqtr_std` (`std_id`),
|
||||
KEY `idx_mrqtr_tenant` (`tenant_id`),
|
||||
KEY `idx_mrqtr_inspect_time` (`inspect_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检记录';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_record_line` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`record_id` varchar(32) NOT NULL COMMENT '主表 mes_xsl_rubber_quick_test_record.id',
|
||||
`data_point_id` varchar(32) DEFAULT NULL COMMENT '数据点 mes_xsl_rubber_quick_test_data_point.id',
|
||||
`inspect_item` varchar(128) DEFAULT NULL COMMENT '检验项目(数据点名称,只读带出)',
|
||||
`lower_limit` decimal(18,6) DEFAULT NULL COMMENT '检验下限(只读带出)',
|
||||
`inspect_value` decimal(18,6) DEFAULT NULL COMMENT '检验值',
|
||||
`upper_limit` decimal(18,6) DEFAULT NULL COMMENT '检验上限(只读带出)',
|
||||
`sort_no` int DEFAULT NULL COMMENT '排序号',
|
||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_mrqtrl_record` (`record_id`),
|
||||
UNIQUE KEY `uk_mrqtrl_record_point` (`record_id`, `data_point_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检记录明细';
|
||||
|
||||
SET @mes_tenant_id = 1002;
|
||||
|
||||
SET @mes_quality_pid = IFNULL(
|
||||
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||
'1860000000000000162'
|
||||
);
|
||||
|
||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||
VALUES ('1860000000000000192', @mes_quality_pid, '胶料快检记录', '/xslmes/mesXslRubberQuickTestRecord', 'xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecordList', 'MesXslRubberQuickTestRecordList', 1, NULL, '1', 5, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||
`component_name` = VALUES(`component_name`), `sort_no` = VALUES(`sort_no`), `is_leaf` = VALUES(`is_leaf`), `keep_alive` = VALUES(`keep_alive`);
|
||||
|
||||
UPDATE `sys_permission` SET `icon` = 'ant-design:file-search-outlined' WHERE `id` = '1860000000000000192' AND `del_flag` = 0;
|
||||
|
||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||
('1860000000000000193', '1860000000000000192', '新增', 2, 'mes:mes_xsl_rubber_quick_test_record:add', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000194', '1860000000000000192', '编辑', 2, 'mes:mes_xsl_rubber_quick_test_record:edit', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000195', '1860000000000000192', '删除', 2, 'mes:mes_xsl_rubber_quick_test_record:delete', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000196', '1860000000000000192', '批量删除', 2, 'mes:mes_xsl_rubber_quick_test_record:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000197', '1860000000000000192', '导出', 2, 'mes:mes_xsl_rubber_quick_test_record:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000198', '1860000000000000192', '导入', 2, 'mes:mes_xsl_rubber_quick_test_record:importExcel', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000199', '1860000000000000192', '从胶料生成', 2, 'mes:mes_xsl_rubber_quick_test_record:batchFromMaterial', '1', '1', 0, 'admin', NOW())
|
||||
ON DUPLICATE KEY UPDATE `perms` = VALUES(`perms`), `name` = VALUES(`name`);
|
||||
|
||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||
('1860000000000000200', '1860000000000000011', '胶料快检', 2, 'mes:mes_material:rubberQuickTestInspect', '1', '1', 0, 'admin', NOW())
|
||||
ON DUPLICATE KEY UPDATE `perms` = VALUES(`perms`), `name` = VALUES(`name`);
|
||||
|
||||
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NOW(), '127.0.0.1'
|
||||
FROM sys_role r
|
||||
CROSS JOIN sys_permission p
|
||||
WHERE r.tenant_id = @mes_tenant_id
|
||||
AND r.role_code = 'admin'
|
||||
AND p.id IN (
|
||||
'1860000000000000192',
|
||||
'1860000000000000193', '1860000000000000194', '1860000000000000195', '1860000000000000196',
|
||||
'1860000000000000197', '1860000000000000198', '1860000000000000199',
|
||||
'1860000000000000200'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_permission rp
|
||||
WHERE rp.role_id = r.id AND rp.permission_id = p.id
|
||||
);
|
||||
@@ -22,6 +22,7 @@ import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.net.*;
|
||||
import java.sql.Date;
|
||||
import java.util.*;
|
||||
@@ -1046,7 +1047,7 @@ public class oConvertUtils {
|
||||
BigDecimal bigDecimal = new BigDecimal(uploadCount);
|
||||
//换算成MB
|
||||
BigDecimal divide = bigDecimal.divide(new BigDecimal(1048576));
|
||||
count = divide.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
count = divide.setScale(2, RoundingMode.HALF_UP).doubleValue();
|
||||
return count;
|
||||
}
|
||||
return count;
|
||||
|
||||
@@ -414,3 +414,55 @@ jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/ser
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixerPsCompileServiceImpl.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecList.vue
|
||||
|
||||
-- author:cursor---date:20260526--for: 【配方日志查询】配合示方/混炼示方修改日志记录与查询接口 -----------
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_107__mes_xsl_formula_spec_edit_log.sql
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslFormulaSpecEditLog.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslFormulaSpecEditChangeItemVO.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslFormulaSpecEditLogDetailVO.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslFormulaSpecEditLogMapper.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/MesXslFormulaSpecEditLogDiffUtil.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslFormulaSpecEditLogService.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecEditLogServiceImpl.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslFormulaSpecEditLogController.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
|
||||
|
||||
-- author:cursor---date:20260526--for: 【配方日志查询】前端列表页与修改对比弹窗 -----------
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpecEditLog/MesXslFormulaSpecEditLog.api.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpecEditLog/MesXslFormulaSpecEditLog.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpecEditLog/MesXslFormulaSpecEditLogList.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpecEditLog/components/MesXslFormulaSpecEditLogCompareModal.vue
|
||||
|
||||
-- author:cursor---date:20260526--for: 【配方日志查询】明细对比展示逐行逐字段变更内容 -----------
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/MesXslFormulaSpecEditLogDiffUtil.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpecEditLog/MesXslFormulaSpecEditLog.data.ts
|
||||
|
||||
-- author:jiangxh---date:20260525--for: 【MES】胶料快检记录主子表、质量管理菜单、胶料信息批量检验生成 ---
|
||||
jeecg-boot/db/mes-xsl-rubber-quick-test-record-menu-permission.sql
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_108__mes_xsl_rubber_quick_test_record.sql
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslRubberQuickTestRecord.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslRubberQuickTestRecordLine.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRubberQuickTestRecordPage.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRubberQuickTestRecordBatchFromMaterialVO.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslRubberQuickTestRecordMapper.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslRubberQuickTestRecordLineMapper.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRubberQuickTestRecordService.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRubberQuickTestRecordServiceImpl.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRubberQuickTestRecordController.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecordList.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.api.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/components/MesXslRubberQuickTestRecordModal.vue
|
||||
jeecgboot-vue3/src/views/mes/material/MesMaterialList.vue
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_109__mes_xsl_rubber_quick_test_record_no.sql
|
||||
|
||||
-- author:jiangxh---date:20260528--for: 【MES】胶料快检记录单号、无弹窗直接生成、列表去掉新增、明细不可增删 ---
|
||||
jeecg-boot/db/mes-xsl-rubber-quick-test-record-menu-permission.sql
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslRubberQuickTestRecord.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRubberQuickTestRecordService.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRubberQuickTestRecordServiceImpl.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecordList.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/components/MesXslRubberQuickTestRecordModal.vue
|
||||
jeecgboot-vue3/src/views/mes/material/MesMaterialList.vue
|
||||
|
||||
@@ -0,0 +1,546 @@
|
||||
package org.jeecg.modules.xslmes.common;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpec;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecLine;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslFormulaSpecEditChangeItemVO;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage;
|
||||
|
||||
/**
|
||||
* 配方示方修改日志:快照序列化与差异对比
|
||||
*/
|
||||
public final class MesXslFormulaSpecEditLogDiffUtil {
|
||||
|
||||
public static final String SECTION_MAIN = "main";
|
||||
public static final String SECTION_LINE = "line";
|
||||
public static final String SECTION_MATERIAL = "material";
|
||||
public static final String SECTION_STEP = "step";
|
||||
public static final String SECTION_DOWN_STEP = "downStep";
|
||||
public static final String SECTION_TCU = "tcu";
|
||||
|
||||
private static final Set<String> IGNORE_FIELDS = Set.of(
|
||||
"id",
|
||||
"createBy",
|
||||
"createTime",
|
||||
"updateBy",
|
||||
"updateTime",
|
||||
"delFlag",
|
||||
"tenantId",
|
||||
"sysOrgCode",
|
||||
"createBy_dictText",
|
||||
"lineList",
|
||||
"materialList",
|
||||
"stepList",
|
||||
"downStepList",
|
||||
"tcuList");
|
||||
|
||||
private static final Map<String, String> FORMULA_MAIN_LABELS = buildFormulaMainLabels();
|
||||
private static final Map<String, String> MIXING_MAIN_LABELS = buildMixingMainLabels();
|
||||
private static final Map<String, String> SECTION_LABELS = Map.of(
|
||||
SECTION_MAIN, "主表",
|
||||
SECTION_LINE, "配合明细",
|
||||
SECTION_MATERIAL, "橡胶及配合剂",
|
||||
SECTION_STEP, "混合步骤",
|
||||
SECTION_DOWN_STEP, "下密炼机条件",
|
||||
SECTION_TCU, "TCU温度条件");
|
||||
|
||||
private static final Map<String, Map<String, String>> SECTION_FIELD_LABELS = buildSectionFieldLabels();
|
||||
private static final Map<String, String[]> SECTION_SUMMARY_FIELDS = buildSectionSummaryFields();
|
||||
|
||||
private MesXslFormulaSpecEditLogDiffUtil() {}
|
||||
|
||||
public static String buildFormulaSnapshotJson(MesXslFormulaSpec main, List<MesXslFormulaSpecLine> lineList) {
|
||||
Map<String, Object> snapshot = new LinkedHashMap<>(2);
|
||||
snapshot.put(SECTION_MAIN, toJsonObject(main));
|
||||
snapshot.put(SECTION_LINE, lineList == null ? List.of() : lineList);
|
||||
return toJson(snapshot);
|
||||
}
|
||||
|
||||
public static String buildMixingSnapshotJson(MesXslMixingSpecPage page) {
|
||||
if (page == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> snapshot = new LinkedHashMap<>(5);
|
||||
snapshot.put(SECTION_MAIN, toJsonObject(page));
|
||||
snapshot.put(SECTION_MATERIAL, page.getMaterialList() == null ? List.of() : page.getMaterialList());
|
||||
snapshot.put(SECTION_STEP, page.getStepList() == null ? List.of() : page.getStepList());
|
||||
snapshot.put(SECTION_DOWN_STEP, page.getDownStepList() == null ? List.of() : page.getDownStepList());
|
||||
snapshot.put(SECTION_TCU, page.getTcuList() == null ? List.of() : page.getTcuList());
|
||||
return toJson(snapshot);
|
||||
}
|
||||
|
||||
public static List<MesXslFormulaSpecEditChangeItemVO> compare(
|
||||
String specType, String beforeSnapshot, String afterSnapshot) {
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items = new ArrayList<>();
|
||||
JSONObject before = parseObject(beforeSnapshot);
|
||||
JSONObject after = parseObject(afterSnapshot);
|
||||
Map<String, String> mainLabels = SPEC_TYPE_MIXING.equals(specType) ? MIXING_MAIN_LABELS : FORMULA_MAIN_LABELS;
|
||||
compareMainSection(items, before == null ? null : before.getJSONObject(SECTION_MAIN),
|
||||
after == null ? null : after.getJSONObject(SECTION_MAIN), mainLabels);
|
||||
if (SPEC_TYPE_FORMULA.equals(specType)) {
|
||||
compareChildListSection(items, SECTION_LINE, before, after);
|
||||
} else {
|
||||
compareChildListSection(items, SECTION_MATERIAL, before, after);
|
||||
compareChildListSection(items, SECTION_STEP, before, after);
|
||||
compareChildListSection(items, SECTION_DOWN_STEP, before, after);
|
||||
compareChildListSection(items, SECTION_TCU, before, after);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public static String buildSummary(String actionType, List<MesXslFormulaSpecEditChangeItemVO> items) {
|
||||
if ("create".equals(actionType)) {
|
||||
return "新增示方";
|
||||
}
|
||||
if ("delete".equals(actionType)) {
|
||||
return "删除示方";
|
||||
}
|
||||
if (items == null || items.isEmpty()) {
|
||||
return "内容无变化";
|
||||
}
|
||||
Set<String> labels = new LinkedHashSet<>();
|
||||
for (MesXslFormulaSpecEditChangeItemVO item : items) {
|
||||
if (SECTION_MAIN.equals(item.getSection()) && StringUtils.isNotBlank(item.getFieldLabel())) {
|
||||
labels.add(item.getFieldLabel());
|
||||
} else if (StringUtils.isNotBlank(item.getSectionLabel())) {
|
||||
labels.add(item.getSectionLabel());
|
||||
}
|
||||
}
|
||||
if (labels.isEmpty()) {
|
||||
return "内容有变更";
|
||||
}
|
||||
String joined = String.join("、", labels);
|
||||
if (joined.length() > 180) {
|
||||
joined = joined.substring(0, 177) + "...";
|
||||
}
|
||||
return "修改:" + joined;
|
||||
}
|
||||
|
||||
private static void compareMainSection(
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items,
|
||||
JSONObject before,
|
||||
JSONObject after,
|
||||
Map<String, String> labels) {
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
if (before != null) {
|
||||
keys.addAll(before.keySet());
|
||||
}
|
||||
if (after != null) {
|
||||
keys.addAll(after.keySet());
|
||||
}
|
||||
for (String key : keys) {
|
||||
if (IGNORE_FIELDS.contains(key)) {
|
||||
continue;
|
||||
}
|
||||
String beforeVal = formatValue(before == null ? null : before.get(key));
|
||||
String afterVal = formatValue(after == null ? null : after.get(key));
|
||||
if (Objects.equals(beforeVal, afterVal)) {
|
||||
continue;
|
||||
}
|
||||
MesXslFormulaSpecEditChangeItemVO item = new MesXslFormulaSpecEditChangeItemVO();
|
||||
item.setSection(SECTION_MAIN);
|
||||
item.setSectionLabel(SECTION_LABELS.get(SECTION_MAIN));
|
||||
item.setFieldKey(key);
|
||||
item.setFieldLabel(labels.getOrDefault(key, key));
|
||||
item.setBeforeValue(beforeVal);
|
||||
item.setAfterValue(afterVal);
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private static void compareChildListSection(
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items, String section, JSONObject before, JSONObject after) {
|
||||
List<JSONObject> beforeList = toJsonObjectList(before == null ? null : before.get(section));
|
||||
List<JSONObject> afterList = toJsonObjectList(after == null ? null : after.get(section));
|
||||
if (Objects.equals(normalizeListJson(beforeList), normalizeListJson(afterList))) {
|
||||
return;
|
||||
}
|
||||
Map<String, String> fieldLabels = SECTION_FIELD_LABELS.getOrDefault(section, Map.of());
|
||||
String[] summaryFields = SECTION_SUMMARY_FIELDS.getOrDefault(section, new String[0]);
|
||||
Map<String, JSONObject> beforeMap = indexRows(beforeList);
|
||||
Map<String, JSONObject> afterMap = indexRows(afterList);
|
||||
Set<String> rowKeys = new LinkedHashSet<>();
|
||||
rowKeys.addAll(beforeMap.keySet());
|
||||
rowKeys.addAll(afterMap.keySet());
|
||||
for (String rowKey : rowKeys) {
|
||||
JSONObject beforeRow = beforeMap.get(rowKey);
|
||||
JSONObject afterRow = afterMap.get(rowKey);
|
||||
String rowLabel = formatRowLabel(rowKey);
|
||||
if (beforeRow == null && afterRow != null) {
|
||||
addChangeItem(items, section, rowKey + "_add", rowLabel + "(新增)", "",
|
||||
formatRowSummary(afterRow, fieldLabels, summaryFields));
|
||||
continue;
|
||||
}
|
||||
if (beforeRow != null && afterRow == null) {
|
||||
addChangeItem(items, section, rowKey + "_del", rowLabel + "(删除)",
|
||||
formatRowSummary(beforeRow, fieldLabels, summaryFields), "");
|
||||
continue;
|
||||
}
|
||||
compareRowFields(items, section, rowKey, rowLabel, beforeRow, afterRow, fieldLabels);
|
||||
}
|
||||
}
|
||||
|
||||
private static void compareRowFields(
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items,
|
||||
String section,
|
||||
String rowKey,
|
||||
String rowLabel,
|
||||
JSONObject beforeRow,
|
||||
JSONObject afterRow,
|
||||
Map<String, String> fieldLabels) {
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
keys.addAll(beforeRow.keySet());
|
||||
keys.addAll(afterRow.keySet());
|
||||
for (String key : keys) {
|
||||
if (IGNORE_FIELDS.contains(key) || isRowMetaField(key)) {
|
||||
continue;
|
||||
}
|
||||
String beforeVal = formatValue(beforeRow.get(key));
|
||||
String afterVal = formatValue(afterRow.get(key));
|
||||
if (Objects.equals(beforeVal, afterVal)) {
|
||||
continue;
|
||||
}
|
||||
addChangeItem(
|
||||
items,
|
||||
section,
|
||||
rowKey + "_" + key,
|
||||
rowLabel + "·" + fieldLabels.getOrDefault(key, key),
|
||||
beforeVal,
|
||||
afterVal);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addChangeItem(
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items,
|
||||
String section,
|
||||
String fieldKey,
|
||||
String fieldLabel,
|
||||
String beforeValue,
|
||||
String afterValue) {
|
||||
MesXslFormulaSpecEditChangeItemVO item = new MesXslFormulaSpecEditChangeItemVO();
|
||||
item.setSection(section);
|
||||
item.setSectionLabel(SECTION_LABELS.getOrDefault(section, section));
|
||||
item.setFieldKey(fieldKey);
|
||||
item.setFieldLabel(fieldLabel);
|
||||
item.setBeforeValue(beforeValue);
|
||||
item.setAfterValue(afterValue);
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
/** 按 sortNo 优先、否则按列表序号建立行索引 */
|
||||
private static Map<String, JSONObject> indexRows(List<JSONObject> rows) {
|
||||
Map<String, JSONObject> map = new LinkedHashMap<>();
|
||||
if (rows == null) {
|
||||
return map;
|
||||
}
|
||||
int index = 0;
|
||||
for (JSONObject row : rows) {
|
||||
index++;
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
Integer sortNo = row.getInteger("sortNo");
|
||||
String key = sortNo != null ? "sort_" + sortNo : "idx_" + index;
|
||||
map.put(key, row);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static String formatRowLabel(String rowKey) {
|
||||
if (rowKey.startsWith("sort_")) {
|
||||
return "第" + rowKey.substring(5) + "行";
|
||||
}
|
||||
if (rowKey.startsWith("idx_")) {
|
||||
return "第" + rowKey.substring(4) + "行";
|
||||
}
|
||||
return rowKey;
|
||||
}
|
||||
|
||||
private static String formatRowSummary(JSONObject row, Map<String, String> fieldLabels, String[] summaryFields) {
|
||||
if (row == null || row.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
List<String> parts = new ArrayList<>();
|
||||
for (String key : summaryFields) {
|
||||
String val = formatValue(row.get(key));
|
||||
if (StringUtils.isBlank(val)) {
|
||||
continue;
|
||||
}
|
||||
parts.add(fieldLabels.getOrDefault(key, key) + "=" + val);
|
||||
}
|
||||
if (parts.isEmpty()) {
|
||||
for (String key : fieldLabels.keySet()) {
|
||||
String val = formatValue(row.get(key));
|
||||
if (StringUtils.isBlank(val)) {
|
||||
continue;
|
||||
}
|
||||
parts.add(fieldLabels.get(key) + "=" + val);
|
||||
if (parts.size() >= 4) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.join(",", parts);
|
||||
}
|
||||
|
||||
private static boolean isRowMetaField(String key) {
|
||||
return "formulaSpecId".equals(key) || "mixingSpecId".equals(key);
|
||||
}
|
||||
|
||||
private static List<JSONObject> toJsonObjectList(Object listObj) {
|
||||
if (listObj == null) {
|
||||
return List.of();
|
||||
}
|
||||
JSONArray array = listObj instanceof JSONArray jsonArray ? jsonArray : JSON.parseArray(toJson(listObj));
|
||||
if (array == null || array.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<JSONObject> rows = new ArrayList<>(array.size());
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
Object element = array.get(i);
|
||||
if (element instanceof JSONObject jsonObject) {
|
||||
rows.add(jsonObject);
|
||||
} else if (element != null) {
|
||||
rows.add(JSON.parseObject(toJson(element)));
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static Map<String, Map<String, String>> buildSectionFieldLabels() {
|
||||
Map<String, Map<String, String>> map = new LinkedHashMap<>();
|
||||
map.put(SECTION_LINE, buildFormulaLineLabels());
|
||||
map.put(SECTION_MATERIAL, buildMixingMaterialLabels());
|
||||
map.put(SECTION_STEP, buildMixingStepLabels());
|
||||
map.put(SECTION_DOWN_STEP, buildMixingStepLabels());
|
||||
map.put(SECTION_TCU, buildMixingTcuLabels());
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, String[]> buildSectionSummaryFields() {
|
||||
Map<String, String[]> map = new LinkedHashMap<>();
|
||||
map.put(SECTION_LINE, new String[] {"mixerMaterialName", "mixerMaterialCode", "phr", "step"});
|
||||
map.put(SECTION_MATERIAL, new String[] {"mixerMaterialName", "unitWeight", "accumWeight", "seqNo"});
|
||||
map.put(SECTION_STEP, new String[] {"actionName", "actionSec", "tempC", "speedRpm"});
|
||||
map.put(SECTION_DOWN_STEP, new String[] {"actionName", "actionSec", "tempC", "speedRpm"});
|
||||
map.put(SECTION_TCU, new String[] {"sectionType", "frontRotorTemp", "rearRotorTemp", "drugWeighPos"});
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, String> buildFormulaLineLabels() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("sortNo", "序号");
|
||||
map.put("phr", "PHR");
|
||||
map.put("mixerMaterialCode", "物料编码");
|
||||
map.put("mixerMaterialName", "物料名称");
|
||||
map.put("materialDesc", "物料描述");
|
||||
map.put("step", "STEP");
|
||||
map.put("weighMode", "称量方式");
|
||||
map.put("weightPercent", "重量%");
|
||||
map.put("volume", "体积");
|
||||
map.put("remark", "备注");
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
map.put("stage" + i, "混合段" + i);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, String> buildMixingMaterialLabels() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("sortNo", "序号");
|
||||
map.put("materialMajor", "物料大类");
|
||||
map.put("materialMinor", "物料小类");
|
||||
map.put("materialKind", "种类");
|
||||
map.put("mixerMaterialName", "物料名称");
|
||||
map.put("mixerMaterialDesc", "物料描述");
|
||||
map.put("unitWeight", "单重");
|
||||
map.put("accumWeight", "累计");
|
||||
map.put("seqNo", "顺序");
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, String> buildMixingStepLabels() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("sortNo", "序号");
|
||||
map.put("actionName", "动作");
|
||||
map.put("actionSec", "时间(秒)");
|
||||
map.put("protectSec", "保护时间");
|
||||
map.put("tempC", "温度(℃)");
|
||||
map.put("powerKw", "功率(Kw)");
|
||||
map.put("energyKwh", "能量(Kwh)");
|
||||
map.put("comboMode", "组合");
|
||||
map.put("speedRpm", "转速(rpm)");
|
||||
map.put("pressureMpa", "压力(Mpa)");
|
||||
map.put("boltPercent", "栓(%)");
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, String> buildMixingTcuLabels() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("sortNo", "序号");
|
||||
map.put("sectionType", "区分");
|
||||
map.put("frontRotorTemp", "前转子温度");
|
||||
map.put("rearRotorTemp", "后转子温度");
|
||||
map.put("frontChamberTemp", "前混炼室温度");
|
||||
map.put("rearChamberTemp", "后混炼室温度");
|
||||
map.put("topPlugTemp", "上下顶栓温度");
|
||||
map.put("drugWeighPos", "药品称量位置");
|
||||
return map;
|
||||
}
|
||||
|
||||
private static JSONObject parseObject(String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parseObject(json);
|
||||
}
|
||||
|
||||
private static JSONObject toJsonObject(Object obj) {
|
||||
if (obj == null) {
|
||||
return new JSONObject();
|
||||
}
|
||||
JSONObject jsonObject = JSON.parseObject(toJson(obj));
|
||||
for (String ignore : IGNORE_FIELDS) {
|
||||
jsonObject.remove(ignore);
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
private static String toJson(Object obj) {
|
||||
return JSON.toJSONStringWithDateFormat(
|
||||
obj, "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteMapNullValue);
|
||||
}
|
||||
|
||||
private static String normalizeListJson(Object listObj) {
|
||||
if (listObj == null) {
|
||||
return "[]";
|
||||
}
|
||||
if (listObj instanceof List<?> list) {
|
||||
return normalizeListJson(list);
|
||||
}
|
||||
return toJson(listObj);
|
||||
}
|
||||
|
||||
private static String normalizeListJson(List<JSONObject> rows) {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return "[]";
|
||||
}
|
||||
List<JSONObject> normalized = new ArrayList<>(rows.size());
|
||||
for (JSONObject row : rows) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
JSONObject copy = new JSONObject(row);
|
||||
for (String ignore : IGNORE_FIELDS) {
|
||||
copy.remove(ignore);
|
||||
}
|
||||
copy.remove("formulaSpecId");
|
||||
copy.remove("mixingSpecId");
|
||||
normalized.add(copy);
|
||||
}
|
||||
return toJson(normalized);
|
||||
}
|
||||
|
||||
private static String formatValue(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
if (value instanceof BigDecimal decimal) {
|
||||
return decimal.stripTrailingZeros().toPlainString();
|
||||
}
|
||||
if (value instanceof Date date) {
|
||||
return toJson(date).replace("\"", "");
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
private static Map<String, String> buildFormulaMainLabels() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("category", "分类");
|
||||
map.put("specCode", "示方编号");
|
||||
map.put("rubberCode", "胶料代号");
|
||||
map.put("rubberMaterialId", "胶料ID");
|
||||
map.put("basicFormula", "基本配合");
|
||||
map.put("issueDate", "发行日期");
|
||||
map.put("purpose", "用途");
|
||||
map.put("issueNumber", "发行编号");
|
||||
map.put("mixingStages", "混合段数");
|
||||
map.put("mixingMachine", "混合机器");
|
||||
map.put("issueDeptId", "发行部门ID");
|
||||
map.put("issueDeptName", "发行部门");
|
||||
map.put("status", "状态");
|
||||
map.put("aRubberTotalPhr", "A胶合计PHR");
|
||||
map.put("totalPhr", "总PHR");
|
||||
map.put("naturalRubber", "天然橡胶");
|
||||
map.put("syntheticRubber", "合成橡胶");
|
||||
map.put("totalAmount", "总量");
|
||||
map.put("weightUnitPrice", "重量单价");
|
||||
map.put("volumeUnitPrice", "体积单价");
|
||||
map.put("qRubberSg", "Q胶比重");
|
||||
map.put("aRubberSg", "A胶比重");
|
||||
map.put("proofreadBy", "校对人");
|
||||
map.put("proofreadTime", "校对时间");
|
||||
map.put("auditBy", "审核人");
|
||||
map.put("auditTime", "审核时间");
|
||||
map.put("approveBy", "批准人");
|
||||
map.put("approveTime", "批准时间");
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
map.put("stage" + i + "Total", "混合段" + i + "合计");
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, String> buildMixingMainLabels() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("specName", "规格");
|
||||
map.put("purpose", "用途");
|
||||
map.put("machineId", "机台ID");
|
||||
map.put("machineName", "机台");
|
||||
map.put("makeDate", "制作日期");
|
||||
map.put("issueNumber", "发行编号");
|
||||
map.put("convertFactor", "换算系数");
|
||||
map.put("fillVolume", "填充体积");
|
||||
map.put("recycleCarbonSec", "回收炭黑(秒)");
|
||||
map.put("motherRubberSg", "母胶比重");
|
||||
map.put("finalRubberSg", "终炼胶比重");
|
||||
map.put("applyFactory", "适用工厂");
|
||||
map.put("stageCount", "段数");
|
||||
map.put("pureMixSec", "纯混炼时间(秒)");
|
||||
map.put("recycleCarbonKg", "回收炭黑(kg)");
|
||||
map.put("autoSmallPrintSetting", "自动小票打印设置");
|
||||
map.put("setTrainCount", "设定车次");
|
||||
map.put("sideWallWaterTemp", "侧壁水温");
|
||||
map.put("overtimeDischargeSec", "超时排胶(秒)");
|
||||
map.put("overtempDischargeSec", "超温排胶(秒)");
|
||||
map.put("overtempDischargeTemp", "超温排胶温度");
|
||||
map.put("doorWaterTemp", "门水温");
|
||||
map.put("rotorWaterTemp", "转子水温");
|
||||
map.put("maxFeedTemp", "最高投料温度");
|
||||
map.put("draftBy", "编制人");
|
||||
map.put("draftTime", "编制时间");
|
||||
map.put("proofreadBy", "校对人");
|
||||
map.put("proofreadTime", "校对时间");
|
||||
map.put("auditBy", "审核人");
|
||||
map.put("auditTime", "审核时间");
|
||||
map.put("approveBy", "批准人");
|
||||
map.put("approveTime", "批准时间");
|
||||
map.put("changeDate", "变更日期");
|
||||
return map;
|
||||
}
|
||||
|
||||
public static final String SPEC_TYPE_FORMULA = "formula";
|
||||
public static final String SPEC_TYPE_MIXING = "mixing";
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.jeecg.modules.xslmes.common;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.TenantConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
|
||||
/**
|
||||
* MES 租户解析工具(xslmes 模块内统一入口,避免各 Service 重复依赖 TokenUtils)
|
||||
*/
|
||||
public final class MesXslTenantUtils {
|
||||
|
||||
private MesXslTenantUtils() {}
|
||||
|
||||
/**
|
||||
* 解析租户 ID:优先实体上下文,其次 TenantContext,最后从当前请求头/参数读取
|
||||
*/
|
||||
public static Integer resolveTenantId(Integer contextTenantId) {
|
||||
if (contextTenantId != null) {
|
||||
return contextTenantId;
|
||||
}
|
||||
String tenantId = getTenantIdString();
|
||||
if (oConvertUtils.isEmpty(tenantId)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(tenantId.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 尽力解析租户 ID,无法解析时返回 0 */
|
||||
public static int resolveTenantIdBestEffort() {
|
||||
return oConvertUtils.getInt(getTenantIdString(), 0);
|
||||
}
|
||||
|
||||
private static String getTenantIdString() {
|
||||
String tenantId = TenantContext.getTenant();
|
||||
if (oConvertUtils.isNotEmpty(tenantId)) {
|
||||
return tenantId;
|
||||
}
|
||||
try {
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
if (request != null) {
|
||||
tenantId = getTenantIdByRequest(request);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 非 Web 请求上下文时忽略
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
/** 与 TokenUtils.getTenantIdByRequest 行为一致 */
|
||||
private static String getTenantIdByRequest(HttpServletRequest request) {
|
||||
String tenantId = request.getParameter(TenantConstant.TENANT_ID);
|
||||
if (tenantId == null) {
|
||||
tenantId = oConvertUtils.getString(request.getHeader(CommonConstant.TENANT_ID));
|
||||
}
|
||||
if (oConvertUtils.isNotEmpty(tenantId) && "undefined".equals(tenantId)) {
|
||||
return null;
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.jeecg.modules.xslmes.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecEditLog;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecEditLogService;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslFormulaSpecEditLogDetailVO;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* 配方示方修改日志(只读查询)
|
||||
*/
|
||||
@Tag(name = "配方日志查询")
|
||||
@RestController
|
||||
@RequestMapping("/xslmes/mesXslFormulaSpecEditLog")
|
||||
@Slf4j
|
||||
public class MesXslFormulaSpecEditLogController
|
||||
extends JeecgController<MesXslFormulaSpecEditLog, IMesXslFormulaSpecEditLogService> {
|
||||
|
||||
@Operation(summary = "配方日志查询-分页列表")
|
||||
@RequiresPermissions("xslmes:mes_xsl_formula_spec_edit_log:list")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<MesXslFormulaSpecEditLog>> queryPageList(
|
||||
MesXslFormulaSpecEditLog entity,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<MesXslFormulaSpecEditLog> qw = QueryGenerator.initQueryWrapper(entity, req.getParameterMap());
|
||||
qw.orderByDesc("modify_time");
|
||||
Page<MesXslFormulaSpecEditLog> page = new Page<>(pageNo, pageSize);
|
||||
return Result.OK(service.page(page, qw));
|
||||
}
|
||||
|
||||
@Operation(summary = "配方日志查询-详情(含对比项)")
|
||||
@RequiresPermissions("xslmes:mes_xsl_formula_spec_edit_log:list")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<MesXslFormulaSpecEditLogDetailVO> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||
MesXslFormulaSpecEditLogDetailVO detail = service.getDetail(id);
|
||||
if (detail == null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(detail);
|
||||
}
|
||||
|
||||
@RequiresPermissions("xslmes:mes_xsl_formula_spec_edit_log:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, MesXslFormulaSpecEditLog entity) {
|
||||
return super.exportXls(request, entity, MesXslFormulaSpecEditLog.class, "配方日志查询");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.jeecg.modules.xslmes.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRackTrainCountSettingService;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslRackTrainCountSettingVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 架子车数设定(数据来源于混炼示方,不建独立业务表)
|
||||
*/
|
||||
@Tag(name = "架子车数设定")
|
||||
@RestController
|
||||
@RequestMapping("/xslmes/mesXslRackTrainCountSetting")
|
||||
@Slf4j
|
||||
public class MesXslRackTrainCountSettingController {
|
||||
|
||||
@Autowired
|
||||
private IMesXslRackTrainCountSettingService rackTrainCountSettingService;
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定分页列表-----------
|
||||
@Operation(summary = "架子车数设定-分页列表查询")
|
||||
@RequiresPermissions("xslmes:mes_xsl_rack_train_count_setting:list")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<MesXslRackTrainCountSettingVO>> queryPageList(
|
||||
MesXslRackTrainCountSettingVO query,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(name = "keyword", required = false) String keyword) {
|
||||
return Result.OK(rackTrainCountSettingService.queryPage(pageNo, pageSize, query, keyword));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定分页列表-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定详情查询-----------
|
||||
@Operation(summary = "架子车数设定-通过id查询")
|
||||
@RequiresPermissions("xslmes:mes_xsl_rack_train_count_setting:list")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<MesXslRackTrainCountSettingVO> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||
MesXslRackTrainCountSettingVO detail = rackTrainCountSettingService.queryById(id);
|
||||
if (detail == null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(detail);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定详情查询-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定保存-----------
|
||||
@AutoLog(value = "架子车数设定-保存")
|
||||
@Operation(summary = "架子车数设定-保存设定车数")
|
||||
@RequiresPermissions("xslmes:mes_xsl_rack_train_count_setting:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody MesXslRackTrainCountSettingVO setting) {
|
||||
try {
|
||||
rackTrainCountSettingService.saveSetting(setting);
|
||||
return Result.OK("保存成功!");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return Result.error(ex.getMessage());
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定保存-----------
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package org.jeecg.modules.xslmes.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.mes.material.entity.MesMaterial;
|
||||
import org.jeecg.modules.mes.material.service.IMesMaterialService;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecord;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecordLine;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestType;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestRecordService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestTypeService;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslRubberQuickTestRecordBatchFromMaterialVO;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslRubberQuickTestRecordPage;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* MES 胶料快检记录(主子表)
|
||||
*/
|
||||
@Tag(name = "MES胶料快检记录")
|
||||
@RestController
|
||||
@RequestMapping("/xslmes/mesXslRubberQuickTestRecord")
|
||||
@Slf4j
|
||||
public class MesXslRubberQuickTestRecordController
|
||||
extends JeecgController<MesXslRubberQuickTestRecord, IMesXslRubberQuickTestRecordService> {
|
||||
|
||||
@Autowired
|
||||
private IMesXslRubberQuickTestRecordService mesXslRubberQuickTestRecordService;
|
||||
|
||||
@Autowired
|
||||
private IMesMaterialService mesMaterialService;
|
||||
|
||||
@Autowired
|
||||
private IMesXslRubberQuickTestTypeService mesXslRubberQuickTestTypeService;
|
||||
|
||||
@Operation(summary = "MES胶料快检记录-分页列表查询")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<MesXslRubberQuickTestRecord>> queryPageList(
|
||||
MesXslRubberQuickTestRecord model,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<MesXslRubberQuickTestRecord> queryWrapper =
|
||||
QueryGenerator.initQueryWrapper(model, req.getParameterMap());
|
||||
queryWrapper.orderByDesc("inspect_time", "create_time");
|
||||
Page<MesXslRubberQuickTestRecord> page = new Page<>(pageNo, pageSize);
|
||||
IPage<MesXslRubberQuickTestRecord> pageList = mesXslRubberQuickTestRecordService.page(page, queryWrapper);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES胶料快检记录-添加")
|
||||
@Operation(summary = "MES胶料快检记录-添加")
|
||||
@RequiresPermissions("mes:mes_xsl_rubber_quick_test_record:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody MesXslRubberQuickTestRecordPage page) {
|
||||
MesXslRubberQuickTestRecord main = new MesXslRubberQuickTestRecord();
|
||||
BeanUtils.copyProperties(page, main);
|
||||
String err = validateForSave(main, page.getLineList());
|
||||
if (err != null) {
|
||||
return Result.error(err);
|
||||
}
|
||||
try {
|
||||
mesXslRubberQuickTestRecordService.saveMain(main, page.getLineList());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES胶料快检记录-编辑")
|
||||
@Operation(summary = "MES胶料快检记录-编辑")
|
||||
@RequiresPermissions("mes:mes_xsl_rubber_quick_test_record:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody MesXslRubberQuickTestRecordPage page) {
|
||||
MesXslRubberQuickTestRecord main = new MesXslRubberQuickTestRecord();
|
||||
BeanUtils.copyProperties(page, main);
|
||||
String err = validateForSave(main, page.getLineList());
|
||||
if (err != null) {
|
||||
return Result.error(err);
|
||||
}
|
||||
try {
|
||||
mesXslRubberQuickTestRecordService.updateMain(main, page.getLineList());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
return Result.OK("编辑成功!");
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES胶料快检记录-从胶料批量生成")
|
||||
@Operation(summary = "MES胶料快检记录-从胶料信息批量生成")
|
||||
@RequiresPermissions("mes:mes_material:rubberQuickTestInspect")
|
||||
@PostMapping(value = "/batchFromMaterial")
|
||||
public Result<List<String>> batchFromMaterial(@RequestBody MesXslRubberQuickTestRecordBatchFromMaterialVO vo) {
|
||||
try {
|
||||
List<String> ids = mesXslRubberQuickTestRecordService.batchFromMaterial(vo);
|
||||
return Result.OK("成功生成 " + ids.size() + " 条快检记录", ids);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES胶料快检记录-删除")
|
||||
@Operation(summary = "MES胶料快检记录-通过id删除")
|
||||
@RequiresPermissions("mes:mes_xsl_rubber_quick_test_record:delete")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
|
||||
mesXslRubberQuickTestRecordService.delMain(id);
|
||||
return Result.OK("删除成功!");
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES胶料快检记录-批量删除")
|
||||
@Operation(summary = "MES胶料快检记录-批量删除")
|
||||
@RequiresPermissions("mes:mes_xsl_rubber_quick_test_record:deleteBatch")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
|
||||
mesXslRubberQuickTestRecordService.delBatchMain(Arrays.asList(ids.split(",")));
|
||||
return Result.OK("批量删除成功!");
|
||||
}
|
||||
|
||||
@Operation(summary = "MES胶料快检记录-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<MesXslRubberQuickTestRecord> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||
MesXslRubberQuickTestRecord entity = mesXslRubberQuickTestRecordService.getById(id);
|
||||
if (entity == null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(entity);
|
||||
}
|
||||
|
||||
@Operation(summary = "MES胶料快检记录-查询明细")
|
||||
@GetMapping(value = "/queryLineListByRecordId")
|
||||
public Result<List<MesXslRubberQuickTestRecordLine>> queryLineListByRecordId(
|
||||
@RequestParam(name = "id", required = true) String id) {
|
||||
return Result.OK(mesXslRubberQuickTestRecordService.selectLinesByRecordId(id));
|
||||
}
|
||||
|
||||
@RequiresPermissions("mes:mes_xsl_rubber_quick_test_record:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, MesXslRubberQuickTestRecord model) {
|
||||
return super.exportXls(request, model, MesXslRubberQuickTestRecord.class, "MES胶料快检记录");
|
||||
}
|
||||
|
||||
@RequiresPermissions("mes:mes_xsl_rubber_quick_test_record:importExcel")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
return super.importExcel(request, response, MesXslRubberQuickTestRecord.class);
|
||||
}
|
||||
|
||||
private String validateForSave(MesXslRubberQuickTestRecord main, List<MesXslRubberQuickTestRecordLine> lineList) {
|
||||
if (main == null) {
|
||||
return "参数不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(main.getRubberMaterialId())) {
|
||||
return "请选择胶料";
|
||||
}
|
||||
MesMaterial material = mesMaterialService.getById(main.getRubberMaterialId());
|
||||
if (material == null) {
|
||||
return "所选胶料不存在";
|
||||
}
|
||||
main.setRubberMaterialName(material.getMaterialName());
|
||||
|
||||
if (oConvertUtils.isNotEmpty(main.getQuickTestTypeId())) {
|
||||
MesXslRubberQuickTestType type = mesXslRubberQuickTestTypeService.getById(main.getQuickTestTypeId());
|
||||
if (type == null) {
|
||||
return "所选检验类型不存在";
|
||||
}
|
||||
main.setQuickTestTypeName(type.getTypeName());
|
||||
}
|
||||
|
||||
if (lineList == null || lineList.isEmpty()) {
|
||||
return "请维护检验明细";
|
||||
}
|
||||
for (int i = 0; i < lineList.size(); i++) {
|
||||
MesXslRubberQuickTestRecordLine line = lineList.get(i);
|
||||
if (line == null || oConvertUtils.isEmpty(line.getInspectItem())) {
|
||||
return "第 " + (i + 1) + " 行检验项目不能为空";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.jeecg.modules.xslmes.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecg.common.aspect.annotation.Dict;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* 配方示方修改日志(配合示方/混炼示方)
|
||||
*/
|
||||
@Data
|
||||
@TableName("mes_xsl_formula_spec_edit_log")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Schema(description = "配方示方修改日志")
|
||||
public class MesXslFormulaSpecEditLog implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Excel(name = "示方分类", width = 12, dicCode = "xslmes_formula_spec_edit_log_type")
|
||||
@Dict(dicCode = "xslmes_formula_spec_edit_log_type")
|
||||
@Schema(description = "示方分类 formula=配合示方 mixing=混炼示方")
|
||||
private String specType;
|
||||
|
||||
@Schema(description = "示方主表ID")
|
||||
private String specId;
|
||||
|
||||
@Excel(name = "示方标识", width = 22)
|
||||
@Schema(description = "示方标识(胶料代号/规格名)")
|
||||
private String specTitle;
|
||||
|
||||
@Excel(name = "发行编号", width = 16)
|
||||
@Schema(description = "发行编号")
|
||||
private String issueNumber;
|
||||
|
||||
@Excel(name = "操作类型", width = 10, dicCode = "xslmes_formula_spec_edit_log_action")
|
||||
@Dict(dicCode = "xslmes_formula_spec_edit_log_action")
|
||||
@Schema(description = "操作类型 create=新增 update=修改 delete=删除")
|
||||
private String actionType;
|
||||
|
||||
@Excel(name = "修改内容", width = 50)
|
||||
@Schema(description = "修改内容摘要")
|
||||
private String changeSummary;
|
||||
|
||||
@Schema(description = "变更前快照JSON")
|
||||
private String beforeSnapshot;
|
||||
|
||||
@Schema(description = "变更后快照JSON")
|
||||
private String afterSnapshot;
|
||||
|
||||
@Excel(name = "修改时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "修改时间")
|
||||
private Date modifyTime;
|
||||
|
||||
@Schema(description = "修改人账号")
|
||||
private String modifyBy;
|
||||
|
||||
@Excel(name = "修改人", width = 14)
|
||||
@Schema(description = "修改人姓名")
|
||||
private String modifyByName;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Integer tenantId;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package org.jeecg.modules.xslmes.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecg.common.aspect.annotation.Dict;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* MES 胶料快检记录主表
|
||||
*/
|
||||
@Data
|
||||
@TableName("mes_xsl_rubber_quick_test_record")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Schema(description = "MES胶料快检记录")
|
||||
public class MesXslRubberQuickTestRecord implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
@Excel(name = "单号", width = 16)
|
||||
@Schema(description = "单号(JL+yyyyMMdd+4位流水)")
|
||||
private String recordNo;
|
||||
|
||||
@Schema(description = "胶料ID mes_material.id")
|
||||
private String rubberMaterialId;
|
||||
|
||||
@Excel(name = "胶料名称", width = 20)
|
||||
@Schema(description = "胶料名称冗余")
|
||||
private String rubberMaterialName;
|
||||
|
||||
@Schema(description = "引用的实验标准ID")
|
||||
private String stdId;
|
||||
|
||||
@Schema(description = "生产机台ID")
|
||||
private String prodEquipmentLedgerId;
|
||||
|
||||
@Excel(name = "生产机台", width = 16)
|
||||
@Schema(description = "生产机台名称冗余")
|
||||
private String prodEquipmentName;
|
||||
|
||||
@Excel(name = "生产日期", width = 12, format = "yyyy-MM-dd")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private Date productionDate;
|
||||
|
||||
@Excel(name = "车次编号", width = 14)
|
||||
private String trainNo;
|
||||
|
||||
@Excel(name = "班次", width = 10, dicCode = "xslmes_rubber_quick_test_work_shift")
|
||||
@Dict(dicCode = "xslmes_rubber_quick_test_work_shift")
|
||||
private String workShift;
|
||||
|
||||
@Excel(name = "班组", width = 10, dicCode = "xslmes_rubber_quick_test_work_team")
|
||||
@Dict(dicCode = "xslmes_rubber_quick_test_work_team")
|
||||
private String workTeam;
|
||||
|
||||
@Excel(name = "检验次数", width = 10)
|
||||
private Integer inspectTimes;
|
||||
|
||||
@Excel(name = "检验时间", width = 18, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inspectTime;
|
||||
|
||||
@Schema(description = "检验人用户ID")
|
||||
private String inspectorUserId;
|
||||
|
||||
private String inspectorUsername;
|
||||
|
||||
@Excel(name = "检验人", width = 12)
|
||||
private String inspectorRealname;
|
||||
|
||||
@Schema(description = "检验类型ID")
|
||||
private String quickTestTypeId;
|
||||
|
||||
@Excel(name = "检验类型", width = 16)
|
||||
private String quickTestTypeName;
|
||||
|
||||
@Excel(name = "检验结果", width = 10, dicCode = "xslmes_rubber_quick_test_record_result")
|
||||
@Dict(dicCode = "xslmes_rubber_quick_test_record_result")
|
||||
private String inspectResult;
|
||||
|
||||
@Excel(name = "生产计划号", width = 16)
|
||||
private String productionPlanNo;
|
||||
|
||||
@Schema(description = "检验机台ID")
|
||||
private String inspectEquipmentLedgerId;
|
||||
|
||||
@Excel(name = "检验机台", width = 16)
|
||||
private String inspectEquipmentName;
|
||||
|
||||
@Excel(name = "胶料卡片号", width = 16)
|
||||
private String rubberCardNo;
|
||||
|
||||
@Excel(name = "胶料批次", width = 16)
|
||||
private String rubberBatchNo;
|
||||
|
||||
private Integer tenantId;
|
||||
private String sysOrgCode;
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
@TableLogic
|
||||
private Integer delFlag;
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<MesXslRubberQuickTestRecordLine> lineList;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.jeecg.modules.xslmes.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* MES 胶料快检记录明细
|
||||
*/
|
||||
@Data
|
||||
@TableName("mes_xsl_rubber_quick_test_record_line")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Schema(description = "MES胶料快检记录明细")
|
||||
public class MesXslRubberQuickTestRecordLine implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
private String recordId;
|
||||
|
||||
private String dataPointId;
|
||||
|
||||
@Excel(name = "检验项目", width = 18)
|
||||
private String inspectItem;
|
||||
|
||||
@Excel(name = "检验下限", width = 12, type = 10)
|
||||
private BigDecimal lowerLimit;
|
||||
|
||||
@Excel(name = "检验值", width = 12, type = 10)
|
||||
private BigDecimal inspectValue;
|
||||
|
||||
@Excel(name = "检验上限", width = 12, type = 10)
|
||||
private BigDecimal upperLimit;
|
||||
|
||||
private Integer sortNo;
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.jeecg.modules.xslmes.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecEditLog;
|
||||
|
||||
/**
|
||||
* 配方示方修改日志
|
||||
*/
|
||||
public interface MesXslFormulaSpecEditLogMapper extends BaseMapper<MesXslFormulaSpecEditLog> {}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.jeecg.modules.xslmes.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecordLine;
|
||||
|
||||
public interface MesXslRubberQuickTestRecordLineMapper extends BaseMapper<MesXslRubberQuickTestRecordLine> {}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.jeecg.modules.xslmes.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecord;
|
||||
|
||||
public interface MesXslRubberQuickTestRecordMapper extends BaseMapper<MesXslRubberQuickTestRecord> {}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.jeecg.modules.xslmes.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpec;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecEditLog;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecLine;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslFormulaSpecEditChangeItemVO;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslFormulaSpecEditLogDetailVO;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage;
|
||||
|
||||
/**
|
||||
* 配方示方修改日志
|
||||
*/
|
||||
public interface IMesXslFormulaSpecEditLogService extends IService<MesXslFormulaSpecEditLog> {
|
||||
|
||||
String SPEC_TYPE_FORMULA = "formula";
|
||||
String SPEC_TYPE_MIXING = "mixing";
|
||||
String ACTION_CREATE = "create";
|
||||
String ACTION_UPDATE = "update";
|
||||
String ACTION_DELETE = "delete";
|
||||
|
||||
void recordFormulaCreate(MesXslFormulaSpec main, List<MesXslFormulaSpecLine> lineList);
|
||||
|
||||
void recordFormulaUpdate(
|
||||
MesXslFormulaSpec beforeMain,
|
||||
List<MesXslFormulaSpecLine> beforeLines,
|
||||
MesXslFormulaSpec afterMain,
|
||||
List<MesXslFormulaSpecLine> afterLines);
|
||||
|
||||
void recordFormulaDelete(MesXslFormulaSpec main, List<MesXslFormulaSpecLine> lineList);
|
||||
|
||||
void recordMixingCreate(MesXslMixingSpecPage after);
|
||||
|
||||
void recordMixingUpdate(MesXslMixingSpecPage before, MesXslMixingSpecPage after);
|
||||
|
||||
void recordMixingDelete(MesXslMixingSpecPage before);
|
||||
|
||||
MesXslFormulaSpecEditLogDetailVO getDetail(String id);
|
||||
|
||||
List<MesXslFormulaSpecEditChangeItemVO> buildChangeItems(
|
||||
String specType, String beforeSnapshot, String afterSnapshot);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.jeecg.modules.xslmes.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslRackTrainCountSettingVO;
|
||||
|
||||
/**
|
||||
* 架子车数设定(基于混炼示方查询,不建独立业务表)
|
||||
*/
|
||||
public interface IMesXslRackTrainCountSettingService {
|
||||
|
||||
/**
|
||||
* 分页查询架子车数设定列表
|
||||
*/
|
||||
IPage<MesXslRackTrainCountSettingVO> queryPage(
|
||||
Integer pageNo,
|
||||
Integer pageSize,
|
||||
MesXslRackTrainCountSettingVO query,
|
||||
String keyword);
|
||||
|
||||
/**
|
||||
* 按混炼示方ID查询详情
|
||||
*/
|
||||
MesXslRackTrainCountSettingVO queryById(String id);
|
||||
|
||||
/**
|
||||
* 保存设定车数(回写混炼示方 set_train_count 字段)
|
||||
*/
|
||||
void saveSetting(MesXslRackTrainCountSettingVO setting);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.jeecg.modules.xslmes.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecord;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecordLine;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslRubberQuickTestRecordBatchFromMaterialVO;
|
||||
|
||||
public interface IMesXslRubberQuickTestRecordService extends IService<MesXslRubberQuickTestRecord> {
|
||||
|
||||
void saveMain(MesXslRubberQuickTestRecord main, List<MesXslRubberQuickTestRecordLine> lineList);
|
||||
|
||||
void updateMain(MesXslRubberQuickTestRecord main, List<MesXslRubberQuickTestRecordLine> lineList);
|
||||
|
||||
void delMain(String id);
|
||||
|
||||
void delBatchMain(Collection<? extends Serializable> idList);
|
||||
|
||||
List<MesXslRubberQuickTestRecordLine> selectLinesByRecordId(String recordId);
|
||||
|
||||
String generateRecordNo(MesXslRubberQuickTestRecord context);
|
||||
|
||||
List<String> batchFromMaterial(MesXslRubberQuickTestRecordBatchFromMaterialVO vo);
|
||||
}
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslDowntimeMainType;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslDowntimeMainTypeMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslDowntimeMainTypeService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslDowntimeMainTypeServiceImpl extends ServiceImpl<MesXslDowntim
|
||||
if (oConvertUtils.isEmpty(downtimeType)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslDowntimeMainType> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslDowntimeMainType::getDowntimeType, downtimeType.trim());
|
||||
w.and(
|
||||
@@ -39,25 +37,5 @@ public class MesXslDowntimeMainTypeServiceImpl extends ServiceImpl<MesXslDowntim
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslDowntimeMainType context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260518 for:【MES】停机类型同租户不可重复;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslDowntimeType;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslDowntimeTypeMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslDowntimeTypeService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslDowntimeTypeServiceImpl extends ServiceImpl<MesXslDowntimeTyp
|
||||
if (oConvertUtils.isEmpty(downtimeType)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslDowntimeType> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslDowntimeType::getDowntimeType, downtimeType.trim());
|
||||
w.and(
|
||||
@@ -39,25 +37,5 @@ public class MesXslDowntimeTypeServiceImpl extends ServiceImpl<MesXslDowntimeTyp
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslDowntimeType context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260518 for:【MES】停机类型名称同租户不可重复;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipInspectConfig;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipInspectConfigLine;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslEquipInspectConfigLineMapper;
|
||||
@@ -89,7 +87,7 @@ public class MesXslEquipInspectConfigServiceImpl
|
||||
if (oConvertUtils.isEmpty(equipmentLedgerId) || oConvertUtils.isEmpty(configType)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslEquipInspectConfig> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslEquipInspectConfig::getEquipmentLedgerId, equipmentLedgerId.trim());
|
||||
w.eq(MesXslEquipInspectConfig::getConfigType, configType.trim());
|
||||
@@ -107,26 +105,5 @@ public class MesXslEquipInspectConfigServiceImpl
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslEquipInspectConfig context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260519 for:【MES】设备点检配置:同设备同类型(点检/保养)仅允许一条主数据-----------
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipInspectConfig;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipInspectConfigLine;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipInspectRecord;
|
||||
@@ -104,7 +102,7 @@ public class MesXslEquipInspectRecordServiceImpl
|
||||
public String generateRecordNo(MesXslEquipInspectRecord context) {
|
||||
String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||
String prefix = "EC" + dateStr;
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
|
||||
LambdaQueryWrapper<MesXslEquipInspectRecord> qw = new LambdaQueryWrapper<>();
|
||||
qw.likeRight(MesXslEquipInspectRecord::getRecordNo, prefix);
|
||||
@@ -288,25 +286,4 @@ public class MesXslEquipInspectRecordServiceImpl
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260521 for:【MES】不合格点检记录登记处理人及处理时间-----------
|
||||
|
||||
private static Integer resolveTenantId(MesXslEquipInspectRecord context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipmentCategory;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslEquipmentCategoryMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslEquipmentCategoryService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslEquipmentCategoryServiceImpl extends ServiceImpl<MesXslEquipm
|
||||
if (oConvertUtils.isEmpty(categoryName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslEquipmentCategory> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslEquipmentCategory::getCategoryName, categoryName.trim());
|
||||
w.and(
|
||||
@@ -60,25 +58,5 @@ public class MesXslEquipmentCategoryServiceImpl extends ServiceImpl<MesXslEquipm
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260514 for:【MES】设备类型按类别名称解析关联,提供未删除单条查询-----------
|
||||
|
||||
private static Integer resolveTenantId(MesXslEquipmentCategory context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260514 for:【MES】设备类别名称同租户唯一;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipmentLedger;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslEquipmentLedgerMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslEquipmentLedgerService;
|
||||
@@ -35,7 +33,7 @@ public class MesXslEquipmentLedgerServiceImpl extends ServiceImpl<MesXslEquipmen
|
||||
if (oConvertUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslEquipmentLedger> w = new LambdaQueryWrapper<>();
|
||||
w.eq(column, value.trim());
|
||||
w.and(q -> q.eq(MesXslEquipmentLedger::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslEquipmentLedger::getDelFlag));
|
||||
@@ -48,25 +46,5 @@ public class MesXslEquipmentLedgerServiceImpl extends ServiceImpl<MesXslEquipmen
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslEquipmentLedger context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260518 for:【MES】设备台账编号、名称同租户不可重复-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipmentPart;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslEquipmentPartMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslEquipmentPartService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslEquipmentPartServiceImpl extends ServiceImpl<MesXslEquipmentP
|
||||
if (oConvertUtils.isEmpty(partCode)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslEquipmentPart> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslEquipmentPart::getPartCode, partCode.trim());
|
||||
w.and(
|
||||
@@ -39,26 +37,6 @@ public class MesXslEquipmentPartServiceImpl extends ServiceImpl<MesXslEquipmentP
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslEquipmentPart context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260515 for:【MES】设备部位代码同租户不可重复;仅统计未删除(del_flag=0 或 null)-----------
|
||||
|
||||
//update-begin---author:jiangxh ---date:20260515 for:【MES】设备部位按代码+租户查询单条,供设备小部位导入解析大部位-----------
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipmentSubPart;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslEquipmentSubPartMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslEquipmentSubPartService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslEquipmentSubPartServiceImpl extends ServiceImpl<MesXslEquipme
|
||||
if (oConvertUtils.isEmpty(subPartCode)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslEquipmentSubPart> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslEquipmentSubPart::getSubPartCode, subPartCode.trim());
|
||||
w.and(
|
||||
@@ -39,25 +37,5 @@ public class MesXslEquipmentSubPartServiceImpl extends ServiceImpl<MesXslEquipme
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslEquipmentSubPart context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260515 for:【MES】设备小部位代码同租户不可重复;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipmentType;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslEquipmentTypeMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslEquipmentTypeService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslEquipmentTypeServiceImpl extends ServiceImpl<MesXslEquipmentT
|
||||
if (oConvertUtils.isEmpty(typeName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslEquipmentType> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslEquipmentType::getTypeName, typeName.trim());
|
||||
w.and(
|
||||
@@ -39,25 +37,5 @@ public class MesXslEquipmentTypeServiceImpl extends ServiceImpl<MesXslEquipmentT
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslEquipmentType context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260514 for:【MES】设备类型名称同租户唯一;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.modules.xslmes.common.MesXslFormulaSpecEditLogDiffUtil;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpec;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecEditLog;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecLine;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslFormulaSpecEditLogMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecEditLogService;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslFormulaSpecEditChangeItemVO;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslFormulaSpecEditLogDetailVO;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 配方示方修改日志
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MesXslFormulaSpecEditLogServiceImpl
|
||||
extends ServiceImpl<MesXslFormulaSpecEditLogMapper, MesXslFormulaSpecEditLog>
|
||||
implements IMesXslFormulaSpecEditLogService {
|
||||
|
||||
@Override
|
||||
public void recordFormulaCreate(MesXslFormulaSpec main, List<MesXslFormulaSpecLine> lineList) {
|
||||
if (main == null || StringUtils.isBlank(main.getId())) {
|
||||
return;
|
||||
}
|
||||
String afterSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildFormulaSnapshotJson(main, lineList);
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items =
|
||||
buildChangeItems(SPEC_TYPE_FORMULA, null, afterSnapshot);
|
||||
saveLog(SPEC_TYPE_FORMULA, main.getId(), resolveFormulaTitle(main), main.getIssueNumber(),
|
||||
ACTION_CREATE, null, afterSnapshot, items, main.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordFormulaUpdate(
|
||||
MesXslFormulaSpec beforeMain,
|
||||
List<MesXslFormulaSpecLine> beforeLines,
|
||||
MesXslFormulaSpec afterMain,
|
||||
List<MesXslFormulaSpecLine> afterLines) {
|
||||
if (afterMain == null || StringUtils.isBlank(afterMain.getId())) {
|
||||
return;
|
||||
}
|
||||
String beforeSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildFormulaSnapshotJson(beforeMain, beforeLines);
|
||||
String afterSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildFormulaSnapshotJson(afterMain, afterLines);
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items =
|
||||
buildChangeItems(SPEC_TYPE_FORMULA, beforeSnapshot, afterSnapshot);
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
saveLog(
|
||||
SPEC_TYPE_FORMULA,
|
||||
afterMain.getId(),
|
||||
resolveFormulaTitle(afterMain),
|
||||
afterMain.getIssueNumber(),
|
||||
ACTION_UPDATE,
|
||||
beforeSnapshot,
|
||||
afterSnapshot,
|
||||
items,
|
||||
afterMain.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordFormulaDelete(MesXslFormulaSpec main, List<MesXslFormulaSpecLine> lineList) {
|
||||
if (main == null || StringUtils.isBlank(main.getId())) {
|
||||
return;
|
||||
}
|
||||
String beforeSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildFormulaSnapshotJson(main, lineList);
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items =
|
||||
buildChangeItems(SPEC_TYPE_FORMULA, beforeSnapshot, null);
|
||||
saveLog(
|
||||
SPEC_TYPE_FORMULA,
|
||||
main.getId(),
|
||||
resolveFormulaTitle(main),
|
||||
main.getIssueNumber(),
|
||||
ACTION_DELETE,
|
||||
beforeSnapshot,
|
||||
null,
|
||||
items,
|
||||
main.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordMixingCreate(MesXslMixingSpecPage after) {
|
||||
if (after == null || StringUtils.isBlank(after.getId())) {
|
||||
return;
|
||||
}
|
||||
String afterSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildMixingSnapshotJson(after);
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items =
|
||||
buildChangeItems(SPEC_TYPE_MIXING, null, afterSnapshot);
|
||||
saveLog(
|
||||
SPEC_TYPE_MIXING,
|
||||
after.getId(),
|
||||
resolveMixingTitle(after),
|
||||
after.getIssueNumber(),
|
||||
ACTION_CREATE,
|
||||
null,
|
||||
afterSnapshot,
|
||||
items,
|
||||
after.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordMixingUpdate(MesXslMixingSpecPage before, MesXslMixingSpecPage after) {
|
||||
if (after == null || StringUtils.isBlank(after.getId())) {
|
||||
return;
|
||||
}
|
||||
String beforeSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildMixingSnapshotJson(before);
|
||||
String afterSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildMixingSnapshotJson(after);
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items =
|
||||
buildChangeItems(SPEC_TYPE_MIXING, beforeSnapshot, afterSnapshot);
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
saveLog(
|
||||
SPEC_TYPE_MIXING,
|
||||
after.getId(),
|
||||
resolveMixingTitle(after),
|
||||
after.getIssueNumber(),
|
||||
ACTION_UPDATE,
|
||||
beforeSnapshot,
|
||||
afterSnapshot,
|
||||
items,
|
||||
after.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordMixingDelete(MesXslMixingSpecPage before) {
|
||||
if (before == null || StringUtils.isBlank(before.getId())) {
|
||||
return;
|
||||
}
|
||||
String beforeSnapshot = MesXslFormulaSpecEditLogDiffUtil.buildMixingSnapshotJson(before);
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items =
|
||||
buildChangeItems(SPEC_TYPE_MIXING, beforeSnapshot, null);
|
||||
saveLog(
|
||||
SPEC_TYPE_MIXING,
|
||||
before.getId(),
|
||||
resolveMixingTitle(before),
|
||||
before.getIssueNumber(),
|
||||
ACTION_DELETE,
|
||||
beforeSnapshot,
|
||||
null,
|
||||
items,
|
||||
before.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MesXslFormulaSpecEditLogDetailVO getDetail(String id) {
|
||||
MesXslFormulaSpecEditLog row = getById(id);
|
||||
if (row == null) {
|
||||
return null;
|
||||
}
|
||||
MesXslFormulaSpecEditLogDetailVO detail = new MesXslFormulaSpecEditLogDetailVO();
|
||||
detail.setId(row.getId());
|
||||
detail.setSpecType(row.getSpecType());
|
||||
detail.setSpecId(row.getSpecId());
|
||||
detail.setSpecTitle(row.getSpecTitle());
|
||||
detail.setIssueNumber(row.getIssueNumber());
|
||||
detail.setActionType(row.getActionType());
|
||||
detail.setChangeSummary(row.getChangeSummary());
|
||||
detail.setBeforeSnapshot(row.getBeforeSnapshot());
|
||||
detail.setAfterSnapshot(row.getAfterSnapshot());
|
||||
detail.setModifyTime(row.getModifyTime());
|
||||
detail.setModifyBy(row.getModifyBy());
|
||||
detail.setModifyByName(row.getModifyByName());
|
||||
detail.setTenantId(row.getTenantId());
|
||||
detail.setBeforeData(parseSnapshot(row.getBeforeSnapshot()));
|
||||
detail.setAfterData(parseSnapshot(row.getAfterSnapshot()));
|
||||
detail.setChangeItems(buildChangeItems(row.getSpecType(), row.getBeforeSnapshot(), row.getAfterSnapshot()));
|
||||
return detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MesXslFormulaSpecEditChangeItemVO> buildChangeItems(
|
||||
String specType, String beforeSnapshot, String afterSnapshot) {
|
||||
return MesXslFormulaSpecEditLogDiffUtil.compare(specType, beforeSnapshot, afterSnapshot);
|
||||
}
|
||||
|
||||
private void saveLog(
|
||||
String specType,
|
||||
String specId,
|
||||
String specTitle,
|
||||
String issueNumber,
|
||||
String actionType,
|
||||
String beforeSnapshot,
|
||||
String afterSnapshot,
|
||||
List<MesXslFormulaSpecEditChangeItemVO> items,
|
||||
Integer tenantId) {
|
||||
LoginUser loginUser = resolveLoginUser();
|
||||
MesXslFormulaSpecEditLog row = new MesXslFormulaSpecEditLog();
|
||||
row.setSpecType(specType);
|
||||
row.setSpecId(specId);
|
||||
row.setSpecTitle(specTitle);
|
||||
row.setIssueNumber(issueNumber);
|
||||
row.setActionType(actionType);
|
||||
row.setChangeSummary(MesXslFormulaSpecEditLogDiffUtil.buildSummary(actionType, items));
|
||||
row.setBeforeSnapshot(beforeSnapshot);
|
||||
row.setAfterSnapshot(afterSnapshot);
|
||||
row.setModifyTime(new Date());
|
||||
if (loginUser != null) {
|
||||
row.setModifyBy(loginUser.getUsername());
|
||||
row.setModifyByName(StringUtils.defaultIfBlank(loginUser.getRealname(), loginUser.getUsername()));
|
||||
} else {
|
||||
row.setModifyByName("未知");
|
||||
}
|
||||
row.setTenantId(tenantId);
|
||||
save(row);
|
||||
}
|
||||
|
||||
private static Object parseSnapshot(String snapshot) {
|
||||
if (StringUtils.isBlank(snapshot)) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(snapshot);
|
||||
}
|
||||
|
||||
private static String resolveFormulaTitle(MesXslFormulaSpec main) {
|
||||
if (main == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(main.getRubberCode())) {
|
||||
return main.getRubberCode();
|
||||
}
|
||||
if (StringUtils.isNotBlank(main.getSpecCode())) {
|
||||
return main.getSpecCode();
|
||||
}
|
||||
return main.getIssueNumber();
|
||||
}
|
||||
|
||||
private static String resolveMixingTitle(MesXslMixingSpecPage page) {
|
||||
if (page == null) {
|
||||
return null;
|
||||
}
|
||||
return StringUtils.defaultIfBlank(page.getSpecName(), page.getIssueNumber());
|
||||
}
|
||||
|
||||
private static LoginUser resolveLoginUser() {
|
||||
try {
|
||||
Object principal = SecurityUtils.getSubject().getPrincipal();
|
||||
if (principal instanceof LoginUser loginUser) {
|
||||
return loginUser;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 免密或未登录场景
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import org.jeecg.modules.xslmes.entity.MesXslMixingSpecMaterial;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslFormulaSpecLineMapper;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslFormulaSpecMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslEquipmentLedgerService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecEditLogService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecSettingService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslMixerMaterialKindCfgService;
|
||||
@@ -95,6 +96,9 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
@Resource
|
||||
private IMesXslMixerMaterialKindCfgService mesXslMixerMaterialKindCfgService;
|
||||
|
||||
@Resource
|
||||
private IMesXslFormulaSpecEditLogService mesXslFormulaSpecEditLogService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMain(MesXslFormulaSpec main, List<MesXslFormulaSpecLine> lineList) {
|
||||
@@ -105,21 +109,43 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
this.save(main);
|
||||
insertLines(main, lineList);
|
||||
refreshMainSummary(main.getId());
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】配合示方新增落库修改日志-----------
|
||||
MesXslFormulaSpec saved = getByIdWithLines(main.getId());
|
||||
if (saved != null) {
|
||||
mesXslFormulaSpecEditLogService.recordFormulaCreate(saved, saved.getLineList());
|
||||
}
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】配合示方新增落库修改日志-----------
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateMain(MesXslFormulaSpec main, List<MesXslFormulaSpecLine> lineList) {
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】配合示方编辑前抓取快照-----------
|
||||
MesXslFormulaSpec before = getByIdWithLines(main.getId());
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】配合示方编辑前抓取快照-----------
|
||||
fillMainDefaults(main);
|
||||
this.updateById(main);
|
||||
lineMapper.delete(new LambdaQueryWrapper<MesXslFormulaSpecLine>().eq(MesXslFormulaSpecLine::getFormulaSpecId, main.getId()));
|
||||
insertLines(main, lineList);
|
||||
refreshMainSummary(main.getId());
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】配合示方编辑落库修改日志-----------
|
||||
MesXslFormulaSpec after = getByIdWithLines(main.getId());
|
||||
if (after != null) {
|
||||
mesXslFormulaSpecEditLogService.recordFormulaUpdate(
|
||||
before, before == null ? null : before.getLineList(), after, after.getLineList());
|
||||
}
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】配合示方编辑落库修改日志-----------
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delMain(String id) {
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】配合示方删除落库修改日志-----------
|
||||
MesXslFormulaSpec before = getByIdWithLines(id);
|
||||
if (before != null) {
|
||||
mesXslFormulaSpecEditLogService.recordFormulaDelete(before, before.getLineList());
|
||||
}
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】配合示方删除落库修改日志-----------
|
||||
lineMapper.delete(new LambdaQueryWrapper<MesXslFormulaSpecLine>().eq(MesXslFormulaSpecLine::getFormulaSpecId, id));
|
||||
this.removeById(id);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslInspectMaintainItem;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslInspectMaintainItemMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslInspectMaintainItemService;
|
||||
@@ -36,7 +34,7 @@ public class MesXslInspectMaintainItemServiceImpl
|
||||
if (oConvertUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslInspectMaintainItem> w = new LambdaQueryWrapper<>();
|
||||
w.eq(column, value.trim());
|
||||
w.and(
|
||||
@@ -53,26 +51,5 @@ public class MesXslInspectMaintainItemServiceImpl
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslInspectMaintainItem context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260519 for:【MES】点检及保养项目:项目名称/编号同租户不可重复-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslManufacturer;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslManufacturerMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslManufacturerService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslManufacturerServiceImpl extends ServiceImpl<MesXslManufacture
|
||||
if (oConvertUtils.isEmpty(manufacturerName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslManufacturer> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslManufacturer::getManufacturerName, manufacturerName.trim());
|
||||
w.and(
|
||||
@@ -39,25 +37,5 @@ public class MesXslManufacturerServiceImpl extends ServiceImpl<MesXslManufacture
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslManufacturer context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260515 for:【MES】厂家名称同租户不可重复;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
@@ -13,10 +12,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
|
||||
import org.jeecg.modules.system.entity.SysCategory;
|
||||
import org.jeecg.modules.system.entity.SysDict;
|
||||
@@ -24,6 +20,7 @@ import org.jeecg.modules.system.entity.SysDictItem;
|
||||
import org.jeecg.modules.system.service.ISysCategoryService;
|
||||
import org.jeecg.modules.system.service.ISysDictItemService;
|
||||
import org.jeecg.modules.system.service.ISysDictService;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixerMaterialKindCfg;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslMixerMaterialKindCfgMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslMixerMaterialKindCfgService;
|
||||
@@ -73,7 +70,7 @@ public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
if (lines == null || lines.isEmpty()) {
|
||||
throw new JeecgBootException("请至少维护一条种类配置明细");
|
||||
}
|
||||
Integer resolvedTenantId = resolveTenantId(tenantId);
|
||||
Integer resolvedTenantId = MesXslTenantUtils.resolveTenantId(tenantId);
|
||||
Set<String> kindKeys = new HashSet<>();
|
||||
Set<String> categoryRefIds = new HashSet<>();
|
||||
for (MesXslMixerMaterialKindCfg line : lines) {
|
||||
@@ -124,7 +121,7 @@ public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
cfg.setKindKey(item.getItemValue());
|
||||
cfg.setKindName(item.getItemText());
|
||||
cfg.setPriority(item.getSortOrder() != null ? item.getSortOrder() : priorityBase + index * 10);
|
||||
cfg.setTenantId(resolveTenantId(tenantId));
|
||||
cfg.setTenantId(MesXslTenantUtils.resolveTenantId(tenantId));
|
||||
result.add(cfg);
|
||||
index++;
|
||||
}
|
||||
@@ -183,7 +180,7 @@ public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
cfg.setKindKey(refCode);
|
||||
cfg.setKindName(category.getName());
|
||||
cfg.setPriority(priorityBase + i * 10);
|
||||
cfg.setTenantId(resolveTenantId(tenantId));
|
||||
cfg.setTenantId(MesXslTenantUtils.resolveTenantId(tenantId));
|
||||
result.add(cfg);
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
@@ -197,7 +194,7 @@ public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
qw.eq(MesXslMixerMaterialKindCfg::getSourceType, sourceType);
|
||||
qw.eq(MesXslMixerMaterialKindCfg::getSourceRootCode, sourceRootCode);
|
||||
qw.and(q -> q.eq(MesXslMixerMaterialKindCfg::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslMixerMaterialKindCfg::getDelFlag));
|
||||
Integer resolvedTenantId = resolveTenantId(tenantId);
|
||||
Integer resolvedTenantId = MesXslTenantUtils.resolveTenantId(tenantId);
|
||||
if (resolvedTenantId != null && MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||
qw.eq(MesXslMixerMaterialKindCfg::getTenantId, resolvedTenantId);
|
||||
}
|
||||
@@ -246,7 +243,7 @@ public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
|
||||
@Override
|
||||
public void checkDuplicate(MesXslMixerMaterialKindCfg line, String excludeId) {
|
||||
Integer tenantId = resolveTenantId(line.getTenantId());
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(line.getTenantId());
|
||||
LambdaQueryWrapper<MesXslMixerMaterialKindCfg> kindQw = new LambdaQueryWrapper<>();
|
||||
kindQw.eq(MesXslMixerMaterialKindCfg::getKindKey, line.getKindKey().trim());
|
||||
kindQw.and(q -> q.eq(MesXslMixerMaterialKindCfg::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslMixerMaterialKindCfg::getDelFlag));
|
||||
@@ -274,30 +271,6 @@ public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(Integer tenantId) {
|
||||
if (tenantId != null) {
|
||||
return tenantId;
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
if (request != null) {
|
||||
ts = TokenUtils.getTenantIdByRequest(request);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置展开与批量保存-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A52】密炼物料种类配置关联解析-----------
|
||||
@@ -307,7 +280,7 @@ public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
public MesXslMixerMaterialKindLookupVO loadKindLookup(Integer tenantId) {
|
||||
LambdaQueryWrapper<MesXslMixerMaterialKindCfg> qw = new LambdaQueryWrapper<>();
|
||||
qw.and(q -> q.eq(MesXslMixerMaterialKindCfg::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslMixerMaterialKindCfg::getDelFlag));
|
||||
Integer resolvedTenantId = resolveTenantId(tenantId);
|
||||
Integer resolvedTenantId = MesXslTenantUtils.resolveTenantId(tenantId);
|
||||
if (resolvedTenantId != null && MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||
qw.eq(MesXslMixerMaterialKindCfg::getTenantId, resolvedTenantId);
|
||||
}
|
||||
|
||||
@@ -38,8 +38,10 @@ import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecMapper;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecMaterialMapper;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecStepMapper;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecTcuMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecEditLogService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslMixingSpecService;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -74,6 +76,9 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
@Resource
|
||||
private ISysCategoryService sysCategoryService;
|
||||
|
||||
@Resource
|
||||
private IMesXslFormulaSpecEditLogService mesXslFormulaSpecEditLogService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMain(
|
||||
@@ -93,6 +98,11 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
trace.step("saveMain", "mainId", main.getId());
|
||||
saveChildren(main.getId(), materialList, stepList, downStepList, tcuList, trace);
|
||||
trace.finish();
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】混炼示方新增落库修改日志-----------
|
||||
// 直接用已落库的入参构建快照,避免二次全量 queryPageById() 查询
|
||||
mesXslFormulaSpecEditLogService.recordMixingCreate(
|
||||
buildPageFromInput(main, materialList, stepList, downStepList, tcuList));
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】混炼示方新增落库修改日志-----------
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子表新增保存-----------
|
||||
}
|
||||
|
||||
@@ -109,6 +119,9 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
trace.logPayloadSize(materialList, stepList, downStepList, tcuList);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子表编辑保存-----------
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】混炼示方编辑前抓取快照-----------
|
||||
MesXslMixingSpecPage before = queryPageById(main.getId());
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】混炼示方编辑前抓取快照-----------
|
||||
normalizeMain(main);
|
||||
trace.step("normalizeMain");
|
||||
this.updateById(main);
|
||||
@@ -116,6 +129,11 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
clearChildren(main.getId(), trace);
|
||||
saveChildren(main.getId(), materialList, stepList, downStepList, tcuList, trace);
|
||||
trace.finish();
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】混炼示方编辑落库修改日志-----------
|
||||
// 直接用已落库的入参构建 after 快照,避免二次全量 queryPageById() 查询(节省 5 次 SELECT)
|
||||
mesXslFormulaSpecEditLogService.recordMixingUpdate(before,
|
||||
buildPageFromInput(main, materialList, stepList, downStepList, tcuList));
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】混炼示方编辑落库修改日志-----------
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子表编辑保存-----------
|
||||
}
|
||||
|
||||
@@ -150,6 +168,14 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
if (CollectionUtils.isEmpty(mains)) {
|
||||
return;
|
||||
}
|
||||
//update-begin---author:cursor ---date:20260526 for:【配方日志查询】混炼示方删除落库修改日志-----------
|
||||
for (String id : ids) {
|
||||
MesXslMixingSpecPage before = queryPageById(id);
|
||||
if (before != null) {
|
||||
mesXslFormulaSpecEditLogService.recordMixingDelete(before);
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260526 for:【配方日志查询】混炼示方删除落库修改日志-----------
|
||||
clearChildrenBatch(ids);
|
||||
this.removeByIds(ids);
|
||||
Set<String> specNames = new LinkedHashSet<>();
|
||||
@@ -814,4 +840,21 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
this.update(wrapper);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260526 for:【XSLMES-20260526-A61】混炼示方密炼PS审批联动同步审批人-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260527 for:【配方日志查询】从入参直接构建快照,避免二次查库-----------
|
||||
private MesXslMixingSpecPage buildPageFromInput(
|
||||
MesXslMixingSpec main,
|
||||
List<MesXslMixingSpecMaterial> materialList,
|
||||
List<MesXslMixingSpecStep> stepList,
|
||||
List<MesXslMixingSpecDownStep> downStepList,
|
||||
List<MesXslMixingSpecTcu> tcuList) {
|
||||
MesXslMixingSpecPage page = new MesXslMixingSpecPage();
|
||||
BeanUtils.copyProperties(main, page);
|
||||
page.setMaterialList(materialList != null ? materialList : new ArrayList<>());
|
||||
page.setStepList(stepList != null ? stepList : new ArrayList<>());
|
||||
page.setDownStepList(downStepList != null ? downStepList : new ArrayList<>());
|
||||
page.setTcuList(tcuList != null ? tcuList : new ArrayList<>());
|
||||
return page;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260527 for:【配方日志查询】从入参直接构建快照,避免二次查库-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslProcessOperation;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslProcessOperationMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslProcessOperationService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslProcessOperationServiceImpl extends ServiceImpl<MesXslProcess
|
||||
if (oConvertUtils.isEmpty(operationCode)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslProcessOperation> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslProcessOperation::getOperationCode, operationCode.trim());
|
||||
// 仅与「未删除」数据判重:del_flag=0 或历史空值视为正常行,排除已删除(del_flag=1)
|
||||
@@ -41,25 +39,5 @@ public class MesXslProcessOperationServiceImpl extends ServiceImpl<MesXslProcess
|
||||
}
|
||||
|
||||
/** 与 MybatisInterceptor 注入 tenant_id 的取值顺序尽量一致,避免前端未传 tenantId 时查不到已存在数据 */
|
||||
private static Integer resolveTenantId(MesXslProcessOperation context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260514 for:【MES】工序编码同租户唯一;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixingSpec;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecEditLogService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslMixingSpecService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRackTrainCountSettingService;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslRackTrainCountSettingVO;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class MesXslRackTrainCountSettingServiceImpl implements IMesXslRackTrainCountSettingService {
|
||||
|
||||
private static final String STATUS_SET = "1";
|
||||
private static final String STATUS_UNSET = "0";
|
||||
|
||||
private final IMesXslMixingSpecService mixingSpecService;
|
||||
private final IMesXslFormulaSpecEditLogService mesXslFormulaSpecEditLogService;
|
||||
|
||||
public MesXslRackTrainCountSettingServiceImpl(
|
||||
IMesXslMixingSpecService mixingSpecService,
|
||||
IMesXslFormulaSpecEditLogService mesXslFormulaSpecEditLogService) {
|
||||
this.mixingSpecService = mixingSpecService;
|
||||
this.mesXslFormulaSpecEditLogService = mesXslFormulaSpecEditLogService;
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定分页查询混炼示方-----------
|
||||
@Override
|
||||
public IPage<MesXslRackTrainCountSettingVO> queryPage(
|
||||
Integer pageNo, Integer pageSize, MesXslRackTrainCountSettingVO query, String keyword) {
|
||||
LambdaQueryWrapper<MesXslMixingSpec> wrapper = buildQueryWrapper(query, keyword);
|
||||
wrapper.orderByDesc(MesXslMixingSpec::getUpdateTime).orderByDesc(MesXslMixingSpec::getCreateTime);
|
||||
Page<MesXslMixingSpec> page = mixingSpecService.page(new Page<>(pageNo, pageSize), wrapper);
|
||||
return page.convert(this::toVo);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定分页查询混炼示方-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定按ID查询-----------
|
||||
@Override
|
||||
public MesXslRackTrainCountSettingVO queryById(String id) {
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return null;
|
||||
}
|
||||
MesXslMixingSpec spec = mixingSpecService.getById(id);
|
||||
if (spec == null || isDeleted(spec)) {
|
||||
return null;
|
||||
}
|
||||
return toVo(spec);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定按ID查询-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定保存回写混炼示方-----------
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveSetting(MesXslRackTrainCountSettingVO setting) {
|
||||
if (setting == null) {
|
||||
throw new IllegalArgumentException("参数不能为空");
|
||||
}
|
||||
String mixingSpecId = StringUtils.isNotBlank(setting.getMixingSpecId()) ? setting.getMixingSpecId() : setting.getId();
|
||||
if (StringUtils.isBlank(mixingSpecId)) {
|
||||
throw new IllegalArgumentException("混炼示方ID不能为空");
|
||||
}
|
||||
MesXslMixingSpec existing = mixingSpecService.getById(mixingSpecId);
|
||||
if (existing == null || isDeleted(existing)) {
|
||||
throw new IllegalArgumentException("混炼示方不存在或已删除");
|
||||
}
|
||||
if (setting.getSetTrainCount() != null && setting.getSetTrainCount() < 0) {
|
||||
throw new IllegalArgumentException("设定车数不能小于0");
|
||||
}
|
||||
//update-begin---author:cursor ---date:20260528 for:【XSLMES-20260528-A03】架子车数设定保存写入配方日志-----------
|
||||
MesXslMixingSpecPage before = mixingSpecService.queryPageById(mixingSpecId);
|
||||
if (before == null) {
|
||||
throw new IllegalArgumentException("混炼示方不存在或已删除");
|
||||
}
|
||||
Integer newSetTrainCount = setting.getSetTrainCount();
|
||||
if (Objects.equals(before.getSetTrainCount(), newSetTrainCount)) {
|
||||
return;
|
||||
}
|
||||
MesXslMixingSpec update = new MesXslMixingSpec();
|
||||
update.setId(mixingSpecId);
|
||||
update.setSetTrainCount(newSetTrainCount);
|
||||
mixingSpecService.updateById(update);
|
||||
MesXslMixingSpecPage after = copyMixingPageWithSetTrainCount(before, newSetTrainCount);
|
||||
mesXslFormulaSpecEditLogService.recordMixingUpdate(before, after);
|
||||
//update-end---author:cursor ---date:20260528 for:【XSLMES-20260528-A03】架子车数设定保存写入配方日志-----------
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【XSLMES-20260528-A01】架子车数设定保存回写混炼示方-----------
|
||||
|
||||
private LambdaQueryWrapper<MesXslMixingSpec> buildQueryWrapper(MesXslRackTrainCountSettingVO query, String keyword) {
|
||||
LambdaQueryWrapper<MesXslMixingSpec> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MesXslMixingSpec::getDelFlag, 0);
|
||||
if (query != null) {
|
||||
if (query.getTenantId() != null) {
|
||||
wrapper.eq(MesXslMixingSpec::getTenantId, query.getTenantId());
|
||||
}
|
||||
if (StringUtils.isNotBlank(query.getSpecCode())) {
|
||||
wrapper.like(MesXslMixingSpec::getSpecName, query.getSpecCode().trim());
|
||||
}
|
||||
if (StringUtils.isNotBlank(query.getMixingSpecId())) {
|
||||
wrapper.eq(MesXslMixingSpec::getId, query.getMixingSpecId().trim());
|
||||
}
|
||||
if (StringUtils.isNotBlank(query.getMachineId())) {
|
||||
wrapper.eq(MesXslMixingSpec::getMachineId, query.getMachineId().trim());
|
||||
}
|
||||
if (StringUtils.isNotBlank(query.getMachineName())) {
|
||||
wrapper.like(MesXslMixingSpec::getMachineName, query.getMachineName().trim());
|
||||
}
|
||||
if (STATUS_SET.equals(query.getStatus())) {
|
||||
wrapper.isNotNull(MesXslMixingSpec::getSetTrainCount);
|
||||
} else if (STATUS_UNSET.equals(query.getStatus())) {
|
||||
wrapper.isNull(MesXslMixingSpec::getSetTrainCount);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotBlank(keyword)) {
|
||||
String kw = keyword.trim();
|
||||
wrapper.and(w -> w.like(MesXslMixingSpec::getSpecName, kw)
|
||||
.or()
|
||||
.like(MesXslMixingSpec::getMachineName, kw)
|
||||
.or()
|
||||
.like(MesXslMixingSpec::getIssueNumber, kw));
|
||||
}
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private MesXslRackTrainCountSettingVO toVo(MesXslMixingSpec spec) {
|
||||
MesXslRackTrainCountSettingVO vo = new MesXslRackTrainCountSettingVO();
|
||||
vo.setId(spec.getId());
|
||||
vo.setMixingSpecId(spec.getId());
|
||||
vo.setTenantId(spec.getTenantId());
|
||||
vo.setSpecCode(spec.getSpecName());
|
||||
vo.setMachineId(spec.getMachineId());
|
||||
vo.setMachineName(spec.getMachineName());
|
||||
vo.setSetTrainCount(spec.getSetTrainCount());
|
||||
vo.setStatus(resolveStatus(spec.getSetTrainCount()));
|
||||
vo.setCreateBy(spec.getCreateBy());
|
||||
vo.setCreateTime(spec.getCreateTime());
|
||||
vo.setUpdateBy(spec.getUpdateBy());
|
||||
vo.setUpdateTime(spec.getUpdateTime());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private String resolveStatus(Integer setTrainCount) {
|
||||
return setTrainCount != null ? STATUS_SET : STATUS_UNSET;
|
||||
}
|
||||
|
||||
private boolean isDeleted(MesXslMixingSpec spec) {
|
||||
return spec.getDelFlag() != null && spec.getDelFlag() != 0;
|
||||
}
|
||||
|
||||
/** 基于变更前快照复制 after,仅更新设定车数,避免二次全量查子表 */
|
||||
private static MesXslMixingSpecPage copyMixingPageWithSetTrainCount(MesXslMixingSpecPage before, Integer setTrainCount) {
|
||||
MesXslMixingSpecPage after = new MesXslMixingSpecPage();
|
||||
BeanUtils.copyProperties(before, after);
|
||||
after.setMaterialList(before.getMaterialList());
|
||||
after.setStepList(before.getStepList());
|
||||
after.setDownStepList(before.getDownStepList());
|
||||
after.setTcuList(before.getTcuList());
|
||||
after.setSetTrainCount(setTrainCount);
|
||||
return after;
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,10 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestDataPoint;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslRubberQuickTestDataPointMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestDataPointService;
|
||||
@@ -25,7 +23,7 @@ public class MesXslRubberQuickTestDataPointServiceImpl
|
||||
if (oConvertUtils.isEmpty(pointName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslRubberQuickTestDataPoint> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslRubberQuickTestDataPoint::getPointName, pointName.trim());
|
||||
w.and(
|
||||
@@ -70,25 +68,5 @@ public class MesXslRubberQuickTestDataPointServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslRubberQuickTestDataPoint context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260522 for:【MES】胶料快检数据点名称同租户唯一-----------
|
||||
}
|
||||
|
||||
@@ -12,18 +12,16 @@ import java.util.Collection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestMethod;
|
||||
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestMethodLine;
|
||||
@@ -66,7 +64,7 @@ public class MesXslRubberQuickTestMethodServiceImpl
|
||||
|
||||
public String generateNextMethodCode(MesXslRubberQuickTestMethod context) {
|
||||
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
|
||||
Integer max = baseMapper.selectMaxNumericMethodCode(tenantId);
|
||||
|
||||
@@ -94,7 +92,7 @@ public class MesXslRubberQuickTestMethodServiceImpl
|
||||
|
||||
}
|
||||
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
|
||||
LambdaQueryWrapper<MesXslRubberQuickTestMethod> w = new LambdaQueryWrapper<>();
|
||||
|
||||
@@ -286,45 +284,6 @@ public class MesXslRubberQuickTestMethodServiceImpl
|
||||
|
||||
|
||||
|
||||
private static Integer resolveTenantId(MesXslRubberQuickTestMethod context) {
|
||||
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
|
||||
return context.getTenantId();
|
||||
|
||||
}
|
||||
|
||||
String ts = TenantContext.getTenant();
|
||||
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
|
||||
try {
|
||||
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
return Integer.parseInt(ts.trim());
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//update-end---author:jiangxh ---date:20260522 for:【MES】胶料快检实验方法编号001递增、名称同租户唯一、主子保存-----------
|
||||
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.io.Serializable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.mes.material.entity.MesMaterial;
|
||||
import org.jeecg.modules.mes.material.service.IMesMaterialService;
|
||||
import org.jeecg.modules.xslmes.common.XslMesBizConstants;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestMethod;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecord;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecordLine;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestStd;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestStdLine;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestType;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslRubberQuickTestRecordLineMapper;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslRubberQuickTestRecordMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestMethodService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestRecordService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestStdService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestTypeService;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslRubberQuickTestRecordBatchFromMaterialVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Service
|
||||
public class MesXslRubberQuickTestRecordServiceImpl
|
||||
extends ServiceImpl<MesXslRubberQuickTestRecordMapper, MesXslRubberQuickTestRecord>
|
||||
implements IMesXslRubberQuickTestRecordService {
|
||||
|
||||
private static final String STD_ENABLE_IN_USE = "1";
|
||||
|
||||
@Autowired
|
||||
private MesXslRubberQuickTestRecordLineMapper mesXslRubberQuickTestRecordLineMapper;
|
||||
|
||||
@Autowired
|
||||
private IMesXslRubberQuickTestStdService mesXslRubberQuickTestStdService;
|
||||
|
||||
@Autowired
|
||||
private IMesXslRubberQuickTestMethodService mesXslRubberQuickTestMethodService;
|
||||
|
||||
@Autowired
|
||||
private IMesMaterialService mesMaterialService;
|
||||
|
||||
@Autowired
|
||||
private IMesXslRubberQuickTestTypeService mesXslRubberQuickTestTypeService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMain(MesXslRubberQuickTestRecord main, List<MesXslRubberQuickTestRecordLine> lineList) {
|
||||
if (oConvertUtils.isEmpty(main.getRecordNo())) {
|
||||
main.setRecordNo(generateRecordNo(main));
|
||||
}
|
||||
this.save(main);
|
||||
insertLines(main.getId(), lineList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateMain(MesXslRubberQuickTestRecord main, List<MesXslRubberQuickTestRecordLine> lineList) {
|
||||
if (oConvertUtils.isNotEmpty(main.getId())) {
|
||||
MesXslRubberQuickTestRecord old = this.getById(main.getId());
|
||||
if (old != null) {
|
||||
main.setRecordNo(old.getRecordNo());
|
||||
}
|
||||
}
|
||||
this.updateById(main);
|
||||
mesXslRubberQuickTestRecordLineMapper.delete(
|
||||
new LambdaQueryWrapper<MesXslRubberQuickTestRecordLine>()
|
||||
.eq(MesXslRubberQuickTestRecordLine::getRecordId, main.getId()));
|
||||
insertLines(main.getId(), lineList);
|
||||
}
|
||||
|
||||
private void insertLines(String recordId, List<MesXslRubberQuickTestRecordLine> lineList) {
|
||||
if (CollectionUtils.isEmpty(lineList)) {
|
||||
return;
|
||||
}
|
||||
int sort = 0;
|
||||
for (MesXslRubberQuickTestRecordLine line : lineList) {
|
||||
line.setId(null);
|
||||
line.setRecordId(recordId);
|
||||
line.setSortNo(sort++);
|
||||
mesXslRubberQuickTestRecordLineMapper.insert(line);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delMain(String id) {
|
||||
mesXslRubberQuickTestRecordLineMapper.delete(
|
||||
new LambdaQueryWrapper<MesXslRubberQuickTestRecordLine>().eq(MesXslRubberQuickTestRecordLine::getRecordId, id));
|
||||
this.removeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delBatchMain(Collection<? extends Serializable> idList) {
|
||||
for (Serializable id : idList) {
|
||||
delMain(id.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MesXslRubberQuickTestRecordLine> selectLinesByRecordId(String recordId) {
|
||||
return mesXslRubberQuickTestRecordLineMapper.selectList(
|
||||
new LambdaQueryWrapper<MesXslRubberQuickTestRecordLine>()
|
||||
.eq(MesXslRubberQuickTestRecordLine::getRecordId, recordId)
|
||||
.orderByAsc(MesXslRubberQuickTestRecordLine::getSortNo));
|
||||
}
|
||||
|
||||
//update-begin---author:jiangxh ---date:20260528 for:【MES】胶料快检记录单号JL+日期+4位流水自动生成-----------
|
||||
@Override
|
||||
public String generateRecordNo(MesXslRubberQuickTestRecord context) {
|
||||
String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||
String prefix = "JL" + dateStr;
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
|
||||
LambdaQueryWrapper<MesXslRubberQuickTestRecord> qw = new LambdaQueryWrapper<>();
|
||||
qw.likeRight(MesXslRubberQuickTestRecord::getRecordNo, prefix);
|
||||
qw.and(q -> q.eq(MesXslRubberQuickTestRecord::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslRubberQuickTestRecord::getDelFlag));
|
||||
if (tenantId != null) {
|
||||
qw.eq(MesXslRubberQuickTestRecord::getTenantId, tenantId);
|
||||
}
|
||||
qw.select(MesXslRubberQuickTestRecord::getRecordNo);
|
||||
qw.orderByDesc(MesXslRubberQuickTestRecord::getRecordNo);
|
||||
qw.last("LIMIT 1");
|
||||
|
||||
MesXslRubberQuickTestRecord last = this.getOne(qw, false);
|
||||
int nextSeq = 1;
|
||||
if (last != null
|
||||
&& oConvertUtils.isNotEmpty(last.getRecordNo())
|
||||
&& last.getRecordNo().length() >= prefix.length() + 4) {
|
||||
String suffix = last.getRecordNo().substring(prefix.length());
|
||||
try {
|
||||
nextSeq = Integer.parseInt(suffix) + 1;
|
||||
} catch (NumberFormatException ignored) {
|
||||
nextSeq = (int) this.count(qw) + 1;
|
||||
}
|
||||
}
|
||||
return prefix + String.format("%04d", nextSeq);
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslRubberQuickTestRecord context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260528 for:【MES】胶料快检记录单号JL+日期+4位流水自动生成-----------
|
||||
|
||||
//update-begin---author:jiangxh ---date:20260525 for:【MES】胶料信息多选按实验标准批量生成快检记录-----------
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<String> batchFromMaterial(MesXslRubberQuickTestRecordBatchFromMaterialVO vo) {
|
||||
if (vo == null || CollectionUtils.isEmpty(vo.getMaterialIds())) {
|
||||
throw new JeecgBootException("请至少选择一条胶料信息");
|
||||
}
|
||||
List<String> createdIds = new ArrayList<>();
|
||||
for (String materialId : vo.getMaterialIds()) {
|
||||
if (oConvertUtils.isEmpty(materialId)) {
|
||||
continue;
|
||||
}
|
||||
String mid = materialId.trim();
|
||||
MesMaterial material = mesMaterialService.getById(mid);
|
||||
if (material == null) {
|
||||
throw new JeecgBootException("胶料不存在:" + mid);
|
||||
}
|
||||
MesXslRubberQuickTestStd std = findApprovedStdForMaterial(mid);
|
||||
if (std == null) {
|
||||
throw new JeecgBootException(
|
||||
"胶料【" + material.getMaterialName() + "】未找到已启用且已批准的实验标准,请先在胶料快检实验标准中维护");
|
||||
}
|
||||
List<MesXslRubberQuickTestStdLine> stdLines = mesXslRubberQuickTestStdService.selectLinesByStdId(std.getId());
|
||||
if (CollectionUtils.isEmpty(stdLines)) {
|
||||
throw new JeecgBootException("胶料【" + material.getMaterialName() + "】关联的实验标准无明细数据");
|
||||
}
|
||||
MesXslRubberQuickTestRecord main = buildMainFromMaterial(material, std);
|
||||
List<MesXslRubberQuickTestRecordLine> recordLines = new ArrayList<>();
|
||||
for (MesXslRubberQuickTestStdLine stdLine : stdLines) {
|
||||
MesXslRubberQuickTestRecordLine rl = new MesXslRubberQuickTestRecordLine();
|
||||
rl.setDataPointId(stdLine.getDataPointId());
|
||||
rl.setInspectItem(stdLine.getPointName());
|
||||
rl.setLowerLimit(stdLine.getLowerLimit());
|
||||
rl.setUpperLimit(stdLine.getUpperLimit());
|
||||
recordLines.add(rl);
|
||||
}
|
||||
saveMain(main, recordLines);
|
||||
createdIds.add(main.getId());
|
||||
}
|
||||
if (createdIds.isEmpty()) {
|
||||
throw new JeecgBootException("未生成任何快检记录");
|
||||
}
|
||||
return createdIds;
|
||||
}
|
||||
|
||||
private MesXslRubberQuickTestStd findApprovedStdForMaterial(String materialId) {
|
||||
LambdaQueryWrapper<MesXslRubberQuickTestStd> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslRubberQuickTestStd::getRubberMaterialId, materialId);
|
||||
w.eq(MesXslRubberQuickTestStd::getEnableStatus, STD_ENABLE_IN_USE);
|
||||
w.eq(MesXslRubberQuickTestStd::getAuditStatus, XslMesBizConstants.RUBBER_QUICK_TEST_STD_AUDIT_APPROVED);
|
||||
w.and(q -> q.eq(MesXslRubberQuickTestStd::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslRubberQuickTestStd::getDelFlag));
|
||||
w.orderByDesc(MesXslRubberQuickTestStd::getCreateTime);
|
||||
w.last("LIMIT 1");
|
||||
return mesXslRubberQuickTestStdService.getOne(w, false);
|
||||
}
|
||||
|
||||
private MesXslRubberQuickTestRecord buildMainFromMaterial(MesMaterial material, MesXslRubberQuickTestStd std) {
|
||||
MesXslRubberQuickTestRecord main = new MesXslRubberQuickTestRecord();
|
||||
main.setRubberMaterialId(material.getId());
|
||||
main.setRubberMaterialName(material.getMaterialName());
|
||||
main.setStdId(std.getId());
|
||||
if (oConvertUtils.isNotEmpty(std.getTestMethodId())) {
|
||||
MesXslRubberQuickTestMethod method = mesXslRubberQuickTestMethodService.getById(std.getTestMethodId());
|
||||
if (method != null && oConvertUtils.isNotEmpty(method.getQuickTestTypeId())) {
|
||||
fillQuickTestType(main, method.getQuickTestTypeId());
|
||||
}
|
||||
}
|
||||
return main;
|
||||
}
|
||||
|
||||
private void fillQuickTestType(MesXslRubberQuickTestRecord main, String typeId) {
|
||||
MesXslRubberQuickTestType type = mesXslRubberQuickTestTypeService.getById(typeId);
|
||||
if (type != null) {
|
||||
main.setQuickTestTypeId(type.getId());
|
||||
main.setQuickTestTypeName(type.getTypeName());
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260525 for:【MES】胶料信息多选按实验标准批量生成快检记录-----------
|
||||
}
|
||||
@@ -6,13 +6,11 @@ import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.XslMesBizConstants;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestStd;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestStdLine;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslRubberQuickTestStdLineMapper;
|
||||
@@ -37,7 +35,7 @@ public class MesXslRubberQuickTestStdServiceImpl
|
||||
if (oConvertUtils.isEmpty(stdName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslRubberQuickTestStd> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslRubberQuickTestStd::getStdName, stdName.trim());
|
||||
w.and(q -> q.eq(MesXslRubberQuickTestStd::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslRubberQuickTestStd::getDelFlag));
|
||||
@@ -158,25 +156,5 @@ public class MesXslRubberQuickTestStdServiceImpl
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260525 for:【MES】原材料检验标准密炼PS批准时关联实验标准置已批准-----------
|
||||
|
||||
private static Integer resolveTenantId(MesXslRubberQuickTestStd context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260525 for:【MES】胶料快检实验标准名称同租户唯一、主子保存-----------
|
||||
}
|
||||
|
||||
@@ -2,12 +2,10 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestType;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslRubberQuickTestTypeMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRubberQuickTestTypeService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslRubberQuickTestTypeServiceImpl
|
||||
//update-begin---author:jiangxh ---date:20260522 for:【MES】胶料快检实验类型编号001递增、名称同租户唯一-----------
|
||||
@Override
|
||||
public String generateNextTypeCode(MesXslRubberQuickTestType context) {
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
Integer max = baseMapper.selectMaxNumericTypeCode(tenantId);
|
||||
int next = (max == null ? 0 : max) + 1;
|
||||
if (next > 999) {
|
||||
@@ -36,7 +34,7 @@ public class MesXslRubberQuickTestTypeServiceImpl
|
||||
if (oConvertUtils.isEmpty(typeName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslRubberQuickTestType> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslRubberQuickTestType::getTypeName, typeName.trim());
|
||||
w.and(
|
||||
@@ -84,25 +82,5 @@ public class MesXslRubberQuickTestTypeServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslRubberQuickTestType context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260522 for:【MES】胶料快检实验类型编号001递增、名称同租户唯一-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslSparePart;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslSparePartMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslSparePartService;
|
||||
@@ -21,7 +19,7 @@ public class MesXslSparePartServiceImpl extends ServiceImpl<MesXslSparePartMappe
|
||||
if (oConvertUtils.isEmpty(sparePartName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslSparePart> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslSparePart::getSparePartName, sparePartName.trim());
|
||||
w.and(
|
||||
@@ -38,25 +36,5 @@ public class MesXslSparePartServiceImpl extends ServiceImpl<MesXslSparePartMappe
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(MesXslSparePart context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260515 for:【MES】备品件名称同租户不可重复;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslSparePartsCategory;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslSparePartsCategoryMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslSparePartsCategoryService;
|
||||
@@ -22,7 +20,7 @@ public class MesXslSparePartsCategoryServiceImpl extends ServiceImpl<MesXslSpare
|
||||
if (oConvertUtils.isEmpty(categoryName)) {
|
||||
return false;
|
||||
}
|
||||
Integer tenantId = resolveTenantId(context);
|
||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(context != null ? context.getTenantId() : null);
|
||||
LambdaQueryWrapper<MesXslSparePartsCategory> w = new LambdaQueryWrapper<>();
|
||||
w.eq(MesXslSparePartsCategory::getCategoryName, categoryName.trim());
|
||||
w.and(
|
||||
@@ -60,25 +58,5 @@ public class MesXslSparePartsCategoryServiceImpl extends ServiceImpl<MesXslSpare
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260515 for:【MES】备品件类别按名称+租户查询单条,供备品件信息导入解析-----------
|
||||
|
||||
private static Integer resolveTenantId(MesXslSparePartsCategory context) {
|
||||
if (context != null && context.getTenantId() != null) {
|
||||
return context.getTenantId();
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
ts = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:jiangxh ---date:20260515 for:【MES】备品件类别名称同租户不可重复;仅统计未删除(del_flag=0 或 null)-----------
|
||||
}
|
||||
|
||||
@@ -5,14 +5,12 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.system.service.ISysCategoryService;
|
||||
import org.jeecg.modules.xslmes.config.XslMesWarehouseAreaCapacityProperties;
|
||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslWarehouseAreaCapacityCfg;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslWarehouseAreaCapacityCfgMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaCapacityCfgService;
|
||||
@@ -51,14 +49,7 @@ public class MesXslWarehouseAreaCapacityCfgServiceImpl extends ServiceImpl<MesXs
|
||||
}
|
||||
|
||||
private int resolveTenantIdBestEffort() {
|
||||
String t = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(t)) {
|
||||
try {
|
||||
t = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return oConvertUtils.getInt(t, 0);
|
||||
return MesXslTenantUtils.resolveTenantIdBestEffort();
|
||||
}
|
||||
|
||||
/** 页面保存必须用有效租户 */
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.jeecg.modules.xslmes.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 配方日志字段级对比项
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "配方日志对比项")
|
||||
public class MesXslFormulaSpecEditChangeItemVO {
|
||||
|
||||
@Schema(description = "所属区块 main/line/material/step/downStep/tcu")
|
||||
private String section;
|
||||
|
||||
@Schema(description = "区块中文名")
|
||||
private String sectionLabel;
|
||||
|
||||
@Schema(description = "字段键")
|
||||
private String fieldKey;
|
||||
|
||||
@Schema(description = "字段中文名")
|
||||
private String fieldLabel;
|
||||
|
||||
@Schema(description = "变更前值")
|
||||
private String beforeValue;
|
||||
|
||||
@Schema(description = "变更后值")
|
||||
private String afterValue;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.jeecg.modules.xslmes.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecEditLog;
|
||||
|
||||
/**
|
||||
* 配方日志详情(含对比项)
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "配方日志详情")
|
||||
public class MesXslFormulaSpecEditLogDetailVO extends MesXslFormulaSpecEditLog {
|
||||
|
||||
@Schema(description = "变更前快照对象")
|
||||
private Object beforeData;
|
||||
|
||||
@Schema(description = "变更后快照对象")
|
||||
private Object afterData;
|
||||
|
||||
@Schema(description = "字段级对比项")
|
||||
private List<MesXslFormulaSpecEditChangeItemVO> changeItems;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.jeecg.modules.xslmes.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* 架子车数设定(数据来源:混炼示方,不落独立业务表)
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "架子车数设定")
|
||||
public class MesXslRackTrainCountSettingVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键(同混炼示方ID)")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Integer tenantId;
|
||||
|
||||
@Schema(description = "示方编号")
|
||||
private String specCode;
|
||||
|
||||
@Schema(description = "混炼示方ID")
|
||||
private String mixingSpecId;
|
||||
|
||||
@Schema(description = "机台ID")
|
||||
private String machineId;
|
||||
|
||||
@Schema(description = "机台名称")
|
||||
private String machineName;
|
||||
|
||||
@Schema(description = "设定车数")
|
||||
private Integer setTrainCount;
|
||||
|
||||
@Schema(description = "状态:1已设定 0未设定")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "修改时间")
|
||||
private Date updateTime;
|
||||
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "创建时间")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.jeecg.modules.xslmes.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* 从胶料信息批量生成快检记录(共用主表字段)
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "胶料快检记录-从胶料批量生成")
|
||||
public class MesXslRubberQuickTestRecordBatchFromMaterialVO {
|
||||
|
||||
@Schema(description = "胶料ID列表 mes_material.id")
|
||||
private List<String> materialIds;
|
||||
|
||||
private String prodEquipmentLedgerId;
|
||||
private String prodEquipmentName;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private Date productionDate;
|
||||
|
||||
private String trainNo;
|
||||
private String workShift;
|
||||
private String workTeam;
|
||||
private Integer inspectTimes;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inspectTime;
|
||||
|
||||
private String inspectorUserId;
|
||||
private String inspectorUsername;
|
||||
private String inspectorRealname;
|
||||
|
||||
@Schema(description = "检验类型ID mes_xsl_rubber_quick_test_type.id")
|
||||
private String quickTestTypeId;
|
||||
|
||||
private String inspectResult;
|
||||
private String productionPlanNo;
|
||||
|
||||
private String inspectEquipmentLedgerId;
|
||||
private String inspectEquipmentName;
|
||||
|
||||
private String rubberCardNo;
|
||||
private String rubberBatchNo;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.jeecg.modules.xslmes.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRubberQuickTestRecord;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MesXslRubberQuickTestRecordPage extends MesXslRubberQuickTestRecord {}
|
||||
@@ -495,9 +495,48 @@ jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestStd/components/MesXslRubber
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestStd/components/MesXslRubberQuickTestMethodSelectModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestStd/components/MesXslRubberQuickTestStdMixerPsSelectModal.vue
|
||||
|
||||
-- author:jiangxh---date:20260525--for: 【MES】原材料检验标准密炼PS批准时关联胶料快检实验标准置已批准(反审核不回退,触发由审核改为批准)---
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/XslMesBizConstants.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRubberQuickTestStdService.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRubberQuickTestStdServiceImpl.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixerPsCompileServiceImpl.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRubberQuickTestStdController.java
|
||||
|
||||
-- author:xsl---date:20260528--for: 【IM聊天-OA】IM群聊:创建群聊、群列表、群消息收发与未读 ---
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_110__sys_im_group_chat.sql
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/dto/SysImCreateGroupDTO.java
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/entity/SysImConversation.java
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/vo/SysImConversationVO.java
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/mapper/SysImConversationMapper.java
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/mapper/xml/SysImConversationMapper.xml
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/service/ISysImChatService.java
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/service/impl/SysImChatServiceImpl.java
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/controller/SysImChatController.java
|
||||
jeecgboot-vue3/src/views/system/im/im.api.ts
|
||||
jeecgboot-vue3/src/views/system/im/ImCreateGroupModal.vue
|
||||
jeecgboot-vue3/src/views/system/im/ImChat.vue
|
||||
|
||||
-- author:jiangxh---date:20260525--for: 【MES】胶料快检记录主子表、质量管理菜单、胶料信息批量检验生成 ---
|
||||
jeecg-boot/db/mes-xsl-rubber-quick-test-record-menu-permission.sql
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_108__mes_xsl_rubber_quick_test_record.sql
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslRubberQuickTestRecord.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslRubberQuickTestRecordLine.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRubberQuickTestRecordPage.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRubberQuickTestRecordBatchFromMaterialVO.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslRubberQuickTestRecordMapper.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslRubberQuickTestRecordLineMapper.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRubberQuickTestRecordService.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRubberQuickTestRecordServiceImpl.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRubberQuickTestRecordController.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecordList.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.api.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/components/MesXslRubberQuickTestRecordModal.vue
|
||||
jeecgboot-vue3/src/views/mes/material/MesMaterialList.vue
|
||||
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_109__mes_xsl_rubber_quick_test_record_no.sql
|
||||
|
||||
-- author:jiangxh---date:20260528--for: 【MES】胶料快检记录单号、无弹窗直接生成、列表去掉新增、明细不可增删 ---
|
||||
jeecg-boot/db/mes-xsl-rubber-quick-test-record-menu-permission.sql
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslRubberQuickTestRecord.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRubberQuickTestRecordService.java
|
||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRubberQuickTestRecordServiceImpl.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecordList.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestRecord/components/MesXslRubberQuickTestRecordModal.vue
|
||||
jeecgboot-vue3/src/views/mes/material/MesMaterialList.vue
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
package org.jeecg.modules.im.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.jeecg.modules.im.vo.SysImMessageVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.im.dto.SysImCreateGroupDTO;
|
||||
import org.jeecg.modules.im.dto.SysImGroupMembersDTO;
|
||||
import org.jeecg.modules.im.dto.SysImSendMessageDTO;
|
||||
import org.jeecg.modules.im.service.ISysImChatService;
|
||||
import org.jeecg.modules.im.vo.SysImContactVO;
|
||||
import org.jeecg.modules.im.vo.SysImConversationVO;
|
||||
import org.jeecg.modules.im.vo.SysImGroupDetailVO;
|
||||
import org.jeecg.modules.im.vo.SysImMessageVO;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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 jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 租户 IM 聊天
|
||||
*/
|
||||
@Tag(name = "IM聊天")
|
||||
@RestController
|
||||
@RequestMapping("/sys/im/chat")
|
||||
@Slf4j
|
||||
public class SysImChatController {
|
||||
|
||||
@Autowired
|
||||
private ISysImChatService imChatService;
|
||||
@Autowired
|
||||
private ISysUserService sysUserService;
|
||||
|
||||
private LoginUser currentUser() {
|
||||
return (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
}
|
||||
|
||||
private Integer resolveTenantId(LoginUser user, HttpServletRequest request) {
|
||||
Integer tenantId = oConvertUtils.getInt(TokenUtils.getTenantIdByRequest(request), 0);
|
||||
if (tenantId != null && tenantId > 0) {
|
||||
return tenantId;
|
||||
}
|
||||
SysUser sysUser = sysUserService.getById(user.getId());
|
||||
if (sysUser != null && sysUser.getLoginTenantId() != null && sysUser.getLoginTenantId() > 0) {
|
||||
return sysUser.getLoginTenantId();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】会话列表接口-----------
|
||||
@Operation(summary = "IM聊天-会话列表")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@GetMapping("/conversations")
|
||||
public Result<List<SysImConversationVO>> conversations(HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.listConversations(user.getId(), resolveTenantId(user, request)));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】会话列表接口-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】打开单聊接口-----------
|
||||
@Operation(summary = "IM聊天-打开单聊")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PostMapping("/open")
|
||||
public Result<SysImConversationVO> open(@RequestParam(name = "targetUserId") String targetUserId, HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.openSingleConversation(user.getId(), resolveTenantId(user, request), user.getOrgCode(), targetUserId));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】打开单聊接口-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】消息列表接口-----------
|
||||
@Operation(summary = "IM聊天-消息列表")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@GetMapping("/messages")
|
||||
public Result<IPage<SysImMessageVO>> messages(
|
||||
@RequestParam(name = "conversationId") String conversationId,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "20") Integer pageSize,
|
||||
@RequestParam(name = "startTime", required = false) String startTime,
|
||||
@RequestParam(name = "beforeTime", required = false) String beforeTime) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.listMessages(user.getId(), conversationId, pageNo, pageSize, startTime, beforeTime));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】消息列表接口-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】发送消息接口-----------
|
||||
@Operation(summary = "IM聊天-发送消息")
|
||||
@RequiresPermissions("sys:im:chat:send")
|
||||
@PostMapping("/send")
|
||||
public Result<SysImMessageVO> send(@RequestBody SysImSendMessageDTO dto, HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.sendMessage(user.getId(), resolveTenantId(user, request), dto));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】发送消息接口-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】标记已读接口-----------
|
||||
@Operation(summary = "IM聊天-标记已读")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PutMapping("/read")
|
||||
public Result<String> read(@RequestParam(name = "conversationId") String conversationId) {
|
||||
LoginUser user = currentUser();
|
||||
imChatService.markRead(user.getId(), conversationId);
|
||||
return Result.OK();
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】标记已读接口-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】本部门成员接口-----------
|
||||
@Operation(summary = "IM聊天-本部门成员")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@GetMapping("/deptMembers")
|
||||
public Result<List<SysImContactVO>> deptMembers(@RequestParam(name = "keyword", required = false) String keyword, HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.listDeptMembers(user.getId(), resolveTenantId(user, request), user.getOrgCode(), keyword));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】本部门成员接口-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】联系人接口(兼容保留,同本部门)-----------
|
||||
@Operation(summary = "IM聊天-租户联系人")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@GetMapping("/contacts")
|
||||
public Result<List<SysImContactVO>> contacts(@RequestParam(name = "keyword", required = false) String keyword, HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.listDeptMembers(user.getId(), resolveTenantId(user, request), user.getOrgCode(), keyword));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】联系人接口(兼容保留,同本部门)-----------
|
||||
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】群聊接口-----------
|
||||
@Operation(summary = "IM聊天-群聊列表")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@GetMapping("/groups")
|
||||
public Result<List<SysImConversationVO>> groups(HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.listGroupConversations(user.getId(), resolveTenantId(user, request)));
|
||||
}
|
||||
|
||||
@Operation(summary = "IM聊天-创建群聊")
|
||||
@RequiresPermissions("sys:im:chat:group")
|
||||
@PostMapping("/group/create")
|
||||
public Result<SysImConversationVO> createGroup(@RequestBody SysImCreateGroupDTO dto, HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.createGroupConversation(user.getId(), resolveTenantId(user, request), user.getOrgCode(), dto));
|
||||
}
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】群聊接口-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群管理接口-----------
|
||||
@Operation(summary = "IM聊天-群聊详情")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@GetMapping("/group/detail")
|
||||
public Result<SysImGroupDetailVO> groupDetail(@RequestParam(name = "conversationId") String conversationId) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.getGroupDetail(user.getId(), conversationId));
|
||||
}
|
||||
|
||||
@Operation(summary = "IM聊天-添加群成员")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PostMapping("/group/addMembers")
|
||||
public Result<SysImConversationVO> addGroupMembers(@RequestBody SysImGroupMembersDTO dto, HttpServletRequest request) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.addGroupMembers(user.getId(), resolveTenantId(user, request), user.getOrgCode(), dto));
|
||||
}
|
||||
|
||||
@Operation(summary = "IM聊天-移除群成员")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PostMapping("/group/removeMember")
|
||||
public Result<String> removeGroupMember(@RequestParam(name = "conversationId") String conversationId,
|
||||
@RequestParam(name = "memberUserId") String memberUserId) {
|
||||
LoginUser user = currentUser();
|
||||
imChatService.removeGroupMember(user.getId(), conversationId, memberUserId);
|
||||
return Result.OK("移除成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "IM聊天-修改群名称")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PostMapping("/group/rename")
|
||||
public Result<SysImConversationVO> renameGroup(@RequestParam(name = "conversationId") String conversationId,
|
||||
@RequestParam(name = "groupName") String groupName) {
|
||||
LoginUser user = currentUser();
|
||||
return Result.OK(imChatService.renameGroup(user.getId(), conversationId, groupName));
|
||||
}
|
||||
|
||||
@Operation(summary = "IM聊天-转让群主")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PostMapping("/group/transfer")
|
||||
public Result<String> transferGroupOwner(@RequestParam(name = "conversationId") String conversationId,
|
||||
@RequestParam(name = "newOwnerId") String newOwnerId) {
|
||||
LoginUser user = currentUser();
|
||||
imChatService.transferGroupOwner(user.getId(), conversationId, newOwnerId);
|
||||
return Result.OK("转让成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "IM聊天-退出群聊")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PostMapping("/group/quit")
|
||||
public Result<String> quitGroup(@RequestParam(name = "conversationId") String conversationId) {
|
||||
LoginUser user = currentUser();
|
||||
imChatService.quitGroup(user.getId(), conversationId);
|
||||
return Result.OK("已退出群聊");
|
||||
}
|
||||
|
||||
@Operation(summary = "IM聊天-解散群聊")
|
||||
@RequiresPermissions("sys:im:chat:list")
|
||||
@PostMapping("/group/dismiss")
|
||||
public Result<String> dismissGroup(@RequestParam(name = "conversationId") String conversationId) {
|
||||
LoginUser user = currentUser();
|
||||
imChatService.dismissGroup(user.getId(), conversationId);
|
||||
return Result.OK("群聊已解散");
|
||||
}
|
||||
//update-end---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群管理接口-----------
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.jeecg.modules.im.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 创建 IM 群聊
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建IM群聊")
|
||||
public class SysImCreateGroupDTO {
|
||||
|
||||
@Schema(description = "群名称")
|
||||
private String groupName;
|
||||
|
||||
@Schema(description = "群成员用户ID(不含本人时可自动加入创建人)")
|
||||
private List<String> memberUserIds;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.jeecg.modules.im.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IM 群聊添加成员
|
||||
*/
|
||||
//update-begin---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-添加群成员-----------
|
||||
@Data
|
||||
@Schema(description = "IM群聊添加成员")
|
||||
public class SysImGroupMembersDTO {
|
||||
|
||||
@Schema(description = "会话ID")
|
||||
private String conversationId;
|
||||
|
||||
@Schema(description = "新增成员用户ID")
|
||||
private List<String> memberUserIds;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-添加群成员-----------
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.jeecg.modules.im.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 发送 IM 消息 DTO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "发送IM消息")
|
||||
public class SysImSendMessageDTO {
|
||||
|
||||
@Schema(description = "会话ID")
|
||||
private String conversationId;
|
||||
@Schema(description = "消息内容")
|
||||
private String content;
|
||||
@Schema(description = "消息类型 text/image/file")
|
||||
private String msgType;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.jeecg.modules.im.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* IM 会话
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_im_conversation")
|
||||
public class SysImConversation implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
/** 会话类型 single单聊 group群聊 */
|
||||
private String convType;
|
||||
/** 单聊唯一键 */
|
||||
private String userPairKey;
|
||||
/** 群名称 */
|
||||
private String groupName;
|
||||
/** 群主用户ID */
|
||||
private String ownerId;
|
||||
/** 租户ID */
|
||||
private Integer tenantId;
|
||||
/** 最后一条消息摘要 */
|
||||
private String lastContent;
|
||||
/** 最后消息时间 */
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastTime;
|
||||
private String createBy;
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private String updateBy;
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.jeecg.modules.im.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* IM 会话成员
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_im_conversation_member")
|
||||
public class SysImConversationMember implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
private String conversationId;
|
||||
private String userId;
|
||||
private Integer unreadCount;
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastReadTime;
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.jeecg.modules.im.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* IM 消息
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_im_message")
|
||||
public class SysImMessage implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
private String conversationId;
|
||||
private String senderId;
|
||||
private String content;
|
||||
/** 消息类型 text/image/file */
|
||||
private String msgType;
|
||||
private Integer tenantId;
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.jeecg.modules.im.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.jeecg.modules.im.entity.SysImConversation;
|
||||
import org.jeecg.modules.im.vo.SysImConversationVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IM 会话 Mapper
|
||||
*/
|
||||
public interface SysImConversationMapper extends BaseMapper<SysImConversation> {
|
||||
|
||||
/**
|
||||
* 查询当前用户的会话列表
|
||||
*/
|
||||
List<SysImConversationVO> listMyConversations(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
|
||||
|
||||
/**
|
||||
* 查询当前用户的群聊列表
|
||||
*/
|
||||
List<SysImConversationVO> listMyGroupConversations(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.jeecg.modules.im.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.jeecg.modules.im.entity.SysImConversationMember;
|
||||
|
||||
/**
|
||||
* IM 会话成员 Mapper
|
||||
*/
|
||||
public interface SysImConversationMemberMapper extends BaseMapper<SysImConversationMember> {
|
||||
|
||||
/**
|
||||
* 未读数 +1(排除发送人)
|
||||
*/
|
||||
int incrementUnreadExceptSender(@Param("conversationId") String conversationId, @Param("senderId") String senderId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.jeecg.modules.im.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.im.entity.SysImMessage;
|
||||
|
||||
/**
|
||||
* IM 消息 Mapper
|
||||
*/
|
||||
public interface SysImMessageMapper extends BaseMapper<SysImMessage> {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.jeecg.modules.im.mapper.SysImConversationMapper">
|
||||
|
||||
<select id="listMyConversations" resultType="org.jeecg.modules.im.vo.SysImConversationVO">
|
||||
SELECT
|
||||
c.id AS conversationId,
|
||||
c.conv_type AS convType,
|
||||
c.last_content AS lastContent,
|
||||
c.last_time AS lastTime,
|
||||
m.unread_count AS unreadCount,
|
||||
u.id AS targetUserId,
|
||||
u.realname AS targetRealname,
|
||||
u.username AS targetUsername,
|
||||
u.avatar AS targetAvatar
|
||||
FROM sys_im_conversation_member m
|
||||
INNER JOIN sys_im_conversation c ON c.id = m.conversation_id
|
||||
INNER JOIN sys_im_conversation_member om ON om.conversation_id = c.id AND om.user_id != m.user_id
|
||||
INNER JOIN sys_user u ON u.id = om.user_id
|
||||
WHERE m.user_id = #{userId}
|
||||
AND c.tenant_id = #{tenantId}
|
||||
AND c.conv_type = 'single'
|
||||
ORDER BY c.last_time DESC
|
||||
</select>
|
||||
|
||||
<select id="listMyGroupConversations" resultType="org.jeecg.modules.im.vo.SysImConversationVO">
|
||||
SELECT
|
||||
c.id AS conversationId,
|
||||
c.conv_type AS convType,
|
||||
c.group_name AS groupName,
|
||||
c.owner_id AS ownerId,
|
||||
c.last_content AS lastContent,
|
||||
c.last_time AS lastTime,
|
||||
m.unread_count AS unreadCount,
|
||||
(
|
||||
SELECT COUNT(1)
|
||||
FROM sys_im_conversation_member gm
|
||||
WHERE gm.conversation_id = c.id
|
||||
) AS memberCount
|
||||
FROM sys_im_conversation_member m
|
||||
INNER JOIN sys_im_conversation c ON c.id = m.conversation_id
|
||||
WHERE m.user_id = #{userId}
|
||||
AND c.tenant_id = #{tenantId}
|
||||
AND c.conv_type = 'group'
|
||||
ORDER BY c.last_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.jeecg.modules.im.mapper.SysImConversationMemberMapper">
|
||||
|
||||
<update id="incrementUnreadExceptSender">
|
||||
UPDATE sys_im_conversation_member
|
||||
SET unread_count = unread_count + 1
|
||||
WHERE conversation_id = #{conversationId}
|
||||
AND user_id != #{senderId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.jeecg.modules.im.service;
|
||||
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
import org.jeecg.modules.im.dto.SysImCreateGroupDTO;
|
||||
import org.jeecg.modules.im.dto.SysImGroupMembersDTO;
|
||||
import org.jeecg.modules.im.dto.SysImSendMessageDTO;
|
||||
|
||||
import org.jeecg.modules.im.vo.SysImContactVO;
|
||||
|
||||
import org.jeecg.modules.im.vo.SysImConversationVO;
|
||||
|
||||
import org.jeecg.modules.im.vo.SysImGroupDetailVO;
|
||||
|
||||
import org.jeecg.modules.im.vo.SysImMessageVO;
|
||||
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* IM 聊天服务
|
||||
|
||||
*/
|
||||
|
||||
public interface ISysImChatService {
|
||||
|
||||
|
||||
|
||||
List<SysImConversationVO> listConversations(String userId, Integer tenantId);
|
||||
|
||||
|
||||
|
||||
SysImConversationVO openSingleConversation(String userId, Integer tenantId, String orgCode, String targetUserId);
|
||||
|
||||
List<SysImConversationVO> listGroupConversations(String userId, Integer tenantId);
|
||||
|
||||
SysImConversationVO createGroupConversation(String userId, Integer tenantId, String orgCode, SysImCreateGroupDTO dto);
|
||||
|
||||
IPage<SysImMessageVO> listMessages(String userId, String conversationId, Integer pageNo, Integer pageSize, String startTime, String beforeTime);
|
||||
|
||||
|
||||
|
||||
SysImMessageVO sendMessage(String userId, Integer tenantId, SysImSendMessageDTO dto);
|
||||
|
||||
|
||||
|
||||
void markRead(String userId, String conversationId);
|
||||
|
||||
|
||||
|
||||
List<SysImContactVO> listDeptMembers(String userId, Integer tenantId, String orgCode, String keyword);
|
||||
|
||||
//update-begin---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群管理接口-----------
|
||||
/** 群聊详情(含成员列表,区分群主) */
|
||||
SysImGroupDetailVO getGroupDetail(String userId, String conversationId);
|
||||
|
||||
/** 添加群成员(所有群成员可操作) */
|
||||
SysImConversationVO addGroupMembers(String userId, Integer tenantId, String orgCode, SysImGroupMembersDTO dto);
|
||||
|
||||
/** 移除群成员(仅群主) */
|
||||
void removeGroupMember(String userId, String conversationId, String memberUserId);
|
||||
|
||||
/** 修改群名称(仅群主) */
|
||||
SysImConversationVO renameGroup(String userId, String conversationId, String groupName);
|
||||
|
||||
/** 转让群主(仅群主) */
|
||||
void transferGroupOwner(String userId, String conversationId, String newOwnerId);
|
||||
|
||||
/** 退出群聊(非群主成员) */
|
||||
void quitGroup(String userId, String conversationId);
|
||||
|
||||
/** 解散群聊(仅群主) */
|
||||
void dismissGroup(String userId, String conversationId);
|
||||
//update-end---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群管理接口-----------
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,859 @@
|
||||
package org.jeecg.modules.im.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.WebsocketConst;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.DateUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.im.dto.SysImCreateGroupDTO;
|
||||
import org.jeecg.modules.im.dto.SysImGroupMembersDTO;
|
||||
import org.jeecg.modules.im.dto.SysImSendMessageDTO;
|
||||
import org.jeecg.modules.im.entity.SysImConversation;
|
||||
import org.jeecg.modules.im.entity.SysImConversationMember;
|
||||
import org.jeecg.modules.im.entity.SysImMessage;
|
||||
import org.jeecg.modules.im.mapper.SysImConversationMapper;
|
||||
import org.jeecg.modules.im.mapper.SysImConversationMemberMapper;
|
||||
import org.jeecg.modules.im.mapper.SysImMessageMapper;
|
||||
import org.jeecg.modules.im.service.ISysImChatService;
|
||||
import org.jeecg.modules.im.vo.SysImContactVO;
|
||||
import org.jeecg.modules.im.vo.SysImConversationVO;
|
||||
import org.jeecg.modules.im.vo.SysImGroupDetailVO;
|
||||
import org.jeecg.modules.im.vo.SysImGroupMemberVO;
|
||||
import org.jeecg.modules.im.vo.SysImMessageVO;
|
||||
import org.jeecg.modules.message.websocket.WebSocket;
|
||||
import org.jeecg.modules.system.entity.SysDepart;
|
||||
import org.jeecg.modules.system.entity.SysPermission;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.entity.SysUserDepart;
|
||||
import org.jeecg.modules.system.mapper.SysDepartMapper;
|
||||
import org.jeecg.modules.system.mapper.SysUserDepartMapper;
|
||||
import org.jeecg.modules.system.mapper.SysUserMapper;
|
||||
import org.jeecg.modules.system.mapper.SysUserTenantMapper;
|
||||
import org.jeecg.modules.system.service.ISysPermissionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* IM 聊天服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SysImChatServiceImpl implements ISysImChatService {
|
||||
|
||||
private static final String CONV_TYPE_SINGLE = "single";
|
||||
private static final String CONV_TYPE_GROUP = "group";
|
||||
private static final String MSG_TYPE_TEXT = "text";
|
||||
private static final String MSG_TYPE_IMAGE = "image";
|
||||
private static final String MSG_TYPE_BIZ_RECORD = "biz_record";
|
||||
private static final String MSG_IMAGE_PREVIEW = "[图片]";
|
||||
private static final String MSG_BIZ_RECORD_PREVIEW = "[业务数据]";
|
||||
private static final String IM_RECORD_QUERY_KEY = "imRecordId";
|
||||
|
||||
@Autowired
|
||||
private ISysPermissionService sysPermissionService;
|
||||
@Autowired
|
||||
private SysImConversationMapper conversationMapper;
|
||||
@Autowired
|
||||
private SysImConversationMemberMapper memberMapper;
|
||||
@Autowired
|
||||
private SysImMessageMapper messageMapper;
|
||||
@Autowired
|
||||
private SysUserMapper userMapper;
|
||||
@Autowired
|
||||
private SysUserTenantMapper userTenantMapper;
|
||||
@Autowired
|
||||
private SysUserDepartMapper userDepartMapper;
|
||||
@Autowired
|
||||
private SysDepartMapper departMapper;
|
||||
@Autowired
|
||||
private WebSocket webSocket;
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】会话列表-----------
|
||||
@Override
|
||||
public List<SysImConversationVO> listConversations(String userId, Integer tenantId) {
|
||||
if (oConvertUtils.isEmpty(userId) || tenantId == null || tenantId <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return conversationMapper.listMyConversations(userId, tenantId);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】会话列表-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】打开单聊会话-----------
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SysImConversationVO openSingleConversation(String userId, Integer tenantId, String orgCode, String targetUserId) {
|
||||
String pairKey = buildPairKey(userId, targetUserId);
|
||||
SysImConversation conversation = conversationMapper.selectOne(new LambdaQueryWrapper<SysImConversation>()
|
||||
.eq(SysImConversation::getTenantId, tenantId)
|
||||
.eq(SysImConversation::getUserPairKey, pairKey));
|
||||
Date now = new Date();
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】已有会话快速打开-----------
|
||||
if (conversation != null) {
|
||||
return buildConversationVo(conversation, userId, targetUserId);
|
||||
}
|
||||
validateTenantChat(userId, tenantId, orgCode, targetUserId);
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】已有会话快速打开-----------
|
||||
conversation = new SysImConversation();
|
||||
conversation.setConvType(CONV_TYPE_SINGLE);
|
||||
conversation.setUserPairKey(pairKey);
|
||||
conversation.setTenantId(tenantId);
|
||||
conversation.setCreateBy(userId);
|
||||
conversation.setCreateTime(now);
|
||||
conversation.setUpdateTime(now);
|
||||
conversationMapper.insert(conversation);
|
||||
createMember(conversation.getId(), userId, now);
|
||||
createMember(conversation.getId(), targetUserId, now);
|
||||
return buildConversationVo(conversation.getId(), userId, tenantId, targetUserId);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】打开单聊会话-----------
|
||||
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】群聊会话列表与创建-----------
|
||||
@Override
|
||||
public List<SysImConversationVO> listGroupConversations(String userId, Integer tenantId) {
|
||||
if (oConvertUtils.isEmpty(userId) || tenantId == null || tenantId <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return conversationMapper.listMyGroupConversations(userId, tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SysImConversationVO createGroupConversation(String userId, Integer tenantId, String orgCode, SysImCreateGroupDTO dto) {
|
||||
if (dto == null || oConvertUtils.isEmpty(dto.getGroupName())) {
|
||||
throw new JeecgBootException("群名称不能为空");
|
||||
}
|
||||
String groupName = dto.getGroupName().trim();
|
||||
if (groupName.length() > 30) {
|
||||
throw new JeecgBootException("群名称不能超过30字");
|
||||
}
|
||||
List<String> memberIds = normalizeGroupMemberIds(userId, dto.getMemberUserIds());
|
||||
if (memberIds.size() < 2) {
|
||||
throw new JeecgBootException("群聊至少需要2名成员");
|
||||
}
|
||||
if (memberIds.size() > 50) {
|
||||
throw new JeecgBootException("群成员不能超过50人");
|
||||
}
|
||||
for (String memberId : memberIds) {
|
||||
if (userId.equals(memberId)) {
|
||||
continue;
|
||||
}
|
||||
validateTenantChat(userId, tenantId, orgCode, memberId);
|
||||
}
|
||||
Date now = new Date();
|
||||
SysImConversation conversation = new SysImConversation();
|
||||
conversation.setConvType(CONV_TYPE_GROUP);
|
||||
conversation.setGroupName(groupName);
|
||||
conversation.setOwnerId(userId);
|
||||
conversation.setTenantId(tenantId);
|
||||
conversation.setCreateBy(userId);
|
||||
conversation.setCreateTime(now);
|
||||
conversation.setUpdateTime(now);
|
||||
conversationMapper.insert(conversation);
|
||||
for (String memberId : memberIds) {
|
||||
createMember(conversation.getId(), memberId, now);
|
||||
}
|
||||
return buildGroupConversationVo(conversation, userId);
|
||||
}
|
||||
|
||||
private List<String> normalizeGroupMemberIds(String creatorId, List<String> memberUserIds) {
|
||||
Set<String> memberIds = new LinkedHashSet<>();
|
||||
memberIds.add(creatorId);
|
||||
if (memberUserIds != null) {
|
||||
for (String memberId : memberUserIds) {
|
||||
if (oConvertUtils.isNotEmpty(memberId)) {
|
||||
memberIds.add(memberId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(memberIds);
|
||||
}
|
||||
|
||||
private SysImConversationVO buildGroupConversationVo(SysImConversation conversation, String userId) {
|
||||
SysImConversationMember member = getMember(userId, conversation.getId());
|
||||
SysImConversationVO vo = new SysImConversationVO();
|
||||
vo.setConversationId(conversation.getId());
|
||||
vo.setConvType(CONV_TYPE_GROUP);
|
||||
vo.setGroupName(conversation.getGroupName());
|
||||
vo.setOwnerId(conversation.getOwnerId());
|
||||
vo.setLastContent(conversation.getLastContent());
|
||||
vo.setLastTime(conversation.getLastTime());
|
||||
vo.setUnreadCount(member == null ? 0 : member.getUnreadCount());
|
||||
Long memberCount = memberMapper.selectCount(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversation.getId()));
|
||||
vo.setMemberCount(memberCount == null ? 0 : memberCount.intValue());
|
||||
return vo;
|
||||
}
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】群聊会话列表与创建-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群管理-----------
|
||||
private static final int GROUP_MEMBER_MAX = 50;
|
||||
|
||||
@Override
|
||||
public SysImGroupDetailVO getGroupDetail(String userId, String conversationId) {
|
||||
SysImConversation conversation = assertGroupConversation(userId, conversationId);
|
||||
List<SysImConversationMember> members = memberMapper.selectList(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversationId)
|
||||
.orderByAsc(SysImConversationMember::getCreateTime));
|
||||
List<String> memberUserIds = members.stream()
|
||||
.map(SysImConversationMember::getUserId)
|
||||
.filter(oConvertUtils::isNotEmpty)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<String, SysUser> userMap = new HashMap<>(memberUserIds.size());
|
||||
if (!memberUserIds.isEmpty()) {
|
||||
List<SysUser> users = userMapper.selectBatchIds(memberUserIds);
|
||||
if (users != null) {
|
||||
for (SysUser user : users) {
|
||||
userMap.put(user.getId(), user);
|
||||
}
|
||||
}
|
||||
}
|
||||
String ownerId = conversation.getOwnerId();
|
||||
List<SysImGroupMemberVO> memberVoList = new ArrayList<>(members.size());
|
||||
for (SysImConversationMember member : members) {
|
||||
SysUser user = userMap.get(member.getUserId());
|
||||
SysImGroupMemberVO memberVo = new SysImGroupMemberVO();
|
||||
memberVo.setUserId(member.getUserId());
|
||||
memberVo.setOwner(member.getUserId() != null && member.getUserId().equals(ownerId));
|
||||
if (user != null) {
|
||||
memberVo.setRealname(user.getRealname());
|
||||
memberVo.setUsername(user.getUsername());
|
||||
memberVo.setAvatar(user.getAvatar());
|
||||
}
|
||||
memberVoList.add(memberVo);
|
||||
}
|
||||
// 群主排在最前
|
||||
memberVoList.sort(Comparator.comparingInt(item -> Boolean.TRUE.equals(item.getOwner()) ? 0 : 1));
|
||||
SysImGroupDetailVO detail = new SysImGroupDetailVO();
|
||||
detail.setConversationId(conversation.getId());
|
||||
detail.setGroupName(conversation.getGroupName());
|
||||
detail.setOwnerId(ownerId);
|
||||
detail.setMemberCount(memberVoList.size());
|
||||
detail.setOwner(userId.equals(ownerId));
|
||||
detail.setMembers(memberVoList);
|
||||
return detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SysImConversationVO addGroupMembers(String userId, Integer tenantId, String orgCode, SysImGroupMembersDTO dto) {
|
||||
if (dto == null || oConvertUtils.isEmpty(dto.getConversationId())) {
|
||||
throw new JeecgBootException("会话不存在");
|
||||
}
|
||||
SysImConversation conversation = assertGroupConversation(userId, dto.getConversationId());
|
||||
if (dto.getMemberUserIds() == null || dto.getMemberUserIds().isEmpty()) {
|
||||
throw new JeecgBootException("请选择要添加的成员");
|
||||
}
|
||||
// 已在群成员
|
||||
Set<String> existIds = memberMapper.selectList(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversation.getId()))
|
||||
.stream().map(SysImConversationMember::getUserId).collect(Collectors.toSet());
|
||||
// 去重并过滤已存在成员
|
||||
List<String> toAdd = new ArrayList<>();
|
||||
Set<String> seen = new LinkedHashSet<>();
|
||||
for (String memberId : dto.getMemberUserIds()) {
|
||||
if (oConvertUtils.isEmpty(memberId) || existIds.contains(memberId) || !seen.add(memberId)) {
|
||||
continue;
|
||||
}
|
||||
toAdd.add(memberId);
|
||||
}
|
||||
if (toAdd.isEmpty()) {
|
||||
throw new JeecgBootException("所选成员已在群内");
|
||||
}
|
||||
if (existIds.size() + toAdd.size() > GROUP_MEMBER_MAX) {
|
||||
throw new JeecgBootException("群成员不能超过" + GROUP_MEMBER_MAX + "人");
|
||||
}
|
||||
// 校验同租户、同部门
|
||||
for (String memberId : toAdd) {
|
||||
validateTenantChat(userId, tenantId, orgCode, memberId);
|
||||
}
|
||||
Date now = new Date();
|
||||
for (String memberId : toAdd) {
|
||||
createMember(conversation.getId(), memberId, now);
|
||||
}
|
||||
return buildGroupConversationVo(conversation, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeGroupMember(String userId, String conversationId, String memberUserId) {
|
||||
SysImConversation conversation = assertGroupConversation(userId, conversationId);
|
||||
assertGroupOwner(userId, conversation);
|
||||
if (oConvertUtils.isEmpty(memberUserId)) {
|
||||
throw new JeecgBootException("请选择要移除的成员");
|
||||
}
|
||||
if (memberUserId.equals(conversation.getOwnerId())) {
|
||||
throw new JeecgBootException("群主不能被移除");
|
||||
}
|
||||
memberMapper.delete(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversationId)
|
||||
.eq(SysImConversationMember::getUserId, memberUserId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SysImConversationVO renameGroup(String userId, String conversationId, String groupName) {
|
||||
SysImConversation conversation = assertGroupConversation(userId, conversationId);
|
||||
assertGroupOwner(userId, conversation);
|
||||
if (oConvertUtils.isEmpty(groupName)) {
|
||||
throw new JeecgBootException("群名称不能为空");
|
||||
}
|
||||
String name = groupName.trim();
|
||||
if (name.length() > 30) {
|
||||
throw new JeecgBootException("群名称不能超过30字");
|
||||
}
|
||||
conversation.setGroupName(name);
|
||||
conversation.setUpdateBy(userId);
|
||||
conversation.setUpdateTime(new Date());
|
||||
conversationMapper.updateById(conversation);
|
||||
return buildGroupConversationVo(conversation, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void transferGroupOwner(String userId, String conversationId, String newOwnerId) {
|
||||
SysImConversation conversation = assertGroupConversation(userId, conversationId);
|
||||
assertGroupOwner(userId, conversation);
|
||||
if (oConvertUtils.isEmpty(newOwnerId)) {
|
||||
throw new JeecgBootException("请选择新群主");
|
||||
}
|
||||
if (newOwnerId.equals(userId)) {
|
||||
throw new JeecgBootException("不能转让给自己");
|
||||
}
|
||||
if (getMember(newOwnerId, conversationId) == null) {
|
||||
throw new JeecgBootException("新群主必须是群成员");
|
||||
}
|
||||
conversation.setOwnerId(newOwnerId);
|
||||
conversation.setUpdateBy(userId);
|
||||
conversation.setUpdateTime(new Date());
|
||||
conversationMapper.updateById(conversation);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void quitGroup(String userId, String conversationId) {
|
||||
SysImConversation conversation = assertGroupConversation(userId, conversationId);
|
||||
if (userId.equals(conversation.getOwnerId())) {
|
||||
throw new JeecgBootException("群主不能退出群聊,请先转让群主或解散群聊");
|
||||
}
|
||||
memberMapper.delete(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversationId)
|
||||
.eq(SysImConversationMember::getUserId, userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void dismissGroup(String userId, String conversationId) {
|
||||
SysImConversation conversation = assertGroupConversation(userId, conversationId);
|
||||
assertGroupOwner(userId, conversation);
|
||||
memberMapper.delete(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversationId));
|
||||
conversationMapper.deleteById(conversationId);
|
||||
}
|
||||
|
||||
/** 校验会话存在、为群聊且当前用户是群成员 */
|
||||
private SysImConversation assertGroupConversation(String userId, String conversationId) {
|
||||
if (oConvertUtils.isEmpty(conversationId)) {
|
||||
throw new JeecgBootException("会话不存在");
|
||||
}
|
||||
SysImConversation conversation = conversationMapper.selectById(conversationId);
|
||||
if (conversation == null || !CONV_TYPE_GROUP.equals(conversation.getConvType())) {
|
||||
throw new JeecgBootException("群聊不存在");
|
||||
}
|
||||
assertMember(userId, conversationId);
|
||||
return conversation;
|
||||
}
|
||||
|
||||
/** 校验当前用户是群主 */
|
||||
private void assertGroupOwner(String userId, SysImConversation conversation) {
|
||||
if (!userId.equals(conversation.getOwnerId())) {
|
||||
throw new JeecgBootException("仅群主可执行该操作");
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群管理-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】消息分页-----------
|
||||
@Override
|
||||
public IPage<SysImMessageVO> listMessages(String userId, String conversationId, Integer pageNo, Integer pageSize, String startTime, String beforeTime) {
|
||||
assertMember(userId, conversationId);
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】默认本周消息+向上翻页加载-----------
|
||||
Date start = parseMessageTime(startTime);
|
||||
Date before = parseMessageTime(beforeTime);
|
||||
int currentPage = (start != null || before != null) ? 1 : (pageNo == null || pageNo < 1 ? 1 : pageNo);
|
||||
int size = pageSize == null || pageSize < 1 ? 20 : pageSize;
|
||||
Page<SysImMessage> page = new Page<>(currentPage, size);
|
||||
if (currentPage == 1) {
|
||||
page.setSearchCount(false);
|
||||
}
|
||||
LambdaQueryWrapper<SysImMessage> wrapper = new LambdaQueryWrapper<SysImMessage>()
|
||||
.eq(SysImMessage::getConversationId, conversationId);
|
||||
if (start != null) {
|
||||
wrapper.ge(SysImMessage::getCreateTime, start);
|
||||
}
|
||||
if (before != null) {
|
||||
wrapper.lt(SysImMessage::getCreateTime, before);
|
||||
}
|
||||
wrapper.orderByDesc(SysImMessage::getCreateTime);
|
||||
IPage<SysImMessage> messagePage = messageMapper.selectPage(page, wrapper);
|
||||
Page<SysImMessageVO> voPage = new Page<>(currentPage, size, messagePage.getTotal());
|
||||
List<SysImMessageVO> records = toMessageVoList(messagePage.getRecords(), userId);
|
||||
Collections.reverse(records);
|
||||
voPage.setRecords(records);
|
||||
return voPage;
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】默认本周消息+向上翻页加载-----------
|
||||
}
|
||||
|
||||
private Date parseMessageTime(String timeText) {
|
||||
if (oConvertUtils.isEmpty(timeText)) {
|
||||
return null;
|
||||
}
|
||||
Date date = DateUtils.parseDatetime(timeText);
|
||||
if (date == null) {
|
||||
date = DateUtils.str2Date(timeText, DateUtils.date_sdf.get());
|
||||
}
|
||||
return date;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】消息分页-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】发送消息-----------
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SysImMessageVO sendMessage(String userId, Integer tenantId, SysImSendMessageDTO dto) {
|
||||
if (dto == null || oConvertUtils.isEmpty(dto.getConversationId()) || oConvertUtils.isEmpty(dto.getContent())) {
|
||||
throw new JeecgBootException("消息内容不能为空");
|
||||
}
|
||||
assertMember(userId, dto.getConversationId());
|
||||
Date now = new Date();
|
||||
SysImMessage message = new SysImMessage();
|
||||
message.setConversationId(dto.getConversationId());
|
||||
message.setSenderId(userId);
|
||||
message.setContent(dto.getContent().trim());
|
||||
message.setMsgType(oConvertUtils.isEmpty(dto.getMsgType()) ? MSG_TYPE_TEXT : dto.getMsgType());
|
||||
message.setTenantId(tenantId);
|
||||
message.setCreateTime(now);
|
||||
messageMapper.insert(message);
|
||||
|
||||
SysImConversation conversation = conversationMapper.selectById(dto.getConversationId());
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】图片消息会话摘要-----------
|
||||
String lastContent = resolveLastContent(message.getMsgType(), message.getContent());
|
||||
if (CONV_TYPE_GROUP.equals(conversation.getConvType())) {
|
||||
SysUser sender = userMapper.selectById(userId);
|
||||
String senderName = sender != null ? oConvertUtils.getString(sender.getRealname(), sender.getUsername()) : "";
|
||||
if (oConvertUtils.isNotEmpty(senderName)) {
|
||||
lastContent = senderName + ": " + lastContent;
|
||||
}
|
||||
}
|
||||
conversation.setLastContent(truncate(lastContent, 200));
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】图片消息会话摘要-----------
|
||||
conversation.setLastTime(now);
|
||||
conversation.setUpdateTime(now);
|
||||
conversationMapper.updateById(conversation);
|
||||
|
||||
memberMapper.incrementUnreadExceptSender(dto.getConversationId(), userId);
|
||||
SysImMessageVO messageVo = toMessageVo(message, userId);
|
||||
fillBizRecordReceiverPermission(messageVo, message, userId, new HashMap<>(4));
|
||||
pushChatMessage(dto.getConversationId(), userId, messageVo, conversation.getConvType());
|
||||
return messageVo;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】发送消息-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】标记已读-----------
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void markRead(String userId, String conversationId) {
|
||||
SysImConversationMember member = getMember(userId, conversationId);
|
||||
if (member == null) {
|
||||
return;
|
||||
}
|
||||
member.setUnreadCount(0);
|
||||
member.setLastReadTime(new Date());
|
||||
memberMapper.updateById(member);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】标记已读-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】本部门成员列表(含会话摘要)-----------
|
||||
@Override
|
||||
public List<SysImContactVO> listDeptMembers(String userId, Integer tenantId, String orgCode, String keyword) {
|
||||
String resolvedOrgCode = resolveOrgCode(userId, tenantId, orgCode);
|
||||
if (oConvertUtils.isEmpty(resolvedOrgCode)) {
|
||||
throw new JeecgBootException("未获取到当前部门,请切换部门后重试");
|
||||
}
|
||||
List<SysUser> users = userDepartMapper.querySameDepartUserList(resolvedOrgCode, userId, tenantId, keyword);
|
||||
if (users == null || users.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<String, SysImConversationVO> convMap = new HashMap<>(16);
|
||||
if (tenantId != null && tenantId > 0) {
|
||||
for (SysImConversationVO conv : conversationMapper.listMyConversations(userId, tenantId)) {
|
||||
if (oConvertUtils.isNotEmpty(conv.getTargetUserId())) {
|
||||
convMap.put(conv.getTargetUserId(), conv);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<SysImContactVO> result = users.stream().map(user -> {
|
||||
SysImContactVO vo = toContactVo(user);
|
||||
SysImConversationVO conv = convMap.get(user.getId());
|
||||
if (conv != null) {
|
||||
vo.setConversationId(conv.getConversationId());
|
||||
vo.setLastContent(conv.getLastContent());
|
||||
vo.setLastTime(conv.getLastTime());
|
||||
vo.setUnreadCount(conv.getUnreadCount());
|
||||
}
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
result.sort(Comparator
|
||||
.comparing(SysImContactVO::getLastTime, Comparator.nullsLast(Comparator.reverseOrder()))
|
||||
.thenComparing(item -> oConvertUtils.getString(item.getRealname(), item.getUsername())));
|
||||
return result;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】本部门成员列表(含会话摘要)-----------
|
||||
|
||||
private SysImContactVO toContactVo(SysUser user) {
|
||||
SysImContactVO vo = new SysImContactVO();
|
||||
vo.setId(user.getId());
|
||||
vo.setUsername(user.getUsername());
|
||||
vo.setRealname(user.getRealname());
|
||||
vo.setAvatar(user.getAvatar());
|
||||
vo.setOrgCodeTxt(user.getOrgCodeTxt());
|
||||
vo.setUnreadCount(0);
|
||||
return vo;
|
||||
}
|
||||
|
||||
private String resolveOrgCode(String userId, Integer tenantId, String orgCode) {
|
||||
if (oConvertUtils.isNotEmpty(orgCode)) {
|
||||
return orgCode;
|
||||
}
|
||||
if (tenantId != null && tenantId > 0) {
|
||||
List<SysUserDepart> departs = userDepartMapper.getTenantUserDepart(userId, String.valueOf(tenantId));
|
||||
if (departs != null && !departs.isEmpty()) {
|
||||
SysDepart depart = departMapper.selectById(departs.get(0).getDepId());
|
||||
if (depart != null && oConvertUtils.isNotEmpty(depart.getOrgCode())) {
|
||||
return depart.getOrgCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
List<SysUserDepart> departs = userDepartMapper.getUserDepartByUid(userId);
|
||||
if (departs != null && !departs.isEmpty()) {
|
||||
SysDepart depart = departMapper.selectById(departs.get(0).getDepId());
|
||||
if (depart != null) {
|
||||
return depart.getOrgCode();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void createMember(String conversationId, String userId, Date now) {
|
||||
SysImConversationMember member = new SysImConversationMember();
|
||||
member.setConversationId(conversationId);
|
||||
member.setUserId(userId);
|
||||
member.setUnreadCount(0);
|
||||
member.setCreateTime(now);
|
||||
memberMapper.insert(member);
|
||||
}
|
||||
|
||||
private SysImConversationVO buildConversationVo(String conversationId, String userId, Integer tenantId, String targetUserId) {
|
||||
SysImConversation conversation = conversationMapper.selectById(conversationId);
|
||||
if (conversation == null) {
|
||||
return new SysImConversationVO();
|
||||
}
|
||||
return buildConversationVo(conversation, userId, targetUserId);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】已有会话快速打开-----------
|
||||
private SysImConversationVO buildConversationVo(SysImConversation conversation, String userId, String targetUserId) {
|
||||
SysUser target = userMapper.selectById(targetUserId);
|
||||
SysImConversationMember member = getMember(userId, conversation.getId());
|
||||
SysImConversationVO vo = new SysImConversationVO();
|
||||
vo.setConversationId(conversation.getId());
|
||||
vo.setConvType(CONV_TYPE_SINGLE);
|
||||
vo.setLastContent(conversation.getLastContent());
|
||||
vo.setLastTime(conversation.getLastTime());
|
||||
vo.setUnreadCount(member == null ? 0 : member.getUnreadCount());
|
||||
if (target != null) {
|
||||
vo.setTargetUserId(target.getId());
|
||||
vo.setTargetRealname(target.getRealname());
|
||||
vo.setTargetUsername(target.getUsername());
|
||||
vo.setTargetAvatar(target.getAvatar());
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】已有会话快速打开-----------
|
||||
|
||||
private void validateTenantChat(String userId, Integer tenantId, String orgCode, String targetUserId) {
|
||||
if (oConvertUtils.isEmpty(targetUserId)) {
|
||||
throw new JeecgBootException("请选择聊天对象");
|
||||
}
|
||||
if (userId.equals(targetUserId)) {
|
||||
throw new JeecgBootException("不能与自己聊天");
|
||||
}
|
||||
if (tenantId == null || tenantId <= 0) {
|
||||
throw new JeecgBootException("请先选择租户");
|
||||
}
|
||||
Integer selfExist = userTenantMapper.userTenantIzExist(userId, tenantId);
|
||||
Integer targetExist = userTenantMapper.userTenantIzExist(targetUserId, tenantId);
|
||||
if (selfExist == null || selfExist <= 0 || targetExist == null || targetExist <= 0) {
|
||||
throw new JeecgBootException("仅支持与当前租户内用户聊天");
|
||||
}
|
||||
SysUser target = userMapper.selectById(targetUserId);
|
||||
if (target == null) {
|
||||
throw new JeecgBootException("聊天对象不存在");
|
||||
}
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】限制同部门聊天-----------
|
||||
String resolvedOrgCode = resolveOrgCode(userId, tenantId, orgCode);
|
||||
if (oConvertUtils.isEmpty(resolvedOrgCode)) {
|
||||
throw new JeecgBootException("未获取到当前部门,请切换部门后重试");
|
||||
}
|
||||
if (userDepartMapper.countUserInDepartOrgCode(targetUserId, resolvedOrgCode, tenantId) <= 0) {
|
||||
throw new JeecgBootException("仅支持与同部门用户聊天");
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】限制同部门聊天-----------
|
||||
}
|
||||
|
||||
private String buildPairKey(String userId1, String userId2) {
|
||||
if (userId1.compareTo(userId2) <= 0) {
|
||||
return userId1 + "_" + userId2;
|
||||
}
|
||||
return userId2 + "_" + userId1;
|
||||
}
|
||||
|
||||
private SysImConversationMember getMember(String userId, String conversationId) {
|
||||
return memberMapper.selectOne(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversationId)
|
||||
.eq(SysImConversationMember::getUserId, userId));
|
||||
}
|
||||
|
||||
private void assertMember(String userId, String conversationId) {
|
||||
if (getMember(userId, conversationId) == null) {
|
||||
throw new JeecgBootException("无权访问该会话");
|
||||
}
|
||||
}
|
||||
|
||||
private SysImMessageVO toMessageVo(SysImMessage message, String currentUserId) {
|
||||
return toMessageVo(message, currentUserId, null);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】消息列表批量填充发送人-----------
|
||||
private List<SysImMessageVO> toMessageVoList(List<SysImMessage> messages, String currentUserId) {
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> senderIds = messages.stream()
|
||||
.map(SysImMessage::getSenderId)
|
||||
.filter(oConvertUtils::isNotEmpty)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<String, SysUser> userMap = new HashMap<>(senderIds.size());
|
||||
if (!senderIds.isEmpty()) {
|
||||
List<SysUser> users = userMapper.selectBatchIds(senderIds);
|
||||
if (users != null) {
|
||||
for (SysUser user : users) {
|
||||
userMap.put(user.getId(), user);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<SysImMessageVO> result = new ArrayList<>(messages.size());
|
||||
Map<String, Boolean> receiverPermissionCache = new HashMap<>(16);
|
||||
for (SysImMessage message : messages) {
|
||||
SysImMessageVO vo = toMessageVo(message, currentUserId, userMap);
|
||||
fillBizRecordReceiverPermission(vo, message, currentUserId, receiverPermissionCache);
|
||||
result.add(vo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SysImMessageVO toMessageVo(SysImMessage message, String currentUserId, Map<String, SysUser> userMap) {
|
||||
SysImMessageVO vo = new SysImMessageVO();
|
||||
vo.setId(message.getId());
|
||||
vo.setConversationId(message.getConversationId());
|
||||
vo.setSenderId(message.getSenderId());
|
||||
vo.setContent(message.getContent());
|
||||
vo.setMsgType(message.getMsgType());
|
||||
vo.setCreateTime(message.getCreateTime());
|
||||
vo.setMine(currentUserId.equals(message.getSenderId()));
|
||||
SysUser sender = userMap == null ? null : userMap.get(message.getSenderId());
|
||||
if (sender == null && userMap == null) {
|
||||
sender = userMapper.selectById(message.getSenderId());
|
||||
}
|
||||
if (sender != null) {
|
||||
vo.setSenderName(sender.getRealname());
|
||||
vo.setSenderAvatar(sender.getAvatar());
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】发送方提示接收方无功能权限-----------
|
||||
private void fillBizRecordReceiverPermission(SysImMessageVO vo, SysImMessage message, String currentUserId, Map<String, Boolean> cache) {
|
||||
if (!Boolean.TRUE.equals(vo.getMine()) || !MSG_TYPE_BIZ_RECORD.equals(vo.getMsgType())) {
|
||||
return;
|
||||
}
|
||||
SysImConversation conversation = conversationMapper.selectById(message.getConversationId());
|
||||
if (conversation == null || !CONV_TYPE_SINGLE.equals(conversation.getConvType())) {
|
||||
return;
|
||||
}
|
||||
String pagePath = extractBizRecordPagePath(vo.getContent());
|
||||
if (oConvertUtils.isEmpty(pagePath)) {
|
||||
return;
|
||||
}
|
||||
String peerUserId = resolvePeerUserId(message.getConversationId(), currentUserId);
|
||||
if (oConvertUtils.isEmpty(peerUserId)) {
|
||||
return;
|
||||
}
|
||||
String cacheKey = peerUserId + "|" + normalizeBizPagePath(pagePath);
|
||||
Boolean hasPermission = cache.get(cacheKey);
|
||||
if (hasPermission == null) {
|
||||
hasPermission = hasUserPagePathPermission(peerUserId, pagePath);
|
||||
cache.put(cacheKey, hasPermission);
|
||||
}
|
||||
vo.setReceiverHasBizPagePermission(hasPermission);
|
||||
}
|
||||
|
||||
private String resolvePeerUserId(String conversationId, String currentUserId) {
|
||||
List<SysImConversationMember> members = memberMapper.selectList(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversationId));
|
||||
if (members == null || members.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (SysImConversationMember member : members) {
|
||||
if (!currentUserId.equals(member.getUserId())) {
|
||||
return member.getUserId();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String extractBizRecordPagePath(String content) {
|
||||
if (oConvertUtils.isEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject obj = JSONObject.parseObject(content);
|
||||
return obj.getString("pagePath");
|
||||
} catch (Exception e) {
|
||||
log.debug("解析业务明细 pagePath 失败: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeBizPagePath(String pagePath) {
|
||||
if (oConvertUtils.isEmpty(pagePath)) {
|
||||
return "";
|
||||
}
|
||||
String path = pagePath.split("\\?")[0];
|
||||
if (path.contains("&" + IM_RECORD_QUERY_KEY + "=") || path.contains("?" + IM_RECORD_QUERY_KEY + "=")) {
|
||||
path = path.replaceAll("[?&]" + IM_RECORD_QUERY_KEY + "=[^&]*", "");
|
||||
if (path.endsWith("?") || path.endsWith("&")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
}
|
||||
if (path.endsWith("/") && path.length() > 1) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private boolean hasUserPagePathPermission(String userId, String pagePath) {
|
||||
String targetPath = normalizeBizPagePath(pagePath);
|
||||
if (oConvertUtils.isEmpty(userId) || oConvertUtils.isEmpty(targetPath)) {
|
||||
return false;
|
||||
}
|
||||
List<SysPermission> permissions = sysPermissionService.queryByUser(userId);
|
||||
if (permissions == null || permissions.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (SysPermission permission : permissions) {
|
||||
if (permission == null || oConvertUtils.isEmpty(permission.getUrl())) {
|
||||
continue;
|
||||
}
|
||||
if (permission.getMenuType() != null && permission.getMenuType() == 2) {
|
||||
continue;
|
||||
}
|
||||
String url = normalizeBizPagePath(permission.getUrl());
|
||||
if (targetPath.equals(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】发送方提示接收方无功能权限-----------
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】消息列表批量填充发送人-----------
|
||||
|
||||
private void pushChatMessage(String conversationId, String senderId, SysImMessageVO messageVo, String convType) {
|
||||
List<SysImConversationMember> members = memberMapper.selectList(new LambdaQueryWrapper<SysImConversationMember>()
|
||||
.eq(SysImConversationMember::getConversationId, conversationId));
|
||||
for (SysImConversationMember member : members) {
|
||||
if (senderId.equals(member.getUserId())) {
|
||||
continue;
|
||||
}
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put(WebsocketConst.MSG_CMD, WebsocketConst.MSG_CHAT);
|
||||
obj.put(WebsocketConst.MSG_USER_ID, member.getUserId());
|
||||
obj.put("conversationId", conversationId);
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】WebSocket推送会话类型区分群聊-----------
|
||||
obj.put("convType", convType);
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】WebSocket推送会话类型区分群聊-----------
|
||||
obj.put("messageId", messageVo.getId());
|
||||
obj.put("senderId", messageVo.getSenderId());
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】WebSocket推送补全头像字段-----------
|
||||
obj.put("senderName", messageVo.getSenderName());
|
||||
obj.put("senderAvatar", messageVo.getSenderAvatar());
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】WebSocket推送补全头像字段-----------
|
||||
obj.put("content", messageVo.getContent());
|
||||
obj.put("msgType", messageVo.getMsgType());
|
||||
obj.put("createTime", messageVo.getCreateTime());
|
||||
webSocket.sendMessage(member.getUserId(), obj.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
private String truncate(String content, int maxLen) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
return content.length() <= maxLen ? content : content.substring(0, maxLen);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260528 for:【IM聊天-OA】图片消息会话摘要-----------
|
||||
private String resolveLastContent(String msgType, String content) {
|
||||
if (MSG_TYPE_IMAGE.equals(msgType)) {
|
||||
return MSG_IMAGE_PREVIEW;
|
||||
}
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】业务明细消息会话摘要-----------
|
||||
if (MSG_TYPE_BIZ_RECORD.equals(msgType)) {
|
||||
return resolveBizRecordPreview(content);
|
||||
}
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】业务明细消息会话摘要-----------
|
||||
return content;
|
||||
}
|
||||
|
||||
private String resolveBizRecordPreview(String content) {
|
||||
if (oConvertUtils.isEmpty(content)) {
|
||||
return MSG_BIZ_RECORD_PREVIEW;
|
||||
}
|
||||
try {
|
||||
JSONObject obj = JSONObject.parseObject(content);
|
||||
String pageTitle = obj.getString("pageTitle");
|
||||
if (oConvertUtils.isNotEmpty(pageTitle)) {
|
||||
return MSG_BIZ_RECORD_PREVIEW + pageTitle;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("解析业务明细消息摘要失败: {}", e.getMessage());
|
||||
}
|
||||
return MSG_BIZ_RECORD_PREVIEW;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260528 for:【IM聊天-OA】图片消息会话摘要-----------
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.jeecg.modules.im.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* IM 联系人 VO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "IM联系人")
|
||||
public class SysImContactVO {
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
private String id;
|
||||
@Schema(description = "账号")
|
||||
private String username;
|
||||
@Schema(description = "姓名")
|
||||
private String realname;
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
@Schema(description = "部门")
|
||||
private String orgCodeTxt;
|
||||
@Schema(description = "会话ID")
|
||||
private String conversationId;
|
||||
@Schema(description = "最后消息摘要")
|
||||
private String lastContent;
|
||||
@Schema(description = "最后消息时间")
|
||||
private java.util.Date lastTime;
|
||||
@Schema(description = "未读数")
|
||||
private Integer unreadCount;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.jeecg.modules.im.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* IM 会话列表 VO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "IM会话")
|
||||
public class SysImConversationVO {
|
||||
|
||||
@Schema(description = "会话ID")
|
||||
private String conversationId;
|
||||
@Schema(description = "会话类型")
|
||||
private String convType;
|
||||
@Schema(description = "最后消息摘要")
|
||||
private String lastContent;
|
||||
@Schema(description = "最后消息时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastTime;
|
||||
@Schema(description = "未读数")
|
||||
private Integer unreadCount;
|
||||
@Schema(description = "对方用户ID")
|
||||
private String targetUserId;
|
||||
@Schema(description = "对方姓名")
|
||||
private String targetRealname;
|
||||
@Schema(description = "对方账号")
|
||||
private String targetUsername;
|
||||
@Schema(description = "对方头像")
|
||||
private String targetAvatar;
|
||||
@Schema(description = "群名称")
|
||||
private String groupName;
|
||||
@Schema(description = "群成员数")
|
||||
private Integer memberCount;
|
||||
@Schema(description = "群主用户ID")
|
||||
private String ownerId;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.jeecg.modules.im.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IM 群聊详情(群设置)
|
||||
*/
|
||||
//update-begin---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群详情-----------
|
||||
@Data
|
||||
@Schema(description = "IM群聊详情")
|
||||
public class SysImGroupDetailVO {
|
||||
|
||||
@Schema(description = "会话ID")
|
||||
private String conversationId;
|
||||
@Schema(description = "群名称")
|
||||
private String groupName;
|
||||
@Schema(description = "群主用户ID")
|
||||
private String ownerId;
|
||||
@Schema(description = "群成员数")
|
||||
private Integer memberCount;
|
||||
@Schema(description = "当前用户是否群主")
|
||||
private Boolean owner;
|
||||
@Schema(description = "群成员列表")
|
||||
private List<SysImGroupMemberVO> members;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群详情-----------
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.jeecg.modules.im.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* IM 群成员
|
||||
*/
|
||||
//update-begin---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群成员展示-----------
|
||||
@Data
|
||||
@Schema(description = "IM群成员")
|
||||
public class SysImGroupMemberVO {
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
private String userId;
|
||||
@Schema(description = "姓名")
|
||||
private String realname;
|
||||
@Schema(description = "账号")
|
||||
private String username;
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
@Schema(description = "是否群主")
|
||||
private Boolean owner;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260529 for:【IM聊天-OA】群设置-群成员展示-----------
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.jeecg.modules.im.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* IM 消息 VO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "IM消息")
|
||||
public class SysImMessageVO {
|
||||
|
||||
@Schema(description = "消息ID")
|
||||
private String id;
|
||||
@Schema(description = "会话ID")
|
||||
private String conversationId;
|
||||
@Schema(description = "发送人ID")
|
||||
private String senderId;
|
||||
@Schema(description = "发送人姓名")
|
||||
private String senderName;
|
||||
@Schema(description = "发送人头像")
|
||||
private String senderAvatar;
|
||||
@Schema(description = "消息内容")
|
||||
private String content;
|
||||
@Schema(description = "消息类型")
|
||||
private String msgType;
|
||||
@Schema(description = "是否本人发送")
|
||||
private Boolean mine;
|
||||
@Schema(description = "业务明细接收方是否有对应功能权限(仅发送方可见)")
|
||||
private Boolean receiverHasBizPagePermission;
|
||||
@Schema(description = "发送时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -102,9 +102,17 @@ public interface SysUserDepartMapper extends BaseMapper<SysUserDepart>{
|
||||
|
||||
/**
|
||||
* 通过用户id集合获取用户id和部门code
|
||||
*
|
||||
* @param userIdList
|
||||
* @return
|
||||
*/
|
||||
List<SysUserSysDepPostModel> getUserDepPostByUserIds(@Param("userIdList") List<String> userIdList);
|
||||
|
||||
/**
|
||||
* 查询同部门用户(精确 orgCode,不含下级部门)
|
||||
*/
|
||||
List<SysUser> querySameDepartUserList(@Param("orgCode") String orgCode, @Param("excludeUserId") String excludeUserId,
|
||||
@Param("tenantId") Integer tenantId, @Param("keyword") String keyword);
|
||||
|
||||
/**
|
||||
* 用户是否属于指定 orgCode 部门
|
||||
*/
|
||||
int countUserInDepartOrgCode(@Param("userId") String userId, @Param("orgCode") String orgCode, @Param("tenantId") Integer tenantId);
|
||||
}
|
||||
|
||||
@@ -142,4 +142,40 @@
|
||||
#{userId}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- IM聊天:同部门用户(精确 orgCode) -->
|
||||
<select id="querySameDepartUserList" resultType="org.jeecg.modules.system.entity.SysUser">
|
||||
select distinct a.* from sys_user a
|
||||
inner join sys_user_depart b on b.user_id = a.id
|
||||
inner join sys_depart c on b.dep_id = c.id
|
||||
where a.del_flag = 0 and a.status = 1
|
||||
and c.org_code = #{orgCode}
|
||||
and a.username != '_reserve_user_external'
|
||||
<if test="excludeUserId != null and excludeUserId != ''">
|
||||
and a.id != #{excludeUserId}
|
||||
</if>
|
||||
<if test="tenantId != null and tenantId > 0">
|
||||
and a.id in (
|
||||
select user_id from sys_user_tenant where tenant_id = #{tenantId} and status = '1'
|
||||
)
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
<bind name="bindKeyword" value="'%' + keyword + '%'"/>
|
||||
and (a.username like #{bindKeyword} or a.realname like #{bindKeyword})
|
||||
</if>
|
||||
order by a.sort, a.realname
|
||||
</select>
|
||||
|
||||
<select id="countUserInDepartOrgCode" resultType="int">
|
||||
select count(1) from sys_user a
|
||||
inner join sys_user_depart b on b.user_id = a.id
|
||||
inner join sys_depart c on b.dep_id = c.id
|
||||
where a.del_flag = 0 and a.id = #{userId}
|
||||
and c.org_code = #{orgCode}
|
||||
<if test="tenantId != null and tenantId > 0">
|
||||
and a.id in (
|
||||
select user_id from sys_user_tenant where tenant_id = #{tenantId} and status = '1'
|
||||
)
|
||||
</if>
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -157,6 +157,7 @@ spring:
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://xsl.qdxsl.top:50768/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
# url: jdbc:mysql://localhost:3307/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: 123456
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
-- 分类字典:统一租户ID为1002(幂等)
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @mes_tenant_id = 1002;
|
||||
|
||||
UPDATE `sys_category`
|
||||
SET `tenant_id` = @mes_tenant_id,
|
||||
`update_by` = 'admin',
|
||||
`update_time` = NOW()
|
||||
WHERE IFNULL(`tenant_id`, 0) <> @mes_tenant_id;
|
||||
@@ -0,0 +1,75 @@
|
||||
-- 配方日志查询:配合示方/混炼示方修改日志表 + 字典 + 菜单
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_formula_spec_edit_log` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`spec_type` varchar(20) NOT NULL COMMENT '示方分类 formula=配合示方 mixing=混炼示方',
|
||||
`spec_id` varchar(32) NOT NULL COMMENT '示方主表ID',
|
||||
`spec_title` varchar(200) DEFAULT NULL COMMENT '示方标识(胶料代号/规格名)',
|
||||
`issue_number` varchar(64) DEFAULT NULL COMMENT '发行编号',
|
||||
`action_type` varchar(20) NOT NULL COMMENT '操作类型 create=新增 update=修改 delete=删除',
|
||||
`change_summary` varchar(2000) DEFAULT NULL COMMENT '修改内容摘要',
|
||||
`before_snapshot` longtext COMMENT '变更前快照JSON',
|
||||
`after_snapshot` longtext COMMENT '变更后快照JSON',
|
||||
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`modify_by` varchar(64) DEFAULT NULL COMMENT '修改人账号',
|
||||
`modify_by_name` varchar(100) DEFAULT NULL COMMENT '修改人姓名',
|
||||
`tenant_id` int DEFAULT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_fsel_spec_type` (`spec_type`),
|
||||
KEY `idx_fsel_spec_id` (`spec_id`),
|
||||
KEY `idx_fsel_issue_no` (`issue_number`),
|
||||
KEY `idx_fsel_modify_time` (`modify_time`),
|
||||
KEY `idx_fsel_tenant` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES配方示方修改日志';
|
||||
|
||||
INSERT IGNORE INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
VALUES ('1995000000000000107', '配方日志示方分类', 'xslmes_formula_spec_edit_log_type', '配方日志查询-示方分类', 0, 'admin', NOW(), 0, 1002);
|
||||
|
||||
INSERT IGNORE INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000001071', '1995000000000000107', '配合示方', 'formula', 1, 1, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000001072', '1995000000000000107', '混炼示方', 'mixing', 2, 1, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
VALUES ('1995000000000000108', '配方日志操作类型', 'xslmes_formula_spec_edit_log_action', '配方日志查询-操作类型', 0, 'admin', NOW(), 0, 1002);
|
||||
|
||||
INSERT IGNORE INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000001081', '1995000000000000108', '新增', 'create', 1, 1, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000001082', '1995000000000000108', '修改', 'update', 2, 1, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000001083', '1995000000000000108', '删除', 'delete', 3, 1, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (
|
||||
`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`,
|
||||
`menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`,
|
||||
`hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`,
|
||||
`del_flag`, `rule_flag`, `status`, `internal_or_external`
|
||||
) VALUES (
|
||||
'177925970995560', '1900000000000000810', '配方日志查询', '/xslmes/mesXslFormulaSpecEditLog',
|
||||
'xslmes/mesXslFormulaSpecEditLog/MesXslFormulaSpecEditLogList', 1, 'MesXslFormulaSpecEditLogList', NULL,
|
||||
1, NULL, '0', 5.50, 0, 'ant-design:file-search-outlined', 0, 1,
|
||||
0, 0, '配合示方/混炼示方修改日志查询与对比', 'admin', NOW(), 'admin', NOW(),
|
||||
0, 0, '1', 0
|
||||
);
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
VALUES ('177925970995561', '177925970995560', '查询', 2, 'xslmes:mes_xsl_formula_spec_edit_log:list', '1', 1.00, 0, 1, 0, '1', 0, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
VALUES ('177925970995562', '177925970995560', '导出', 2, 'xslmes:mes_xsl_formula_spec_edit_log:exportXls', '1', 2.00, 0, 1, 0, '1', 0, 'admin', NOW());
|
||||
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1'
|
||||
FROM `sys_role` r
|
||||
CROSS JOIN `sys_permission` p
|
||||
WHERE r.`role_code` = 'admin'
|
||||
AND p.`id` IN ('177925970995560', '177925970995561', '177925970995562')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission` rp
|
||||
WHERE rp.`role_id` = r.id AND rp.`permission_id` = p.id
|
||||
);
|
||||
@@ -0,0 +1,40 @@
|
||||
-- 架子车数设定:菜单与字典(数据来源于混炼示方,不建业务表)
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT IGNORE INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
VALUES ('1995000000000000109', '架子车数设定状态', 'xslmes_rack_train_count_setting_status', '架子车数设定-是否已设定车数', 0, 'admin', NOW(), 0, 1002);
|
||||
|
||||
INSERT IGNORE INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000001091', '1995000000000000109', '已设定', '1', 1, 1, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000001092', '1995000000000000109', '未设定', '0', 2, 1, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (
|
||||
`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`,
|
||||
`menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`,
|
||||
`hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`,
|
||||
`del_flag`, `rule_flag`, `status`, `internal_or_external`
|
||||
) VALUES (
|
||||
'177925970995570', '1900000000000000810', '架子车数设定', '/xslmes/mesXslRackTrainCountSetting',
|
||||
'xslmes/mesXslRackTrainCountSetting/MesXslRackTrainCountSettingList', 1, 'MesXslRackTrainCountSettingList', NULL,
|
||||
1, NULL, '0', 2.50, 0, 'ant-design:car-outlined', 0, 1,
|
||||
0, 0, '基于混炼示方查询与维护设定车数', 'admin', NOW(), 'admin', NOW(),
|
||||
0, 0, '1', 0
|
||||
);
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
VALUES ('177925970995571', '177925970995570', '查询', 2, 'xslmes:mes_xsl_rack_train_count_setting:list', '1', 1.00, 0, 1, 0, '1', 0, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
VALUES ('177925970995572', '177925970995570', '编辑', 2, 'xslmes:mes_xsl_rack_train_count_setting:edit', '1', 2.00, 0, 1, 0, '1', 0, 'admin', NOW());
|
||||
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1'
|
||||
FROM `sys_role` r
|
||||
CROSS JOIN `sys_permission` p
|
||||
WHERE r.`role_code` = 'admin'
|
||||
AND p.`id` IN ('177925970995570', '177925970995571', '177925970995572')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission` rp WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||
);
|
||||
@@ -0,0 +1,149 @@
|
||||
-- 胶料快检记录(主子表):字典 + 建表 + 菜单(质量管理下)+ 按钮 + 胶料信息「检验」按钮 + 租户 admin 授权
|
||||
-- 权限前缀:mes:mes_xsl_rubber_quick_test_record:*
|
||||
-- 菜单 ID 段 1860000000000000192
|
||||
-- SET @mes_tenant_id:多租户 admin 授权目标租户
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检记录检验结果', 'xslmes_rubber_quick_test_record_result', '1合格0不合格', 0, 'admin', NOW(), 0, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_record_result' AND `del_flag` = 0);
|
||||
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, '合格', '1', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_record_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, '不合格', '0', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_record_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||
|
||||
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检班次', 'xslmes_rubber_quick_test_work_shift', '班次', 0, 'admin', NOW(), 0, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_work_shift' AND `del_flag` = 0);
|
||||
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||
FROM `sys_dict` d
|
||||
CROSS JOIN (
|
||||
SELECT '早班' AS txt, '1' AS val, 1 AS ord UNION ALL
|
||||
SELECT '中班', '2', 2 UNION ALL
|
||||
SELECT '晚班', '3', 3
|
||||
) v
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_work_shift'
|
||||
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||
|
||||
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检班组', 'xslmes_rubber_quick_test_work_team', '班组', 0, 'admin', NOW(), 0, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_work_team' AND `del_flag` = 0);
|
||||
|
||||
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||
FROM `sys_dict` d
|
||||
CROSS JOIN (
|
||||
SELECT '甲班' AS txt, '1' AS val, 1 AS ord UNION ALL
|
||||
SELECT '乙班', '2', 2 UNION ALL
|
||||
SELECT '丙班', '3', 3
|
||||
) v
|
||||
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_work_team'
|
||||
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_record` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`rubber_material_id` varchar(32) DEFAULT NULL COMMENT '胶料 mes_material.id',
|
||||
`rubber_material_name` varchar(128) DEFAULT NULL COMMENT '胶料名称冗余',
|
||||
`std_id` varchar(32) DEFAULT NULL COMMENT '引用的实验标准 mes_xsl_rubber_quick_test_std.id',
|
||||
`prod_equipment_ledger_id` varchar(32) DEFAULT NULL COMMENT '生产机台 mes_xsl_equipment_ledger.id',
|
||||
`prod_equipment_name` varchar(128) DEFAULT NULL COMMENT '生产机台名称冗余',
|
||||
`production_date` date DEFAULT NULL COMMENT '生产日期',
|
||||
`train_no` varchar(64) DEFAULT NULL COMMENT '车次编号',
|
||||
`work_shift` varchar(8) DEFAULT NULL COMMENT '班次(字典xslmes_rubber_quick_test_work_shift)',
|
||||
`work_team` varchar(8) DEFAULT NULL COMMENT '班组(字典xslmes_rubber_quick_test_work_team)',
|
||||
`inspect_times` int DEFAULT NULL COMMENT '检验次数',
|
||||
`inspect_time` datetime DEFAULT NULL COMMENT '检验时间',
|
||||
`inspector_user_id` varchar(32) DEFAULT NULL COMMENT '检验人用户ID',
|
||||
`inspector_username` varchar(64) DEFAULT NULL COMMENT '检验人账号冗余',
|
||||
`inspector_realname` varchar(64) DEFAULT NULL COMMENT '检验人姓名冗余',
|
||||
`quick_test_type_id` varchar(32) DEFAULT NULL COMMENT '检验类型 mes_xsl_rubber_quick_test_type.id',
|
||||
`quick_test_type_name` varchar(128) DEFAULT NULL COMMENT '检验类型名称冗余',
|
||||
`inspect_result` varchar(2) DEFAULT NULL COMMENT '检验结果(字典xslmes_rubber_quick_test_record_result:1合格0不合格)',
|
||||
`production_plan_no` varchar(100) DEFAULT NULL COMMENT '生产计划号',
|
||||
`inspect_equipment_ledger_id` varchar(32) DEFAULT NULL COMMENT '检验机台 mes_xsl_equipment_ledger.id',
|
||||
`inspect_equipment_name` varchar(128) DEFAULT NULL COMMENT '检验机台名称冗余',
|
||||
`rubber_card_no` varchar(100) DEFAULT NULL COMMENT '胶料卡片号',
|
||||
`rubber_batch_no` varchar(100) DEFAULT NULL COMMENT '胶料批次',
|
||||
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_mrqtr_material` (`rubber_material_id`),
|
||||
KEY `idx_mrqtr_std` (`std_id`),
|
||||
KEY `idx_mrqtr_tenant` (`tenant_id`),
|
||||
KEY `idx_mrqtr_inspect_time` (`inspect_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检记录';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_record_line` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`record_id` varchar(32) NOT NULL COMMENT '主表 mes_xsl_rubber_quick_test_record.id',
|
||||
`data_point_id` varchar(32) DEFAULT NULL COMMENT '数据点 mes_xsl_rubber_quick_test_data_point.id',
|
||||
`inspect_item` varchar(128) DEFAULT NULL COMMENT '检验项目(数据点名称,只读带出)',
|
||||
`lower_limit` decimal(18,6) DEFAULT NULL COMMENT '检验下限(只读带出)',
|
||||
`inspect_value` decimal(18,6) DEFAULT NULL COMMENT '检验值',
|
||||
`upper_limit` decimal(18,6) DEFAULT NULL COMMENT '检验上限(只读带出)',
|
||||
`sort_no` int DEFAULT NULL COMMENT '排序号',
|
||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_mrqtrl_record` (`record_id`),
|
||||
UNIQUE KEY `uk_mrqtrl_record_point` (`record_id`, `data_point_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检记录明细';
|
||||
|
||||
SET @mes_tenant_id = 1002;
|
||||
|
||||
SET @mes_quality_pid = IFNULL(
|
||||
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||
'1860000000000000162'
|
||||
);
|
||||
|
||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||
VALUES ('1860000000000000192', @mes_quality_pid, '胶料快检记录', '/xslmes/mesXslRubberQuickTestRecord', 'xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecordList', 'MesXslRubberQuickTestRecordList', 1, NULL, '1', 5, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||
`component_name` = VALUES(`component_name`), `sort_no` = VALUES(`sort_no`), `is_leaf` = VALUES(`is_leaf`), `keep_alive` = VALUES(`keep_alive`);
|
||||
|
||||
UPDATE `sys_permission` SET `icon` = 'ant-design:file-search-outlined' WHERE `id` = '1860000000000000192' AND `del_flag` = 0;
|
||||
|
||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||
('1860000000000000193', '1860000000000000192', '新增', 2, 'mes:mes_xsl_rubber_quick_test_record:add', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000194', '1860000000000000192', '编辑', 2, 'mes:mes_xsl_rubber_quick_test_record:edit', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000195', '1860000000000000192', '删除', 2, 'mes:mes_xsl_rubber_quick_test_record:delete', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000196', '1860000000000000192', '批量删除', 2, 'mes:mes_xsl_rubber_quick_test_record:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000197', '1860000000000000192', '导出', 2, 'mes:mes_xsl_rubber_quick_test_record:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000198', '1860000000000000192', '导入', 2, 'mes:mes_xsl_rubber_quick_test_record:importExcel', '1', '1', 0, 'admin', NOW()),
|
||||
('1860000000000000199', '1860000000000000192', '从胶料生成', 2, 'mes:mes_xsl_rubber_quick_test_record:batchFromMaterial', '1', '1', 0, 'admin', NOW())
|
||||
ON DUPLICATE KEY UPDATE `perms` = VALUES(`perms`), `name` = VALUES(`name`);
|
||||
|
||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||
('1860000000000000200', '1860000000000000011', '胶料快检', 2, 'mes:mes_material:rubberQuickTestInspect', '1', '1', 0, 'admin', NOW())
|
||||
ON DUPLICATE KEY UPDATE `perms` = VALUES(`perms`), `name` = VALUES(`name`);
|
||||
|
||||
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NOW(), '127.0.0.1'
|
||||
FROM sys_role r
|
||||
CROSS JOIN sys_permission p
|
||||
WHERE r.tenant_id = @mes_tenant_id
|
||||
AND r.role_code = 'admin'
|
||||
AND p.id IN (
|
||||
'1860000000000000192',
|
||||
'1860000000000000193', '1860000000000000194', '1860000000000000195', '1860000000000000196',
|
||||
'1860000000000000197', '1860000000000000198', '1860000000000000199',
|
||||
'1860000000000000200'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_permission rp
|
||||
WHERE rp.role_id = r.id AND rp.permission_id = p.id
|
||||
);
|
||||
@@ -0,0 +1,6 @@
|
||||
-- 胶料快检记录:单号 record_no(JL+yyyyMMdd+4位流水)
|
||||
ALTER TABLE `mes_xsl_rubber_quick_test_record`
|
||||
ADD COLUMN `record_no` varchar(32) DEFAULT NULL COMMENT '单号(JL+日期+4位流水,如JL202605280001)' AFTER `id`;
|
||||
|
||||
ALTER TABLE `mes_xsl_rubber_quick_test_record`
|
||||
ADD UNIQUE KEY `uk_mrqtr_record_no` (`record_no`);
|
||||
@@ -0,0 +1,70 @@
|
||||
-- 租户 IM 聊天:表结构 + 我的租户菜单
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_im_conversation` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`conv_type` varchar(10) NOT NULL DEFAULT 'single' COMMENT '会话类型 single单聊',
|
||||
`user_pair_key` varchar(80) DEFAULT NULL COMMENT '单聊唯一键(较小userId_较大userId)',
|
||||
`tenant_id` int DEFAULT NULL COMMENT '租户ID',
|
||||
`last_content` varchar(500) DEFAULT NULL COMMENT '最后一条消息摘要',
|
||||
`last_time` datetime DEFAULT NULL COMMENT '最后消息时间',
|
||||
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_conv_pair` (`tenant_id`, `user_pair_key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IM会话表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_im_conversation_member` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`conversation_id` varchar(32) NOT NULL COMMENT '会话ID',
|
||||
`user_id` varchar(32) NOT NULL COMMENT '用户ID',
|
||||
`unread_count` int NOT NULL DEFAULT 0 COMMENT '未读数',
|
||||
`last_read_time` datetime DEFAULT NULL COMMENT '最后已读时间',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_member` (`conversation_id`, `user_id`),
|
||||
KEY `idx_im_member_user` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IM会话成员表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_im_message` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`conversation_id` varchar(32) NOT NULL COMMENT '会话ID',
|
||||
`sender_id` varchar(32) NOT NULL COMMENT '发送人ID',
|
||||
`content` text COMMENT '消息内容',
|
||||
`msg_type` varchar(20) NOT NULL DEFAULT 'text' COMMENT '消息类型 text/image/file',
|
||||
`tenant_id` int DEFAULT NULL COMMENT '租户ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '发送时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_im_msg_conv_time` (`conversation_id`, `create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IM消息表';
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (
|
||||
`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`,
|
||||
`menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`,
|
||||
`hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`,
|
||||
`del_flag`, `rule_flag`, `status`, `internal_or_external`
|
||||
) VALUES (
|
||||
'1995000000000000110', '1674708136602542082', 'IM聊天', '/my/ImChat',
|
||||
'system/im/ImChat', 1, 'ImChat', NULL,
|
||||
1, NULL, '0', 1.10, 0, 'ant-design:message-outlined', 0, 1,
|
||||
0, 0, '租户内用户即时聊天', 'admin', NOW(), 'admin', NOW(),
|
||||
0, 0, '1', 0
|
||||
);
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000000111', '1995000000000000110', '查询', 2, 'sys:im:chat:list', '1', 1.00, 0, 1, 0, '1', 0, 'admin', NOW());
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000000112', '1995000000000000110', '发送', 2, 'sys:im:chat:send', '1', 2.00, 0, 1, 0, '1', 0, 'admin', NOW());
|
||||
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1'
|
||||
FROM `sys_role` r
|
||||
CROSS JOIN `sys_permission` p
|
||||
WHERE r.`role_code` = 'admin'
|
||||
AND p.`id` IN ('1995000000000000110', '1995000000000000111', '1995000000000000112')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission` rp WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
-- IM 群聊:扩展会话表字段
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
ALTER TABLE `sys_im_conversation`
|
||||
ADD COLUMN `group_name` varchar(100) DEFAULT NULL COMMENT '群名称' AFTER `user_pair_key`,
|
||||
ADD COLUMN `owner_id` varchar(32) DEFAULT NULL COMMENT '群主用户ID' AFTER `group_name`;
|
||||
|
||||
ALTER TABLE `sys_im_conversation`
|
||||
MODIFY COLUMN `conv_type` varchar(10) NOT NULL DEFAULT 'single' COMMENT '会话类型 single单聊 group群聊';
|
||||
|
||||
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
VALUES ('1995000000000000113', '1995000000000000110', '创建群聊', 2, 'sys:im:chat:group', '1', 3.00, 0, 1, 0, '1', 0, 'admin', NOW());
|
||||
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1'
|
||||
FROM `sys_role` r
|
||||
CROSS JOIN `sys_permission` p
|
||||
WHERE r.`role_code` = 'admin'
|
||||
AND p.`id` = '1995000000000000113'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission` rp WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||
);
|
||||
@@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""生成分类字典「行政区划」Flyway SQL(国家-省-市-县)"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
MYSQL_DIR = Path(__file__).resolve().parents[1]
|
||||
JEECG_BOOT = Path(__file__).resolve().parents[9]
|
||||
PCA_PATH = JEECG_BOOT / 'jeecg-boot-base-core/src/main/resources/static/pca.json'
|
||||
OUT_PATH = MYSQL_DIR / 'V3.9.2_105__sys_region_category_dict.sql'
|
||||
|
||||
MES_TENANT_ID = 1002
|
||||
|
||||
pca = json.loads(PCA_PATH.read_text(encoding='utf-8'))
|
||||
|
||||
OTHER_COUNTRIES = [
|
||||
('阿富汗', 'AF'), ('阿尔巴尼亚', 'AL'), ('阿尔及利亚', 'DZ'), ('安道尔', 'AD'), ('安哥拉', 'AO'),
|
||||
('安提瓜和巴布达', 'AG'), ('阿根廷', 'AR'), ('亚美尼亚', 'AM'), ('澳大利亚', 'AU'), ('奥地利', 'AT'),
|
||||
('阿塞拜疆', 'AZ'), ('巴哈马', 'BS'), ('巴林', 'BH'), ('孟加拉国', 'BD'), ('巴巴多斯', 'BB'),
|
||||
('白俄罗斯', 'BY'), ('比利时', 'BE'), ('伯利兹', 'BZ'), ('贝宁', 'BJ'), ('不丹', 'BT'),
|
||||
('玻利维亚', 'BO'), ('波黑', 'BA'), ('博茨瓦纳', 'BW'), ('巴西', 'BR'), ('文莱', 'BN'),
|
||||
('保加利亚', 'BG'), ('布基纳法索', 'BF'), ('布隆迪', 'BI'), ('佛得角', 'CV'), ('柬埔寨', 'KH'),
|
||||
('喀麦隆', 'CM'), ('加拿大', 'CA'), ('中非', 'CF'), ('乍得', 'TD'), ('智利', 'CL'),
|
||||
('哥伦比亚', 'CO'), ('科摩罗', 'KM'), ('刚果(布)', 'CG'), ('刚果(金)', 'CD'), ('哥斯达黎加', 'CR'),
|
||||
('科特迪瓦', 'CI'), ('克罗地亚', 'HR'), ('古巴', 'CU'), ('塞浦路斯', 'CY'), ('捷克', 'CZ'),
|
||||
('丹麦', 'DK'), ('吉布提', 'DJ'), ('多米尼克', 'DM'), ('多米尼加', 'DO'), ('厄瓜多尔', 'EC'),
|
||||
('埃及', 'EG'), ('萨尔瓦多', 'SV'), ('赤道几内亚', 'GQ'), ('厄立特里亚', 'ER'), ('爱沙尼亚', 'EE'),
|
||||
('斯威士兰', 'SZ'), ('埃塞俄比亚', 'ET'), ('斐济', 'FJ'), ('芬兰', 'FI'), ('法国', 'FR'),
|
||||
('加蓬', 'GA'), ('冈比亚', 'GM'), ('格鲁吉亚', 'GE'), ('德国', 'DE'), ('加纳', 'GH'),
|
||||
('希腊', 'GR'), ('格林纳达', 'GD'), ('危地马拉', 'GT'), ('几内亚', 'GN'), ('几内亚比绍', 'GW'),
|
||||
('圭亚那', 'GY'), ('海地', 'HT'), ('洪都拉斯', 'HN'), ('匈牙利', 'HU'), ('冰岛', 'IS'),
|
||||
('印度', 'IN'), ('印度尼西亚', 'ID'), ('伊朗', 'IR'), ('伊拉克', 'IQ'), ('爱尔兰', 'IE'),
|
||||
('以色列', 'IL'), ('意大利', 'IT'), ('牙买加', 'JM'), ('日本', 'JP'), ('约旦', 'JO'),
|
||||
('哈萨克斯坦', 'KZ'), ('肯尼亚', 'KE'), ('基里巴斯', 'KI'), ('科威特', 'KW'), ('吉尔吉斯斯坦', 'KG'),
|
||||
('老挝', 'LA'), ('拉脱维亚', 'LV'), ('黎巴嫩', 'LB'), ('莱索托', 'LS'), ('利比里亚', 'LR'),
|
||||
('利比亚', 'LY'), ('列支敦士登', 'LI'), ('立陶宛', 'LT'), ('卢森堡', 'LU'), ('马达加斯加', 'MG'),
|
||||
('马拉维', 'MW'), ('马来西亚', 'MY'), ('马尔代夫', 'MV'), ('马里', 'ML'), ('马耳他', 'MT'),
|
||||
('马绍尔群岛', 'MH'), ('毛里塔尼亚', 'MR'), ('毛里求斯', 'MU'), ('墨西哥', 'MX'), ('密克罗尼西亚', 'FM'),
|
||||
('摩尔多瓦', 'MD'), ('摩纳哥', 'MC'), ('蒙古', 'MN'), ('黑山', 'ME'), ('摩洛哥', 'MA'),
|
||||
('莫桑比克', 'MZ'), ('缅甸', 'MM'), ('纳米比亚', 'NA'), ('瑙鲁', 'NR'), ('尼泊尔', 'NP'),
|
||||
('荷兰', 'NL'), ('新西兰', 'NZ'), ('尼加拉瓜', 'NI'), ('尼日尔', 'NE'), ('尼日利亚', 'NG'),
|
||||
('朝鲜', 'KP'), ('北马其顿', 'MK'), ('挪威', 'NO'), ('阿曼', 'OM'), ('巴基斯坦', 'PK'),
|
||||
('帕劳', 'PW'), ('巴拿马', 'PA'), ('巴布亚新几内亚', 'PG'), ('巴拉圭', 'PY'), ('秘鲁', 'PE'),
|
||||
('菲律宾', 'PH'), ('波兰', 'PL'), ('葡萄牙', 'PT'), ('卡塔尔', 'QA'), ('罗马尼亚', 'RO'),
|
||||
('俄罗斯', 'RU'), ('卢旺达', 'RW'), ('圣基茨和尼维斯', 'KN'), ('圣卢西亚', 'LC'), ('圣文森特和格林纳丁斯', 'VC'),
|
||||
('萨摩亚', 'WS'), ('圣马力诺', 'SM'), ('圣多美和普林西比', 'ST'), ('沙特阿拉伯', 'SA'), ('塞内加尔', 'SN'),
|
||||
('塞尔维亚', 'RS'), ('塞舌尔', 'SC'), ('塞拉利昂', 'SL'), ('新加坡', 'SG'), ('斯洛伐克', 'SK'),
|
||||
('斯洛文尼亚', 'SI'), ('所罗门群岛', 'SB'), ('索马里', 'SO'), ('南非', 'ZA'), ('韩国', 'KR'),
|
||||
('南苏丹', 'SS'), ('西班牙', 'ES'), ('斯里兰卡', 'LK'), ('苏丹', 'SD'), ('苏里南', 'SR'),
|
||||
('瑞典', 'SE'), ('瑞士', 'CH'), ('叙利亚', 'SY'), ('塔吉克斯坦', 'TJ'), ('坦桑尼亚', 'TZ'),
|
||||
('泰国', 'TH'), ('东帝汶', 'TL'), ('多哥', 'TG'), ('汤加', 'TO'), ('特立尼达和多巴哥', 'TT'),
|
||||
('突尼斯', 'TN'), ('土耳其', 'TR'), ('土库曼斯坦', 'TM'), ('图瓦卢', 'TV'), ('乌干达', 'UG'),
|
||||
('乌克兰', 'UA'), ('阿联酋', 'AE'), ('英国', 'GB'), ('美国', 'US'), ('乌拉圭', 'UY'),
|
||||
('乌兹别克斯坦', 'UZ'), ('瓦努阿图', 'VU'), ('梵蒂冈', 'VA'), ('委内瑞拉', 'VE'), ('越南', 'VN'),
|
||||
('也门', 'YE'), ('赞比亚', 'ZM'), ('津巴布韦', 'ZW'),
|
||||
]
|
||||
|
||||
seq = 0
|
||||
|
||||
|
||||
def make_id(code: str) -> str:
|
||||
global seq
|
||||
if code.isdigit():
|
||||
return f'1995{int(code):015d}'
|
||||
seq += 1
|
||||
return f'1995900000000{seq:06d}'
|
||||
|
||||
|
||||
def esc(s: str) -> str:
|
||||
return s.replace('\\', '\\\\').replace("'", "''")
|
||||
|
||||
|
||||
nodes = []
|
||||
root_id = '1995000000000000001'
|
||||
nodes.append({'id': root_id, 'pid': '0', 'name': '行政区划', 'code': 'SYS_REGION', 'has_child': '1'})
|
||||
|
||||
cn_id = make_id('86')
|
||||
nodes.append({'id': cn_id, 'pid': root_id, 'name': '中国', 'code': 'SYS_REGION_86', 'has_child': '1'})
|
||||
|
||||
for prov_code, prov_name in pca['86'].items():
|
||||
prov_id = make_id(prov_code)
|
||||
cities = pca.get(prov_code, {})
|
||||
nodes.append({'id': prov_id, 'pid': cn_id, 'name': prov_name, 'code': prov_code, 'has_child': '1' if cities else '0'})
|
||||
for city_code, city_name in cities.items():
|
||||
city_id = make_id(city_code)
|
||||
counties = pca.get(city_code, {})
|
||||
nodes.append({'id': city_id, 'pid': prov_id, 'name': city_name, 'code': city_code, 'has_child': '1' if counties else '0'})
|
||||
for county_code, county_name in counties.items():
|
||||
nodes.append({'id': make_id(county_code), 'pid': city_id, 'name': county_name, 'code': county_code, 'has_child': '0'})
|
||||
|
||||
for name, iso in OTHER_COUNTRIES:
|
||||
nodes.append({'id': make_id(f'C{iso}'), 'pid': root_id, 'name': name, 'code': f'SYS_REGION_{iso}', 'has_child': '0'})
|
||||
|
||||
lines = [
|
||||
'-- 分类字典:国家-省-市-县 四级行政区划(幂等)',
|
||||
'-- 数据来源:JeecgBoot pca.json + ISO3166 国家列表',
|
||||
'SET NAMES utf8mb4;',
|
||||
'',
|
||||
]
|
||||
batch_size = 50
|
||||
for i in range(0, len(nodes), batch_size):
|
||||
batch = nodes[i : i + batch_size]
|
||||
lines.append('INSERT INTO `sys_category` (`id`, `pid`, `name`, `code`, `has_child`, `is_rubber`, `tenant_id`, `create_by`, `create_time`)')
|
||||
values = []
|
||||
for n in batch:
|
||||
values.append(
|
||||
"SELECT '{id}', '{pid}', '{name}', '{code}', '{has_child}', '0', {tenant_id}, 'admin', NOW() FROM DUAL "
|
||||
"WHERE NOT EXISTS (SELECT 1 FROM `sys_category` WHERE `code` = '{code}')".format(
|
||||
id=n['id'], pid=n['pid'], name=esc(n['name']), code=n['code'], has_child=n['has_child'], tenant_id=MES_TENANT_ID
|
||||
)
|
||||
)
|
||||
lines.append('\nUNION ALL\n'.join(values))
|
||||
lines.append(';')
|
||||
lines.append('')
|
||||
|
||||
OUT_PATH.write_text('\n'.join(lines), encoding='utf-8')
|
||||
print(f'Generated {len(nodes)} nodes -> {OUT_PATH}')
|
||||
print(f'File size: {OUT_PATH.stat().st_size / 1024:.1f} KB')
|
||||
16
jeecgboot-vue3/src/design/im-record-locate.less
Normal file
16
jeecgboot-vue3/src/design/im-record-locate.less
Normal file
@@ -0,0 +1,16 @@
|
||||
.im-record-locate-row {
|
||||
> td {
|
||||
background: #fff7e6 !important;
|
||||
animation: im-record-locate-flash 1.2s ease-in-out 2;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes im-record-locate-flash {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: inset 0 0 0 9999px rgba(250, 173, 20, 0.08);
|
||||
}
|
||||
50% {
|
||||
box-shadow: inset 0 0 0 9999px rgba(250, 173, 20, 0.22);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
@import 'public.less';
|
||||
@import 'ant/index.less';
|
||||
@import './theme.less';
|
||||
@import './im-record-locate.less';
|
||||
@import './entry.css';
|
||||
|
||||
input:-webkit-autofill {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { reactive, ref, Ref, unref } from 'vue';
|
||||
import { reactive, ref, Ref, unref, onUnmounted, watch, nextTick } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { merge } from 'lodash-es';
|
||||
import { DynamicProps } from '/#/utils';
|
||||
import { BasicTableProps, TableActionType, useTable } from '/@/components/Table';
|
||||
@@ -9,6 +10,16 @@ import { useMethods } from '/@/hooks/system/useMethods';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { filterObj } from '/@/utils/common/compUtils';
|
||||
import { isFunction } from '@/utils/is';
|
||||
import { registerImPageListProvider } from '/@/views/system/im/imPageListRegistry';
|
||||
import { buildImPageListSnapshot } from '/@/views/system/im/imPageListUtil';
|
||||
import { IM_RECORD_QUERY_KEY } from '/@/views/system/im/imBizRecordMessage';
|
||||
import {
|
||||
IM_RECORD_LOCATE_CLEAR_EVENT,
|
||||
IM_RECORD_LOCATE_EVENT,
|
||||
removeImRecordQueryFromRoute,
|
||||
resolveImLocateRecordId,
|
||||
scrollToImRecordRowWithRetry,
|
||||
} from '/@/views/system/im/imRecordLocate';
|
||||
const { handleExportXls, handleImportXls } = useMethods();
|
||||
|
||||
// 定义 useListPage 方法所需参数
|
||||
@@ -59,7 +70,168 @@ export function useListPage(options: ListPageOptions) {
|
||||
|
||||
const tableContext = useListTable(options.tableProps);
|
||||
|
||||
const [, { getForm, reload, setLoading, getColumns }, { selectedRowKeys }] = tableContext;
|
||||
const route = useRoute();
|
||||
const [, tableMethods, { selectedRowKeys }] = tableContext;
|
||||
const { getForm, reload, setLoading, getColumns } = tableMethods;
|
||||
const imHighlightRecordId = ref('');
|
||||
let clearHighlightTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let locatingRecordId = '';
|
||||
|
||||
onUnmounted(() => {
|
||||
if (clearHighlightTimer) {
|
||||
clearTimeout(clearHighlightTimer);
|
||||
clearHighlightTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】列表页注册 IM 明细快照提供器-----------
|
||||
onUnmounted(
|
||||
registerImPageListProvider(() => {
|
||||
const sourceColumns = tableMethods.getColumns?.() || options.tableProps?.columns || [];
|
||||
return buildImPageListSnapshot({
|
||||
title: (options.tableProps?.title as string) || '',
|
||||
pagePath: route.fullPath,
|
||||
rowKey: (options.tableProps?.rowKey as string) || 'id',
|
||||
sourceColumns,
|
||||
records: tableMethods.getDataSource?.() || [],
|
||||
});
|
||||
}),
|
||||
);
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】列表页注册 IM 明细快照提供器-----------
|
||||
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】IM 消息链接跳转后定位列表行-----------
|
||||
function isTableReady() {
|
||||
try {
|
||||
tableMethods.getDataSource?.();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function applyImRecordRowClassName() {
|
||||
if (!isTableReady()) {
|
||||
return;
|
||||
}
|
||||
const rowKey = (options.tableProps?.rowKey as string) || 'id';
|
||||
tableMethods.setProps?.({
|
||||
rowClassName: (record: Recordable) => {
|
||||
if (imHighlightRecordId.value && String(record[rowKey]) === imHighlightRecordId.value) {
|
||||
return 'im-record-locate-row';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 等待列表首屏数据加载(兼容 immediate:false + 左侧树页面) */
|
||||
async function waitForLocateContext(maxWaitMs = 3500) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < maxWaitMs) {
|
||||
if (!isTableReady()) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
continue;
|
||||
}
|
||||
const data = tableMethods.getDataSource?.() || [];
|
||||
if (data.length > 0) {
|
||||
return true;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
}
|
||||
return isTableReady();
|
||||
}
|
||||
|
||||
function findRecordInTable(recordId: string) {
|
||||
const rowKey = (options.tableProps?.rowKey as string) || 'id';
|
||||
const data = tableMethods.getDataSource?.() || [];
|
||||
return data.some((item) => String(item[rowKey]) === recordId);
|
||||
}
|
||||
|
||||
function clearImRecordHighlight() {
|
||||
imHighlightRecordId.value = '';
|
||||
applyImRecordRowClassName();
|
||||
}
|
||||
|
||||
function scheduleClearImRecordHighlight(delayMs = 3500) {
|
||||
if (clearHighlightTimer) {
|
||||
clearTimeout(clearHighlightTimer);
|
||||
}
|
||||
clearHighlightTimer = setTimeout(() => {
|
||||
clearImRecordHighlight();
|
||||
clearHighlightTimer = null;
|
||||
}, delayMs);
|
||||
}
|
||||
|
||||
async function applyImRecordHighlight(recordId: string) {
|
||||
imHighlightRecordId.value = recordId;
|
||||
applyImRecordRowClassName();
|
||||
await nextTick();
|
||||
await new Promise((resolve) => requestAnimationFrame(() => resolve(undefined)));
|
||||
applyImRecordRowClassName();
|
||||
await scrollToImRecordRowWithRetry(recordId);
|
||||
}
|
||||
|
||||
async function locateImRecordRow(recordId: string) {
|
||||
if (locatingRecordId === recordId) {
|
||||
return;
|
||||
}
|
||||
locatingRecordId = recordId;
|
||||
try {
|
||||
if (!(await waitForLocateContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!findRecordInTable(recordId)) {
|
||||
$message.createMessage.warning('当前列表中未找到对应数据');
|
||||
removeImRecordQueryFromRoute();
|
||||
return;
|
||||
}
|
||||
|
||||
await applyImRecordHighlight(recordId);
|
||||
scheduleClearImRecordHighlight();
|
||||
// 直链 URL 场景:仅改地址栏,避免 router.replace 导致 fullPath 变化 remount
|
||||
removeImRecordQueryFromRoute();
|
||||
} finally {
|
||||
locatingRecordId = '';
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.path, route.query[IM_RECORD_QUERY_KEY]] as const,
|
||||
([path, queryRecordId]) => {
|
||||
const recordId = resolveImLocateRecordId(path, queryRecordId);
|
||||
if (!recordId) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => locateImRecordRow(recordId));
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function handleImRecordLocateEvent(e: Event) {
|
||||
const detail = (e as CustomEvent<{ path: string; recordId: string }>).detail;
|
||||
if (!detail?.path || detail.path !== route.path || !detail.recordId) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => locateImRecordRow(detail.recordId));
|
||||
}
|
||||
|
||||
function handleImRecordLocateClearEvent() {
|
||||
if (clearHighlightTimer) {
|
||||
clearTimeout(clearHighlightTimer);
|
||||
clearHighlightTimer = null;
|
||||
}
|
||||
locatingRecordId = '';
|
||||
clearImRecordHighlight();
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener(IM_RECORD_LOCATE_EVENT, handleImRecordLocateEvent);
|
||||
window.removeEventListener(IM_RECORD_LOCATE_CLEAR_EVENT, handleImRecordLocateClearEvent);
|
||||
});
|
||||
window.addEventListener(IM_RECORD_LOCATE_EVENT, handleImRecordLocateEvent);
|
||||
window.addEventListener(IM_RECORD_LOCATE_CLEAR_EVENT, handleImRecordLocateClearEvent);
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】IM 消息链接跳转后定位列表行-----------
|
||||
|
||||
// 导出 excel
|
||||
async function onExportXls() {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useRouter } from 'vue-router';
|
||||
import { REDIRECT_NAME } from '/@/router/constant';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
import { clearImRecordLocateState, stripImRecordQuery } from '/@/views/system/im/imRecordLocate';
|
||||
|
||||
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
|
||||
|
||||
@@ -43,10 +44,17 @@ export function useGo(_router?: Router) {
|
||||
* @description: redo current page
|
||||
*/
|
||||
export const useRedo = (_router?: Router, otherQuery?: Recordable) => {
|
||||
const { push, currentRoute } = _router || useRouter();
|
||||
const { query, params = {}, name, fullPath } = unref(currentRoute.value);
|
||||
const router = _router || useRouter();
|
||||
const { push, currentRoute, resolve: resolveRoute } = router;
|
||||
function redo(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】标签页刷新时取消 IM 定位-----------
|
||||
clearImRecordLocateState();
|
||||
const rawRoute = unref(currentRoute.value);
|
||||
let { query, params = {}, name, fullPath } = rawRoute;
|
||||
query = stripImRecordQuery(query as Recordable);
|
||||
fullPath = resolveRoute({ path: rawRoute.path, query, hash: rawRoute.hash }).fullPath;
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】标签页刷新时取消 IM 定位-----------
|
||||
if (name === REDIRECT_NAME) {
|
||||
resolve(false);
|
||||
return;
|
||||
|
||||
@@ -2,16 +2,64 @@
|
||||
|
||||
import { unref } from 'vue';
|
||||
import { useWebSocket, WebSocketResult } from '@vueuse/core';
|
||||
import md5 from 'crypto-js/md5';
|
||||
import { getToken } from '/@/utils/auth';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
|
||||
let result: WebSocketResult<any>;
|
||||
const listeners = new Map();
|
||||
let connectedUrl = '';
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天】WS 重连后通知监听方重新拉取消息-----------
|
||||
let wsConnectionCount = 0;
|
||||
const reconnectListeners = new Set<() => void>();
|
||||
|
||||
export function onWebSocketReconnect(callback: () => void) {
|
||||
reconnectListeners.add(callback);
|
||||
return () => reconnectListeners.delete(callback);
|
||||
}
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天】WS 重连后通知监听方重新拉取消息-----------
|
||||
|
||||
/**
|
||||
* 构建系统 WebSocket 地址(含 context-path,如 /jeecg-boot)
|
||||
*/
|
||||
export function buildSystemWebSocketUrl(): string {
|
||||
const glob = useGlobSetting();
|
||||
const userStore = useUserStore();
|
||||
const userInfo = unref(userStore.getUserInfo);
|
||||
if (!userInfo?.id) {
|
||||
return '';
|
||||
}
|
||||
const token = getToken() || '';
|
||||
const wsClientId = md5(token).toString();
|
||||
const wsUserId = `${userInfo.id}_${wsClientId}`;
|
||||
let base = (glob.domainUrl || '').replace('https://', 'wss://').replace('http://', 'ws://');
|
||||
base = base.replace(/\/$/, '');
|
||||
const apiPath = (glob.apiUrl || '/jeecg-boot').replace(/\/$/, '');
|
||||
const prefix = apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
|
||||
return `${base}${prefix}/websocket/${wsUserId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保 WebSocket 已连接(聊天页等场景可主动调用)
|
||||
*/
|
||||
export function ensureWebSocketConnected(): void {
|
||||
const url = buildSystemWebSocketUrl();
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
if (result?.status?.value === 'OPEN' && connectedUrl === url) {
|
||||
return;
|
||||
}
|
||||
connectWebSocket(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启 WebSocket 链接,全局只需执行一次
|
||||
* @param url
|
||||
*/
|
||||
export function connectWebSocket(url: string) {
|
||||
connectedUrl = url;
|
||||
// 代码逻辑说明: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
|
||||
const token = (getToken() || '') as string;
|
||||
result = useWebSocket(url, {
|
||||
@@ -28,7 +76,15 @@ export function connectWebSocket(url: string) {
|
||||
protocols: [token],
|
||||
// 代码逻辑说明: [issues/6662] 演示系统socket总断,换一个写法
|
||||
onConnected: function (ws) {
|
||||
wsConnectionCount++;
|
||||
console.log('[WebSocket] 连接成功', ws);
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天】WS 重连后通知监听方重新拉取消息-----------
|
||||
if (wsConnectionCount > 1) {
|
||||
reconnectListeners.forEach((cb) => {
|
||||
try { cb(); } catch (err) { console.error(err); }
|
||||
});
|
||||
}
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天】WS 重连后通知监听方重新拉取消息-----------
|
||||
},
|
||||
onDisconnected: function (ws, event) {
|
||||
console.log('[WebSocket] 连接断开:', ws, event);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<Tooltip :title="tooltipTitle" placement="bottom" :mouseEnterDelay="0.5">
|
||||
<span :class="`${prefixCls}-action__item refresh-cache-item`" class="refresh-cache-btn" @click="clearCache">
|
||||
<SyncOutlined :spin="loading" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
import { SyncOutlined } from '@ant-design/icons-vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useRefreshCache } from './useRefreshCache';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RefreshCache',
|
||||
components: { Tooltip, SyncOutlined },
|
||||
setup() {
|
||||
const { prefixCls } = useDesign('layout-header');
|
||||
const { loading, clearCache, tooltipTitle } = useRefreshCache();
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
loading,
|
||||
clearCache,
|
||||
tooltipTitle,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.refresh-cache-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 12px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div :class="[prefixCls, { 'is-disabled': imPageActive }]" @click="openChat">
|
||||
<MessageOutlined />
|
||||
<ImChatModal @register="registerModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from 'vue';
|
||||
import { MessageOutlined } from '@ant-design/icons-vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import ImChatModal from '/@/views/system/im/ImChatModal.vue';
|
||||
import { prefetchImChatData } from '/@/views/system/im/imCache';
|
||||
import { useImChat } from '/@/views/system/im/useImChat';
|
||||
import { refreshImUnread } from '/@/views/system/im/useImUnread';
|
||||
import { useImChatPageActive } from '/@/views/system/im/imSession';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HeaderImChat',
|
||||
components: {
|
||||
MessageOutlined,
|
||||
ImChatModal,
|
||||
},
|
||||
setup() {
|
||||
const { prefixCls } = useDesign('header-im-chat');
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const imPageActive = useImChatPageActive();
|
||||
const { openChatModal } = useImChat();
|
||||
|
||||
function openChat() {
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】头部打开 IM 时快照当前功能页名称-----------
|
||||
openChatModal(openModal);
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】头部打开 IM 时快照当前功能页名称-----------
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
prefetchImChatData();
|
||||
refreshImUnread(true);
|
||||
});
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
imPageActive,
|
||||
openChat,
|
||||
registerModal,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-header-im-chat';
|
||||
|
||||
.@{prefix-cls} {
|
||||
cursor: pointer;
|
||||
padding: 0 10px;
|
||||
font-size: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
width: 0.9em;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -14,3 +14,7 @@ export const ErrorAction = createAsyncComponent(() => import('./ErrorAction.vue'
|
||||
export const LockScreen = createAsyncComponent(() => import('./LockScreen.vue'));
|
||||
|
||||
export { FullScreen };
|
||||
|
||||
export { default as RefreshCache } from './RefreshCache.vue';
|
||||
|
||||
export const ImChat = createAsyncComponent(() => import('./im-chat/index.vue'));
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<Badge :count="messageCount" :overflowCount="9" :offset="[-4, 18]" :numberStyle="numberStyle" @click="clickBadge('')">
|
||||
<Badge :count="messageCount" :overflowCount="99" :offset="[-4, 18]" :numberStyle="numberStyle" @click="clickBadge('')">
|
||||
<BellOutlined />
|
||||
</Badge>
|
||||
|
||||
<DynamicNotice ref="dynamicNoticeRef" v-bind="dynamicNoticeProps" />
|
||||
<DetailModal @register="registerDetail" />
|
||||
|
||||
<sys-message-modal @register="registerMessageModal" @refresh="reloadCount" :messageCount="messageCount"></sys-message-modal>
|
||||
<sys-message-modal @register="registerMessageModal" @refresh="reloadCount" :systemMessageCount="systemMessageCount"></sys-message-modal>
|
||||
<!-- 修改密码弹窗 -->
|
||||
<ChangePasswordModal @register="changePwdModal"></ChangePasswordModal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, unref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { computed, defineComponent, ref, unref, reactive, onMounted, onUnmounted, getCurrentInstance } from 'vue';
|
||||
import { Popover, Tabs, Badge } from 'ant-design-vue';
|
||||
import { BellOutlined } from '@ant-design/icons-vue';
|
||||
// import { tabListData } from './data';
|
||||
@@ -25,16 +25,16 @@
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
|
||||
import { connectWebSocket, onWebSocket, offWebSocket, buildSystemWebSocketUrl } from '/@/hooks/web/useWebSocket';
|
||||
import { readAllMsg } from '/@/views/monitor/mynews/mynews.api';
|
||||
import { getToken } from '/@/utils/auth';
|
||||
import md5 from 'crypto-js/md5';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import SysMessageModal from '/@/views/system/message/components/SysMessageModal.vue'
|
||||
import ChangePasswordModal from './ChangePasswordModal.vue'
|
||||
import { ElectronEnum } from '/@/enums/jeecgEnum';
|
||||
import { defHttp } from "@/utils/http/axios";
|
||||
import { handleImChatSocket } from '/@/views/system/im/imCache';
|
||||
import { useImUnread } from '/@/views/system/im/useImUnread';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -85,11 +85,20 @@
|
||||
}
|
||||
|
||||
const popoverVisible = ref<boolean>(false);
|
||||
const systemMessageCount = ref(0);
|
||||
const { totalUnread: imUnreadCount, conversationUnreadCount: imConversationUnreadCount, refreshImUnread: refreshImUnreadCount } = useImUnread();
|
||||
const messageCount = computed(() => (systemMessageCount.value || 0) + (imConversationUnreadCount.value || 0));
|
||||
|
||||
onMounted(() => {
|
||||
initWebSocket();
|
||||
refreshImUnreadCount(true);
|
||||
loadData();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
offWebSocket(onWebSocketMessage);
|
||||
});
|
||||
|
||||
const messageCount = ref<number>(0)
|
||||
function mapAnnouncement(item) {
|
||||
return {
|
||||
...item,
|
||||
@@ -113,7 +122,7 @@
|
||||
let msgCount = await getUnreadMessageCount();
|
||||
// 代码逻辑说明: 【QQYUN-12162】OA项目改造,系统重消息拆分,目前消息都在一起 需按分类进行拆分---
|
||||
unReadNum.value = msgCount;
|
||||
messageCount.value = msgCount.count?msgCount.count:0;
|
||||
systemMessageCount.value = msgCount.count ? msgCount.count : 0;
|
||||
// 代码逻辑说明: 【JHHB-13】桌面应用消息通知
|
||||
if (glob.isElectronPlatform) {
|
||||
window[ElectronEnum.ELECTRON_API].sendNotifyFlash(messageCount.value);
|
||||
@@ -124,7 +133,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
function onWebSocketMessage(data) {
|
||||
if (data.cmd === 'chat') {
|
||||
handleImChatSocket(data);
|
||||
refreshImUnreadCount(false);
|
||||
return;
|
||||
}
|
||||
if (data.cmd === 'topic' || data.cmd === 'user') {
|
||||
if (data.noticeType) {
|
||||
noticeType.value = data.noticeType;
|
||||
}
|
||||
notification(data);
|
||||
loadData();
|
||||
window.setTimeout(() => loadData(), 800);
|
||||
}
|
||||
}
|
||||
|
||||
function onNoticeClick(record) {
|
||||
try {
|
||||
@@ -148,35 +171,17 @@
|
||||
|
||||
// 初始化 WebSocket
|
||||
function initWebSocket() {
|
||||
let token = getToken();
|
||||
//将登录token生成一个短的标识
|
||||
let wsClientId = md5(token);
|
||||
let userId = unref(userStore.getUserInfo).id + "_" + wsClientId;
|
||||
// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
|
||||
let url = glob.domainUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
|
||||
const url = buildSystemWebSocketUrl();
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
connectWebSocket(url);
|
||||
onWebSocket(onWebSocketMessage);
|
||||
}
|
||||
|
||||
function onWebSocketMessage(data) {
|
||||
if (data.cmd === 'topic' || data.cmd === 'user') {
|
||||
// 代码逻辑说明: VUEN-1674【严重bug】系统通知,为什么必须刷新右上角才提示
|
||||
if(data.noticeType){
|
||||
noticeType.value = data.noticeType;
|
||||
}
|
||||
//后台保存数据太慢 前端延迟刷新消息
|
||||
setTimeout(()=>{
|
||||
// 代码逻辑说明: 【JHHB-13】桌面应用消息通知
|
||||
notification(data);
|
||||
loadData();
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
// 桌面应用通知
|
||||
function notification(data) {
|
||||
if (glob.isElectronPlatform && (data.noticeType || data.cmd == 'email')) {
|
||||
// 流程、文件、日程、系统、会议
|
||||
// flow、file、plan、system、meeting
|
||||
let title = '';
|
||||
let msgTxt = '';
|
||||
let path = '';
|
||||
@@ -203,6 +208,7 @@
|
||||
window[ElectronEnum.ELECTRON_API].sendNotification(`有新的${title}消息`, msgTxt, path);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空消息
|
||||
function onEmptyNotify() {
|
||||
popoverVisible.value = false;
|
||||
@@ -212,6 +218,7 @@
|
||||
try {
|
||||
await editCementSend(id);
|
||||
await loadData();
|
||||
refreshImUnreadCount(true);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { refreshCache, queryAllDictItems } from '/@/views/system/dict/dict.api';
|
||||
import { refreshDragCache } from '/@/api/common/api';
|
||||
import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
|
||||
import { removeAuthCache, setAuthCache } from '/@/utils/auth';
|
||||
|
||||
/** 顶部/用户菜单共用的刷新缓存逻辑 */
|
||||
export function useRefreshCache() {
|
||||
const { t } = useI18n();
|
||||
const { createMessage } = useMessage();
|
||||
const userStore = useUserStore();
|
||||
const loading = ref(false);
|
||||
|
||||
async function clearCache() {
|
||||
if (loading.value) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await refreshCache();
|
||||
await refreshDragCache();
|
||||
if (result.success) {
|
||||
const res = await queryAllDictItems();
|
||||
removeAuthCache(DB_DICT_DATA_KEY);
|
||||
setAuthCache(DB_DICT_DATA_KEY, res.result);
|
||||
createMessage.success(t('layout.header.refreshCacheComplete'));
|
||||
userStore.setAllDictItems(res.result);
|
||||
} else {
|
||||
createMessage.error(t('layout.header.refreshCacheFailure'));
|
||||
}
|
||||
} catch {
|
||||
createMessage.error(t('layout.header.refreshCacheFailure'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
clearCache,
|
||||
tooltipTitle: t('layout.header.dropdownItemRefreshCache'),
|
||||
};
|
||||
}
|
||||
@@ -44,7 +44,6 @@
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/src/hooks/web/useMessage';
|
||||
import { useGo } from '/@/hooks/web/usePage';
|
||||
import headerImg from '/@/assets/images/header.jpg';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
@@ -52,15 +51,11 @@
|
||||
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
|
||||
import { refreshCache, queryAllDictItems } from '/@/views/system/dict/dict.api';
|
||||
import { DB_DICT_DATA_KEY } from '/src/enums/cacheEnum';
|
||||
import { removeAuthCache, setAuthCache } from '/src/utils/auth';
|
||||
import { useRefreshCache } from '../useRefreshCache';
|
||||
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
||||
import { getRefPromise } from '/@/utils/index';
|
||||
import { refreshDragCache } from "@/api/common/api";
|
||||
|
||||
type MenuEvent = 'logout' | 'doc' | 'lock' | 'cache' | 'depart' | 'defaultHomePage' | 'password' | 'account';
|
||||
const { createMessage } = useMessage();
|
||||
export default defineComponent({
|
||||
name: 'UserDropdown',
|
||||
components: {
|
||||
@@ -84,6 +79,7 @@
|
||||
const passwordVisible = ref(false);
|
||||
const lockActionVisible = ref(false);
|
||||
const lockActionRef = ref(null);
|
||||
const { clearCache } = useRefreshCache();
|
||||
|
||||
const getUserInfo = computed(() => {
|
||||
const { realname = '', avatar, desc } = userStore.getUserInfo || {};
|
||||
@@ -119,22 +115,6 @@
|
||||
openWindow(SITE_URL);
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
async function clearCache() {
|
||||
const result = await refreshCache();
|
||||
const dragRes = await refreshDragCache();
|
||||
console.log('dragRes', dragRes);
|
||||
if (result.success) {
|
||||
const res = await queryAllDictItems();
|
||||
removeAuthCache(DB_DICT_DATA_KEY);
|
||||
setAuthCache(DB_DICT_DATA_KEY, res.result);
|
||||
createMessage.success(t('layout.header.refreshCacheComplete'));
|
||||
// 代码逻辑说明: 【issues/7433】vue3 数据字典优化建议
|
||||
userStore.setAllDictItems(res.result);
|
||||
} else {
|
||||
createMessage.error(t('layout.header.refreshCacheFailure'));
|
||||
}
|
||||
}
|
||||
// 切换部门
|
||||
function updateCurrentDepart() {
|
||||
loginSelectRef.value.show();
|
||||
|
||||
@@ -29,10 +29,14 @@
|
||||
|
||||
<Notify v-if="getShowNotice" :class="`${prefixCls}-action__item notify-item`" />
|
||||
|
||||
<ImChat :class="`${prefixCls}-action__item im-chat-item`" />
|
||||
|
||||
<FullScreen v-if="getShowFullScreen" :class="`${prefixCls}-action__item fullscreen-item`" />
|
||||
|
||||
<LockScreen v-if="getUseLockPage" />
|
||||
|
||||
<RefreshCache />
|
||||
|
||||
<AppLocalePicker v-if="getShowLocalePicker" :reload="true" :showText="false" :class="`${prefixCls}-action__item`" />
|
||||
|
||||
<UserDropDown :theme="getHeaderTheme" />
|
||||
@@ -64,7 +68,7 @@
|
||||
import { SettingButtonPositionEnum } from '/@/enums/appEnum';
|
||||
import { AppLocalePicker } from '/@/components/Application';
|
||||
|
||||
import { UserDropDown, LayoutBreadcrumb, FullScreen, Notify, ErrorAction, LockScreen } from './components';
|
||||
import { UserDropDown, LayoutBreadcrumb, FullScreen, Notify, ImChat, ErrorAction, LockScreen, RefreshCache } from './components';
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
@@ -86,9 +90,11 @@
|
||||
LayoutBreadcrumb,
|
||||
LayoutMenu,
|
||||
UserDropDown,
|
||||
RefreshCache,
|
||||
AppLocalePicker,
|
||||
FullScreen,
|
||||
Notify,
|
||||
ImChat,
|
||||
AppSearch,
|
||||
ErrorAction,
|
||||
LockScreen,
|
||||
|
||||
@@ -198,6 +198,8 @@ export const useUserStore = defineStore({
|
||||
await this.setLoginInfo({ ...data, isLogin: true });
|
||||
// 代码逻辑说明: 登录成功后缓存拖拽模块的接口前缀
|
||||
localStorage.setItem(JDragConfigEnum.DRAG_BASE_URL, useGlobSetting().domainUrl);
|
||||
// 登录后异步预取 IM 聊天数据,减少打开聊天时的等待
|
||||
import('/@/views/system/im/imCache').then(({ prefetchImChatData }) => prefetchImChatData());
|
||||
|
||||
// 代码逻辑说明: 修复登录成功后,没有正确重定向的问题
|
||||
let redirect = router.currentRoute.value?.query?.redirect as string;
|
||||
@@ -284,6 +286,9 @@ export const useUserStore = defineStore({
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录前清除 IM 聊天缓存
|
||||
import('/@/views/system/im/imCache').then(({ clearImCache }) => clearImCache());
|
||||
|
||||
// let username:any = this.userInfo && this.userInfo.username;
|
||||
// if(username){
|
||||
// removeAuthCache(username)
|
||||
|
||||
@@ -5,6 +5,15 @@
|
||||
<a-button type="primary" v-auth="'mes:mes_material:add'" @click="handleAdd" preIcon="ant-design:plus-outlined">新增</a-button>
|
||||
<a-button type="primary" v-auth="'mes:mes_material:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
|
||||
<j-upload-button type="primary" v-auth="'mes:mes_material:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-auth="'mes:mes_material:rubberQuickTestInspect'"
|
||||
preIcon="ant-design:experiment-outlined"
|
||||
:disabled="selectedRowKeys.length === 0"
|
||||
@click="handleRubberQuickTest"
|
||||
>
|
||||
检验
|
||||
</a-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
@@ -29,7 +38,10 @@ import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import MesMaterialModal from './modules/MesMaterialModal.vue';
|
||||
import { columns, searchFormSchema } from './MesMaterial.data';
|
||||
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './MesMaterial.api';
|
||||
import { batchFromMaterial } from '/@/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.api';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const { tableContext, onExportXls, onImportXls } = useListPage({
|
||||
tableProps: {
|
||||
@@ -63,6 +75,18 @@ async function batchHandleDelete() {
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
async function handleRubberQuickTest() {
|
||||
if (!selectedRowKeys.value?.length) {
|
||||
createMessage.warning('请至少选择一条胶料');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await batchFromMaterial({ materialIds: [...selectedRowKeys.value] });
|
||||
createMessage.success('快检记录已生成,请到「胶料快检记录」中编辑');
|
||||
} catch (e: any) {
|
||||
createMessage.error(e?.message || '生成失败');
|
||||
}
|
||||
}
|
||||
function getTableAction(record) {
|
||||
return [{ label: '编辑', onClick: handleEdit.bind(null, record), auth: 'mes:mes_material:edit' }];
|
||||
}
|
||||
|
||||
442
jeecgboot-vue3/src/views/system/im/ImBizRecordMessageContent.vue
Normal file
442
jeecgboot-vue3/src/views/system/im/ImBizRecordMessageContent.vue
Normal file
@@ -0,0 +1,442 @@
|
||||
<template>
|
||||
|
||||
<div class="im-biz-record-message">
|
||||
|
||||
<div v-if="showNoPermission" class="im-biz-record-no-permission">暂无当前消息权限</div>
|
||||
|
||||
<template v-else>
|
||||
|
||||
<template v-if="isSingleItem">
|
||||
|
||||
<div class="im-biz-record-item">
|
||||
|
||||
<div class="im-biz-record-table-wrap">
|
||||
|
||||
<table class="im-biz-record-table im-biz-record-table--detail">
|
||||
|
||||
<tbody>
|
||||
|
||||
<tr v-for="field in resolveItemFields(singleItem)" :key="field.label">
|
||||
|
||||
<th>{{ field.label }}</th>
|
||||
|
||||
<td>{{ field.value }}</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<a class="im-biz-record-link" @click.prevent="handleLinkClick(singleItem.linkPath)">
|
||||
|
||||
<Icon icon="ant-design:link-outlined" />
|
||||
|
||||
<span>查看并定位到此数据</span>
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
<!-- 多条:列表表,第一列为定位链接 -->
|
||||
|
||||
<template v-else>
|
||||
|
||||
<div class="im-biz-record-table-wrap im-biz-record-table-wrap--list">
|
||||
|
||||
<table class="im-biz-record-table im-biz-record-table--list">
|
||||
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
|
||||
<th class="im-biz-record-link-col">链接</th>
|
||||
|
||||
<th v-for="columnLabel in listColumnLabels" :key="columnLabel">{{ columnLabel }}</th>
|
||||
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
<tr v-for="(item, index) in payload.items" :key="item.recordId || index">
|
||||
|
||||
<td class="im-biz-record-link-col">
|
||||
|
||||
<a class="im-biz-record-link" @click.prevent="handleLinkClick(item.linkPath)">
|
||||
|
||||
<Icon icon="ant-design:link-outlined" />
|
||||
|
||||
<span>定位</span>
|
||||
|
||||
</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td v-for="columnLabel in listColumnLabels" :key="columnLabel">
|
||||
|
||||
{{ getFieldValue(item, columnLabel) }}
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<div v-if="showPeerNoPermissionTip" class="im-biz-record-peer-tip">对方无此功能权限</div>
|
||||
|
||||
</template>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import type { ImBizRecordItem, ImBizRecordPayload } from './imBizRecordMessage';
|
||||
|
||||
import {
|
||||
|
||||
getImBizRecordFieldValueByLabel,
|
||||
|
||||
resolveImBizRecordItemFields,
|
||||
|
||||
resolveImBizRecordListColumnLabels,
|
||||
|
||||
} from './imBizRecordMessage';
|
||||
|
||||
import { navigateImBizRecordLink } from './imRecordLocate';
|
||||
|
||||
import { hasImBizRecordPagePermission } from './imBizRecordPermission';
|
||||
|
||||
|
||||
|
||||
defineOptions({ name: 'ImBizRecordMessageContent' });
|
||||
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
payload: ImBizRecordPayload;
|
||||
|
||||
mine?: boolean;
|
||||
|
||||
receiverHasBizPagePermission?: boolean;
|
||||
|
||||
}>();
|
||||
|
||||
|
||||
|
||||
const isSingleItem = computed(() => props.payload.items.length === 1);
|
||||
|
||||
const singleItem = computed(() => props.payload.items[0]);
|
||||
|
||||
const listColumnLabels = computed(() => resolveImBizRecordListColumnLabels(props.payload.items));
|
||||
|
||||
const hasPagePermission = computed(() => hasImBizRecordPagePermission(props.payload.pagePath));
|
||||
|
||||
const showNoPermission = computed(() => !props.mine && !hasPagePermission.value);
|
||||
|
||||
const showPeerNoPermissionTip = computed(
|
||||
() => !!props.mine && props.receiverHasBizPagePermission === false,
|
||||
);
|
||||
|
||||
function resolveItemFields(item: ImBizRecordItem) {
|
||||
|
||||
return resolveImBizRecordItemFields(item);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getFieldValue(item: ImBizRecordItem, label: string) {
|
||||
|
||||
return getImBizRecordFieldValueByLabel(item, label);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function handleLinkClick(linkPath: string) {
|
||||
|
||||
if (!linkPath || showNoPermission.value) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
await navigateImBizRecordLink(linkPath);
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.im-biz-record-message {
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
gap: 12px;
|
||||
|
||||
min-width: 280px;
|
||||
|
||||
max-width: 420px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.im-biz-record-no-permission {
|
||||
|
||||
padding: 12px 10px;
|
||||
|
||||
font-size: 13px;
|
||||
|
||||
line-height: 1.5;
|
||||
|
||||
color: #8c8c8c;
|
||||
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.im-biz-record-peer-tip {
|
||||
|
||||
display: inline-flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
align-self: flex-start;
|
||||
|
||||
margin-top: 4px;
|
||||
|
||||
padding: 2px 8px;
|
||||
|
||||
border-radius: 10px;
|
||||
|
||||
background: #fff7e6;
|
||||
|
||||
border: 1px solid #ffd591;
|
||||
|
||||
font-size: 12px;
|
||||
|
||||
line-height: 1.5;
|
||||
|
||||
color: #d46b08;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.im-biz-record-item {
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
gap: 8px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.im-biz-record-table-wrap {
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
border: 1px solid #f0f0f0;
|
||||
|
||||
border-radius: 6px;
|
||||
|
||||
background: #fff;
|
||||
|
||||
|
||||
|
||||
&--list {
|
||||
|
||||
overflow-x: auto;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.im-biz-record-table {
|
||||
|
||||
width: 100%;
|
||||
|
||||
border-collapse: collapse;
|
||||
|
||||
font-size: 13px;
|
||||
|
||||
line-height: 1.5;
|
||||
|
||||
|
||||
|
||||
th,
|
||||
|
||||
td {
|
||||
|
||||
padding: 8px 10px;
|
||||
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
vertical-align: top;
|
||||
|
||||
word-break: break-word;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
tr:last-child {
|
||||
|
||||
th,
|
||||
|
||||
td {
|
||||
|
||||
border-bottom: none;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
&--detail {
|
||||
|
||||
table-layout: fixed;
|
||||
|
||||
|
||||
|
||||
th {
|
||||
|
||||
width: 38%;
|
||||
|
||||
background: #fafafa;
|
||||
|
||||
color: #595959;
|
||||
|
||||
font-weight: 500;
|
||||
|
||||
text-align: left;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
td {
|
||||
|
||||
color: #262626;
|
||||
|
||||
background: #fff;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
&--list {
|
||||
|
||||
min-width: 100%;
|
||||
|
||||
table-layout: auto;
|
||||
|
||||
|
||||
|
||||
thead th {
|
||||
|
||||
background: #fafafa;
|
||||
|
||||
color: #595959;
|
||||
|
||||
font-weight: 500;
|
||||
|
||||
text-align: left;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
tbody td {
|
||||
|
||||
color: #262626;
|
||||
|
||||
background: #fff;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.im-biz-record-link-col {
|
||||
|
||||
width: 72px;
|
||||
|
||||
min-width: 72px;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.im-biz-record-link {
|
||||
|
||||
display: inline-flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 4px;
|
||||
|
||||
font-size: 12px;
|
||||
|
||||
color: #1677ff;
|
||||
|
||||
text-decoration: underline;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
|
||||
|
||||
&:hover {
|
||||
|
||||
color: #0958d9;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
2189
jeecgboot-vue3/src/views/system/im/ImChat.vue
Normal file
2189
jeecgboot-vue3/src/views/system/im/ImChat.vue
Normal file
File diff suppressed because it is too large
Load Diff
413
jeecgboot-vue3/src/views/system/im/ImChatInput.vue
Normal file
413
jeecgboot-vue3/src/views/system/im/ImChatInput.vue
Normal file
@@ -0,0 +1,413 @@
|
||||
<template>
|
||||
<div class="im-chat-input-wrap">
|
||||
<div class="im-chat-input" :class="{ focused: inputFocused, disabled: disabled }">
|
||||
<a-textarea
|
||||
ref="textareaRef"
|
||||
:value="modelValue"
|
||||
:auto-size="{ minRows: 3, maxRows: 8 }"
|
||||
placeholder="请输入消息"
|
||||
:bordered="false"
|
||||
:disabled="disabled"
|
||||
class="im-chat-input-textarea"
|
||||
@update:value="handleInput"
|
||||
@focus="inputFocused = true"
|
||||
@blur="handleBlur"
|
||||
@keydown="handleKeydown"
|
||||
/>
|
||||
<div class="im-chat-input-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<a-popover
|
||||
v-model:open="emojiVisible"
|
||||
trigger="click"
|
||||
placement="topLeft"
|
||||
overlay-class-name="im-emoji-popover"
|
||||
:overlay-style="{ padding: 0 }"
|
||||
:get-popup-container="getPopupContainer"
|
||||
>
|
||||
<template #content>
|
||||
<Picker
|
||||
:picker-styles="pickerStyles"
|
||||
:i18n="emojiI18n"
|
||||
:data="emojiIndex"
|
||||
emoji="grinning"
|
||||
:native="true"
|
||||
:show-preview="false"
|
||||
:infinite-scroll="false"
|
||||
:show-search="true"
|
||||
:show-skin-tones="false"
|
||||
set="native"
|
||||
@select="handleSelectEmoji"
|
||||
/>
|
||||
</template>
|
||||
<button type="button" class="toolbar-btn" title="表情" :disabled="disabled">
|
||||
<Icon icon="ant-design:smile-outlined" />
|
||||
</button>
|
||||
</a-popover>
|
||||
|
||||
<a-upload
|
||||
:show-upload-list="false"
|
||||
:action="uploadUrl"
|
||||
:headers="uploadHeaders"
|
||||
:data="{ biz: 'im/chat' }"
|
||||
:accept="IM_IMAGE_ACCEPT"
|
||||
:before-upload="beforeImageUpload"
|
||||
:disabled="disabled || uploadingImage"
|
||||
@change="handleImageUploadChange"
|
||||
>
|
||||
<button type="button" class="toolbar-btn" title="发送图片" :disabled="disabled || uploadingImage">
|
||||
<Icon v-if="!uploadingImage" icon="ant-design:picture-outlined" />
|
||||
<LoadingOutlined v-else spin />
|
||||
</button>
|
||||
</a-upload>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<div class="send-btn-group">
|
||||
<a-button type="primary" class="send-btn" :disabled="!canSend" :loading="sending" @click="emitSend">
|
||||
发送(S)
|
||||
</a-button>
|
||||
<a-dropdown :trigger="['click']" placement="topRight">
|
||||
<a-button type="primary" class="send-btn-arrow" :disabled="sending">
|
||||
<Icon icon="ant-design:down-outlined" />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu :selected-keys="[sendMode]" @click="handleSendModeChange">
|
||||
<a-menu-item key="enter">按 Enter 发送</a-menu-item>
|
||||
<a-menu-item key="ctrlEnter">按 Ctrl+Enter 发送</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="im-chat-input-hint">{{ sendModeHint }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import type { UploadChangeParam, UploadProps } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import 'emoji-mart-vue-fast/css/emoji-mart.css';
|
||||
import { getGloablEmojiIndex } from '/@/components/jeecg/comment/useComment';
|
||||
import { uploadUrl } from '/@/api/common/api';
|
||||
import { getHeaders } from '/@/utils/common/compUtils';
|
||||
import { getEmojiInsertText, IM_IMAGE_ACCEPT, IM_IMAGE_MAX_SIZE } from './imMessageUtil';
|
||||
|
||||
type SendMode = 'enter' | 'ctrlEnter';
|
||||
const SEND_MODE_KEY = 'im-chat-send-mode';
|
||||
|
||||
defineOptions({ name: 'ImChatInput' });
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string;
|
||||
disabled?: boolean;
|
||||
sending?: boolean;
|
||||
}>(),
|
||||
{
|
||||
disabled: false,
|
||||
sending: false,
|
||||
},
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
(e: 'send'): void;
|
||||
(e: 'image-uploaded', imagePath: string): void;
|
||||
}>();
|
||||
|
||||
const emojiI18n = {
|
||||
categories: {
|
||||
recent: '最近使用',
|
||||
smileys: '全部表情',
|
||||
people: '人物',
|
||||
nature: '自然',
|
||||
foods: '食物',
|
||||
activity: '活动',
|
||||
places: '地点',
|
||||
objects: '物品',
|
||||
symbols: '符号',
|
||||
flags: '旗帜',
|
||||
},
|
||||
search: '搜索表情',
|
||||
notfound: '未找到表情',
|
||||
};
|
||||
|
||||
const pickerStyles = { width: '360px', border: 'none' };
|
||||
const emojiIndex = getGloablEmojiIndex();
|
||||
const uploadHeaders = getHeaders();
|
||||
const textareaRef = ref<{ resizableTextArea?: { textArea: HTMLTextAreaElement } }>();
|
||||
const inputFocused = ref(false);
|
||||
const emojiVisible = ref(false);
|
||||
const uploadingImage = ref(false);
|
||||
const sendMode = ref<SendMode>(loadSendMode());
|
||||
|
||||
const canSend = computed(() => !props.disabled && !!props.modelValue.trim() && !props.sending);
|
||||
|
||||
const sendModeHint = computed(() =>
|
||||
sendMode.value === 'enter' ? 'Enter / Alt+S 发送,Ctrl+Enter 换行' : 'Ctrl+Enter / Alt+S 发送,Enter 换行',
|
||||
);
|
||||
|
||||
function loadSendMode(): SendMode {
|
||||
const saved = localStorage.getItem(SEND_MODE_KEY);
|
||||
return saved === 'ctrlEnter' ? 'ctrlEnter' : 'enter';
|
||||
}
|
||||
|
||||
function handleSendModeChange({ key }: { key: string }) {
|
||||
sendMode.value = key as SendMode;
|
||||
localStorage.setItem(SEND_MODE_KEY, key);
|
||||
}
|
||||
|
||||
function getPopupContainer(triggerNode: HTMLElement) {
|
||||
return triggerNode.parentElement || document.body;
|
||||
}
|
||||
|
||||
function getTextareaEl() {
|
||||
return textareaRef.value?.resizableTextArea?.textArea;
|
||||
}
|
||||
|
||||
function handleInput(value: string) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
|
||||
function handleBlur() {
|
||||
window.setTimeout(() => {
|
||||
inputFocused.value = false;
|
||||
}, 120);
|
||||
}
|
||||
|
||||
function insertAtCursor(text: string) {
|
||||
const el = getTextareaEl();
|
||||
const current = props.modelValue || '';
|
||||
if (!el) {
|
||||
emit('update:modelValue', current + text);
|
||||
return;
|
||||
}
|
||||
const start = el.selectionStart ?? current.length;
|
||||
const end = el.selectionEnd ?? start;
|
||||
const nextValue = current.slice(0, start) + text + current.slice(end);
|
||||
emit('update:modelValue', nextValue);
|
||||
const cursor = start + text.length;
|
||||
window.requestAnimationFrame(() => {
|
||||
el.focus();
|
||||
el.setSelectionRange(cursor, cursor);
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelectEmoji(item: Record<string, any>) {
|
||||
const text = getEmojiInsertText(item);
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
insertAtCursor(text);
|
||||
emojiVisible.value = false;
|
||||
inputFocused.value = true;
|
||||
window.requestAnimationFrame(() => getTextareaEl()?.focus());
|
||||
}
|
||||
|
||||
function emitSend() {
|
||||
if (!canSend.value) {
|
||||
return;
|
||||
}
|
||||
emit('send');
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.altKey && !e.ctrlKey && !e.shiftKey && (e.key === 's' || e.key === 'S')) {
|
||||
e.preventDefault();
|
||||
emitSend();
|
||||
return;
|
||||
}
|
||||
if (e.key !== 'Enter') {
|
||||
return;
|
||||
}
|
||||
const enterSend = sendMode.value === 'enter';
|
||||
const shouldSend = enterSend ? !e.ctrlKey && !e.shiftKey : e.ctrlKey && !e.shiftKey;
|
||||
const shouldNewLine = enterSend ? e.ctrlKey || e.shiftKey : !e.ctrlKey || e.shiftKey;
|
||||
|
||||
if (shouldSend) {
|
||||
e.preventDefault();
|
||||
emitSend();
|
||||
return;
|
||||
}
|
||||
if (shouldNewLine && e.ctrlKey && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
insertAtCursor('\n');
|
||||
}
|
||||
}
|
||||
|
||||
const beforeImageUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
if (!file.type?.startsWith('image/')) {
|
||||
message.warning('仅支持发送图片文件');
|
||||
return false;
|
||||
}
|
||||
if (file.size > IM_IMAGE_MAX_SIZE) {
|
||||
message.warning('图片大小不能超过10MB');
|
||||
return false;
|
||||
}
|
||||
if (props.disabled) {
|
||||
message.warning('请先选择聊天对象');
|
||||
return false;
|
||||
}
|
||||
uploadingImage.value = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
function handleImageUploadChange(info: UploadChangeParam) {
|
||||
const { file } = info;
|
||||
if (file.status === 'uploading') {
|
||||
return;
|
||||
}
|
||||
uploadingImage.value = false;
|
||||
if (file.status === 'error') {
|
||||
message.error(`${file.name} 上传失败`);
|
||||
return;
|
||||
}
|
||||
if (file.status !== 'done') {
|
||||
return;
|
||||
}
|
||||
const response = file.response as { success?: boolean; message?: string } | undefined;
|
||||
if (!response?.success) {
|
||||
message.warning(response?.message || '图片上传失败');
|
||||
return;
|
||||
}
|
||||
const imagePath = response.message;
|
||||
if (imagePath) {
|
||||
emit('image-uploaded', imagePath);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.im-chat-input-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.im-chat-input {
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
|
||||
&.focused {
|
||||
border-color: #91caff;
|
||||
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.08);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: #fafafa;
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
|
||||
.im-chat-input-textarea {
|
||||
padding: 12px 14px 4px !important;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
resize: none;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
:deep(textarea) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.im-chat-input-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px 10px 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: #666;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: #f5f5f5;
|
||||
color: #1677ff;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.send-btn-group {
|
||||
display: inline-flex;
|
||||
align-items: stretch;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.send-btn {
|
||||
min-width: 80px;
|
||||
border-radius: 8px 0 0 8px;
|
||||
font-weight: 500;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.send-btn-arrow {
|
||||
width: 32px;
|
||||
padding: 0;
|
||||
border-radius: 0 8px 8px 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.im-chat-input-hint {
|
||||
margin-top: 6px;
|
||||
padding: 0 4px;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
color: #bfbfbf;
|
||||
line-height: 1.4;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.im-emoji-popover {
|
||||
.ant-popover-inner-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.emoji-mart-bar {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.emoji-mart-emoji span {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user