feat(WMS): 新增dispatchTasksToWCS.vue批量下发弹窗组件 - 工具栏按钮触发弹窗 - 表格展示选中任务,可编辑地址和优先级 - 非可下发状态任务行标红且不可编辑 - 显示下发失败任务列表
feat: 新增任务管理功能及优化任务处理逻辑
1. 新增任务状态管理功能,支持根据任务ID修改状态
2. 新增移库任务完成功能,自动更新库存和货位状态
3. 新增机械手任务功能,支持组盘、换盘、拆盘操作
4. 新增自动出库任务功能,定时处理到期库存
5. 新增空托盘任务功能,支持空托盘出入库
6. 新增手动任务功能,支持手动创建和下发任务
7. 优化任务下发逻辑,增加重复任务检测
8. 前端任务下发界面增加托盘号编辑功能
9. 新增任务结果DTO,统一任务处理结果格式
10. 优化任务状态枚举和描述信息
fix(WMS): 合并gridBody组件解决弹窗无法打开问题 - 创建gridBodyExtension.vue组合手动创建任务和手动下发任务两个弹窗 - 删除独立的dispatchTasksToWCS.vue - task.js改为引用gridBodyExtension
fix(WMS): 修复DispatchTasksToWCSAsync编译错误 - 修正wcsResult?.Message为wcsResult?.Data?.Message - 修正Error方法参数数量(只接受1个参数)
feat(WMS): task.js添加工具栏'手动下发'按钮 - 引入dispatchTasksToWCS.vue组件 - 点击按钮获取选中任务并打开下发弹窗
fix(WMS): 修复dispatchTasksToWCS组件问题 - 修正API路由为/api/Task/DispatchTasksToWCS - 添加取消按钮 - 删除未使用的dispatchableStatuses
fix(WMS): ITaskService接口添加DispatchTasksToWCSAsync声明
feat(WMS): TaskController新增DispatchTasksToWCS接口 POST /api/TaskInfo/DispatchTasksToWCS
fix(WMS): 修复DispatchTasksToWCS数据一致性问题 - 先调用WCS再更新DB
feat(WMS): TaskService新增DispatchTasksToWCSAsync方法 - 校验任务状态,仅入库新单/出库新单/移库新单可下发 - 更新任务地址和优先级后调用WCS ReceiveManualTask接口 - 返回批量下发结果(成功/失败列表及原因)
feat(WMS): 新增手动下发任务Dto - DispatchTaskDto: 下发请求参数 - DispatchTaskResultDto: 单个任务下发结果 - DispatchResultDto: 批量下发结果
fix: 修复手动下发任务计划中的三个问题 1. 修正API路由为/api/TaskInfo/DispatchTasksToWCS 2. TaskId类型改为long与SPEC一致 3. 修正前端Vue组件中任务类型/状态枚举值
docs: 添加手动下发任务到WCS功能实施计划
docs: 添加手动下发任务到WCS功能设计文档
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | { |
| | | "lastSentAt": "2026-04-12T12:35:52.350Z" |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | namespace WIDESEAWCS_DTO.TaskInfo |
| | | { |
| | | /// <summary> |
| | | /// WCSæ¥æ¶WMSä»»å¡çç»æDto |
| | | /// </summary> |
| | | public class ReceiveTaskResultDto |
| | | { |
| | | /// <summary> |
| | | /// æ¯å¦æå |
| | | /// </summary> |
| | | public bool Success { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¶æ¯ |
| | | /// </summary> |
| | | public string Message { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°å»ºç任塿°é |
| | | /// </summary> |
| | | public int CreatedCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// éå¤/å·²åå¨çä»»å¡å表ï¼åªå
å«ä»»å¡å·åæçå·ï¼ |
| | | /// </summary> |
| | | public List<DuplicateTaskDto> DuplicateTasks { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éå¤ä»»å¡ä¿¡æ¯Dto |
| | | /// </summary> |
| | | public class DuplicateTaskDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | public int TaskNum { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçå· |
| | | /// </summary> |
| | | public string PalletCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡ç±»å |
| | | /// </summary> |
| | | public int TaskType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å½åç¶æ |
| | | /// </summary> |
| | | public int TaskStatus { get; set; } |
| | | } |
| | | } |
| | |
| | | WebResponseContent content = new WebResponseContent(); |
| | | try |
| | | { |
| | | // æ¶ééå¤ä»»å¡çä¿¡æ¯ |
| | | var duplicateTasks = new List<DuplicateTaskDto>(); |
| | | // ååºéåï¼å®å
¨å é¤å¹¶æ¶é被移é¤ç项 |
| | | for (int i = taskDTOs.Count - 1; i >= 0; i--) |
| | | { |
| | | var item = taskDTOs[i]; |
| | | var exists = BaseDal.QueryFirst(x => x.TaskNum == item.TaskNum || x.PalletCode == item.PalletCode); |
| | | if (exists != null) |
| | | { |
| | | duplicateTasks.Add(new DuplicateTaskDto |
| | | { |
| | | TaskNum = exists.TaskNum, |
| | | PalletCode = exists.PalletCode, |
| | | TaskType = exists.TaskType, |
| | | TaskStatus = exists.TaskStatus |
| | | }); |
| | | taskDTOs.RemoveAt(i); |
| | | } |
| | | } |
| | | |
| | | // è°ç¨ ReceiveWMSTask å建 WCS ä»»å¡ |
| | | content = ReceiveWMSTask(taskDTOs); |
| | | |
| | | // 妿æéå¤ä»»å¡ï¼ä¿®æ¹è¿åç»æ |
| | | if (duplicateTasks.Count > 0 && content.Status) |
| | | { |
| | | var result = new ReceiveTaskResultDto |
| | | { |
| | | Success = true, |
| | | Message = content.Message + $"ï¼å
¶ä¸{duplicateTasks.Count}个任å¡å¨WCSä¸å·²åå¨", |
| | | CreatedCount = taskDTOs.Count, |
| | | DuplicateTasks = duplicateTasks |
| | | }; |
| | | content.Data = result; |
| | | } |
| | | |
| | | return content; |
| | | } |
| | | catch (Exception ex) |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <!-- æå¨å建任å¡å¼¹çª --> |
| | | <vol-box v-model="showManualCreate" :lazy="true" width="500px" :padding="15" title="æå¨å建任å¡"> |
| | | <el-form :model="manualFormData" ref="form" label-width="100px"> |
| | | <el-form-item label="ä»»å¡ç±»å" prop="taskType" required> |
| | | <el-select v-model="manualFormData.taskType" placeholder="è¯·éæ©ä»»å¡ç±»å"> |
| | | <el-option label="å
¥åº" value="å
¥åº"></el-option> |
| | | <el-option label="åºåº" value="åºåº"></el-option> |
| | | <el-option label="ç§»åº" value="ç§»åº"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="èµ·ç¹å°å" prop="sourceAddress" required> |
| | | <el-input v-model="manualFormData.sourceAddress" placeholder="请è¾å
¥èµ·ç¹å°å"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="ç»ç¹å°å" prop="targetAddress" required> |
| | | <el-input v-model="manualFormData.targetAddress" placeholder="请è¾å
¥ç»ç¹å°å"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="æ¡ç " prop="barcode" required> |
| | | <el-input v-model="manualFormData.barcode" placeholder="请è¾å
¥æ¡ç "></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="ä»åºID" prop="warehouseId" required> |
| | | <el-input v-model="manualFormData.warehouseId" placeholder="请è¾å
¥ä»åºID"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼å
级"> |
| | | <el-input v-model="manualFormData.grade" readonly></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" size="small" @click="submitManualCreate">ç¡®å®</el-button> |
| | | <el-button type="danger" size="small" @click="showManualCreate = false">å
³é</el-button> |
| | | </template> |
| | | </vol-box> |
| | | |
| | | <!-- æå¨ä¸åä»»å¡å¼¹çª --> |
| | | <vol-box v-model="showDispatch" :lazy="true" width="900px" :padding="15" title="æå¨ä¸åä»»å¡å° WCS"> |
| | | <div v-if="dispatchRows.length > 0" class="dispatch-info"> |
| | | å·²é任塿°: <span class="count">{{ dispatchRows.length }}</span> 个 |
| | | </div> |
| | | |
| | | <el-table :data="dispatchTableData" border style="width: 100%; margin-top: 10px" max-height="400"> |
| | | <el-table-column prop="taskNum" label="ä»»å¡å·" width="120"></el-table-column> |
| | | <el-table-column prop="palletCode" label="æçå·" width="140"> |
| | | <template v-slot="{ row }"> |
| | | <el-input v-if="row && row.editable" v-model="row.palletCode" size="small" placeholder="请è¾å
¥"></el-input> |
| | | <span v-else>{{ row ? row.palletCode : '' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="sourceAddress" label="èµ·ç¹å°å" width="160"> |
| | | <template v-slot="{ row }"> |
| | | <el-input v-if="row && row.editable" v-model="row.sourceAddress" size="small" placeholder="请è¾å
¥"></el-input> |
| | | <span v-else>{{ row ? row.sourceAddress : '' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="targetAddress" label="ç»ç¹å°å" width="160"> |
| | | <template v-slot="{ row }"> |
| | | <el-input v-if="row && row.editable" v-model="row.targetAddress" size="small" placeholder="请è¾å
¥"></el-input> |
| | | <span v-else>{{ row ? row.targetAddress : '' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="grade" label="ä¼å
级" width="100"> |
| | | <template v-slot="{ row }"> |
| | | <el-input-number v-if="row && row.editable" v-model="row.grade" :min="1" :max="99" size="small" style="width: 80px"></el-input-number> |
| | | <span v-else>{{ row ? row.grade : '' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="statusName" label="ç¶æ" width="120"> |
| | | <template v-slot="{ row }"> |
| | | <span :class="{ 'status-error': row && !row.editable }">{{ row ? row.statusName : '' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div v-if="dispatchFailResults && dispatchFailResults.length > 0" class="fail-results"> |
| | | <div class="fail-title">ä¸å失败任å¡ï¼</div> |
| | | <el-table :data="dispatchFailResults" border style="width: 100%; margin-top: 10px" max-height="200"> |
| | | <el-table-column prop="taskNum" label="ä»»å¡å·" width="120"></el-table-column> |
| | | <el-table-column prop="errorMessage" label="失败åå "></el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <el-button type="primary" size="small" @click="submitDispatch" :loading="dispatchLoading">确认ä¸å</el-button> |
| | | <el-button type="danger" size="small" @click="showDispatch = false">åæ¶</el-button> |
| | | </template> |
| | | </vol-box> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import VolBox from "@/components/basic/VolBox.vue"; |
| | | |
| | | export default { |
| | | components: { VolBox }, |
| | | emits: ["parentCall"], |
| | | data() { |
| | | return { |
| | | // æå¨åå»ºä»»å¡ |
| | | showManualCreate: false, |
| | | manualFormData: { |
| | | taskType: "", |
| | | sourceAddress: "", |
| | | targetAddress: "", |
| | | barcode: "", |
| | | warehouseId: "", |
| | | grade: 1, |
| | | }, |
| | | // æå¨ä¸åä»»å¡ |
| | | showDispatch: false, |
| | | dispatchLoading: false, |
| | | dispatchRows: [], |
| | | dispatchTableData: [], |
| | | dispatchFailResults: [], |
| | | dispatchOriginalData: [], |
| | | }; |
| | | }, |
| | | methods: { |
| | | // æå¨åå»ºä»»å¡ - æå¼ |
| | | open() { |
| | | this.showManualCreate = true; |
| | | this.resetManualForm(); |
| | | }, |
| | | resetManualForm() { |
| | | this.manualFormData = { |
| | | taskType: "", |
| | | sourceAddress: "", |
| | | targetAddress: "", |
| | | barcode: "", |
| | | warehouseId: "", |
| | | grade: 1, |
| | | }; |
| | | }, |
| | | submitManualCreate() { |
| | | if (!this.manualFormData.taskType) return this.$message.error("è¯·éæ©ä»»å¡ç±»å"); |
| | | if (!this.manualFormData.sourceAddress) return this.$message.error("请è¾å
¥èµ·ç¹å°å"); |
| | | if (!this.manualFormData.targetAddress) return this.$message.error("请è¾å
¥ç»ç¹å°å"); |
| | | if (!this.manualFormData.barcode) return this.$message.error("请è¾å
¥æ¡ç "); |
| | | if (!this.manualFormData.warehouseId) return this.$message.error("请è¾å
¥ä»åºID"); |
| | | |
| | | this.http |
| | | .post("/api/Task/CreateManualTask", this.manualFormData, "æ°æ®å¤çä¸...") |
| | | .then((res) => { |
| | | if (!res.status) return this.$message.error(res.message); |
| | | this.$message.success("ä»»å¡å建æå"); |
| | | this.showManualCreate = false; |
| | | this.$emit("parentCall", ($vue) => { $vue.refresh(); }); |
| | | }); |
| | | }, |
| | | // æå¨ä¸åä»»å¡ - æå¼ |
| | | openDispatch(rows) { |
| | | console.log('openDispatch called with rows:', rows); |
| | | console.log('rows type:', typeof rows, Array.isArray(rows)); |
| | | // ç¡®ä¿æ¯æ°ç» |
| | | if (!rows) { |
| | | this.dispatchRows = []; |
| | | } else if (Array.isArray(rows)) { |
| | | this.dispatchRows = rows; |
| | | } else { |
| | | // 妿æ¯å个对象ï¼å
è£
ææ°ç» |
| | | this.dispatchRows = [rows]; |
| | | } |
| | | console.log('dispatchRows length:', this.dispatchRows.length); |
| | | this.dispatchFailResults = []; |
| | | // å
åå§åæ°æ®ï¼åæ¾ç¤ºå¼¹çª |
| | | this.initDispatchTableData(); |
| | | this.showDispatch = true; |
| | | }, |
| | | initDispatchTableData() { |
| | | console.log('initDispatchTableData dispatchRows:', this.dispatchRows); |
| | | console.log('dispatchRows isArray:', Array.isArray(this.dispatchRows), 'length:', this.dispatchRows?.length); |
| | | |
| | | // ç¡®ä¿ dispatchRows æ¯æ°ç» |
| | | if (!this.dispatchRows || !Array.isArray(this.dispatchRows)) { |
| | | console.warn('dispatchRows 䏿¯æææ°ç»ï¼é置为空æ°ç»'); |
| | | this.dispatchTableData = []; |
| | | return; |
| | | } |
| | | |
| | | const statusNames = { |
| | | 'inbound_200': 'å
¥åºæ°å', |
| | | 'outbound_100': 'åºåºæ°å', |
| | | 'relocation_300': 'ç§»åºæ°å', |
| | | 'outbound_110': 'å åæºåºåºæ§è¡ä¸', |
| | | 'outbound_115': 'å åæºåºåºå®æ', |
| | | 'inbound_220': 'è¾é线å
¥åºæ§è¡ä¸', |
| | | 'inbound_230': 'å åæºå
¥åºæ§è¡ä¸', |
| | | 'inbound_290': 'å
¥åºä»»å¡å®æ', |
| | | 'outbound_120': 'è¾é线åºåºæ§è¡ä¸', |
| | | 'outbound_125': 'è¾é线åºåºå®æ', |
| | | 'outbound_200': 'åºåºä»»å¡å®æ', |
| | | 'relocation_310': 'å åæºç§»åºæ§è¡ä¸' |
| | | }; |
| | | |
| | | const result = []; |
| | | const original = []; |
| | | let filteredCount = 0; // è®°å½è¢«è¿æ»¤ç任塿°é |
| | | for (const row of this.dispatchRows) { |
| | | console.log('Processing row:', row); |
| | | // Handle possible field name differences (camelCase vs PascalCase) |
| | | const taskId = row.taskId ?? row.Id ?? row.id ?? 0; |
| | | const taskType = row.taskType ?? row.TaskType ?? row.task_type ?? 0; |
| | | const taskStatus = row.taskStatus ?? row.TaskStatus ?? row.task_status ?? 0; |
| | | console.log('taskId:', taskId, 'taskType:', taskType, 'taskStatus:', taskStatus); |
| | | const statusKey = this.getStatusKey(taskType, taskStatus); |
| | | console.log('statusKey:', statusKey); |
| | | const statusName = statusNames[statusKey] || `ç¶æ${taskStatus}`; |
| | | const editable = this.isEditable(taskType, taskStatus); |
| | | console.log('editable:', editable, 'æ ¹æ® taskType:', taskType, 'taskStatus:', taskStatus); |
| | | |
| | | // éæ°å»ºç¶æçä»»å¡ä¸æ¾ç¤ºå¨å¼¹çªä¸ |
| | | if (!editable) { |
| | | console.log(`ä»»å¡${taskId}ç¶æ[${statusName}]䏿¯æ°å»ºï¼å·²è¿æ»¤`); |
| | | filteredCount++; |
| | | continue; |
| | | } |
| | | |
| | | const item = { |
| | | taskId: taskId, |
| | | taskNum: row.taskNum ?? row.TaskNum ?? 0, |
| | | sourceAddress: row.sourceAddress || row.SourceAddress || '', |
| | | targetAddress: row.targetAddress || row.TargetAddress || '', |
| | | grade: row.grade ?? row.Grade ?? 1, |
| | | statusName: statusName, |
| | | palletCode: row.palletCode || row.PalletCode || '', |
| | | editable: editable, |
| | | taskType: taskType, |
| | | taskStatus: taskStatus |
| | | }; |
| | | result.push(item); |
| | | // ä¿ååå§æ°æ®ç坿¬ |
| | | original.push({ ...item }); |
| | | } |
| | | this.dispatchTableData = result; |
| | | this.dispatchOriginalData = original; |
| | | console.log('dispatchTableData result:', this.dispatchTableData); |
| | | console.log('dispatchOriginalData:', this.dispatchOriginalData); |
| | | |
| | | // 妿æè¢«è¿æ»¤çä»»å¡ï¼æ¾ç¤ºæç¤º |
| | | if (filteredCount > 0) { |
| | | this.$message.warning(`éä¸ç${this.dispatchRows.length}个任å¡ä¸æ${filteredCount}ä¸ªéæ°å»ºç¶æä»»å¡å·²è¿æ»¤`); |
| | | } |
| | | }, |
| | | getStatusKey(taskType, taskStatus) { |
| | | if (taskType === 200) return `inbound_${taskStatus}`; |
| | | if (taskType === 100) return `outbound_${taskStatus}`; |
| | | if (taskType === 300) return `relocation_${taskStatus}`; |
| | | return `other_${taskStatus}`; |
| | | }, |
| | | isEditable(taskType, taskStatus) { |
| | | if (taskType === 200 && taskStatus === 200) return true; |
| | | if (taskType === 100 && taskStatus === 100) return true; |
| | | if (taskType === 300 && taskStatus === 300) return true; |
| | | return false; |
| | | }, |
| | | submitDispatch() { |
| | | if (this.dispatchTableData.length === 0) return this.$message.error("请å
鿩任å¡"); |
| | | |
| | | const dispatchData = this.dispatchTableData.map(row => ({ |
| | | taskId: row.taskId, |
| | | palletCode: row.palletCode, |
| | | sourceAddress: row.sourceAddress, |
| | | targetAddress: row.targetAddress, |
| | | grade: row.grade |
| | | })); |
| | | console.log('Dispatching data:', dispatchData); |
| | | |
| | | this.dispatchLoading = true; |
| | | this.http |
| | | .post("/api/Task/DispatchTasksToWCS", dispatchData, "æ°æ®å¤çä¸...") |
| | | .then((res) => { |
| | | this.dispatchLoading = false; |
| | | if (!res.status) { |
| | | this.$message.error(res.message); |
| | | if (res.data && res.data.failResults) { |
| | | this.dispatchFailResults = res.data.failResults; |
| | | } |
| | | return; |
| | | } |
| | | |
| | | if (res.data && res.data.failCount === 0) { |
| | | this.$message.success(res.message); |
| | | this.showDispatch = false; |
| | | this.$emit("parentCall", ($vue) => { $vue.refresh(); }); |
| | | return; |
| | | } |
| | | |
| | | if (res.data && res.data.failResults) { |
| | | this.dispatchFailResults = res.data.failResults; |
| | | } |
| | | |
| | | if (res.data && res.data.failCount > 0 && res.data.successCount > 0) { |
| | | this.$message.warning(res.message); |
| | | } else { |
| | | this.$message.error(res.message); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | this.dispatchLoading = false; |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dispatch-info { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | .dispatch-info .count { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | .status-error { |
| | | color: #f56c6c; |
| | | } |
| | | .fail-results { |
| | | margin-top: 15px; |
| | | padding: 10px; |
| | | background: #fef0f0; |
| | | border-radius: 4px; |
| | | } |
| | | .fail-title { |
| | | font-size: 14px; |
| | | color: #f56c6c; |
| | | margin-bottom: 5px; |
| | | } |
| | | </style> |
| | |
| | | |
| | | //æ¤jsæä»¶æ¯ç¨æ¥èªå®ä¹æ©å±ä¸å¡ä»£ç ï¼å¯ä»¥æ©å±ä¸äºèªå®ä¹é¡µé¢æè
éæ°é
ç½®çæç代ç |
| | | import addManualTask from './extend/addManualTask.vue' |
| | | import gridBodyExtension from './extend/gridBodyExtension.vue' |
| | | |
| | | let extension = { |
| | | components: { |
| | | //æ¥è¯¢ç颿©å±ç»ä»¶ |
| | | gridHeader: '', |
| | | gridBody: addManualTask, |
| | | gridBody: gridBodyExtension, |
| | | gridFooter: '', |
| | | //æ°å»ºãç¼è¾å¼¹åºæ¡æ©å±ç»ä»¶ |
| | | modelHeader: '', |
| | | modelBody: '', |
| | | modelFooter: '' |
| | | modelFooter: '', |
| | | }, |
| | | tableAction: '', //æå®æå¼ 表çæé(è¿éå¡«å表å,é»è®¤ä¸ç¨å¡«å) |
| | | buttons: { view: [], box: [], detail: [] }, //æ©å±çæé® |
| | |
| | | this.$refs.gridBody.open(); |
| | | } |
| | | }); |
| | | //æ·»å "æå¨ä¸å"æé® |
| | | this.buttons.push({ |
| | | name: 'æå¨ä¸å', |
| | | icon: 'el-icon-s-promotion', |
| | | type: 'primary', |
| | | value: 'DispatchTasksToWCS', |
| | | onClick: () => { |
| | | let rows = this.$refs.table.getSelected(); |
| | | if (rows.length == 0) return this.$error("请å
鿩任å¡"); |
| | | this.$refs.gridBody.openDispatch(rows); |
| | | } |
| | | }); |
| | | |
| | | let TaskHandCancelBtn = this.buttons.find(x => x.value == 'TaskHandCancel'); |
| | | if (TaskHandCancelBtn) { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Text.Json.Serialization; |
| | | |
| | | namespace WIDESEA_DTO.Task |
| | | { |
| | | /// <summary> |
| | | /// æå¨ä¸åä»»å¡Dto |
| | | /// </summary> |
| | | public class DispatchTaskDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡ID |
| | | /// </summary> |
| | | [JsonPropertyName("taskId")] |
| | | public long TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçå· |
| | | /// </summary> |
| | | [JsonPropertyName("palletCode")] |
| | | public string PalletCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// èµ·ç¹å°å |
| | | /// </summary> |
| | | [JsonPropertyName("sourceAddress")] |
| | | public string SourceAddress { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç»ç¹å°å |
| | | /// </summary> |
| | | [JsonPropertyName("targetAddress")] |
| | | public string TargetAddress { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¼å
级 |
| | | /// </summary> |
| | | [JsonPropertyName("grade")] |
| | | public int Grade { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡ä¸åç»æDto |
| | | /// </summary> |
| | | public class DispatchTaskResultDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡ID |
| | | /// </summary> |
| | | [JsonPropertyName("taskId")] |
| | | public long TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | [JsonPropertyName("taskNum")] |
| | | public int TaskNum { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¯å¦æå |
| | | /// </summary> |
| | | [JsonPropertyName("success")] |
| | | public bool Success { get; set; } |
| | | |
| | | /// <summary> |
| | | /// éè¯¯ä¿¡æ¯ |
| | | /// </summary> |
| | | [JsonPropertyName("errorMessage")] |
| | | public string ErrorMessage { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¹éä¸åç»æDto |
| | | /// </summary> |
| | | public class DispatchResultDto |
| | | { |
| | | /// <summary> |
| | | /// æåæ°é |
| | | /// </summary> |
| | | [JsonPropertyName("successCount")] |
| | | public int SuccessCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 失败æ°é |
| | | /// </summary> |
| | | [JsonPropertyName("failCount")] |
| | | public int FailCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 失败任å¡å表 |
| | | /// </summary> |
| | | [JsonPropertyName("failResults")] |
| | | public List<DispatchTaskResultDto> FailResults { get; set; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | namespace WIDESEA_DTO.Task |
| | | { |
| | | /// <summary> |
| | | /// WCSæ¥æ¶WMSä»»å¡çç»æDto |
| | | /// </summary> |
| | | public class ReceiveTaskResultDto |
| | | { |
| | | /// <summary> |
| | | /// æ¯å¦æå |
| | | /// </summary> |
| | | public bool Success { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¶æ¯ |
| | | /// </summary> |
| | | public string Message { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°å»ºç任塿°é |
| | | /// </summary> |
| | | public int CreatedCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// éå¤/å·²åå¨çä»»å¡å表 |
| | | /// </summary> |
| | | public List<DuplicateTaskDto> DuplicateTasks { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éå¤ä»»å¡ä¿¡æ¯Dto |
| | | /// </summary> |
| | | public class DuplicateTaskDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | public int TaskNum { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçå· |
| | | /// </summary> |
| | | public string PalletCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡ç±»å |
| | | /// </summary> |
| | | public int TaskType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å½åç¶æ |
| | | /// </summary> |
| | | public int TaskStatus { get; set; } |
| | | } |
| | | } |
| | |
| | | /// <returns></returns> |
| | | Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto); |
| | | |
| | | /// <summary> |
| | | /// æå¨ä¸åä»»å¡å°WCS |
| | | /// </summary> |
| | | /// <param name="dtos">ä¸åä»»å¡åæ°å表</param> |
| | | /// <returns>æ¹éä¸åç»æ</returns> |
| | | Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos); |
| | | |
| | | |
| | | #region æå·åºä»»å¡æ¨¡å |
| | | /// <summary> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using Microsoft.Extensions.Configuration; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region èªå¨åºåºä»»å¡ |
| | | |
| | | /// <summary> |
| | | /// èªå¨å建åºåºä»»å¡ - æ¥è¯¢å°æåºåå¹¶åå»ºä»»å¡ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateAutoOutboundTasksAsync() |
| | | { |
| | | try |
| | | { |
| | | // 1. æ¥è¯¢å°æåºå |
| | | var expiredStocks = await _stockInfoService.Repository |
| | | .QueryDataNavAsync(s => s.OutboundDate <= DateTime.Now |
| | | && s.StockStatus == StockStatusEmun.å
¥åºå®æ.GetHashCode()); |
| | | |
| | | if (expiredStocks == null || !expiredStocks.Any()) |
| | | { |
| | | return WebResponseContent.Instance.OK("æ å°æåºåéè¦å¤ç"); |
| | | } |
| | | |
| | | // è¿æ»¤æä½ç½®ä¸ä½ç½®æåºåçè®°å½ |
| | | expiredStocks = expiredStocks |
| | | .Where(s => s.LocationDetails != null |
| | | && s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode()) |
| | | .ToList(); |
| | | |
| | | if (!expiredStocks.Any()) |
| | | { |
| | | return WebResponseContent.Instance.OK("æ ç¬¦åæ¡ä»¶çå°æåºå"); |
| | | } |
| | | |
| | | // 2. æ£æ¥å·²åå¨çä»»å¡ |
| | | var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList(); |
| | | var existingTasks = await Repository.QueryDataAsync(t => |
| | | palletCodes.Contains(t.PalletCode) |
| | | && (t.TaskStatus == TaskStatusEnum.New.GetHashCode() |
| | | || t.TaskStatus == TaskStatusEnum.SC_Executing.GetHashCode() |
| | | || t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode())); |
| | | |
| | | var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet(); |
| | | |
| | | // 3. çééè¦å¤ççåºå |
| | | var stocksToProcess = expiredStocks |
| | | .Where(s => !processedPallets.Contains(s.PalletCode)) |
| | | .ToList(); |
| | | |
| | | if (!stocksToProcess.Any()) |
| | | { |
| | | return WebResponseContent.Instance.OK("ææå°æåºåå·²åå¨ä»»å¡"); |
| | | } |
| | | |
| | | // 4. è·åé
ç½®çç®æ å°åæ å° |
| | | var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses") |
| | | .Get<Dictionary<string, List<string>>>() |
| | | ?? new Dictionary<string, List<string>>(); |
| | | |
| | | // 5. æ¹éåå»ºä»»å¡ |
| | | var taskList = new List<Dt_Task>(); |
| | | foreach (var stock in stocksToProcess) |
| | | { |
| | | // æ ¹æ®å··éç¡®å®ç®æ å°åï¼ä¼å
æ ¹æ® Remark ç¡®å®ï¼Remark ä¸ºç©ºåæ ¹æ®å··éé
ç½®ï¼ |
| | | var targetAddress = DetermineTargetAddressByRemark( |
| | | stock.Remark ?? "", |
| | | stock.LocationDetails?.RoadwayNo ?? "", |
| | | targetAddressMap); |
| | | |
| | | var task = new Dt_Task |
| | | { |
| | | WarehouseId = stock.WarehouseId, |
| | | PalletCode = stock.PalletCode, |
| | | PalletType = stock.PalletType, |
| | | SourceAddress = stock.LocationCode, |
| | | CurrentAddress = stock.LocationCode, |
| | | NextAddress = targetAddress, |
| | | TargetAddress = targetAddress, |
| | | Roadway = stock.LocationDetails?.RoadwayNo ?? "", |
| | | TaskType = TaskTypeEnum.Outbound.GetHashCode(), |
| | | TaskStatus = TaskStatusEnum.New.GetHashCode(), |
| | | Grade = 1, |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | Creater = "system_auto" |
| | | }; |
| | | taskList.Add(task); |
| | | } |
| | | |
| | | var transactionResult = await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | var addResult = await BaseDal.AddDataAsync(taskList) > 0; |
| | | if (!addResult) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æ¹éå建任å¡å¤±è´¥ï¼å
± {taskList.Count} 个任å¡"); |
| | | } |
| | | |
| | | // ä»»å¡å建æååï¼åæ¥éå®åºååè´§ä½ç¶æï¼é¿å
éå¤åé
|
| | | var stocksToUpdate = stocksToProcess |
| | | .Select(s => |
| | | { |
| | | s.StockStatus = StockStatusEmun.åºåºéå®.GetHashCode(); |
| | | return s; |
| | | }) |
| | | .ToList(); |
| | | |
| | | var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate); |
| | | if (!updateStockResult) |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä»»å¡å建æåï¼ä½åºåç¶ææ´æ°å¤±è´¥ï¼å
± {stocksToUpdate.Count} æ¡"); |
| | | } |
| | | |
| | | var locationsToUpdate = stocksToProcess |
| | | .Where(s => s.LocationDetails != null) |
| | | .GroupBy(s => s.LocationDetails.Id) |
| | | .Select(g => |
| | | { |
| | | var location = g.First().LocationDetails; |
| | | location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode(); |
| | | return location; |
| | | }) |
| | | .ToList(); |
| | | |
| | | if (locationsToUpdate.Any()) |
| | | { |
| | | var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate); |
| | | if (!updateLocationResult) |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä»»å¡å建æåï¼ä½è´§ä½ç¶ææ´æ°å¤±è´¥ï¼å
± {locationsToUpdate.Count} æ¡"); |
| | | } |
| | | } |
| | | |
| | | return WebResponseContent.Instance.OK(); |
| | | }); |
| | | if (!transactionResult.Status) |
| | | { |
| | | return transactionResult; |
| | | } |
| | | |
| | | // 6. éç¥ WCSï¼å¼æ¥ï¼ä¸å½±å主æµç¨ï¼ |
| | | _ = Task.Run(() => |
| | | { |
| | | try |
| | | { |
| | | var wmstaskDtos = _mapper.Map<List<WMSTaskDTO>>(taskList); |
| | | _httpClientHelper.Post<WebResponseContent>( |
| | | "http://localhost:9292/api/Task/ReceiveTask", |
| | | wmstaskDtos.ToJson()); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // WCS éç¥å¤±è´¥ä¸å½±åä»»å¡å建ï¼è®°å½æ¥å¿å³å¯ |
| | | Console.WriteLine($"WCS æ¹ééç¥å¤±è´¥ï¼ä»»å¡æ°é: {taskList.Count}, é误: {ex.Message}"); |
| | | } |
| | | }); |
| | | |
| | | return WebResponseContent.Instance.OK($"æåå建 {taskList.Count} 个åºåºä»»å¡", taskList.Count); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"èªå¨å建åºåºä»»å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion èªå¨åºåºä»»å¡ |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEA_Common.Constants; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Common.WareHouseEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region å
¥åºä»»å¡ |
| | | |
| | | /// <summary> |
| | | /// å建任å¡ï¼ç»çå
¥åºä»»å¡ã空æçååºä»»å¡ï¼ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateTaskInboundAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode); |
| | | if (content.Status) |
| | | { |
| | | return content; |
| | | } |
| | | |
| | | if (string.IsNullOrWhiteSpace(taskDto.PalletCode) || |
| | | string.IsNullOrWhiteSpace(taskDto.Roadway)) |
| | | { |
| | | return WebResponseContent.Instance.Error("æ æçä»»å¡è¯¦æ
"); |
| | | } |
| | | |
| | | if (taskDto.TaskType != TaskTypeEnum.Inbound && taskDto.TaskType != TaskTypeEnum.InEmpty) |
| | | { |
| | | return WebResponseContent.Instance.Error("æ æçä»»å¡è¯¦æ
"); |
| | | } |
| | | |
| | | // ä½¿ç¨ switch è¡¨è¾¾å¼æ å°ä»»å¡ç±»å |
| | | int taskInboundType = taskDto.TaskType switch |
| | | { |
| | | TaskTypeEnum.Inbound => TaskInboundTypeEnum.Inbound.GetHashCode(), |
| | | TaskTypeEnum.InEmpty => TaskInboundTypeEnum.InEmpty.GetHashCode(), |
| | | _ => 0 // ç论ä¸ä¸ä¼èµ°å°è¿éï¼å 为已ç»éªè¯è¿äº |
| | | }; |
| | | |
| | | var task = new Dt_Task |
| | | { |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | PalletCode = taskDto.PalletCode, |
| | | PalletType = taskDto.PalletType, |
| | | Roadway = taskDto.Roadway, |
| | | TaskType = taskInboundType, |
| | | TaskStatus = TaskInStatusEnum.InNew.GetHashCode(), |
| | | SourceAddress = taskDto.SourceAddress, |
| | | TargetAddress = taskDto.TargetAddress, |
| | | CurrentAddress = taskDto.SourceAddress, |
| | | NextAddress = taskDto.TargetAddress, |
| | | WarehouseId = taskDto.WarehouseId, |
| | | Grade = 1, |
| | | Creater = "system" |
| | | }; |
| | | |
| | | var result = await Repository.AddDataAsync(task) > 0; |
| | | if (!result) return WebResponseContent.Instance.Error("ä»»å¡å建失败"); |
| | | |
| | | var wmstaskDto = _mapper.Map<WMSTaskDTO>(task); |
| | | return WebResponseContent.Instance.OK("ä»»å¡å建æå", wmstaskDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä»»å¡å建失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åå¯å
¥åºè´§ä½ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> GetTasksLocationAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var locationInfo = await _locationInfoService.GetLocationInfo(task.Roadway); |
| | | if (locationInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode(); |
| | | task.CurrentAddress = task.SourceAddress; |
| | | task.NextAddress = locationInfo.LocationCode; |
| | | task.TargetAddress = locationInfo.LocationCode; |
| | | task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode(); |
| | | |
| | | var updateTaskResult = await BaseDal.UpdateDataAsync(task); |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo); |
| | | if (!updateTaskResult || !updateLocationResult) |
| | | { |
| | | return WebResponseContent.Instance.Error("任塿´æ°å¤±è´¥"); |
| | | } |
| | | |
| | | return WebResponseContent.Instance.OK("任塿´æ°æå", locationInfo.LocationCode); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"è·åä»»å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å
¥åºä»»å¡å®æï¼æ·»å åºåï¼ä¿®æ¹è´§ä½ç¶æï¼å é¤ä»»å¡æ°æ®ï¼æ·»å åå²ä»»å¡æ°æ® |
| | | /// </summary> |
| | | public async Task<WebResponseContent> InboundFinishTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | // 夿æ¯ä¸æ¯æå·åºä»»å¡ |
| | | if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1) |
| | | { |
| | | return await CompleteAgvInboundTaskAsync(taskDto); |
| | | } |
| | | |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | stockInfo.LocationCode = location.LocationCode; |
| | | stockInfo.LocationId = location.Id; |
| | | |
| | | SetOutboundDateByRoadway(task, stockInfo); |
| | | |
| | | stockInfo.StockStatus = StockStatusEmun.å
¥åºå®æ.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.InStock.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | // è°ç¨MESæçè¿ç« |
| | | //var inboundRequest = new InboundInContainerRequest |
| | | //{ |
| | | // EquipmentCode = "STK-GROUP-001", |
| | | // ResourceCode = "STK-GROUP-001", |
| | | // LocalTime = DateTime.Now, |
| | | // ContainerCode = taskDto.PalletCode |
| | | //}; |
| | | //var inboundResult = _mesService.InboundInContainer(inboundRequest); |
| | | //if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess) |
| | | //{ |
| | | // return content.Error($"ä»»å¡å®æå¤±è´¥ï¼MESè¿ç«å¤±è´¥: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | //} |
| | | return await CompleteTaskAsync(task, "å
¥åºå®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ ¹æ®å··éç±»å设置åºåçåºåºæ¶é´å夿³¨ |
| | | /// </summary> |
| | | /// <param name="task">ä»»å¡ä¿¡æ¯</param> |
| | | /// <param name="stockInfo">åºåä¿¡æ¯</param> |
| | | private void SetOutboundDateByRoadway(Dt_Task task, Dt_StockInfo stockInfo) |
| | | { |
| | | var now = DateTime.Now; |
| | | if (task.Roadway.Contains("GW")) |
| | | { |
| | | stockInfo.OutboundDate = string.IsNullOrEmpty(stockInfo.Remark) |
| | | ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST) |
| | | : stockInfo.Remark == StockRemarkConstants.GW1 |
| | | ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_SECOND) |
| | | : now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST); |
| | | |
| | | stockInfo.Remark = string.IsNullOrEmpty(stockInfo.Remark) |
| | | ? StockRemarkConstants.GW1 |
| | | : stockInfo.Remark == StockRemarkConstants.GW1 |
| | | ? StockRemarkConstants.GW2 |
| | | : stockInfo.Remark; |
| | | } |
| | | else if (task.Roadway.Contains("CW")) |
| | | { |
| | | stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_CW1); |
| | | if (stockInfo.Remark == StockRemarkConstants.GW2) |
| | | stockInfo.Remark = StockRemarkConstants.CW1; |
| | | } |
| | | else |
| | | { |
| | | stockInfo.OutboundDate = now; |
| | | } |
| | | } |
| | | |
| | | #endregion å
¥åºä»»å¡ |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.ComponentModel; |
| | | using System.Reflection; |
| | | using Newtonsoft.Json; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region æå¨ä»»å¡ |
| | | |
| | | /// <summary> |
| | | /// æå¨åå»ºä»»å¡ |
| | | /// </summary> |
| | | /// <param name="dto">æå¨å建任å¡åæ°</param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto) |
| | | { |
| | | try |
| | | { |
| | | int taskType; |
| | | int taskStatus; |
| | | switch (dto.TaskType) |
| | | { |
| | | case "å
¥åº": |
| | | taskType = TaskInboundTypeEnum.Inbound.GetHashCode(); |
| | | taskStatus = TaskInStatusEnum.InNew.GetHashCode(); |
| | | break; |
| | | |
| | | case "åºåº": |
| | | taskType = TaskOutboundTypeEnum.Outbound.GetHashCode(); |
| | | taskStatus = TaskOutStatusEnum.OutNew.GetHashCode(); |
| | | break; |
| | | |
| | | case "ç§»åº": |
| | | taskType = TaskRelocationTypeEnum.Relocation.GetHashCode(); |
| | | taskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode(); |
| | | break; |
| | | |
| | | default: |
| | | return WebResponseContent.Instance.Error($"䏿¯æçä»»å¡ç±»å: {dto.TaskType}"); |
| | | } |
| | | |
| | | int taskNum = await BaseDal.GetTaskNo(); |
| | | |
| | | var task = new Dt_Task |
| | | { |
| | | TaskNum = taskNum, |
| | | PalletCode = dto.Barcode, |
| | | SourceAddress = dto.SourceAddress, |
| | | TargetAddress = dto.TargetAddress, |
| | | TaskType = taskType, |
| | | TaskStatus = taskStatus, |
| | | Grade = dto.Grade, |
| | | Roadway = dto.TargetAddress, |
| | | WarehouseId = dto.WarehouseId, |
| | | CurrentAddress = dto.SourceAddress, |
| | | NextAddress = dto.TargetAddress, |
| | | Creater = "manual", |
| | | CreateDate = DateTime.Now, |
| | | ModifyDate = DateTime.Now |
| | | }; |
| | | |
| | | var wmsTaskDtos = new List<WMSTaskDTO>() |
| | | { |
| | | new() |
| | | { |
| | | TaskNum = task.TaskNum, |
| | | PalletCode = task.PalletCode, |
| | | SourceAddress = task.SourceAddress, |
| | | TargetAddress = task.TargetAddress, |
| | | TaskType = task.TaskType, |
| | | Roadway = task.Roadway, |
| | | TaskStatus = task.TaskStatus, |
| | | WarehouseId = task.WarehouseId |
| | | } |
| | | }; |
| | | |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | // ä¿åå°æ°æ®åºå¹¶åæ¥åéç»WCS |
| | | var result = await BaseDal.AddDataAsync(task) > 0; |
| | | if (!result) |
| | | return WebResponseContent.Instance.Error("å建任å¡å¤±è´¥"); |
| | | |
| | | var wcsResult = _httpClientHelper.Post<WebResponseContent>( |
| | | "http://localhost:9292/api/Task/ReceiveManualTask", |
| | | wmsTaskDtos.ToJson()); |
| | | |
| | | if (!wcsResult.IsSuccess || !wcsResult.Data.Status) |
| | | return WebResponseContent.Instance.Error($"ä»»å¡å·²å建ä½åéç»WCS失败: {wcsResult.Data?.Message}"); |
| | | |
| | | return WebResponseContent.Instance.OK($"æå¨åå»ºä»»å¡æåï¼ä»»å¡å·: {taskNum}"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æå¨å建任å¡å¼å¸¸: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå¨ä¸åä»»å¡å°WCSï¼æ¹éå¤çï¼ |
| | | /// </summary> |
| | | /// <param name="dtos">ä¸åä»»å¡åæ°å表</param> |
| | | /// <returns>æ¹éä¸åç»æ</returns> |
| | | public async Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos) |
| | | { |
| | | try |
| | | { |
| | | if (dtos == null || !dtos.Any()) |
| | | return WebResponseContent.Instance.Error("è¯·éæ©è¦ä¸åçä»»å¡"); |
| | | |
| | | var resultDto = new DispatchResultDto |
| | | { |
| | | SuccessCount = 0, |
| | | FailCount = 0, |
| | | FailResults = new List<DispatchTaskResultDto>() |
| | | }; |
| | | |
| | | // ç¬¬ä¸æ¥ï¼æ¥è¯¢å¹¶æ ¡éªææä»»å¡ç¶æï¼æ¶éææä»»å¡ |
| | | var validTasks = new List<(DispatchTaskDto Dto, Dt_Task Task)>(); |
| | | |
| | | foreach (var dto in dtos) |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(t => t.TaskId == dto.TaskId); |
| | | if (task == null) |
| | | { |
| | | resultDto.FailResults.Add(new DispatchTaskResultDto |
| | | { |
| | | TaskId = dto.TaskId, |
| | | TaskNum = 0, |
| | | Success = false, |
| | | ErrorMessage = "ä»»å¡ä¸åå¨" |
| | | }); |
| | | resultDto.FailCount++; |
| | | continue; |
| | | } |
| | | |
| | | // æ ¡éªä»»å¡ç¶æï¼ä»
å
¥åºæ°å/åºåºæ°å/ç§»åºæ°åå¯ä¸å |
| | | bool canDispatch = false; |
| | | if (task.TaskType == TaskInboundTypeEnum.Inbound.GetHashCode() |
| | | && task.TaskStatus == TaskInStatusEnum.InNew.GetHashCode()) |
| | | canDispatch = true; |
| | | else if (task.TaskType == TaskOutboundTypeEnum.Outbound.GetHashCode() |
| | | && task.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode()) |
| | | canDispatch = true; |
| | | else if (task.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode() |
| | | && task.TaskStatus == TaskRelocationStatusEnum.RelocationNew.GetHashCode()) |
| | | canDispatch = true; |
| | | |
| | | if (!canDispatch) |
| | | { |
| | | var statusName = GetTaskStatusName(task.TaskType, task.TaskStatus); |
| | | resultDto.FailResults.Add(new DispatchTaskResultDto |
| | | { |
| | | TaskId = dto.TaskId, |
| | | TaskNum = task.TaskNum, |
| | | Success = false, |
| | | ErrorMessage = $"ä»»å¡ç¶æ[{statusName}]ä¸å
许ä¸å" |
| | | }); |
| | | resultDto.FailCount++; |
| | | continue; |
| | | } |
| | | |
| | | validTasks.Add((dto, task)); |
| | | } |
| | | |
| | | // 妿å
¨é¨æ ¡éªå¤±è´¥ï¼ç´æ¥è¿å |
| | | if (validTasks.Count == 0) |
| | | return WebResponseContent.Instance.Error($"ä¸å失败ï¼å
±{resultDto.FailCount}个任å¡"); |
| | | |
| | | // ç¬¬äºæ¥ï¼æé ææWMSTaskDTOï¼ä¸æ¬¡æ§è°ç¨WCS |
| | | var wmsTaskDtos = validTasks.Select(vt => new WMSTaskDTO |
| | | { |
| | | Id = vt.Task.TaskId, |
| | | TaskNum = vt.Task.TaskNum, |
| | | PalletCode = vt.Dto.PalletCode, |
| | | SourceAddress = vt.Dto.SourceAddress, |
| | | TargetAddress = vt.Dto.TargetAddress, |
| | | CurrentAddress = vt.Dto.SourceAddress, |
| | | NextAddress = vt.Dto.TargetAddress, |
| | | TaskType = vt.Task.TaskType, |
| | | TaskStatus = vt.Task.TaskStatus, |
| | | Roadway = vt.Task.Roadway, |
| | | Grade = vt.Dto.Grade, |
| | | WarehouseId = vt.Task.WarehouseId, |
| | | PalletType = vt.Task.PalletType |
| | | }).ToList(); |
| | | |
| | | // 䏿¬¡æ§è°ç¨WCSæ¹éæ¥å£ |
| | | var wcsResult = _httpClientHelper.Post<WebResponseContent>( |
| | | "http://localhost:9292/api/Task/ReceiveManualTask", |
| | | wmsTaskDtos.ToJson()); |
| | | |
| | | if (wcsResult == null || !wcsResult.IsSuccess) |
| | | { |
| | | // WCSè°ç¨å¤±è´¥ï¼ææä»»å¡é½ç®å¤±è´¥ |
| | | // å°è¯ä»WCSååºä¸è§£æé误详æ
|
| | | string errorDetail = ""; |
| | | if (wcsResult?.Data != null) |
| | | { |
| | | // å°è¯å°Data转æ¢ä¸ºéè¯¯ä¿¡æ¯ |
| | | try |
| | | { |
| | | var errorData = wcsResult.Data.ToString(); |
| | | errorDetail = $"WCSé误: {errorData}"; |
| | | } |
| | | catch |
| | | { |
| | | errorDetail = wcsResult?.ErrorMessage ?? "WCSååºå¼å¸¸"; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | errorDetail = wcsResult?.ErrorMessage ?? "WCSååºå¼å¸¸"; |
| | | } |
| | | |
| | | foreach (var vt in validTasks) |
| | | { |
| | | resultDto.FailResults.Add(new DispatchTaskResultDto |
| | | { |
| | | TaskId = vt.Dto.TaskId, |
| | | TaskNum = vt.Task.TaskNum, |
| | | Success = false, |
| | | ErrorMessage = $"{errorDetail} (ä»»å¡å·:{vt.Task.TaskNum}, æç:{vt.Dto.PalletCode})" |
| | | }); |
| | | resultDto.FailCount++; |
| | | } |
| | | resultDto.SuccessCount = 0; |
| | | return WebResponseContent.Instance.Error($"WCSæ¹éä¸å失败ï¼å
±{resultDto.FailCount}个任å¡"); |
| | | } |
| | | |
| | | // WCSè°ç¨æåï¼è§£æè¿åçç»æåæ°æ® |
| | | ReceiveTaskResultDto wcsResultData = null; |
| | | try |
| | | { |
| | | if (wcsResult.Data?.Data != null) |
| | | { |
| | | var jsonStr = wcsResult.Data.Data.ToString(); |
| | | if (!string.IsNullOrEmpty(jsonStr) && jsonStr.Contains("duplicateTasks")) |
| | | { |
| | | wcsResultData = JsonConvert.DeserializeObject<ReceiveTaskResultDto>(jsonStr); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // è§£æWCSè¿åæ°æ®å¤±è´¥ï¼è®°å½æ¥å¿ä½ç»§ç»å¤ç |
| | | Console.WriteLine($"è§£æWCSè¿åæ°æ®å¼å¸¸: {ex.Message}"); |
| | | } |
| | | |
| | | // 妿æéå¤ä»»å¡ï¼è®°å½å°å¤±è´¥ç»æä¸ |
| | | if (wcsResultData?.DuplicateTasks != null && wcsResultData.DuplicateTasks.Count > 0) |
| | | { |
| | | foreach (var dup in wcsResultData.DuplicateTasks) |
| | | { |
| | | var statusName = GetTaskStatusName(dup.TaskType, dup.TaskStatus); |
| | | resultDto.FailResults.Add(new DispatchTaskResultDto |
| | | { |
| | | TaskId = 0, // éå¤ä»»å¡å¯è½æ²¡æWMSçTaskId |
| | | TaskNum = dup.TaskNum, |
| | | Success = false, |
| | | ErrorMessage = $"WCSä¸å·²åå¨è¯¥ä»»å¡(æç:{dup.PalletCode}, ç¶æ:{statusName})" |
| | | }); |
| | | resultDto.FailCount++; |
| | | } |
| | | } |
| | | |
| | | // ç¬¬ä¸æ¥ï¼WCSè°ç¨æååï¼æ¹éæ´æ°DB |
| | | foreach (var vt in validTasks) |
| | | { |
| | | vt.Task.PalletCode = vt.Dto.PalletCode; |
| | | vt.Task.SourceAddress = vt.Dto.SourceAddress; |
| | | vt.Task.TargetAddress = vt.Dto.TargetAddress; |
| | | vt.Task.CurrentAddress = vt.Dto.SourceAddress; |
| | | vt.Task.NextAddress = vt.Dto.TargetAddress; |
| | | vt.Task.Grade = vt.Dto.Grade; |
| | | vt.Task.Dispatchertime = DateTime.Now; |
| | | } |
| | | |
| | | // æ¹éæ´æ°DB |
| | | var tasksToUpdate = validTasks.Select(vt => vt.Task).ToList(); |
| | | await BaseDal.UpdateDataAsync(tasksToUpdate); |
| | | |
| | | resultDto.SuccessCount = validTasks.Count - (wcsResultData?.DuplicateTasks?.Count ?? 0); |
| | | |
| | | if (resultDto.FailCount == 0) |
| | | return WebResponseContent.Instance.OK($"æåä¸å{resultDto.SuccessCount}个任å¡", resultDto); |
| | | |
| | | var errorResponse = WebResponseContent.Instance.Error($"é¨åä¸åæå{resultDto.SuccessCount}个ï¼å¤±è´¥{resultDto.FailCount}个"); |
| | | errorResponse.Data = resultDto; |
| | | return errorResponse; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä¸åä»»å¡å¼å¸¸: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åä»»å¡ç¶æåç§° |
| | | /// </summary> |
| | | /// <param name="taskType">ä»»å¡ç±»å</param> |
| | | /// <param name="taskStatus">ä»»å¡ç¶æ</param> |
| | | /// <returns>ç¶æç䏿æè¿°</returns> |
| | | private string GetTaskStatusName(int taskType, int taskStatus) |
| | | { |
| | | FieldInfo? fieldInfo = taskType switch |
| | | { |
| | | _ when taskType == TaskInboundTypeEnum.Inbound.GetHashCode() => typeof(TaskInStatusEnum).GetField(((TaskInStatusEnum)taskStatus).ToString()), |
| | | _ when taskType == TaskOutboundTypeEnum.Outbound.GetHashCode() => typeof(TaskOutStatusEnum).GetField(((TaskOutStatusEnum)taskStatus).ToString()), |
| | | _ when taskType == TaskRelocationTypeEnum.Relocation.GetHashCode() => typeof(TaskRelocationStatusEnum).GetField(((TaskRelocationStatusEnum)taskStatus).ToString()), |
| | | _ => null |
| | | }; |
| | | |
| | | var descAttr = fieldInfo?.GetCustomAttributes(typeof(DescriptionAttribute), false) |
| | | .FirstOrDefault() as DescriptionAttribute; |
| | | return descAttr?.Description ?? taskStatus.ToString(); |
| | | } |
| | | |
| | | #endregion æå¨ä»»å¡ |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEA_Common.Constants; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Common.WareHouseEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region åºåºä»»å¡ |
| | | |
| | | /// <summary> |
| | | /// æ ¹æ®æå®çä»»å¡è¯¦æ
弿¥å建æ°çåºåºä»»å¡ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateTaskOutboundAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var stockResult = await _stockInfoService.GetStockInfoAsync(taskDto.WarehouseId); |
| | | if (stockResult == null || !stockResult.Any()) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°åºåä¿¡æ¯"); |
| | | |
| | | var taskList = stockResult.Select(item => new Dt_Task |
| | | { |
| | | WarehouseId = item.WarehouseId, |
| | | PalletCode = item.PalletCode, |
| | | PalletType = item.PalletType, |
| | | SourceAddress = item.LocationCode, |
| | | TargetAddress = taskDto.TargetAddress, |
| | | Roadway = item.LocationDetails.RoadwayNo, |
| | | TaskType = TaskTypeEnum.Outbound.GetHashCode(), |
| | | TaskStatus = TaskStatusEnum.New.GetHashCode(), |
| | | Grade = 1, |
| | | TaskNum = 0, |
| | | CurrentAddress = item.LocationCode, |
| | | NextAddress = taskDto.TargetAddress, |
| | | Creater = "system", |
| | | }).ToList(); |
| | | |
| | | var result = await BaseDal.AddDataAsync(taskList) > 0; |
| | | var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null; |
| | | return WebResponseContent.Instance.OK(result ? "ä»»å¡å建æå" : "ä»»å¡å建失败", wmstaskDto ?? new object()); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä»»å¡å建失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åºåºä»»å¡å®æ ï¼ä¿®æ¹åºåï¼ä¿®æ¹è´§ä½ç¶æï¼å é¤ä»»å¡æ°æ®ï¼æ·»å åå²ä»»å¡æ°æ® |
| | | /// </summary> |
| | | public async Task<WebResponseContent> OutboundFinishTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | // 夿æ¯ä¸æ¯æå·åºä»»å¡ |
| | | if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1) |
| | | { |
| | | OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto() |
| | | { |
| | | TaskId = task.OrderNo ?? string.Empty, |
| | | DevId = task.TargetAddress ?? string.Empty, |
| | | ReqTime = DateTime.Now.ToString() |
| | | }; |
| | | return await OutTaskComplete(outTaskCompleteDto); |
| | | } |
| | | |
| | | WebResponseContent content = new WebResponseContent(); |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | stockInfo.LocationId = 0; |
| | | stockInfo.LocationCode = string.Empty; |
| | | stockInfo.OutboundDate = DateTime.Now; |
| | | |
| | | location.LocationStatus = LocationStatusEnum.Free.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | // 髿¸©2å·åºåºå°CWSC1æ¶ï¼èªå¨å建å
¥åºä»»å¡å°å¸¸æ¸©1å·å··é |
| | | WMSTaskDTO? inboundTaskDto = null; |
| | | if (task.TargetAddress == TaskAddressConstants.GW2_ADDRESS) |
| | | { |
| | | var inboundTask = new Dt_Task |
| | | { |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | PalletCode = task.PalletCode, |
| | | PalletType = task.PalletType, |
| | | Roadway = "CW1", |
| | | TaskType = TaskInboundTypeEnum.Inbound.GetHashCode(), |
| | | TaskStatus = TaskInStatusEnum.InNew.GetHashCode(), |
| | | SourceAddress = task.TargetAddress, |
| | | TargetAddress = task.TargetAddress, |
| | | CurrentAddress = task.TargetAddress, |
| | | NextAddress = task.TargetAddress, |
| | | WarehouseId = (int)WarehouseEnum.CW1, |
| | | Grade = 1, |
| | | Creater = "system_auto" |
| | | }; |
| | | await Repository.AddDataAsync(inboundTask); |
| | | inboundTaskDto = _mapper.Map<WMSTaskDTO>(inboundTask); |
| | | } |
| | | |
| | | var completeResult = await CompleteTaskAsync(task, "åºåºå®æ"); |
| | | if (!completeResult.Status) |
| | | return completeResult; |
| | | |
| | | // è¿åå
¥åºä»»å¡ä¿¡æ¯ï¼å¦ææï¼ |
| | | if (inboundTaskDto != null) |
| | | { |
| | | return content.OK("åºåºå®æï¼å·²å建å
¥åºä»»å¡", inboundTaskDto); |
| | | } |
| | | return content.OK("åºåºå®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion åºåºä»»å¡ |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.Task; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region ç§»åºä»»å¡ |
| | | |
| | | /// <summary> |
| | | /// ç§»åºä»»å¡å®æï¼ä¿®æ¹åºåä½ç½®ä¸ç¶æï¼ä¿®æ¹æº/ç®æ è´§ä½ç¶æï¼å é¤ä»»å¡æ°æ® |
| | | /// </summary> |
| | | public async Task<WebResponseContent> RelocationFinishTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => |
| | | s.PalletCode == taskDto.PalletCode && |
| | | s.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode()); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçç§»åºä»»å¡"); |
| | | |
| | | var sourceLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress); |
| | | if (sourceLocation == null) return WebResponseContent.Instance.Error("æªæ¾å°ç§»åºæºè´§ä½"); |
| | | |
| | | var targetLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress); |
| | | if (targetLocation == null) return WebResponseContent.Instance.Error("æªæ¾å°ç§»åºç®æ è´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | stockInfo.LocationCode = targetLocation.LocationCode; |
| | | stockInfo.LocationId = targetLocation.Id; |
| | | stockInfo.StockStatus = StockStatusEmun.å
¥åºå®æ.GetHashCode(); |
| | | |
| | | sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode(); |
| | | targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode(); |
| | | |
| | | var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation); |
| | | var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | |
| | | if (!updateSourceResult || !updateTargetResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ç§»åºä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return await CompleteTaskAsync(task, "ç§»åºå®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion ç§»åºä»»å¡ |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.Stock; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region æºæ¢°æä»»å¡ |
| | | |
| | | /// <summary> |
| | | /// åå»ºæºæ¢°æç»çä»»å¡ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateRobotGroupPalletTaskAsync(StockDTO stock) |
| | | { |
| | | return await CreateRobotPalletTaskAsync( |
| | | stock, |
| | | "ç»ç", |
| | | RobotTaskTypeEnum.GroupPallet, |
| | | s => string.IsNullOrWhiteSpace(s.TargetPalletNo) ? s.SourcePalletNo : s.TargetPalletNo, |
| | | requireStockWithoutLocation: false); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åå»ºæºæ¢°ææ¢çä»»å¡ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock) |
| | | { |
| | | return await CreateRobotPalletTaskAsync( |
| | | stock, |
| | | "æ¢ç", |
| | | RobotTaskTypeEnum.ChangePallet, |
| | | s => s.SourcePalletNo, |
| | | requireStockWithoutLocation: true, |
| | | stockPalletCodeSelector: s => s.SourcePalletNo); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åå»ºæºæ¢°ææçä»»å¡ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateRobotSplitPalletTaskAsync(StockDTO stock) |
| | | { |
| | | return await CreateRobotPalletTaskAsync( |
| | | stock, |
| | | "æç", |
| | | RobotTaskTypeEnum.SplitPallet, |
| | | s => s.SourcePalletNo, |
| | | requireStockWithoutLocation: true, |
| | | stockPalletCodeSelector: s => s.SourcePalletNo); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åå»ºæºæ¢°æä»»å¡çéç¨æ¹æ³ |
| | | /// </summary> |
| | | /// <param name="stock">åºåä¿¡æ¯</param> |
| | | /// <param name="taskName">ä»»å¡åç§°ï¼ç»ç/æ¢ç/æçï¼</param> |
| | | /// <param name="taskType">æºæ¢°æä»»å¡ç±»å</param> |
| | | /// <param name="palletCodeSelector">æçå·éæ©å¨</param> |
| | | /// <param name="requireStockWithoutLocation">æ¯å¦è¦æ±åºåæªç»å®è´§ä½</param> |
| | | /// <param name="stockPalletCodeSelector">åºåæ¥è¯¢ç¨çæçå·éæ©å¨</param> |
| | | /// <returns>åå»ºç»æ</returns> |
| | | private async Task<WebResponseContent> CreateRobotPalletTaskAsync( |
| | | StockDTO stock, |
| | | string taskName, |
| | | RobotTaskTypeEnum taskType, |
| | | Func<StockDTO, string?> palletCodeSelector, |
| | | bool requireStockWithoutLocation, |
| | | Func<StockDTO, string?>? stockPalletCodeSelector = null) |
| | | { |
| | | try |
| | | { |
| | | if (stock == null) |
| | | return WebResponseContent.Instance.Error("ä»»å¡åæ°ä¸è½ä¸ºç©º"); |
| | | |
| | | var palletCode = palletCodeSelector(stock)?.Trim(); |
| | | if (string.IsNullOrWhiteSpace(palletCode)) |
| | | return WebResponseContent.Instance.Error("æçå·ä¸è½ä¸ºç©º"); |
| | | |
| | | var sourceLineNo = stock.SourceLineNo?.Trim(); |
| | | var targetLineNo = stock.TargetLineNo?.Trim(); |
| | | if (string.IsNullOrWhiteSpace(sourceLineNo) || string.IsNullOrWhiteSpace(targetLineNo)) |
| | | return WebResponseContent.Instance.Error("æ¥æºçº¿ä½ç¼å·åç®æ 线ä½ç¼å·ä¸è½ä¸ºç©º"); |
| | | |
| | | var existingTask = await BaseDal.QueryFirstAsync(t => |
| | | t.PalletCode == palletCode && |
| | | (t.TaskStatus == TaskRobotStatusEnum.RobotNew.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotExecuting.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotPickFinish.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotPutFinish.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotPending.GetHashCode())); |
| | | if (existingTask != null) |
| | | return WebResponseContent.Instance.Error($"æç[{palletCode}]å·²å卿ªå®æä»»å¡"); |
| | | |
| | | Dt_StockInfo? stockInfo = null; |
| | | if (requireStockWithoutLocation) |
| | | { |
| | | var stockPalletCode = (stockPalletCodeSelector ?? palletCodeSelector).Invoke(stock)?.Trim(); |
| | | if (string.IsNullOrWhiteSpace(stockPalletCode)) |
| | | return WebResponseContent.Instance.Error("æºæçå·ä¸è½ä¸ºç©º"); |
| | | |
| | | stockInfo = await _stockInfoService.GetStockInfoAsync(stockPalletCode); |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error($"æç[{stockPalletCode}]åºåä¸åå¨"); |
| | | |
| | | if (stockInfo.LocationId > 0 || !string.IsNullOrWhiteSpace(stockInfo.LocationCode)) |
| | | return WebResponseContent.Instance.Error($"æç[{stockPalletCode}]åºåå·²ç»å®è´§ä½ï¼ä¸è½åå»ºæºæ¢°æ{taskName}ä»»å¡"); |
| | | } |
| | | var section = App.Configuration.GetSection("RobotTaskAddressRules").GetSection(targetLineNo).GetChildren().Select(c => c.Value).ToArray(); |
| | | if (section.Length < 2) |
| | | return WebResponseContent.Instance.Error($"æªæ¾å°çº¿ä½[{targetLineNo}]çå°åé
ç½®"); |
| | | |
| | | var task = new Dt_Task |
| | | { |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | PalletCode = palletCode, |
| | | PalletType = stockInfo?.PalletType ?? 0, |
| | | Roadway = stock.Roadway ?? string.Empty, |
| | | TaskType = taskType.GetHashCode(), |
| | | TaskStatus = TaskRobotStatusEnum.RobotNew.GetHashCode(), |
| | | SourceAddress = section[0]!, |
| | | TargetAddress = section[1]!, |
| | | CurrentAddress = targetLineNo, |
| | | NextAddress = targetLineNo, |
| | | WarehouseId = stockInfo?.WarehouseId ?? 1, |
| | | Grade = 1, |
| | | Remark = $"æºæ¢°æ{taskName}", |
| | | Creater = "system" |
| | | }; |
| | | |
| | | var result = await Repository.AddDataAsync(task) > 0; |
| | | if (!result) |
| | | return WebResponseContent.Instance.Error($"æºæ¢°æ{taskName}ä»»å¡å建失败"); |
| | | |
| | | var wmstaskDto = _mapper.Map<WMSTaskDTO>(task) ?? new WMSTaskDTO(); |
| | | return WebResponseContent.Instance.OK($"æºæ¢°æ{taskName}ä»»å¡å建æå", wmstaskDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æºæ¢°æ{taskName}ä»»å¡å建失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion æºæ¢°æä»»å¡ |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.Task; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region ä»»å¡ç¶æç®¡ç |
| | | |
| | | /// <summary> |
| | | /// ä¿®æ¹ä»»å¡ç¶æï¼æ ¹æ®ä»»å¡IDä¿®æ¹ä¸ºæå®ç¶æï¼ |
| | | /// </summary> |
| | | /// <param name="taskDto">ä»»å¡ç¶ææ´æ°åæ°</param> |
| | | /// <returns>ä¿®æ¹ç»æ</returns> |
| | | public async Task<WebResponseContent> UpdateTaskByStatusAsync(UpdateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var taskInfo = await BaseDal.QueryFirstAsync(s => s.TaskNum == taskDto.Id); |
| | | if (taskInfo == null) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | taskInfo.TaskStatus = taskDto.NewStatus; |
| | | taskInfo.NextAddress = taskDto.NextAddress; |
| | | taskInfo.CurrentAddress = taskDto.CurrentAddress; |
| | | await BaseDal.UpdateDataAsync(taskInfo); |
| | | |
| | | return WebResponseContent.Instance.OK("ä¿®æ¹æå", taskInfo); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä¿®æ¹å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion ä»»å¡ç¶æç®¡ç |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using Mapster; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | #region 空æçä»»å¡ |
| | | |
| | | /// <summary> |
| | | /// å建空æçå
¥åºä»»å¡ |
| | | /// </summary> |
| | | /// <param name="taskDto"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode); |
| | | if (content.Status) |
| | | { |
| | | return content; |
| | | } |
| | | |
| | | return WebResponseContent.Instance.OK("æ¥è¯¢æå"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æ¥è¯¢ä»»å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 空æçå
¥åºå®æ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | stockInfo.LocationCode = location.LocationCode; |
| | | stockInfo.LocationId = location.Id; |
| | | stockInfo.StockStatus = StockStatusEmun.空æçåºå.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.InStock.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "空æçå
¥åºå®æ"); |
| | | if (!saveTaskHistoryResult.Status) |
| | | return saveTaskHistoryResult; |
| | | |
| | | var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "空æçå
¥åºå®æ"); |
| | | if (!saveStockHistoryResult.Status) |
| | | return saveStockHistoryResult; |
| | | |
| | | var deleteResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteResult) return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å建空æçåºåºä»»å¡ |
| | | /// </summary> |
| | | /// <param name="taskDto"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var dt_Task = await BaseDal.QueryFirstAsync(x => x.TargetAddress == taskDto.TargetAddress); |
| | | if (dt_Task != null) |
| | | { |
| | | var taskDTO = dt_Task.Adapt<WMSTaskDTO>(); |
| | | return WebResponseContent.Instance.OK("ä»»å¡å建æå", taskDTO); |
| | | } |
| | | |
| | | var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.空æçåºå.GetHashCode()); |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçåºåä¿¡æ¯"); |
| | | |
| | | var task = new Dt_Task() |
| | | { |
| | | WarehouseId = stockInfo.LocationDetails.WarehouseId, |
| | | PalletCode = stockInfo.PalletCode, |
| | | PalletType = stockInfo.PalletType, |
| | | SourceAddress = stockInfo.LocationCode, |
| | | CurrentAddress = stockInfo.LocationCode, |
| | | NextAddress = taskDto.TargetAddress, |
| | | TargetAddress = taskDto.TargetAddress, |
| | | Roadway = stockInfo.LocationDetails.RoadwayNo, |
| | | TaskType = TaskOutboundTypeEnum.OutEmpty.GetHashCode(), |
| | | TaskStatus = TaskStatusEnum.New.GetHashCode(), |
| | | Grade = 1, |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | Creater = "system", |
| | | }; |
| | | |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationId); |
| | | locationInfo.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode(); |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo); |
| | | if (!updateLocationResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å建失败"); |
| | | |
| | | var taskDtos = task.Adapt<WMSTaskDTO>(); |
| | | |
| | | var addResult = await BaseDal.AddDataAsync(task) > 0; |
| | | if (!addResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å建失败"); |
| | | return WebResponseContent.Instance.OK("ä»»å¡å建æå", taskDtos); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æ¥è¯¢ä»»å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 空æçåºåºå®æ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | stockInfo.LocationId = 0; |
| | | stockInfo.LocationCode = string.Empty; |
| | | stockInfo.StockStatus = StockStatusEmun.åºåºå®æ.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.Free.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "空æçåºåºå®æ"); |
| | | if (!saveTaskHistoryResult.Status) |
| | | return saveTaskHistoryResult; |
| | | |
| | | var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "空æçåºåºå®æ"); |
| | | if (!saveStockHistoryResult.Status) |
| | | return saveStockHistoryResult; |
| | | |
| | | var deleteResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteResult) return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion 空æçä»»å¡ |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå¨ä¸åä»»å¡å°WCS |
| | | /// </summary> |
| | | /// <param name="dtos">ä¸åä»»å¡åæ°å表</param> |
| | | /// <returns>æ¹éä¸åç»æ</returns> |
| | | [HttpGet, HttpPost, Route("DispatchTasksToWCS"), AllowAnonymous] |
| | | public async Task<WebResponseContent?> DispatchTasksToWCSAsync([FromBody] List<DispatchTaskDto> dtos) |
| | | { |
| | | return await Service.DispatchTasksToWCSAsync(dtos); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åå¯å
¥åºè´§ä½ |
| | | /// </summary> |
| | | /// <param name="taskDto"></param> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # æå¨ä¸åä»»å¡å° WCS åè½å®æ½è®¡å |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ WMS å端任å¡ç®¡ç页颿·»å å·¥å
·æ æé®ï¼æ¯æéä¸ä¸ä¸ªæå¤ä¸ªä»»å¡åï¼ç¼è¾å°åï¼èµ·ç¹/ç»ç¹ï¼åä¼å
级åæå¨ä¸åå° WCSã |
| | | |
| | | **Architecture:** |
| | | - åç«¯ï¼æ°å¢ `dispatchTasksToWCS.vue` å¼¹çªç»ä»¶ï¼å¤ç¨ `task.js` æ©å±æºå¶æ·»å å·¥å
·æ æé® |
| | | - åç«¯ï¼æ°å¢ `DispatchTasksToWCSAsync` æ¹æ³ï¼å¤ç¨ `ReceiveManualTask` æ¥å£ä¸åå° WCS |
| | | |
| | | **Tech Stack:** Vue 3 + Element Plusï¼å端ï¼ï¼ASP.NET Core + SqlSugarï¼åç«¯ï¼ |
| | | |
| | | --- |
| | | |
| | | ## æä»¶ç»æ |
| | | |
| | | | æ¹å¨ç±»å | æä»¶è·¯å¾ | |
| | | |----------|----------| |
| | | | æ°å¢ | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` | |
| | | | ä¿®æ¹ | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | |
| | | | æ°å¢ | `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` | |
| | | | ä¿®æ¹ | `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` | |
| | | | ä¿®æ¹ | `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` | |
| | | |
| | | --- |
| | | |
| | | ## Task 1: å端 - æ°å¢ DispatchTaskDto |
| | | |
| | | **æä»¶:** |
| | | - å建: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` |
| | | |
| | | - [ ] **å建 DispatchTaskDto.cs** |
| | | |
| | | ```csharp |
| | | using System.Text.Json.Serialization; |
| | | |
| | | namespace WIDESEA_DTO.Task |
| | | { |
| | | /// <summary> |
| | | /// æå¨ä¸åä»»å¡Dto |
| | | /// </summary> |
| | | public class DispatchTaskDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡ID |
| | | /// </summary> |
| | | [JsonPropertyName("taskId")] |
| | | public long TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// èµ·ç¹å°å |
| | | /// </summary> |
| | | [JsonPropertyName("sourceAddress")] |
| | | public string SourceAddress { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç»ç¹å°å |
| | | /// </summary> |
| | | [JsonPropertyName("targetAddress")] |
| | | public string TargetAddress { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¼å
级 |
| | | /// </summary> |
| | | [JsonPropertyName("grade")] |
| | | public int Grade { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡ä¸åç»æDto |
| | | /// </summary> |
| | | public class DispatchTaskResultDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡ID |
| | | /// </summary> |
| | | [JsonPropertyName("taskId")] |
| | | public long TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | [JsonPropertyName("taskNum")] |
| | | public int TaskNum { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¯å¦æå |
| | | /// </summary> |
| | | [JsonPropertyName("success")] |
| | | public bool Success { get; set; } |
| | | |
| | | /// <summary> |
| | | /// éè¯¯ä¿¡æ¯ |
| | | /// </summary> |
| | | [JsonPropertyName("errorMessage")] |
| | | public string ErrorMessage { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¹éä¸åç»æDto |
| | | /// </summary> |
| | | public class DispatchResultDto |
| | | { |
| | | /// <summary> |
| | | /// æåæ°é |
| | | /// </summary> |
| | | [JsonPropertyName("successCount")] |
| | | public int SuccessCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 失败æ°é |
| | | /// </summary> |
| | | [JsonPropertyName("failCount")] |
| | | public int FailCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 失败任å¡å表 |
| | | /// </summary> |
| | | [JsonPropertyName("failResults")] |
| | | public List<DispatchTaskResultDto> FailResults { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Commit** |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs |
| | | git commit -m "feat(WMS): æ°å¢æå¨ä¸åä»»å¡Dto |
| | | - DispatchTaskDto: ä¸å请æ±åæ° |
| | | - DispatchTaskResultDto: å个任å¡ä¸åç»æ |
| | | - DispatchResultDto: æ¹éä¸åç»æ |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 2: å端 - TaskService æ°å¢ DispatchTasksToWCSAsync |
| | | |
| | | **æä»¶:** |
| | | - ä¿®æ¹: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` |
| | | |
| | | **åç½®äºè§£:** |
| | | - ä»»å¡ç¶ææä¸¾ï¼`TaskInStatusEnum.InNew`ï¼å
¥åºæ°åï¼ã`TaskOutStatusEnum.OutNew`ï¼åºåºæ°åï¼ã`TaskRelocationStatusEnum.RelocationNew`ï¼ç§»åºæ°åï¼ |
| | | - å·²æ WCS è°ç¨æ¹å¼ï¼`_httpClientHelper.Post<WebResponseContent>("http://localhost:9292/api/Task/ReceiveManualTask", json)` |
| | | - å·²æäºå¡å¤çï¼`await _unitOfWorkManage.BeginTranAsync(async () => { ... })` |
| | | - å·²æ `WMSTaskDTO` ç¨äºç» WCS åéä»»å¡ |
| | | |
| | | **å¨ `TaskService_WCS.cs` æ«å°¾ `#endregion WCSé»è¾å¤ç` ä¹åæ·»å ï¼** |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// æå¨ä¸åä»»å¡å°WCS |
| | | /// </summary> |
| | | /// <param name="dtos">ä¸åä»»å¡åæ°å表</param> |
| | | /// <returns>æ¹éä¸åç»æ</returns> |
| | | public async Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos) |
| | | { |
| | | try |
| | | { |
| | | if (dtos == null || !dtos.Any()) |
| | | return WebResponseContent.Instance.Error("è¯·éæ©è¦ä¸åçä»»å¡"); |
| | | |
| | | var resultDto = new DispatchResultDto |
| | | { |
| | | SuccessCount = 0, |
| | | FailCount = 0, |
| | | FailResults = new List<DispatchTaskResultDto>() |
| | | }; |
| | | |
| | | foreach (var dto in dtos) |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(t => t.TaskId == dto.TaskId); |
| | | if (task == null) |
| | | { |
| | | resultDto.FailResults.Add(new DispatchTaskResultDto |
| | | { |
| | | TaskId = dto.TaskId, |
| | | TaskNum = 0, |
| | | Success = false, |
| | | ErrorMessage = "ä»»å¡ä¸åå¨" |
| | | }); |
| | | resultDto.FailCount++; |
| | | continue; |
| | | } |
| | | |
| | | // æ ¡éªä»»å¡ç¶æï¼ä»
å
¥åºæ°å/åºåºæ°å/ç§»åºæ°åå¯ä¸å |
| | | bool canDispatch = false; |
| | | if (task.TaskType == TaskInboundTypeEnum.Inbound.GetHashCode() |
| | | && task.TaskStatus == TaskInStatusEnum.InNew.GetHashCode()) |
| | | canDispatch = true; |
| | | else if (task.TaskType == TaskOutboundTypeEnum.Outbound.GetHashCode() |
| | | && task.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode()) |
| | | canDispatch = true; |
| | | else if (task.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode() |
| | | && task.TaskStatus == TaskRelocationStatusEnum.RelocationNew.GetHashCode()) |
| | | canDispatch = true; |
| | | |
| | | if (!canDispatch) |
| | | { |
| | | var statusName = GetTaskStatusName(task.TaskType, task.TaskStatus); |
| | | resultDto.FailResults.Add(new DispatchTaskResultDto |
| | | { |
| | | TaskId = dto.TaskId, |
| | | TaskNum = task.TaskNum, |
| | | Success = false, |
| | | ErrorMessage = $"ä»»å¡ç¶æ[{statusName}]ä¸å
许ä¸å" |
| | | }); |
| | | resultDto.FailCount++; |
| | | continue; |
| | | } |
| | | |
| | | // æ´æ°ä»»å¡çå°ååä¼å
级 |
| | | task.SourceAddress = dto.SourceAddress; |
| | | task.TargetAddress = dto.TargetAddress; |
| | | task.CurrentAddress = dto.SourceAddress; |
| | | task.NextAddress = dto.TargetAddress; |
| | | task.Grade = dto.Grade; |
| | | task.Dispatchertime = DateTime.Now; |
| | | |
| | | await BaseDal.UpdateDataAsync(task); |
| | | |
| | | // æé WMSTaskDTOåéç»WCS |
| | | var wmsTaskDto = new WMSTaskDTO |
| | | { |
| | | Id = task.TaskId, |
| | | TaskNum = task.TaskNum, |
| | | PalletCode = task.PalletCode, |
| | | SourceAddress = task.SourceAddress, |
| | | TargetAddress = task.TargetAddress, |
| | | CurrentAddress = task.CurrentAddress, |
| | | NextAddress = task.NextAddress, |
| | | TaskType = task.TaskType, |
| | | TaskStatus = task.TaskStatus, |
| | | Roadway = task.Roadway, |
| | | Grade = task.Grade, |
| | | WarehouseId = task.WarehouseId, |
| | | PalletType = task.PalletType |
| | | }; |
| | | |
| | | var wcsResult = _httpClientHelper.Post<WebResponseContent>( |
| | | "http://localhost:9292/api/Task/ReceiveManualTask", |
| | | new List<WMSTaskDTO> { wmsTaskDto }.ToJson()); |
| | | |
| | | if (wcsResult != null && wcsResult.IsSuccess) |
| | | { |
| | | resultDto.SuccessCount++; |
| | | } |
| | | else |
| | | { |
| | | resultDto.FailResults.Add(new DispatchTaskResultDto |
| | | { |
| | | TaskId = dto.TaskId, |
| | | TaskNum = task.TaskNum, |
| | | Success = false, |
| | | ErrorMessage = wcsResult?.Message ?? "WCSååºå¼å¸¸" |
| | | }); |
| | | resultDto.FailCount++; |
| | | } |
| | | } |
| | | |
| | | if (resultDto.FailCount == 0) |
| | | return WebResponseContent.Instance.OK($"æåä¸å{resultDto.SuccessCount}个任å¡", resultDto); |
| | | |
| | | if (resultDto.SuccessCount == 0) |
| | | return WebResponseContent.Instance.Error($"ä¸å失败ï¼å
±{resultDto.FailCount}个任å¡", resultDto); |
| | | |
| | | return WebResponseContent.Instance.Error($"é¨åä¸åæå{resultDto.SuccessCount}个ï¼å¤±è´¥{resultDto.FailCount}个", resultDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä¸åä»»å¡å¼å¸¸: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åä»»å¡ç¶æåç§° |
| | | /// </summary> |
| | | private string GetTaskStatusName(int taskType, int taskStatus) |
| | | { |
| | | if (taskType == TaskInboundTypeEnum.Inbound.GetHashCode()) |
| | | return ((TaskInStatusEnum)taskStatus).ToString(); |
| | | if (taskType == TaskOutboundTypeEnum.Outbound.GetHashCode()) |
| | | return ((TaskOutStatusEnum)taskStatus).ToString(); |
| | | if (taskType == TaskRelocationTypeEnum.Relocation.GetHashCode()) |
| | | return ((TaskRelocationStatusEnum)taskStatus).ToString(); |
| | | return taskStatus.ToString(); |
| | | } |
| | | ``` |
| | | |
| | | **注æï¼** éè¦å¨æä»¶é¡¶é¨ç¡®è®¤å·²æä»¥ä¸ usingï¼ |
| | | ```csharp |
| | | using WIDESEA_DTO.Task; |
| | | |
| | | > **ç¶æåæ¥è¯´æ**ï¼WMS ä¸åæååï¼WMS 端任å¡ç¶æä¿æ"New"ä¸åï¼ä¸åæ¢å°æ§è¡ä¸ç¶æãè¿æ¯ç°æ `CreateManualTaskAsync` ç设计模å¼ï¼å 为 WCS ä¾§æç¬ç«çä»»å¡çå½å¨æï¼ï¼ä¸è·è¿æ¤ PR æ¹åãå¦é WMS 端ä¹åæ¥ç¶æï¼éå¦è¡è¯ä¼° WCSâWMS çç¶æåè°æºå¶ã |
| | | using WIDESEA_Model.Models; |
| | | ``` |
| | | |
| | | - [ ] **Commit** |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs |
| | | git commit -m "feat(WMS): TaskServiceæ°å¢DispatchTasksToWCSAsyncæ¹æ³ |
| | | - æ ¡éªä»»å¡ç¶æï¼ä»
å
¥åºæ°å/åºåºæ°å/ç§»åºæ°åå¯ä¸å |
| | | - æ´æ°ä»»å¡å°ååä¼å
级åè°ç¨WCS ReceiveManualTaskæ¥å£ |
| | | - è¿åæ¹éä¸åç»æï¼æå/失败å表ååå ï¼ |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 3: å端 - TaskController æ°å¢ä¸åæ¥å£ |
| | | |
| | | **æä»¶:** |
| | | - ä¿®æ¹: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` |
| | | |
| | | **å¨ Controller 䏿·»å 以䏿¹æ³ï¼æ¾å¨å
¶ä»æ¹æ³éè¿ï¼å»ºè®®å¨ CreateManualTaskAsync ä¹åï¼ï¼** |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// æå¨ä¸åä»»å¡å°WCS |
| | | /// </summary> |
| | | /// <param name="dtos">ä¸åä»»å¡åæ°å表</param> |
| | | /// <returns>æ¹éä¸åç»æ</returns> |
| | | [HttpGet, HttpPost, Route("DispatchTasksToWCS"), AllowAnonymous] |
| | | public async Task<WebResponseContent?> DispatchTasksToWCSAsync([FromBody] List<DispatchTaskDto> dtos) |
| | | { |
| | | return await Service.DispatchTasksToWCSAsync(dtos); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Commit** |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs |
| | | git commit -m "feat(WMS): TaskControlleræ°å¢DispatchTasksToWCSæ¥å£ |
| | | POST /api/TaskInfo/DispatchTasksToWCS |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 4: å端 - æ°å¢ dispatchTasksToWCS.vue å¼¹çªç»ä»¶ |
| | | |
| | | **æä»¶:** |
| | | - å建: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` |
| | | |
| | | **åèç°æ addManualTask.vue çæ¨¡å¼ï¼ä½¿ç¨ el-table å®ç°æ¹éç¼è¾ï¼** |
| | | |
| | | ```vue |
| | | <template> |
| | | <div> |
| | | <vol-box v-model="showBox" :lazy="true" width="900px" :padding="15" title="æå¨ä¸åä»»å¡å° WCS"> |
| | | <div v-if="selectedRows.length > 0" class="dispatch-info"> |
| | | å·²é任塿°: <span class="count">{{ selectedRows.length }}</span> 个 |
| | | </div> |
| | | |
| | | <el-table :data="tableData" border style="width: 100%; margin-top: 10px" max-height="400"> |
| | | <el-table-column prop="taskNum" label="ä»»å¡å·" width="120"></el-table-column> |
| | | <el-table-column prop="sourceAddress" label="èµ·ç¹å°å" width="160"> |
| | | <template slot-scope="scope"> |
| | | <el-input |
| | | v-if="scope.row.editable" |
| | | v-model="scope.row.sourceAddress" |
| | | size="small" |
| | | placeholder="请è¾å
¥" |
| | | ></el-input> |
| | | <span v-else>{{ scope.row.sourceAddress }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="targetAddress" label="ç»ç¹å°å" width="160"> |
| | | <template slot-scope="scope"> |
| | | <el-input |
| | | v-if="scope.row.editable" |
| | | v-model="scope.row.targetAddress" |
| | | size="small" |
| | | placeholder="请è¾å
¥" |
| | | ></el-input> |
| | | <span v-else>{{ scope.row.targetAddress }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="grade" label="ä¼å
级" width="100"> |
| | | <template slot-scope="scope"> |
| | | <el-input-number |
| | | v-if="scope.row.editable" |
| | | v-model="scope.row.grade" |
| | | :min="1" |
| | | :max="99" |
| | | size="small" |
| | | style="width: 80px" |
| | | ></el-input-number> |
| | | <span v-else>{{ scope.row.grade }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="statusName" label="ç¶æ" width="120"> |
| | | <template slot-scope="scope"> |
| | | <span :class="{ 'status-error': !scope.row.editable }">{{ scope.row.statusName }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="palletCode" label="æçå·"></el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å¤±è´¥ç»æ --> |
| | | <div v-if="failResults.length > 0" class="fail-results"> |
| | | <div class="fail-title">ä¸å失败任å¡ï¼</div> |
| | | <el-table :data="failResults" border style="width: 100%; margin-top: 10px" max-height="200"> |
| | | <el-table-column prop="taskNum" label="ä»»å¡å·" width="120"></el-table-column> |
| | | <el-table-column prop="errorMessage" label="失败åå "></el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <el-button type="primary" size="small" @click="submit" :loading="loading">确认ä¸å</el-button> |
| | | <el-button type="danger" size="small" @click="showBox = false">å
³é</el-button> |
| | | </template> |
| | | </vol-box> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import VolBox from "@/components/basic/VolBox.vue"; |
| | | |
| | | export default { |
| | | components: { VolBox }, |
| | | data() { |
| | | return { |
| | | showBox: false, |
| | | loading: false, |
| | | selectedRows: [], |
| | | tableData: [], |
| | | failResults: [], |
| | | // å¯ä¸åç¶æçä»»å¡ç±»ååç¶æå¼ï¼éä¸å端 TaskTypeEnum æä¸¾ä¸è´ï¼ |
| | | // Inbound=200, Outbound=100, Relocation=300 |
| | | // ç¶æ: InNew=200, OutNew=100, RelocationNew=300 |
| | | dispatchableStatuses: { |
| | | inbound: { taskType: 200, status: 200 }, // å
¥åºæ°å |
| | | outbound: { taskType: 100, status: 100 }, // åºåºæ°å |
| | | relocation: { taskType: 300, status: 300 } // ç§»åºæ°å |
| | | } |
| | | }; |
| | | }, |
| | | methods: { |
| | | open(rows) { |
| | | this.showBox = true; |
| | | this.selectedRows = rows || []; |
| | | this.failResults = []; |
| | | this.initTableData(); |
| | | }, |
| | | initTableData() { |
| | | // ä»»å¡ç¶æåç§°æ å°ï¼éä¸å端æä¸¾ä¸è´ï¼ |
| | | // taskType: Inbound=200, Outbound=100, Relocation=300 |
| | | // status: InNew=200, OutNew=100, RelocationNew=300, SC_OutExecuting=110, etc. |
| | | const statusNames = { |
| | | 'inbound_200': 'å
¥åºæ°å', |
| | | 'outbound_100': 'åºåºæ°å', |
| | | 'relocation_300': 'ç§»åºæ°å', |
| | | 'outbound_110': 'å åæºåºåºæ§è¡ä¸', |
| | | 'outbound_115': 'å åæºåºåºå®æ', |
| | | 'inbound_220': 'è¾é线å
¥åºæ§è¡ä¸', |
| | | 'inbound_230': 'å åæºå
¥åºæ§è¡ä¸', |
| | | 'inbound_290': 'å
¥åºä»»å¡å®æ', |
| | | 'outbound_120': 'è¾é线åºåºæ§è¡ä¸', |
| | | 'outbound_125': 'è¾é线åºåºå®æ', |
| | | 'outbound_200': 'åºåºä»»å¡å®æ', |
| | | 'relocation_310': 'å åæºç§»åºæ§è¡ä¸' |
| | | }; |
| | | |
| | | this.tableData = this.selectedRows.map(row => { |
| | | const taskType = row.taskType || 0; |
| | | const taskStatus = row.taskStatus || 0; |
| | | const statusKey = this.getStatusKey(taskType, taskStatus); |
| | | const statusName = statusNames[statusKey] || `ç¶æ${taskStatus}`; |
| | | |
| | | // 夿æ¯å¦å¯ç¼è¾ï¼ä»
å
¥åºæ°å/åºåºæ°å/ç§»åºæ°åå¯ç¼è¾ |
| | | const editable = this.isEditable(taskType, taskStatus); |
| | | |
| | | return { |
| | | taskId: row.taskId, |
| | | taskNum: row.taskNum, |
| | | sourceAddress: row.sourceAddress || '', |
| | | targetAddress: row.targetAddress || '', |
| | | grade: row.grade || 1, |
| | | statusName: statusName, |
| | | palletCode: row.palletCode || '', |
| | | editable: editable, |
| | | taskType: taskType, |
| | | taskStatus: taskStatus |
| | | }; |
| | | }); |
| | | }, |
| | | getStatusKey(taskType, taskStatus) { |
| | | if (taskType === 200) return `inbound_${taskStatus}`; |
| | | if (taskType === 100) return `outbound_${taskStatus}`; |
| | | if (taskType === 300) return `relocation_${taskStatus}`; |
| | | return `other_${taskStatus}`; |
| | | }, |
| | | isEditable(taskType, taskStatus) { |
| | | if (taskType === 200 && taskStatus === 200) return true; // å
¥åºæ°å |
| | | if (taskType === 100 && taskStatus === 100) return true; // åºåºæ°å |
| | | if (taskType === 300 && taskStatus === 300) return true; // ç§»åºæ°å |
| | | return false; |
| | | }, |
| | | submit() { |
| | | if (this.tableData.length === 0) return this.$message.error("请å
鿩任å¡"); |
| | | |
| | | const dispatchData = this.tableData.map(row => ({ |
| | | taskId: row.taskId, |
| | | sourceAddress: row.sourceAddress, |
| | | targetAddress: row.targetAddress, |
| | | grade: row.grade |
| | | })); |
| | | |
| | | this.loading = true; |
| | | this.http |
| | | .post("/api/TaskInfo/DispatchTasksToWCS", dispatchData, "æ°æ®å¤çä¸...") |
| | | .then((res) => { |
| | | this.loading = false; |
| | | if (!res.status) { |
| | | this.$message.error(res.message); |
| | | // æ¾ç¤ºå¤±è´¥å表 |
| | | if (res.data && res.data.failResults) { |
| | | this.failResults = res.data.failResults; |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // å
¨é¨æå |
| | | if (res.data && res.data.failCount === 0) { |
| | | this.$message.success(res.message); |
| | | this.showBox = false; |
| | | this.$emit("parentCall", ($vue) => { |
| | | $vue.refresh(); |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // é¨åæåæå
¨é¨å¤±è´¥ |
| | | if (res.data && res.data.failResults) { |
| | | this.failResults = res.data.failResults; |
| | | } |
| | | |
| | | if (res.data && res.data.failCount > 0 && res.data.successCount > 0) { |
| | | this.$message.warning(res.message); |
| | | } else { |
| | | this.$message.error(res.message); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | this.loading = false; |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dispatch-info { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | .dispatch-info .count { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | .status-error { |
| | | color: #f56c6c; |
| | | } |
| | | .fail-results { |
| | | margin-top: 15px; |
| | | padding: 10px; |
| | | background: #fef0f0; |
| | | border-radius: 4px; |
| | | } |
| | | .fail-title { |
| | | font-size: 14px; |
| | | color: #f56c6c; |
| | | margin-bottom: 5px; |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | - [ ] **Commit** |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue |
| | | git commit -m "feat(WMS): æ°å¢dispatchTasksToWCS.vueæ¹éä¸åå¼¹çªç»ä»¶ |
| | | - å·¥å
·æ æé®è§¦åå¼¹çª |
| | | - è¡¨æ ¼å±ç¤ºéä¸ä»»å¡ï¼å¯ç¼è¾å°ååä¼å
级 |
| | | - éå¯ä¸åç¶æä»»å¡è¡æ 红ä¸ä¸å¯ç¼è¾ |
| | | - æ¾ç¤ºä¸å失败任å¡å表 |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 5: å端 - task.js æ·»å å·¥å
·æ æé® |
| | | |
| | | **æä»¶:** |
| | | - ä¿®æ¹: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` |
| | | |
| | | **éè¦ä¿®æ¹ä¸¤å¤ï¼** |
| | | |
| | | 1. å¨ `components` é¨åå¼å
¥å¼¹çªç»ä»¶ï¼ |
| | | ```javascript |
| | | import addManualTask from './extend/addManualTask.vue' |
| | | import dispatchTasksToWCS from './extend/dispatchTasksToWCS.vue' // æ°å¢ |
| | | ``` |
| | | |
| | | 2. å¨ `components` å¯¹è±¡ä¸æ³¨åï¼ |
| | | ```javascript |
| | | components: { |
| | | gridBody: addManualTask, |
| | | dispatchBody: dispatchTasksToWCS, // æ°å¢ |
| | | }, |
| | | ``` |
| | | |
| | | 3. å¨ `onInit()` æ¹æ³ä¸æ·»å æå¨ä¸åæé®ï¼å¨æå¨åå»ºä»»å¡æé®ä¹åï¼ï¼ |
| | | |
| | | ```javascript |
| | | // æ·»å "æå¨ä¸å"æé® |
| | | this.buttons.push({ |
| | | name: 'æå¨ä¸å', |
| | | icon: 'el-icon-s-promotion', |
| | | type: 'primary', |
| | | value: 'DispatchTasksToWCS', |
| | | onClick: () => { |
| | | let rows = this.$refs.table.getSelected(); |
| | | if (rows.length == 0) return this.$error("请å
鿩任å¡"); |
| | | this.$refs.dispatchBody.open(rows); |
| | | } |
| | | }); |
| | | ``` |
| | | |
| | | **宿´ä¿®æ¹åç task.js å
³é®é¨åï¼** |
| | | |
| | | ```javascript |
| | | import addManualTask from './extend/addManualTask.vue' |
| | | import dispatchTasksToWCS from './extend/dispatchTasksToWCS.vue' |
| | | |
| | | let extension = { |
| | | components: { |
| | | gridBody: addManualTask, |
| | | dispatchBody: dispatchTasksToWCS, |
| | | }, |
| | | buttons: { view: [], box: [], detail: [] }, |
| | | methods: { |
| | | onInit() { |
| | | //æ·»å "æå¨å建任å¡"æé® |
| | | this.buttons.push({ |
| | | name: 'æå¨å建任å¡', |
| | | icon: 'el-icon-plus', |
| | | type: 'primary', |
| | | value: 'ManualCreateTask', |
| | | onClick: () => { |
| | | this.$refs.gridBody.open(); |
| | | } |
| | | }); |
| | | |
| | | // æ·»å "æå¨ä¸å"æé® |
| | | this.buttons.push({ |
| | | name: 'æå¨ä¸å', |
| | | icon: 'el-icon-s-promotion', |
| | | type: 'primary', |
| | | value: 'DispatchTasksToWCS', |
| | | onClick: () => { |
| | | let rows = this.$refs.table.getSelected(); |
| | | if (rows.length == 0) return this.$error("请å
鿩任å¡"); |
| | | this.$refs.dispatchBody.open(rows); |
| | | } |
| | | }); |
| | | // ... å
¶ä½ç°ææé®é»è¾ä¿æä¸å |
| | | }, |
| | | // ... å
¶ä½æ¹æ³ä¿æä¸å |
| | | } |
| | | }; |
| | | export default extension; |
| | | ``` |
| | | |
| | | - [ ] **Commit** |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js |
| | | git commit -m "feat(WMS): task.jsæ·»å å·¥å
·æ 'æå¨ä¸å'æé® |
| | | - å¼å
¥dispatchTasksToWCS.vueç»ä»¶ |
| | | - ç¹å»æé®è·åéä¸ä»»å¡å¹¶æå¼ä¸åå¼¹çª |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 6: éªè¯æå»º |
| | | |
| | | - [ ] **WMS å端æå»º** |
| | | |
| | | ```bash |
| | | cd D:/Git/ShanMeiXinNengYuan/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer |
| | | dotnet build |
| | | ``` |
| | | |
| | | - [ ] **WMS å端æå»º** |
| | | |
| | | ```bash |
| | | cd D:/Git/ShanMeiXinNengYuan/Code/WMS/WIDESEA_WMSClient |
| | | npm run serve |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## éªæ¶æ å |
| | | |
| | | - [ ] å·¥å
·æ æ·»å "æå¨ä¸å"æé®ï¼æªéä¸ä»»å¡æ¶ç¹å»æç¤º"请å
鿩任å¡" |
| | | - [ ] å¼¹çªæ£ç¡®æ¾ç¤ºææéä¸ä»»å¡ |
| | | - [ ] ä»
å
¥åºæ°å/åºåºæ°å/ç§»åºæ°åç¶æçä»»å¡å¯ç¼è¾å°ååä¼å
级 |
| | | - [ ] å
¶ä»ç¶æä»»å¡è¡æ 红ï¼å°å/ä¼å
级ä¸å¯ç¼è¾ |
| | | - [ ] 确认ä¸ååè°ç¨å端æ¥å£ |
| | | - [ ] æåæ¶å¼¹çªå
³éå¹¶å·æ°å表ï¼å¤±è´¥æ¶æ¾ç¤ºå¤±è´¥ä»»å¡ååå |
| | | - [ ] æ¹éä¸åæ¶æ£ç¡®ç»è®¡æå/失败æ°é |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # æå¨ä¸åä»»å¡å° WCS åè½è®¾è®¡ |
| | | |
| | | **æ¥æ**: 2026-04-12 |
| | | **ç¶æ**: å·²æ¹å |
| | | |
| | | --- |
| | | |
| | | ## 1. åè½æ¦è¿° |
| | | |
| | | å¨ WMS å端任å¡ç®¡ç页颿·»å å·¥å
·æ æé®ï¼æ¯æä»ä»»å¡å表ä¸éæ©ä¸ä¸ªæå¤ä¸ªä»»å¡ï¼æå¨ç¼è¾å°åï¼èµ·ç¹/ç»ç¹ï¼åä¼å
级åï¼ä¸åå° WCS ç³»ç»æ§è¡ã |
| | | |
| | | **使ç¨åºæ¯**ï¼å½ä»»å¡å è®¾å¤æ
éãç½ç»é®é¢çåå æªè½æ£å¸¸ä¸åæ¶ï¼æä½å坿å¨éæ°ä¸åã |
| | | |
| | | --- |
| | | |
| | | ## 2. å端æ¹å¨ |
| | | |
| | | ### 2.1 æ°å¢æä»¶ |
| | | |
| | | | æä»¶ | ç¨é | |
| | | |------|------| |
| | | | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` | æ¹éä¸åç¼è¾å¼¹çªç»ä»¶ | |
| | | |
| | | ### 2.2 ä¿®æ¹æä»¶ |
| | | |
| | | | æä»¶ | æ¹å¨ | |
| | | |------|------| |
| | | | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | æ·»å å·¥å
·æ "æå¨ä¸å"æé® | |
| | | |
| | | ### 2.3 å¼¹çªäº¤äº |
| | | |
| | | **è§¦åæ¹å¼**ï¼å·¥å
·æ ç¹å»"æå¨ä¸å"æé® |
| | | |
| | | **åç½®æ¡ä»¶**ï¼è³å°éä¸ä¸é¡¹ä»»å¡ |
| | | |
| | | **å¼¹çªå
容**ï¼ |
| | | - 顶鍿¾ç¤º"å·²é任塿°: N 个" |
| | | - è¡¨æ ¼å±ç¤ºææéä¸ä»»å¡ï¼åï¼ä»»å¡å·ãèµ·ç¹å°åãç»ç¹å°åãä¼å
级ãç¶æ |
| | | - å°ååæ®µï¼`el-input` æ `el-select`ï¼åå³äºæ¯å¦ææ åå°ååå
¸ï¼ |
| | | - ä¼å
级ï¼`el-input-number`ï¼èå´ 1-99 |
| | | - ç¶æåï¼ä»
å±ç¤ºï¼ä¸å¯ç¼è¾ |
| | | |
| | | **æé®**ï¼åæ¶ã确认ä¸å |
| | | |
| | | **ä¸åå**ï¼ |
| | | - æåï¼å¼¹çªå
³éï¼å·æ°ä»»å¡å表 |
| | | - 失败ï¼å¼¹çªå
æ¾ç¤ºå¤±è´¥ä»»å¡å表ï¼ä»»å¡å· + 失败åå ï¼ |
| | | |
| | | --- |
| | | |
| | | ## 3. å端æ¹å¨ |
| | | |
| | | ### 3.1 æ°å¢ DTO |
| | | |
| | | **æä»¶**: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` |
| | | |
| | | ```csharp |
| | | public class DispatchTaskDto |
| | | { |
| | | public long TaskId { get; set; } |
| | | public string SourceAddress { get; set; } |
| | | public string TargetAddress { get; set; } |
| | | public int Priority { get; set; } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.2 æ°å¢æ¥å£ |
| | | |
| | | **Controller**: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` |
| | | |
| | | ``` |
| | | POST /api/TaskInfo/DispatchTasksToWCS |
| | | ``` |
| | | |
| | | **Service**: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` |
| | | |
| | | æ°å¢æ¹æ³ `DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos)` |
| | | |
| | | ### 3.3 ä¸å¡é»è¾ |
| | | |
| | | ``` |
| | | 1. æ ¡éªéä¸ä»»å¡æ°é > 0 |
| | | 2. æ ¡éªä»»å¡ç¶æï¼ä»
å
许以ä¸ç¶æçä»»å¡ä¸åï¼ |
| | | - å
¥åºï¼InNewï¼å
¥åºæ°åï¼ |
| | | - åºåºï¼OutNewï¼åºåºæ°åï¼ |
| | | - ç§»åºï¼RelocationNewï¼ç§»åºæ°åï¼ |
| | | 3. æ´æ°ä»»å¡çå°åï¼SourceAddressãTargetAddressï¼åä¼å
级ï¼Gradeï¼ |
| | | 4. è°ç¨ WCS æ¥å£ï¼POST http://localhost:9292/api/Task/ReceiveManualTask |
| | | 5. æ±æ»æ¯æ¡ä»»å¡çä¸åç»æï¼æå/失败ååå ï¼ |
| | | 6. è¿åç»å端 |
| | | ``` |
| | | |
| | | ### 3.4 WCS æ¥å£å¤ç¨ |
| | | |
| | | **å·²ææ¥å£**: `POST /api/Task/ReceiveManualTask`ï¼commit b032763ï¼ |
| | | |
| | | WMS è°ç¨è¯¥æ¥å£ï¼å°ä»»å¡æ°æ®ä»¥ `WMSTaskDTO` æ ¼å¼åéã |
| | | |
| | | --- |
| | | |
| | | ## 4. é误å¤ç |
| | | |
| | | | åºæ¯ | å端å¤ç | å端å¤ç | |
| | | |------|----------|----------| |
| | | | æªéä¸ä»»å¡ | æé®ç½®ç°ï¼ç¹å»æç¤º"请å
鿩任å¡" | è¿åé误æç¤º | |
| | | | ä»»å¡ç¶æä¸å¯ä¸å | è¡å
ç¶æåæ çº¢ï¼å°å/ä¼å
级置ç°ä¸å¯ç¼è¾ | æ ¡éªå¹¶è¿åä¸å¯ä¸åä»»å¡å表 | |
| | | | WCS è°ç¨è¶
æ¶ | è¯¥ä»»å¡æ è®°"ä¸åè¶
æ¶" | æ è®°ä»»å¡ï¼ä¸åç»æè¿åå端 | |
| | | | WCS è¿å失败 | æ¾ç¤º WCS é误åå ï¼å¦"设å¤å¿"ï¼ | éä¼ WCS éè¯¯ä¿¡æ¯ | |
| | | | å
¨é¨æå | å¼¹çªå
³éï¼å·æ°å表 | è¿åæå | |
| | | |
| | | --- |
| | | |
| | | ## 5. æ¶åçä»£ç æä»¶ |
| | | |
| | | | å±çº§ | æä»¶è·¯å¾ | æ¹å¨ç±»å | |
| | | |------|----------|----------| |
| | | | å端 | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | ä¿®æ¹ | |
| | | | å端 | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` | æ°å¢ | |
| | | | å端 | `WMS/WIDESEA_WMSClient/src/api/` | æ°å¢ API æ¹æ³ | |
| | | | å端 | `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` | æ°å¢ | |
| | | | å端 | `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` | ä¿®æ¹ | |
| | | | å端 | `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` | ä¿®æ¹ | |
| | | | å端 | `WMS/WIDESEA_WMSServer/WIDESEA_Core/Helper/HTTP/HttpClientHelper.cs` | å¤ç¨ | |
| | | |
| | | --- |
| | | |
| | | ## 6. UI 示æå¾ |
| | | |
| | | ``` |
| | | âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ |
| | | â æå¨ä¸åä»»å¡å° WCS [X] â |
| | | ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ⤠|
| | | â å·²é任塿°: 5 个 â |
| | | â âââââââââââââââââââââââââââââââââââââââââââââââââââââââ â |
| | | â â ä»»å¡å· â èµ·ç¹å°å â ç»ç¹å°å â ä¼å
级 â ç¶æ â â |
| | | â ââââââââââââââââââââââââââââââââââââââââââââââââââââââ⤠â |
| | | â â T001 â [PLC-A01 ] â [Rack-05] â [ 5 ] â å
¥åºæ°åâ â |
| | | â â T002 â [PLC-A02 ] â [Rack-06] â [ 3 ] â å
¥åºæ°åâ â |
| | | â âââââââââââââââââââââââââââââââââââââââââââââââââââââââ â |
| | | ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ⤠|
| | | â [åæ¶] [确认ä¸å] â |
| | | âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 7. éªæ¶æ å |
| | | |
| | | - [ ] å·¥å
·æ æ·»å "æå¨ä¸å"æé®ï¼æªéä¸ä»»å¡æ¶æé®å¯ç¨ä½ç¹å»æç¤ºéæ© |
| | | - [ ] å¼¹çªæ£ç¡®æ¾ç¤ºææéä¸ä»»å¡ |
| | | - [ ] ä»
å
¥åºæ°å/åºåºæ°å/ç§»åºæ°åç¶æçä»»å¡å¯ç¼è¾å°ååä¼å
级 |
| | | - [ ] 确认ä¸ååè°ç¨å端æ¥å£ |
| | | - [ ] æå/å¤±è´¥ç»ææ£ç¡®åé¦ç»ç¨æ· |
| | | - [ ] æååå·æ°ä»»å¡å表 |