1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| CREATE TABLE InventoryTransaction (
TransactionID INT PRIMARY KEY IDENTITY(1,1), -- 流水ID
ItemID INT NOT NULL, -- 物料ID
WarehouseID INT NOT NULL, -- 仓库ID
TransactionType TINYINT NOT NULL, -- 交易类型(1:入库, 2:出库, 3:调拨, 4:盘点)
Quantity DECIMAL(10,2) NOT NULL, -- 数量
UnitPrice DECIMAL(10,2), -- 单价
TotalAmount DECIMAL(12,2), -- 总金额
RelatedOrderID VARCHAR(50), -- 关联订单号
OperatorID INT NOT NULL, -- 操作人员ID
DepartmentID INT, -- 部门ID
TransactionTime DATETIME NOT NULL, -- 交易时间
RecordTime DATETIME NOT NULL DEFAULT GETDATE(), -- 记录时间
Notes VARCHAR(500), -- 备注
Status TINYINT NOT NULL DEFAULT 2 -- 状态(0(无效)、1(有效/已审批)、2(待审批)、3(已拒绝))
)
|
<font style="background-color:rgb(250, 250, 250);">
状态字段扩展 </font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
:将Status字段含义扩展为:0(无效)、1(有效/已审批)、2(待审批)、3(已拒绝)</font>
<font style="background-color:rgb(250, 250, 250);">
MyBatis实现</font>
<font style="background-color:rgb(250, 250, 250);">
Mapper接口设计</font>
1
2
3
4
5
6
7
8
9
10
11
12
13
| public interface InventoryTransactionMapper {
// 查询待审批的库存流水记录
List<InventoryTransaction> findPendingTransactions(Map<String, Object> params);
// 批量更新流水状态
int batchUpdateStatus(@Param("ids") List<Integer> transactionIds,
@Param("status") int status,
@Param("approverID") int approverID);
// 获取流水详情
InventoryTransaction getTransactionById(int transactionId);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| <mapper namespace="com.irrigation.warehouse.mapper.InventoryTransactionMapper">
<select id="findPendingTransactions" resultMap="transactionResultMap">
SELECT t.*, i.ItemName, w.WarehouseName, o.OperatorName
FROM InventoryTransaction t
JOIN Item i ON t.ItemID = i.ItemID
JOIN Warehouse w ON t.WarehouseID = w.WarehouseID
JOIN Operator o ON t.OperatorID = o.OperatorID
WHERE t.Status = 2
<if test="itemId != null">AND t.ItemID = #{itemId}</if>
<if test="warehouseId != null">AND t.WarehouseID = #{warehouseId}</if>
<if test="startDate != null">AND t.TransactionTime >= #{startDate}</if>
<if test="endDate != null">AND t.TransactionTime <= #{endDate}</if>
ORDER BY t.TransactionTime DESC
</select>
<update id="batchUpdateStatus">
UPDATE InventoryTransaction
SET Status = #{status},
Notes = CONCAT(Notes, '; 审批时间: ', CONVERT(VARCHAR, GETDATE(), 120),
', 审批人: ', (SELECT OperatorName FROM Operator WHERE OperatorID = #{approverID}))
WHERE TransactionID IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
</mapper>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| @Service
@Transactional
public class InventoryApprovalServiceImpl implements InventoryApprovalService {
@Autowired
private InventoryTransactionMapper transactionMapper;
@Override
public Page<InventoryTransaction> getPendingTransactions(TransactionQueryDTO queryParams) {
// 构建查询参数并执行分页查询
PageHelper.startPage(queryParams.getPageNum(), queryParams.getPageSize());
List<InventoryTransaction> transactions = transactionMapper.findPendingTransactions(buildQueryMap(queryParams));
return new PageInfo<>(transactions).toPage();
}
@Override
public boolean approveTransactions(ApprovalDTO approvalDTO) {
// 执行批量审批,更新状态
return transactionMapper.batchUpdateStatus(
approvalDTO.getTransactionIds(),
approvalDTO.isApproved() ? 1 : 3, // 1=已批准, 3=已拒绝
approvalDTO.getApproverID()
) > 0;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| @RestController
@RequestMapping("/api/inventory/approval")
public class InventoryApprovalController {
@Autowired
private InventoryApprovalService approvalService;
@GetMapping("/pending")
public ResultDTO<Page<InventoryTransaction>> getPendingTransactions(TransactionQueryDTO queryParams) {
return ResultDTO.success(approvalService.getPendingTransactions(queryParams));
}
@PostMapping("/submit")
public ResultDTO<Boolean> submitApproval(@RequestBody ApprovalDTO approvalDTO) {
boolean result = approvalService.approveTransactions(approvalDTO);
return result ? ResultDTO.success(true) : ResultDTO.fail("审批失败,请重试");
}
}
|
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
前端Vue实现</font>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // api/inventory.js
import request from '@/utils/request'
export function getPendingTransactions(query) {
return request({
url: '/api/inventory/approval/pending',
method: 'get',
params: query
})
}
export function submitApproval(data) {
return request({
url: '/api/inventory/approval/submit',
method: 'post',
data
})
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
| <template>
<!-- 假设这是已有的库存流水列表页面 -->
<div class="inventory-list">
<!-- 已有的搜索功能区域 -->
<div class="existing-search-area">
<!-- 现有代码... -->
</div>
<!-- 添加的操作按钮区域 -->
<div class="operation-buttons">
<el-button
type="primary"
icon="el-icon-check"
:disabled="selectedRows.length === 0"
@click="handleApprove">审批</el-button>
<el-button
type="danger"
icon="el-icon-close"
:disabled="selectedRows.length === 0"
@click="handleReject">拒绝</el-button>
</div>
<!-- 已有的表格组件,添加@selection-change事件 -->
<el-table
ref="inventoryTable"
:data="inventoryList"
@selection-change="handleSelectionChange">
<!-- 添加选择列 -->
<el-table-column type="selection" width="55" />
<!-- 现有的表格列... -->
</el-table>
<!-- 现有的分页组件... -->
</div>
</template>
<script>
// 引入已有的API方法,添加submitApproval方法
import { submitApproval } from '@/api/inventory'
export default {
// 扩展现有组件
data() {
// 添加选中行数据
return {
selectedRows: []
}
},
methods: {
// 处理表格选择变化
handleSelectionChange(val) {
this.selectedRows = val
},
// 处理审批操作
handleApprove() {
if (this.selectedRows.length === 0) return
this.$confirm(`确认审批选中的 ${this.selectedRows.length} 条记录?`, '确认信息', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.submitApprovalRequest(true)
})
},
// 处理拒绝操作
handleReject() {
if (this.selectedRows.length === 0) return
this.$confirm(`确认拒绝选中的 ${this.selectedRows.length} 条记录?`, '确认信息', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.submitApprovalRequest(false)
})
},
// 提交审批请求
submitApprovalRequest(isApproved) {
const transactionIds = this.selectedRows.map(item => item.transactionID)
submitApproval({
transactionIds,
approved: isApproved,
approverID: this.$store.getters.userId
}).then(() => {
this.$message.success('操作成功')
// 刷新列表数据
this.refreshList()
// 清空选择
this.$refs.inventoryTable.clearSelection()
}).catch(error => {
this.$message.error('操作失败: ' + error.message)
})
},
// 刷新列表数据(调用现有方法)
refreshList() {
// 调用已有的获取列表数据方法
this.getInventoryList()
}
}
}
</script>
<style scoped>
.operation-buttons {
margin: 10px 0;
text-align: right;
}
</style>
|
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
接口测试设计–库存审批接口</font>
<font style="background-color:rgb(250, 250, 250);">
接口基本信息</font>
<font style="background-color:rgb(250, 250, 250);">
接口URL</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
:</font><font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
/api/inventory/approval/submit</font>
<font style="background-color:rgb(250, 250, 250);">
请求方法</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
: POST</font>
<font style="background-color:rgb(250, 250, 250);">
Content-Type</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
: application/json</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);"></font>
测试用例设计
正常审批测试
请求体:
1
2
3
4
5
6
| {
"transactionIds": [1001, 1002, 1003],
"approved": true,
"approverID": 101
}
|
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
预期响应:</font>
1
2
3
4
5
6
7
| {
"code": 200,
"success": true,
"message": "审批成功",
"data": true
}
|
<font style="background-color:rgb(250, 250, 250);">
正常拒绝测试</font>
请求体:
1
2
3
4
5
6
| {
"transactionIds": [1004, 1005],
"approved": false,
"approverID": 101
}
|
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
预期响应:</font>
1
2
3
4
5
6
7
| {
"code": 200,
"success": true,
"message": "拒绝成功",
"data": true
}
|
<font style="background-color:rgb(250, 250, 250);">
异常测试 - 审批人ID不存在</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
请求体:</font>
1
2
3
4
5
6
| {
"transactionIds": [1006],
"approved": true,
"approverID": 999 // 不存在的审批人ID
}
|
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
预期响应:</font>
1
2
3
4
5
6
7
| {
"code": 400,
"success": false,
"message": "审批人不存在",
"data": null
}
|
<font style="background-color:rgb(250, 250, 250);">
异常测试 - 流水ID不存在</font>
请求体:
1
2
3
4
5
6
| {
"transactionIds": [9999], // 不存在的流水ID
"approved": true,
"approverID": 101
}
|
预期响应:
1
2
3
4
5
6
7
| {
"code": 404,
"success": false,
"message": "未找到指定的流水记录",
"data": null
}
|
<font style="background-color:rgb(250, 250, 250);">
异常测试 - 权限不足</font>
请求体:
1
2
3
4
5
6
| {
"transactionIds": [1007],
"approved": true,
"approverID": 102 // 无权限的审批人ID
}
|
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
预期响应:</font>
1
2
3
4
5
6
7
| {
"code": 403,
"success": false,
"message": "无权限执行此操作",
"data": null
}
|
<font style="background-color:rgb(250, 250, 250);">
测试前置条件</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
确保测试数据库中存在ID为1001-1007的待审批记录</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
确保ID为101的用户有审批权限,ID为102的用户无审批权限</font>
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
确保测试环境的接口认证已正确设置(如需要Token)</font>
<font style="background-color:rgb(250, 250, 250);">
Postman测试</font>
<font style="background-color:rgb(250, 250, 250);">
设置环境变量</font>
1
2
| baseUrl: http://your-test-server:port
token: eyJhbGciOiJIUzI1NiJ9... // 认证token
|
<font style="background-color:rgb(250, 250, 250);">
添加预请求脚本</font>
1
2
3
4
5
6
7
8
9
10
11
12
| // 如需要在请求前动态获取token
pm.sendRequest({
url: pm.environment.get("baseUrl") + "/api/auth/login",
method: "POST",
header: { "Content-Type": "application/json" },
body: { mode: "raw", raw: JSON.stringify({ username: "testuser", password: "password" }) }
}, function(err, res) {
if (!err) {
pm.environment.set("token", res.json().data.token);
}
});
|
<font style="background-color:rgb(250, 250, 250);">
添加测试脚本</font>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // 验证响应状态
pm.test("Status code is 200", function() {
pm.response.to.have.status(200);
});
// 验证响应内容
pm.test("Response is successful", function() {
var jsonData = pm.response.json();
pm.expect(jsonData.success).to.be.true;
pm.expect(jsonData.code).to.equal(200);
});
// 响应时间测试
pm.test("Response time is acceptable", function() {
pm.expect(pm.response.responseTime).to.be.below(300);
});
|
<font style="color:rgb(55, 65, 81);background-color:rgb(250, 250, 250);">
</font>