feat(asset): 新增资产库存明细功能

- 创建 AmsAssetStock 实体类用于库存汇总统计
- 实现 AmsAssetStockController 提供库存查询和导出功能
- 开发 AmsAssetStockMapper 和 XML 映射文件进行数据库操作
- 添加 AmsAssetStockService 业务逻辑处理层
- 集成前端 stock.html 页面实现库存汇总和明细查看
- 支持按分类、名称、仓库、状态等条件筛选库存
- 实现点击汇总行查看对应资产明细的功能
- 添加单元测试验证库存查询逻辑的正确性
main
yangk 2 days ago
parent d587e1a8b1
commit 5fae393ac0

@ -0,0 +1,117 @@
package com.ruoyi.asset.controller;
import java.util.List;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetCategory;
import com.ruoyi.asset.domain.AmsAssetStock;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.service.IAmsAssetCategoryService;
import com.ruoyi.asset.service.IAmsAssetStockService;
import com.ruoyi.asset.service.IAmsWarehouseService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
/**
* Controller
*
* @author Yangk
*/
@Controller
@RequestMapping("/asset/stock")
public class AmsAssetStockController extends BaseController
{
private static final String ENABLED_YES = "Y";
private String prefix = "asset/stock";
@Autowired
private IAmsAssetStockService amsAssetStockService;
@Autowired
private IAmsAssetCategoryService amsAssetCategoryService;
@Autowired
private IAmsWarehouseService amsWarehouseService;
@RequiresPermissions("asset:stock:view")
@GetMapping()
public String stock(ModelMap mmap)
{
putStockOptions(mmap);
mmap.put("defaultAssetStatus", AssetStatus.IN_STOCK);
return prefix + "/stock";
}
/**
*
*/
@RequiresPermissions("asset:stock:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(AmsAssetStock amsAssetStock)
{
startPage();
List<AmsAssetStock> list = amsAssetStockService.selectAmsAssetStockList(amsAssetStock);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions("asset:stock:detail")
@PostMapping("/detail")
@ResponseBody
public TableDataInfo detail(AmsAssetStock amsAssetStock)
{
startPage();
List<AmsAsset> list = amsAssetStockService.selectAmsAssetStockDetailList(amsAssetStock);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions("asset:stock:export")
@Log(title = "资产库存明细", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ResponseBody
public AjaxResult export(AmsAssetStock amsAssetStock)
{
List<AmsAssetStock> list = amsAssetStockService.selectAmsAssetStockList(amsAssetStock);
ExcelUtil<AmsAssetStock> util = new ExcelUtil<AmsAssetStock>(AmsAssetStock.class);
return util.exportExcel(list, "资产库存明细数据");
}
private void putStockOptions(ModelMap mmap)
{
mmap.put("categoryList", selectEnabledCategoryList());
mmap.put("warehouseList", selectEnabledWarehouseList());
}
private List<AmsAssetCategory> selectEnabledCategoryList()
{
AmsAssetCategory category = new AmsAssetCategory();
category.setEnabled(ENABLED_YES);
return amsAssetCategoryService.selectAmsAssetCategoryList(category);
}
private List<AmsWarehouse> selectEnabledWarehouseList()
{
AmsWarehouse warehouse = new AmsWarehouse();
warehouse.setEnabled(ENABLED_YES);
return amsWarehouseService.selectAmsWarehouseList(warehouse);
}
}

@ -0,0 +1,241 @@
package com.ruoyi.asset.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.core.domain.BaseEntity;
/**
*
*
* @author Yangk
*/
public class AmsAssetStock extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 内部汇总键,用于下钻明细时精确匹配空值维度 */
private String groupKey;
/** 资产类别ID */
private Long categoryId;
/** 类别编码 */
private String categoryCode;
/** 类别名称 */
@Excel(name = "资产类别")
private String categoryName;
/** 资产名称 */
@Excel(name = "资产名称")
private String assetName;
/** 规格型号 */
@Excel(name = "规格型号")
private String specModel;
/** 品牌 */
@Excel(name = "品牌")
private String brand;
/** 仓库ID */
private Long warehouseId;
/** 仓库编码 */
private String warehouseCode;
/** 仓库名称 */
@Excel(name = "所属仓库")
private String warehouseName;
/** 位置ID */
private Long locationId;
/** 位置编码 */
private String locationCode;
/** 位置名称 */
private String locationName;
/** 资产状态 */
@Excel(name = "资产状态", dictType = "ams_asset_status")
private String assetStatus;
/** 库存数量 */
@Excel(name = "库存数量", cellType = ColumnType.NUMERIC)
private Long stockQuantity;
public String getGroupKey()
{
return groupKey;
}
public void setGroupKey(String groupKey)
{
this.groupKey = groupKey;
}
public Long getCategoryId()
{
return categoryId;
}
public void setCategoryId(Long categoryId)
{
this.categoryId = categoryId;
}
public String getCategoryCode()
{
return categoryCode;
}
public void setCategoryCode(String categoryCode)
{
this.categoryCode = categoryCode;
}
public String getCategoryName()
{
return categoryName;
}
public void setCategoryName(String categoryName)
{
this.categoryName = categoryName;
}
public String getAssetName()
{
return assetName;
}
public void setAssetName(String assetName)
{
this.assetName = assetName;
}
public String getSpecModel()
{
return specModel;
}
public void setSpecModel(String specModel)
{
this.specModel = specModel;
}
public String getBrand()
{
return brand;
}
public void setBrand(String brand)
{
this.brand = brand;
}
public Long getWarehouseId()
{
return warehouseId;
}
public void setWarehouseId(Long warehouseId)
{
this.warehouseId = warehouseId;
}
public String getWarehouseCode()
{
return warehouseCode;
}
public void setWarehouseCode(String warehouseCode)
{
this.warehouseCode = warehouseCode;
}
public String getWarehouseName()
{
return warehouseName;
}
public void setWarehouseName(String warehouseName)
{
this.warehouseName = warehouseName;
}
public Long getLocationId()
{
return locationId;
}
public void setLocationId(Long locationId)
{
this.locationId = locationId;
}
public String getLocationCode()
{
return locationCode;
}
public void setLocationCode(String locationCode)
{
this.locationCode = locationCode;
}
public String getLocationName()
{
return locationName;
}
public void setLocationName(String locationName)
{
this.locationName = locationName;
}
public String getAssetStatus()
{
return assetStatus;
}
public void setAssetStatus(String assetStatus)
{
this.assetStatus = assetStatus;
}
public Long getStockQuantity()
{
return stockQuantity;
}
public void setStockQuantity(Long stockQuantity)
{
this.stockQuantity = stockQuantity;
}
@Override
public String toString()
{
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("groupKey", getGroupKey())
.append("categoryId", getCategoryId())
.append("categoryCode", getCategoryCode())
.append("categoryName", getCategoryName())
.append("assetName", getAssetName())
.append("specModel", getSpecModel())
.append("brand", getBrand())
.append("warehouseId", getWarehouseId())
.append("warehouseCode", getWarehouseCode())
.append("warehouseName", getWarehouseName())
.append("locationId", getLocationId())
.append("locationCode", getLocationCode())
.append("locationName", getLocationName())
.append("assetStatus", getAssetStatus())
.append("stockQuantity", getStockQuantity())
.toString();
}
}

@ -0,0 +1,29 @@
package com.ruoyi.asset.mapper;
import java.util.List;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetStock;
/**
* Mapper
*
* @author Yangk
*/
public interface AmsAssetStockMapper
{
/**
*
*
* @param amsAssetStock
* @return
*/
public List<AmsAssetStock> selectAmsAssetStockList(AmsAssetStock amsAssetStock);
/**
*
*
* @param amsAssetStock
* @return
*/
public List<AmsAsset> selectAmsAssetStockDetailList(AmsAssetStock amsAssetStock);
}

@ -0,0 +1,29 @@
package com.ruoyi.asset.service;
import java.util.List;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetStock;
/**
* Service
*
* @author Yangk
*/
public interface IAmsAssetStockService
{
/**
*
*
* @param amsAssetStock
* @return
*/
public List<AmsAssetStock> selectAmsAssetStockList(AmsAssetStock amsAssetStock);
/**
*
*
* @param amsAssetStock
* @return
*/
public List<AmsAsset> selectAmsAssetStockDetailList(AmsAssetStock amsAssetStock);
}

@ -0,0 +1,58 @@
package com.ruoyi.asset.service.impl;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetStock;
import com.ruoyi.asset.mapper.AmsAssetStockMapper;
import com.ruoyi.asset.service.IAmsAssetStockService;
import com.ruoyi.common.utils.StringUtils;
/**
* Service
*
* @author Yangk
*/
@Service
public class AmsAssetStockServiceImpl implements IAmsAssetStockService
{
@Autowired
private AmsAssetStockMapper amsAssetStockMapper;
/**
*
*/
@Override
public List<AmsAssetStock> selectAmsAssetStockList(AmsAssetStock amsAssetStock)
{
AmsAssetStock query = defaultStockQuery(amsAssetStock);
return amsAssetStockMapper.selectAmsAssetStockList(query);
}
/**
*
*/
@Override
public List<AmsAsset> selectAmsAssetStockDetailList(AmsAssetStock amsAssetStock)
{
AmsAssetStock query = defaultStockQuery(amsAssetStock);
if (StringUtils.isEmpty(query.getGroupKey()))
{
return Collections.emptyList();
}
return amsAssetStockMapper.selectAmsAssetStockDetailList(query);
}
private AmsAssetStock defaultStockQuery(AmsAssetStock amsAssetStock)
{
AmsAssetStock query = amsAssetStock == null ? new AmsAssetStock() : amsAssetStock;
if (query.getAssetStatus() == null)
{
query.setAssetStatus(AssetStatus.IN_STOCK);
}
return query;
}
}

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.asset.mapper.AmsAssetStockMapper">
<resultMap type="AmsAssetStock" id="AmsAssetStockResult">
<result property="groupKey" column="group_key" />
<result property="categoryId" column="category_id" />
<result property="categoryCode" column="category_code" />
<result property="categoryName" column="category_name" />
<result property="assetName" column="asset_name" />
<result property="specModel" column="spec_model" />
<result property="brand" column="brand" />
<result property="warehouseId" column="warehouse_id" />
<result property="warehouseCode" column="warehouse_code" />
<result property="warehouseName" column="warehouse_name" />
<result property="assetStatus" column="asset_status" />
<result property="stockQuantity" column="stock_quantity" />
</resultMap>
<resultMap type="AmsAsset" id="AmsAssetStockDetailResult">
<result property="assetId" column="asset_id" />
<result property="assetCode" column="asset_code" />
<result property="assetName" column="asset_name" />
<result property="categoryId" column="category_id" />
<result property="categoryCode" column="category_code" />
<result property="categoryName" column="category_name" />
<result property="specModel" column="spec_model" />
<result property="brand" column="brand" />
<result property="warehouseId" column="warehouse_id" />
<result property="warehouseCode" column="warehouse_code" />
<result property="warehouseName" column="warehouse_name" />
<result property="locationId" column="location_id" />
<result property="locationCode" column="location_code" />
<result property="locationName" column="location_name" />
<result property="useDeptName" column="use_dept_name" />
<result property="useUserName" column="use_user_name" />
<result property="assetStatus" column="asset_status" />
<result property="tagCode" column="tag_code" />
<result property="epcCode" column="epc_code" />
</resultMap>
<sql id="assetStockGroupKey">
sha2(concat_ws('|',
ifnull(cast(category_id as char), '__NULL__'),
ifnull(category_code, '__NULL__'),
ifnull(category_name, '__NULL__'),
ifnull(asset_name, '__NULL__'),
ifnull(spec_model, '__NULL__'),
ifnull(brand, '__NULL__'),
ifnull(cast(warehouse_id as char), '__NULL__'),
ifnull(warehouse_code, '__NULL__'),
ifnull(warehouse_name, '__NULL__'),
ifnull(asset_status, '__NULL__')
), 256)
</sql>
<sql id="assetStockWhere">
<where>
del_flag = '0'
<if test="categoryId != null"> and category_id = #{categoryId}</if>
<if test="assetName != null and assetName != ''"> and asset_name like concat('%', #{assetName}, '%')</if>
<if test="specModel != null and specModel != ''"> and spec_model like concat('%', #{specModel}, '%')</if>
<if test="brand != null and brand != ''"> and brand like concat('%', #{brand}, '%')</if>
<if test="warehouseId != null"> and warehouse_id = #{warehouseId}</if>
<if test="assetStatus != null and assetStatus != ''"> and asset_status = #{assetStatus}</if>
</where>
</sql>
<select id="selectAmsAssetStockList" parameterType="AmsAssetStock" resultMap="AmsAssetStockResult">
select
<include refid="assetStockGroupKey"/> as group_key,
category_id,
category_code,
category_name,
asset_name,
spec_model,
brand,
warehouse_id,
warehouse_code,
warehouse_name,
asset_status,
count(1) as stock_quantity
from ams_asset
<include refid="assetStockWhere"/>
group by
category_id,
category_code,
category_name,
asset_name,
spec_model,
brand,
warehouse_id,
warehouse_code,
warehouse_name,
asset_status
order by category_name, asset_name, spec_model, brand, warehouse_name, asset_status
</select>
<select id="selectAmsAssetStockDetailList" parameterType="AmsAssetStock" resultMap="AmsAssetStockDetailResult">
select asset_id,
asset_code,
asset_name,
category_id,
category_code,
category_name,
spec_model,
brand,
warehouse_id,
warehouse_code,
warehouse_name,
location_id,
location_code,
location_name,
use_dept_name,
use_user_name,
asset_status,
tag_code,
epc_code
from ams_asset
where del_flag = '0'
and asset_status = #{assetStatus}
and <include refid="assetStockGroupKey"/> = #{groupKey}
order by asset_code
</select>
</mapper>

@ -0,0 +1,230 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('资产库存明细')" />
<th:block th:include="include :: select2-css" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<li>
<label>资产类别:</label>
<select name="categoryId" class="select2-control" style="width: 170px">
<option value="">所有</option>
<option th:each="category : ${categoryList}" th:text="${category.categoryName}" th:value="${category.categoryId}"></option>
</select>
</li>
<li>
<label>资产名称:</label>
<input type="text" name="assetName"/>
</li>
<li>
<label>规格型号:</label>
<input type="text" name="specModel"/>
</li>
<li>
<label>品牌:</label>
<input type="text" name="brand"/>
</li>
<li>
<label>所属仓库:</label>
<select name="warehouseId" class="select2-control" style="width: 170px">
<option value="">所有</option>
<option th:each="warehouse : ${warehouseList}" th:text="${warehouse.warehouseName}" th:value="${warehouse.warehouseId}"></option>
</select>
</li>
<li>
<label>资产状态:</label>
<select name="assetStatus" th:with="type=${@dict.getType('ams_asset_status')}">
<option value="">所有</option>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:selected="${dict.dictValue == defaultAssetStatus}"></option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="searchStock()"><i class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="resetSearch()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="asset:stock:export">
<i class="fa fa-download"></i> 导出汇总
</a>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="stock-table"></table>
</div>
<div class="col-sm-12">
<h4 class="form-header h4">库存构成明细</h4>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="detail-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: select2-js" />
<script th:inline="javascript">
var detailFlag = [[${@permission.hasPermi('asset:stock:detail')}]];
var assetStatusDatas = [[${@dict.getType('ams_asset_status')}]];
var defaultAssetStatus = [[${defaultAssetStatus}]];
var prefix = ctx + "asset/stock";
var currentStockRow = null;
$(function() {
$(".select2-control").select2({
placeholder: "请选择",
allowClear: true
});
initDetailTable();
initStockTable();
});
function initDetailTable() {
var detailOptions = {
id: "detail-table",
toolbar: "detail-toolbar",
url: prefix + "/detail",
firstLoad: false,
modalName: "库存构成明细",
showSearch: false,
showRefresh: false,
showColumns: false,
showToggle: false,
queryParams: function(params) {
params = $.extend(params, $.common.formToJSON("formId"));
params.groupKey = currentStockRow ? currentStockRow.groupKey : "";
params.assetStatus = currentStockRow ? currentStockRow.assetStatus : defaultAssetStatus;
return params;
},
columns: [{
field: 'assetCode',
title: '资产编码'
},
{
field: 'assetName',
title: '资产名称'
},
{
field: 'categoryName',
title: '资产类别'
},
{
field: 'specModel',
title: '规格型号'
},
{
field: 'brand',
title: '品牌'
},
{
field: 'warehouseName',
title: '所属仓库'
},
{
field: 'locationName',
title: '存放位置'
},
{
field: 'assetStatus',
title: '资产状态',
formatter: function(value, row, index) {
return $.table.selectDictLabel(assetStatusDatas, value);
}
},
{
field: 'tagCode',
title: '标签编码'
},
{
field: 'epcCode',
title: 'EPC编码'
}]
};
$.table.init(detailOptions);
}
function initStockTable() {
var options = {
id: "stock-table",
url: prefix + "/list",
exportUrl: prefix + "/export",
modalName: "资产库存明细",
columns: [{
field: 'groupKey',
title: '汇总键',
visible: false
},
{
field: 'categoryName',
title: '资产类别'
},
{
field: 'assetName',
title: '资产名称'
},
{
field: 'specModel',
title: '规格型号'
},
{
field: 'brand',
title: '品牌'
},
{
field: 'warehouseName',
title: '所属仓库'
},
{
field: 'assetStatus',
title: '资产状态',
formatter: function(value, row, index) {
return $.table.selectDictLabel(assetStatusDatas, value);
}
},
{
field: 'stockQuantity',
title: '库存数量',
align: 'right'
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
actions.push('<a class="btn btn-info btn-xs ' + detailFlag + '" href="javascript:void(0)" onclick="showStockDetail(' + index + ')"><i class="fa fa-list"></i>查看明细</a>');
return actions.join('');
}
}]
};
$.table.init(options);
}
function showStockDetail(index) {
currentStockRow = $("#stock-table").bootstrapTable("getData")[index];
$("#detail-table").bootstrapTable("refresh", {pageNumber: 1});
}
function searchStock() {
currentStockRow = null;
$("#detail-table").bootstrapTable("removeAll");
$.table.search("formId", "stock-table");
}
function resetSearch() {
$.form.reset();
$(".select2-control").val("").trigger("change");
$("select[name='assetStatus']").val(defaultAssetStatus).trigger("change");
searchStock();
}
</script>
</body>
</html>

@ -0,0 +1,101 @@
package com.ruoyi.asset.service.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetStock;
import com.ruoyi.asset.mapper.AmsAssetStockMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class AmsAssetStockServiceImplTest
{
@Mock
private AmsAssetStockMapper amsAssetStockMapper;
@InjectMocks
private AmsAssetStockServiceImpl service;
/** 库存页默认看在库资产,避免空状态查询把待入库、报废等非库存数据混入汇总 */
@Test
void selectAmsAssetStockListShouldDefaultToInStock()
{
AmsAssetStock query = new AmsAssetStock();
when(amsAssetStockMapper.selectAmsAssetStockList(query)).thenReturn(List.of());
service.selectAmsAssetStockList(query);
ArgumentCaptor<AmsAssetStock> captor = ArgumentCaptor.forClass(AmsAssetStock.class);
verify(amsAssetStockMapper).selectAmsAssetStockList(captor.capture());
assertEquals(AssetStatus.IN_STOCK, captor.getValue().getAssetStatus());
}
/** 用户显式选择状态时必须尊重筛选条件,支持查看维修中、借用中等资产分布 */
@Test
void selectAmsAssetStockListShouldRespectExplicitStatus()
{
AmsAssetStock query = new AmsAssetStock();
query.setAssetStatus(AssetStatus.REPAIRING);
when(amsAssetStockMapper.selectAmsAssetStockList(query)).thenReturn(List.of());
service.selectAmsAssetStockList(query);
ArgumentCaptor<AmsAssetStock> captor = ArgumentCaptor.forClass(AmsAssetStock.class);
verify(amsAssetStockMapper).selectAmsAssetStockList(captor.capture());
assertEquals(AssetStatus.REPAIRING, captor.getValue().getAssetStatus());
}
@Test
void selectAmsAssetStockListShouldReturnStockQuantityFromMapper()
{
AmsAssetStock row = new AmsAssetStock();
row.setAssetName("手持终端");
row.setSpecModel("PDA-X1");
row.setStockQuantity(3L);
when(amsAssetStockMapper.selectAmsAssetStockList(any(AmsAssetStock.class))).thenReturn(List.of(row));
List<AmsAssetStock> result = service.selectAmsAssetStockList(new AmsAssetStock());
assertEquals(1, result.size());
assertEquals(3L, result.get(0).getStockQuantity());
}
/** 没有汇总键时不允许直接查明细,防止空条件误扫整张资产表 */
@Test
void selectAmsAssetStockDetailListShouldReturnEmptyWhenGroupKeyBlank()
{
AmsAssetStock query = new AmsAssetStock();
List<AmsAsset> result = service.selectAmsAssetStockDetailList(query);
assertTrue(result.isEmpty());
verify(amsAssetStockMapper, never()).selectAmsAssetStockDetailList(any(AmsAssetStock.class));
}
@Test
void selectAmsAssetStockDetailListShouldUseGroupKeyAndDefaultStatus()
{
AmsAssetStock query = new AmsAssetStock();
query.setGroupKey("group-1");
when(amsAssetStockMapper.selectAmsAssetStockDetailList(query)).thenReturn(List.of(new AmsAsset()));
service.selectAmsAssetStockDetailList(query);
ArgumentCaptor<AmsAssetStock> captor = ArgumentCaptor.forClass(AmsAssetStock.class);
verify(amsAssetStockMapper).selectAmsAssetStockDetailList(captor.capture());
assertEquals("group-1", captor.getValue().getGroupKey());
assertEquals(AssetStatus.IN_STOCK, captor.getValue().getAssetStatus());
}
}
Loading…
Cancel
Save