diff --git a/docs/double-rack-ddl.sql b/docs/double-rack-ddl.sql new file mode 100644 index 00000000..74382905 --- /dev/null +++ b/docs/double-rack-ddl.sql @@ -0,0 +1,81 @@ +-- 双机架 (double-rack) 数据库 DDL +-- 在 jdbc:mysql://140.143.206.120:13306/double-rack 上执行 + +-- 工艺方案主表 +CREATE TABLE IF NOT EXISTS `mill_process_recipe` ( + `recipe_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `recipe_no` VARCHAR(64) NOT NULL COMMENT '方案记录号(唯一)', + `alloy_no` VARCHAR(32) NOT NULL COMMENT '合金号', + `pass_count` INT NOT NULL DEFAULT 0 COMMENT '道次数量', + `in_thick` DECIMAL(8,3) DEFAULT NULL COMMENT '原料厚度(mm)', + `out_thick` DECIMAL(8,3) DEFAULT NULL COMMENT '成品厚度(mm)', + `out_width` DECIMAL(8,1) DEFAULT NULL COMMENT '成品宽度(mm)', + `status` CHAR(1) NOT NULL DEFAULT '0' COMMENT '状态: 0-正常 1-停用', + `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志: 0-存在 2-删除', + `create_by` VARCHAR(64) DEFAULT NULL, + `create_time` DATETIME DEFAULT NULL, + `update_by` VARCHAR(64) DEFAULT NULL, + `update_time` DATETIME DEFAULT NULL, + `remark` VARCHAR(512) DEFAULT NULL, + PRIMARY KEY (`recipe_id`), + UNIQUE KEY `uk_recipe_no` (`recipe_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='双机架工艺方案主表'; + +-- 工艺方案道次表 +CREATE TABLE IF NOT EXISTS `mill_process_pass` ( + `pass_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `recipe_id` BIGINT NOT NULL COMMENT '关联方案ID', + `pass_no` INT NOT NULL COMMENT '道次序号', + `in_thick` DECIMAL(8,3) DEFAULT NULL COMMENT '入口厚度(mm)', + `out_thick` DECIMAL(8,3) DEFAULT NULL COMMENT '出口厚度(mm)', + `width` DECIMAL(8,1) DEFAULT NULL COMMENT '宽度(mm)', + `roll_force` DECIMAL(10,2) DEFAULT NULL COMMENT '轧制力(kN)', + `in_tension` DECIMAL(10,2) DEFAULT NULL COMMENT '入口张力(kN)', + `out_tension` DECIMAL(10,2) DEFAULT NULL COMMENT '出口张力(kN)', + `max_speed` DECIMAL(8,2) DEFAULT NULL COMMENT '最高速度(m/min)', + `in_unit_tension` DECIMAL(10,4) DEFAULT NULL COMMENT '入口单位张力(N/mm²)', + `out_unit_tension` DECIMAL(10,4) DEFAULT NULL COMMENT '出口单位张力(N/mm²)', + `reduction` DECIMAL(8,3) DEFAULT NULL COMMENT '压下量(mm)', + `total_reduction` DECIMAL(8,3) DEFAULT NULL COMMENT '总压下量(mm)', + `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志', + `create_by` VARCHAR(64) DEFAULT NULL, + `create_time` DATETIME DEFAULT NULL, + `update_by` VARCHAR(64) DEFAULT NULL, + `update_time` DATETIME DEFAULT NULL, + `remark` VARCHAR(512) DEFAULT NULL, + PRIMARY KEY (`pass_id`), + KEY `idx_recipe_id` (`recipe_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='双机架工艺方案道次表'; + +-- 生产计划表(轧制队列) +CREATE TABLE IF NOT EXISTS `mill_production_plan` ( + `plan_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `plan_no` VARCHAR(64) NOT NULL COMMENT '计划号', + `plan_status` CHAR(1) NOT NULL DEFAULT '0' COMMENT '计划状态: 0-待生产 1-生产中 2-完成 3-撤销', + `prod_status` VARCHAR(16) NOT NULL DEFAULT 'Idle' COMMENT '生产状态: Idle/Rolling/NextCoil/Done/Error', + `sort_no` INT NOT NULL DEFAULT 0 COMMENT '队列序号', + `in_mat_no` VARCHAR(64) DEFAULT NULL COMMENT '钢卷编号(入场钢卷号)', + `sg_sign` VARCHAR(64) DEFAULT NULL COMMENT '合金牌号', + `in_mat_thick` DECIMAL(8,3) DEFAULT NULL COMMENT '采料厚度(mm)', + `in_mat_width` DECIMAL(8,1) DEFAULT NULL COMMENT '采料宽度(mm)', + `in_mat_wt` DECIMAL(10,3) DEFAULT NULL COMMENT '采料重量(t)', + `in_mat_len` DECIMAL(10,2) DEFAULT NULL COMMENT '采料长度(m)', + `in_mat_in_dia` DECIMAL(8,1) DEFAULT NULL COMMENT '采料内径(mm)', + `in_mat_dia` DECIMAL(8,1) DEFAULT NULL COMMENT '采料外径(mm)', + `out_thick` DECIMAL(8,3) DEFAULT NULL COMMENT '成品厚度(mm)', + `pass_count` INT NOT NULL DEFAULT 0 COMMENT '道次数', + `recipe_id` BIGINT DEFAULT NULL COMMENT '关联工艺方案ID', + `recipe_no` VARCHAR(64) DEFAULT NULL COMMENT '工艺方案号', + `enter_coil_no` VARCHAR(64) DEFAULT NULL COMMENT '关联三级入场钢卷号', + `current_coil_no` VARCHAR(64) DEFAULT NULL COMMENT '关联三级当前钢卷号', + `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志', + `create_by` VARCHAR(64) DEFAULT NULL, + `create_time` DATETIME DEFAULT NULL, + `update_by` VARCHAR(64) DEFAULT NULL, + `update_time` DATETIME DEFAULT NULL, + `remark` VARCHAR(512) DEFAULT NULL, + PRIMARY KEY (`plan_id`), + KEY `idx_in_mat_no` (`in_mat_no`), + KEY `idx_enter_coil_no` (`enter_coil_no`), + KEY `idx_current_coil_no` (`current_coil_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='双机架生产计划表(轧制队列)'; diff --git a/klp-admin/src/main/resources/application-dev.yml b/klp-admin/src/main/resources/application-dev.yml index a5fe3783..38d92067 100644 --- a/klp-admin/src/main/resources/application-dev.yml +++ b/klp-admin/src/main/resources/application-dev.yml @@ -98,6 +98,14 @@ spring: url: jdbc:mysql://140.143.206.120:3306/cgldb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: klp password: KeLunPu123@ + # 双机架数据源 + double-rack: + lazy: true + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://140.143.206.120:13306/double-rack?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true + username: klp + password: KeLunPu@123 # Oracle 数据源 acid-l2: lazy: true @@ -167,7 +175,7 @@ redisson: # 客户端名称 clientName: ${klp.name} # 最小空闲连接数 - connectionMinimumIdleSize: 8 + connectionMinimumIdleSize: 4 # 连接池大小 connectionPoolSize: 32 # 连接空闲超时,单位:毫秒 diff --git a/klp-admin/src/main/resources/application-prod.yml b/klp-admin/src/main/resources/application-prod.yml index 5a18a336..f4555360 100644 --- a/klp-admin/src/main/resources/application-prod.yml +++ b/klp-admin/src/main/resources/application-prod.yml @@ -103,6 +103,15 @@ spring: password: root hikari: connectionTestQuery: SELECT 1 FROM DUAL + + # 双机架数据源 + double-rack: + lazy: true + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://140.143.206.120:13306/double-rack?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true + username: klp + password: KeLunPu@123 # postgres: # type: ${spring.datasource.type} # driverClassName: org.postgresql.Driver diff --git a/klp-ui/src/api/wms/drMill.js b/klp-ui/src/api/wms/drMill.js new file mode 100644 index 00000000..83868bd2 --- /dev/null +++ b/klp-ui/src/api/wms/drMill.js @@ -0,0 +1,100 @@ +import request from '@/utils/request' + +// ── 双机架工艺规程 ── + +export function listDrRecipe(params) { + return request({ url: '/dr/mill/recipe/list', method: 'get', params }) +} + +export function getDrRecipeDetail(id) { + return request({ url: `/dr/mill/recipe/${id}`, method: 'get' }) +} + +export function addDrRecipe(data) { + return request({ url: '/dr/mill/recipe', method: 'post', data }) +} + +export function updateDrRecipe(data) { + return request({ url: '/dr/mill/recipe', method: 'put', data }) +} + +export function delDrRecipe(ids) { + return request({ url: `/dr/mill/recipe/${ids}`, method: 'delete' }) +} + +// ── 双机架工艺规程版本 ── + +export function listDrRecipeVersions(recipeId) { + return request({ url: `/dr/mill/recipe/version/list/${recipeId}`, method: 'get' }) +} + +export function getDrRecipeVersionDetail(versionId) { + return request({ url: `/dr/mill/recipe/version/${versionId}`, method: 'get' }) +} + +export function addDrRecipeVersion(data) { + return request({ url: '/dr/mill/recipe/version', method: 'post', data }) +} + +export function updateDrRecipeVersion(data) { + return request({ url: '/dr/mill/recipe/version', method: 'put', data }) +} + +export function activateDrRecipeVersion(versionId) { + return request({ url: `/dr/mill/recipe/version/activate/${versionId}`, method: 'put' }) +} + +export function delDrRecipeVersion(versionId) { + return request({ url: `/dr/mill/recipe/version/${versionId}`, method: 'delete' }) +} + +// ── 双机架生产计划 ── + +export function listDrPlan(params) { + return request({ url: '/dr/mill/plan/list', method: 'get', params }) +} + +export function addDrPlan(data) { + return request({ url: '/dr/mill/plan', method: 'post', data }) +} + +export function updateDrPlan(data) { + return request({ url: '/dr/mill/plan', method: 'put', data }) +} + +export function delDrPlan(id) { + return request({ url: `/dr/mill/plan/${id}`, method: 'delete' }) +} + +export function getDrPlanByActionId(actionId) { + return request({ url: `/dr/mill/plan/byAction/${actionId}`, method: 'get' }) +} + +/** 实绩分页查询(double-rack 库,按创建时间倒序) */ +export function listDrActualPage(params) { + return request({ url: '/dr/mill/plan/actual/page', method: 'get', params }) +} + +export function moveUpDrPlan(id) { + return request({ url: `/dr/mill/plan/moveUp/${id}`, method: 'put' }) +} + +export function moveDownDrPlan(id) { + return request({ url: `/dr/mill/plan/moveDown/${id}`, method: 'put' }) +} + +export function finishDrPlan(id) { + return request({ url: `/dr/mill/plan/finish/${id}`, method: 'put' }) +} + +// ── WMS 钢卷号查询(供计划绑定) ── + +export function queryCoilByNo(coilNo) { + return request({ url: '/wms/materialCoil/queryByCoilNo', method: 'get', params: { coilNo } }) +} + +// ── 双机架操作录入(保存 coilWarehouseOperationLog) ── + +import { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } from '@/api/wms/coilWarehouseOperationLog' + +export { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } diff --git a/klp-ui/src/layout/components/TagsView/ScrollPane.vue b/klp-ui/src/layout/components/TagsView/ScrollPane.vue index f92d99b7..b7251076 100644 --- a/klp-ui/src/layout/components/TagsView/ScrollPane.vue +++ b/klp-ui/src/layout/components/TagsView/ScrollPane.vue @@ -88,6 +88,12 @@ export default { } .el-scrollbar__wrap { height: 39px; + overflow-x: auto; + overflow-y: hidden; + } + .el-scrollbar__view { + display: inline-block; /* 让所有 tag 排在同一行,不换行 */ + white-space: nowrap; } } } diff --git a/klp-ui/src/layout/components/TagsView/index.vue b/klp-ui/src/layout/components/TagsView/index.vue index 3d898700..d780c960 100644 --- a/klp-ui/src/layout/components/TagsView/index.vue +++ b/klp-ui/src/layout/components/TagsView/index.vue @@ -245,7 +245,7 @@ export default { // background: #454c51; border-bottom: 1px solid #a0a6ad; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.05) inset; - + .tags-view-wrapper { .tags-view-item { display: inline-block; @@ -253,6 +253,7 @@ export default { cursor: pointer; height: 26px; line-height: 26px; + white-space: nowrap; border: 1px solid #a0a6ad; color: #111; // 标签金属渐变背景 @@ -262,32 +263,32 @@ export default { margin-left: 5px; margin-top: 4px; border-radius: 4px; - box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1), + box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1), -1px -1px 2px rgba(255, 255, 255, 0.05); transition: all 0.2s ease; - + &:first-of-type { margin-left: 15px; } - + &:last-of-type { margin-right: 15px; } - + &:hover { - box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15), + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15), -1px -1px 2px rgba(255, 255, 255, 0.08); border-color: #8d939b; } - + &.active { // 激活状态主色调渐变 background: linear-gradient(145deg, #6b809d, #637994); color: #fff; border-color: #5a6d86; - box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15), + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15), -1px -1px 2px rgba(255, 255, 255, 0.1) inset; - + &::before { content: ''; background: rgba(255, 255, 255, 0.8); @@ -302,7 +303,7 @@ export default { } } } - + .contextmenu { margin: 0; // 右键菜单金属背景 @@ -317,15 +318,15 @@ export default { font-size: 12px; font-weight: 400; color: #606266; - box-shadow: 3px 3px 6px rgba(0, 0, 0, 0.15), + box-shadow: 3px 3px 6px rgba(0, 0, 0, 0.15), -2px -2px 5px rgba(255, 255, 255, 0.08); - + li { margin: 0; padding: 7px 16px; cursor: pointer; transition: all 0.2s ease; - + &:hover { background: rgba(99, 121, 148, 0.15); color: #637994; @@ -349,13 +350,13 @@ export default { transition: all .3s cubic-bezier(.645, .045, .355, 1); transform-origin: 100% 50%; margin-left: 4px; - + &:before { transform: scale(.6); display: inline-block; vertical-align: -3px; } - + &:hover { background-color: rgba(0, 0, 0, 0.15); color: #ddd; @@ -363,7 +364,7 @@ export default { inset -1px -1px 2px rgba(255, 255, 255, 0.05); } } - + &.active { .el-icon-close:hover { background-color: rgba(255, 255, 255, 0.2); @@ -372,4 +373,4 @@ export default { } } } - \ No newline at end of file + diff --git a/klp-ui/src/views/micro/pages/dr/components/Actual.vue b/klp-ui/src/views/micro/pages/dr/components/Actual.vue new file mode 100644 index 00000000..eb1f4fdc --- /dev/null +++ b/klp-ui/src/views/micro/pages/dr/components/Actual.vue @@ -0,0 +1,468 @@ + + + + + diff --git a/klp-ui/src/views/micro/pages/dr/components/Plan.vue b/klp-ui/src/views/micro/pages/dr/components/Plan.vue new file mode 100644 index 00000000..46e56f0b --- /dev/null +++ b/klp-ui/src/views/micro/pages/dr/components/Plan.vue @@ -0,0 +1,516 @@ + + + + + diff --git a/klp-ui/src/views/micro/pages/dr/components/ProcessSpec.vue b/klp-ui/src/views/micro/pages/dr/components/ProcessSpec.vue new file mode 100644 index 00000000..cd68086f --- /dev/null +++ b/klp-ui/src/views/micro/pages/dr/components/ProcessSpec.vue @@ -0,0 +1,485 @@ + + + + + diff --git a/klp-ui/src/views/micro/pages/dr/index.vue b/klp-ui/src/views/micro/pages/dr/index.vue new file mode 100644 index 00000000..acbf332f --- /dev/null +++ b/klp-ui/src/views/micro/pages/dr/index.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/klp-ui/src/views/timing/acid/rollConfig.vue b/klp-ui/src/views/timing/acid/rollConfig.vue new file mode 100644 index 00000000..3ce9290f --- /dev/null +++ b/klp-ui/src/views/timing/acid/rollConfig.vue @@ -0,0 +1,355 @@ + + + + + diff --git a/klp-ui/src/views/timing/acid/rollHistory.vue b/klp-ui/src/views/timing/acid/rollHistory.vue new file mode 100644 index 00000000..c00b93f1 --- /dev/null +++ b/klp-ui/src/views/timing/acid/rollHistory.vue @@ -0,0 +1,273 @@ + + + + + diff --git a/klp-ui/src/views/wms/coil/do/dr-normal.vue b/klp-ui/src/views/wms/coil/do/dr-normal.vue new file mode 100644 index 00000000..874dc0b0 --- /dev/null +++ b/klp-ui/src/views/wms/coil/do/dr-normal.vue @@ -0,0 +1,243 @@ + + + + + diff --git a/klp-ui/src/views/wms/coil/do/dr-repair.vue b/klp-ui/src/views/wms/coil/do/dr-repair.vue new file mode 100644 index 00000000..3c1bf4a1 --- /dev/null +++ b/klp-ui/src/views/wms/coil/do/dr-repair.vue @@ -0,0 +1,231 @@ + + + + + diff --git a/klp-ui/src/views/wms/coil/panels/DrMatchPanel.vue b/klp-ui/src/views/wms/coil/panels/DrMatchPanel.vue new file mode 100644 index 00000000..4646b7ce --- /dev/null +++ b/klp-ui/src/views/wms/coil/panels/DrMatchPanel.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/klp-ui/src/views/wms/coil/typing.vue b/klp-ui/src/views/wms/coil/typing.vue index 683c8f97..2eff8fe2 100644 --- a/klp-ui/src/views/wms/coil/typing.vue +++ b/klp-ui/src/views/wms/coil/typing.vue @@ -7,7 +7,7 @@ 钢卷信息更新
- +
--> @@ -26,9 +26,10 @@
- +
- + +
@@ -326,7 +327,8 @@ import AbnormalForm from './components/AbnormalForm'; import { generateCoilNoPrefix } from "@/utils/coil/coilNo"; import { addCoilContractRel } from "@/api/wms/coilContractRel"; import ContractSelect from "@/components/KLPService/ContractSelect"; -import L2MatchPanel from './panels/L2MatchPanel.vue'; +import L2MatchPanel from './panels/L2MatchPanel.vue' +import DrMatchPanel from './panels/DrMatchPanel.vue'; export default { @@ -340,6 +342,7 @@ export default { AbnormalForm, ContractSelect, L2MatchPanel, + DrMatchPanel, }, dicts: ['coil_quality_status', 'coil_abnormal_position', 'coil_abnormal_code', 'coil_abnormal_degree', 'coil_business_purpose'], data() { @@ -429,6 +432,7 @@ export default { ], }, actionId: null, + actionType: null, // 待操作类型,504/524 = 双机架 acidPrefill: { visible: false, type: 'info', @@ -462,6 +466,10 @@ export default { l2HotCoilId() { return (this.currentInfo && this.currentInfo.enterCoilNo) || '' }, + /** 是否双机架工序(actionType 504=正常 / 524=修复) */ + isDrAction() { + return this.actionType === 504 || this.actionType === 524 + }, // 动态显示标签 getItemLabel() { if (this.updateForm.materialType === '成品') { @@ -490,6 +498,11 @@ export default { // 填写生产开始时间 this.$set(this.updateForm, 'productionStartTime', pendingActionRes.data.createTime) + // 记录操作类型(用于判断是否双机架工序) + if (pendingActionRes.data && pendingActionRes.data.actionType != null) { + this.actionType = pendingActionRes.data.actionType + } + if (coilId) { await this.loadCoilInfo(coilId); } @@ -523,6 +536,20 @@ export default { } }, methods: { + /** 双机架计划/道次快捷写入 */ + applyDrFill(data) { + if (data.outThick != null) this.$set(this.updateForm, 'actualThickness', parseFloat(data.outThick)) + if (data.inMatWidth != null) this.$set(this.updateForm, 'actualWidth', parseFloat(data.inMatWidth)) + if (data.inMatWeight != null) { + this.$set(this.updateForm, 'netWeight', parseFloat(data.inMatWeight)) + this.$set(this.updateForm, 'grossWeight', parseFloat(data.inMatWeight)) + } + if (data.inMatLength != null) { + this.$set(this.updateForm, 'actualLength', parseFloat(data.inMatLength)) + this.$set(this.updateForm, 'length', parseFloat(data.inMatLength)) + } + }, + applyL2Fill(data) { if (data.entry_weight != null) { const w = parseFloat(data.entry_weight) diff --git a/klp-ui/src/views/wms/mill/dr-plan.vue b/klp-ui/src/views/wms/mill/dr-plan.vue new file mode 100644 index 00000000..b3f06c84 --- /dev/null +++ b/klp-ui/src/views/wms/mill/dr-plan.vue @@ -0,0 +1,385 @@ + + + + + diff --git a/klp-ui/src/views/wms/mill/dr-process.vue b/klp-ui/src/views/wms/mill/dr-process.vue new file mode 100644 index 00000000..357981d4 --- /dev/null +++ b/klp-ui/src/views/wms/mill/dr-process.vue @@ -0,0 +1,461 @@ + + + + + diff --git a/klp-ui/src/views/wms/processSpec/planSpec.vue b/klp-ui/src/views/wms/processSpec/planSpec.vue index 25035a53..51228f16 100644 --- a/klp-ui/src/views/wms/processSpec/planSpec.vue +++ b/klp-ui/src/views/wms/processSpec/planSpec.vue @@ -65,7 +65,7 @@ /> - 点位名称: + 参数名称: 新建参数 + 新建方案点位 + >管理点位 模板导入
- + - - - - - - - + + + + + + + + + + + + + - - -
@@ -361,6 +274,16 @@ + + + + + @@ -488,7 +411,7 @@ const TEMPLATE_HEADERS = ['段类型', '段名称', '点位名称', '参数名 /** 表头字段映射 */ const HEADER_MAP = { '段类型': 'segmentType', - '段名称': 'segmentName', + '段名称': 'segmentName', '点位名称': 'pointName', '参数名称': 'paramName', '设定值': 'targetValue', @@ -514,9 +437,8 @@ export default { appliedFilterName: '', planList: [], planLoading: false, - selectedPlan: null, - paramList: [], - paramLoading: false, + allParamList: [], + allParamLoading: false, planOpen: false, planTitle: '', planSubmitLoading: false, @@ -531,6 +453,7 @@ export default { paramSubmitLoading: false, paramForm: {}, paramRules: { + planId: [{ required: true, message: '请选择所属点位', trigger: 'change' }], paramCode: [{ required: true, message: '参数编码不能为空', trigger: 'blur' }], paramName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }] }, @@ -632,12 +555,6 @@ export default { extra.sort((a, b) => String(a.label).localeCompare(String(b.label), 'zh-CN')) return [...SEGMENT_FORM_OPTIONS, ...extra] }, - /** 当前选中点位下的异常条目 */ - planAnomalies() { - if (!this.selectedPlan) return [] - return this.allAnomalies.filter(a => a.paramCode === this.selectedPlan.pointCode || - this.paramList.some(p => p.paramCode === a.paramCode)) - }, /** paramCode → anomaly 的快速索引(用于状态列) */ paramAnomalyMap() { const map = {} @@ -648,45 +565,42 @@ export default { if (!this.coilAnomalyCoilId) return [] return this.allAnomalies.filter(a => a.coilId === this.coilAnomalyCoilId) }, - filteredPlans() { + /** 所有 plan 的参数平铺成一行,带 plan 的段/点位信息 */ + flatRows() { + const SEG_LABELS = { INLET: '入口段', PROCESS: '工艺段', OUTLET: '出口段' } + const planMap = {} + for (const plan of this.planList) planMap[plan.planId] = plan + const rows = [] + for (const param of this.allParamList) { + const plan = planMap[param.planId] + if (!plan) continue + rows.push({ + ...param, + _planId: plan.planId, + _segmentType: plan.segmentType, + _segmentLabel: SEG_LABELS[plan.segmentType] || plan.segmentName || plan.segmentType, + _pointName: plan.pointName, + _sortOrder: plan.sortOrder || 0 + }) + } + rows.sort((a, b) => { + const s = (a._sortOrder || 0) - (b._sortOrder || 0) + return s !== 0 ? s : (a.paramId || 0) - (b.paramId || 0) + }) + return rows + }, + filteredFlatRows() { const type = this.activeSegmentType - const sub = this.activeSegmentName - return this.planList.filter(p => { - const typeOk = - type === '' || type === undefined || type === null - ? true - : String(p.segmentType) === String(type) - let nameOkSeg = true - if (typeOk && type !== '' && type !== undefined && type !== null) { - if (sub === '' || sub === undefined || sub === null) { - nameOkSeg = true - } else if (sub === '__EMPTY__') { - const sn = p.segmentName != null ? String(p.segmentName).trim() : '' - nameOkSeg = !sn - } else { - const sn = p.segmentName != null ? String(p.segmentName).trim() : '' - nameOkSeg = sn === String(sub).trim() - } - } - const nameOk = !this.appliedFilterName || (p.pointName || '').includes(this.appliedFilterName) - return typeOk && nameOkSeg && nameOk + const name = this.appliedFilterName + return this.flatRows.filter(r => { + const typeOk = !type || String(r._segmentType) === String(type) + const nameOk = !name || (r.paramName || '').includes(name) || (r.paramCode || '').includes(name) + return typeOk && nameOk }) } }, watch: { $route: { immediate: true, handler() { this.syncFromRoute() } }, - planAnomalies: { - handler(list) { - if (list.length && this.anomalyExpanded) { - this.$nextTick(() => this.renderAnomalyChart()) - } - } - }, - anomalyExpanded(val) { - if (val && this.planAnomalies.length) { - this.$nextTick(() => this.renderAnomalyChart()) - } - }, segmentTypeTabOptions: { handler() { const a = this.activeSegmentType @@ -852,22 +766,26 @@ export default { }, loadPlans() { this.planLoading = true - this.selectedPlan = null - this.paramList = [] listProcessPlan({ versionId: this.versionId, pageNum: 1, pageSize: 500 }).then(res => { this.planList = res.rows || [] + this.loadAllParams() }).catch(e => console.error(e)).finally(() => { this.planLoading = false }) }, - loadParams(planId) { - this.paramLoading = true - listProcessPlanParam({ planId, pageNum: 1, pageSize: 500 }).then(res => { - this.paramList = res.rows || [] - }).catch(e => console.error(e)).finally(() => { this.paramLoading = false }) - }, - onPlanSelect(row) { - if (!row) return - this.selectedPlan = row - this.loadParams(row.planId) + async loadAllParams() { + if (!this.planList.length) { this.allParamList = []; return } + this.allParamLoading = true + try { + const results = await Promise.all( + this.planList.map(plan => + listProcessPlanParam({ planId: plan.planId, pageNum: 1, pageSize: 500 }) + .then(res => res.rows || []) + .catch(() => []) + ) + ) + this.allParamList = results.flat() + } finally { + this.allParamLoading = false + } }, applyFilter() { this.appliedFilterName = this.filterName }, resetFilter() { @@ -921,28 +839,33 @@ export default { }).catch(() => {}) }, openParamDialog(planRow, paramRow) { - const targetPlan = planRow || this.selectedPlan - if (!targetPlan) { this.$message.warning('请先选择一个方案点位'); return } - if (!this.selectedPlan || this.selectedPlan.planId !== targetPlan.planId) { - this.selectedPlan = targetPlan - this.loadParams(targetPlan.planId) - } this.paramForm = paramRow ? { ...paramRow } : { - planId: targetPlan.planId, - paramCode: undefined, - paramName: undefined, + planId: planRow ? planRow.planId : undefined, + paramCode: undefined, + paramName: undefined, targetValue: undefined, - lowerLimit: undefined, - upperLimit: undefined, - unit: undefined, - remark: undefined + lowerLimit: undefined, + upperLimit: undefined, + unit: undefined, + remark: undefined } this.paramTitle = paramRow ? '编辑方案参数' : '新建方案参数' this.paramOpen = true this.$nextTick(() => this.$refs.paramFormRef && this.$refs.paramFormRef.clearValidate()) }, + editParamFromFlat(row) { + this.openParamDialog(null, row) + }, + deleteParamFromFlat(row) { + this.$modal.confirm('确认删除该参数?').then(() => { + return delProcessPlanParam(row.paramId) + }).then(() => { + this.$modal.msgSuccess('删除成功') + this.loadAllParams() + }).catch(() => {}) + }, submitParam() { this.$refs.paramFormRef.validate(ok => { if (!ok) return @@ -951,18 +874,10 @@ export default { req.then(() => { this.$modal.msgSuccess('保存成功') this.paramOpen = false - this.loadParams(this.selectedPlan.planId) + this.loadAllParams() }).catch(e => console.error(e)).finally(() => { this.paramSubmitLoading = false }) }) }, - removeParam(row) { - this.$modal.confirm('确认删除该参数?').then(() => { - return delProcessPlanParam(row.paramId) - }).then(() => { - this.$modal.msgSuccess('删除成功') - this.loadParams(this.selectedPlan.planId) - }).catch(() => {}) - }, // ===================== 导入相关方法 ===================== /** * 打开导入对话框 @@ -1218,7 +1133,7 @@ export default { async batchImport() { // �照点位名称分组,每个点位创建一条记录,然后添加多个参数 const pointGroups = {} - + // 分组处理 this.tableData.forEach(row => { const pointKey = `${row.segmentType}_${row.segmentName}_${row.pointName}` @@ -1249,12 +1164,12 @@ export default { try { await this.importOnePoint(group) this.importedCount += group.params.length - + // 更新进度 index += group.params.length const currentProgress = Math.round((index / this.totalCount) * 100) this.progress = currentProgress - + await new Promise(resolve => setTimeout(resolve, 50)) } catch (error) { throw new Error(`导入点位"${group.pointName}"失败:${error.message}`) @@ -1275,7 +1190,7 @@ export default { pointCode: group.pointCode, sortOrder: 0 } - + const planRes = await addProcessPlan(planParams) if (planRes.code !== 200) { throw new Error(`点位创建失败:${planRes.msg || '接口返回异常'}`) @@ -1293,7 +1208,7 @@ export default { lowerLimit: param.lowerLimit ? Number(param.lowerLimit) : null, unit: param.unit } - + const paramRes = await addProcessPlanParam(paramParams) if (paramRes.code !== 200) { throw new Error(`参数"${param.paramName}"创建失败:${paramRes.msg || '接口返回异常'}`) @@ -1354,7 +1269,7 @@ export default { // 创建工作簿 const wb = XLSX.utils.book_new() const ws = XLSX.utils.aoa_to_sheet(templateData) - + // 设置列宽 ws['!cols'] = [ { wch: 10 }, // 段类型 @@ -1366,7 +1281,7 @@ export default { { wch: 10 }, // 下限 { wch: 10 } // 单位 ] - + XLSX.utils.book_append_sheet(wb, ws, '导入模板') XLSX.writeFile(wb, '方案点位导入模板.xlsx') }, @@ -1558,6 +1473,19 @@ export default { .btn-danger { color: #f56c6c; } +/* ── 段 chip ── */ +.seg-chip { + display: inline-block; + padding: 2px 7px; + border-radius: 10px; + font-size: 11px; + font-weight: 600; + white-space: nowrap; +} +.seg-inlet { background: #ecf5ff; color: #3a6ea8; border: 1px solid #b3d8ff; } +.seg-process { background: #f0f9eb; color: #3a7a2a; border: 1px solid #b3e19d; } +.seg-outlet { background: #fdf6ec; color: #a86a00; border: 1px solid #f5dab1; } + /* ── 偏差分析 ── */ .anomaly-section-header { display: flex; diff --git a/klp-wms/src/main/java/com/klp/controller/DrMillProcessRecipeController.java b/klp-wms/src/main/java/com/klp/controller/DrMillProcessRecipeController.java new file mode 100644 index 00000000..b39ef854 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/controller/DrMillProcessRecipeController.java @@ -0,0 +1,94 @@ +package com.klp.controller; + +import com.klp.common.core.controller.BaseController; +import com.klp.common.core.domain.R; +import com.klp.domain.DrMillProcessRecipe; +import com.klp.domain.DrMillProcessRecipeVersion; +import com.klp.service.IDrMillProcessRecipeService; +import com.klp.service.IDrMillProcessRecipeVersionService; +import com.klp.service.impl.DrRecipeSyncService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/dr/mill/recipe") +public class DrMillProcessRecipeController extends BaseController { + + private final IDrMillProcessRecipeService recipeService; + private final IDrMillProcessRecipeVersionService versionService; + private final DrRecipeSyncService syncService; + + // ─── 方案 ──────────────────────────────────────────────── + + @GetMapping("/list") + public R> list(DrMillProcessRecipe query) { + List list = recipeService.selectList(query); + // 每次查询时补齐缺失的 L3 工艺规程(幂等,已存在则跳过) + syncService.syncRecipesToSpec(list); + return R.ok(list); + } + + @GetMapping("/{id}") + public R detail(@PathVariable Long id) { + return R.ok(recipeService.selectDetailById(id)); + } + + @PostMapping + public R add(@RequestBody DrMillProcessRecipe recipe) { + recipeService.save(recipe); + // 新增方案后立即同步 L3 工艺规程 + syncService.syncRecipesToSpec(Collections.singletonList(recipe)); + return R.ok(recipe.getRecipeId()); + } + + @PutMapping + public R edit(@RequestBody DrMillProcessRecipe recipe) { + recipeService.update(recipe); + return R.ok(); + } + + @DeleteMapping("/{ids}") + public R remove(@PathVariable Long[] ids) { + recipeService.deleteByIds(ids); + return R.ok(); + } + + // ─── 版本 ──────────────────────────────────────────────── + + @GetMapping("/version/list/{recipeId}") + public R> versionList(@PathVariable Long recipeId) { + return R.ok(versionService.listByRecipeId(recipeId)); + } + + @GetMapping("/version/{versionId}") + public R versionDetail(@PathVariable Long versionId) { + return R.ok(versionService.getDetailById(versionId)); + } + + @PostMapping("/version") + public R addVersion(@RequestBody DrMillProcessRecipeVersion version) { + return R.ok(versionService.save(version)); + } + + @PutMapping("/version") + public R editVersion(@RequestBody DrMillProcessRecipeVersion version) { + versionService.update(version); + return R.ok(); + } + + @PutMapping("/version/activate/{versionId}") + public R activate(@PathVariable Long versionId) { + versionService.activate(versionId); + return R.ok(); + } + + @DeleteMapping("/version/{versionId}") + public R deleteVersion(@PathVariable Long versionId) { + versionService.deleteById(versionId); + return R.ok(); + } +} diff --git a/klp-wms/src/main/java/com/klp/controller/DrMillProductionPlanController.java b/klp-wms/src/main/java/com/klp/controller/DrMillProductionPlanController.java new file mode 100644 index 00000000..e3a2ace9 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/controller/DrMillProductionPlanController.java @@ -0,0 +1,83 @@ +package com.klp.controller; + +import com.klp.common.core.controller.BaseController; +import com.klp.common.core.domain.R; +import com.klp.common.core.page.TableDataInfo; +import com.klp.domain.DrMillProductionPlan; +import com.klp.service.IDrMillProductionPlanService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 双机架生产计划管理 + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/dr/mill/plan") +public class DrMillProductionPlanController extends BaseController { + + private final IDrMillProductionPlanService planService; + + @GetMapping("/list") + public R> list(DrMillProductionPlan query) { + return R.ok(planService.selectList(query)); + } + + /** 实绩分页查询(按创建时间倒序,支持卷号/状态/时间范围过滤) */ + @GetMapping("/actual/page") + public TableDataInfo actualPage(DrMillProductionPlan query) { + return planService.selectPageList(query); + } + + @GetMapping("/{id}") + public R detail(@PathVariable Long id) { + return R.ok(planService.selectById(id)); + } + + /** + * 通过待操作ID查询绑定计划(含道次),供录入页快捷写入 + * planNo = "DR" + actionId + */ + @GetMapping("/byAction/{actionId}") + public R getByActionId(@PathVariable Long actionId) { + return R.ok(planService.selectByActionId(actionId)); + } + + @PostMapping + public R add(@RequestBody DrMillProductionPlan plan) { + planService.insert(plan); + return R.ok(); + } + + @PutMapping + public R edit(@RequestBody DrMillProductionPlan plan) { + planService.update(plan); + return R.ok(); + } + + @DeleteMapping("/{id}") + public R remove(@PathVariable Long id) { + planService.deleteById(id); + return R.ok(); + } + + @PutMapping("/moveUp/{id}") + public R moveUp(@PathVariable Long id) { + planService.moveUp(id); + return R.ok(); + } + + @PutMapping("/moveDown/{id}") + public R moveDown(@PathVariable Long id) { + planService.moveDown(id); + return R.ok(); + } + + @PutMapping("/finish/{id}") + public R finish(@PathVariable Long id) { + planService.finish(id); + return R.ok(); + } +} diff --git a/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java b/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java index 3ebed3ce..6a3a21e1 100644 --- a/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java +++ b/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java @@ -616,6 +616,16 @@ public class WmsMaterialCoilController extends BaseController { } } + /** + * 根据入场钢卷号或当前钢卷号查询钢卷信息,供双机架计划绑定使用 + * + * @param coilNo 入场钢卷号或当前钢卷号 + */ + @GetMapping("/queryByCoilNo") + public R queryByCoilNo(@NotBlank(message = "钢卷号不能为空") @RequestParam String coilNo) { + return R.ok(iWmsMaterialCoilService.queryByCoilNo(coilNo)); + } + /** * 冷硬卷切边统计 diff --git a/klp-wms/src/main/java/com/klp/domain/DrMillProcessPass.java b/klp-wms/src/main/java/com/klp/domain/DrMillProcessPass.java new file mode 100644 index 00000000..e2a6abd5 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/DrMillProcessPass.java @@ -0,0 +1,33 @@ +package com.klp.domain; + +import lombok.Data; +import java.math.BigDecimal; +import java.util.Date; + +/** 双机架工艺方案道次(对应 double-rack.mill_process_pass) */ +@Data +public class DrMillProcessPass { + + private Long passId; + private Long recipeId; + /** 所属版本 ID */ + private Long versionId; + private Integer passNo; + private BigDecimal inThick; + private BigDecimal outThick; + private BigDecimal width; + private BigDecimal rollForce; + private BigDecimal inTension; + private BigDecimal outTension; + private BigDecimal maxSpeed; + private BigDecimal inUnitTension; + private BigDecimal outUnitTension; + private BigDecimal reduction; + private BigDecimal totalReduction; + private String delFlag; + private String createBy; + private Date createTime; + private String updateBy; + private Date updateTime; + private String remark; +} diff --git a/klp-wms/src/main/java/com/klp/domain/DrMillProcessRecipe.java b/klp-wms/src/main/java/com/klp/domain/DrMillProcessRecipe.java new file mode 100644 index 00000000..5e1630df --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/DrMillProcessRecipe.java @@ -0,0 +1,31 @@ +package com.klp.domain; + +import lombok.Data; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** 双机架工艺方案主表(对应 double-rack.mill_process_recipe) */ +@Data +public class DrMillProcessRecipe { + + private Long recipeId; + private String recipeNo; + private String alloyNo; + private Integer passCount; + private BigDecimal inThick; + private BigDecimal outThick; + private BigDecimal outWidth; + /** 0-正常 1-停用 */ + private String status; + /** 0-存在 2-删除 */ + private String delFlag; + private String createBy; + private Date createTime; + private String updateBy; + private Date updateTime; + private String remark; + + /** 关联道次(非数据库字段) */ + private List passList; +} diff --git a/klp-wms/src/main/java/com/klp/domain/DrMillProcessRecipeVersion.java b/klp-wms/src/main/java/com/klp/domain/DrMillProcessRecipeVersion.java new file mode 100644 index 00000000..b76c98fd --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/DrMillProcessRecipeVersion.java @@ -0,0 +1,28 @@ +package com.klp.domain; + +import lombok.Data; +import java.util.Date; +import java.util.List; + +/** 双机架工艺方案版本(double-rack.mill_process_recipe_version) */ +@Data +public class DrMillProcessRecipeVersion { + + private Long versionId; + private Long recipeId; + /** 版本号,如 V1.0 */ + private String versionCode; + /** 1=已激活 0=未激活 */ + private Integer isActive; + /** 0=草稿 1=已发布 */ + private String status; + private String createBy; + private Date createTime; + private String updateBy; + private Date updateTime; + private String remark; + private String delFlag; + + /** 关联道次(非数据库字段) */ + private List passList; +} diff --git a/klp-wms/src/main/java/com/klp/domain/DrMillProductionPlan.java b/klp-wms/src/main/java/com/klp/domain/DrMillProductionPlan.java new file mode 100644 index 00000000..da89344b --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/DrMillProductionPlan.java @@ -0,0 +1,66 @@ +package com.klp.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 双机架生产计划(对应 double-rack.mill_production_plan)。 + * enterCoilNo / currentCoilNo 用于绑定三级 WMS 钢卷数据。 + */ +@Data +public class DrMillProductionPlan { + + private Long planId; + private String planNo; + /** 0-待生产 1-生产中 2-完成 3-撤销 */ + private String planStatus; + /** Idle / Rolling / NextCoil / Done */ + private String prodStatus; + private Integer sortNo; + private String inMatNo; + private String alloyNo; + private BigDecimal inMatThick; + private BigDecimal inMatWidth; + private BigDecimal inMatWeight; + private BigDecimal inMatLength; + private BigDecimal inMatId; + private BigDecimal inMatOd; + private BigDecimal outThick; + private Integer passCount; + private Long recipeId; + private String recipeNo; + /** 绑定的工艺版本 ID */ + private Long versionId; + /** 关联三级 WMS 入场钢卷号 */ + private String enterCoilNo; + /** 关联三级 WMS 当前钢卷号 */ + private String currentCoilNo; + /** 绑定方案的道次列表(非DB字段,按需填充) */ + private List passList; + + private String delFlag; + private String createBy; + private Date createTime; + private String updateBy; + private Date updateTime; + private String remark; + + // ── 非 DB 查询字段(分页 + 时间范围) ────────────── + @TableField(exist = false) + private Integer pageNum; + @TableField(exist = false) + private Integer pageSize; + @TableField(exist = false) + private String createStartTime; + @TableField(exist = false) + private String createEndTime; + + public int getOffset() { + int num = (pageNum != null && pageNum > 0) ? pageNum : 1; + int size = (pageSize != null && pageSize > 0) ? pageSize : 50; + return (num - 1) * size; + } +} diff --git a/klp-wms/src/main/java/com/klp/mapper/DrMillProcessPassMapper.java b/klp-wms/src/main/java/com/klp/mapper/DrMillProcessPassMapper.java new file mode 100644 index 00000000..021afd2c --- /dev/null +++ b/klp-wms/src/main/java/com/klp/mapper/DrMillProcessPassMapper.java @@ -0,0 +1,21 @@ +package com.klp.mapper; + +import com.klp.domain.DrMillProcessPass; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface DrMillProcessPassMapper { + List selectByVersionId(Long versionId); + List selectByRecipeId(Long recipeId); + int insertBatch(List list); + int deleteByVersionId(Long versionId); + int deleteByRecipeId(Long recipeId); + /** 查出所有 version_id 为空的道次所属 recipe_id(去重) */ + List selectDistinctRecipeIdsWithOrphanPasses(); + /** 将指定方案下所有 version_id 为空的道次批量设置 version_id */ + int updateVersionIdForOrphans(@Param("recipeId") Long recipeId, + @Param("versionId") Long versionId); +} diff --git a/klp-wms/src/main/java/com/klp/mapper/DrMillProcessRecipeMapper.java b/klp-wms/src/main/java/com/klp/mapper/DrMillProcessRecipeMapper.java new file mode 100644 index 00000000..6851396f --- /dev/null +++ b/klp-wms/src/main/java/com/klp/mapper/DrMillProcessRecipeMapper.java @@ -0,0 +1,14 @@ +package com.klp.mapper; + +import com.klp.domain.DrMillProcessRecipe; +import org.apache.ibatis.annotations.Mapper; +import java.util.List; + +@Mapper +public interface DrMillProcessRecipeMapper { + List selectList(DrMillProcessRecipe query); + DrMillProcessRecipe selectById(Long recipeId); + int insert(DrMillProcessRecipe recipe); + int update(DrMillProcessRecipe recipe); + int deleteBatchByIds(Long[] ids); +} diff --git a/klp-wms/src/main/java/com/klp/mapper/DrMillProcessRecipeVersionMapper.java b/klp-wms/src/main/java/com/klp/mapper/DrMillProcessRecipeVersionMapper.java new file mode 100644 index 00000000..c7b59b71 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/mapper/DrMillProcessRecipeVersionMapper.java @@ -0,0 +1,24 @@ +package com.klp.mapper; + +import com.klp.domain.DrMillProcessRecipeVersion; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import java.util.List; + +@Mapper +public interface DrMillProcessRecipeVersionMapper { + List selectByRecipeId(Long recipeId); + /** 查询指定方案已有的 V1.0 版本(用于迁移幂等判断) */ + DrMillProcessRecipeVersion selectV1ByRecipeId(@Param("recipeId") Long recipeId); + /** 查询指定方案当前激活版本 */ + DrMillProcessRecipeVersion selectActiveByRecipeId(@Param("recipeId") Long recipeId); + DrMillProcessRecipeVersion selectById(Long versionId); + int insert(DrMillProcessRecipeVersion version); + int update(DrMillProcessRecipeVersion version); + int deleteById(Long versionId); + int deleteByRecipeIds(@Param("ids") Long[] ids); + /** 将同方案其他版本置为未激活 */ + int deactivateOthers(@Param("recipeId") Long recipeId, @Param("versionId") Long versionId); + /** 激活指定版本 */ + int activate(Long versionId); +} diff --git a/klp-wms/src/main/java/com/klp/mapper/DrMillProductionPlanMapper.java b/klp-wms/src/main/java/com/klp/mapper/DrMillProductionPlanMapper.java new file mode 100644 index 00000000..e3b387d7 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/mapper/DrMillProductionPlanMapper.java @@ -0,0 +1,21 @@ +package com.klp.mapper; + +import com.klp.domain.DrMillProductionPlan; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import java.util.List; + +@Mapper +public interface DrMillProductionPlanMapper { + List selectList(DrMillProductionPlan query); + /** 分页查询(配合 PageHelper 或手动 LIMIT/OFFSET) */ + List selectPageList(DrMillProductionPlan query); + long countPageList(DrMillProductionPlan query); + DrMillProductionPlan selectById(Long planId); + DrMillProductionPlan selectByPlanNo(@Param("planNo") String planNo); + int insert(DrMillProductionPlan plan); + int update(DrMillProductionPlan plan); + int deleteById(Long planId); + int updateSortNo(@Param("planId") Long planId, @Param("sortNo") int sortNo); + int countByStatus(@Param("status") String status); +} diff --git a/klp-wms/src/main/java/com/klp/service/IDrMillProcessRecipeService.java b/klp-wms/src/main/java/com/klp/service/IDrMillProcessRecipeService.java new file mode 100644 index 00000000..7af1bbb9 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/IDrMillProcessRecipeService.java @@ -0,0 +1,12 @@ +package com.klp.service; + +import com.klp.domain.DrMillProcessRecipe; +import java.util.List; + +public interface IDrMillProcessRecipeService { + List selectList(DrMillProcessRecipe query); + DrMillProcessRecipe selectDetailById(Long id); + int save(DrMillProcessRecipe recipe); + int update(DrMillProcessRecipe recipe); + int deleteByIds(Long[] ids); +} diff --git a/klp-wms/src/main/java/com/klp/service/IDrMillProcessRecipeVersionService.java b/klp-wms/src/main/java/com/klp/service/IDrMillProcessRecipeVersionService.java new file mode 100644 index 00000000..048f24eb --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/IDrMillProcessRecipeVersionService.java @@ -0,0 +1,13 @@ +package com.klp.service; + +import com.klp.domain.DrMillProcessRecipeVersion; +import java.util.List; + +public interface IDrMillProcessRecipeVersionService { + List listByRecipeId(Long recipeId); + DrMillProcessRecipeVersion getDetailById(Long versionId); + Long save(DrMillProcessRecipeVersion version); + void update(DrMillProcessRecipeVersion version); + void activate(Long versionId); + void deleteById(Long versionId); +} diff --git a/klp-wms/src/main/java/com/klp/service/IDrMillProductionPlanService.java b/klp-wms/src/main/java/com/klp/service/IDrMillProductionPlanService.java new file mode 100644 index 00000000..304cc756 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/IDrMillProductionPlanService.java @@ -0,0 +1,20 @@ +package com.klp.service; + +import com.klp.common.core.page.TableDataInfo; +import com.klp.domain.DrMillProductionPlan; +import java.util.List; + +public interface IDrMillProductionPlanService { + List selectList(DrMillProductionPlan query); + /** 分页查询实绩(按创建时间倒序) */ + TableDataInfo selectPageList(DrMillProductionPlan query); + DrMillProductionPlan selectById(Long id); + /** 通过待操作ID查计划(planNo = "DR" + actionId),并附带道次列表 */ + DrMillProductionPlan selectByActionId(Long actionId); + int insert(DrMillProductionPlan plan); + int update(DrMillProductionPlan plan); + int deleteById(Long id); + int moveUp(Long id); + int moveDown(Long id); + int finish(Long id); +} diff --git a/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java b/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java index 583716e2..ba5ad1cb 100644 --- a/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java +++ b/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java @@ -349,5 +349,10 @@ public interface IWmsMaterialCoilService { * @param response HTTP响应对象 */ void exportAbnormalReport(WmsMaterialCoilBo bo, HttpServletResponse response); + + /** + * 根据入场钢卷号或当前钢卷号查询钢卷,供双机架计划绑定使用 + */ + com.klp.domain.vo.WmsMaterialCoilVo queryByCoilNo(String coilNo); } diff --git a/klp-wms/src/main/java/com/klp/service/impl/DrMigrationService.java b/klp-wms/src/main/java/com/klp/service/impl/DrMigrationService.java new file mode 100644 index 00000000..4b4ff8b8 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/impl/DrMigrationService.java @@ -0,0 +1,65 @@ +package com.klp.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.klp.domain.DrMillProcessRecipeVersion; +import com.klp.mapper.DrMillProcessPassMapper; +import com.klp.mapper.DrMillProcessRecipeVersionMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 双机架历史道次版本迁移 + * 将所有 version_id IS NULL 的道次归入对应方案的 V1.0 版本 + */ +@Slf4j +@DS("double-rack") +@RequiredArgsConstructor +@Service +public class DrMigrationService { + + private final DrMillProcessPassMapper passMapper; + private final DrMillProcessRecipeVersionMapper versionMapper; + + @Transactional(rollbackFor = Exception.class) + public void migrateOrphanPassesToV1() { + List recipeIds = passMapper.selectDistinctRecipeIdsWithOrphanPasses(); + if (recipeIds.isEmpty()) { + log.info("[DR迁移] 无需迁移:所有道次已绑定版本"); + return; + } + + int newVersionCount = 0; + int migratedPassCount = 0; + + for (Long recipeId : recipeIds) { + // 幂等:V1.0 已存在则复用,避免重复执行时重复创建 + DrMillProcessRecipeVersion v1 = versionMapper.selectV1ByRecipeId(recipeId); + if (v1 == null) { + v1 = new DrMillProcessRecipeVersion(); + v1.setRecipeId(recipeId); + v1.setVersionCode("V1.0"); + v1.setIsActive(1); // 默认激活 + v1.setStatus("1"); // 已发布 + v1.setCreateBy("system"); + v1.setUpdateBy("system"); + v1.setRemark("系统启动时自动迁移历史数据"); + versionMapper.insert(v1); + newVersionCount++; + log.info("[DR迁移] 方案 {} 新建 V1.0,versionId={}", recipeId, v1.getVersionId()); + } else { + log.info("[DR迁移] 方案 {} 已有 V1.0(versionId={}),直接复用", recipeId, v1.getVersionId()); + } + + int rows = passMapper.updateVersionIdForOrphans(recipeId, v1.getVersionId()); + migratedPassCount += rows; + log.info("[DR迁移] 方案 {} 迁移 {} 条道次 → versionId={}", recipeId, rows, v1.getVersionId()); + } + + log.info("[DR迁移] 完成:共处理方案 {} 个,新建版本 {} 个,迁移道次 {} 条", + recipeIds.size(), newVersionCount, migratedPassCount); + } +} diff --git a/klp-wms/src/main/java/com/klp/service/impl/DrMillProcessRecipeServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/DrMillProcessRecipeServiceImpl.java new file mode 100644 index 00000000..5280a831 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/impl/DrMillProcessRecipeServiceImpl.java @@ -0,0 +1,75 @@ +package com.klp.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.klp.common.helper.LoginHelper; +import com.klp.domain.DrMillProcessRecipe; +import com.klp.domain.DrMillProcessRecipeVersion; +import com.klp.mapper.DrMillProcessPassMapper; +import com.klp.mapper.DrMillProcessRecipeMapper; +import com.klp.mapper.DrMillProcessRecipeVersionMapper; +import com.klp.service.IDrMillProcessRecipeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@DS("double-rack") +@RequiredArgsConstructor +@Service +public class DrMillProcessRecipeServiceImpl implements IDrMillProcessRecipeService { + + private final DrMillProcessRecipeMapper recipeMapper; + private final DrMillProcessPassMapper passMapper; + private final DrMillProcessRecipeVersionMapper versionMapper; + + @Override + public List selectList(DrMillProcessRecipe query) { + return recipeMapper.selectList(query); + } + + @Override + public DrMillProcessRecipe selectDetailById(Long id) { + DrMillProcessRecipe recipe = recipeMapper.selectById(id); + if (recipe != null) { + // 优先取激活版本的道次;无激活版本则返回空列表,避免多版本混合 + DrMillProcessRecipeVersion active = versionMapper.selectActiveByRecipeId(id); + if (active != null) { + recipe.setPassList(passMapper.selectByVersionId(active.getVersionId())); + } + } + return recipe; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int save(DrMillProcessRecipe recipe) { + String user = LoginHelper.getUsername(); + recipe.setCreateBy(user); + recipe.setUpdateBy(user); + recipeMapper.insert(recipe); + // 道次现在通过版本接口管理,不再直接挂在方案上 + return 1; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int update(DrMillProcessRecipe recipe) { + recipe.setUpdateBy(LoginHelper.getUsername()); + recipeMapper.update(recipe); + // 道次通过版本接口管理,不在此处操作 + return 1; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Long[] ids) { + for (Long id : ids) { + // 级联删版本下的道次,再删版本,再删方案 + passMapper.deleteByRecipeId(id); + versionMapper.deleteByRecipeIds(new Long[]{id}); + } + return recipeMapper.deleteBatchByIds(ids); + } + +} diff --git a/klp-wms/src/main/java/com/klp/service/impl/DrMillProcessRecipeVersionServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/DrMillProcessRecipeVersionServiceImpl.java new file mode 100644 index 00000000..51489c83 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/impl/DrMillProcessRecipeVersionServiceImpl.java @@ -0,0 +1,86 @@ +package com.klp.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.klp.common.helper.LoginHelper; +import com.klp.domain.DrMillProcessPass; +import com.klp.domain.DrMillProcessRecipeVersion; +import com.klp.mapper.DrMillProcessPassMapper; +import com.klp.mapper.DrMillProcessRecipeVersionMapper; +import com.klp.service.IDrMillProcessRecipeVersionService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@DS("double-rack") +@RequiredArgsConstructor +@Service +public class DrMillProcessRecipeVersionServiceImpl implements IDrMillProcessRecipeVersionService { + + private final DrMillProcessRecipeVersionMapper versionMapper; + private final DrMillProcessPassMapper passMapper; + + @Override + public List listByRecipeId(Long recipeId) { + return versionMapper.selectByRecipeId(recipeId); + } + + @Override + public DrMillProcessRecipeVersion getDetailById(Long versionId) { + DrMillProcessRecipeVersion v = versionMapper.selectById(versionId); + if (v != null) { + v.setPassList(passMapper.selectByVersionId(versionId)); + } + return v; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long save(DrMillProcessRecipeVersion version) { + String user = LoginHelper.getUsername(); + version.setCreateBy(user); + version.setUpdateBy(user); + versionMapper.insert(version); + savePassList(version); + return version.getVersionId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(DrMillProcessRecipeVersion version) { + version.setUpdateBy(LoginHelper.getUsername()); + versionMapper.update(version); + passMapper.deleteByVersionId(version.getVersionId()); + savePassList(version); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void activate(Long versionId) { + DrMillProcessRecipeVersion v = versionMapper.selectById(versionId); + if (v == null) throw new RuntimeException("版本不存在"); + versionMapper.deactivateOthers(v.getRecipeId(), versionId); + versionMapper.activate(versionId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteById(Long versionId) { + passMapper.deleteByVersionId(versionId); + versionMapper.deleteById(versionId); + } + + private void savePassList(DrMillProcessRecipeVersion version) { + List list = version.getPassList(); + if (list == null || list.isEmpty()) return; + String user = LoginHelper.getUsername(); + for (DrMillProcessPass p : list) { + p.setRecipeId(version.getRecipeId()); + p.setVersionId(version.getVersionId()); + p.setCreateBy(user); + p.setUpdateBy(user); + } + passMapper.insertBatch(list); + } +} diff --git a/klp-wms/src/main/java/com/klp/service/impl/DrMillProductionPlanServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/DrMillProductionPlanServiceImpl.java new file mode 100644 index 00000000..8d4f612e --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/impl/DrMillProductionPlanServiceImpl.java @@ -0,0 +1,118 @@ +package com.klp.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.klp.common.core.page.TableDataInfo; +import com.klp.common.helper.LoginHelper; +import com.klp.domain.DrMillProductionPlan; +import com.klp.mapper.DrMillProcessPassMapper; +import com.klp.mapper.DrMillProductionPlanMapper; +import com.klp.service.IDrMillProductionPlanService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@DS("double-rack") +@RequiredArgsConstructor +@Service +public class DrMillProductionPlanServiceImpl implements IDrMillProductionPlanService { + + private final DrMillProductionPlanMapper planMapper; + private final DrMillProcessPassMapper passMapper; + + @Override + public List selectList(DrMillProductionPlan query) { + return planMapper.selectList(query); + } + + @Override + public TableDataInfo selectPageList(DrMillProductionPlan query) { + if (query.getPageNum() == null || query.getPageNum() < 1) query.setPageNum(1); + if (query.getPageSize() == null || query.getPageSize() < 1) query.setPageSize(50); + long total = planMapper.countPageList(query); + List rows = planMapper.selectPageList(query); + TableDataInfo data = TableDataInfo.build(rows); + data.setTotal(total); + return data; + } + + @Override + public DrMillProductionPlan selectById(Long id) { + return planMapper.selectById(id); + } + + @Override + public DrMillProductionPlan selectByActionId(Long actionId) { + String planNo = "DR" + actionId; + DrMillProductionPlan plan = planMapper.selectByPlanNo(planNo); + if (plan != null && plan.getRecipeId() != null) { + plan.setPassList(passMapper.selectByRecipeId(plan.getRecipeId())); + } + return plan; + } + + @Override + public int insert(DrMillProductionPlan plan) { + String user = LoginHelper.getUsername(); + plan.setCreateBy(user); + plan.setUpdateBy(user); + List all = planMapper.selectList(new DrMillProductionPlan()); + plan.setSortNo(all.size() + 1); + if (plan.getPlanNo() == null || plan.getPlanNo().isEmpty()) { + plan.setPlanNo("DR" + System.currentTimeMillis()); + } + return planMapper.insert(plan); + } + + @Override + public int update(DrMillProductionPlan plan) { + plan.setUpdateBy(LoginHelper.getUsername()); + return planMapper.update(plan); + } + + @Override + public int deleteById(Long id) { + return planMapper.deleteById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int moveUp(Long id) { + DrMillProductionPlan cur = planMapper.selectById(id); + if (cur == null || cur.getSortNo() <= 1) return 0; + List all = planMapper.selectList(new DrMillProductionPlan()); + DrMillProductionPlan prev = all.stream() + .filter(p -> p.getSortNo() == cur.getSortNo() - 1) + .findFirst().orElse(null); + if (prev == null) return 0; + planMapper.updateSortNo(cur.getPlanId(), prev.getSortNo()); + planMapper.updateSortNo(prev.getPlanId(), cur.getSortNo()); + return 1; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int moveDown(Long id) { + DrMillProductionPlan cur = planMapper.selectById(id); + if (cur == null) return 0; + List all = planMapper.selectList(new DrMillProductionPlan()); + DrMillProductionPlan next = all.stream() + .filter(p -> p.getSortNo() == cur.getSortNo() + 1) + .findFirst().orElse(null); + if (next == null) return 0; + planMapper.updateSortNo(cur.getPlanId(), next.getSortNo()); + planMapper.updateSortNo(next.getPlanId(), cur.getSortNo()); + return 1; + } + + @Override + public int finish(Long id) { + DrMillProductionPlan plan = planMapper.selectById(id); + if (plan == null) return 0; + plan.setUpdateBy(LoginHelper.getUsername()); + plan.setPlanStatus("2"); + plan.setProdStatus("Done"); + return planMapper.update(plan); + } +} diff --git a/klp-wms/src/main/java/com/klp/service/impl/DrRecipeSyncService.java b/klp-wms/src/main/java/com/klp/service/impl/DrRecipeSyncService.java new file mode 100644 index 00000000..e61e5543 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/impl/DrRecipeSyncService.java @@ -0,0 +1,96 @@ +package com.klp.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.klp.domain.DrMillProcessRecipe; +import com.klp.domain.WmsProcessSpec; +import com.klp.domain.WmsProductionLine; +import com.klp.mapper.WmsProcessSpecMapper; +import com.klp.mapper.WmsProductionLineMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 双机架工艺方案 → L3规程(wms_process_spec)同步服务 + *

+ * 注意:此 Service 不标注 @DS,默认走 master 数据源, + * 供 Controller 在 double-rack 查询完方案列表后调用, + * 将缺失的 wms_process_spec 记录自动补齐。 + *

+ */ +@Slf4j +@RequiredArgsConstructor +@Service +public class DrRecipeSyncService { + + /** 双机架产线编号,与 wms_production_line.line_code 对应 */ + private static final String DR_LINE_CODE = "DR"; + private static final String DR_LINE_NAME = "双机架轧机"; + + /** spec_code 前缀,区分同名方案号属于哪条产线 */ + private static final String SPEC_CODE_PREFIX = "DR-"; + + private final WmsProcessSpecMapper specMapper; + private final WmsProductionLineMapper lineMapper; + + /** + * 检查并补充 wms_process_spec: + * 对列表中每条方案,若在 L3 规程表中不存在则自动新增。 + */ + @Transactional(rollbackFor = Exception.class) + public void syncRecipesToSpec(List recipes) { + if (recipes == null || recipes.isEmpty()) return; + + Long lineId = getOrCreateDrLineId(); + + int created = 0; + for (DrMillProcessRecipe recipe : recipes) { + String specCode = SPEC_CODE_PREFIX + recipe.getRecipeNo(); + LambdaQueryWrapper qw = Wrappers.lambdaQuery(); + qw.eq(WmsProcessSpec::getSpecCode, specCode); + if (specMapper.selectCount(qw) == 0) { + WmsProcessSpec spec = new WmsProcessSpec(); + spec.setSpecCode(specCode); + spec.setSpecName(recipe.getRecipeNo()); + spec.setSpecType("PROCESS"); + spec.setLineId(lineId); + spec.setIsEnabled(1); + spec.setRemark("由双机架工艺方案自动同步"); + specMapper.insert(spec); + created++; + log.info("[DR同步] 新增 wms_process_spec: specCode={}, recipeId={}", + specCode, recipe.getRecipeId()); + } + } + if (created > 0) { + log.info("[DR同步] 本次共补充 {} 条 L3 工艺规程", created); + } + } + + /** + * 查找或创建双机架产线记录,返回 line_id。 + */ + private Long getOrCreateDrLineId() { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(WmsProductionLine::getLineCode, DR_LINE_CODE); + WmsProductionLine line = lineMapper.selectOne(lqw); + if (line == null) { + line = new WmsProductionLine(); + line.setLineCode(DR_LINE_CODE); + line.setLineName(DR_LINE_NAME); + line.setCapacity(BigDecimal.ZERO); + line.setUnit("t"); + line.setIsEnabled(1); + line.setRemark("双机架轧机产线,系统自动创建"); + lineMapper.insert(line); + log.info("[DR同步] 新建 wms_production_line: lineCode={}, lineId={}", + DR_LINE_CODE, line.getLineId()); + } + return line.getLineId(); + } +} diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsCoilPendingActionServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsCoilPendingActionServiceImpl.java index a58e1c2c..5a976247 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsCoilPendingActionServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsCoilPendingActionServiceImpl.java @@ -19,8 +19,12 @@ import com.klp.domain.vo.TheoryCycleRegressionResultVo; import com.klp.domain.vo.TheoryCycleRegressionVo; import com.klp.domain.vo.WmsCoilPendingActionVo; import com.klp.domain.vo.WmsCoilPendingActionIdCoilVo; +import com.klp.domain.DrMillProductionPlan; +import com.klp.domain.WmsRawMaterial; +import com.klp.mapper.DrMillProductionPlanMapper; import com.klp.mapper.WmsCoilPendingActionMapper; import com.klp.mapper.WmsMaterialCoilMapper; +import com.klp.mapper.WmsRawMaterialMapper; import com.klp.service.IWmsCoilPendingActionService; import com.klp.system.service.ISysUserService; import lombok.RequiredArgsConstructor; @@ -52,8 +56,14 @@ public class WmsCoilPendingActionServiceImpl implements IWmsCoilPendingActionSer private final ISysUserService userService; private final WmsMaterialCoilMapper materialCoilMapper; + private final WmsRawMaterialMapper rawMaterialMapper; + private final DrMillProductionPlanMapper drPlanMapper; private final StringRedisTemplate stringRedisTemplate; + /** 双机架工序 / 修复的 actionType */ + private static final int ACTION_TYPE_DR_NORMAL = 504; + private static final int ACTION_TYPE_DR_REPAIR = 524; + private static final String REDIS_KEY_IDEAL_CYCLE = "oee:ideal-cycle-time"; /** @@ -256,10 +266,99 @@ public class WmsCoilPendingActionServiceImpl implements IWmsCoilPendingActionSer boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setActionId(add.getActionId()); + // 双机架工序/修复:同步在 double-rack 数据库创建生产计划 + if (ACTION_TYPE_DR_NORMAL == add.getActionType() || ACTION_TYPE_DR_REPAIR == add.getActionType()) { + autoCreateDrPlan(add); + } } return flag; } + /** + * 自动创建双机架生产计划(写入 double-rack 数据源)。 + * 同时查询钢卷库(wms_material_coil)和原材料库(wms_raw_material), + * 将完整的钢卷/原料规格信息写入计划。 + * planNo = "DR" + actionId,保证唯一且可追溯。 + */ + @com.baomidou.dynamic.datasource.annotation.DS("double-rack") + private void autoCreateDrPlan(WmsCoilPendingAction action) { + try { + DrMillProductionPlan plan = new DrMillProductionPlan(); + plan.setPlanNo("DR" + action.getActionId()); + + if (action.getCoilId() != null) { + // ① 查钢卷库 + WmsMaterialCoil coil = materialCoilMapper.selectById(action.getCoilId()); + if (coil != null) { + plan.setInMatNo(coil.getEnterCoilNo() != null ? coil.getEnterCoilNo() : coil.getCurrentCoilNo()); + plan.setEnterCoilNo(coil.getEnterCoilNo()); + plan.setCurrentCoilNo(coil.getCurrentCoilNo()); + + // 实测厚度(优先) + if (coil.getActualThickness() != null) { + try { plan.setInMatThick(new java.math.BigDecimal(coil.getActualThickness())); } catch (Exception ignored) {} + } + // 实测宽度(优先) + plan.setInMatWidth(coil.getActualWidth()); + // 净重 > 毛重 + plan.setInMatWeight(coil.getNetWeight() != null ? coil.getNetWeight() : coil.getGrossWeight()); + // 长度 + plan.setInMatLength(coil.getLength()); + + // ② 查原材料库(itemType='raw_material' 时通过 itemId 关联) + if ("raw_material".equals(coil.getItemType()) && coil.getItemId() != null) { + WmsRawMaterial rm = rawMaterialMapper.selectById(coil.getItemId()); + if (rm != null) { + // 钢种/合金号 + plan.setAlloyNo(rm.getSteelGrade()); + // 标称厚度(实测没有时用标称补齐) + if (plan.getInMatThick() == null && rm.getThickness() != null) { + plan.setInMatThick(rm.getThickness()); + } + // 标称宽度(实测没有时用标称补齐) + if (plan.getInMatWidth() == null && rm.getWidth() != null) { + plan.setInMatWidth(rm.getWidth()); + } + // 卷重(WMS 净重没有时用原材料卷重补齐) + if (plan.getInMatWeight() == null && rm.getCoilWeight() != null) { + plan.setInMatWeight(rm.getCoilWeight()); + } + // 目标冷轧厚度 / 宽度写入出口目标厚度 + if (rm.getTargetColdThickness() != null) { + plan.setOutThick(rm.getTargetColdThickness()); + } + } + } + } + } else if (action.getCurrentCoilNo() != null) { + plan.setInMatNo(action.getCurrentCoilNo()); + plan.setCurrentCoilNo(action.getCurrentCoilNo()); + } + + // 修复工序在备注中标记 + if (ACTION_TYPE_DR_REPAIR == action.getActionType()) { + plan.setRemark("[修复] " + (action.getRemark() != null ? action.getRemark() : "")); + } else { + plan.setRemark(action.getRemark()); + } + + String user = LoginHelper.getUsername(); + plan.setCreateBy(user); + plan.setUpdateBy(user); + plan.setPlanStatus("0"); + plan.setProdStatus("Idle"); + + // 排到队列末尾 + List all = drPlanMapper.selectList(new DrMillProductionPlan()); + plan.setSortNo(all.size() + 1); + + drPlanMapper.insert(plan); + } catch (Exception e) { + org.slf4j.LoggerFactory.getLogger(getClass()) + .error("[双机架] 自动创建生产计划失败, actionId={}", action.getActionId(), e); + } + } + /** * 修改钢卷待操作 */ diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java index 2fa05f60..d3734ce0 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java @@ -1356,12 +1356,12 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { } result = updateBySingle(bo, qrcodeStepType); // 返回新钢卷ID字符串 } - + // 如果有关联的操作记录ID,调用完成接口 if (bo.getActionId() != null && bo.getActionId() > 0) { coilPendingActionService.completeAction(bo.getActionId(), result); } - + return result; } @@ -5743,5 +5743,15 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { return baseMapper.update(null, updateWrapper) > 0; } + @Override + public WmsMaterialCoilVo queryByCoilNo(String coilNo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(WmsMaterialCoil::getEnterCoilNo, coilNo) + .or() + .eq(WmsMaterialCoil::getCurrentCoilNo, coilNo); + lqw.last("LIMIT 1"); + return baseMapper.selectVoOne(lqw); + } + } diff --git a/klp-wms/src/main/java/com/klp/task/DrMigrationRunner.java b/klp-wms/src/main/java/com/klp/task/DrMigrationRunner.java new file mode 100644 index 00000000..bef49bd8 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/task/DrMigrationRunner.java @@ -0,0 +1,33 @@ +package com.klp.task; + +import com.klp.service.impl.DrMigrationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +/** + * 应用启动后自动执行双机架历史道次版本迁移。 + * 逻辑幂等:若已迁移完毕则无任何 DB 写操作,仅打印一条 info 日志。 + */ +@Slf4j +@Order(100) +@Component +@RequiredArgsConstructor +public class DrMigrationRunner implements ApplicationRunner { + + private final DrMigrationService migrationService; + + @Override + public void run(ApplicationArguments args) { + log.info("[DR迁移] 开始检查历史道次版本迁移..."); + try { + migrationService.migrateOrphanPassesToV1(); + } catch (Exception e) { + // 迁移失败不阻断服务启动,只记录错误 + log.error("[DR迁移] 执行异常,请人工检查 mill_process_pass 表", e); + } + } +} diff --git a/klp-wms/src/main/resources/mapper/DrMillProcessPassMapper.xml b/klp-wms/src/main/resources/mapper/DrMillProcessPassMapper.xml new file mode 100644 index 00000000..e249ca77 --- /dev/null +++ b/klp-wms/src/main/resources/mapper/DrMillProcessPassMapper.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO mill_process_pass ( + recipe_id, version_id, pass_no, in_thick, out_thick, width, + roll_force, in_tension, out_tension, max_speed, + in_unit_tension, out_unit_tension, reduction, total_reduction, + create_by, create_time, update_by, update_time, remark, del_flag + ) VALUES + + (#{p.recipeId}, #{p.versionId}, #{p.passNo}, #{p.inThick}, #{p.outThick}, #{p.width}, + #{p.rollForce}, #{p.inTension}, #{p.outTension}, #{p.maxSpeed}, + #{p.inUnitTension}, #{p.outUnitTension}, #{p.reduction}, #{p.totalReduction}, + #{p.createBy}, NOW(), #{p.updateBy}, NOW(), #{p.remark}, '0') + + + + + UPDATE mill_process_pass SET del_flag = '2', update_time = NOW() + WHERE version_id = #{versionId} + + + + UPDATE mill_process_pass SET del_flag = '2', update_time = NOW() + WHERE recipe_id = #{recipeId} + + + + + + UPDATE mill_process_pass + SET version_id = #{versionId}, + update_time = NOW() + WHERE recipe_id = #{recipeId} + AND version_id IS NULL + AND del_flag = '0' + + + diff --git a/klp-wms/src/main/resources/mapper/DrMillProcessRecipeMapper.xml b/klp-wms/src/main/resources/mapper/DrMillProcessRecipeMapper.xml new file mode 100644 index 00000000..a59182f9 --- /dev/null +++ b/klp-wms/src/main/resources/mapper/DrMillProcessRecipeMapper.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + recipe_id, recipe_no, alloy_no, pass_count, in_thick, out_thick, out_width, + status, del_flag, create_by, create_time, update_by, update_time, remark + + + + + + + + INSERT INTO mill_process_recipe ( + recipe_no, alloy_no, pass_count, in_thick, out_thick, out_width, + status, create_by, create_time, update_by, update_time, remark, del_flag + ) VALUES ( + #{recipeNo}, #{alloyNo}, #{passCount}, #{inThick}, #{outThick}, #{outWidth}, + #{status}, #{createBy}, NOW(), #{updateBy}, NOW(), #{remark}, '0' + ) + + + + UPDATE mill_process_recipe + SET recipe_no = #{recipeNo}, + alloy_no = #{alloyNo}, + pass_count = #{passCount}, + in_thick = #{inThick}, + out_thick = #{outThick}, + out_width = #{outWidth}, + status = #{status}, + update_by = #{updateBy}, + update_time = NOW(), + remark = #{remark} + WHERE recipe_id = #{recipeId} + + + + UPDATE mill_process_recipe SET del_flag = '2', update_time = NOW() + WHERE recipe_id IN + #{id} + + + diff --git a/klp-wms/src/main/resources/mapper/DrMillProcessRecipeVersionMapper.xml b/klp-wms/src/main/resources/mapper/DrMillProcessRecipeVersionMapper.xml new file mode 100644 index 00000000..575d4991 --- /dev/null +++ b/klp-wms/src/main/resources/mapper/DrMillProcessRecipeVersionMapper.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + version_id, recipe_id, version_code, is_active, status, + create_by, create_time, update_by, update_time, remark, del_flag + + + + + + + + + + + + INSERT INTO mill_process_recipe_version ( + recipe_id, version_code, is_active, status, + create_by, create_time, update_by, update_time, remark, del_flag + ) VALUES ( + #{recipeId}, #{versionCode}, IFNULL(#{isActive}, 0), IFNULL(#{status}, '0'), + #{createBy}, NOW(), #{updateBy}, NOW(), #{remark}, '0' + ) + + + + UPDATE mill_process_recipe_version + SET version_code = #{versionCode}, + status = #{status}, + update_by = #{updateBy}, + update_time = NOW(), + remark = #{remark} + WHERE version_id = #{versionId} + + + + UPDATE mill_process_recipe_version SET del_flag = '2', update_time = NOW() + WHERE version_id = #{versionId} + + + + UPDATE mill_process_recipe_version SET del_flag = '2', update_time = NOW() + WHERE recipe_id IN + #{id} + + + + UPDATE mill_process_recipe_version + SET is_active = 0, update_time = NOW() + WHERE recipe_id = #{recipeId} AND version_id != #{versionId} AND del_flag = '0' + + + + UPDATE mill_process_recipe_version + SET is_active = 1, status = '1', update_time = NOW() + WHERE version_id = #{versionId} + + + diff --git a/klp-wms/src/main/resources/mapper/DrMillProductionPlanMapper.xml b/klp-wms/src/main/resources/mapper/DrMillProductionPlanMapper.xml new file mode 100644 index 00000000..c1eea7ba --- /dev/null +++ b/klp-wms/src/main/resources/mapper/DrMillProductionPlanMapper.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + plan_id, plan_no, plan_status, prod_status, sort_no, + in_mat_no, sg_sign, in_mat_thick, in_mat_width, in_mat_wt, in_mat_len, + in_mat_in_dia, in_mat_dia, out_thick, pass_count, recipe_id, recipe_no, + version_id, enter_coil_no, current_coil_no, + del_flag, create_by, create_time, update_by, update_time, remark + + + + + + + WHERE del_flag = '0' + + AND (in_mat_no LIKE CONCAT('%',#{inMatNo},'%') + OR enter_coil_no LIKE CONCAT('%',#{inMatNo},'%') + OR current_coil_no LIKE CONCAT('%',#{inMatNo},'%')) + + + AND plan_status = #{planStatus} + + + AND create_time >= #{createStartTime} + + + AND create_time <= #{createEndTime} + + + + + + + + + + + + + + + INSERT INTO mill_production_plan ( + plan_no, plan_status, prod_status, sort_no, + in_mat_no, sg_sign, in_mat_thick, in_mat_width, in_mat_wt, in_mat_len, + in_mat_in_dia, in_mat_dia, out_thick, pass_count, recipe_id, recipe_no, + version_id, enter_coil_no, current_coil_no, + create_by, create_time, update_by, update_time, remark, del_flag + ) VALUES ( + #{planNo}, IFNULL(#{planStatus},'0'), IFNULL(#{prodStatus},'Idle'), IFNULL(#{sortNo},0), + #{inMatNo}, #{alloyNo}, #{inMatThick}, #{inMatWidth}, #{inMatWeight}, #{inMatLength}, + #{inMatId}, #{inMatOd}, #{outThick}, IFNULL(#{passCount},0), #{recipeId}, #{recipeNo}, + #{versionId}, #{enterCoilNo}, #{currentCoilNo}, + #{createBy}, NOW(), #{updateBy}, NOW(), #{remark}, '0' + ) + + + + UPDATE mill_production_plan + SET plan_status = #{planStatus}, + prod_status = #{prodStatus}, + in_mat_no = #{inMatNo}, + sg_sign = #{alloyNo}, + in_mat_thick = #{inMatThick}, + in_mat_width = #{inMatWidth}, + in_mat_wt = #{inMatWeight}, + in_mat_len = #{inMatLength}, + in_mat_in_dia = #{inMatId}, + in_mat_dia = #{inMatOd}, + out_thick = #{outThick}, + pass_count = #{passCount}, + recipe_id = #{recipeId}, + recipe_no = #{recipeNo}, + version_id = #{versionId}, + enter_coil_no = #{enterCoilNo}, + current_coil_no = #{currentCoilNo}, + update_by = #{updateBy}, + update_time = NOW(), + remark = #{remark} + WHERE plan_id = #{planId} + + + + UPDATE mill_production_plan SET del_flag = '2', update_time = NOW() + WHERE plan_id = #{planId} + + + + UPDATE mill_production_plan SET sort_no = #{sortNo} WHERE plan_id = #{planId} + + +