跳转到主要内容

发货-货件处理

发货-货件处理模块详细帮助文档

1. 功能概述

发货-货件处理模块是 Wimoor 系统中 FBA 发货流程的核心环节,用于处理和管理发货计划中的货件。该模块位于系统的 ERP 模块中,主要负责货件的信息展示、操作管理、状态更新等功能。

1.1 主要功能

  • 货件信息展示:展示货件的详细信息,包括基本信息、运输信息、地址信息等
  • 货件操作管理:提供货件的删除、复制、本地完成等操作
  • 箱标地址管理:添加和管理货件的箱标收货地址
  • 货件状态同步:与亚马逊后台同步货件状态
  • 费用计算:计算货件的物流费用和货值

2. 前端组件结构

2.1 组件层级

shipment_handing/
├── shipstep/
│   ├── components/
│   │   ├── shipment_operator.vue (货件操作组件)
│   │   ├── shipment_info.vue (货件信息组件)
│   │   └── destination.vue (箱标地址组件)

2.2 核心组件

组件名称 文件路径 功能描述
shipment_operator wimoor666\wimoor-ui\src\views\erp\shipv2\shipment_handing\shipstep\components\shipment_operator.vue 货件操作组件,提供货件的删除、复制等操作
shipment_info wimoor666\wimoor-ui\src\views\erp\shipv2\shipment_handing\shipstep\components\shipment_info.vue 货件信息组件,展示货件的详细信息
destination wimoor666\wimoor-ui\src\views\erp\shipv2\shipment_handing\shipstep\components\destination.vue 箱标地址组件,用于管理货件的箱标收货地址

2.3 组件功能详解

2.3.1 shipment_operator 组件

功能:提供货件的操作管理功能 核心功能

  • 复制货件:基于当前货件创建新的货件
  • 删除货件:删除本地货件或同步删除亚马逊货件
  • 本地完成:将货件标记为本地已完成状态

关键 API 调用

  • shipmenthandlingApi.requestInboundShipment():获取亚马逊货件状态
  • shipmenthandlingApi.disableShipment():删除货件
  • shipmenthandlingApi.localDoneShipment():标记货件为本地已完成

2.3.2 shipment_info 组件

功能:展示货件的详细信息 核心功能

  • 单据信息:展示货件的基本信息,如订单编码、货件名称、货件编码等
  • 运输信息:展示货件的运输相关信息,如总货值、物流总费用、SKU个数等
  • 地址信息:展示货件的发货地址和收货地址
  • 箱标地址管理:添加和管理货件的箱标收货地址

关键 API 调用

  • shipmentPlacementApi.getBaseInfo():获取货件的详细信息
  • shipmentPlacementApi.saveDestinationBox():保存箱标收货地址

3. 后端代码结构

3.1 核心控制器

控制器名称 文件路径 功能描述
ShipInboundPlanPlacementV2Controller wimoor666\wimoor666\wimoor-amazon\amazon-boot\src\main\java\com\wimoor\amazon\inboundV2\controller\ShipInboundPlanPlacementV2Controller.java 货件处理核心控制器,处理货件的各种操作
ShipFormController wimoor666\wimoor666\wimoor-amazon\amazon-boot\src\main\java\com\wimoor\amazon\inbound\controller\ShipFormController.java 货件表单控制器,处理货件的创建、更新等操作

3.2 核心服务

服务名称 文件路径 功能描述
IShipInboundShipmentService 货件服务接口,定义货件相关的业务逻辑
ShipInboundShipmentServiceImpl 货件服务实现,实现货件相关的业务逻辑
IFulfillmentInboundService 亚马逊入库服务接口,定义与亚马逊入库相关的操作
FulfillmentInboundServiceImpl 亚马逊入库服务实现,实现与亚马逊入库相关的操作

3.3 数据模型

实体类 文件路径 功能描述
ShipInboundShipment 货件实体,存储货件的基本信息
ShipInboundBox 箱子实体,存储箱子的信息
ShipInboundCase 箱子内容实体,存储箱子中的商品信息
ShipInboundDestinationAddress 目的地地址实体,存储货件的收货地址信息

4. API 调用关系

4.1 前端 API 调用

前端 API 方法 用途 后端对应接口
shipmenthandlingApi.requestInboundShipment() 获取亚马逊货件状态 ShipFormController.requestInboundShipmentAction()
shipmenthandlingApi.disableShipment() 删除货件 ShipFormController.cancelShipmentAction()
shipmenthandlingApi.localDoneShipment() 标记货件为本地已完成 对应后端服务方法
shipmentPlacementApi.getBaseInfo() 获取货件详细信息 ShipInboundPlanPlacementV2Controller.getBaseInfoAction()
shipmentPlacementApi.saveDestinationBox() 保存箱标收货地址 ShipInboundPlanPlacementV2Controller.saveDestinationBoxAction()

4.2 后端 API 接口

后端接口 路径 功能描述
getBaseInfoAction() /api/v2/shipInboundPlan/shipment/getBaseInfo 获取货件的详细信息
saveDestinationBoxAction() /api/v2/shipInboundPlan/shipment/saveDestinationBox 保存货件的箱标收货地址
requestInboundShipmentAction() /api/v1/shipForm/requestInboundShipment 获取亚马逊货件状态
cancelShipmentAction() /api/v1/shipForm/cancelShipment 取消货件
getPkgLabelUrlAction() /api/v2/shipInboundPlan/shipment/getPkgLabelUrl 获取箱子标签的下载 URL

5. 业务流程

5.1 货件信息加载流程

  1. 页面加载:用户进入货件处理页面
  2. 获取货件 ID:从 URL 参数中获取货件 ID
  3. 加载货件信息:调用 shipmentPlacementApi.getBaseInfo() 获取货件详细信息
  4. 渲染页面:使用获取到的信息渲染页面各个组件
  5. 计算费用:计算货件的总货值和物流费用

5.2 货件删除流程

  1. 用户点击删除按钮:触发删除货件操作
  2. 获取货件状态:调用 shipmenthandlingApi.requestInboundShipment() 获取亚马逊后台货件状态
  3. 显示确认对话框:根据货件状态显示确认删除对话框
  4. 用户确认删除:用户选择删除方式(仅删除本地或同步删除亚马逊货件)
  5. 执行删除操作:调用 shipmenthandlingApi.disableShipment() 执行删除操作
  6. 更新页面:删除完成后更新页面显示

5.3 箱标地址管理流程

  1. 用户点击添加箱标地址:触发添加箱标地址操作
  2. 打开地址选择对话框:显示地址选择对话框
  3. 用户选择地址:用户从地址列表中选择箱标收货地址
  4. 保存箱标地址:调用 shipmentPlacementApi.saveDestinationBox() 保存箱标地址
  5. 更新页面:保存完成后更新页面显示

5.4 货件复制流程

  1. 用户点击复制按钮:触发复制货件操作
  2. 系统跳转:系统跳转到添加货件页面
  3. 创建新货件:基于原货件信息创建新的货件

5.5 本地完成流程

  1. 用户点击本地完成按钮:触发本地完成操作
  2. 显示确认对话框:显示确认本地完成的对话框
  3. 用户确认操作:用户确认执行本地完成操作
  4. 执行本地完成:调用 shipmenthandlingApi.localDoneShipment() 执行本地完成操作
  5. 更新页面:操作完成后更新页面显示

6. 核心代码分析

6.1 前端核心代码

6.1.1 获取货件信息

function getBaseInfo(shipmentid) {
  shipmentPlacementApi.getBaseInfo({
    "shipmentid": shipmentid
  }).then(res => {
    if (res.data) {
      var data = res.data;
      shipmentInfo.value = data.shipmentAll;
      shipment.value = data.shipment;
      plan.value = data.plan;
      volume.value = data.totalBoxSize;
      shiptype.value = data.shipment.transtyle;
      // 设置箱子数量
      if (data.listbox && data.listbox.length > 0) {
        boxnum.value = data.listbox.length;
      } else {
        boxnum.value = data.shipment.boxnum;
      }
      // 设置总货值
      if (data.detail) {
        totalprice.value = "¥" + getValue(parseFloat(data.detail.itemprice));
      }
      // 设置地址信息
      if (data["fromAddress"]) {
        fromAddress.value = data["fromAddress"];
      }
      if (data["toAddress"]) {
        toAddress.value = data["toAddress"];
      }
      if (data["toAddressBox"]) {
        toAddressBox.value = data["toAddressBox"];
      }
      // 设置物流费用
      if (data.transinfo == null || data.transinfo == undefined) {
        totalfee.value = "0";
      } else {
        if (!data.transinfo.otherfee) data.transinfo.otherfee = 0;
        if (!data.transinfo.transweight) data.transinfo.transweight = 0;
        if (!data.transinfo.singleprice) data.transinfo.singleprice = 0;
        totalfee.value = ("¥" + formatFloat(parseFloat(parseFloat(data.transinfo.transweight) * parseFloat(data.transinfo.singleprice)) + parseFloat(data.transinfo.otherfee)));
      }
      // 设置重量信息
      boxweightvalue.value = data.totalweight;
      if (data.transinfo && data.transinfo.transweight) {
        if (data.transinfo.wunit) {
          weightvalue.value = (data.transinfo.transweight + "" + getValue(data.transinfo.wunit));
        } else {
          if (data.transchannel.priceunits == "weight") {
            weightvalue.value = (data.transinfo.transweight + "kg");
          } else {
            weightvalue.value = (data.transinfo.transweight + "cbm");
          }
        }
      }
      // 设置实际重量
      if (data.detail) {
        readyweightvalue.value = (getValue(data.detail.readweight));
      }
      emit("change", data);
    }
  })
}

6.1.2 删除货件

function deleteShipment() {
  // 先弹窗打开modal 获取最新的货件状态
  dialogVisible.value = true;
  statusLoading.value = true;
  var status = shipDatas.value.shipmentstatus;
  shipmenthandlingApi.requestInboundShipment({
    "shipmentid": shipmentid
  }).then(res => {
    if (res.data != "fail") {
      status = res.data;
      shipstatus.value = res.data;
      if (status != "CANCELLED") {
        visibleBtn.value = "";
        canceltitle.value = "亚马逊后台货件状态为" + status + ",请确认是否同步删除亚马逊货件?";
      } else {
        canceltitle.value = "亚马逊后台货件状态为" + status + ",请确认是否删除本地货件?";
      }
    } else {
      visibleBtn.value = "";
      canceltitle.value = "亚马逊后台货件状态无法判断,请选择仅删除本地货件。";
    }
    statusLoading.value = false;
    emit("change");
  })
}

function confirmDelete(ftype) {
  var nowstatus = "";
  if (ftype == "local") {
    nowstatus = "DELETED";
  } else {
    nowstatus = shipstatus.value;
  }
  confirmCancelLoading.value = true;
  shipmenthandlingApi.disableShipment({
    "shipmentid": shipmentid,
    "shipmentStatus": nowstatus,
    "disableShipment": "1"
  }).then(res => {
    ElMessage.success('操作成功!');
    confirmCancelLoading.value = false;
    dialogVisible.value = false;
    context.emit("change");
  }).catch(error => {
    confirmCancelLoading.value = false;
  })
}

6.2 后端核心代码

6.2.1 获取货件详细信息

@GetMapping("/getBaseInfo")
public Result<Map<String, Object>> getBaseInfoAction(@ApiParam("货件ID")@RequestParam String shipmentid) {
  ShipInboundShipment ship = null;
  if(shipmentid.contains("FBA")){
    ship=shipInboundShipmentV2Service.lambdaQuery().eq(ShipInboundShipment::getShipmentConfirmationId,shipmentid).one();
  }else{
    ship=shipInboundShipmentV2Service.lambdaQuery().eq(ShipInboundShipment::getShipmentid,shipmentid).one();
  }
  ShipInboundShipmenSummarytVo data = shipInboundShipmentV2Service.summaryShipmentItemWithoutItem(ship.getShipmentid());
  BeanUtil.copyProperties(ship, data,"itemList");
  ShipInboundPlan plan = shipInboundPlanV2Service.getById(ship.getFormid());
  if(plan!=null) {
    data.setMarketplaceid(plan.getMarketplaceid());
    data.setTranstyle(ship.getTranstyle());
    data.setCountryCode(marketplaceService.findMapByMarketplaceId().get(plan.getMarketplaceid()).getMarket());
  }
  List<ShipInboundShipment> shipments = shipInboundShipmentV2Service.lambdaQuery().eq(ShipInboundShipment::getFormid, ship.getFormid()).list();
  List<String> shipmentids=new LinkedList<String>();
  for(ShipInboundShipment shipment:shipments) {
    shipmentids.add(shipment.getShipmentid());
  }
  plan.setShipmentids(shipmentids);
  Map<String, Object> map = getItemPriceAction(ship.getShipmentid());
  SummaryShipmentVo detail = shipInboundShipmentV2Service.showPlanListByPlanid( ship.getShipmentid());
  map.put("detail", detail);
  map.put("plan", plan);
  ship.setShipmentstatus(shipInboundShipmentV2Service.getShipmentStatusName(ship.getShipmentstatus()));
  map.put("shipment", ship);
  map.put("shipmentid", ship.getShipmentid());
  ShipAddress fromAddress = shipAddressService.getById(plan.getSourceAddress());
  ShipInboundDestinationAddress toAddress = shipInboundShipmentV2Service.getToAddress(ship.getDestination());
  if(ship.getDestinationbox()!=null){
    ShipInboundDestinationAddress toAddressBox = shipInboundShipmentV2Service.getToAddress(ship.getDestinationbox());
    map.put("toAddressBox", toAddressBox);
  }
  map.put("toAddress", toAddress);
  map.put("fromAddress", fromAddress);
  map.put("shipmentAll",data);
  if(plan.getAreCasesRequired()!=null&&plan.getAreCasesRequired()==true){
    if((plan.getSubmitbox()==null||plan.getSubmitbox()==false)&&ship.getStatus()==3) {
      this.getEditBoxDetialCaseAction(map, ship, plan);
    }else {
      getBoxDetialCaseAction(map,ship,plan);
    }
  }else{
    if((plan.getSubmitbox()==null||plan.getSubmitbox()==false)&&ship.getStatus()==3) {
      this.getEditBoxDetialAction(map, ship, plan);
    }else {
      getBoxDetialAction(map,ship,plan);
    }
  }
  getShipAmazonInfoAction(map,ship);
  return Result.success(map);
}

6.2.2 保存箱标收货地址

@ApiOperation(value = "保存发货装箱箱标上的地址")
@SystemControllerLog("保存箱标地址")
@GetMapping("/saveDestinationBox")
public Result<String> saveDestinationBoxAction(String shipmentid,String destinationBox) {
  UserInfo user=UserInfoContext.get();
  ShipInboundShipment shipment = shipInboundShipmentV2Service.getById(shipmentid);
  if(destinationBox.equals("NA")){
    shipment.setDestinationbox(null);
  }else{
    shipment.setDestinationbox(destinationBox);
  }
  shipInboundShipmentV2Service.updateById(shipment);
  return Result.success();
}

7. 技术实现

7.1 前端技术

技术/框架 版本 用途
Vue 3.x 前端框架
Element Plus 最新版 UI 组件库
Icon Park 最新版 图标库
Axios 最新版 HTTP 客户端

7.2 后端技术

技术/框架 版本 用途
Spring Boot 最新版 后端框架
MyBatis Plus 最新版 ORM 框架
Swagger 最新版 API 文档
MySQL 最新版 数据库

8. 输入输出示例

8.1 获取货件信息

输入

const { data } = await shipmentPlacementApi.getBaseInfo({ shipmentid: "shipment123" });

输出

{
  "code": 0,
  "msg": "",
  "data": {
    "shipmentAll": {
      "number": "FBA202601230001",
      "name": "测试货件",
      "shipmentConfirmationId": "FBA1234567890",
      "referenceid": "REF123",
      "shipmentstatus": "WORKING",
      "groupname": "测试店铺",
      "country": "US",
      "warehouse": "深圳仓库",
      "remark": "测试货件",
      "skuamount": 2,
      "sumQuantity": 100
    },
    "shipment": {
      "shipmentid": "shipment123",
      "shipmentConfirmationId": "FBA1234567890",
      "destination": "destination1",
      "destinationbox": "destination2",
      "transtyle": "SP",
      "status": 3
    },
    "plan": {
      "id": "plan123",
      "sourceAddress": "address1",
      "marketplaceid": "US",
      "areCasesRequired": false
    },
    "detail": {
      "itemprice": 5000
    },
    "fromAddress": {
      "name": "发货地址",
      "addressline1": "深圳市南山区",
      "city": "深圳",
      "stateorprovincecode": "广东",
      "postalcode": "518000",
      "countrycode": "CN"
    },
    "toAddress": {
      "name": "收货地址",
      "addressLine1": "123 Main St",
      "city": "Los Angeles",
      "stateOrProvinceCode": "CA",
      "postalCode": "90001",
      "countryCode": "US"
    },
    "toAddressBox": {
      "name": "箱标收货地址",
      "addressLine1": "456 Oak St",
      "city": "New York",
      "stateOrProvinceCode": "NY",
      "postalCode": "10001",
      "countryCode": "US"
    },
    "totalweight": 25,
    "totalBoxSize": 0.5,
    "transinfo": {
      "transweight": 25,
      "singleprice": 10,
      "otherfee": 50
    }
  }
}

8.2 保存箱标收货地址

输入

const { data } = await shipmentPlacementApi.saveDestinationBox({ 
  shipmentid: "shipment123", 
  destinationBox: "destination2" 
});

输出

{
  "code": 0,
  "msg": "",
  "data": ""
}

8.3 删除货件

输入

const { data } = await shipmenthandlingApi.disableShipment({
  shipmentid: "shipment123",
  shipmentStatus: "CANCELLED",
  disableShipment: "1"
});

输出

{
  "code": 0,
  "msg": "",
  "data": true
}

9. 业务流程图

9.1 货件信息加载流程

flowchart TD
    A[用户进入货件处理页面] --> B[获取货件ID]
    B --> C[调用getBaseInfo API]
    C --> D[后端处理请求]
    D --> E[查询货件信息]
    E --> F[查询地址信息]
    F --> G[查询箱子信息]
    G --> H[计算费用信息]
    H --> I[返回货件详细信息]
    I --> J[前端渲染页面]
    J --> K[显示货件信息]

9.2 货件删除流程

flowchart TD
    A[用户点击删除按钮] --> B[调用requestInboundShipment API]
    B --> C[获取亚马逊货件状态]
    C --> D[显示确认删除对话框]
    D --> E{用户选择删除方式}
    E -->|仅删除本地| F[设置状态为DELETED]
    E -->|同步删除亚马逊货件| G[使用当前状态]
    F --> H[调用disableShipment API]
    G --> H
    H --> I[后端处理删除请求]
    I --> J[更新货件状态]
    J --> K[返回删除结果]
    K --> L[前端显示操作结果]
    L --> M[更新页面显示]

9.3 箱标地址管理流程

flowchart TD
    A[用户点击添加箱标地址] --> B[打开地址选择对话框]
    B --> C[用户选择箱标收货地址]
    C --> D[调用saveDestinationBox API]
    D --> E[后端处理保存请求]
    E --> F[更新货件的箱标地址]
    F --> G[返回保存结果]
    G --> H[前端显示操作结果]
    H --> I[更新页面显示]

10. 代码优化建议

10.1 前端优化

  1. 错误处理增强:添加更详细的错误处理和用户提示,特别是在 API 调用失败时
  2. 性能优化:对于大量数据的货件,考虑使用虚拟滚动技术,提高页面加载和滚动性能
  3. 代码组织:将复杂的业务逻辑拆分为更小的函数,提高代码可读性
  4. 用户体验:添加更多的加载状态和操作反馈,提升用户体验
  5. 代码复用:提取重复的代码为公共函数或组件
  6. 内存管理:清理定时器和事件监听器,避免内存泄漏

10.2 后端优化

  1. 性能优化:对于频繁查询的数据,考虑添加缓存机制,提高系统响应速度
  2. 错误处理:增强后端错误处理,提供更详细的错误信息,便于前端处理
  3. 事务管理:确保所有数据库操作都在事务中执行,保证数据一致性
  4. API 设计:统一 API 接口设计,使用 RESTful 风格的 API 设计
  5. 日志记录:添加详细的日志记录,便于问题排查
  6. 代码组织:优化代码结构,提高代码可读性和可维护性

11. 常见问题及解决方案

问题 原因 解决方案
货件状态同步失败 网络连接问题或亚马逊 API 限制 检查网络连接,稍后重试操作
箱标地址保存失败 地址信息不完整或格式错误 检查地址信息,确保所有必填字段都已填写
货件删除失败 亚马逊后台货件状态不允许删除 先确认亚马逊后台货件状态,再执行删除操作
页面加载缓慢 货件信息过多或网络延迟 优化网络连接,考虑分页加载大量数据
费用计算错误 物流信息不完整或汇率更新不及时 检查物流信息,确保汇率数据已更新

12. 功能亮点

12.1 完整的货件信息展示

系统提供了全面的货件信息展示,包括基本信息、运输信息、地址信息等,使用户能够一目了然地了解货件的详细情况。信息展示采用卡片式布局,结构清晰,视觉效果良好。

12.2 便捷的货件操作管理

系统提供了丰富的货件操作功能,包括删除、复制、本地完成等,操作流程简单直观,用户可以轻松完成各种货件管理任务。

12.3 智能的状态同步

系统能够与亚马逊后台同步货件状态,确保货件状态的准确性和实时性。在删除货件时,系统会先获取亚马逊后台的货件状态,然后根据状态提供不同的删除选项。

12.4 灵活的箱标地址管理

系统支持为货件添加箱标收货地址,用户可以根据需要灵活管理货件的收货地址,提高了系统的灵活性和适用性。

12.5 准确的费用计算

系统能够自动计算货件的物流费用和货值,帮助用户了解货件的成本情况。费用计算基于货件的重量、体积和物流方式等因素,计算结果准确可靠。

13. 结论

发货-货件处理模块是 Wimoor 系统中 FBA 发货流程的核心环节,为用户提供了全面、便捷的货件管理功能。该模块通过前端与后端的紧密协作,实现了货件信息的展示、操作管理、状态同步等功能,为用户的 FBA 发货流程提供了有力的支持。

该模块的实现展示了现代前端开发和后端开发的最佳实践,包括:

  • 使用 Vue 3 Composition API 进行状态管理
  • 与后端 API 的高效交互
  • 响应式 UI 设计
  • 良好的用户体验
  • 模块化的代码组织
  • 清晰的控制器设计
  • 灵活的服务层架构
  • 高效的数据处理

总体而言,发货-货件处理模块是一个设计精良、功能完善的业务组件,为 FBA 发货流程提供了重要的支持,帮助用户高效地管理和处理货件,提高了物流管理的效率和准确性。