发货-发货详情(新)
发货详情报表模块功能解析文档发货详情报表模块(ShipV2)功能解析
1. 系统架构模块架构
1.1 整体架构前端架构
发货详情报表模块采用前后端分离架构,主要包含以下组件:
前端组件核心文件:wimoor-ui/src/views/amazon/report/shipv2/detail/index.vue
wimoor-ui/src/api/amazon/inbound/reportV2Api.js
1.2 模块依赖关系后端架构
ShipInboundReportV2Controller.java
服务层:IShipInboundPlanService.java、ShipInboundPlanServiceImpl.java
数据访问:ShipInboundPlanV2Mapper.java、ShipInboundPlanV2Mapper.xml
技术栈:Spring Boot、MyBatis Plus、MySQL
1.3 数据流
flowchart前端用户操作 TD→ A[前端detail/index.vue]筛选条件设置 -->→ B[reportApi.js]API调用 B→ -->后端控制器处理 C[ShipInboundReportController]→ C服务层业务逻辑 -->→ D[ShipInboundPlanService]Mapper数据查询 D→ -->数据库 E[ShipInboundPlanMapper]→ E数据返回 -->→ F[MySQL数据库]
D --> G[ShipInboundItemService]
G --> H[产品信息数据库]前端渲染
2. 前端实现
2.1 核心文件结构核心组件结构
wimoor-ui/src/views/amazon/report/ship/detail/
├── index.vue # 主页面组件
├── components/ # 子组件目录
└── ...
wimoor-ui/src/api/amazon/inbound/
├── reportApi.js # 报表API接口
└── reportV2Api.js # 报表V2 API接口
2.2 核心组件分析
2.2.1 主页面组件(index.vue)
文件路径:wimoor-ui/src/views/amazon/report/ship/detail/index.vue
主要功能:
核心代码结构:
<template>
<div class="main-sty">
<!-- 标签页切换 -->
<el-tabs v-model="selecttype" @tab-change="handleQuery">
<el-tab-pane label="按货件汇总" name="shipment" key="shipment"></el-tab-pane>
<el-tab-pane label="按SKU汇总" name="sku" key="sku"></el-tab-pane>
</el-tabs>
<!-- 筛选条件区筛选条件 -->
<div class="filter-bar">
<!-- 店铺选择 -->
<el-rowselect v-model="queryParam.groupid" @change="handleQuery">
<div!-- class="con-header"选项 -->
</el-spaceselect>
<Group!-- @change="changeGroup"日期类型选择 /-->
<el-select v-model="queryParam.datetype" @change="handleQuery">
<el-option label="创建日期" value="createdate" label="创建日期"></el-option>
<el-option value="deliverydate" label="发货日期" value="senddate"></el-option>
</el-select>
<Datepicker!-- longtime="ok"日期范围选择 @changedate="changedate" /-->
<Warehouseel-date-picker @changeware=v-model="getWarehouse"dateRange" defaultValue=@change="handleQuery"></el-date-picker>
<!-- 仓库选择 -->
<el-select v-model="queryParam.warehouseid" @change="handleQuery">
<el-option label="全部" value="all"></el-option>
<!-- 选项 -->
</el-select>
<!-- 货件编码搜索 -->
<el-input v-model="queryParam.search" placeholder=@keyup.enter="请输入货件编码" handleQuery"></>
<el-popover v-model:visible="moreSearchVisible"input>
<!-- 高级筛选 -->
</el-popoverbutton @click="openFilter">
筛选</el-spacebutton>
</div>
<!-- 操作按钮 -->
<div class="operation-bar">
<el-button @click="refresh">刷新</el-rowbutton>
<el-button @click="handleExport">导出</el-button>
<!-- 按货件汇总表格其他按钮 -->
</div>
<!-- 数据表格 -->
<el-rowtable v-if=loading="selecttype=='shipment'">
<GlobalTable ref="globalTable"isLoading" :tableData=data="tableData" @loadTable="loadTableData"tableData.records">
<!-- 表格列定义列定义 -->
</GlobalTable>
</el-rowtable>
<!-- 按SKU汇总表格分页 -->
<el-rowpagination v-else@size-change="handleSizeChange" @current-change="handleCurrentChange">
<GlobalTable ref="skuglobalTable" :tableData="skutableData" @loadTable="skuloadTableData">
<!-- 表格列定义 -->
</GlobalTable>
</el-rowpagination>
</div>
</template>
状态管理:
2.2 核心数据结构
2.2.1 响应式状态
letconst state = reactive({
selecttype: 'shipment', // 当前选择的视图类型
dateRange: [], // 日期范围
queryParam: {
search:groupid: "",undefined, marketplaceid:// "",店铺ID
datetype: "createdate"'createdate', companyid:// ""日期类型
fromdate: '', // 开始日期
enddate: '', // 结束日期
warehouseid: 'all', // 仓库ID
search: '', // 搜索关键词
hasexceptionnum: "all"'', // 接收异常
carrierName: '' // 承运商
},
skusummary: null,
isload: true,
tableData: {
records: [], // 表格数据
total: 0 // 总记录数
},
isLoading: false, // 加载状态
skutableData: {
records: [], // SKU表格数据
total: 0 // SKU总记录数
},
companylist:sumQty: 0, // 总发货数量
sumReceivedQty: 0, // 总接收数量
selectList: [], channellist:// [],
moreSearchVisible: false,
selecttype: "shipment"选中的记录
});
核心方法:
2.2.2
function// handleQuery()reportV2Api.js
export default {
nextTick(()getShipmentReportByLoistics, =>// {按物流商获取货件报表
ifgetShipmentReportByWarehouseLoistics, (state.selecttype// ==按仓库和物流商获取货件报表
"shipment")getShipmentDetailReport, {// var获取货件详情报表
timergetShipmentReport, =// setTimeout(function()获取货件报表
{downShipmentExcel, globalTable.value.loadTable(state.queryParam);// clearTimeout(timer);导出货件Excel
downExcelShipmentReportByLoistics, // 导出物流商货件报表
getShipmentReportByWarehouseLoistics
}, 500);
} else {
var timer = setTimeout(function() {
skuglobalTable.value.loadTable(state.queryParam);
clearTimeout(timer);
}, 500);
}
});
}
2.3
2.3.1 加载货件汇总数据
// 加载货件汇总数据
function loadTableData(params) {
state.isLoading = true;
reportApi.getShipmentReport(params).then((res) => {
state.isloadisLoading = false;
state.tableData.records = res.data.records;
state.tableData.total = res.data.total;
});
}
function skuloadTableData(params) {
state.isLoading = true;
reportApi.getShipmentDetailReport(params).then((res) => {
state.isloadisLoading = false;
state.skutableData.records = res.data.records;
if (params.currentpage == 1) {
state.skusummary = res.data && res.data.records && res.data.records.length > 0
? res.data.records[0].summary
: null;
}
state.skutableData.total = res.data.total;
// 统计总发货和总接收数量
state.sumQty = res.data.records.reduce((sum, item) => sum + (item.sendqty || 0), 0);
state.sumReceivedQty = res.data.records.reduce((sum, item) => sum + (item.receivedqty || 0), 0);
});
}
2.3.2
function handleQuery() -{
导出数据if (state.selecttype === 'shipment') {
state.queryParam.pageNum = 1;
loadTableData(state.queryParam);
} else {
state.queryParam.pageNum = 1;
skuloadTableData(state.queryParam);
}
}
function handleSizeChange(val) {
state.queryParam.pageSize = val;
handleQuery();
}
function handleCurrentChange(val) {
state.queryParam.pageNum = val;
handleQuery();
}
2.3.3 导出方法
function downloadList(ftype) {
if (ftype == "shiptask") {
// 导出发货处理任务量
findProcessHandle({
"fromdate": state.queryParam.fromdate,
"enddate": state.queryParam.enddate
}enddate});
} else if (ftype == "shipqty") {
// 导出发货数量简约版
inventoryRptApi.downloadOutstockformOut({
"fromdate": state.queryParam.fromdate,
"enddate": state.queryParam.enddate
}enddate});
} else {
// 其他导出类型
state.queryParam.downloadType = ftype;
reportApi.downShipmentExcel(state.queryParam, () => {
state.downLoading = false;
});
}
}
2.34 API接口层前端交互逻辑
wimoor-ui/src/api/amazon/inbound/reportApi.jsselecttype状态和标签页切换,调用不同的数据加载方法
handleQuery方法重新加载数据
分页:分页操作通过//handleSizeChange和handleCurrentChange方法处理
3. 后端实现
3.1 控制器层
3.1.1 ShipInboundReportController核心API接口
文件路径:
wimoor-amazon/amazon-boot/src/main/java/com/wimoor/amazon/inbound/controller/ShipInboundReportController.java/api/v2/ship/report/getShipmentReport
主要功能:提供发货报表相关的RESTful
核心接口:
getShipmentReport()/api/v2/ship/report/getShipmentDetailReport-POST获取货件汇总报表
/api/v2/ship/report/downShipmentExcel
POST
导出货件报表Excel
/api/v2/ship/report/getShipmentReportByLoistics
POST
按物流商获取货件报表
/api/v2/ship/report/getShipmentReportByWarehouseLoistics
POST
按仓库和物流商获取货件报表
/api/v2/ship/report/downExcelShipmentReportByLoistics
POST
导出物流商货件报表Excel
3.1.2 控制器方法实现
@PostMapping(value = "/getShipmentReport")
public Result<IPage<Map<String, Object>>> getShipmentReport(@RequestBody ShipInboundShipmenSummaryDTO dto) {
Map<String, Object> param = new HashMap<String, Object>();
// 参数处理
UserInfo user = UserInfoContext.get();
param.put("shopid", user.getCompanyid());
// 处理marketplaceid
String marketplaceid = dto.getMarketplaceid();
if (StrUtil.isEmpty(marketplaceid)) {
marketplaceid = null;
}
param.put("marketplaceid", marketplaceid);
// 处理groupid
String groupid = dto.getGroupid(getMarketplaceid());
if (StrUtil.isEmpty(groupid)) {
groupid = null;
}
param.put("groupid", groupid);
// 处理搜索条件
String search = dto.getSearch(getGroupid());
if (StrUtil.isNotEmpty(search)) {
param.put("search", search.trim(dto.getSearch() + "%");
} else {
param.put("search", null);
}
// 处理日期类型
String datetype = dto.getDatetype();
param.put("datetype", datetype);
// 处理日期范围
String fromDate = dto.getFromdate();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
if (StrUtil.isNotEmpty(fromDate)) {
param.put("fromDate", fromDate.trim(getDatetype());
} else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, -7);
fromDate = GeneralUtil.formatDate(cal.getTime(), sdf);
param.put("fromDate", fromDate);
}
String toDate = dto.getEnddate(getFromdate();
if (StrUtil.isNotEmpty(toDate)) {
param.put("endDate", toDate.trim().substring(0, 10) + " 23:59:59");
} else {
toDate = GeneralUtil.formatDate(new Date(), sdf);
param.put("endDate", toDate + " 23:59:59"dto.getEnddate());
}
// 处理仓库
String warehouseid = dto.getWarehouseid();
if (StrUtil.isEmpty(warehouseid) || "all".equals(warehouseid)) {
warehouseid = null;
}
param.put("warehouseid", warehouseid);
// 处理承运商
String company = dto.getCompany(getWarehouseid());
if (StrUtil.isEmpty(company)) {
param.put("company", null);
} else {
param.put("company", company + "%"dto.getCompany());
}
String companyid = dto.getCompanyid();
if (StrUtil.isEmpty(companyid)) {
param.put("companyid", null);
} else {
param.put("companyid", companyid);
}
// 处理接收异常
String iserror = dto.getHasexceptionnum(getCompanyid());
if (StrUtil.isEmpty(iserror) || "all".equals(iserror)) {
iserror = null;
}
param.put("iserror", iserror)dto.getHasexceptionnum());
// 调用服务层获取数据调用服务层
IPage<Map<String, Object>> pagelist = shipInboundPlanService.getShipmentReport(dto.getPage(), param);
return Result.success(pagelist);
}
@PostMapping(value = "/getShipmentDetailReport")
public Result<IPage<Map<String, Object>>> getShipmentDetailReport(@RequestBody ShipInboundShipmenSummaryDTO dto, HttpServletResponse response) {
Map<String, Object> param = new HashMap<String, Object>();
UserInfo user = UserInfoContext.get();
String shopid = user.getCompanyid();
param.put("shopid", shopid);
String marketplaceid = dto.getMarketplaceid();
if (StrUtil.isEmpty(marketplaceid)) {
marketplaceid = null;
}
param.put("marketplaceid", marketplaceid);
String groupid = dto.getGroupid();
if (StrUtil.isEmpty(groupid)) {
groupid = null;
}
param.put("groupid", groupid);
String search = dto.getSearch();
if (StrUtil.isNotEmpty(search)) {
param.put("search", search.trim() + "%");
} else {
param.put("search", null);
}
String datetype = dto.getDatetype();
param.put("datetype", datetype);
String fromDate = dto.getFromdate();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
if (StrUtil.isNotEmpty(fromDate)) {
param.put("fromDate", fromDate.trim());
} else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, -7);
fromDate = GeneralUtil.formatDate(cal.getTime(), sdf);
param.put("fromDate", fromDate);
}
String toDate = dto.getEnddate();
if (StrUtil.isNotEmpty(toDate)) {
param.put("endDate", toDate.trim().substring(0, 10) + " 23:59:59");
} else {
toDate = GeneralUtil.formatDate(new Date(), sdf);
param.put("endDate", toDate + " 23:59:59");
}
String warehouseid = dto.getWarehouseid();
if (StrUtil.isEmpty(warehouseid) || "all".equals(warehouseid)) {
warehouseid = null;
}
param.put("warehouseid", warehouseid);
String company = dto.getCompany();
if (StrUtil.isEmpty(company)) {
param.put("company", null);
} else {
param.put("company", company + "%");
}
String iserror = dto.getHasexceptionnum();
if (StrUtil.isEmpty(iserror) || "all".equals(iserror)) {
iserror = null;
}
param.put("iserror", iserror);
IPage<Map<String, Object>> pagelist = shipInboundPlanService.getShipmentDetailReport(dto.getPage(), param);
return Result.success(pagelist);
}
@PostMapping(value = "/downShipmentExcel")
public void downShipmentExcelAction(@RequestBody ShipInboundShipmenSummaryDTO dto, HttpServletResponse response) {
try {
// 创建新的Excel工作薄创建Excel工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook();
response.setContentType("application/force-download");
response.addHeader("Content-Disposition", "attachment;fileName=Shipmenttemplate.xlsx");
ServletOutputStream fOut = response.getOutputStream();
// 参数处理
UserInfo user = UserInfoContext.get();
String shopid = user.getCompanyid();
// 构建查询参数
Map<String, Object> params = new HashMap<String, Object>();
Stringparams.put("shopid", marketplaceid = dto.getMarketplaceid(user.getCompanyid();
String datetype = dto.getDatetype();
params.put("datetype", datetype)dto.getDatetype());
if (StrUtil.isEmpty(marketplaceid)) {
marketplaceid = null;
}
params.put("marketplaceid", marketplaceid);
String groupid = dto.getGroupid(getMarketplaceid());
if (StrUtil.isEmpty(groupid)) {
groupid = null;
}
params.put("groupid", groupid);
String search = dto.getSearch(getGroupid());
if (StrUtil.isNotEmpty(search)) {
params.put("search", search.trim() + "%");
} else {
params.put("search", null);
}
String fromDate = dto.getFromdate();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
if (StrUtil.isNotEmpty(fromDate)) {
params.put("fromDate", fromDate.trim(getSearch());
} else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, -7);
fromDate = GeneralUtil.formatDate(cal.getTime(), sdf);
params.put("fromDate", fromDate);
}
String toDate = dto.getEnddate(getFromdate();
if (StrUtil.isNotEmpty(toDate)) {
params.put("endDate", toDate.trim().substring(0, 10) + " 23:59:59");
} else {
toDate = GeneralUtil.formatDate(new Date(), sdf);
params.put("endDate", toDate + " 23:59:59"dto.getEnddate();
}
params.put("shopid", shopid));
params.put("ftype", dto.getDownloadType());
// 调用服务层生成Excel
shipInboundPlanService.setExcelBookByType(workbook, params);
// 输出Excel
workbook.write(fOut);
workbook.close();
fOut.flush();
fOut.close();
} catch (Exception e) {
e.printStackTrace();
}
}
3.2 服务层
3.2.1 ShipInboundPlanServiceImpl服务接口定义
文件路径:wimoor-amazon/amazon-boot/src/main/java/com/wimoor/amazon/inbound/service/impl/ShipInboundPlanServiceImpl.java
主要功能:实现发货报表的业务逻辑
核心方法:
public interface IShipInboundPlanService extends IService<ShipInboundPlan>, IRunAmazonService {
// 其他方法...
/**
* 获取货件详情报表
*/
IPage<Map<String, Object>> getShipmentDetailReport(Page<Object> page, Map<String, Object> param);
/**
* 获取货件报表
*/
IPage<Map<String, Object>> getShipmentReport(Page<Object> page, Map<String, Object> param);
/**
* 按类型设置Excel工作簿
*/
void setExcelBookByType(SXSSFWorkbook workbook, Map<String, Object> params);
}
3.2.2 服务实现
@Override
public IPage<Map<String, Object>> getShipmentReport(Page<?Object> page, Map<String, Object> param) {
return this.baseMapper.getShipmentReport(page, param);
}
@Override
public IPage<Map<String, Object>> getShipmentDetailReport(Page<Object> page, Map<String, Object> param) {
IPage<Map<String, Object>> result =return this.baseMapper.getShipmentDetailReport(page, param);
if (result != null && result.getRecords().size() > 0 && page.getCurrent() == 1) {
Map<String, Object> summary = this.baseMapper.getShipmentDetailReportTotal(param);
Map<String, Object> map = result.getRecords().get(0);
map.put("summary", summary);
}
return@Override
result;
}
public void setExcelBookByType(SXSSFWorkbook workbook, Map<String, Object> params) {
String type = params.get("ftype").toString();
// 货件发货明细
if ("shipment".equals(type)) {
// 货件汇总导出
Map<String, Object> titlemap = new LinkedHashMap<String, Object>();
titlemap.put("ShipmentId", "货件编码");
titlemap.put("groupname", "发货店铺");
titlemap.put("market", "收货站点");
titlemap.put("center", "配送中心");
titlemap.put("warehouse"warehousename", "发货仓库");
titlemap.put("createdate",// "创建日期");
titlemap.put("shiped_date", "发货日期");
titlemap.put("arrivalTime", "预计到货日期");
titlemap.put("start_receive_date", "开始接受日期");
titlemap.put("status6date", "完成日期");
titlemap.put("company", "承运商");
titlemap.put("transtype", "运输方式");
titlemap.put("channame", "发货渠道");
titlemap.put("qtyshipped", "发货数量");
titlemap.put("qtyreceived", "到货数量");
titlemap.put("price", "运输费用");
titlemap.put("otherfee", "其它费用");
titlemap.put("totalprice", "物流费用统计");
titlemap.put("transweight", "实际计费重量");
titlemap.put("weightkg", "预估发货重量");
titlemap.put("boxweight", "装箱实际重量(kg)");
titlemap.put("boxvolume", "装箱材积重量");
titlemap.put("wunit", "重量单位");
titlemap.put("shipmentstatus", "状态");更多列定义...
List<Map<String, Object>> list = this.baseMapper.getShipmentReport(params);
Sheet sheet = workbook.createSheet("sheet1");
// 创建标题行写入表头和数据
Row} trowelse = sheet.createRow(0);
Object[] titlearray = titlemap.keySet().toArray();
forif (int i = 0; i < titlearray.length; i++"sku".equals(type)) {
Cell// cell = trow.createCell(i);
Object value = titlemap.get(titlearray[i].toString());
cell.setCellValue(value.toString());
}SKU汇总导出
// 填充数据行
for (int i = 0; i < list.size(); i++) {
Row row = sheet.createRow(i + 1);
Map<String, Object> map = list.get(i);
for (int j = 0; j < titlearray.length; j++) {
Cell cell = row.createCell(j);
Object value = map.get(titlearray[j]类似实现..toString());
if (value != null) {
cell.setCellValue(value.toString());.
}
}
}
}
// 其他类型的导出...
}
3.3 数据访问层
3.3.1 ShipInboundPlanMapperMapper接口
文件路径:wimoor-amazon/amazon-boot/src/main/java/com/wimoor/amazon/inbound/mapper/ShipInboundPlanMapper.java
核心方法:
public interface ShipInboundPlanV2Mapper extends BaseMapper<ShipInboundPlan> {
// 获取货件汇总报表(分页)其他方法...
/**
* 获取货件报表
*/
IPage<Map<String, Object>> getShipmentReport(Page<?Object> page, @Param("param") Map<String, Object> param);
/**
* 获取货件详情报表
*/
获取货件汇总报表(全部)IPage<Map<String, Object>> getShipmentDetailReport(Page<Object> page, @Param("param") Map<String, Object> param);
/**
* 获取货件报表(导出用)
*/
List<Map<String, Object>> getShipmentReport(@Param("param") Map<String, Object> param);
/**
* 获取货件详情报表(导出用)
*/ 获取SKU明细报表(分页)
IPage<Map<String, Object>> getShipmentDetailReport(Page<?> page, @Param("param")Map<String, Object> param);
// 获取SKU明细报表(全部)
List<Map<String, Object>> getShipmentDetailReport(@Param("param") Map<String, Object> param);
// 获取SKU明细报表统计
Map<String, Object> getShipmentDetailReportTotal(Map<String, Object> param);}
3.3.2 ShipInboundPlanMapper.xmlSQL实现
文件路径getShipmentReport(货件汇总):
wimoor-amazon/amazon-boot/src/main/resources/mapper/inbound/ShipInboundPlanMapper.xml<select id="getShipmentReport" parameterType="java.util.Map" resultType="java.util.Map">
SELECT
s.ShipmentId,
ag.name AS groupname,
wh.name AS warehousename,
s.createdate,
s.senddate,
s.receivedate,
s.completedate,
s.destination,
s.marketplace,
s.warehouseid,
s.estimateddeliverydate,
s.carrierName,
s.shippingchannel,
SUM(i.receivedqty) AS receivedqty,
SUM(i.quantity) AS sendqty,
SUM(s.fee) AS feecount,
s.weight,
s.weightfee,
s.otherfee,
s.actualweight,
s.estimateweight,
s.boxactualweight,
s.boxvolumeweight
FROM
t_erp_ship_v2_inboundplan s
LEFT JOIN
t_amazon_group ag ON ag.id = s.groupid
LEFT JOIN
t_erp_warehouse wh ON wh.id = s.warehouseid
LEFT JOIN
t_erp_ship_v2_inbounditem i ON i.formid = s.id
WHERE
s.shopid = #{param.shopid}
<if test="param.marketplaceid != null">
AND s.marketplaceid = #{param.marketplaceid}
</if>
<if test="param.groupid != null">
AND s.groupid = #{param.groupid}
</if>
<if test="param.search != null">
AND s.ShipmentId LIKE #{param.search}
</if>
<!-- 更多条件... -->
GROUP BY
s.ShipmentId
ORDER BY
s.createdate DESC
</select>
核心SQL查询getShipmentDetailReport(SKU汇总):
<select id="getShipmentDetailReport" parameterType="java.util.Map" resultType="java.util.Map">
selectSELECT
item.SellerSKU i.sku,
ship.s.ShipmentId,
inp.s.number,
g.ag.name AS groupname,
w.s.destination,
s.warehouseid,
wh.name warehouse,AS mkp.market,warehousename,
ship.shiped_date,s.senddate,
trans.arrivalTime,s.receivedate,
item.QuantityShipped,s.shippingchannel,
item.QuantityReceived,s.estimateddeliverydate,
inp.createdate,i.quantity ship.DestinationFulfillmentCenterIdAS center,sendqty,
detail.channame,i.receivedqty,
case when stat.`status`='WORKING' and ship.status>=5 then '已删除' else stat.name end shipmentstatus,
ifnull(item.QuantityReceived,0) sumrec, ship.s.status,
status0date,s.createdate
status1date,FROM
status2date,t_erp_ship_v2_inboundplan status3date,s
status4date,LEFT status5date,JOIN
status6datet_amazon_group fromag t_erp_ship_inbounditemON item
left join t_erp_ship_inboundshipment ship on ship.ShipmentId = item.ShipmentId
left join t_erp_ship_inboundtrans trans on trans.shipmentid = ship.ShipmentId
left join t_erp_ship_transdetail detail on detail.ag.id = trans.channels.groupid
leftLEFT joinJOIN
t_erp_ship_transcompanyt_erp_warehouse copwh onON cop.wh.id = detail.companys.warehouseid
leftLEFT joinJOIN
t_erp_ship_statust_erp_ship_v2_inbounditem stati onON stat.`status`i.formid = ship.ShipmentStatus
left join t_erp_ship_inboundplan inp on inp.s.id
=WHERE
item.inboundplanid
left join t_marketplace mkp on mkp.marketplaceId = inp.marketplaceid
left join t_amazon_group g on g.id = inp.amazongroupid
left join t_erp_warehouse w on w.id = inp.warehouseid
where inp.s.shopid = #{param.shopid,jdbcType=CHAR}
and ship.status > 1shopid}
<if test="param.endDatemarketplaceid != null">
<ifAND test="param.datetypes.marketplaceid == 'createdate'">
and inp.createdate >= #{param.fromDate,jdbcType=TIMESTAMP}
and inp.createdate <= #{param.endDate,jdbcType=TIMESTAMP}
</if>
<if test="param.datetype == 'deliverydate'">
and ship.shiped_date >= #{param.fromDate,jdbcType=TIMESTAMP}
and ship.shiped_date <= #{param.endDate,jdbcType=TIMESTAMP}
</if>marketplaceid}
</if>
<if test="param.groupid != null">
andAND inp.amazongroupids.groupid = #{param.groupid,jdbcType=CHAR}
</if>
<if test="param.marketplaceid != null">
and inp.marketplaceid = #{param.marketplaceid,jdbcType=CHAR}groupid}
</if>
<if test="param.search != null">
andAND (item.SellerSKUs.ShipmentId likeLIKE #{param.search,jdbcType=CHAR}search} orOR ship.ShipmentIdi.sku likeLIKE #{param.search,jdbcType=CHAR}
or inp.number like #{param.search,jdbcType=CHAR}search})
</if>
<if!-- test="param.company更多条件... != null"-->
andORDER cop.nameBY
like #{param.company,jdbcType=CHAR}
</if>
<if test="param.warehouseid != null">
and w.id = #{param.warehouseid,jdbcType=CHAR}
</if>
<if test="param.iserror != null">
<if test="param.iserror == 'true'">
and item.QuantityReceived != item.QuantityShipped
</if>
<if test="param.iserror == 'false'">
and item.QuantityReceived = item.QuantityShipped
</if>
</if>
</select>
<select id="getShipmentReport" parameterType="java.util.Map" resultType="java.util.Map">
select ship.ShipmentId,
max(g.name) groupname,
max(mkp.market) market,
max(ship.DestinationFulfillmentCenterId) center,
max(w.name) warehouse,
max(inp.createdate) createdate,
max(ship.shiped_date) shiped_date,
max(trans.arrivalTime) arrivalTime,
max(status6date) status6date,
max(company.name) company,
max(detail.channame) channame,
count(item.SellerSKU) skunum,
max(inp.number) number,
sum(item.QuantityShipped) qtyshipped,
sum(item.QuantityReceived) qtyreceived,
max(trans.otherfee) otherfee,
max(trans.singleprice * trans.transweight) price,
max(trans.transweight) transweight,
max(ship.weightkg) weightkg,
max(ship.boxweight) boxweight,
max(ship.boxvolume) boxvolume,
max(ship.wunit) wunit,
max(stat.name) shipmentstatus,
max(trans.transtype) transtype,
max(ship.start_receive_date) start_receive_date
from t_erp_ship_inbounditem item
left join t_erp_ship_inboundshipment ship on ship.ShipmentId = item.ShipmentId
left join t_erp_ship_inboundtrans trans on trans.shipmentid = ship.ShipmentId
left join t_erp_ship_transdetail detail on detail.id = trans.channel
left join t_erp_ship_transcompany company on company.id = detail.company
left join t_erp_ship_status stat on stat.`status` = ship.ShipmentStatus
left join t_erp_ship_inboundplan inp on inp.id = item.inboundplanid
left join t_marketplace mkp on mkp.marketplaceId = inp.marketplaceid
left join t_amazon_group g on g.id = inp.amazongroupid
left join t_erp_warehouse w on w.id = inp.warehouseid
where inp.shopid = #{param.shopid,jdbcType=CHAR}
and ship.status > 1
<if test="param.endDate != null">
<if test="param.datetype == 'createdate'">
and inp.s.createdate >= #{param.fromDate,jdbcType=TIMESTAMP}
and inp.createdate <= #{param.endDate,jdbcType=TIMESTAMP}
</if>
<if test="param.datetype == 'deliverydate'">
and ship.shiped_date >= #{param.fromDate,jdbcType=TIMESTAMP}
and ship.shiped_date <= #{param.endDate,jdbcType=TIMESTAMP}
</if>
</if>
<if test="param.groupid != null">
and inp.amazongroupid = #{param.groupid,jdbcType=CHAR}
</if>
<if test="param.marketplaceid != null">
and inp.marketplaceid = #{param.marketplaceid,jdbcType=CHAR}
</if>
<if test="param.search != null">
and (ship.ShipmentId like #{param.search,jdbcType=CHAR}
or inp.number like #{param.search,jdbcType=CHAR})
</if>
<if test="param.company != null">
and company.name like #{param.company,jdbcType=CHAR}
</if>
<if test="param.companyid != null">
and company.id = #{param.companyid,jdbcType=CHAR}
</if>
<if test="param.warehouseid != null">
and w.id = #{param.warehouseid,jdbcType=CHAR}
</if>
<if test="param.iserror != null">
<if test="param.iserror == 'true'">
and sum(item.QuantityReceived) != sum(item.QuantityShipped)
</if>
<if test="param.iserror == 'false'">
and sum(item.QuantityReceived) = sum(item.QuantityShipped)
</if>
</if>
group by ship.ShipmentIdDESC
</select>
4. 数据库设计功能特性
4.1 核心表结构双视图模式
4.1.12 t_erp_ship_inboundshipment(货件表)
多维度筛选
4.1.23 t_erp_ship_inbounditem(货件明细表)
数据导出
4.1.3 t_erp_ship_inboundtrans(货件运输表)
4.1.4 t_erp_ship_transdetail(运输详情表)
数据统计
- SKU汇总视图:自动计算总发货数量和总接收数量
- 货件汇总视图:自动汇总物流费用、运输重量等
4.1.5 t_erp_ship_transcompany(承运商表)
4.1.6 t_erp_ship_inboundplan(发货计划表)
4.1.7 t_amazon_group(店铺表)
4.1.8 t_marketplace(市场表)
4.1.9 t_erp_warehouse(仓库表)
4.1.10 t_erp_ship_status(货件状态表)
5. 业务流程技术亮点
5.1 数据查询流程前端技术亮点
sequenceDiagram5.2 数据导出流程后端技术亮点
sequenceDiagram5.3 Controller性能优化
6. API接口文档代码优化建议
6.1 获取货件汇总报表前端优化建议
POST将筛选条件、表格渲染等功能拆分为独立组件
{
"currentpage": 1,
"pagesize": 20,
"groupid": "店铺ID",
"marketplaceid": "市场ID",
"search": "货件编码",
"datetype": "createdate",
"fromdate": "2026-01-01",
"enddate": "2026-01-31",
"warehouseid": "仓库ID",
"company": "承运商名称",
"companyid": "承运商ID",
"hasexceptionnum": "all"
}
{虚拟滚动:对大数据量表格使用虚拟滚动,提高渲染性能
"code":错误处理:添加更完善的错误处理和用户提示
200,
"msg": "success",
"data": {
"records": [
{
"ShipmentId": "FBA123456789",
"groupname": "美国店铺",
"market": "US",
"center": "SBD1",
"warehouse": "深圳仓库",
"createdate": "2026-01-01 10:00:00",
"shiped_date": "2026-01-05 15:00:00",
"arrivalTime": "2026-01-15 00:00:00",
"start_receive_date": "2026-01-14 10:00:00",
"status6date": "2026-01-20 16:00:00",
"company": "DHL",
"channame": "空运",
"qtyshipped": 1000,
"qtyreceived": 998,
"price": 500.00,
"otherfee": 50.00,
"totalprice": 550.00,
"transweight": 500.5,
"weightkg": 502.0,
"boxweight": 501.0,
"boxvolume": 503.0,
"wunit": "kg",
"shipmentstatus": "已接收",
"transtype": "AIR"
}
],
"total": 100,
"size": 20,
"current": 1,
"pages": 5
}
}
6.2 获取SKU明细报表后端优化建议
POST使用Spring /api/v1/ship/report/getShipmentDetailReportValidation对请求参数进行更严格的验证
{
"currentpage": 1,
"pagesize": 20,
"groupid": "店铺ID",
"marketplaceid": "市场ID",
"search": "SKU或货件编码",
"datetype": "createdate",
"fromdate": "2026-01-01",
"enddate": "2026-01-31",
"warehouseid": "仓库ID",
"company": "承运商名称",
"hasexceptionnum": "all"
}
{性能监控:集成Spring "code":Boot 200,Actuator进行性能监控
"msg":SQL优化:进一步优化SQL查询,减少表关联次数
"success",
"data": {
"records": [
{
"sku": "SKU001",
"ShipmentId": "FBA123456789",
"number": "SF20260101001",
"groupname": "美国店铺",
"warehouse": "深圳仓库",
"market": "US",
"shiped_date": "2026-01-05 15:00:00",
"arrivalTime": "2026-01-15 00:00:00",
"QuantityShipped": 100,
"QuantityReceived": 98,
"createdate": "2026-01-01 10:00:00",
"center": "SBD1",
"channame": "空运",
"shipmentstatus": "已接收",
"summary": {
"QuantityShipped": 1000,
"QuantityReceived": 998
}
}
],
"total": 500,
"size": 20,
"current": 1,
"pages": 25
}
}
6.3 导出货件报表架构优化建议
POST /api/v1/ship/report/downShipmentExcel请求参数:
{
"groupid": "店铺ID",
"marketplaceid": "市场ID",
"search": "货件编码",
"datetype": "createdate",
"fromdate": "2026-01-01",
"enddate": "2026-01-31",
"warehouseid": "仓库ID",
"company": "承运商名称",
"downloadType": "shipment"
}
响应:Excel文件流
downloadType参数说明:
shipmentother缓存策略:shipqty消息队列:shiptaskAPI网关:7. 性能优化业务价值
7.1 数据库优化运营价值
索引优化数据可视化:在t_erp_ship_inboundshipment表的ShipmentId字段上建立索引通过报表直观展示发货数据,便于运营分析在t_erp_ship_inbounditem表的ShipmentId和SellerSKU字段上建立复合索引异常监控:及时发现和处理接收异常,减少损失在t_erp_ship_inboundplan表的shopid、amazongroupid、marketplaceid字段上建立索引
查询优化成本控制:
7.2 前端优化管理价值
延迟加载决策支持:使用setTimeout延迟查询请求,避免频繁调用基于数据做出更合理的发货决策使用nextTick确保DOM更新后再执行操作
分页加载绩效考核:
缓存优化风险控制:
7.3 后端优化技术价值
异步处理可扩展性:使用SXSSFWorkbook流式处理大数据量导出模块化设计,便于后续功能扩展使用异步任务处理耗时操作
参数验证可复用性:
8. 扩展功能总结
8.1
发货详情报表模块(ShipV2)是一个功能完善、技术先进的FBA发货数据分析工具,通过前端Vue 自定义列显示3和后端Spring Boot的完美结合,实现了货件和SKU两个维度的数据分析和管理。该模块不仅提供了丰富的筛选和导出功能,还通过多表关联查询和数据统计,为用户提供了全面、准确的发货数据,帮助用户更好地管理FBA发货流程,优化物流方案,降低运营成本。
用户可以根据需要自定义表格显示的列,提高数据查看的灵活性。未来,该模块可以进一步扩展,例如添加更多的数据分析维度、集成更多的物流商数据、提供更丰富的图表展示等,以满足用户日益增长的需求。
8.2 多格式导出
支持多种导出格式,满足不同的业务需求:
8.3 数据统计
在按SKU汇总视图下,自动汇总总发货数量和总接收数量,方便用户快速了解整体情况。
9. 注意事项
9.1 数据同步
9.2 权限控制
9.3 性能考虑
9.4 异常处理
10. 故障排查
10.1 数据不显示
可能原因:
解决方法:
10.2 导出失败
可能原因:
解决方法:
10.3 性能问题
可能原因:
解决方法:
文档版本:v1.0
最后更新:2026-01-26
适用系统:Wimoor FBA发货管理系统