| | |
| | | .DS_Store |
| | | **/.DS_Store |
| | | logs |
| | | logs/** |
| | | logs/** |
| | | /log.path_IS_UNDEFINED/*.log |
| | |
| | | // get a list of records based on an array of ids |
| | | getMany: async (resource, params) => { |
| | | // console.log("getMany", resource, params); |
| | | const res = await request.post(resource + "/many/" + params.ids); |
| | | // Old format: [1, 2, 3] (array of integers) |
| | | // New format: [{id: 1, sort: 1}, {id: 2, sort: 2}] (array of objects) |
| | | const ids = params.ids.map(id => { |
| | | if (typeof id === 'object' && id !== null && 'id' in id) { |
| | | return id.id; |
| | | } |
| | | return id; |
| | | }); |
| | | const res = await request.post(resource + "/many/" + ids.join(',')); |
| | | const { code, msg, data } = res.data; |
| | | if (code === 200) { |
| | | return Promise.resolve({ |
| | |
| | | // update a list of records based on an array of ids and a common patch |
| | | updateMany: async (resource, params) => { |
| | | console.log("updateMany", resource, params); |
| | | // Extract IDs from params.ids - handle both formats: |
| | | // Old format: [1, 2, 3] (array of integers) |
| | | // New format: [{id: 1, sort: 1}, {id: 2, sort: 2}] (array of objects) |
| | | const ids = params.ids.map(id => { |
| | | if (typeof id === 'object' && id !== null && 'id' in id) { |
| | | return id.id; |
| | | } |
| | | return id; // Already a number |
| | | }); |
| | | const res = await request.post( |
| | | resource + "/update/many", |
| | | params.ids.map((id) => ({ id, ...params.data })), |
| | | ids.map((id) => ({ id, ...params.data })), |
| | | ); |
| | | const { code, msg, data } = res.data; |
| | | if (code === 200) { |
| | |
| | | // delete a list of records based on an array of ids |
| | | deleteMany: async (resource, params) => { |
| | | console.log("deleteMany", resource, params); |
| | | const res = await request.post(resource + "/remove/" + params?.ids); |
| | | // Extract IDs from params.ids - handle both formats: |
| | | // Old format: [1, 2, 3] (array of integers) |
| | | // New format: [{id: 1, sort: 1}, {id: 2, sort: 2}] (array of objects) |
| | | const ids = params?.ids ? params.ids.map(id => { |
| | | if (typeof id === 'object' && id !== null && 'id' in id) { |
| | | return id.id; |
| | | } |
| | | return id; // Already a number |
| | | }) : []; |
| | | const res = await request.post(resource + "/remove/" + ids.join(',')); |
| | | const { code, msg, data } = res.data; |
| | | if (code === 200) { |
| | | return Promise.resolve({ |
| | |
| | | name: "name", |
| | | wareId: "ware", |
| | | code: "code", |
| | | sort: "Sort", |
| | | shipperId: "shipperId", |
| | | supplierId: "supplierId", |
| | | flagMinus: "flagMinus", |
| | |
| | | type: "库区类型", |
| | | wareId: "所属仓库", |
| | | code: "库区编码", |
| | | sort: "序号", |
| | | shipperId: "货主", |
| | | supplierId: "供应商", |
| | | flagMinus: "允许负库存", |
| | |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceArrayInput source="areas" reference="warehouseAreas"> |
| | | <ReferenceArrayInput source="areas" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }}> |
| | | <SelectArrayInput |
| | | label="table.field.basStation.crossZoneArea" |
| | | optionText="name" |
| | |
| | | import MemoInput from "../../components/MemoInput"; |
| | | import StatusSelectInput from "../../components/StatusSelectInput"; |
| | | import DictionarySelect from "../../components/DictionarySelect"; |
| | | import AreasSortInput from "../../components/AreasSortInput"; |
| | | |
| | | const FormToolbar = () => { |
| | | const { getValues } = useFormContext(); |
| | |
| | | mutationMode={EDIT_MODE} |
| | | actions={<CustomerTopToolBar />} |
| | | aside={<EditBaseAside />} |
| | | transform={(data) => { |
| | | // 保存前转换:将 areas 从纯ID数组转换为 [{id, sort}] 格式 |
| | | // 从隐藏字段 areasSort 获取排序信息 |
| | | const areas = data.areas || []; |
| | | const areasSort = data.areasSort || []; |
| | | |
| | | if (areas.length > 0) { |
| | | if (typeof areas[0] === 'number') { |
| | | // 如果是纯ID数组,使用 areasSort 中的排序信息 |
| | | if (areasSort.length > 0 && typeof areasSort[0] === 'object') { |
| | | // 使用 areasSort 中的排序信息,但只保留 areas 中存在的ID |
| | | const areaIds = new Set(areas); |
| | | const sortedAreas = areasSort |
| | | .filter(item => areaIds.has(item.id)) |
| | | .sort((a, b) => (a.sort || 0) - (b.sort || 0)); |
| | | |
| | | // 如果 areasSort 中有所有ID的排序信息,使用它 |
| | | if (sortedAreas.length === areas.length) { |
| | | data.areas = sortedAreas; |
| | | } else { |
| | | // 否则,为缺失的ID添加默认排序 |
| | | const existingIds = new Set(sortedAreas.map(item => item.id)); |
| | | const missingIds = areas.filter(id => !existingIds.has(id)); |
| | | const maxSort = sortedAreas.length > 0 |
| | | ? Math.max(...sortedAreas.map(item => item.sort || 1)) |
| | | : 0; |
| | | const newItems = missingIds.map((id, index) => ({ |
| | | id: id, |
| | | sort: maxSort + index + 1, |
| | | })); |
| | | data.areas = [...sortedAreas, ...newItems]; |
| | | } |
| | | } else { |
| | | // 如果没有排序信息,使用默认排序 |
| | | data.areas = areas.map((id, index) => ({ |
| | | id: id, |
| | | sort: index + 1, |
| | | })); |
| | | } |
| | | } |
| | | // 删除临时字段 |
| | | delete data.areasSort; |
| | | } |
| | | return data; |
| | | }} |
| | | > |
| | | <SimpleForm |
| | | shouldUnregister |
| | |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <ReferenceArrayInput source="areas" reference="warehouseAreas"> |
| | | <ReferenceArrayInput |
| | | source="areas" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | format={(value) => { |
| | | // 从后端接收时:将 [{id, sort}] 转换为 [id, id, ...] |
| | | if (!value || !Array.isArray(value)) return []; |
| | | if (value.length === 0) return []; |
| | | // 如果是对象数组,提取id |
| | | if (typeof value[0] === 'object' && value[0] !== null && value[0].id !== undefined) { |
| | | return value.map(item => item.id); |
| | | } |
| | | // 如果已经是纯ID数组,直接返回 |
| | | return value; |
| | | }} |
| | | parse={(value) => { |
| | | // 保存时:保持原值,由 AreasSortInput 处理转换 |
| | | return value; |
| | | }} |
| | | > |
| | | <SelectArrayInput |
| | | label="table.field.basContainer.areas" |
| | | optionText="name" |
| | |
| | | /> |
| | | </ReferenceArrayInput> |
| | | </Stack> |
| | | {/* 下方显示已选库区和排序编辑 */} |
| | | <AreasSortInput source="areas" /> |
| | | {/* 隐藏字段:存储排序信息 */} |
| | | <TextInput source="areasSort" style={{ display: 'none' }} /> |
| | | |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.basContainer.areas" |
| | | property={record.areas} |
| | | property={ |
| | | record.areas && Array.isArray(record.areas) |
| | | ? record.areas.map(area => { |
| | | if (typeof area === 'object' && area !== null && 'id' in area) { |
| | | return area.id; |
| | | } |
| | | return area; |
| | | }).join(', ') |
| | | : record.areas |
| | | } |
| | | /> |
| | | </Grid> |
| | | |
| | |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceInput source="area" reference="warehouseAreas"> |
| | | <ReferenceInput source="area" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }}> |
| | | <SelectInput |
| | | label="table.field.basStation.area" |
| | | optionText="name" |
| | |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceArrayInput source="areaIds" reference="warehouseAreas"> |
| | | <ReferenceArrayInput source="areaIds" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }}> |
| | | <SelectArrayInput |
| | | label="table.field.basStation.crossZoneArea" |
| | | optionText="name" |
| | |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <ReferenceInput source="area" reference="warehouseAreas"> |
| | | <ReferenceInput source="area" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }}> |
| | | <SelectInput |
| | | label="table.field.basStation.area" |
| | | optionText="name" |
| | |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <ReferenceArrayInput source="areaIds" reference="warehouseAreas"> |
| | | <ReferenceArrayInput source="areaIds" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }}> |
| | | <SelectArrayInput |
| | | label="table.field.basStation.crossZoneArea" |
| | | optionText="name" |
| | |
| | | |
| | | setLoading(true); |
| | | try { |
| | | const res = await request.post(`/warehouseAreas/many/${record.areas.join(',')}`); |
| | | // 提取排序信息和ID |
| | | // Old format: [1, 2, 3] (array of integers) |
| | | // New format: [{id: 1, sort: 1}, {id: 2, sort: 2}] (array of objects) |
| | | const isObjectArray = record.areas.length > 0 && |
| | | typeof record.areas[0] === 'object' && |
| | | record.areas[0] !== null && |
| | | 'id' in record.areas[0]; |
| | | |
| | | let areaIds = []; |
| | | let sortMap = new Map(); // 存储 id -> sort 的映射 |
| | | |
| | | if (isObjectArray) { |
| | | // 对象数组格式,提取ID和排序信息 |
| | | areaIds = record.areas.map(area => { |
| | | const id = area.id; |
| | | sortMap.set(id, area.sort || 0); |
| | | return id; |
| | | }); |
| | | } else { |
| | | // 纯ID数组格式 |
| | | areaIds = record.areas.map(id => Number(id)); |
| | | } |
| | | |
| | | const res = await request.post(`/warehouseAreas/many/${areaIds.join(',')}`); |
| | | if (res?.data?.code === 200) { |
| | | setAreaNames(res.data.data || []); |
| | | let areas = res.data.data || []; |
| | | |
| | | // 如果有排序信息,按排序值排序 |
| | | if (sortMap.size > 0) { |
| | | areas = areas.sort((a, b) => { |
| | | const sortA = sortMap.get(a.id) || 0; |
| | | const sortB = sortMap.get(b.id) || 0; |
| | | return sortA - sortB; |
| | | }); |
| | | } |
| | | |
| | | setAreaNames(areas); |
| | | } |
| | | } catch (error) { |
| | | console.error('获取区域名称失败:', error); |
| | |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceInput source="areaIdStart" label="table.field.deviceBind.typeId" reference="warehouseAreas" filter={{}}> |
| | | <ReferenceInput source="areaIdStart" label="table.field.deviceBind.typeId" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }} filter={{}}> |
| | | <AutocompleteInput optionValue="id" optionText="name" label={translate('table.field.deviceSite.areaIdStart')} /> |
| | | </ReferenceInput> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceInput source="areaIdEnd" label="table.field.deviceBind.typeId" reference="warehouseAreas" filter={{}}> |
| | | <ReferenceInput source="areaIdEnd" label="table.field.deviceBind.typeId" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }} filter={{}}> |
| | | <AutocompleteInput optionValue="id" optionText="name" label={translate('table.field.deviceSite.areaIdEnd')} /> |
| | | </ReferenceInput> |
| | | </Grid> |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | filter={{ warehouseId: formData.warehouseId }} |
| | | > |
| | | <AutocompleteInput |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | filter={{ warehouseId }} |
| | | > |
| | | <AutocompleteInput |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | filter={{ warehouseId }} |
| | | > |
| | | <AutocompleteInput |
| | |
| | | source="areaId" |
| | | label="table.field.loc.areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.locArea.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.locArea.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | filter={{ warehouseId }} |
| | | > |
| | | <AutocompleteInput |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | filter={{ warehouseId }} |
| | | > |
| | | <AutocompleteInput |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.locAreaMatRela.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.locAreaMatRela.areaId" |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.areaId" |
| New file |
| | |
| | | /** |
| | | * |
| | | * @author chen.lin |
| | | * @time 2026-02-02 |
| | | * 库区排序编辑组件 |
| | | * 功能:显示已选中的库区,允许修改排序值 |
| | | * 数据格式:[{"id": 1, "sort": 1}, {"id": 2, "sort": 2}] |
| | | * |
| | | * 使用隐藏字段存储排序信息,保存时合并到 areas 字段 |
| | | */ |
| | | import React, { useState, useEffect, useRef, useMemo } from 'react'; |
| | | import { useFormContext } from 'react-hook-form'; |
| | | import { |
| | | Box, |
| | | TextField, |
| | | Chip, |
| | | IconButton, |
| | | Paper, |
| | | Typography, |
| | | Stack, |
| | | } from '@mui/material'; |
| | | import DeleteIcon from '@mui/icons-material/Delete'; |
| | | import request from '@/utils/request'; |
| | | const AreasSortInput = ({ source }) => { |
| | | const { setValue, watch } = useFormContext(); |
| | | const sortSource = `${source}Sort`; // 隐藏字段名:areasSort |
| | | const [areas, setAreas] = useState([]); |
| | | const [selectedAreas, setSelectedAreas] = useState([]); |
| | | const [loading, setLoading] = useState(false); |
| | | const currentValue = watch(source) || []; |
| | | const prevSelectedAreasRef = useRef([]); |
| | | |
| | | // 加载所有库区选项(用于获取名称) |
| | | useEffect(() => { |
| | | const loadAreas = async () => { |
| | | setLoading(true); |
| | | try { |
| | | const res = await request.post('/warehouseAreas/list', {}); |
| | | if (res?.data?.code === 200) { |
| | | setAreas(res.data.data || []); |
| | | } else { |
| | | console.error('加载库区失败:', res?.data?.msg); |
| | | } |
| | | } catch (error) { |
| | | console.error('加载库区失败:', error); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | }; |
| | | loadAreas(); |
| | | }, []); |
| | | |
| | | // 初始化选中项:将后端数据格式转换为前端格式 |
| | | useEffect(() => { |
| | | // currentValue 现在应该是纯ID数组 [1, 2, 3],因为 ReferenceArrayInput 的 format 已经处理了 |
| | | if (currentValue && currentValue.length > 0) { |
| | | // 检查是否是纯ID数组 |
| | | const isIdArray = currentValue.every(item => typeof item === 'number' || typeof item === 'string'); |
| | | |
| | | if (isIdArray) { |
| | | // 纯ID数组格式 [1, 2, 3],需要与现有数据合并 |
| | | const currentIds = new Set(currentValue.map(id => Number(id))); |
| | | |
| | | // 保留已有的排序信息(如果存在) |
| | | const existingAreas = prevSelectedAreasRef.current.filter(item => |
| | | currentIds.has(Number(item.id)) |
| | | ); |
| | | const existingIds = new Set(existingAreas.map(item => Number(item.id))); |
| | | |
| | | // 找出新增的ID |
| | | const newIds = currentValue |
| | | .map(id => Number(id)) |
| | | .filter(id => !existingIds.has(id)); |
| | | |
| | | // 为新增的ID创建排序项(默认排序从库区的 sort 字段获取,如果没有则使用已有最大排序值+1) |
| | | const maxSort = existingAreas.length > 0 |
| | | ? Math.max(...existingAreas.map(item => item.sort || 1), 0) |
| | | : 0; |
| | | const newItems = newIds.map((id, index) => { |
| | | // 从 areas 数组中查找对应库区的 sort 字段 |
| | | const area = areas.find(a => a.id === id); |
| | | const defaultSort = area && area.sort !== undefined && area.sort !== null |
| | | ? area.sort |
| | | : (maxSort + index + 1); |
| | | return { |
| | | id: id, |
| | | sort: defaultSort, |
| | | }; |
| | | }); |
| | | |
| | | // 合并已有项和新项 |
| | | const converted = [...existingAreas, ...newItems]; |
| | | setSelectedAreas(converted); |
| | | prevSelectedAreasRef.current = converted; |
| | | // 保存排序信息到隐藏字段 |
| | | setValue(sortSource, converted, { shouldValidate: false }); |
| | | } else { |
| | | // 如果已经是对象数组格式 [{id, sort}](从后端直接加载的情况) |
| | | const sorted = [...currentValue].sort((a, b) => { |
| | | const sortA = a.sort || 0; |
| | | const sortB = b.sort || 0; |
| | | return sortA - sortB; |
| | | }); |
| | | setSelectedAreas(sorted); |
| | | prevSelectedAreasRef.current = sorted; |
| | | // 保存排序信息到隐藏字段 |
| | | setValue(sortSource, sorted, { shouldValidate: false }); |
| | | // 同时更新 ReferenceArrayInput 的值为纯ID数组 |
| | | const ids = sorted.map(item => item.id); |
| | | setValue(source, ids, { shouldValidate: false }); |
| | | } |
| | | } else { |
| | | setSelectedAreas([]); |
| | | prevSelectedAreasRef.current = []; |
| | | // 清空排序信息 |
| | | setValue(sortSource, [], { shouldValidate: false }); |
| | | } |
| | | }, [currentValue, source, setValue]); |
| | | |
| | | // 处理删除库区 |
| | | const handleDeleteArea = (id) => { |
| | | const filtered = selectedAreas.filter(item => item.id !== id); |
| | | setSelectedAreas(filtered); |
| | | prevSelectedAreasRef.current = filtered; |
| | | // 更新表单值为纯ID数组(ReferenceArrayInput 需要) |
| | | const ids = filtered.map(item => item.id); |
| | | setValue(source, ids, { shouldValidate: true, shouldDirty: true, shouldTouch: true }); |
| | | // 更新排序信息 |
| | | setValue(sortSource, filtered, { shouldValidate: false, shouldDirty: true, shouldTouch: true }); |
| | | }; |
| | | |
| | | // 处理修改排序值 |
| | | const handleSortChange = (id, newSort) => { |
| | | const sortValue = parseInt(newSort) || 1; |
| | | const updated = selectedAreas.map(item => { |
| | | if (item.id === id) { |
| | | return { ...item, sort: sortValue }; |
| | | } |
| | | return item; |
| | | }); |
| | | // 按排序值排序 |
| | | const sorted = [...updated].sort((a, b) => { |
| | | const sortA = a.sort || 0; |
| | | const sortB = b.sort || 0; |
| | | return sortA - sortB; |
| | | }); |
| | | setSelectedAreas(sorted); |
| | | prevSelectedAreasRef.current = sorted; |
| | | // 更新排序信息到隐藏字段,设置 shouldDirty: true 以触发表单的 dirty 状态 |
| | | setValue(sortSource, sorted, { shouldValidate: false, shouldDirty: true, shouldTouch: true }); |
| | | }; |
| | | |
| | | // 获取库区名称 |
| | | const getAreaName = (id) => { |
| | | if (!id) return '未知'; |
| | | const area = areas.find(a => a.id === id); |
| | | return area ? area.name : `ID: ${id}`; |
| | | }; |
| | | |
| | | // 确保列表始终按排序值排序 |
| | | const sortedAreas = useMemo(() => { |
| | | if (!selectedAreas || selectedAreas.length === 0) { |
| | | return []; |
| | | } |
| | | return [...selectedAreas].sort((a, b) => { |
| | | const sortA = a.sort || 0; |
| | | const sortB = b.sort || 0; |
| | | return sortA - sortB; |
| | | }); |
| | | }, [selectedAreas]); |
| | | |
| | | // 如果没有选中的库区,不显示 |
| | | if (!sortedAreas || sortedAreas.length === 0) { |
| | | return null; |
| | | } |
| | | |
| | | return ( |
| | | <Box sx={{ mt: 2 }}> |
| | | <Typography |
| | | variant="body2" |
| | | sx={{ |
| | | mb: 1, |
| | | color: 'text.secondary', |
| | | width: '100%', |
| | | }} |
| | | > |
| | | 已选库区(可修改排序): |
| | | </Typography> |
| | | |
| | | {/* 表头 */} |
| | | <Paper |
| | | elevation={0} |
| | | sx={{ |
| | | p: 1, |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | gap: 2, |
| | | width: '100%', |
| | | bgcolor: 'grey.100', |
| | | mb: 0.5, |
| | | }} |
| | | > |
| | | <Box sx={{ flex: 1, minWidth: 0 }}> |
| | | <Typography variant="body2" sx={{ fontWeight: 'medium' }}> |
| | | 库区 |
| | | </Typography> |
| | | </Box> |
| | | <Box sx={{ width: 120, textAlign: 'center' }}> |
| | | <Typography variant="body2" sx={{ fontWeight: 'medium' }}> |
| | | 排序 |
| | | </Typography> |
| | | </Box> |
| | | <Box sx={{ width: 48 }}></Box> {/* 删除按钮占位 */} |
| | | </Paper> |
| | | |
| | | <Stack spacing={1}> |
| | | {sortedAreas.map((item) => { |
| | | // 确保 item.id 存在且有效 |
| | | if (!item || item.id === undefined || item.id === null) { |
| | | return null; |
| | | } |
| | | return ( |
| | | <Paper |
| | | key={item.id} |
| | | elevation={1} |
| | | sx={{ |
| | | p: 1.5, |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | gap: 2, |
| | | width: '100%', |
| | | height: '30px', |
| | | minHeight: 'auto', |
| | | maxWidth: '100%', |
| | | boxSizing: 'border-box', |
| | | }} |
| | | > |
| | | {/* 左边:库区名称 */} |
| | | <Box sx={{ flex: 1, minWidth: 0 }}> |
| | | <Chip |
| | | label={getAreaName(item.id)} |
| | | size="small" |
| | | sx={{ maxWidth: '100%' }} |
| | | /> |
| | | </Box> |
| | | |
| | | {/* 右边:排序输入框(无 label) */} |
| | | <TextField |
| | | type="number" |
| | | value={item.sort || ''} |
| | | onChange={(e) => handleSortChange(item.id, e.target.value)} |
| | | size="small" |
| | | placeholder="排序" |
| | | sx={{ |
| | | width: 120, |
| | | '& .MuiInputBase-root': { |
| | | height: '30px', |
| | | }, |
| | | '& .MuiInputBase-input': { |
| | | height: '30px', |
| | | padding: '8.5px 14px', |
| | | } |
| | | }} |
| | | inputProps={{ |
| | | min: 1, |
| | | step: 1 |
| | | }} |
| | | /> |
| | | |
| | | {/* 删除按钮 */} |
| | | <IconButton |
| | | size="small" |
| | | onClick={() => handleDeleteArea(item.id)} |
| | | color="error" |
| | | > |
| | | <DeleteIcon fontSize="small" /> |
| | | </IconButton> |
| | | </Paper> |
| | | ); |
| | | })} |
| | | </Stack> |
| | | </Box> |
| | | ); |
| | | }; |
| | | |
| | | export default AreasSortInput; |
| | |
| | | import EditIcon from '@mui/icons-material/Edit'; |
| | | import { useState, useEffect } from 'react'; |
| | | import { useState, useEffect, useMemo } from 'react'; |
| | | import { |
| | | Button, useListContext, SelectInput, |
| | | required, SelectArrayInput, |
| | | useTranslate, useNotify |
| | | } from 'react-admin'; |
| | | import { useFormContext } from 'react-hook-form'; |
| | | import request from '@/utils/request'; |
| | | |
| | | const DictionarySelect = (props) => { |
| | |
| | | } = props; |
| | | const translate = useTranslate(); |
| | | const notify = useNotify(); |
| | | const { watch } = useFormContext(); |
| | | const [list, setList] = useState([]); |
| | | const [loading, setLoading] = useState(false); |
| | | |
| | | // 获取当前表单值 |
| | | const currentValue = watch(name); |
| | | |
| | | useEffect(() => { |
| | | http(); |
| | |
| | | } |
| | | }; |
| | | |
| | | // 确保当前值在选项中,如果不在且正在加载,添加占位选项 |
| | | const choices = useMemo(() => { |
| | | if (!list || list.length === 0) { |
| | | // 如果列表为空但当前有值,添加占位选项以避免警告 |
| | | if (currentValue !== undefined && currentValue !== null && currentValue !== '') { |
| | | return [{ id: currentValue, name: String(currentValue) }]; |
| | | } |
| | | return []; |
| | | } |
| | | |
| | | // 检查当前值是否在选项中 |
| | | const valueExists = list.some(item => String(item.id) === String(currentValue)); |
| | | |
| | | // 如果当前值不在选项中,添加它(可能是加载延迟导致的) |
| | | if (currentValue !== undefined && currentValue !== null && currentValue !== '' && !valueExists) { |
| | | return [...list, { id: currentValue, name: String(currentValue) }]; |
| | | } |
| | | |
| | | return list; |
| | | }, [list, currentValue]); |
| | | |
| | | const InputComponent = multiple ? SelectArrayInput : SelectInput; |
| | | |
| | | return ( |
| | | <InputComponent |
| | | source={name} |
| | | choices={list} |
| | | choices={choices} |
| | | isLoading={loading} |
| | | {...parmas} |
| | | /> |
| New file |
| | |
| | | import React, { useState, useEffect } from 'react'; |
| | | import { useFormContext } from 'react-hook-form'; |
| | | import { |
| | | Box, |
| | | TextField, |
| | | Autocomplete, |
| | | Chip, |
| | | IconButton, |
| | | Paper, |
| | | Typography, |
| | | Stack, |
| | | } from '@mui/material'; |
| | | import DeleteIcon from '@mui/icons-material/Delete'; |
| | | import request from '@/utils/request'; |
| | | |
| | | /** |
| | | * @author chen.lin |
| | | * @time 2026-02-02 |
| | | * 可排序的库区选择组件 |
| | | * 数据格式:[{"id": 1, "sort": 1}, {"id": 2, "sort": 2}] |
| | | * |
| | | * 功能: |
| | | * 1. 上方:多选框选择库区 |
| | | * 2. 下方:显示已选中的库区,每行显示库区名称和排序输入框 |
| | | * 3. 默认排序为1,可以修改排序值 |
| | | */ |
| | | const SortableAreasInput = ({ source, label, validate, ...props }) => { |
| | | const { setValue, watch } = useFormContext(); |
| | | const [areas, setAreas] = useState([]); |
| | | const [selectedAreas, setSelectedAreas] = useState([]); |
| | | const [loading, setLoading] = useState(false); |
| | | const currentValue = watch(source) || []; |
| | | |
| | | // 加载所有库区选项 |
| | | useEffect(() => { |
| | | const loadAreas = async () => { |
| | | setLoading(true); |
| | | try { |
| | | const res = await request.post('/warehouseAreas/list', {}); |
| | | if (res?.data?.code === 200) { |
| | | setAreas(res.data.data || []); |
| | | } else { |
| | | console.error('加载库区失败:', res?.data?.msg); |
| | | } |
| | | } catch (error) { |
| | | console.error('加载库区失败:', error); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | }; |
| | | loadAreas(); |
| | | }, []); |
| | | |
| | | // 初始化选中项:将后端数据格式转换为前端格式 |
| | | useEffect(() => { |
| | | if (currentValue && currentValue.length > 0) { |
| | | // 如果已经是对象数组格式 [{id, sort}] |
| | | if (typeof currentValue[0] === 'object' && currentValue[0].id !== undefined) { |
| | | const sorted = [...currentValue].sort((a, b) => { |
| | | const sortA = a.sort || 0; |
| | | const sortB = b.sort || 0; |
| | | return sortA - sortB; |
| | | }); |
| | | setSelectedAreas(sorted); |
| | | } else { |
| | | // 如果是旧格式 [1, 2, 3],转换为新格式,默认排序为1 |
| | | const converted = currentValue.map((id, index) => ({ |
| | | id: id, |
| | | sort: index + 1, |
| | | })); |
| | | setSelectedAreas(converted); |
| | | // 同步更新表单值 |
| | | setValue(source, converted, { shouldValidate: false }); |
| | | } |
| | | } else { |
| | | setSelectedAreas([]); |
| | | } |
| | | }, [currentValue, source, setValue]); |
| | | |
| | | // 处理添加库区 |
| | | const handleAddArea = (event, newValue) => { |
| | | if (newValue && !selectedAreas.find(item => item.id === newValue.id)) { |
| | | // 默认排序为1,如果已有数据,使用最大排序值+1 |
| | | const maxSort = selectedAreas.length > 0 |
| | | ? Math.max(...selectedAreas.map(item => item.sort || 1)) |
| | | : 0; |
| | | const newItem = { |
| | | id: newValue.id, |
| | | sort: maxSort + 1, |
| | | }; |
| | | const updated = [...selectedAreas, newItem]; |
| | | setSelectedAreas(updated); |
| | | setValue(source, updated, { shouldValidate: true }); |
| | | } |
| | | }; |
| | | |
| | | // 处理删除库区 |
| | | const handleDeleteArea = (id) => { |
| | | const filtered = selectedAreas.filter(item => item.id !== id); |
| | | setSelectedAreas(filtered); |
| | | setValue(source, filtered, { shouldValidate: true }); |
| | | }; |
| | | |
| | | // 处理修改排序值 |
| | | const handleSortChange = (id, newSort) => { |
| | | const sortValue = parseInt(newSort) || 1; |
| | | const updated = selectedAreas.map(item => { |
| | | if (item.id === id) { |
| | | return { ...item, sort: sortValue }; |
| | | } |
| | | return item; |
| | | }); |
| | | setSelectedAreas(updated); |
| | | setValue(source, updated, { shouldValidate: true }); |
| | | }; |
| | | |
| | | // 获取库区名称 |
| | | const getAreaName = (id) => { |
| | | const area = areas.find(a => a.id === id); |
| | | return area ? area.name : `ID: ${id}`; |
| | | }; |
| | | |
| | | // 过滤已选中的选项 |
| | | const availableOptions = areas.filter( |
| | | area => !selectedAreas.find(item => item.id === area.id) |
| | | ); |
| | | |
| | | return ( |
| | | <Box> |
| | | {/* 多选框 */} |
| | | <Autocomplete |
| | | multiple |
| | | options={availableOptions} |
| | | getOptionLabel={(option) => option.name || ''} |
| | | onChange={handleAddArea} |
| | | loading={loading} |
| | | value={[]} // 始终为空,因为已选中的在下方面板显示 |
| | | renderInput={(params) => ( |
| | | <TextField |
| | | {...params} |
| | | label={label} |
| | | placeholder="选择库区..." |
| | | variant="outlined" |
| | | size="small" |
| | | /> |
| | | )} |
| | | sx={{ mb: 2 }} |
| | | /> |
| | | |
| | | {/* 已选中的库区列表,显示键值对(库区名称:排序值) */} |
| | | {selectedAreas.length > 0 && ( |
| | | <Box sx={{ mt: 2 }}> |
| | | <Typography variant="body2" sx={{ mb: 1, color: 'text.secondary' }}> |
| | | 已选库区(可修改排序): |
| | | </Typography> |
| | | <Stack spacing={1}> |
| | | {selectedAreas.map((item) => ( |
| | | <Paper |
| | | key={item.id} |
| | | elevation={1} |
| | | sx={{ |
| | | p: 1.5, |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | gap: 2, |
| | | }} |
| | | > |
| | | {/* 左边:库区名称 */} |
| | | <Box sx={{ flex: 1, minWidth: 0 }}> |
| | | <Chip |
| | | label={getAreaName(item.id)} |
| | | size="small" |
| | | sx={{ maxWidth: '100%' }} |
| | | /> |
| | | </Box> |
| | | |
| | | {/* 右边:排序输入框 */} |
| | | <TextField |
| | | type="number" |
| | | label="排序" |
| | | value={item.sort || ''} |
| | | onChange={(e) => handleSortChange(item.id, e.target.value)} |
| | | size="small" |
| | | sx={{ width: 120 }} |
| | | inputProps={{ |
| | | min: 1, |
| | | step: 1 |
| | | }} |
| | | /> |
| | | |
| | | {/* 删除按钮 */} |
| | | <IconButton |
| | | size="small" |
| | | onClick={() => handleDeleteArea(item.id)} |
| | | color="error" |
| | | > |
| | | <DeleteIcon fontSize="small" /> |
| | | </IconButton> |
| | | </Paper> |
| | | ))} |
| | | </Stack> |
| | | </Box> |
| | | )} |
| | | </Box> |
| | | ); |
| | | }; |
| | | |
| | | export default SortableAreasInput; |
| | |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceInput source="typeId" label="table.field.deviceBind.typeId" reference="warehouseAreas" filter={{}}> |
| | | <ReferenceInput source="typeId" label="table.field.deviceBind.typeId" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }} filter={{}}> |
| | | <AutocompleteInput optionValue="id" optionText="name" label="table.field.deviceBind.typeId" /> |
| | | </ReferenceInput> |
| | | </Grid> |
| | |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <ReferenceInput source="typeId" label="table.field.deviceBind.typeId" reference="warehouseAreas" filter={{}}> |
| | | <ReferenceInput source="typeId" label="table.field.deviceBind.typeId" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }} filter={{}}> |
| | | <AutocompleteInput optionValue="id" optionText="name" label="table.field.deviceBind.typeId" /> |
| | | </ReferenceInput> |
| | | </Stack> |
| | |
| | | <ReferenceInput |
| | | source="areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | filter={{ warehouseId }} |
| | | > |
| | | <AutocompleteInput |
| | |
| | | source="areaId" |
| | | label="table.field.loc.areaId" |
| | | reference="warehouseAreas" |
| | | sort={{ field: 'sort', order: 'ASC' }} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.areaId" |
| | |
| | | /> |
| | | </Grid> |
| | | <Grid item md={2}> |
| | | <ReferenceInput source="areaId" reference="warehouseAreas"> |
| | | <ReferenceInput source="areaId" reference="warehouseAreas" sort={{ field: 'sort', order: 'ASC' }}> |
| | | <AutocompleteInput |
| | | optionText='name' |
| | | optionValue="id" |
| New file |
| | |
| | | import React, { useState, useRef, useEffect, useMemo } from "react"; |
| | | import { |
| | | CreateBase, |
| | | useTranslate, |
| | | TextInput, |
| | | NumberInput, |
| | | BooleanInput, |
| | | DateInput, |
| | | SaveButton, |
| | | SelectInput, |
| | | ReferenceInput, |
| | | ReferenceArrayInput, |
| | | AutocompleteInput, |
| | | Toolbar, |
| | | required, |
| | | useDataProvider, |
| | | useNotify, |
| | | Form, |
| | | useCreateController, |
| | | } from 'react-admin'; |
| | | import { |
| | | Dialog, |
| | | DialogActions, |
| | | DialogContent, |
| | | DialogTitle, |
| | | Stack, |
| | | Grid, |
| | | Box, |
| | | } from '@mui/material'; |
| | | import DialogCloseButton from "../components/DialogCloseButton"; |
| | | import StatusSelectInput from "../components/StatusSelectInput"; |
| | | import MemoInput from "../components/MemoInput"; |
| | | |
| | | const TaskPathTemplateMergeCreate = (props) => { |
| | | const { open, setOpen } = props; |
| | | |
| | | const translate = useTranslate(); |
| | | const notify = useNotify(); |
| | | |
| | | const handleClose = (event, reason) => { |
| | | if (reason !== "backdropClick") { |
| | | setOpen(false); |
| | | } |
| | | }; |
| | | |
| | | const handleSuccess = async (data) => { |
| | | setOpen(false); |
| | | notify('common.response.success'); |
| | | }; |
| | | |
| | | const handleError = async (error) => { |
| | | notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } }); |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <CreateBase |
| | | record={{}} |
| | | transform={(data) => { |
| | | return data; |
| | | }} |
| | | mutationOptions={{ onSuccess: handleSuccess, onError: handleError }} |
| | | > |
| | | <Dialog |
| | | open={open} |
| | | onClose={handleClose} |
| | | aria-labelledby="form-dialog-title" |
| | | fullWidth |
| | | disableRestoreFocus |
| | | maxWidth="md" // 'xs' | 'sm' | 'md' | 'lg' | 'xl' |
| | | > |
| | | <Form> |
| | | <DialogTitle id="form-dialog-title" sx={{ |
| | | position: 'sticky', |
| | | top: 0, |
| | | backgroundColor: 'background.paper', |
| | | zIndex: 1000 |
| | | }} |
| | | > |
| | | {translate('create.title')} |
| | | <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}> |
| | | <DialogCloseButton onClose={handleClose} /> |
| | | </Box> |
| | | </DialogTitle> |
| | | <DialogContent sx={{ mt: 2 }}> |
| | | <Grid container rowSpacing={2} columnSpacing={2}> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.templateCode" |
| | | source="templateCode" |
| | | parse={v => v} |
| | | autoFocus |
| | | validate={required()} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.templateName" |
| | | source="templateName" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.sourceType" |
| | | source="sourceType" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.targetType" |
| | | source="targetType" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.conditionExpression" |
| | | source="conditionExpression" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.conditionDesc" |
| | | source="conditionDesc" |
| | | parse={v => v} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.version" |
| | | source="version" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.isCurrent" |
| | | source="isCurrent" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <DateInput |
| | | label="table.field.taskPathTemplateMerge.effectiveTime" |
| | | source="effectiveTime" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <DateInput |
| | | label="table.field.taskPathTemplateMerge.expireTime" |
| | | source="expireTime" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.priority" |
| | | source="priority" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.timeoutMinutes" |
| | | source="timeoutMinutes" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.maxRetryTimes" |
| | | source="maxRetryTimes" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.retryIntervalSeconds" |
| | | source="retryIntervalSeconds" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.remark" |
| | | source="remark" |
| | | parse={v => v} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.stepSize" |
| | | source="stepSize" |
| | | /> |
| | | </Grid> |
| | | |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <StatusSelectInput /> |
| | | </Grid> |
| | | <Grid item xs={12} display="flex" gap={1}> |
| | | <Stack direction="column" spacing={1} width={'100%'}> |
| | | <MemoInput /> |
| | | </Stack> |
| | | </Grid> |
| | | </Grid> |
| | | </DialogContent> |
| | | <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}> |
| | | <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }} > |
| | | <SaveButton /> |
| | | </Toolbar> |
| | | </DialogActions> |
| | | </Form> |
| | | </Dialog> |
| | | </CreateBase> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default TaskPathTemplateMergeCreate; |
| New file |
| | |
| | | import React, { useState, useRef, useEffect, useMemo } from "react"; |
| | | import { |
| | | Edit, |
| | | SimpleForm, |
| | | FormDataConsumer, |
| | | useTranslate, |
| | | TextInput, |
| | | NumberInput, |
| | | BooleanInput, |
| | | DateInput, |
| | | SelectInput, |
| | | ReferenceInput, |
| | | ReferenceArrayInput, |
| | | AutocompleteInput, |
| | | SaveButton, |
| | | Toolbar, |
| | | Labeled, |
| | | NumberField, |
| | | required, |
| | | useRecordContext, |
| | | DeleteButton, |
| | | } from 'react-admin'; |
| | | import { useWatch, useFormContext } from "react-hook-form"; |
| | | import { Stack, Grid, Box, Typography } from '@mui/material'; |
| | | import * as Common from '@/utils/common'; |
| | | import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting'; |
| | | import EditBaseAside from "../components/EditBaseAside"; |
| | | import CustomerTopToolBar from "../components/EditTopToolBar"; |
| | | import MemoInput from "../components/MemoInput"; |
| | | import StatusSelectInput from "../components/StatusSelectInput"; |
| | | |
| | | const FormToolbar = () => { |
| | | const { getValues } = useFormContext(); |
| | | |
| | | return ( |
| | | <Toolbar sx={{ justifyContent: 'space-between' }}> |
| | | <SaveButton /> |
| | | <DeleteButton mutationMode="optimistic" /> |
| | | </Toolbar> |
| | | ) |
| | | } |
| | | |
| | | const TaskPathTemplateMergeEdit = () => { |
| | | const translate = useTranslate(); |
| | | |
| | | return ( |
| | | <Edit |
| | | redirect="list" |
| | | mutationMode={EDIT_MODE} |
| | | actions={<CustomerTopToolBar />} |
| | | aside={<EditBaseAside />} |
| | | > |
| | | <SimpleForm |
| | | shouldUnregister |
| | | warnWhenUnsavedChanges |
| | | toolbar={<FormToolbar />} |
| | | mode="onTouched" |
| | | defaultValues={{}} |
| | | // validate={(values) => { }} |
| | | > |
| | | <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}> |
| | | <Grid item xs={12} md={8}> |
| | | <Typography variant="h6" gutterBottom> |
| | | {translate('common.edit.title.main')} |
| | | </Typography> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.templateCode" |
| | | source="templateCode" |
| | | parse={v => v} |
| | | autoFocus |
| | | validate={required()} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.templateName" |
| | | source="templateName" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.sourceType" |
| | | source="sourceType" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.targetType" |
| | | source="targetType" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.conditionExpression" |
| | | source="conditionExpression" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.conditionDesc" |
| | | source="conditionDesc" |
| | | parse={v => v} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.version" |
| | | source="version" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.isCurrent" |
| | | source="isCurrent" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <DateInput |
| | | label="table.field.taskPathTemplateMerge.effectiveTime" |
| | | source="effectiveTime" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <DateInput |
| | | label="table.field.taskPathTemplateMerge.expireTime" |
| | | source="expireTime" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.priority" |
| | | source="priority" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.timeoutMinutes" |
| | | source="timeoutMinutes" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.maxRetryTimes" |
| | | source="maxRetryTimes" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.retryIntervalSeconds" |
| | | source="retryIntervalSeconds" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.taskPathTemplateMerge.remark" |
| | | source="remark" |
| | | parse={v => v} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.taskPathTemplateMerge.stepSize" |
| | | source="stepSize" |
| | | /> |
| | | </Stack> |
| | | |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | | <Typography variant="h6" gutterBottom> |
| | | {translate('common.edit.title.common')} |
| | | </Typography> |
| | | <StatusSelectInput /> |
| | | <Box mt="2em" /> |
| | | <MemoInput /> |
| | | </Grid> |
| | | </Grid> |
| | | </SimpleForm> |
| | | </Edit > |
| | | ) |
| | | } |
| | | |
| | | export default TaskPathTemplateMergeEdit; |
| New file |
| | |
| | | import React, { useState, useRef, useEffect, useMemo, useCallback } from "react"; |
| | | import { useNavigate } from 'react-router-dom'; |
| | | import { |
| | | List, |
| | | DatagridConfigurable, |
| | | SearchInput, |
| | | TopToolbar, |
| | | SelectColumnsButton, |
| | | EditButton, |
| | | FilterButton, |
| | | CreateButton, |
| | | ExportButton, |
| | | BulkDeleteButton, |
| | | WrapperField, |
| | | useRecordContext, |
| | | useTranslate, |
| | | useNotify, |
| | | useListContext, |
| | | FunctionField, |
| | | TextField, |
| | | NumberField, |
| | | DateField, |
| | | BooleanField, |
| | | ReferenceField, |
| | | TextInput, |
| | | DateTimeInput, |
| | | DateInput, |
| | | SelectInput, |
| | | NumberInput, |
| | | ReferenceInput, |
| | | ReferenceArrayInput, |
| | | AutocompleteInput, |
| | | DeleteButton, |
| | | } from 'react-admin'; |
| | | import { Box, Typography, Card, Stack } from '@mui/material'; |
| | | import { styled } from '@mui/material/styles'; |
| | | import TaskPathTemplateMergeCreate from "./TaskPathTemplateMergeCreate"; |
| | | import TaskPathTemplateMergePanel from "./TaskPathTemplateMergePanel"; |
| | | import EmptyData from "../components/EmptyData"; |
| | | import MyCreateButton from "../components/MyCreateButton"; |
| | | import MyExportButton from '../components/MyExportButton'; |
| | | import PageDrawer from "../components/PageDrawer"; |
| | | import MyField from "../components/MyField"; |
| | | import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting'; |
| | | import * as Common from '@/utils/common'; |
| | | |
| | | const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({ |
| | | '& .css-1vooibu-MuiSvgIcon-root': { |
| | | height: '.9em' |
| | | }, |
| | | '& .RaDatagrid-row': { |
| | | cursor: 'auto' |
| | | }, |
| | | '& .column-name': { |
| | | }, |
| | | '& .opt': { |
| | | width: 200 |
| | | }, |
| | | })); |
| | | |
| | | const filters = [ |
| | | <SearchInput source="condition" alwaysOn />, |
| | | <DateInput label='common.time.after' source="timeStart" alwaysOn />, |
| | | <DateInput label='common.time.before' source="timeEnd" alwaysOn />, |
| | | |
| | | <TextInput source="templateCode" label="table.field.taskPathTemplateMerge.templateCode" />, |
| | | <TextInput source="templateName" label="table.field.taskPathTemplateMerge.templateName" />, |
| | | <TextInput source="sourceType" label="table.field.taskPathTemplateMerge.sourceType" />, |
| | | <TextInput source="targetType" label="table.field.taskPathTemplateMerge.targetType" />, |
| | | <TextInput source="conditionExpression" label="table.field.taskPathTemplateMerge.conditionExpression" />, |
| | | <TextInput source="conditionDesc" label="table.field.taskPathTemplateMerge.conditionDesc" />, |
| | | <NumberInput source="version" label="table.field.taskPathTemplateMerge.version" />, |
| | | <NumberInput source="isCurrent" label="table.field.taskPathTemplateMerge.isCurrent" />, |
| | | <DateInput source="effectiveTime" label="table.field.taskPathTemplateMerge.effectiveTime" />, |
| | | <DateInput source="expireTime" label="table.field.taskPathTemplateMerge.expireTime" />, |
| | | <NumberInput source="priority" label="table.field.taskPathTemplateMerge.priority" />, |
| | | <NumberInput source="timeoutMinutes" label="table.field.taskPathTemplateMerge.timeoutMinutes" />, |
| | | <NumberInput source="maxRetryTimes" label="table.field.taskPathTemplateMerge.maxRetryTimes" />, |
| | | <NumberInput source="retryIntervalSeconds" label="table.field.taskPathTemplateMerge.retryIntervalSeconds" />, |
| | | <TextInput source="remark" label="table.field.taskPathTemplateMerge.remark" />, |
| | | <NumberInput source="stepSize" label="table.field.taskPathTemplateMerge.stepSize" />, |
| | | |
| | | <TextInput label="common.field.memo" source="memo" />, |
| | | <SelectInput |
| | | label="common.field.status" |
| | | source="status" |
| | | choices={[ |
| | | { id: '1', name: 'common.enums.statusTrue' }, |
| | | { id: '0', name: 'common.enums.statusFalse' }, |
| | | ]} |
| | | resettable |
| | | />, |
| | | ] |
| | | |
| | | const TaskPathTemplateMergeList = () => { |
| | | const translate = useTranslate(); |
| | | |
| | | const [createDialog, setCreateDialog] = useState(false); |
| | | const [drawerVal, setDrawerVal] = useState(false); |
| | | |
| | | return ( |
| | | <Box display="flex"> |
| | | <List |
| | | sx={{ |
| | | flexGrow: 1, |
| | | transition: (theme) => |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.taskPathTemplateMerge"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | | filters={filters} |
| | | sort={{ field: "create_time", order: "desc" }} |
| | | actions={( |
| | | <TopToolbar> |
| | | <FilterButton /> |
| | | <MyCreateButton onClick={() => { setCreateDialog(true) }} /> |
| | | <SelectColumnsButton preferenceKey='taskPathTemplateMerge' /> |
| | | <MyExportButton /> |
| | | </TopToolbar> |
| | | )} |
| | | perPage={DEFAULT_PAGE_SIZE} |
| | | > |
| | | <StyledDatagrid |
| | | preferenceKey='taskPathTemplateMerge' |
| | | bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />} |
| | | rowClick={(id, resource, record) => false} |
| | | expand={() => <TaskPathTemplateMergePanel />} |
| | | expandSingle={true} |
| | | omit={['id', 'createTime', 'createBy', 'memo']} |
| | | > |
| | | <NumberField source="id" /> |
| | | <TextField source="templateCode" label="table.field.taskPathTemplateMerge.templateCode" /> |
| | | <TextField source="templateName" label="table.field.taskPathTemplateMerge.templateName" /> |
| | | <TextField source="sourceType" label="table.field.taskPathTemplateMerge.sourceType" /> |
| | | <TextField source="targetType" label="table.field.taskPathTemplateMerge.targetType" /> |
| | | <TextField source="conditionExpression" label="table.field.taskPathTemplateMerge.conditionExpression" /> |
| | | <TextField source="conditionDesc" label="table.field.taskPathTemplateMerge.conditionDesc" /> |
| | | <NumberField source="version" label="table.field.taskPathTemplateMerge.version" /> |
| | | <NumberField source="isCurrent" label="table.field.taskPathTemplateMerge.isCurrent" /> |
| | | <DateField source="effectiveTime" label="table.field.taskPathTemplateMerge.effectiveTime" showTime /> |
| | | <DateField source="expireTime" label="table.field.taskPathTemplateMerge.expireTime" showTime /> |
| | | <NumberField source="priority" label="table.field.taskPathTemplateMerge.priority" /> |
| | | <NumberField source="timeoutMinutes" label="table.field.taskPathTemplateMerge.timeoutMinutes" /> |
| | | <NumberField source="maxRetryTimes" label="table.field.taskPathTemplateMerge.maxRetryTimes" /> |
| | | <NumberField source="retryIntervalSeconds" label="table.field.taskPathTemplateMerge.retryIntervalSeconds" /> |
| | | <TextField source="remark" label="table.field.taskPathTemplateMerge.remark" /> |
| | | <NumberField source="stepSize" label="table.field.taskPathTemplateMerge.stepSize" /> |
| | | |
| | | <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}> |
| | | <TextField source="nickname" /> |
| | | </ReferenceField> |
| | | <DateField source="updateTime" label="common.field.updateTime" showTime /> |
| | | <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}> |
| | | <TextField source="nickname" /> |
| | | </ReferenceField> |
| | | <DateField source="createTime" label="common.field.createTime" showTime /> |
| | | <BooleanField source="statusBool" label="common.field.status" sortable={false} /> |
| | | <TextField source="memo" label="common.field.memo" sortable={false} /> |
| | | <WrapperField cellClassName="opt" label="common.field.opt"> |
| | | <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} /> |
| | | <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} /> |
| | | </WrapperField> |
| | | </StyledDatagrid> |
| | | </List> |
| | | <TaskPathTemplateMergeCreate |
| | | open={createDialog} |
| | | setOpen={setCreateDialog} |
| | | /> |
| | | <PageDrawer |
| | | title='TaskPathTemplateMerge Detail' |
| | | drawerVal={drawerVal} |
| | | setDrawerVal={setDrawerVal} |
| | | > |
| | | </PageDrawer> |
| | | </Box> |
| | | ) |
| | | } |
| | | |
| | | export default TaskPathTemplateMergeList; |
| New file |
| | |
| | | import React, { useState, useRef, useEffect, useMemo } from "react"; |
| | | import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material'; |
| | | import { |
| | | useTranslate, |
| | | useRecordContext, |
| | | } from 'react-admin'; |
| | | import PanelTypography from "../components/PanelTypography"; |
| | | import * as Common from '@/utils/common' |
| | | |
| | | const TaskPathTemplateMergePanel = () => { |
| | | const record = useRecordContext(); |
| | | if (!record) return null; |
| | | const translate = useTranslate(); |
| | | return ( |
| | | <> |
| | | <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}> |
| | | <CardContent> |
| | | <Grid container spacing={2}> |
| | | <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}> |
| | | <Typography variant="h6" gutterBottom align="left" sx={{ |
| | | maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' }, |
| | | whiteSpace: 'nowrap', |
| | | overflow: 'hidden', |
| | | textOverflow: 'ellipsis', |
| | | }}> |
| | | {Common.camelToPascalWithSpaces(translate('table.field.taskPathTemplateMerge.id'))}: {record.id} |
| | | </Typography> |
| | | {/* inherit, primary, secondary, textPrimary, textSecondary, error */} |
| | | <Typography variant="h6" gutterBottom align="right" > |
| | | ID: {record.id} |
| | | </Typography> |
| | | </Grid> |
| | | </Grid> |
| | | <Grid container spacing={2}> |
| | | <Grid item xs={12} container alignContent="flex-end"> |
| | | <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}> |
| | | {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo} |
| | | </Typography> |
| | | </Grid> |
| | | </Grid> |
| | | <Box height={20}> </Box> |
| | | <Grid container spacing={2}> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.templateCode" |
| | | property={record.templateCode} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.templateName" |
| | | property={record.templateName} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.sourceType" |
| | | property={record.sourceType} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.targetType" |
| | | property={record.targetType} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.conditionExpression" |
| | | property={record.conditionExpression} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.conditionDesc" |
| | | property={record.conditionDesc} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.version" |
| | | property={record.version} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.isCurrent" |
| | | property={record.isCurrent} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.effectiveTime" |
| | | property={record.effectiveTime$} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.expireTime" |
| | | property={record.expireTime$} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.priority" |
| | | property={record.priority} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.timeoutMinutes" |
| | | property={record.timeoutMinutes} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.maxRetryTimes" |
| | | property={record.maxRetryTimes} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.retryIntervalSeconds" |
| | | property={record.retryIntervalSeconds} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.remark" |
| | | property={record.remark} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.taskPathTemplateMerge.stepSize" |
| | | property={record.stepSize} |
| | | /> |
| | | </Grid> |
| | | |
| | | </Grid> |
| | | </CardContent> |
| | | </Card > |
| | | </> |
| | | ); |
| | | }; |
| | | |
| | | export default TaskPathTemplateMergePanel; |
| New file |
| | |
| | | import React, { useState, useRef, useEffect, useMemo } from "react"; |
| | | import { |
| | | ListGuesser, |
| | | EditGuesser, |
| | | ShowGuesser, |
| | | } from "react-admin"; |
| | | |
| | | import TaskPathTemplateMergeList from "./TaskPathTemplateMergeList"; |
| | | import TaskPathTemplateMergeEdit from "./TaskPathTemplateMergeEdit"; |
| | | |
| | | export default { |
| | | list: TaskPathTemplateMergeList, |
| | | edit: TaskPathTemplateMergeEdit, |
| | | show: ShowGuesser, |
| | | recordRepresentation: (record) => { |
| | | return `${record.id}` |
| | | } |
| | | }; |
| | |
| | | parse={v => v} |
| | | validate={[required()]} |
| | | /> |
| | | <NumberInput |
| | | label="table.field.warehouseAreas.sort" |
| | | source="sort" |
| | | parse={v => v} |
| | | /> |
| | | {/* <SelectInput |
| | | label="table.field.warehouseAreas.flagLabelMange" |
| | | source="flagLabelMange" |
| | |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.warehouseAreas.sort" |
| | | source="sort" |
| | | parse={v => v} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <SelectInput |
| | | label="table.field.warehouseAreas.flagMix" |
| | | source="flagMix" |
| | |
| | | <TextField source="code" label="table.field.warehouseAreas.code" /> |
| | | <TextField source="name" label="table.field.warehouseAreas.name" /> |
| | | <TextField source="type$" label="table.field.warehouseAreas.type"/> |
| | | <NumberField source="sort" label="table.field.warehouseAreas.sort" /> |
| | | <TextField source="shipperId$" label="table.field.warehouseAreas.shipperId" /> |
| | | <NumberField source="supplierId" label="table.field.warehouseAreas.supplierId" /> |
| | | <TextField source="flagMix$" label="table.field.warehouseAreas.flagMix" sortable={false} /> |
| | |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.warehouseAreas.sort" |
| | | property={record.sort} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.warehouseAreas.shipperId" |
| | | property={record.shipperId$} |
| | | /> |
| New file |
| | |
| | | package com.vincent.rsf.openApi.controller; |
| | | |
| | | import com.vincent.rsf.openApi.entity.dto.CommonResponse; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | 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.RestController; |
| | | |
| | | @RestController |
| | | @Api("任务中转站") |
| | | @Slf4j |
| | | @RequestMapping("/mission") |
| | | public class MissionTransferStationController { |
| | | |
| | | @ApiOperation("任务总控") |
| | | @PostMapping("/task/master/control") |
| | | public CommonResponse missionMasterControl(@RequestBody Object objParams) { |
| | | //判断需要下发系统 |
| | | //判断下发方式 |
| | | //返回结果 |
| | | return CommonResponse.ok(); |
| | | } |
| | | } |
| | |
| | | return pdaOtherService.inspectConfirm2(generalParam); |
| | | } |
| | | |
| | | @RequestMapping(value = "/other/station/page") |
| | | public R stationPage(@RequestParam String staNo, |
| | | @RequestParam(defaultValue = "1") Integer curr, |
| | | @RequestParam(defaultValue = "5") Integer limit) { |
| | | |
| | | return pdaOtherService.stationPage(staNo,curr,limit); |
| | | } |
| | | |
| | | |
| | | @PostMapping(value = "/other/staOperate") |
| | | public R staOperate(@RequestBody PdaGeneralParam generalParam) { |
| | | |
| | | return pdaOtherService.staOperate(generalParam,getLoginUser()); |
| | | } |
| | | |
| | | @RequestMapping(value = "/other/loc/page") |
| | | public R locPage(@RequestParam String locNo, |
| | | @RequestParam(defaultValue = "1") Integer curr, |
| | | @RequestParam(defaultValue = "5") Integer limit) { |
| | | |
| | | return pdaOtherService.locPage(locNo,curr,limit); |
| | | } |
| | | |
| | | @PostMapping(value = "/other/locOperate") |
| | | public R locOperate(@RequestBody PdaGeneralParam generalParam) { |
| | | |
| | | return pdaOtherService.locOperate(generalParam,getLoginUser()); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | //盘点库存修改 |
| | | public static String CHECK_LOCITEM_UPDATE = "/rsf-open-api/erp/check/locitem/update"; |
| | | |
| | | //待下发任务发送至中转站 |
| | | public static String MISSION_TRANSFER_STATION = "/rsf-open-api/mission/task/master/control"; |
| | | } |
| | |
| | | |
| | | private List<LocItem> matnrList; |
| | | |
| | | /** |
| | | * 站点操作类型:1.解禁 2.禁用 3.释放 |
| | | */ |
| | | private Integer staOperateType; |
| | | |
| | | private String locNo; |
| | | |
| | | } |
| | |
| | | |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.vincent.rsf.server.api.entity.params.PdaGeneralParam; |
| | | import com.vincent.rsf.server.system.entity.User; |
| | | |
| | | public interface PdaOtherService { |
| | | R transferPage(String orderNo, Integer curr, Integer limit); |
| | |
| | | R inspectConfirm(PdaGeneralParam generalParam); |
| | | |
| | | R inspectConfirm2(PdaGeneralParam generalParam); |
| | | |
| | | R stationPage(String staNo, Integer curr, Integer limit); |
| | | |
| | | R staOperate(PdaGeneralParam generalParam, User user); |
| | | |
| | | R locPage(String locNo, Integer curr, Integer limit); |
| | | |
| | | R locOperate(PdaGeneralParam generalParam, User loginUser); |
| | | } |
| | |
| | | throw new CoolException("站点状态不为空闲"); |
| | | } |
| | | if (basStation.getType().equals(StationTypeEnum.STATION_TYPE_MUTI.type)) { |
| | | throw new CoolException("站点为光电站点,禁止呼叫AGV"); |
| | | throw new CoolException("站点为智能站点,禁止呼叫AGV"); |
| | | } |
| | | |
| | | List<String> areaList = JSONObject.parseArray(basStation.getCrossZoneArea(), String.class); |
| | |
| | | for (BasContainer container : containers) { |
| | | String codeType = container.getCodeType(); // 获取正则表达式 |
| | | if (barcode.matches(codeType)) { // 判断条码是否符合这个正则 |
| | | List<Integer> areaList2 = container.getAreas(); |
| | | List<Integer> areaList2 = container.getAreasIds(); |
| | | if (!areaList2.contains(Integer.valueOf(area))) { |
| | | matches2 = false; |
| | | continue; |
| | |
| | | for (BasContainer container : containers) { |
| | | String codeType = container.getCodeType(); // 获取正则表达式 |
| | | if (barcode.matches(codeType)) { // 判断条码是否符合这个正则 |
| | | List<Integer> areaList2 = container.getAreas(); |
| | | List<Integer> areaList2 = container.getAreasIds(); |
| | | if (!areaList2.contains(Integer.parseInt(area))) { |
| | | matches2 = false; |
| | | continue; |
| | |
| | | } |
| | | Task task = new Task(); |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskStatus(TaskStsType.GENERATE_IN.id) |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id) |
| | | .setTaskType(TaskType.TASK_TYPE_EMPTY_IN.type) |
| | | .setWarehType(WarehType.WAREHOUSE_TYPE_AGV.val)//lsh待修改 |
| | | .setTargLoc(targetLoc) |
| | |
| | | .map(WarehouseRoleMenu::getMenuId) |
| | | .collect(Collectors.toSet()); |
| | | // 获取 areaList 并转换为 Long 类型的 Set |
| | | List<Integer> areaList = container.getAreas(); |
| | | List<Integer> areaList = container.getAreasIds(); |
| | | Set<Long> areaSet = new HashSet<>(); |
| | | if (areaList != null) { |
| | | areaList.forEach(area -> areaSet.add(area.longValue())); |
| | |
| | | .map(WarehouseRoleMenu::getMenuId) |
| | | .collect(Collectors.toSet()); |
| | | // 获取 areaList 并转换为 Long 类型的 Set |
| | | List<Integer> areaList = container.getAreas(); |
| | | List<Integer> areaList = container.getAreasIds(); |
| | | Set<Long> areaSet = new HashSet<>(); |
| | | if (areaList != null) { |
| | | areaList.forEach(area -> areaSet.add(area.longValue())); |
| | |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.api.entity.params.PdaGeneralParam; |
| | | import com.vincent.rsf.server.api.service.PdaOtherService; |
| | | import com.vincent.rsf.server.manager.entity.BasStation; |
| | | import com.vincent.rsf.server.manager.entity.Loc; |
| | | import com.vincent.rsf.server.manager.entity.LocItem; |
| | | import com.vincent.rsf.server.manager.entity.Transfer; |
| | | import com.vincent.rsf.server.manager.enums.LocStatusType; |
| | | import com.vincent.rsf.server.manager.enums.LocStsType; |
| | | import com.vincent.rsf.server.manager.service.BasStationService; |
| | | import com.vincent.rsf.server.manager.service.LocItemService; |
| | | import com.vincent.rsf.server.manager.service.LocService; |
| | | import com.vincent.rsf.server.manager.service.TransferService; |
| | | import com.vincent.rsf.server.system.entity.User; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | |
| | | private LocService locService; |
| | | @Autowired |
| | | private LocItemService locItemService; |
| | | |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | | |
| | | @Override |
| | | public R transferPage(String orderNo, Integer curr, Integer limit) { |
| | |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | @Override |
| | | public R stationPage(String staNo, Integer curr, Integer limit) { |
| | | Page<BasStation> page = new Page<>(curr, limit); |
| | | LambdaQueryWrapper<BasStation> basStationLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
| | | basStationLambdaQueryWrapper.eq(!Cools.isEmpty(staNo), BasStation::getStationName, staNo); |
| | | Page<BasStation> page1 = basStationService.page(page, basStationLambdaQueryWrapper); |
| | | return R.ok(page1); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R staOperate(PdaGeneralParam generalParam, User user) { |
| | | switch (generalParam.getStaOperateType()){ |
| | | case 1: |
| | | stationDisabled(generalParam.getTransferStationNo(),user); |
| | | break; |
| | | case 2: |
| | | stationLiftTheBan(generalParam.getTransferStationNo(),user); |
| | | break; |
| | | case 3: |
| | | stationRelease(generalParam.getTransferStationNo(),user); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | return R.ok(); |
| | | } |
| | | |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void stationDisabled(String transferStationNo,User user){ |
| | | BasStation basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, transferStationNo)); |
| | | if (Cools.isEmpty(basStation)) { |
| | | throw new CoolException("未找到正确的站点"); |
| | | } |
| | | if (basStation.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type) || basStation.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | throw new CoolException("当前站点正在工作"); |
| | | } |
| | | basStation.setUseStatus(LocStsType.LOC_STS_TYPE_X.type); |
| | | basStation.setUpdateBy(user.getId()); |
| | | basStation.setUpdateTime(new Date()); |
| | | basStationService.updateById(basStation); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void stationLiftTheBan(String transferStationNo,User user){ |
| | | BasStation basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, transferStationNo)); |
| | | if (Cools.isEmpty(basStation)) { |
| | | throw new CoolException("未找到正确的站点"); |
| | | } |
| | | if (basStation.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type) || basStation.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | throw new CoolException("当前站点正在工作"); |
| | | } |
| | | |
| | | basStation.setUseStatus(Cools.isEmpty(basStation.getBarcode())?LocStsType.LOC_STS_TYPE_O.type:LocStsType.LOC_STS_TYPE_F.type); |
| | | basStation.setUpdateBy(user.getId()); |
| | | basStation.setUpdateTime(new Date()); |
| | | basStationService.updateById(basStation); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void stationRelease(String transferStationNo,User user){ |
| | | BasStation basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, transferStationNo)); |
| | | if (Cools.isEmpty(basStation)) { |
| | | throw new CoolException("未找到正确的站点"); |
| | | } |
| | | if (basStation.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type) || basStation.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | throw new CoolException("当前站点正在工作"); |
| | | } |
| | | |
| | | basStation.setUseStatus(LocStsType.LOC_STS_TYPE_O.type); |
| | | basStation.setBarcode(null); |
| | | basStation.setUpdateBy(user.getId()); |
| | | basStation.setUpdateTime(new Date()); |
| | | basStationService.updateById(basStation); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public R locPage(String locNo, Integer curr, Integer limit) { |
| | | Page<Loc> page = new Page<>(curr, limit); |
| | | LambdaQueryWrapper<Loc> basStationLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
| | | basStationLambdaQueryWrapper.eq(!Cools.isEmpty(locNo), Loc::getCode, locNo); |
| | | Page<Loc> page1 = locService.page(page, basStationLambdaQueryWrapper); |
| | | return R.ok(page1); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R locOperate(PdaGeneralParam generalParam, User user) { |
| | | switch (generalParam.getStaOperateType()){ |
| | | case 1: |
| | | locDisabled(generalParam.getLocNo(),user); |
| | | break; |
| | | case 2: |
| | | locLiftTheBan(generalParam.getLocNo(),user); |
| | | break; |
| | | case 3: |
| | | locRelease(generalParam.getLocNo(),user); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | return R.ok(); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void locDisabled(String transferStationNo,User user){ |
| | | Loc lco = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, transferStationNo)); |
| | | if (Cools.isEmpty(lco)) { |
| | | throw new CoolException("未找到正确的库位"); |
| | | } |
| | | if (lco.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type) || lco.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | throw new CoolException("当前站点正在工作"); |
| | | } |
| | | lco.setUseStatus(LocStsType.LOC_STS_TYPE_X.type); |
| | | lco.setUpdateBy(user.getId()); |
| | | lco.setUpdateTime(new Date()); |
| | | locService.updateById(lco); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void locLiftTheBan(String transferStationNo,User user){ |
| | | Loc lco = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, transferStationNo)); |
| | | if (Cools.isEmpty(lco)) { |
| | | throw new CoolException("未找到正确的库位"); |
| | | } |
| | | if (lco.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type) || lco.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | throw new CoolException("当前库位正在工作"); |
| | | } |
| | | |
| | | lco.setUseStatus(Cools.isEmpty(lco.getBarcode())?LocStsType.LOC_STS_TYPE_O.type:LocStsType.LOC_STS_TYPE_F.type); |
| | | lco.setUpdateBy(user.getId()); |
| | | lco.setUpdateTime(new Date()); |
| | | locService.updateById(lco); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void locRelease(String transferStationNo,User user){ |
| | | Loc lco = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, transferStationNo)); |
| | | if (Cools.isEmpty(lco)) { |
| | | throw new CoolException("未找到正确的库位"); |
| | | } |
| | | if (lco.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type) || lco.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | throw new CoolException("当前库位正在工作"); |
| | | } |
| | | |
| | | lco.setUseStatus(LocStsType.LOC_STS_TYPE_O.type); |
| | | lco.setBarcode(null); |
| | | lco.setUpdateBy(user.getId()); |
| | | lco.setUpdateTime(new Date()); |
| | | locService.updateById(lco); |
| | | } |
| | | |
| | | } |
| | |
| | | if (Cools.isEmpty(loc)){ |
| | | throw new CoolException("未查询到符合条件的托盘"); |
| | | } |
| | | //生成盘点任务参数 |
| | | //生成任务参数 |
| | | LocToTaskParams locToTaskParams = new LocToTaskParams(); |
| | | locToTaskParams.setType(Constants.TASK_TYPE_OUT_STOCK_EMPTY) |
| | | .setSiteNo(basStation.getStationName()) |
| | |
| | | return R.error("查询条件不能为空"); |
| | | } |
| | | |
| | | // 将ERP参数映射为Java实体字段名(驼峰格式),PageParam会自动转换为数据库字段名(下划线格式) |
| | | // 将ERP参数映射为数据库字段名(下划线格式) |
| | | Map<String, Object> queryMap = new HashMap<>(); |
| | | |
| | | // 从实体类中提取查询条件,映射为数据库字段名 |
| | | |
| | | // 从实体类中提取查询条件,映射为真实的数据库字段名 |
| | | if (StringUtils.isNotBlank(condition.getLocId())) { |
| | | queryMap.put("locCode", condition.getLocId()); |
| | | queryMap.put("loc_code", condition.getLocId()); |
| | | } |
| | | if (StringUtils.isNotBlank(condition.getMatNr())) { |
| | | queryMap.put("matnrCode", condition.getMatNr()); |
| | | queryMap.put("matnr_code", condition.getMatNr()); |
| | | } |
| | | if (StringUtils.isNotBlank(condition.getPlanNo())) { |
| | | queryMap.put("trackCode", condition.getPlanNo()); |
| | | queryMap.put("track_code", condition.getPlanNo()); |
| | | } |
| | | if (StringUtils.isNotBlank(condition.getBatch())) { |
| | | queryMap.put("batch", condition.getBatch()); |
| | | } |
| | | // 注意:orderNo 和 wareHouseId 不在 LocItem 表中,需要通过其他方式查询 |
| | | // orderNo 已在后面单独处理(plat_order_code 和 plat_work_code) |
| | | // wareHouseId 需要通过 Loc 表关联查询,已在后面单独处理 |
| | | |
| | | BaseParam baseParam = new BaseParam(); |
| | | baseParam.syncMap(queryMap); |
| | |
| | | QueryWrapper<LocItem> wrapper = pageParam.buildWrapper(false); |
| | | |
| | | // 订单号/工单号/MES工单号 |
| | | // 订单号可能存储在:1) LocItem.plat_order_code 2) LocItem.plat_work_code 3) WkOrder.code (通过orderId关联) |
| | | if (StringUtils.isNotBlank(condition.getOrderNo())) { |
| | | String orderNo = condition.getOrderNo(); |
| | | wrapper.and(w -> w.eq("plat_order_code", orderNo) |
| | | .or().eq("plat_work_code", orderNo)); |
| | | |
| | | // 先查询WkOrder表,获取匹配的订单ID列表 |
| | | LambdaQueryWrapper<WkOrder> orderWrapper = new LambdaQueryWrapper<>(); |
| | | orderWrapper.eq(WkOrder::getCode, orderNo); |
| | | List<WkOrder> matchingOrders = asnOrderService.list(orderWrapper); |
| | | List<Long> matchingOrderIds = matchingOrders.stream() |
| | | .map(WkOrder::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 构建订单号查询条件:LocItem表的plat_order_code、plat_work_code,或通过orderId关联WkOrder.code |
| | | wrapper.and(w -> { |
| | | w.eq("plat_order_code", orderNo) |
| | | .or().eq("plat_work_code", orderNo); |
| | | // 如果找到了匹配的订单,也查询orderId |
| | | if (!matchingOrderIds.isEmpty()) { |
| | | w.or().in("order_id", matchingOrderIds); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 物料组(需要通过物料表关联查询) |
| | |
| | | import com.vincent.rsf.server.manager.service.impl.LocServiceImpl; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.manager.enums.LocStsType; |
| | | import com.vincent.rsf.server.system.entity.User; |
| | | import com.vincent.rsf.server.system.service.impl.UserServiceImpl; |
| | | import com.vincent.rsf.server.system.utils.SerialRuleUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | |
| | | private RestTemplate restTemplate; |
| | | @Autowired |
| | | private RemotesInfoProperties.RcsApi rcsApi; |
| | | @Autowired |
| | | private UserServiceImpl userService; |
| | | |
| | | |
| | | @Override |
| | |
| | | String targetSite, String sourceSiteNo, Long loginUserId) { |
| | | Task task = new Task(); |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskStatus(TaskStsType.GENERATE_IN.id) |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id) |
| | | .setTaskType(TaskType.TASK_TYPE_IN.type) |
| | | .setWarehType(WarehType.WAREHOUSE_TYPE_CRN.val) |
| | | .setTargLoc(targetLoc) |
| | |
| | | } |
| | | Task task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, params.getSeqNum())); |
| | | if (Objects.isNull(task)) { |
| | | throw new CoolException("任务不存在可已结束!!"); |
| | | throw new CoolException("任务不存在可以结束!!"); |
| | | } |
| | | |
| | | /**料箱搬运中, 修改站点状态*/ |
| | |
| | | throw new CoolException(params.getSourceStaNo()+"站点不存在!!"); |
| | | } |
| | | if (!basStation.getType().equals(StationTypeEnum.STATION_TYPE_MUTI.type)) { |
| | | throw new CoolException(params.getSourceStaNo()+"站点非光电站点!!请使用PDA绑定入库"); |
| | | throw new CoolException(params.getSourceStaNo()+"站点非智能站点!!请使用PDA绑定入库"); |
| | | } |
| | | Task one = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, params.getBarcode())); |
| | | if (!Cools.isEmpty(one)) { |
| | |
| | | GenerateTaskParams taskParams = new GenerateTaskParams(); |
| | | taskParams.setWaitPakins(waitPakins) |
| | | .setSiteId(basStation.getId()); |
| | | R r = taskService.generateTasksWcs(taskParams, 111L,params.getRowList());//lsh待修改 WCS用户信息 |
| | | |
| | | User wcs = userService.getByUsername("wcs", 1L); |
| | | Long wcsId = 1111L; |
| | | if (!Cools.isEmpty(wcs)) { |
| | | wcsId = wcs.getId(); |
| | | } |
| | | R r = taskService.generateTasksWcs(taskParams, wcsId,params.getRowList()); |
| | | if (r.get("msg").equals("任务生成完毕!")) { |
| | | one = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, params.getBarcode())); |
| | | InTaskWcsReportParam inTaskWcsReportParam = new InTaskWcsReportParam(); |
| | |
| | | } |
| | | if (params.getMsgType().equals(WcsMsgTypeEvent.TASK_COMPLETE.event)){ |
| | | if (!Cools.isEmpty(one)) { |
| | | one.setTaskStatus(TaskStsType.GENERATE_IN.id); |
| | | one.setTaskStatus(TaskStsType.MISSION_INITIAL.id); |
| | | one.setOrgSite(one.getTargSite()); |
| | | if (!taskService.updateById(one)) { |
| | | // throw new CoolException("完成任务失败"); |
| | |
| | | continue; |
| | | } |
| | | String shallowLoc = LocUtils.getDeepLoc(locMast1.getCode()); |
| | | if ((ioType == TaskStsType.GENERATE_IN.id && deviceBind.getBeSimilar().equals("1"))) { |
| | | if ((ioType == TaskStsType.MISSION_INITIAL.id && deviceBind.getBeSimilar().equals("1"))) { |
| | | //相似物料打开,判断深库位有没有货,没货就放深库位,有货就不操作 |
| | | Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getCode, shallowLoc) |
| | |
| | | @GetMapping("/basContainer/{id}") |
| | | public R get(@PathVariable("id") Long id) { |
| | | BasContainer basContainer = basContainerService.getById(id); |
| | | // 确保返回的areas按sort字段排序 |
| | | if (basContainer != null) { |
| | | basContainer.sortAreas(); |
| | | } |
| | | return R.ok().add(basContainer); |
| | | } |
| | | |
| | |
| | | basContainer.setCreateTime(new Date()); |
| | | basContainer.setUpdateBy(getLoginUserId()); |
| | | basContainer.setUpdateTime(new Date()); |
| | | |
| | | // 确保areas按sort字段排序 |
| | | basContainer.sortAreas(); |
| | | |
| | | BasContainer container = basContainerService.getOne(new LambdaQueryWrapper<BasContainer>().eq(BasContainer::getContainerType, basContainer.getContainerType())); |
| | | if (null != container) { |
| | | return R.error("该类型已被初始化"); |
| | |
| | | public R update(@RequestBody BasContainer basContainer) { |
| | | basContainer.setUpdateBy(getLoginUserId()); |
| | | basContainer.setUpdateTime(new Date()); |
| | | |
| | | // 确保areas按sort字段排序 |
| | | basContainer.sortAreas(); |
| | | |
| | | if (!basContainerService.updateById(basContainer)) { |
| | | return R.error("Update Fail"); |
| | | } |
| | |
| | | import java.util.Date; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import com.vincent.rsf.server.manager.utils.AreasDeserializer; |
| | | import com.vincent.rsf.server.manager.utils.AreasSerializer; |
| | | import com.vincent.rsf.server.manager.utils.AreasTypeHandler; |
| | | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
| | | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| | | import com.vincent.rsf.server.system.entity.DictData; |
| | | import com.vincent.rsf.server.system.service.DictDataService; |
| | | import lombok.experimental.Accessors; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | |
| | | import com.vincent.rsf.server.system.service.UserService; |
| | | import com.vincent.rsf.server.system.entity.User; |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.ArrayList; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Data |
| | | @Accessors(chain = true) |
| | |
| | | private String codeType; |
| | | |
| | | /** |
| | | * 可入库区 |
| | | * 可入库区(包含排序信息) |
| | | * 格式: [{"id": 1, "sort": 1}, {"id": 2, "sort": 2}] |
| | | */ |
| | | @ApiModelProperty(value = "可入库区") |
| | | @TableField(typeHandler = JacksonTypeHandler.class) |
| | | private List<Integer> areas; |
| | | @ApiModelProperty(value = "可入库区(包含排序信息)") |
| | | @TableField(typeHandler = AreasTypeHandler.class) |
| | | @JsonDeserialize(using = AreasDeserializer.class) |
| | | @JsonSerialize(using = AreasSerializer.class) |
| | | private List<Map<String, Object>> areas; |
| | | |
| | | /** |
| | | * 是否删除 1: 是 0: 否 |
| | |
| | | public BasContainer() { |
| | | } |
| | | |
| | | public BasContainer(Long containerType, String codeType, List<Integer> areas, Integer deleted, Integer status, |
| | | public BasContainer(Long containerType, String codeType, List<Map<String, Object>> areas, Integer deleted, Integer status, |
| | | Integer tenantId, Long createBy, Date createTime, Long updateBy, Date updateTime, String memo) { |
| | | this.containerType = containerType; |
| | | this.codeType = codeType; |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取排序后的库区ID列表(向后兼容方法) |
| | | * @return 排序后的库区ID列表 |
| | | */ |
| | | public List<Integer> getAreasIds() { |
| | | if (Cools.isEmpty(this.areas)) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return this.areas.stream() |
| | | .sorted((a, b) -> { |
| | | Integer sortA = a.get("sort") != null ? ((Number) a.get("sort")).intValue() : Integer.MAX_VALUE; |
| | | Integer sortB = b.get("sort") != null ? ((Number) b.get("sort")).intValue() : Integer.MAX_VALUE; |
| | | return sortA.compareTo(sortB); |
| | | }) |
| | | .map(area -> { |
| | | Object id = area.get("id"); |
| | | if (id instanceof Number) { |
| | | return ((Number) id).intValue(); |
| | | } |
| | | return null; |
| | | }) |
| | | .filter(id -> id != null) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * 对areas按sort字段进行排序 |
| | | */ |
| | | public void sortAreas() { |
| | | if (this.areas != null && !this.areas.isEmpty()) { |
| | | this.areas.sort((a, b) -> { |
| | | Integer sortA = a.get("sort") != null ? ((Number) a.get("sort")).intValue() : Integer.MAX_VALUE; |
| | | Integer sortB = b.get("sort") != null ? ((Number) b.get("sort")).intValue() : Integer.MAX_VALUE; |
| | | return sortA.compareTo(sortB); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
| | | import com.vincent.rsf.server.manager.enums.StationTypeEnum; |
| | | import com.vincent.rsf.server.manager.service.WarehouseAreasService; |
| | | import com.vincent.rsf.server.system.entity.DictData; |
| | |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Data |
| | | @TableName("man_bas_station") |
| | | @TableName(value = "man_bas_station", autoResultMap = true) |
| | | public class BasStation implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | |
| | | @TableField(exist = false) |
| | | private List<Long> containerTypes; |
| | | |
| | | /** |
| | | * 别名 |
| | | */ |
| | | @ApiModelProperty(value = "别名") |
| | | @TableField(typeHandler = JacksonTypeHandler.class) |
| | | private List<String> stationAlias; |
| | | |
| | | public BasStation() { |
| | | } |
| | | |
| | |
| | | private Integer status; |
| | | |
| | | /** |
| | | * 排序字段 |
| | | */ |
| | | @ApiModelProperty(value= "排序字段") |
| | | private Integer sort; |
| | | |
| | | /** |
| | | * 是否删除 1: 是 0: 否 |
| | | */ |
| | | @ApiModelProperty(value= "是否删除 1: 是 0: 否 ") |
| New file |
| | |
| | | package com.vincent.rsf.server.manager.enums; |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @version 1.0 |
| | | * @title TaskType |
| | | * @description |
| | | * @create 2025/3/29 17:02 |
| | | */ |
| | | public enum MissionStepType { |
| | | MISSION_STEP_TYPE_RESPONSE("RESPONSE", "响应"), |
| | | MISSION_STEP_TYPE_RECEIVE("RECEIVE", "接收"), |
| | | MISSION_STEP_TYPE_REQUEST("REQUEST", "请求"), |
| | | MISSION_STEP_TYPE_BIND("BIND", "绑定"), |
| | | MISSION_STEP_TYPE_UNBIND("UNBIND", "解绑"), |
| | | MISSION_STEP_TYPE_VALIDATE("VALIDATE", "校验"), |
| | | MISSION_STEP_TYPE_TRANSFORM("TRANSFORM", "转换"), |
| | | MISSION_STEP_TYPE_OBTAIN("OBTAIN", "获取"), |
| | | MISSION_STEP_TYPE_NO_EXECUTE("NO_EXECUTE", "越过"), |
| | | ; |
| | | public String type; |
| | | public String desc; |
| | | |
| | | MissionStepType(String type, String desc) { |
| | | this.type = type; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | public enum StationTypeEnum { |
| | | //站点类型 |
| | | STATION_TYPE_MUTI(0, "光电站点"), |
| | | STATION_TYPE_MUTI(0, "智能站点"), |
| | | STATION_TYPE_NORMAL(1, "普通站点"), |
| | | ; |
| | | /**站点类型*/ |
| | |
| | | // WCS_PUTAWAY_SUSPEND(13L, "入库任务挂起"),
|
| | |
|
| | | COMPLETE_IN("98", "入库完成"),
|
| | |
|
| | | REPORT_IN("99", "上报完成"),
|
| | |
|
| | | UPDATED_IN("100", "库存更新完成"),
|
| | |
|
| | | GENERATE_OUT("101", "创建出库任务"),
|
| | |
|
| | | WCS_EXECUTE_OUT("102", "WCS、RCS出库任务已下发"),
|
| | |
|
| | | WCS_EXECUTE_OUT_TOTE_LOAD("103", "WCS、RCS取箱完成"),
|
| | |
|
| | | WCS_EXECUTE_OUT_TOTE_UNLOAD("104", "WCS、RCS放箱完成"),
|
| | |
|
| | | WCS_EXECUTE_OUT_TASK_DONE("105", "WCS、RCS任务完成"),
|
| | |
|
| | | WCS_EXECUTE_OUT_ARRIVED("106", "WCS、RCS容器已到达"),
|
| | |
|
| | | WCS_EXECUTE_OUT_CONVEYOR("107", "WCS、RCS容器流动任务已下发"),
|
| | |
|
| | | AWAIT("196","等待确认"),
|
| | |
|
| | | GENERATE_WAVE_SEED("197", "等待容器到达"),
|
| | |
|
| | | COMPLETE_OUT("198", "出库完成"),
|
| | |
|
| | | WAVE_SEED("199", "播种中/盘点中/待确认"),
|
| | |
|
| | | UPDATED_OUT("200", "库存更新完成"),
|
| | |
|
| | |
|
| | |
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_ONE1("1001", "1001.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_ONE2("1002", "1002.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_ONE1("1003", "1003.RCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_ONE2("1004", "1004.RCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_TWO1("1005", "1005.RCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_TWO2("1006", "1006.RCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_TWO1("1007", "1007.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_TWO2("1008", "1008.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_THREE1("1009", "1009.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_THREE2("1010", "1010.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_FOUR1("1011", "1011.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_FOUR2("1012", "1012.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_FIVE1("1013", "1013.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_FIVE2("1014", "1014.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_THREE1("1015", "1015.RCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_THREE2("1016", "1016.RCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_SIX1("1017", "1017.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_SIX2("1018", "1018.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_SEVEN1("1019", "1019.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_SEVEN2("1020", "1020.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_FOUR1("1021", "1021.RCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_FOUR2("1022", "1022.RCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_FIVE1("1023", "1023.RCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_FIVE2("1024", "1024.RCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_EIGHT1("1025", "1025.WCS请求下发中"),
|
| | | MISSION_TEMPLATE_EXECUTE_WCS_EIGHT2("1026", "1026.WCS作业中等待上报完成"),
|
| | | MISSION_TEMPLATE_NO_EXECUTE1("1027", "1027.不执行任务"),
|
| | | MISSION_TEMPLATE_WEIGHING_ONE2("1028", "1028.称重作业中等待上报完成"),
|
| | |
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_ONE3("1029", "1029.RCS绑定数据"),
|
| | | MISSION_TEMPLATE_EXECUTE_HK_RCS_ONE4("1030", "1030.RCS解绑数据"),
|
| | |
|
| | | MISSION_TRANSFER("9999", "9999.任务完成中"),
|
| | | ;
|
| | |
|
| | | public Integer id;
|
| | |
| | | TASK_TYPE_PICK_AGAIN_OUT("103", "拣料出库"), |
| | | TASK_TYPE_MERGE_OUT("104", "并板出库"), |
| | | TASK_TYPE_CHECK_OUT("107", "盘点出库"), |
| | | TASK_TYPE_CROSS_DOCKING_OUT("109", "越库"), |
| | | TASK_TYPE_EMPTY_OUT("110", "空板出库"), |
| | | ; |
| | | public Integer type; |
| | |
| | | public void genReCheck() { |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .select(Task::getId) |
| | | .eq(Task::getTaskStatus, TaskStsType.GENERATE_OUT.id) |
| | | .eq(Task::getTaskStatus, TaskStsType.MISSION_INITIAL.id) |
| | | .eq(Task::getTaskType, TaskType.TASK_TYPE_CHECK_OUT.type)); |
| | | if (tasks.isEmpty()) { |
| | | return; |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.manager.controller.params.LocToTaskParams; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.enums.LocStsType; |
| | | import com.vincent.rsf.server.manager.enums.StationTypeEnum; |
| | | import com.vincent.rsf.framework.common.SnowflakeIdWorker; |
| | | import com.vincent.rsf.server.manager.entity.Loc; |
| | | import com.vincent.rsf.server.manager.entity.Task; |
| | | import com.vincent.rsf.server.manager.enums.TaskStsType; |
| | | import com.vincent.rsf.server.manager.enums.TaskType; |
| | | import com.vincent.rsf.server.manager.service.LocItemService; |
| | | import com.vincent.rsf.server.manager.service.LocService; |
| | | import com.vincent.rsf.server.manager.service.TaskService; |
| | | import com.vincent.rsf.server.manager.service.impl.BasContainerServiceImpl; |
| | | import com.vincent.rsf.server.manager.service.impl.WarehouseAreasServiceImpl; |
| | | import com.vincent.rsf.server.system.constant.GlobalConfigCode; |
| | | import com.vincent.rsf.server.system.entity.*; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private WarehouseAreasServiceImpl warehouseAreasService; |
| | | @Autowired |
| | | private LocService locService; |
| | | @Autowired |
| | | private LocItemService locItemService; |
| | | private TaskPathTemplateMergeServiceImpl taskPathTemplateMergeService; |
| | | @Autowired |
| | | private BasContainerServiceImpl basContainerService; |
| | | private TaskInstanceServiceImpl taskInstanceService; |
| | | @Autowired |
| | | private TaskPathTemplateServiceImpl taskPathTemplateService; |
| | | @Autowired |
| | | private TaskPathTemplateNodeServiceImpl taskPathTemplateNodeService; |
| | | @Autowired |
| | | private TaskInstanceServiceImpl taskInstanceService; |
| | | @Autowired |
| | | private TaskInstanceNodeServiceImpl taskInstanceNodeService; |
| | | @Autowired |
| | | private SubsystemFlowTemplateServiceImpl subsystemFlowTemplateService; |
| | | @Autowired |
| | | private FlowStepLogServiceImpl flowStepLogService; |
| | | @Autowired |
| | | private FlowStepTemplateServiceImpl flowStepTemplateService; |
| | | @Autowired |
| | | private FlowStepInstanceServiceImpl flowStepInstanceService; |
| | | private TaskInstanceNodeServiceImpl taskInstanceNodeService; |
| | | @Autowired |
| | | private SnowflakeIdWorker snowflakeIdWorker; |
| | | @Autowired |
| | | private FlowInstanceServiceImpl flowInstanceService; |
| | | @Autowired |
| | | private FlowStepInstanceServiceImpl flowStepInstanceService; |
| | | |
| | | /** |
| | | * @author Munch D. Luffy |
| | |
| | | * @description: 初始任务规划路径 |
| | | * @version 1.0 |
| | | */ |
| | | @Scheduled(cron = "0/2 * * * * ?") |
| | | @Scheduled(cron = "0/1 * * * * ?") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void missionTemplate() throws Exception { |
| | | Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_TEMPLATE_PLANNING_STEP_FLOW)); |
| | | if (!Boolean.parseBoolean(config.getVal())) { |
| | | return; |
| | | } |
| | | |
| | | List<Task> taskList = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getTaskStatus, TaskStsType.MISSION_INITIAL.id)); |
| | | if (Objects.isNull(taskList)) { |
| | | return; |
| | |
| | | sou = loc.getAreaId().toString(); |
| | | end = task.getTargSite(); |
| | | } |
| | | List<TaskPathTemplate> taskPathTemplates = taskPathTemplateService.list(new LambdaQueryWrapper<TaskPathTemplate>().eq(TaskPathTemplate::getSourceType, sou).eq(TaskPathTemplate::getTargetType, end)); |
| | | if (Objects.isNull(taskPathTemplates) || taskPathTemplates.isEmpty()) { |
| | | List<TaskPathTemplate> list = taskPathTemplateService.list(new LambdaQueryWrapper<>()); |
| | | List<TaskPathTemplateMerge> taskPathTemplateMergeList = taskPathTemplateMergeService.list(new LambdaQueryWrapper<TaskPathTemplateMerge>().eq(TaskPathTemplateMerge::getSourceType, sou).eq(TaskPathTemplateMerge::getTargetType, end)); |
| | | if (Objects.isNull(taskPathTemplateMergeList) || taskPathTemplateMergeList.isEmpty()) { |
| | | List<TaskPathTemplateMerge> list = taskPathTemplateMergeService.list(new LambdaQueryWrapper<TaskPathTemplateMerge>().eq(TaskPathTemplateMerge::getStepSize,1)); |
| | | if (!Cools.isEmpty(list)) { |
| | | List<String[]> stationList = new ArrayList<>(); |
| | | list.forEach(taskPathTemplate -> { |
| | |
| | | }); |
| | | List<Long> longs = RouteWmsStepFlow.routeGet(stationList, sou, end); |
| | | if (longs != null && !longs.isEmpty()) { |
| | | TaskPathTemplate taskPathTemplate = new TaskPathTemplate(); |
| | | taskPathTemplate.setTemplateCode(sou+"===>"+end); |
| | | taskPathTemplate.setTemplateName(sou+"===>"+end); |
| | | taskPathTemplate.setSourceType(sou); |
| | | taskPathTemplate.setTargetType(end); |
| | | taskPathTemplateService.save(taskPathTemplate); |
| | | TaskPathTemplateMerge taskPathTemplateMerge = new TaskPathTemplateMerge(); |
| | | taskPathTemplateMerge.setTemplateCode(sou+"===>"+end); |
| | | taskPathTemplateMerge.setTemplateName(sou+"===>"+end); |
| | | taskPathTemplateMerge.setSourceType(sou); |
| | | taskPathTemplateMerge.setTargetType(end); |
| | | List<Integer> longList = new ArrayList<>(); |
| | | for (Long id : longs) { |
| | | TaskPathTemplateMerge one = taskPathTemplateMergeService.getOne(new LambdaQueryWrapper<TaskPathTemplateMerge>().eq(TaskPathTemplateMerge::getId, id)); |
| | | longList.addAll(one.getConditionExpression()); |
| | | } |
| | | taskPathTemplateMerge.setConditionExpression(longList); |
| | | taskPathTemplateMerge.setStepSize(longList.size()); |
| | | taskPathTemplateMergeService.save(taskPathTemplateMerge); |
| | | } |
| | | System.out.println("任务:"+task.getTaskCode()+"查询步序为:"+longs); |
| | | // System.out.println("任务:"+task.getTaskCode()+"查询步序为:"+longs); |
| | | } |
| | | } else { |
| | | //生成实际路径 |
| | | boolean actualPath = generateActualPath(task, taskPathTemplateMergeList.get(0)); |
| | | if (!actualPath) { |
| | | log.error("生成实际路径失败"); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | | public boolean generateActualPath(Task task,TaskPathTemplateMerge taskPathTemplateMerge) { |
| | | boolean actualPath = false; |
| | | List<Integer> conditionExpression = taskPathTemplateMerge.getConditionExpression(); |
| | | Integer i = 0;//it顺序 |
| | | Integer j = 0;//suT顺序 |
| | | |
| | | // 用于存储所有FlowStepTemplate |
| | | List<FlowStepTemplate> allFlowStepTemplates = new ArrayList<>(); |
| | | |
| | | // 第一遍遍历:收集所有的FlowStepTemplate |
| | | for (Integer id : conditionExpression) { |
| | | TaskPathTemplate taskPathTemplate = taskPathTemplateService.getById(id); |
| | | if (taskPathTemplate == null) { |
| | | return false; |
| | | } |
| | | List<TaskPathTemplateNode> taskPathTemplateNodeList = taskPathTemplateNodeService.list( |
| | | new LambdaQueryWrapper<TaskPathTemplateNode>() |
| | | .eq(TaskPathTemplateNode::getTemplateId, taskPathTemplate.getId())); |
| | | |
| | | for (TaskPathTemplateNode taskPathTemplateNode : taskPathTemplateNodeList) { |
| | | List<SubsystemFlowTemplate> subsystemFlowTemplateList = subsystemFlowTemplateService.list( |
| | | new LambdaQueryWrapper<SubsystemFlowTemplate>() |
| | | .eq(SubsystemFlowTemplate::getFlowCode, taskPathTemplateNode.getNodeCode()) |
| | | .eq(SubsystemFlowTemplate::getSystemCode, taskPathTemplateNode.getSystemCode())); |
| | | |
| | | for (SubsystemFlowTemplate subsystemFlowTemplate : subsystemFlowTemplateList) { |
| | | List<FlowStepTemplate> flowStepTemplateList = flowStepTemplateService.list( |
| | | new LambdaQueryWrapper<FlowStepTemplate>() |
| | | .eq(FlowStepTemplate::getFlowId, subsystemFlowTemplate.getId())); |
| | | |
| | | allFlowStepTemplates.addAll(flowStepTemplateList); |
| | | } |
| | | } |
| | | } |
| | | |
| | | TaskInstance taskInstance = new TaskInstance(taskPathTemplateMerge,task); |
| | | boolean save = taskInstanceService.save(taskInstance); |
| | | if (!save) { |
| | | return false; |
| | | } |
| | | |
| | | // 当前处理的FlowStepTemplate在全局列表中的索引 |
| | | int globalIndex = 0; |
| | | |
| | | for (Integer id : conditionExpression) { |
| | | TaskPathTemplate taskPathTemplate = taskPathTemplateService.getById(id); |
| | | if (taskPathTemplate == null) { |
| | | return false; |
| | | } |
| | | List<TaskPathTemplateNode> taskPathTemplateNodeList = taskPathTemplateNodeService.list( |
| | | new LambdaQueryWrapper<TaskPathTemplateNode>() |
| | | .eq(TaskPathTemplateNode::getTemplateId, taskPathTemplate.getId())); |
| | | |
| | | for (TaskPathTemplateNode taskPathTemplateNode : taskPathTemplateNodeList) { |
| | | TaskInstanceNode taskInstanceNode = new TaskInstanceNode(taskPathTemplateNode); |
| | | taskInstanceNode.setTaskId(taskInstance.getId()); |
| | | taskInstanceNode.setTaskNo(taskInstance.getTaskNo()); |
| | | i++; |
| | | taskInstanceNode.setNodeOrder(i); |
| | | taskInstanceNode.setNodeCode(String.valueOf(snowflakeIdWorker.nextId())); |
| | | taskInstanceNodeService.save(taskInstanceNode); |
| | | |
| | | List<SubsystemFlowTemplate> subsystemFlowTemplateList = subsystemFlowTemplateService.list( |
| | | new LambdaQueryWrapper<SubsystemFlowTemplate>() |
| | | .eq(SubsystemFlowTemplate::getFlowCode, taskPathTemplateNode.getNodeCode()) |
| | | .eq(SubsystemFlowTemplate::getSystemCode, taskPathTemplateNode.getSystemCode())); |
| | | |
| | | for (SubsystemFlowTemplate subsystemFlowTemplate : subsystemFlowTemplateList) { |
| | | FlowInstance flowInstance = new FlowInstance(subsystemFlowTemplate); |
| | | flowInstance.setFlowInstanceNo(String.valueOf(snowflakeIdWorker.nextId())); |
| | | flowInstance.setTaskId(taskInstance.getId()); |
| | | flowInstance.setTaskNo(taskInstance.getTaskNo()); |
| | | flowInstance.setNodeInstanceId(taskInstanceNode.getId()); |
| | | flowInstance.setNodeCode(String.valueOf(snowflakeIdWorker.nextId())); |
| | | flowInstanceService.save(flowInstance); |
| | | |
| | | List<FlowStepTemplate> flowStepTemplateList = flowStepTemplateService.list( |
| | | new LambdaQueryWrapper<FlowStepTemplate>() |
| | | .eq(FlowStepTemplate::getFlowId, subsystemFlowTemplate.getId())); |
| | | |
| | | for (FlowStepTemplate flowStepTemplate : flowStepTemplateList) { |
| | | j++; |
| | | FlowStepInstance flowStepInstance = new FlowStepInstance(flowStepTemplate); |
| | | flowStepInstance.setFlowInstanceId(flowInstance.getId()); |
| | | flowStepInstance.setFlowInstanceNo(flowInstance.getFlowInstanceNo()); |
| | | flowStepInstance.setStepOrder(j); |
| | | flowStepInstance.setStepCode(String.valueOf(snowflakeIdWorker.nextId())); |
| | | flowStepInstance.setWmsNowTaskStatus(flowStepTemplate.getWmsNowTaskStatus()); |
| | | |
| | | // 判断是否是最后一个 |
| | | if (globalIndex < allFlowStepTemplates.size() - 1) { |
| | | // 不是最后一个,取下一个的WmsNowTaskStatus |
| | | FlowStepTemplate nextFlowStep = allFlowStepTemplates.get(globalIndex + 1); |
| | | flowStepInstance.setWmsNextTaskStatus(nextFlowStep.getWmsNowTaskStatus()); |
| | | if (globalIndex == 0){ |
| | | task.setTaskStatus(flowStepTemplate.getWmsNowTaskStatus()); |
| | | flowStepInstance.setStatus((short)1); |
| | | } |
| | | } else { |
| | | // 是最后一个,设置为9999 |
| | | flowStepInstance.setWmsNextTaskStatus(9999); |
| | | } |
| | | |
| | | flowStepInstanceService.save(flowStepInstance); |
| | | globalIndex++; // 更新全局索引 |
| | | } |
| | | } |
| | | } |
| | | } |
| | | task.setDeviceSiteId(taskInstance.getId()); |
| | | taskService.updateById(task); |
| | | return actualPath; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | import com.vincent.rsf.server.manager.service.impl.LocServiceImpl; |
| | | import com.vincent.rsf.server.system.constant.GlobalConfigCode; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.system.entity.Config; |
| | | import com.vincent.rsf.server.system.entity.*; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | | import com.vincent.rsf.server.system.service.impl.FlowInstanceServiceImpl; |
| | | import com.vincent.rsf.server.system.service.impl.FlowStepInstanceServiceImpl; |
| | | import com.vincent.rsf.server.system.service.impl.TaskInstanceNodeServiceImpl; |
| | | import com.vincent.rsf.server.system.service.impl.TaskInstanceServiceImpl; |
| | | import com.vincent.rsf.server.system.utils.SerialRuleUtils; |
| | | import com.vincent.rsf.server.system.utils.SystemAuthUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | private RemotesInfoProperties.RcsApi rcsApi; |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | | @Autowired |
| | | private FlowStepInstanceServiceImpl flowStepInstanceService; |
| | | @Autowired |
| | | private FlowInstanceServiceImpl flowInstanceService; |
| | | @Autowired |
| | | private TaskInstanceNodeServiceImpl taskInstanceNodeService; |
| | | @Autowired |
| | | private TaskInstanceServiceImpl taskInstanceService; |
| | | |
| | | |
| | | /** |
| | | * 任务下发:请求 |
| | | */ |
| | | @Scheduled(cron = "0/2 * * * * ? ") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void missionTaskEXECUTE() { |
| | | List<String> typeList = Arrays.asList( |
| | | MissionStepType.MISSION_STEP_TYPE_REQUEST.type, |
| | | MissionStepType.MISSION_STEP_TYPE_UNBIND.type, |
| | | MissionStepType.MISSION_STEP_TYPE_BIND.type, |
| | | MissionStepType.MISSION_STEP_TYPE_OBTAIN.type, |
| | | MissionStepType.MISSION_STEP_TYPE_NO_EXECUTE.type); |
| | | List<FlowStepInstance> flowStepInstanceList = flowStepInstanceService.list(new LambdaQueryWrapper<FlowStepInstance>() |
| | | .eq(FlowStepInstance::getStatus, 1).in(FlowStepInstance::getStepType,typeList)); |
| | | for (FlowStepInstance flowStepInstance : flowStepInstanceList) { |
| | | FlowInstance flowInstance = flowInstanceService.getById(flowStepInstance.getFlowInstanceId()); |
| | | if (Cools.isEmpty(flowInstance)) { continue;} |
| | | TaskInstanceNode taskInstanceNode = taskInstanceNodeService.getById(flowInstance.getNodeInstanceId()); |
| | | if (Cools.isEmpty(taskInstanceNode)) { continue;} |
| | | TaskInstance taskInstance = taskInstanceService.getById(flowInstance.getTaskId()); |
| | | if (Cools.isEmpty(taskInstance)) { continue;} |
| | | Task task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, taskInstance.getTaskNo())); |
| | | if (Cools.isEmpty(task)) { continue;} |
| | | if (!task.getTaskStatus().equals(flowStepInstance.getWmsNowTaskStatus())) { |
| | | log.error("任务号:"+task.getTaskCode()+"的任务状态与执行档案任务状态:"+flowStepInstance.getWmsNowTaskStatus()+"不一致!!!"); |
| | | continue; |
| | | } |
| | | |
| | | if (flowStepInstance.getStepType().equals(MissionStepType.MISSION_STEP_TYPE_NO_EXECUTE.type)) { |
| | | /**基础配置链接*/ |
| | | log.info("任务越过, 请求参数: {}", JSONObject.toJSONString(flowStepInstance)); |
| | | try { |
| | | task.setTaskStatus(flowStepInstance.getWmsNextTaskStatus()); |
| | | flowStepInstance.setStatus((short)3); |
| | | |
| | | flowStepInstanceService.updateById(flowStepInstance); |
| | | taskService.updateById(task); |
| | | |
| | | if (flowStepInstance.getWmsNextTaskStatus() != 9999) { |
| | | FlowStepInstance nextFlowStepInstance = flowStepInstanceService.getOne(new LambdaQueryWrapper<FlowStepInstance>() |
| | | .eq(FlowStepInstance::getFlowInstanceId, flowStepInstance.getFlowInstanceId()) |
| | | .eq(FlowStepInstance::getFlowInstanceNo, flowStepInstance.getFlowInstanceNo()) |
| | | .eq(FlowStepInstance::getStepOrder, flowStepInstance.getStepOrder() + 1) |
| | | .eq(FlowStepInstance::getWmsNowTaskStatus, flowStepInstance.getWmsNextTaskStatus())); |
| | | nextFlowStepInstance.setStatus((short)1); |
| | | |
| | | flowStepInstanceService.updateById(nextFlowStepInstance); |
| | | } |
| | | } catch (Exception e) { |
| | | throw new CoolException(e.getMessage()); |
| | | } |
| | | } else { |
| | | /**任务下发接口*/ |
| | | String pubTakUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.MISSION_TRANSFER_STATION; |
| | | |
| | | /**基础配置链接*/ |
| | | log.info("任务下发,请求地址: {}, 请求参数: {}", pubTakUrl, JSONObject.toJSONString("")); |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | | HttpEntity httpEntity = new HttpEntity(flowStepInstance, headers); |
| | | ResponseEntity<String> exchange = restTemplate.exchange(pubTakUrl, HttpMethod.POST, httpEntity, String.class); |
| | | log.info("任务下发后,响应结果: {}", exchange); |
| | | if (Objects.isNull(exchange.getBody())) { |
| | | throw new CoolException("任务下发失败!!,返回参数为空!!!"); |
| | | } else { |
| | | try { |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | objectMapper.coercionConfigDefaults() |
| | | .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty); |
| | | CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class); |
| | | if (result.getCode() == 200) { |
| | | task.setTaskStatus(flowStepInstance.getWmsNextTaskStatus()); |
| | | flowStepInstance.setStatus((short)3); |
| | | |
| | | if (flowStepInstance.getStepType().equals(MissionStepType.MISSION_STEP_TYPE_OBTAIN.type)) { |
| | | //获取数据解析 |
| | | //录入 |
| | | } |
| | | |
| | | flowStepInstanceService.updateById(flowStepInstance); |
| | | taskService.updateById(task); |
| | | |
| | | if (flowStepInstance.getWmsNextTaskStatus() != 9999) { |
| | | FlowStepInstance nextFlowStepInstance = flowStepInstanceService.getOne(new LambdaQueryWrapper<FlowStepInstance>() |
| | | .eq(FlowStepInstance::getFlowInstanceId, flowStepInstance.getFlowInstanceId()) |
| | | .eq(FlowStepInstance::getFlowInstanceNo, flowStepInstance.getFlowInstanceNo()) |
| | | .eq(FlowStepInstance::getStepOrder, flowStepInstance.getStepOrder() + 1) |
| | | .eq(FlowStepInstance::getWmsNowTaskStatus, flowStepInstance.getWmsNextTaskStatus())); |
| | | nextFlowStepInstance.setStatus((short)1); |
| | | |
| | | flowStepInstanceService.updateById(nextFlowStepInstance); |
| | | } |
| | | } else { |
| | | flowStepInstance.setRetryTimes(flowStepInstance.getRetryTimes() + 1); |
| | | if (flowStepInstance.getRetryTimes()>5){ |
| | | flowStepInstance.setStatus((short)4); |
| | | flowStepInstanceService.updateById(flowStepInstance); |
| | | log.error("任务下发失败,重试次数大于等于五次,标记为失败!!!"); |
| | | } else { |
| | | flowStepInstanceService.updateById(flowStepInstance); |
| | | log.error("任务下发失败,等待重试...."); |
| | | } |
| | | } |
| | | } catch (JsonProcessingException e) { |
| | | throw new CoolException(e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * @param |
| | | * @return |
| | | * @author Ryan |
| | | * @description 完成入库,更新为对应状态 |
| | | * @time 2026/02/02 12:45 |
| | | */ |
| | | @Scheduled(cron = "0/3 * * * * ?") |
| | | public void completeStock() throws Exception { |
| | | completeInStock(); |
| | | complateOutStock(); |
| | | completeStock9999(); |
| | | } |
| | | |
| | | |
| | | public void completeStock9999() throws Exception { |
| | | try{ |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getTaskStatus, TaskStsType.MISSION_TRANSFER.id).select(Task::getId)); |
| | | if (tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | for (Task task : tasks) { |
| | | if (task.getTaskType().equals(TaskType.TASK_TYPE_IN.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_IN.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_IN.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_EMPTY_IN.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_IN.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | task.setTaskStatus(TaskStsType.COMPLETE_IN.id); |
| | | } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_CROSS_DOCKING_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_EMPTY_OUT.type)) { |
| | | task.setTaskStatus(TaskStsType.COMPLETE_OUT.id); |
| | | } |
| | | taskService.updateById(task); |
| | | } |
| | | // List<Long> longSet = tasks.stream().map(Task::getId).collect(Collectors.toList()); |
| | | // List<Task> vaildTasks = taskService.list(new LambdaQueryWrapper<Task>().in(Task::getId, longSet)); |
| | | // taskService.complateInTask(vaildTasks); |
| | | } catch (Exception e) { |
| | | log.error(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * @param |
| | |
| | | * @description 完成入库,更新库存 |
| | | * @time 2025/4/2 12:37 |
| | | */ |
| | | @Scheduled(cron = "0/3 * * * * ?") |
| | | // @Scheduled(cron = "0/3 * * * * ?") |
| | | public void completeInStock() throws Exception { |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getTaskStatus, TaskStsType.COMPLETE_IN.id).select(Task::getId)); |
| | | if (tasks.isEmpty()) { |
| | | return; |
| | | try{ |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getTaskStatus, TaskStsType.COMPLETE_IN.id).select(Task::getId)); |
| | | if (tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> longSet = tasks.stream().map(Task::getId).collect(Collectors.toList()); |
| | | List<Task> vaildTasks = taskService.list(new LambdaQueryWrapper<Task>().in(Task::getId, longSet)); |
| | | taskService.complateInTask(vaildTasks); |
| | | } catch (Exception e) { |
| | | log.error(e.getMessage()); |
| | | } |
| | | List<Long> longSet = tasks.stream().map(Task::getId).collect(Collectors.toList()); |
| | | List<Task> vaildTasks = taskService.list(new LambdaQueryWrapper<Task>().in(Task::getId, longSet)); |
| | | taskService.complateInTask(vaildTasks); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @description: 完成出库任务,更新库存 |
| | | * @version 1.0 |
| | | */ |
| | | @Scheduled(cron = "0/5 * * * * ? ") |
| | | // @Scheduled(cron = "0/5 * * * * ? ") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void complateOutStock() throws Exception { |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getTaskStatus, TaskStsType.COMPLETE_OUT.id) |
| | | .select(Task::getId)); |
| | | if (tasks.isEmpty()) { |
| | | return; |
| | | try{ |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getTaskStatus, TaskStsType.COMPLETE_OUT.id) |
| | | .select(Task::getId)); |
| | | if (tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> longSet = tasks.stream().map(Task::getId).collect(Collectors.toList()); |
| | | List<Task> vaildTasks = taskService.list(new LambdaQueryWrapper<Task>().in(Task::getId, longSet)); |
| | | taskService.completeTask(vaildTasks); |
| | | } catch (Exception e) { |
| | | log.error(e.getMessage()); |
| | | } |
| | | List<Long> longSet = tasks.stream().map(Task::getId).collect(Collectors.toList()); |
| | | List<Task> vaildTasks = taskService.list(new LambdaQueryWrapper<Task>().in(Task::getId, longSet)); |
| | | taskService.completeTask(vaildTasks); |
| | | |
| | | // List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>() |
| | | // .eq(TaskItem::getWkType, OrderWorkType.ORDER_WORK_TYPE_STOCK_TERANSFER.type) |
| | |
| | | } |
| | | }); |
| | | |
| | | |
| | | // Set<Long> taskIds = taskItems.stream().map(TaskItem::getTaskId).collect(Collectors.toSet()); |
| | | // List<Task> tasks = taskService.listByIds(taskIds); |
| | | // if (!tasks.isEmpty()) { |
| | |
| | | // }); |
| | | // } |
| | | } |
| | | |
| | | /** |
| | | * 非光电站点任务下发 |
| | | */ |
| | | @Scheduled(cron = "0/5 * * * * ? ") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void pubTaskToWcs() { |
| | | Long loginUserId = SystemAuthUtils.getLoginUserId(); |
| | | List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_LOC_MOVE.type, TaskType.TASK_TYPE_EMPTY_IN.type |
| | | , TaskType.TASK_TYPE_CHECK_IN.type, TaskType.TASK_TYPE_MERGE_IN.type, TaskType.TASK_TYPE_EMPTY_OUT.type, TaskType.TASK_TYPE_PICK_IN.type, |
| | | TaskType.TASK_TYPE_PICK_AGAIN_OUT.type, TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type); |
| | | List<Integer> integers = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .in(Task::getTaskType, list) |
| | | .in(Task::getTaskStatus, integers).last("limit 1") |
| | | .orderByDesc(Task::getSort)); |
| | | for (Task task : tasks) { |
| | | /**移库不做站点操作*/ |
| | | if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | BasStation station = basStationService.getOne(new LambdaQueryWrapper<BasStation>() |
| | | .eq(BasStation::getStationName, |
| | | task.getTaskStatus().equals(TaskStsType.GENERATE_IN.id) ? task.getOrgSite() : task.getTargSite())); |
| | | if (Cools.isEmpty(station)){ |
| | | log.info("非光电站点任务下发:站点信息异常,任务信息:"+ JSON.toJSONString(task)); |
| | | continue; |
| | | } |
| | | if (station.getType().equals(StationTypeEnum.STATION_TYPE_MUTI.type)) { |
| | | continue; |
| | | } |
| | | } |
| | | /**下发普通站点任务,报错回滚,不再往下执行*/ |
| | | pubTaskToWcs(task); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @date 2025/9/4 |
| | | * @description: 光电站点任务下发 |
| | | * @version 1.0 |
| | | */ |
| | | @Scheduled(cron = "0/5 * * * * ? ") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void taskToWCS() throws Exception { |
| | | Long loginUserId = SystemAuthUtils.getLoginUserId(); |
| | | List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_LOC_MOVE.type, TaskType.TASK_TYPE_EMPTY_IN.type |
| | | , TaskType.TASK_TYPE_CHECK_IN.type, TaskType.TASK_TYPE_MERGE_IN.type, TaskType.TASK_TYPE_EMPTY_OUT.type, |
| | | TaskType.TASK_TYPE_PICK_AGAIN_OUT.type, TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type); |
| | | List<Integer> integers = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .in(Task::getTaskType, list) |
| | | .in(Task::getTaskStatus, integers) |
| | | .orderByDesc(Task::getSort)); |
| | | for (Task task : tasks) { |
| | | /**移库不做站点操作*/ |
| | | if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | BasStation station = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, |
| | | task.getTaskStatus().equals(TaskStsType.GENERATE_IN.id) ? task.getOrgSite() : task.getTargSite())); |
| | | /**过滤掉普通站点任务*/ |
| | | if (station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) { |
| | | continue; |
| | | } |
| | | Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, task.getBarcode())); |
| | | if (Objects.isNull(loc)) { |
| | | continue; |
| | | } |
| | | //判断是否深库位 |
| | | if (!LocUtils.isShallowLoc(loc.getCode())) { |
| | | //获取深库位对应的浅库位 |
| | | String shallowLoc = LocUtils.getShallowLoc(loc.getCode()); |
| | | if (StringUtils.isBlank(shallowLoc)) { |
| | | continue; |
| | | } |
| | | Loc shalloc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, shallowLoc)); |
| | | if (Objects.isNull(shalloc) || !shalloc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) { |
| | | //如果浅库位不在库跳出循环 |
| | | continue; |
| | | } |
| | | LocToTaskParams params = new LocToTaskParams(); |
| | | params.setOrgLoc(shallowLoc).setType(TaskType.TASK_TYPE_LOC_MOVE.type + ""); |
| | | //生成移库任务 |
| | | Task moveTask = locItemService.genMoveTask(params, loginUserId); |
| | | moveTask.setSort(!Objects.isNull(task.getSort()) ? task.getSort() + 1 : Constants.TASK_SORT_DEFAULT_VALUE + 1); |
| | | if (!taskService.updateById(moveTask)) { |
| | | throw new Exception("任务优先级更新失败!!"); |
| | | } |
| | | } |
| | | } |
| | | /**下发任务*/ |
| | | try { |
| | | pubTaskToWcs(task); |
| | | } catch (Exception e) { |
| | | log.error("任务下发失败!!", e); |
| | | } |
| | | } |
| | | } |
| | | // |
| | | // /** |
| | | // * 非智能站点任务下发 |
| | | // */ |
| | | // @Scheduled(cron = "0/5 * * * * ? ") |
| | | // @Transactional(rollbackFor = Exception.class) |
| | | // public void pubTaskToWcs() { |
| | | // Long loginUserId = SystemAuthUtils.getLoginUserId(); |
| | | // List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_LOC_MOVE.type, TaskType.TASK_TYPE_EMPTY_IN.type |
| | | // , TaskType.TASK_TYPE_CHECK_IN.type, TaskType.TASK_TYPE_MERGE_IN.type, TaskType.TASK_TYPE_EMPTY_OUT.type, TaskType.TASK_TYPE_PICK_IN.type, |
| | | // TaskType.TASK_TYPE_PICK_AGAIN_OUT.type, TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type); |
| | | // List<Integer> integers = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | // List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | // .in(Task::getTaskType, list) |
| | | // .in(Task::getTaskStatus, integers).last("limit 1") |
| | | // .orderByDesc(Task::getSort)); |
| | | // for (Task task : tasks) { |
| | | // /**移库不做站点操作*/ |
| | | // if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | // BasStation station = basStationService.getOne(new LambdaQueryWrapper<BasStation>() |
| | | // .eq(BasStation::getStationName, |
| | | // task.getTaskStatus().equals(TaskStsType.GENERATE_IN.id) ? task.getOrgSite() : task.getTargSite())); |
| | | // if (Cools.isEmpty(station)){ |
| | | // log.info("非智能站点任务下发:站点信息异常,任务信息:"+ JSON.toJSONString(task)); |
| | | // continue; |
| | | // } |
| | | // if (station.getType().equals(StationTypeEnum.STATION_TYPE_MUTI.type)) { |
| | | // continue; |
| | | // } |
| | | // } |
| | | // /**下发普通站点任务,报错回滚,不再往下执行*/ |
| | | // pubTaskToWcs(task); |
| | | // } |
| | | // } |
| | | // |
| | | // /** |
| | | // * @author Ryan |
| | | // * @date 2025/9/4 |
| | | // * @description: 智能站点任务下发 |
| | | // * @version 1.0 |
| | | // */ |
| | | // @Scheduled(cron = "0/5 * * * * ? ") |
| | | // @Transactional(rollbackFor = Exception.class) |
| | | // public void taskToWCS() throws Exception { |
| | | // Long loginUserId = SystemAuthUtils.getLoginUserId(); |
| | | // List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_LOC_MOVE.type, TaskType.TASK_TYPE_EMPTY_IN.type |
| | | // , TaskType.TASK_TYPE_CHECK_IN.type, TaskType.TASK_TYPE_MERGE_IN.type, TaskType.TASK_TYPE_EMPTY_OUT.type, |
| | | // TaskType.TASK_TYPE_PICK_AGAIN_OUT.type, TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type); |
| | | // List<Integer> integers = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | // List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | // .in(Task::getTaskType, list) |
| | | // .in(Task::getTaskStatus, integers) |
| | | // .orderByDesc(Task::getSort)); |
| | | // for (Task task : tasks) { |
| | | // /**移库不做站点操作*/ |
| | | // if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | // BasStation station = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, |
| | | // task.getTaskStatus().equals(TaskStsType.GENERATE_IN.id) ? task.getOrgSite() : task.getTargSite())); |
| | | // /**过滤掉普通站点任务*/ |
| | | // if (station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) { |
| | | // continue; |
| | | // } |
| | | // Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, task.getBarcode())); |
| | | // if (Objects.isNull(loc)) { |
| | | // continue; |
| | | // } |
| | | // //判断是否深库位 |
| | | // if (!LocUtils.isShallowLoc(loc.getCode())) { |
| | | // //获取深库位对应的浅库位 |
| | | // String shallowLoc = LocUtils.getShallowLoc(loc.getCode()); |
| | | // if (StringUtils.isBlank(shallowLoc)) { |
| | | // continue; |
| | | // } |
| | | // Loc shalloc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, shallowLoc)); |
| | | // if (Objects.isNull(shalloc) || !shalloc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) { |
| | | // //如果浅库位不在库跳出循环 |
| | | // continue; |
| | | // } |
| | | // LocToTaskParams params = new LocToTaskParams(); |
| | | // params.setOrgLoc(shallowLoc).setType(TaskType.TASK_TYPE_LOC_MOVE.type + ""); |
| | | // //生成移库任务 |
| | | // Task moveTask = locItemService.genMoveTask(params, loginUserId); |
| | | // moveTask.setSort(!Objects.isNull(task.getSort()) ? task.getSort() + 1 : Constants.TASK_SORT_DEFAULT_VALUE + 1); |
| | | // if (!taskService.updateById(moveTask)) { |
| | | // throw new Exception("任务优先级更新失败!!"); |
| | | // } |
| | | // } |
| | | // } |
| | | // /**下发任务*/ |
| | | // try { |
| | | // pubTaskToWcs(task); |
| | | // } catch (Exception e) { |
| | | // log.error("任务下发失败!!", e); |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | /** |
| | | * 每五秒校验深库位是否为空,如果浅库位有货,将浅库位移至深库位 |
| | |
| | | |
| | | BasStation station = null; |
| | | if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | station = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, task.getTargSite())); |
| | | station = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, task.getTargSite())); |
| | | if (Objects.isNull(station)) { |
| | | throw new CoolException("站点不存在!!"); |
| | | } |
| | |
| | | /**判断是否起点系统类型 非标准程序*/ |
| | | Loc locStart = null; |
| | | if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type) || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_OUT.type) || |
| | | task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type) || task.getTaskType().equals(TaskType.TASK_TYPE_EMPTY_OUT.type)) { |
| | | task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type) || task.getTaskType().equals(TaskType.TASK_TYPE_EMPTY_OUT.type)) { |
| | | locStart = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getOrgLoc())); |
| | | if (Objects.isNull(locStart)) { |
| | | throw new CoolException("源库位不存在!!"); |
| | |
| | | |
| | | |
| | | /**判断是否起点系统类型 非标准程序*/ |
| | | if (locStart == null){ |
| | | if (locStart == null) { |
| | | if (task.getTaskType().equals(TaskType.TASK_TYPE_IN.type) || task.getTaskType().equals(TaskType.TASK_TYPE_EMPTY_IN.type) || |
| | | task.getTaskType().equals(TaskType.TASK_TYPE_PICK_IN.type) || |
| | | task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_IN.type) || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_IN.type) |
| | | ) { |
| | | task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_IN.type) || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_IN.type) |
| | | ) { |
| | | BasStation stationS = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, task.getOrgSite())); |
| | | if (Objects.isNull(stationS)) { |
| | | throw new CoolException("源库位不存在!!"); |
| | |
| | | } |
| | | |
| | | |
| | | /**判断是否光电站点,非光店站点需管控站点状态*/ //目标站点 |
| | | /**判断是否智能站点,非光店站点需管控站点状态*/ //目标站点 |
| | | if (!Objects.isNull(station) && station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) { |
| | | if (task.getTaskType() <= TaskType.TASK_TYPE_CHECK_IN.type && !task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | // if (!station.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) { |
| | |
| | | .setTargSite(checkItem.getSiteNo()) |
| | | .setResource(TaskResouceType.TASK_RESOUCE_CHECK_TYPE.val) |
| | | .setTaskType(TaskType.TASK_TYPE_CHECK_OUT.type) |
| | | .setTaskStatus(TaskStsType.GENERATE_OUT.id) |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id) |
| | | .setCreateTime(new Date()) |
| | | .setUpdateBy(loginUserId) |
| | | .setUpdateTime(new Date()) |
| | |
| | | .setCreateBy(loginUserId) |
| | | .setCreateTime(new Date()) |
| | | .setUpdateTime(new Date()) |
| | | .setTaskStatus(TaskStsType.GENERATE_OUT.id) |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id) |
| | | .setBarcode(loc.getBarcode()) |
| | | .setMemo(map.getMemo()); |
| | | |
| | |
| | | |
| | | //增加对备货单得判断 |
| | | Integer taskStatus = resouce.equals(TaskResouceType.TASK_RESOUCE_STOCK_UP.val) |
| | | ? TaskStsType.MISSION_INITIAL.id:TaskStsType.GENERATE_OUT.id; |
| | | ? TaskStsType.MISSION_INITIAL.id:TaskStsType.MISSION_INITIAL.id; |
| | | |
| | | Task moveTask = new Task(); |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null); |
| | |
| | | .setUpdateBy(loginUserId) |
| | | .setSort(Constants.TASK_SORT_DEFAULT_VALUE) |
| | | .setUpdateTime(new Date()) |
| | | .setTaskStatus(TaskStsType.GENERATE_IN.id) |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id) |
| | | .setBarcode(orgLoc.getBarcode()) |
| | | .setMemo(map.getMemo()); |
| | | |
| | |
| | | } |
| | | Task task = new Task(); |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskStatus(TaskStsType.GENERATE_IN.id) |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id) |
| | | .setTaskType(TaskType.TASK_TYPE_IN.type) |
| | | .setWarehType(WarehType.WAREHOUSE_TYPE_AGV.val) |
| | | .setTargLoc(targetLoc) |
| | |
| | | } |
| | | Task task = new Task(); |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskStatus(TaskStsType.GENERATE_IN.id) |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id) |
| | | .setTaskType(TaskType.TASK_TYPE_IN.type) |
| | | .setResource(TaskResouceType.TASK_RESOUCE_PAKIN_TYPE.val) |
| | | .setTargLoc(targetLoc) |
| | |
| | | for (BasContainer container : containers) { |
| | | String codeType = container.getCodeType(); // 获取正则表达式 |
| | | if (barcode.matches(codeType)) { // 判断条码是否符合这个正则 |
| | | List<Integer> areaList2 = container.getAreas(); |
| | | List<Integer> areaList2 = container.getAreasIds(); |
| | | if (!areaList2.contains(Integer.parseInt(area))) { |
| | | matches2 = false; |
| | | continue; |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Task taskToTop(Long id, Long loginUserId) throws Exception { |
| | | List<Integer> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Integer> longs = Arrays.asList(TaskStsType.MISSION_INITIAL.id, TaskStsType.MISSION_INITIAL.id); |
| | | Task tasks = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getId, id).in(Task::getTaskStatus, longs)); |
| | | if (Objects.isNull(tasks)) { |
| | | throw new CoolException("任务已处执行状态不可一键置顶!!"); |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Task operateComplete(Long id, Long loginUserId) { |
| | | List<Integer> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Integer> longs = Arrays.asList(TaskStsType.MISSION_INITIAL.id, TaskStsType.MISSION_INITIAL.id); |
| | | Task task = taskService.getOne(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getId, id) |
| | | .in(Task::getTaskStatus, longs)); |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R removeTask(Long[] ids, Long loginUserId) { |
| | | List<Integer> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Integer> longs = Arrays.asList(TaskStsType.MISSION_INITIAL.id, TaskStsType.MISSION_INITIAL.id); |
| | | List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type, |
| | | TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_EMPTY_IN.type, TaskType.TASK_TYPE_LOC_MOVE.type, |
| | | TaskType.TASK_TYPE_EMPTY_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type); |
| | |
| | | } |
| | | for (Task task : tasks) { |
| | | //取消移库任务 |
| | | if (task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type) && task.getTaskStatus().equals(TaskStsType.GENERATE_IN.id)) { |
| | | if (task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type) && task.getTaskStatus().equals(TaskStsType.MISSION_INITIAL.id)) { |
| | | if (!locService.update(new LambdaUpdateWrapper<Loc>() |
| | | .eq(Loc::getCode, task.getOrgLoc()) |
| | | .set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_F.type))) { |
| | |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskType(type) |
| | | .setBarcode(task.getBarcode()) |
| | | .setTaskStatus(TaskStsType.GENERATE_IN.id); |
| | | .setTaskStatus(TaskStsType.MISSION_INITIAL.id); |
| | | |
| | | TaskInParam param = new TaskInParam(); |
| | | param.setSourceStaNo(task.getTargSite()) |
| | |
| | | if (Objects.isNull(ids) || ids.isEmpty()) { |
| | | return R.error("任务编码不能为空!!"); |
| | | } |
| | | List<Integer> integers = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Integer> integers = Arrays.asList(TaskStsType.MISSION_INITIAL.id, TaskStsType.MISSION_INITIAL.id); |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .in(Task::getId, ids) |
| | | .in(Task::getTaskStatus, integers) |
| | |
| | | } |
| | | } |
| | | |
| | | /**判断是否光电站点,非光店站点需管控站点状态*/ |
| | | /**判断是否智能站点,非光店站点需管控站点状态*/ |
| | | if (!Objects.isNull(station) && station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) { |
| | | if (task.getTaskType() <= TaskType.TASK_TYPE_CHECK_IN.type && !task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | station.setUseStatus(LocStsType.LOC_STS_TYPE_R.type); |
| New file |
| | |
| | | package com.vincent.rsf.server.manager.utils; |
| | | |
| | | import com.fasterxml.jackson.core.JsonParser; |
| | | import com.fasterxml.jackson.core.JsonToken; |
| | | import com.fasterxml.jackson.databind.DeserializationContext; |
| | | import com.fasterxml.jackson.databind.JsonDeserializer; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * |
| | | * @author chen.lin |
| | | * @time 2026-02-02 |
| | | * Areas 字段自定义反序列化器 |
| | | * 支持两种格式: |
| | | * 1. [1, 2, 3] - 纯ID数组(向后兼容) |
| | | * 2. [{"id": 1, "sort": 1}, {"id": 2, "sort": 2}] - 对象数组(新格式) |
| | | * |
| | | * |
| | | */ |
| | | public class AreasDeserializer extends JsonDeserializer<List<Map<String, Object>>> { |
| | | |
| | | @Override |
| | | public List<Map<String, Object>> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { |
| | | JsonToken currentToken = p.getCurrentToken(); |
| | | |
| | | // 处理 null 值 |
| | | if (currentToken == JsonToken.VALUE_NULL) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | // 处理数组 |
| | | if (currentToken == JsonToken.START_ARRAY) { |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | JsonToken token = p.nextToken(); |
| | | |
| | | while (token != null && token != JsonToken.END_ARRAY) { |
| | | if (token == JsonToken.VALUE_NUMBER_INT) { |
| | | // 处理纯ID数组格式 [1, 2, 3] |
| | | int id = p.getIntValue(); |
| | | Map<String, Object> area = new HashMap<>(); |
| | | area.put("id", id); |
| | | area.put("sort", result.size() + 1); // 默认排序 |
| | | result.add(area); |
| | | token = p.nextToken(); |
| | | } else if (token == JsonToken.START_OBJECT) { |
| | | // 处理对象数组格式 [{"id": 1, "sort": 1}] |
| | | ObjectMapper mapper = (ObjectMapper) p.getCodec(); |
| | | @SuppressWarnings("unchecked") |
| | | Map<String, Object> area = mapper.readValue(p, Map.class); |
| | | result.add(area); |
| | | token = p.nextToken(); |
| | | } else { |
| | | token = p.nextToken(); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | // 如果既不是数组也不是null,返回空列表 |
| | | return new ArrayList<>(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.server.manager.utils; |
| | | |
| | | import com.fasterxml.jackson.core.JsonGenerator; |
| | | import com.fasterxml.jackson.databind.JsonSerializer; |
| | | import com.fasterxml.jackson.databind.SerializerProvider; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * @author chen.lin |
| | | * @time 2026-02-02 |
| | | * Areas 字段自定义序列化器 |
| | | * 将 List<Map<String, Object>> 序列化为 JSON 数组 |
| | | * 支持混合类型(Integer 和 Map)的向后兼容 |
| | | * |
| | | */ |
| | | public class AreasSerializer extends JsonSerializer<Object> { |
| | | |
| | | @Override |
| | | public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { |
| | | if (value == null) { |
| | | gen.writeNull(); |
| | | return; |
| | | } |
| | | |
| | | if (!(value instanceof List)) { |
| | | gen.writeObject(value); |
| | | return; |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | List<Object> list = (List<Object>) value; |
| | | |
| | | gen.writeStartArray(); |
| | | for (Object item : list) { |
| | | if (item instanceof Map) { |
| | | // 已经是 Map 格式 |
| | | @SuppressWarnings("unchecked") |
| | | Map<String, Object> map = (Map<String, Object>) item; |
| | | gen.writeObject(map); |
| | | } else if (item instanceof Number) { |
| | | // 如果是 Number(向后兼容),转换为 Map 格式 |
| | | Map<String, Object> areaMap = new HashMap<>(); |
| | | areaMap.put("id", ((Number) item).intValue()); |
| | | areaMap.put("sort", 1); // 默认排序 |
| | | gen.writeObject(areaMap); |
| | | } else { |
| | | // 其他类型,尝试直接写入 |
| | | gen.writeObject(item); |
| | | } |
| | | } |
| | | gen.writeEndArray(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.server.manager.utils; |
| | | |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import org.apache.ibatis.type.BaseTypeHandler; |
| | | import org.apache.ibatis.type.JdbcType; |
| | | import org.apache.ibatis.type.MappedJdbcTypes; |
| | | import org.apache.ibatis.type.MappedTypes; |
| | | |
| | | import java.sql.CallableStatement; |
| | | import java.sql.PreparedStatement; |
| | | import java.sql.ResultSet; |
| | | import java.sql.SQLException; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * @author chen.lin |
| | | * @time 2026-02-02 |
| | | * Areas 字段自定义 TypeHandler |
| | | * 处理数据库和 Java 对象之间的转换 |
| | | * 支持两种格式: |
| | | * 1. [1, 2, 3] - 纯ID数组(向后兼容) |
| | | * 2. [{"id": 1, "sort": 1}, {"id": 2, "sort": 2}] - 对象数组(新格式) |
| | | * |
| | | */ |
| | | @MappedTypes({List.class}) |
| | | @MappedJdbcTypes(JdbcType.VARCHAR) |
| | | public class AreasTypeHandler extends BaseTypeHandler<List<Map<String, Object>>> { |
| | | |
| | | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| | | private static final TypeReference<List<Object>> LIST_TYPE_REF = new TypeReference<List<Object>>() {}; |
| | | |
| | | /** |
| | | * 解析 JSON 字符串为 List<Map<String, Object>> |
| | | */ |
| | | private List<Map<String, Object>> parse(String json) { |
| | | if (json == null || json.trim().isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | try { |
| | | // 先解析为 List<Object> |
| | | List<Object> rawList = OBJECT_MAPPER.readValue(json, LIST_TYPE_REF); |
| | | |
| | | if (rawList == null || rawList.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | |
| | | // 遍历所有元素并转换 |
| | | for (int i = 0; i < rawList.size(); i++) { |
| | | Object item = rawList.get(i); |
| | | |
| | | if (item instanceof Map) { |
| | | // 已经是对象数组格式 [{"id": 1, "sort": 1}] |
| | | @SuppressWarnings("unchecked") |
| | | Map<String, Object> map = (Map<String, Object>) item; |
| | | result.add(map); |
| | | } else if (item instanceof Number) { |
| | | // 纯ID数组格式 [1, 2, 3],转换为对象数组 |
| | | Map<String, Object> area = new HashMap<>(); |
| | | area.put("id", ((Number) item).intValue()); |
| | | area.put("sort", i + 1); |
| | | result.add(area); |
| | | } |
| | | // 忽略其他类型 |
| | | } |
| | | |
| | | return result; |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("Failed to parse areas JSON: " + json, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将 List<Map<String, Object>> 转换为 JSON 字符串 |
| | | */ |
| | | private String toJson(List<Map<String, Object>> obj) { |
| | | if (obj == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return OBJECT_MAPPER.writeValueAsString(obj); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("Failed to serialize areas to JSON", e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void setNonNullParameter(PreparedStatement ps, int i, List<Map<String, Object>> parameter, JdbcType jdbcType) throws SQLException { |
| | | ps.setString(i, toJson(parameter)); |
| | | } |
| | | |
| | | @Override |
| | | public List<Map<String, Object>> getNullableResult(ResultSet rs, String columnName) throws SQLException { |
| | | String json = rs.getString(columnName); |
| | | return parse(json); |
| | | } |
| | | |
| | | @Override |
| | | public List<Map<String, Object>> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { |
| | | String json = rs.getString(columnIndex); |
| | | return parse(json); |
| | | } |
| | | |
| | | @Override |
| | | public List<Map<String, Object>> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { |
| | | String json = cs.getString(columnIndex); |
| | | return parse(json); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.server.system.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.vincent.rsf.server.common.utils.ExcelUtil; |
| | | import com.vincent.rsf.server.common.annotation.OperationLog; |
| | | import com.vincent.rsf.server.common.domain.BaseParam; |
| | | import com.vincent.rsf.server.common.domain.KeyValVo; |
| | | import com.vincent.rsf.server.common.domain.PageParam; |
| | | import com.vincent.rsf.server.system.entity.TaskPathTemplateMerge; |
| | | import com.vincent.rsf.server.system.service.TaskPathTemplateMergeService; |
| | | import com.vincent.rsf.server.system.controller.BaseController; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.util.*; |
| | | |
| | | @RestController |
| | | public class TaskPathTemplateMergeController extends BaseController { |
| | | |
| | | @Autowired |
| | | private TaskPathTemplateMergeService taskPathTemplateMergeService; |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:list')") |
| | | @PostMapping("/taskPathTemplateMerge/page") |
| | | public R page(@RequestBody Map<String, Object> map) { |
| | | BaseParam baseParam = buildParam(map, BaseParam.class); |
| | | PageParam<TaskPathTemplateMerge, BaseParam> pageParam = new PageParam<>(baseParam, TaskPathTemplateMerge.class); |
| | | return R.ok().add(taskPathTemplateMergeService.page(pageParam, pageParam.buildWrapper(true))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:list')") |
| | | @PostMapping("/taskPathTemplateMerge/list") |
| | | public R list(@RequestBody Map<String, Object> map) { |
| | | return R.ok().add(taskPathTemplateMergeService.list()); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:list')") |
| | | @PostMapping({"/taskPathTemplateMerge/many/{ids}", "/taskPathTemplateMerges/many/{ids}"}) |
| | | public R many(@PathVariable Long[] ids) { |
| | | return R.ok().add(taskPathTemplateMergeService.listByIds(Arrays.asList(ids))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:list')") |
| | | @GetMapping("/taskPathTemplateMerge/{id}") |
| | | public R get(@PathVariable("id") Long id) { |
| | | return R.ok().add(taskPathTemplateMergeService.getById(id)); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:save')") |
| | | @OperationLog("Create 物料权限") |
| | | @PostMapping("/taskPathTemplateMerge/save") |
| | | public R save(@RequestBody TaskPathTemplateMerge taskPathTemplateMerge) { |
| | | taskPathTemplateMerge.setCreateBy(getLoginUserId()); |
| | | taskPathTemplateMerge.setCreateTime(new Date()); |
| | | taskPathTemplateMerge.setUpdateBy(getLoginUserId()); |
| | | taskPathTemplateMerge.setUpdateTime(new Date()); |
| | | if (!taskPathTemplateMergeService.save(taskPathTemplateMerge)) { |
| | | return R.error("Save Fail"); |
| | | } |
| | | return R.ok("Save Success").add(taskPathTemplateMerge); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:update')") |
| | | @OperationLog("Update 物料权限") |
| | | @PostMapping("/taskPathTemplateMerge/update") |
| | | public R update(@RequestBody TaskPathTemplateMerge taskPathTemplateMerge) { |
| | | taskPathTemplateMerge.setUpdateBy(getLoginUserId()); |
| | | taskPathTemplateMerge.setUpdateTime(new Date()); |
| | | if (!taskPathTemplateMergeService.updateById(taskPathTemplateMerge)) { |
| | | return R.error("Update Fail"); |
| | | } |
| | | return R.ok("Update Success").add(taskPathTemplateMerge); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:remove')") |
| | | @OperationLog("Delete 物料权限") |
| | | @PostMapping("/taskPathTemplateMerge/remove/{ids}") |
| | | public R remove(@PathVariable Long[] ids) { |
| | | if (!taskPathTemplateMergeService.removeByIds(Arrays.asList(ids))) { |
| | | return R.error("Delete Fail"); |
| | | } |
| | | return R.ok("Delete Success").add(ids); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:list')") |
| | | @PostMapping("/taskPathTemplateMerge/query") |
| | | public R query(@RequestParam(required = false) String condition) { |
| | | List<KeyValVo> vos = new ArrayList<>(); |
| | | LambdaQueryWrapper<TaskPathTemplateMerge> wrapper = new LambdaQueryWrapper<>(); |
| | | if (!Cools.isEmpty(condition)) { |
| | | wrapper.like(TaskPathTemplateMerge::getId, condition); |
| | | } |
| | | taskPathTemplateMergeService.page(new Page<>(1, 30), wrapper).getRecords().forEach( |
| | | item -> vos.add(new KeyValVo(item.getId(), item.getId())) |
| | | ); |
| | | return R.ok().add(vos); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateMerge:list')") |
| | | @PostMapping("/taskPathTemplateMerge/export") |
| | | public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception { |
| | | ExcelUtil.build(ExcelUtil.create(taskPathTemplateMergeService.list(), TaskPathTemplateMerge.class), response); |
| | | } |
| | | |
| | | } |
| | |
| | | private Date updateTime; |
| | | |
| | | public FlowInstance() {} |
| | | public FlowInstance(SubsystemFlowTemplate subsystemFlowTemplate) { |
| | | this.flowTemplateId = subsystemFlowTemplate.getId(); |
| | | this.flowTemplateCode = subsystemFlowTemplate.getFlowCode(); |
| | | this.templateVersion = subsystemFlowTemplate.getVersion(); |
| | | |
| | | } |
| | | |
| | | public FlowInstance(String flowInstanceNo,Long taskId,String taskNo,Long nodeInstanceId,String nodeCode,Long flowTemplateId,String flowTemplateCode,Integer templateVersion,Short status,String currentStepCode,Integer currentStepOrder,String executeParams,String executeResult,String errorCode,String errorMessage,Date startTime,Date endTime,Date timeoutAt,Integer durationSeconds,Integer retryTimes,Date lastRetryTime,String contextData,Date createTime,Date updateTime) { |
| | | this.flowInstanceNo = flowInstanceNo; |
| | |
| | | private Long stepTemplateId; |
| | | |
| | | /** |
| | | * 状态:0-待执行 1-执行中 2-成功 3-失败 4-跳过 5-超时 |
| | | * 状态:0-排队中 1-待执行 2-执行中 3-执行成功 4-执行失败 5-已跳过 6-已取消 |
| | | */ |
| | | @ApiModelProperty(value= "状态:0-待执行 1-执行中 2-成功 3-失败 4-跳过 5-超时") |
| | | @ApiModelProperty(value= "状态:0-排队中 1-待执行 2-执行中 3-执行成功 4-执行失败 5-已跳过 6-已取消") |
| | | private Short status; |
| | | |
| | | /** |
| | |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date updateTime; |
| | | |
| | | @ApiModelProperty(value= "wms下一步任务类型ID") |
| | | private Integer wmsNextTaskStatus; |
| | | |
| | | @ApiModelProperty(value= "wms当前任务类型ID") |
| | | private Integer wmsNowTaskStatus; |
| | | |
| | | public FlowStepInstance() {} |
| | | public FlowStepInstance(FlowStepTemplate flowStepTemplate) { |
| | | this.stepTemplateId = flowStepTemplate.getId(); |
| | | this.stepName = flowStepTemplate.getStepName(); |
| | | this.stepType = flowStepTemplate.getStepType(); |
| | | } |
| | | |
| | | public FlowStepInstance(Long flowInstanceId,String flowInstanceNo,Integer stepOrder,String stepCode,String stepName,String stepType,Long stepTemplateId,Short status,String executeResult,String errorCode,String errorMessage,Date startTime,Date endTime,Integer durationSeconds,String inputData,String outputData,Integer retryTimes,Date createTime,Date updateTime) { |
| | | this.flowInstanceId = flowInstanceId; |
| | |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date updateTime; |
| | | |
| | | @ApiModelProperty(value= "wms下一步任务状态ID") |
| | | private Integer wmsNowTaskStatus; |
| | | |
| | | public FlowStepTemplate() {} |
| | | |
| | | public FlowStepTemplate(Long flowId,String flowCode,Integer stepOrder,String stepCode,String stepName,String stepType,String actionType,String actionConfig,String inputMapping,String outputMapping,String conditionExpression,Short skipOnFail,Short retryEnabled,String retryConfig,Integer timeoutSeconds,Long createBy,Long updateBy,Date createTime,Date updateTime) { |
| | |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | import com.vincent.rsf.server.manager.entity.Task; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | |
| | | private Date updateTime; |
| | | |
| | | public TaskInstance() {} |
| | | public TaskInstance(TaskPathTemplateMerge taskPathTemplateMerge, Task task) { |
| | | this.templateId = taskPathTemplateMerge.getId(); |
| | | this.templateCode = taskPathTemplateMerge.getTemplateCode(); |
| | | this.templateVersion = taskPathTemplateMerge.getVersion(); |
| | | this.sourceCode = taskPathTemplateMerge.getSourceType(); |
| | | this.targetCode = taskPathTemplateMerge.getTargetType(); |
| | | // this.sourceInfo = taskPathTemplateMerge.getSourceType(); |
| | | // this.targetInfo = taskPathTemplateMerge.getTargetType(); |
| | | this.taskNo = task.getTaskCode(); |
| | | this.bizType = task.getTaskType$(); |
| | | } |
| | | |
| | | public TaskInstance(String taskNo,String bizNo,String bizType,Long templateId,String templateCode,Integer templateVersion,String sourceInfo,String targetInfo,String sourceCode,String targetCode,String plannedPath,String actualPath,Short priority,Date timeoutAt,Short status,String currentNodeCode,String currentNodeName,Integer totalNodes,Integer completedNodes,Double progressRate,Integer estimatedDurationMinutes,Integer actualDurationMinutes,Date startTime,Date endTime,String resultCode,String resultMessage,String resultData,Integer retryTimes,Date lastRetryTime,String extParams,String remark,Long createBy,Long updateBy,Date createTime,Date updateTime) { |
| | | this.taskNo = taskNo; |
| | |
| | | private String executeParams; |
| | | |
| | | /** |
| | | * 状态:0-待执行 1-执行中 2-执行成功 3-执行失败 4-已跳过 5-已取消 |
| | | * 状态:0-排队中 1-待执行 2-执行中 3-执行成功 4-执行失败 5-已跳过 6-已取消 |
| | | */ |
| | | @ApiModelProperty(value= "状态:0-待执行 1-执行中 2-执行成功 3-执行失败 4-已跳过 5-已取消") |
| | | @ApiModelProperty(value= "状态:0-排队中 1-待执行 2-执行中 3-执行成功 4-执行失败 5-已跳过 6-已取消") |
| | | private Short status; |
| | | |
| | | /** |
| | |
| | | private Date updateTime; |
| | | |
| | | public TaskInstanceNode() {} |
| | | public TaskInstanceNode(TaskPathTemplateNode taskPathTemplateNode) { |
| | | this.nodeName = taskPathTemplateNode.getNodeName(); |
| | | this.nodeType = taskPathTemplateNode.getNodeType(); |
| | | this.systemCode = taskPathTemplateNode.getSystemCode(); |
| | | this.systemName = taskPathTemplateNode.getSystemName(); |
| | | } |
| | | |
| | | public TaskInstanceNode(Long taskId,String taskNo,Integer nodeOrder,String nodeCode,String nodeName,String nodeType,String systemCode,String systemName,String executeParams,Short status,String executeResult,String errorCode,String errorMessage,Date estimatedStartTime,Date actualStartTime,Date actualEndTime,Date timeoutAt,Integer durationSeconds,Integer retryTimes,Integer maxRetryTimes,String dependsOnNodes,Date createTime,Date updateTime) { |
| | | this.taskId = taskId; |
| New file |
| | |
| | | package com.vincent.rsf.server.system.entity; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
| | | import lombok.experimental.Accessors; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.framework.common.SpringUtils; |
| | | import com.vincent.rsf.server.system.service.UserService; |
| | | import com.vincent.rsf.server.system.entity.User; |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | @Data |
| | | @Accessors(chain = true) |
| | | @TableName(value = "mission_task_path_template_merge", autoResultMap = true) |
| | | public class TaskPathTemplateMerge implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 主键ID |
| | | */ |
| | | @ApiModelProperty(value= "主键ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 模板编码,唯一标识 |
| | | */ |
| | | @ApiModelProperty(value= "模板编码,唯一标识") |
| | | private String templateCode; |
| | | |
| | | /** |
| | | * 模板名称 |
| | | */ |
| | | @ApiModelProperty(value= "模板名称") |
| | | private String templateName; |
| | | |
| | | /** |
| | | * 起点类型/起点系统标识 |
| | | */ |
| | | @ApiModelProperty(value= "起点类型/起点系统标识") |
| | | private String sourceType; |
| | | |
| | | /** |
| | | * 终点类型/终点系统标识 |
| | | */ |
| | | @ApiModelProperty(value= "终点类型/终点系统标识") |
| | | private String targetType; |
| | | |
| | | /** |
| | | * 条件表达式(JSON),用于更复杂的匹配 |
| | | */ |
| | | @ApiModelProperty(value = "条件表达式(JSON),用于更复杂的匹配") |
| | | @TableField(typeHandler = JacksonTypeHandler.class) |
| | | private List<Integer> conditionExpression; |
| | | |
| | | /** |
| | | * 条件描述,人工可读 |
| | | */ |
| | | @ApiModelProperty(value= "条件描述,人工可读") |
| | | private String conditionDesc; |
| | | |
| | | /** |
| | | * 版本号 |
| | | */ |
| | | @ApiModelProperty(value= "版本号") |
| | | private Integer version; |
| | | |
| | | /** |
| | | * 是否为当前生效版本 0-否 1-是 |
| | | */ |
| | | @ApiModelProperty(value= "是否为当前生效版本 0-否 1-是") |
| | | private Short isCurrent; |
| | | |
| | | /** |
| | | * 生效时间 |
| | | */ |
| | | @ApiModelProperty(value= "生效时间") |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date effectiveTime; |
| | | |
| | | /** |
| | | * 失效时间 |
| | | */ |
| | | @ApiModelProperty(value= "失效时间") |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date expireTime; |
| | | |
| | | /** |
| | | * 优先级 1-10,数字越小优先级越高 |
| | | */ |
| | | @ApiModelProperty(value= "优先级 1-10,数字越小优先级越高") |
| | | private Short priority; |
| | | |
| | | /** |
| | | * 整体超时时间(分钟) |
| | | */ |
| | | @ApiModelProperty(value= "整体超时时间(分钟)") |
| | | private Integer timeoutMinutes; |
| | | |
| | | /** |
| | | * 最大重试次数 |
| | | */ |
| | | @ApiModelProperty(value= "最大重试次数") |
| | | private Integer maxRetryTimes; |
| | | |
| | | /** |
| | | * 重试间隔(秒) |
| | | */ |
| | | @ApiModelProperty(value= "重试间隔(秒)") |
| | | private Integer retryIntervalSeconds; |
| | | |
| | | /** |
| | | * 状态 0-禁用 1-启用 |
| | | */ |
| | | @ApiModelProperty(value= "状态 0-禁用 1-启用") |
| | | private Short status; |
| | | |
| | | /** |
| | | * 备注 |
| | | */ |
| | | @ApiModelProperty(value= "备注") |
| | | private String remark; |
| | | |
| | | /** |
| | | * 创建人 |
| | | */ |
| | | @ApiModelProperty(value= "创建人") |
| | | private Long createBy; |
| | | |
| | | /** |
| | | * 更新人 |
| | | */ |
| | | @ApiModelProperty(value= "更新人") |
| | | private Long updateBy; |
| | | |
| | | /** |
| | | * 创建时间 |
| | | */ |
| | | @ApiModelProperty(value= "创建时间") |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date createTime; |
| | | |
| | | /** |
| | | * 更新时间 |
| | | */ |
| | | @ApiModelProperty(value= "更新时间") |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date updateTime; |
| | | |
| | | /** |
| | | * 步序长度 |
| | | */ |
| | | @ApiModelProperty(value= "步序长度") |
| | | private Integer stepSize; |
| | | |
| | | /** |
| | | * 租户 |
| | | */ |
| | | @ApiModelProperty(value= "租户") |
| | | private Long tenantId; |
| | | |
| | | /** |
| | | * 是否删除 1: 是 0: 否 |
| | | */ |
| | | @ApiModelProperty(value= "是否删除 1: 是 0: 否 ") |
| | | @TableLogic |
| | | private Long deleted; |
| | | |
| | | public TaskPathTemplateMerge() {} |
| | | |
| | | public String getEffectiveTime$(){ |
| | | if (Cools.isEmpty(this.effectiveTime)){ |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.effectiveTime); |
| | | } |
| | | |
| | | public String getExpireTime$(){ |
| | | if (Cools.isEmpty(this.expireTime)){ |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.expireTime); |
| | | } |
| | | |
| | | public String getCreateTime$(){ |
| | | if (Cools.isEmpty(this.createTime)){ |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime); |
| | | } |
| | | |
| | | public String getUpdateTime$(){ |
| | | if (Cools.isEmpty(this.updateTime)){ |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime); |
| | | } |
| | | |
| | | |
| | | |
| | | public Boolean getStatusBool(){ |
| | | if (null == this.status){ return null; } |
| | | switch (this.status){ |
| | | case 1: |
| | | return true; |
| | | case 0: |
| | | return false; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | public String[] route(){ |
| | | return new String[]{this.sourceType, this.targetType, this.stepSize.toString(), this.id.toString()}; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.server.system.mapper; |
| | | |
| | | import com.vincent.rsf.server.system.entity.TaskPathTemplateMerge; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | @Mapper |
| | | @Repository |
| | | public interface TaskPathTemplateMergeMapper extends BaseMapper<TaskPathTemplateMerge> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.server.system.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.vincent.rsf.server.system.entity.TaskPathTemplateMerge; |
| | | |
| | | public interface TaskPathTemplateMergeService extends IService<TaskPathTemplateMerge> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.server.system.service.impl; |
| | | |
| | | import com.vincent.rsf.server.system.mapper.TaskPathTemplateMergeMapper; |
| | | import com.vincent.rsf.server.system.entity.TaskPathTemplateMerge; |
| | | import com.vincent.rsf.server.system.service.TaskPathTemplateMergeService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service("taskPathTemplateMergeService") |
| | | public class TaskPathTemplateMergeServiceImpl extends ServiceImpl<TaskPathTemplateMergeMapper, TaskPathTemplateMerge> implements TaskPathTemplateMergeService { |
| | | |
| | | } |
| New file |
| | |
| | | -- save taskPathTemplateMerge record |
| | | -- mysql |
| | | insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.taskPathTemplateMerge', '0', '/system/taskPathTemplateMerge', 'taskPathTemplateMerge', '0' , '0', '1' , '1'); |
| | | |
| | | insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query 项目实际路径', '', '1', 'system:taskPathTemplateMerge:list', '0', '1', '1'); |
| | | insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Create 项目实际路径', '', '1', 'system:taskPathTemplateMerge:save', '1', '1', '1'); |
| | | insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Update 项目实际路径', '', '1', 'system:taskPathTemplateMerge:update', '2', '1', '1'); |
| | | insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete 项目实际路径', '', '1', 'system:taskPathTemplateMerge:remove', '3', '1', '1'); |
| | | |
| | | -- locale menu name |
| | | taskPathTemplateMerge: 'TaskPathTemplateMerge', |
| | | |
| | | -- locale field |
| | | taskPathTemplateMerge: { |
| | | templateCode: "templateCode", |
| | | templateName: "templateName", |
| | | sourceType: "sourceType", |
| | | targetType: "targetType", |
| | | conditionExpression: "conditionExpression", |
| | | conditionDesc: "conditionDesc", |
| | | version: "version", |
| | | isCurrent: "isCurrent", |
| | | effectiveTime: "effectiveTime", |
| | | expireTime: "expireTime", |
| | | priority: "priority", |
| | | timeoutMinutes: "timeoutMinutes", |
| | | maxRetryTimes: "maxRetryTimes", |
| | | retryIntervalSeconds: "retryIntervalSeconds", |
| | | remark: "remark", |
| | | stepSize: "stepSize", |
| | | }, |
| | | |
| | | -- ResourceContent |
| | | import taskPathTemplateMerge from './taskPathTemplateMerge'; |
| | | |
| | | case 'taskPathTemplateMerge': |
| | | return taskPathTemplateMerge; |
| New file |
| | |
| | | <?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="com.vincent.rsf.server.system.mapper.TaskPathTemplateMergeMapper"> |
| | | |
| | | </mapper> |
| New file |
| | |
| | | -- 为 man_warehouse_areas 表添加 sort 排序字段 |
| | | -- @author chen.lin |
| | | -- @time 2026-02-02 |
| | | |
| | | -- 添加 sort 字段,默认值为 NULL,允许为空 |
| | | ALTER TABLE `man_warehouse_areas` |
| | | ADD COLUMN `sort` INT(11) NULL DEFAULT NULL COMMENT '排序字段' AFTER `status`; |
| | | |
| | | -- 为现有数据设置默认排序值(使用 id 作为初始排序值) |
| | | UPDATE `man_warehouse_areas` |
| | | SET `sort` = `id` |
| | | WHERE `sort` IS NULL; |