feat(asset): 新增资产报废处置功能模块

- 创建报废处置申请页面,包含基本信息和明细信息表格
- 实现报废单据相关实体类 AmsDisposalOrder 和 AmsDisposalOrderItem
- 开发报废管理控制器 AmsDisposalOrderController 提供完整的CRUD操作
- 设计报废操作上下文类 AmsDisposalOperateContext 管理操作人员信息
- 实现报废单据的数据访问层和业务逻辑层功能
- 添加资产选择功能支持批量添加待报废资产
- 实现报废单据的状态流转包括提交、确认、驳回等操作
- 集成权限控制和数据验证机制确保操作安全性
main
yangk 1 week ago
parent d98a5046be
commit a26d6dc54f

@ -0,0 +1,25 @@
package com.ruoyi.asset.constant;
/**
*
*
* @author Yangk
*/
public final class DisposalOrderStatus
{
/** 草稿 */
public static final String DRAFT = "DRAFT";
/** 待确认 */
public static final String PENDING_CONFIRM = "PENDING_CONFIRM";
/** 已报废 */
public static final String DISPOSED_DONE = "DISPOSED_DONE";
/** 已驳回 */
public static final String REJECTED = "REJECTED";
private DisposalOrderStatus()
{
}
}

@ -0,0 +1,222 @@
package com.ruoyi.asset.controller;
import java.util.List;
import org.apache.shiro.authz.annotation.Logical;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsDisposalOperateContext;
import com.ruoyi.asset.domain.AmsDisposalOrder;
import com.ruoyi.asset.service.IAmsDisposalOrderService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
* Controller
*
* @author Yangk
* @date 2026-06-17
*/
@Controller
@RequestMapping("/asset/disposal")
public class AmsDisposalOrderController extends BaseController
{
private String prefix = "asset/disposal";
@Autowired
private IAmsDisposalOrderService amsDisposalOrderService;
@RequiresPermissions("asset:disposal:view")
@GetMapping()
public String disposal()
{
return prefix + "/disposal";
}
/**
*
*/
@RequiresPermissions("asset:disposal:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(AmsDisposalOrder amsDisposalOrder)
{
startPage();
List<AmsDisposalOrder> list = amsDisposalOrderService.selectAmsDisposalOrderList(amsDisposalOrder);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions(value = { "asset:disposal:add", "asset:disposal:edit" }, logical = Logical.OR)
@GetMapping("/selectAsset")
public String selectAsset(@RequestParam(value = "orderId", required = false) Long orderId, ModelMap mmap)
{
mmap.put("orderId", orderId);
return prefix + "/selectAsset";
}
/**
* /
*/
@RequiresPermissions(value = { "asset:disposal:add", "asset:disposal:edit" }, logical = Logical.OR)
@PostMapping("/availableAssetList")
@ResponseBody
public TableDataInfo availableAssetList(AmsAsset asset, @RequestParam(value = "orderId", required = false) Long orderId)
{
startPage();
List<AmsAsset> list = amsDisposalOrderService.selectAvailableDisposalAssetList(asset, orderId);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions("asset:disposal:export")
@Log(title = "报废管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ResponseBody
public AjaxResult export(AmsDisposalOrder amsDisposalOrder)
{
List<AmsDisposalOrder> list = amsDisposalOrderService.selectAmsDisposalOrderList(amsDisposalOrder);
ExcelUtil<AmsDisposalOrder> util = new ExcelUtil<AmsDisposalOrder>(AmsDisposalOrder.class);
return util.exportExcel(list, "报废管理数据");
}
/**
*
*/
@RequiresPermissions("asset:disposal:view")
@GetMapping("/view/{orderId}")
public String view(@PathVariable("orderId") Long orderId, ModelMap mmap)
{
AmsDisposalOrder amsDisposalOrder = amsDisposalOrderService.selectAmsDisposalOrderByOrderId(orderId);
mmap.put("amsDisposalOrder", amsDisposalOrder);
return prefix + "/view";
}
/**
*
*/
@RequiresPermissions("asset:disposal:add")
@GetMapping("/add")
public String add(ModelMap mmap)
{
SysUser currentUser = getSysUser();
mmap.put("applicantId", currentUser.getUserId());
mmap.put("applicantName", currentUser.getUserName());
if (currentUser.getDept() != null)
{
mmap.put("applyDeptId", currentUser.getDeptId());
mmap.put("applyDeptName", currentUser.getDept().getDeptName());
}
return prefix + "/add";
}
/**
* 稿
*/
@RequiresPermissions("asset:disposal:add")
@Log(title = "报废管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(AmsDisposalOrder amsDisposalOrder)
{
return toAjax(amsDisposalOrderService.insertAmsDisposalOrder(amsDisposalOrder, buildOperateContext()));
}
/**
*
*/
@RequiresPermissions("asset:disposal:edit")
@GetMapping("/edit/{orderId}")
public String edit(@PathVariable("orderId") Long orderId, ModelMap mmap)
{
AmsDisposalOrder amsDisposalOrder = amsDisposalOrderService.selectAmsDisposalOrderByOrderId(orderId);
mmap.put("amsDisposalOrder", amsDisposalOrder);
return prefix + "/edit";
}
/**
* 稿
*/
@RequiresPermissions("asset:disposal:edit")
@Log(title = "报废管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(AmsDisposalOrder amsDisposalOrder)
{
return toAjax(amsDisposalOrderService.updateAmsDisposalOrder(amsDisposalOrder, buildOperateContext()));
}
/**
*
*/
@RequiresPermissions("asset:disposal:remove")
@Log(title = "报废管理", businessType = BusinessType.DELETE)
@PostMapping( "/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
return toAjax(amsDisposalOrderService.deleteAmsDisposalOrderByOrderIds(ids));
}
/**
*
*/
@RequiresPermissions("asset:disposal:submit")
@Log(title = "报废管理", businessType = BusinessType.UPDATE)
@PostMapping("/submit/{orderId}")
@ResponseBody
public AjaxResult submit(@PathVariable("orderId") Long orderId)
{
return toAjax(amsDisposalOrderService.submitDisposal(orderId, buildOperateContext()));
}
/**
*
*/
@RequiresPermissions("asset:disposal:confirm")
@Log(title = "报废管理", businessType = BusinessType.UPDATE)
@PostMapping("/confirm/{orderId}")
@ResponseBody
public AjaxResult confirm(@PathVariable("orderId") Long orderId)
{
return toAjax(amsDisposalOrderService.confirmDisposal(orderId, buildOperateContext()));
}
/**
*
*/
@RequiresPermissions("asset:disposal:reject")
@Log(title = "报废管理", businessType = BusinessType.UPDATE)
@PostMapping("/reject/{orderId}")
@ResponseBody
public AjaxResult reject(@PathVariable("orderId") Long orderId, @RequestParam("rejectReason") String rejectReason)
{
return toAjax(amsDisposalOrderService.rejectDisposal(orderId, rejectReason, buildOperateContext()));
}
private AmsDisposalOperateContext buildOperateContext()
{
SysUser currentUser = getSysUser();
Long deptId = currentUser.getDept() == null ? null : currentUser.getDeptId();
String deptName = currentUser.getDept() == null ? null : currentUser.getDept().getDeptName();
return new AmsDisposalOperateContext(currentUser.getUserId(), currentUser.getUserName(), getLoginName(),
deptId, deptName);
}
}

@ -0,0 +1,94 @@
package com.ruoyi.asset.domain;
import java.io.Serializable;
/**
*
*
*
*
* @author Yangk
*/
public class AmsDisposalOperateContext implements Serializable
{
private static final long serialVersionUID = 1L;
/** 操作人ID */
private Long operateUserId;
/** 操作人名称 */
private String operateUserName;
/** 操作人登录账号 */
private String operateLoginName;
/** 操作人部门ID */
private Long operateDeptId;
/** 操作人部门名称 */
private String operateDeptName;
public AmsDisposalOperateContext()
{
}
public AmsDisposalOperateContext(Long operateUserId, String operateUserName, String operateLoginName,
Long operateDeptId, String operateDeptName)
{
this.operateUserId = operateUserId;
this.operateUserName = operateUserName;
this.operateLoginName = operateLoginName;
this.operateDeptId = operateDeptId;
this.operateDeptName = operateDeptName;
}
public Long getOperateUserId()
{
return operateUserId;
}
public void setOperateUserId(Long operateUserId)
{
this.operateUserId = operateUserId;
}
public String getOperateUserName()
{
return operateUserName;
}
public void setOperateUserName(String operateUserName)
{
this.operateUserName = operateUserName;
}
public String getOperateLoginName()
{
return operateLoginName;
}
public void setOperateLoginName(String operateLoginName)
{
this.operateLoginName = operateLoginName;
}
public Long getOperateDeptId()
{
return operateDeptId;
}
public void setOperateDeptId(Long operateDeptId)
{
this.operateDeptId = operateDeptId;
}
public String getOperateDeptName()
{
return operateDeptName;
}
public void setOperateDeptName(String operateDeptName)
{
this.operateDeptName = operateDeptName;
}
}

@ -0,0 +1,269 @@
package com.ruoyi.asset.domain;
import java.util.List;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
/**
* ams_disposal_order
*
* @author Yangk
* @date 2026-06-17
*/
public class AmsDisposalOrder extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 单据ID */
private Long orderId;
/** 报废单号 */
@Excel(name = "报废单号")
private String disposalNo;
/** 申请人ID */
@Excel(name = "申请人ID")
private Long applicantId;
/** 申请人名称快照 */
@Excel(name = "申请人名称快照")
private String applicantName;
/** 申请部门ID */
@Excel(name = "申请部门ID")
private Long applyDeptId;
/** 申请部门名称快照 */
@Excel(name = "申请部门名称快照")
private String applyDeptName;
/** 确认人ID */
@Excel(name = "确认人ID")
private Long confirmUserId;
/** 确认人名称快照 */
@Excel(name = "确认人名称快照")
private String confirmUserName;
/** 确认时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "确认时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date confirmTime;
/** 驳回原因 */
@Excel(name = "驳回原因")
private String rejectReason;
/** 处置方式 */
@Excel(name = "处置方式")
private String disposalMethod;
/** 处置时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "处置时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date disposalTime;
/** 单据状态 */
@Excel(name = "单据状态")
private String orderStatus;
/** 资产编码查询条件,不对应主表字段 */
private String assetCode;
/** 删除标志0存在1删除 */
private String delFlag;
/** 报废单明细信息 */
private List<AmsDisposalOrderItem> amsDisposalOrderItemList;
public void setOrderId(Long orderId)
{
this.orderId = orderId;
}
public Long getOrderId()
{
return orderId;
}
public void setDisposalNo(String disposalNo)
{
this.disposalNo = disposalNo;
}
public String getDisposalNo()
{
return disposalNo;
}
public void setApplicantId(Long applicantId)
{
this.applicantId = applicantId;
}
public Long getApplicantId()
{
return applicantId;
}
public void setApplicantName(String applicantName)
{
this.applicantName = applicantName;
}
public String getApplicantName()
{
return applicantName;
}
public void setApplyDeptId(Long applyDeptId)
{
this.applyDeptId = applyDeptId;
}
public Long getApplyDeptId()
{
return applyDeptId;
}
public void setApplyDeptName(String applyDeptName)
{
this.applyDeptName = applyDeptName;
}
public String getApplyDeptName()
{
return applyDeptName;
}
public void setConfirmUserId(Long confirmUserId)
{
this.confirmUserId = confirmUserId;
}
public Long getConfirmUserId()
{
return confirmUserId;
}
public void setConfirmUserName(String confirmUserName)
{
this.confirmUserName = confirmUserName;
}
public String getConfirmUserName()
{
return confirmUserName;
}
public void setConfirmTime(Date confirmTime)
{
this.confirmTime = confirmTime;
}
public Date getConfirmTime()
{
return confirmTime;
}
public void setRejectReason(String rejectReason)
{
this.rejectReason = rejectReason;
}
public String getRejectReason()
{
return rejectReason;
}
public void setDisposalMethod(String disposalMethod)
{
this.disposalMethod = disposalMethod;
}
public String getDisposalMethod()
{
return disposalMethod;
}
public void setDisposalTime(Date disposalTime)
{
this.disposalTime = disposalTime;
}
public Date getDisposalTime()
{
return disposalTime;
}
public void setOrderStatus(String orderStatus)
{
this.orderStatus = orderStatus;
}
public String getOrderStatus()
{
return orderStatus;
}
public void setAssetCode(String assetCode)
{
this.assetCode = assetCode;
}
public String getAssetCode()
{
return assetCode;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getDelFlag()
{
return delFlag;
}
public List<AmsDisposalOrderItem> getAmsDisposalOrderItemList()
{
return amsDisposalOrderItemList;
}
public void setAmsDisposalOrderItemList(List<AmsDisposalOrderItem> amsDisposalOrderItemList)
{
this.amsDisposalOrderItemList = amsDisposalOrderItemList;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("orderId", getOrderId())
.append("disposalNo", getDisposalNo())
.append("applicantId", getApplicantId())
.append("applicantName", getApplicantName())
.append("applyDeptId", getApplyDeptId())
.append("applyDeptName", getApplyDeptName())
.append("confirmUserId", getConfirmUserId())
.append("confirmUserName", getConfirmUserName())
.append("confirmTime", getConfirmTime())
.append("rejectReason", getRejectReason())
.append("disposalMethod", getDisposalMethod())
.append("disposalTime", getDisposalTime())
.append("orderStatus", getOrderStatus())
.append("assetCode", getAssetCode())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("delFlag", getDelFlag())
.append("amsDisposalOrderItemList", getAmsDisposalOrderItemList())
.toString();
}
}

@ -0,0 +1,223 @@
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.core.domain.BaseEntity;
/**
* ams_disposal_order_item
*
* @author Yangk
* @date 2026-06-17
*/
public class AmsDisposalOrderItem extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 明细ID */
private Long itemId;
/** 报废单ID */
@Excel(name = "报废单ID")
private Long orderId;
/** 报废单号快照 */
@Excel(name = "报废单号快照")
private String disposalNo;
/** 资产ID */
@Excel(name = "资产ID")
private Long assetId;
/** 资产编码快照 */
@Excel(name = "资产编码快照")
private String assetCode;
/** 资产名称快照 */
@Excel(name = "资产名称快照")
private String assetName;
/** 资产类别ID快照 */
@Excel(name = "资产类别ID快照")
private Long categoryId;
/** 类别编码快照 */
@Excel(name = "类别编码快照")
private String categoryCode;
/** 类别名称快照 */
@Excel(name = "类别名称快照")
private String categoryName;
/** 规格型号快照 */
@Excel(name = "规格型号快照")
private String specModel;
/** 品牌快照 */
@Excel(name = "品牌快照")
private String brand;
/** 报废原因 */
@Excel(name = "报废原因")
private String disposalReason;
/** 处置备注 */
@Excel(name = "处置备注")
private String disposalRemark;
/** 删除标志0存在1删除 */
private String delFlag;
public void setItemId(Long itemId)
{
this.itemId = itemId;
}
public Long getItemId()
{
return itemId;
}
public void setOrderId(Long orderId)
{
this.orderId = orderId;
}
public Long getOrderId()
{
return orderId;
}
public void setDisposalNo(String disposalNo)
{
this.disposalNo = disposalNo;
}
public String getDisposalNo()
{
return disposalNo;
}
public void setAssetId(Long assetId)
{
this.assetId = assetId;
}
public Long getAssetId()
{
return assetId;
}
public void setAssetCode(String assetCode)
{
this.assetCode = assetCode;
}
public String getAssetCode()
{
return assetCode;
}
public void setAssetName(String assetName)
{
this.assetName = assetName;
}
public String getAssetName()
{
return assetName;
}
public void setCategoryId(Long categoryId)
{
this.categoryId = categoryId;
}
public Long getCategoryId()
{
return categoryId;
}
public void setCategoryCode(String categoryCode)
{
this.categoryCode = categoryCode;
}
public String getCategoryCode()
{
return categoryCode;
}
public void setCategoryName(String categoryName)
{
this.categoryName = categoryName;
}
public String getCategoryName()
{
return categoryName;
}
public void setSpecModel(String specModel)
{
this.specModel = specModel;
}
public String getSpecModel()
{
return specModel;
}
public void setBrand(String brand)
{
this.brand = brand;
}
public String getBrand()
{
return brand;
}
public void setDisposalReason(String disposalReason)
{
this.disposalReason = disposalReason;
}
public String getDisposalReason()
{
return disposalReason;
}
public void setDisposalRemark(String disposalRemark)
{
this.disposalRemark = disposalRemark;
}
public String getDisposalRemark()
{
return disposalRemark;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getDelFlag()
{
return delFlag;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("itemId", getItemId())
.append("orderId", getOrderId())
.append("disposalNo", getDisposalNo())
.append("assetId", getAssetId())
.append("assetCode", getAssetCode())
.append("assetName", getAssetName())
.append("categoryId", getCategoryId())
.append("categoryCode", getCategoryCode())
.append("categoryName", getCategoryName())
.append("specModel", getSpecModel())
.append("brand", getBrand())
.append("disposalReason", getDisposalReason())
.append("disposalRemark", getDisposalRemark())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("delFlag", getDelFlag())
.toString();
}
}

@ -0,0 +1,114 @@
package com.ruoyi.asset.mapper;
import java.util.List;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsDisposalOrder;
import com.ruoyi.asset.domain.AmsDisposalOrderItem;
/**
* Mapper
*
* @author Yangk
* @date 2026-06-17
*/
public interface AmsDisposalOrderMapper
{
/**
*
*
* @param orderId
* @return
*/
public AmsDisposalOrder selectAmsDisposalOrderByOrderId(Long orderId);
/**
*
*
* @param amsDisposalOrder
* @return
*/
public List<AmsDisposalOrder> selectAmsDisposalOrderList(AmsDisposalOrder amsDisposalOrder);
/**
*
*
* @param amsDisposalOrder
* @return
*/
public int insertAmsDisposalOrder(AmsDisposalOrder amsDisposalOrder);
/**
*
*
* @param amsDisposalOrder
* @return
*/
public int updateAmsDisposalOrder(AmsDisposalOrder amsDisposalOrder);
/**
*
*
* @param orderId
* @return
*/
public int deleteAmsDisposalOrderByOrderId(Long orderId);
/**
*
*
* @param orderIds
* @return
*/
public int deleteAmsDisposalOrderByOrderIds(String[] orderIds);
/**
*
*
* @param orderIds
* @return
*/
public int deleteAmsDisposalOrderItemByOrderIds(String[] orderIds);
/**
*
*
* @param amsDisposalOrderItemList
* @return
*/
public int batchAmsDisposalOrderItem(List<AmsDisposalOrderItem> amsDisposalOrderItemList);
/**
*
*
* @param orderId ID
* @return
*/
public int deleteAmsDisposalOrderItemByOrderId(Long orderId);
/**
*
*
* @param orderId ID
* @return
*/
public AmsDisposalOrder selectAmsDisposalOrderByOrderIdForUpdate(Long orderId);
/**
*
*
* @param asset
* @param currentOrderId ID
* @return
*/
public List<AmsAsset> selectAvailableDisposalAssetList(@org.apache.ibatis.annotations.Param("asset") AmsAsset asset, @org.apache.ibatis.annotations.Param("currentOrderId") Long currentOrderId);
/**
*
*
* @param assetId ID
* @param currentOrderId ID
* @return
*/
public int countOtherActiveDisposalOrderByAssetId(@org.apache.ibatis.annotations.Param("assetId") Long assetId, @org.apache.ibatis.annotations.Param("currentOrderId") Long currentOrderId);
}

@ -0,0 +1,101 @@
package com.ruoyi.asset.service;
import java.util.List;
import com.ruoyi.asset.domain.AmsDisposalOperateContext;
import com.ruoyi.asset.domain.AmsDisposalOrder;
/**
* Service
*
* @author Yangk
* @date 2026-06-17
*/
public interface IAmsDisposalOrderService
{
/**
*
*
* @param orderId
* @return
*/
public AmsDisposalOrder selectAmsDisposalOrderByOrderId(Long orderId);
/**
*
*
* @param amsDisposalOrder
* @return
*/
public List<AmsDisposalOrder> selectAmsDisposalOrderList(AmsDisposalOrder amsDisposalOrder);
/**
*
*
* @param amsDisposalOrder
* @param operateContext
* @return
*/
public int insertAmsDisposalOrder(AmsDisposalOrder amsDisposalOrder, AmsDisposalOperateContext operateContext);
/**
*
*
* @param amsDisposalOrder
* @param operateContext
* @return
*/
public int updateAmsDisposalOrder(AmsDisposalOrder amsDisposalOrder, AmsDisposalOperateContext operateContext);
/**
*
*
* @param orderIds
* @return
*/
public int deleteAmsDisposalOrderByOrderIds(String orderIds);
/**
*
*
* @param orderId
* @return
*/
public int deleteAmsDisposalOrderByOrderId(Long orderId);
/**
*
*
* @param orderId ID
* @param operateContext
* @return
*/
public int submitDisposal(Long orderId, AmsDisposalOperateContext operateContext);
/**
*
*
* @param orderId ID
* @param operateContext
* @return
*/
public int confirmDisposal(Long orderId, AmsDisposalOperateContext operateContext);
/**
*
*
* @param orderId ID
* @param rejectReason
* @param operateContext
* @return
*/
public int rejectDisposal(Long orderId, String rejectReason, AmsDisposalOperateContext operateContext);
/**
*
*
* @param asset
* @param currentOrderId ID
* @return
*/
public List<com.ruoyi.asset.domain.AmsAsset> selectAvailableDisposalAssetList(com.ruoyi.asset.domain.AmsAsset asset, Long currentOrderId);
}

@ -0,0 +1,533 @@
package com.ruoyi.asset.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.asset.constant.DisposalOrderStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsDisposalOperateContext;
import com.ruoyi.asset.domain.AmsDisposalOrder;
import com.ruoyi.asset.domain.AmsDisposalOrderItem;
import com.ruoyi.asset.domain.AssetTransitionContext;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsDisposalOrderMapper;
import com.ruoyi.asset.service.IAmsDisposalOrderService;
import com.ruoyi.asset.service.IAssetStatusTransitionService;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysCodeRuleService;
/**
* Service
*
* IN_STOCKIN_USEDISPOSED
*
* 1. ID
* 2. 稿
* 3. del_flag='1'稿
*
* @author Yangk
* @date 2026-06-17
*/
@Service
public class AmsDisposalOrderServiceImpl implements IAmsDisposalOrderService
{
private static final String DISPOSAL_ORDER_RULE = "DISPOSAL_ORDER";
private static final String DEL_FLAG_NORMAL = "0";
@Autowired
private AmsDisposalOrderMapper amsDisposalOrderMapper;
@Autowired
private AmsAssetMapper amsAssetMapper;
@Autowired
private ISysCodeRuleService sysCodeRuleService;
@Autowired
private IAssetStatusTransitionService assetStatusTransitionService;
/**
*
*
* @param orderId
* @return
*/
@Override
public AmsDisposalOrder selectAmsDisposalOrderByOrderId(Long orderId)
{
return amsDisposalOrderMapper.selectAmsDisposalOrderByOrderId(orderId);
}
/**
*
*
* @param amsDisposalOrder
* @return
*/
@Override
public List<AmsDisposalOrder> selectAmsDisposalOrderList(AmsDisposalOrder amsDisposalOrder)
{
return amsDisposalOrderMapper.selectAmsDisposalOrderList(amsDisposalOrder);
}
/**
* 稿
*
* @param amsDisposalOrder
* @return
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int insertAmsDisposalOrder(AmsDisposalOrder amsDisposalOrder, AmsDisposalOperateContext operateContext)
{
validateOperateContext(operateContext);
// 1. 基础字段与表单校验
validateOrderRequest(amsDisposalOrder);
// 2. 申请人和部门必须以后端登录会话为准,避免前端隐藏字段篡改。
fillApplicantSnapshots(amsDisposalOrder, operateContext);
// 3. 生成 BF 前缀的唯一报废单号
amsDisposalOrder.setDisposalNo(sysCodeRuleService.nextCode(DISPOSAL_ORDER_RULE));
amsDisposalOrder.setOrderStatus(DisposalOrderStatus.DRAFT);
amsDisposalOrder.setDelFlag(DEL_FLAG_NORMAL);
amsDisposalOrder.setCreateBy(operateContext.getOperateLoginName());
amsDisposalOrder.setCreateTime(DateUtils.getNowDate());
// 重置受控的确认及驳回字段,以防越权篡改
amsDisposalOrder.setConfirmUserId(null);
amsDisposalOrder.setConfirmUserName(null);
amsDisposalOrder.setConfirmTime(null);
amsDisposalOrder.setRejectReason(null);
amsDisposalOrder.setDisposalTime(null);
// 4. 防死锁升序加锁、校验资产并回填明细快照
fillDetailSnapshotsAndValidate(amsDisposalOrder, null);
// 5. 保存主表与子明细
int rows = amsDisposalOrderMapper.insertAmsDisposalOrder(amsDisposalOrder);
if (rows != 1 || amsDisposalOrder.getOrderId() == null)
{
throw new ServiceException("报废单保存失败");
}
insertAmsDisposalOrderItem(amsDisposalOrder);
return rows;
}
/**
* 稿
*
* @param amsDisposalOrder
* @return
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int updateAmsDisposalOrder(AmsDisposalOrder amsDisposalOrder, AmsDisposalOperateContext operateContext)
{
validateOperateContext(operateContext);
if (amsDisposalOrder == null || amsDisposalOrder.getOrderId() == null)
{
throw new ServiceException("单据ID不能为空");
}
// 1. 悲观锁锁定主表记录,强校验状态必须为 DRAFT。非草稿状态严禁修改
AmsDisposalOrder current = requireOrderForUpdate(amsDisposalOrder.getOrderId(), DisposalOrderStatus.DRAFT,
"仅草稿状态的报废单允许修改");
// 基础校验
validateOrderRequest(amsDisposalOrder);
// 2. 强行还原单号、申请人及受控属性,强制复归为 DRAFT
amsDisposalOrder.setDisposalNo(current.getDisposalNo());
amsDisposalOrder.setApplicantId(current.getApplicantId());
amsDisposalOrder.setApplicantName(current.getApplicantName());
amsDisposalOrder.setApplyDeptId(current.getApplyDeptId());
amsDisposalOrder.setApplyDeptName(current.getApplyDeptName());
amsDisposalOrder.setConfirmUserId(null);
amsDisposalOrder.setConfirmUserName(null);
amsDisposalOrder.setConfirmTime(null);
amsDisposalOrder.setRejectReason(null);
amsDisposalOrder.setOrderStatus(DisposalOrderStatus.DRAFT);
amsDisposalOrder.setCreateBy(current.getCreateBy());
amsDisposalOrder.setCreateTime(current.getCreateTime());
amsDisposalOrder.setDelFlag(DEL_FLAG_NORMAL);
amsDisposalOrder.setUpdateBy(operateContext.getOperateLoginName());
amsDisposalOrder.setUpdateTime(DateUtils.getNowDate());
// 3. 锁定资产,校验并重新填充快照
fillDetailSnapshotsAndValidate(amsDisposalOrder, amsDisposalOrder.getOrderId());
// 4. 逻辑删除旧明细,重新插入新明细以防冗余
amsDisposalOrderMapper.deleteAmsDisposalOrderItemByOrderId(amsDisposalOrder.getOrderId());
insertAmsDisposalOrderItem(amsDisposalOrder);
// 5. 更新主表,采用 CAS 状态校验
return updateOrderOrThrow(amsDisposalOrder, DisposalOrderStatus.DRAFT, "单据状态已发生改变,更新失败");
}
/**
*
*
* @param orderIds
* @return
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int deleteAmsDisposalOrderByOrderIds(String orderIds)
{
Long[] sortedIds = Arrays.stream(Convert.toStrArray(orderIds))
.map(Long::valueOf).sorted().toArray(Long[]::new);
int rows = 0;
for (Long orderId : sortedIds)
{
// 获取主表悲观锁并核实必须是草稿
AmsDisposalOrder order = amsDisposalOrderMapper.selectAmsDisposalOrderByOrderIdForUpdate(orderId);
if (order == null)
{
continue;
}
if (!DisposalOrderStatus.DRAFT.equals(order.getOrderStatus()))
{
throw new ServiceException(StringUtils.format("报废单【{}】非草稿状态,不允许删除", order.getDisposalNo()));
}
// 级联打上逻辑删除标志
amsDisposalOrderMapper.deleteAmsDisposalOrderItemByOrderId(orderId);
rows += amsDisposalOrderMapper.deleteAmsDisposalOrderByOrderId(orderId);
}
return rows;
}
/**
*
*
* @param orderId
* @return
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int deleteAmsDisposalOrderByOrderId(Long orderId)
{
AmsDisposalOrder order = amsDisposalOrderMapper.selectAmsDisposalOrderByOrderIdForUpdate(orderId);
if (order == null)
{
return 0;
}
if (!DisposalOrderStatus.DRAFT.equals(order.getOrderStatus()))
{
throw new ServiceException(StringUtils.format("报废单【{}】非草稿状态,不允许删除", order.getDisposalNo()));
}
amsDisposalOrderMapper.deleteAmsDisposalOrderItemByOrderId(orderId);
return amsDisposalOrderMapper.deleteAmsDisposalOrderByOrderId(orderId);
}
/**
*
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int submitDisposal(Long orderId, AmsDisposalOperateContext operateContext)
{
validateOperateContext(operateContext);
// 1. 悲观锁锁定主表并校验状态为 DRAFT
AmsDisposalOrder order = requireOrderForUpdate(orderId, DisposalOrderStatus.DRAFT, "仅草稿状态的报废单允许提交");
// 2. 再次悲观锁锁定资产,校验资产状态与跨单据占用,防并发
validateOrderAssetsReady(order);
// 3. CAS 更新单据状态为待确认
order.setOrderStatus(DisposalOrderStatus.PENDING_CONFIRM);
order.setUpdateBy(operateContext.getOperateLoginName());
order.setUpdateTime(DateUtils.getNowDate());
return updateOrderOrThrow(order, DisposalOrderStatus.DRAFT, "报废单提交失败,请刷新后重试");
}
/**
* PENDING_CONFIRM -> DISPOSED_DONE
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int confirmDisposal(Long orderId, AmsDisposalOperateContext operateContext)
{
validateOperateContext(operateContext);
// 1. 锁主表校验状态必须为待确认
AmsDisposalOrder order = requireOrderForUpdate(orderId, DisposalOrderStatus.PENDING_CONFIRM, "仅待确认状态的报废单允许确认报废");
// 2. 升序加锁校验资产状态及跨单占用
validateOrderAssetsReady(order);
Date now = DateUtils.getNowDate();
// 3. 逐一调用公共状态流转服务 confirmDisposal
for (AmsDisposalOrderItem item : order.getAmsDisposalOrderItemList())
{
AssetTransitionContext context = new AssetTransitionContext();
context.setSourceOrderId(order.getOrderId());
context.setSourceOrderNo(order.getDisposalNo());
context.setSourceItemId(item.getItemId());
context.setOperateUserId(operateContext.getOperateUserId());
context.setOperateUserName(StringUtils.trim(operateContext.getOperateUserName()));
context.setOperateLoginName(operateContext.getOperateLoginName());
context.setChangeSummary("确认资产报废");
context.setRemark(item.getDisposalRemark());
// 变更资产状态为 DISPOSED 写入资产履历,报废不清除 RFID 绑定
assetStatusTransitionService.confirmDisposal(item.getAssetId(), context);
}
// 4. 回填确认属性,变更为已报废状态
order.setConfirmUserId(operateContext.getOperateUserId());
order.setConfirmUserName(StringUtils.trim(operateContext.getOperateUserName()));
order.setConfirmTime(now);
order.setDisposalTime(now);
order.setOrderStatus(DisposalOrderStatus.DISPOSED_DONE);
order.setUpdateBy(operateContext.getOperateLoginName());
order.setUpdateTime(now);
return updateOrderOrThrow(order, DisposalOrderStatus.PENDING_CONFIRM, "报废单确认失败,请刷新后重试");
}
/**
* PENDING_CONFIRM -> REJECTED
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int rejectDisposal(Long orderId, String rejectReason, AmsDisposalOperateContext operateContext)
{
validateOperateContext(operateContext);
if (StringUtils.isEmpty(StringUtils.trim(rejectReason)))
{
throw new ServiceException("驳回原因不能为空");
}
validateLength(rejectReason, 500, "驳回原因");
// 1. 锁主表校验状态
AmsDisposalOrder order = requireOrderForUpdate(orderId, DisposalOrderStatus.PENDING_CONFIRM, "仅待确认状态的报废单允许驳回");
// 2. 更新单据状态为已驳回,并记录原因。不改资产状态,不写履历
order.setOrderStatus(DisposalOrderStatus.REJECTED);
order.setRejectReason(StringUtils.trim(rejectReason));
order.setUpdateBy(operateContext.getOperateLoginName());
order.setUpdateTime(DateUtils.getNowDate());
return updateOrderOrThrow(order, DisposalOrderStatus.PENDING_CONFIRM, "报废单驳回失败,请刷新后重试");
}
/**
*
*/
@Override
public List<AmsAsset> selectAvailableDisposalAssetList(AmsAsset asset, Long currentOrderId)
{
return amsDisposalOrderMapper.selectAvailableDisposalAssetList(asset, currentOrderId);
}
/**
*
*
* @param amsDisposalOrder
*/
public void insertAmsDisposalOrderItem(AmsDisposalOrder amsDisposalOrder)
{
List<AmsDisposalOrderItem> amsDisposalOrderItemList = amsDisposalOrder.getAmsDisposalOrderItemList();
Long orderId = amsDisposalOrder.getOrderId();
String disposalNo = amsDisposalOrder.getDisposalNo();
if (StringUtils.isNotNull(amsDisposalOrderItemList))
{
List<AmsDisposalOrderItem> list = new ArrayList<AmsDisposalOrderItem>();
for (AmsDisposalOrderItem amsDisposalOrderItem : amsDisposalOrderItemList)
{
amsDisposalOrderItem.setOrderId(orderId);
amsDisposalOrderItem.setDisposalNo(disposalNo);
amsDisposalOrderItem.setDelFlag(DEL_FLAG_NORMAL);
list.add(amsDisposalOrderItem);
}
if (list.size() > 0)
{
amsDisposalOrderMapper.batchAmsDisposalOrderItem(list);
}
}
}
// ========================================== 私有辅助方法 ==========================================
private AmsDisposalOrder requireOrderForUpdate(Long orderId, String expectedStatus, String errMsg)
{
AmsDisposalOrder order = amsDisposalOrderMapper.selectAmsDisposalOrderByOrderIdForUpdate(orderId);
if (order == null)
{
throw new ServiceException("报废单不存在或已删除");
}
if (!expectedStatus.equals(order.getOrderStatus()))
{
throw new ServiceException(errMsg);
}
return order;
}
private int updateOrderOrThrow(AmsDisposalOrder order, String expectedStatus, String errorMessage)
{
order.getParams().put("expectedOrderStatus", expectedStatus);
if (amsDisposalOrderMapper.updateAmsDisposalOrder(order) != 1)
{
throw new ServiceException(errorMessage);
}
return 1;
}
private void validateOrderRequest(AmsDisposalOrder order)
{
if (order == null)
{
throw new ServiceException("单据数据不能为空");
}
if (order.getAmsDisposalOrderItemList() == null || order.getAmsDisposalOrderItemList().isEmpty())
{
throw new ServiceException("报废明细资产不能为空");
}
validateLength(order.getDisposalMethod(), 64, "处置方式");
validateLength(order.getRemark(), 500, "备注");
}
private void fillApplicantSnapshots(AmsDisposalOrder order, AmsDisposalOperateContext operateContext)
{
order.setApplicantId(operateContext.getOperateUserId());
order.setApplicantName(StringUtils.trim(operateContext.getOperateUserName()));
order.setApplyDeptId(operateContext.getOperateDeptId());
order.setApplyDeptName(StringUtils.trim(operateContext.getOperateDeptName()));
}
private void fillDetailSnapshotsAndValidate(AmsDisposalOrder order, Long currentOrderId)
{
List<AmsDisposalOrderItem> items = order.getAmsDisposalOrderItemList();
// 1. 应用层资产ID升序排序以防并发加锁导致的数据库死锁
List<AmsDisposalOrderItem> sortedItems = new ArrayList<>(items);
sortedItems.sort(java.util.Comparator.comparing(AmsDisposalOrderItem::getAssetId));
java.util.Set<Long> assetIds = new java.util.HashSet<>();
for (AmsDisposalOrderItem item : sortedItems)
{
if (item.getAssetId() == null)
{
throw new ServiceException("明细资产ID不能为空");
}
if (!assetIds.add(item.getAssetId()))
{
throw new ServiceException("不能在一个报废单中选择相同的资产");
}
// 2. 悲观锁定资产,校验其前置实物状态
AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(item.getAssetId());
if (asset == null || "1".equals(asset.getDelFlag()))
{
throw new ServiceException("资产不存在或已删除");
}
if (!"IN_STOCK".equals(asset.getAssetStatus()) && !"IN_USE".equals(asset.getAssetStatus()))
{
throw new ServiceException(StringUtils.format("资产【{}】当前状态【{}】不允许进行报废",
asset.getAssetCode(), asset.getAssetStatus()));
}
// 3. 校验跨单据占用
int occupied = amsDisposalOrderMapper.countOtherActiveDisposalOrderByAssetId(item.getAssetId(), currentOrderId);
if (occupied > 0)
{
throw new ServiceException(StringUtils.format("资产【{}】已被其他未完成报废单占用", asset.getAssetCode()));
}
// 4. 回填最新的物理状态和属性快照,防止过期脏写
item.setAssetCode(asset.getAssetCode());
item.setAssetName(asset.getAssetName());
item.setCategoryId(asset.getCategoryId());
item.setCategoryCode(asset.getCategoryCode());
item.setCategoryName(asset.getCategoryName());
item.setSpecModel(asset.getSpecModel());
item.setBrand(asset.getBrand());
validateLength(item.getDisposalReason(), 500, "报废原因");
validateLength(item.getDisposalRemark(), 500, "处置备注");
}
}
private void validateOrderAssetsReady(AmsDisposalOrder order)
{
List<AmsDisposalOrderItem> items = order.getAmsDisposalOrderItemList();
if (items == null || items.isEmpty())
{
throw new ServiceException("单据明细资产为空");
}
// 按 ID 升序锁定资产
List<AmsDisposalOrderItem> sortedItems = new ArrayList<>(items);
sortedItems.sort(java.util.Comparator.comparing(AmsDisposalOrderItem::getAssetId));
for (AmsDisposalOrderItem item : sortedItems)
{
AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(item.getAssetId());
if (asset == null || "1".equals(asset.getDelFlag()))
{
throw new ServiceException("资产不存在或已删除");
}
if (!"IN_STOCK".equals(asset.getAssetStatus()) && !"IN_USE".equals(asset.getAssetStatus()))
{
throw new ServiceException(StringUtils.format("资产【{}】状态已发生改变,不能进行该操作", asset.getAssetCode()));
}
int occupied = amsDisposalOrderMapper.countOtherActiveDisposalOrderByAssetId(item.getAssetId(), order.getOrderId());
if (occupied > 0)
{
throw new ServiceException(StringUtils.format("资产【{}】已被其他未完成报废单占用", asset.getAssetCode()));
}
}
}
private void validateLength(String value, int maxLength, String fieldName)
{
if (StringUtils.isNotEmpty(value) && value.length() > maxLength)
{
throw new ServiceException(fieldName + "长度不能超过" + maxLength + "个字符");
}
}
private void validateLoginName(String operateLoginName)
{
if (StringUtils.isEmpty(operateLoginName))
{
throw new ServiceException("操作账号不能为空");
}
validateLength(operateLoginName, 64, "操作账号");
}
private void validateOperateContext(AmsDisposalOperateContext operateContext)
{
if (operateContext == null)
{
throw new ServiceException("操作上下文不能为空");
}
validateOperator(operateContext.getOperateUserId(), operateContext.getOperateUserName(), "操作人");
validateLoginName(operateContext.getOperateLoginName());
validateLength(operateContext.getOperateDeptName(), 100, "操作人部门名称");
}
private void validateOperator(Long operateUserId, String operateUserName, String fieldPrefix)
{
if (operateUserId == null)
{
throw new ServiceException(fieldPrefix + "ID不能为空");
}
if (StringUtils.isEmpty(StringUtils.trim(operateUserName)))
{
throw new ServiceException(fieldPrefix + "名称不能为空");
}
validateLength(operateUserName, 100, fieldPrefix + "名称");
}
}

@ -0,0 +1,280 @@
<?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.AmsDisposalOrderMapper">
<resultMap type="AmsDisposalOrder" id="AmsDisposalOrderResult">
<result property="orderId" column="order_id" />
<result property="disposalNo" column="disposal_no" />
<result property="applicantId" column="applicant_id" />
<result property="applicantName" column="applicant_name" />
<result property="applyDeptId" column="apply_dept_id" />
<result property="applyDeptName" column="apply_dept_name" />
<result property="confirmUserId" column="confirm_user_id" />
<result property="confirmUserName" column="confirm_user_name" />
<result property="confirmTime" column="confirm_time" />
<result property="rejectReason" column="reject_reason" />
<result property="disposalMethod" column="disposal_method" />
<result property="disposalTime" column="disposal_time" />
<result property="orderStatus" column="order_status" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="delFlag" column="del_flag" />
</resultMap>
<resultMap id="AmsDisposalOrderAmsDisposalOrderItemResult" type="AmsDisposalOrder" extends="AmsDisposalOrderResult">
<collection property="amsDisposalOrderItemList" ofType="AmsDisposalOrderItem" column="order_id" select="selectAmsDisposalOrderItemList" />
</resultMap>
<resultMap type="AmsAsset" id="AvailableDisposalAssetResult">
<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="assetStatus" column="asset_status" />
<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="tagCode" column="tag_code" />
</resultMap>
<resultMap type="AmsDisposalOrderItem" id="AmsDisposalOrderItemResult">
<result property="itemId" column="item_id" />
<result property="orderId" column="order_id" />
<result property="disposalNo" column="disposal_no" />
<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="disposalReason" column="disposal_reason" />
<result property="disposalRemark" column="disposal_remark" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="delFlag" column="del_flag" />
</resultMap>
<sql id="selectAmsDisposalOrderVo">
select order_id, disposal_no, applicant_id, applicant_name, apply_dept_id, apply_dept_name, confirm_user_id, confirm_user_name, confirm_time, reject_reason, disposal_method, disposal_time, order_status, create_by, create_time, update_by, update_time, remark, del_flag from ams_disposal_order
</sql>
<select id="selectAmsDisposalOrderList" parameterType="AmsDisposalOrder" resultMap="AmsDisposalOrderResult">
<include refid="selectAmsDisposalOrderVo"/>
<where>
del_flag = '0'
<if test="disposalNo != null and disposalNo != ''"> and disposal_no like concat(#{disposalNo}, '%')</if>
<if test="applicantId != null "> and applicant_id = #{applicantId}</if>
<if test="applicantName != null and applicantName != ''"> and applicant_name like concat('%', #{applicantName}, '%')</if>
<if test="applyDeptId != null "> and apply_dept_id = #{applyDeptId}</if>
<if test="applyDeptName != null and applyDeptName != ''"> and apply_dept_name like concat('%', #{applyDeptName}, '%')</if>
<if test="confirmUserId != null "> and confirm_user_id = #{confirmUserId}</if>
<if test="confirmUserName != null and confirmUserName != ''"> and confirm_user_name like concat('%', #{confirmUserName}, '%')</if>
<if test="confirmTime != null "> and confirm_time = #{confirmTime}</if>
<if test="rejectReason != null and rejectReason != ''"> and reject_reason = #{rejectReason}</if>
<if test="disposalMethod != null and disposalMethod != ''"> and disposal_method = #{disposalMethod}</if>
<if test="disposalTime != null "> and disposal_time = #{disposalTime}</if>
<if test="params.beginDisposalTime != null and params.beginDisposalTime != ''">
and disposal_time &gt;= #{params.beginDisposalTime}
</if>
<if test="params.endDisposalTime != null and params.endDisposalTime != ''">
and disposal_time &lt; date_add(#{params.endDisposalTime}, interval 1 day)
</if>
<if test="orderStatus != null and orderStatus != ''"> and order_status = #{orderStatus}</if>
<if test="assetCode != null and assetCode != ''">
and exists (
select 1
from ams_disposal_order_item item
where item.order_id = ams_disposal_order.order_id
and item.del_flag = '0'
and item.asset_code like concat('%', #{assetCode}, '%')
)
</if>
</where>
order by create_time desc, order_id desc
</select>
<select id="selectAmsDisposalOrderByOrderId" parameterType="Long" resultMap="AmsDisposalOrderAmsDisposalOrderItemResult">
select order_id, disposal_no, applicant_id, applicant_name, apply_dept_id, apply_dept_name, confirm_user_id, confirm_user_name, confirm_time, reject_reason, disposal_method, disposal_time, order_status, create_by, create_time, update_by, update_time, remark, del_flag
from ams_disposal_order
where order_id = #{orderId} and del_flag = '0'
</select>
<select id="selectAmsDisposalOrderByOrderIdForUpdate" parameterType="Long" resultMap="AmsDisposalOrderAmsDisposalOrderItemResult">
select order_id, disposal_no, applicant_id, applicant_name, apply_dept_id, apply_dept_name, confirm_user_id, confirm_user_name, confirm_time, reject_reason, disposal_method, disposal_time, order_status, create_by, create_time, update_by, update_time, remark, del_flag
from ams_disposal_order
where order_id = #{orderId} and del_flag = '0'
for update
</select>
<select id="selectAmsDisposalOrderItemList" resultMap="AmsDisposalOrderItemResult">
select item_id, order_id, disposal_no, asset_id, asset_code, asset_name, category_id, category_code, category_name, spec_model, brand, disposal_reason, disposal_remark, create_by, create_time, update_by, update_time, remark, del_flag
from ams_disposal_order_item
where order_id = #{order_id} and del_flag = '0'
</select>
<insert id="insertAmsDisposalOrder" parameterType="AmsDisposalOrder" useGeneratedKeys="true" keyProperty="orderId">
insert into ams_disposal_order
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="disposalNo != null and disposalNo != ''">disposal_no,</if>
<if test="applicantId != null">applicant_id,</if>
<if test="applicantName != null">applicant_name,</if>
<if test="applyDeptId != null">apply_dept_id,</if>
<if test="applyDeptName != null">apply_dept_name,</if>
<if test="confirmUserId != null">confirm_user_id,</if>
<if test="confirmUserName != null">confirm_user_name,</if>
<if test="confirmTime != null">confirm_time,</if>
<if test="rejectReason != null">reject_reason,</if>
<if test="disposalMethod != null">disposal_method,</if>
<if test="disposalTime != null">disposal_time,</if>
<if test="orderStatus != null and orderStatus != ''">order_status,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="delFlag != null and delFlag != ''">del_flag,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="disposalNo != null and disposalNo != ''">#{disposalNo},</if>
<if test="applicantId != null">#{applicantId},</if>
<if test="applicantName != null">#{applicantName},</if>
<if test="applyDeptId != null">#{applyDeptId},</if>
<if test="applyDeptName != null">#{applyDeptName},</if>
<if test="confirmUserId != null">#{confirmUserId},</if>
<if test="confirmUserName != null">#{confirmUserName},</if>
<if test="confirmTime != null">#{confirmTime},</if>
<if test="rejectReason != null">#{rejectReason},</if>
<if test="disposalMethod != null">#{disposalMethod},</if>
<if test="disposalTime != null">#{disposalTime},</if>
<if test="orderStatus != null and orderStatus != ''">#{orderStatus},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="delFlag != null and delFlag != ''">#{delFlag},</if>
</trim>
</insert>
<update id="updateAmsDisposalOrder" parameterType="AmsDisposalOrder">
update ams_disposal_order
<trim prefix="SET" suffixOverrides=",">
<if test="disposalNo != null and disposalNo != ''">disposal_no = #{disposalNo},</if>
<if test="applicantId != null">applicant_id = #{applicantId},</if>
<if test="applicantName != null">applicant_name = #{applicantName},</if>
<if test="applyDeptId != null">apply_dept_id = #{applyDeptId},</if>
<if test="applyDeptName != null">apply_dept_name = #{applyDeptName},</if>
<if test="confirmUserId != null">confirm_user_id = #{confirmUserId},</if>
<if test="confirmUserName != null">confirm_user_name = #{confirmUserName},</if>
<if test="confirmTime != null">confirm_time = #{confirmTime},</if>
<if test="rejectReason != null">reject_reason = #{rejectReason},</if>
<if test="disposalMethod != null">disposal_method = #{disposalMethod},</if>
<if test="disposalTime != null">disposal_time = #{disposalTime},</if>
<if test="orderStatus != null and orderStatus != ''">order_status = #{orderStatus},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
</trim>
where order_id = #{orderId} and del_flag = '0'
<if test="params.expectedOrderStatus != null and params.expectedOrderStatus != ''">
and order_status = #{params.expectedOrderStatus}
</if>
</update>
<update id="deleteAmsDisposalOrderByOrderId" parameterType="Long">
update ams_disposal_order set del_flag = '1' where order_id = #{orderId} and del_flag = '0' and order_status = 'DRAFT'
</update>
<update id="deleteAmsDisposalOrderByOrderIds" parameterType="String">
update ams_disposal_order set del_flag = '1' where order_id in
<foreach item="orderId" collection="array" open="(" separator="," close=")">
#{orderId}
</foreach>
and del_flag = '0' and order_status = 'DRAFT'
</update>
<update id="deleteAmsDisposalOrderItemByOrderIds" parameterType="String">
update ams_disposal_order_item set del_flag = '1' where order_id in
<foreach item="orderId" collection="array" open="(" separator="," close=")">
#{orderId}
</foreach>
and del_flag = '0'
</update>
<update id="deleteAmsDisposalOrderItemByOrderId" parameterType="Long">
update ams_disposal_order_item set del_flag = '1' where order_id = #{orderId} and del_flag = '0'
</update>
<insert id="batchAmsDisposalOrderItem">
insert into ams_disposal_order_item( item_id, order_id, disposal_no, asset_id, asset_code, asset_name, category_id, category_code, category_name, spec_model, brand, disposal_reason, disposal_remark, create_by, create_time, update_by, update_time, remark, del_flag) values
<foreach item="item" index="index" collection="list" separator=",">
( #{item.itemId}, #{item.orderId}, #{item.disposalNo}, #{item.assetId}, #{item.assetCode}, #{item.assetName}, #{item.categoryId}, #{item.categoryCode}, #{item.categoryName}, #{item.specModel}, #{item.brand}, #{item.disposalReason}, #{item.disposalRemark}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}, #{item.remark}, '0')
</foreach>
</insert>
<select id="selectAvailableDisposalAssetList" resultMap="AvailableDisposalAssetResult">
select asset.asset_id, asset.asset_code, asset.asset_name, asset.category_id,
asset.category_code, asset.category_name, asset.spec_model, asset.brand,
asset.asset_status, asset.warehouse_id, asset.warehouse_code, asset.warehouse_name,
asset.location_id, asset.location_code, asset.location_name, asset.tag_code
from ams_asset asset
where asset.del_flag = '0'
and asset.asset_status in ('IN_STOCK', 'IN_USE')
and not exists (
select 1
from ams_disposal_order_item item
inner join ams_disposal_order o on item.order_id = o.order_id
where item.asset_id = asset.asset_id
and item.del_flag = '0'
and o.del_flag = '0'
and o.order_status in ('DRAFT', 'PENDING_CONFIRM')
<if test="currentOrderId != null">
and o.order_id != #{currentOrderId}
</if>
)
<if test="asset.assetCode != null and asset.assetCode != ''">
and asset.asset_code like concat('%', #{asset.assetCode}, '%')
</if>
<if test="asset.assetName != null and asset.assetName != ''">
and asset.asset_name like concat('%', #{asset.assetName}, '%')
</if>
<if test="asset.categoryName != null and asset.categoryName != ''">
and asset.category_name like concat('%', #{asset.categoryName}, '%')
</if>
order by asset.asset_id
</select>
<select id="countOtherActiveDisposalOrderByAssetId" resultType="int">
select count(1)
from ams_disposal_order_item item
inner join ams_disposal_order o on item.order_id = o.order_id
where item.asset_id = #{assetId}
and item.del_flag = '0'
and o.del_flag = '0'
and o.order_status in ('DRAFT', 'PENDING_CONFIRM')
<if test="currentOrderId != null">
and o.order_id != #{currentOrderId}
</if>
</select>
</mapper>

@ -0,0 +1,228 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('新增报废处置申请')" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-disposal-add">
<h4 class="form-header h4">基本信息</h4>
<input type="hidden" name="applicantId" th:value="${applicantId}" />
<input type="hidden" name="applyDeptId" th:value="${applyDeptId}" />
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人:</label>
<div class="col-sm-8">
<input name="applicantName" class="form-control" type="text" th:value="${applicantName}" readonly>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请部门:</label>
<div class="col-sm-8">
<input name="applyDeptName" class="form-control" type="text" th:value="${applyDeptName}" readonly>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label is-required">处置方式:</label>
<div class="col-sm-8">
<input name="disposalMethod" class="form-control" type="text" placeholder="如:报废、变卖、捐赠等" required max-length="64">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label">申请备注:</label>
<div class="col-sm-10">
<textarea name="remark" class="form-control" placeholder="请输入报废备注信息" max-length="500"></textarea>
</div>
</div>
</div>
</div>
<h4 class="form-header h4">报废处置明细信息</h4>
<div class="row">
<div class="col-sm-12">
<div class="btn-group-sm" id="toolbar" role="group">
<button type="button" class="btn btn-success" onclick="selectAssets()"><i class="fa fa-plus"> 选择资产</i></button>
<button type="button" class="btn btn-danger" onclick="removeSelectedAssets()"><i class="fa fa-minus"> 移除资产</i></button>
</div>
<div class="col-sm-12 select-table table-striped" style="padding-top: 10px;">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "asset/disposal";
$("#form-disposal-add").validate({
focusCleanup: true
});
function submitHandler() {
if ($.validate.form()) {
var data = $("#bootstrap-table").bootstrapTable("getData");
if (data.length === 0) {
$.modal.alertWarning("请选择需要报废的资产明细!");
return;
}
syncDetailRows();
$.operate.save(prefix + "/add", $('#form-disposal-add').serialize());
}
}
$(function() {
initDetailTable([]);
});
function initDetailTable(data) {
$.table.init({
data: data,
pagination: false,
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
sidePagination: "client",
columns: [
{ checkbox: true },
{
field: "assetId",
title: "资产编码",
formatter: function(value, row, index) {
return buildAssetCell(row, index);
}
},
{ field: "assetName", title: "资产名称" },
{ field: "categoryName", title: "资产类别" },
{ field: "specModel", title: "规格型号" },
{ field: "brand", title: "品牌" },
{
field: "disposalReason",
title: "报废原因",
formatter: function(value, row, index) {
return buildInput("amsDisposalOrderItemList[" + index + "].disposalReason", value, 500);
}
},
{
field: "disposalRemark",
title: "处置备注",
formatter: function(value, row, index) {
return buildInput("amsDisposalOrderItemList[" + index + "].disposalRemark", value, 500);
}
},
{
title: "操作",
align: "center",
formatter: function(value, row) {
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="removeAsset(\'' + row.assetId + '\')"><i class="fa fa-remove"></i>删除</a>';
}
}
]
});
}
function selectAssets() {
syncDetailRows();
$.modal.openOptions({
title: "选择待报废资产",
url: prefix + "/selectAsset",
width: "1200",
height: "680",
callBack: addSelectedAssets
});
}
function addSelectedAssets(index, layero) {
var selectedAssets = layero.find("iframe")[0].contentWindow.getSelectedAssets();
if (!selectedAssets || selectedAssets.length === 0) {
$.modal.alertWarning("请至少选择一条资产记录");
return;
}
syncDetailRows();
var existing = {};
$.each($("#bootstrap-table").bootstrapTable("getData"), function(i, row) {
existing[String(row.assetId)] = true;
});
$.each(selectedAssets, function(i, asset) {
if (!existing[String(asset.assetId)]) {
$("#bootstrap-table").bootstrapTable("insertRow", {
index: $("#bootstrap-table").bootstrapTable("getData").length,
row: buildDisposalItem(asset)
});
existing[String(asset.assetId)] = true;
}
});
$.modal.close(index);
}
function buildDisposalItem(asset) {
return {
assetId: asset.assetId,
assetCode: asset.assetCode,
assetName: asset.assetName,
categoryName: asset.categoryName,
specModel: asset.specModel,
brand: asset.brand,
disposalReason: "",
disposalRemark: ""
};
}
function removeSelectedAssets() {
var rows = $("#bootstrap-table").bootstrapTable("getSelections");
if (rows.length === 0) {
$.modal.alertWarning("请至少选择一条记录");
return;
}
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", {
field: "assetId",
values: $.map(rows, function(row) { return row.assetId; })
});
}
function removeAsset(assetId) {
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", { field: "assetId", values: [assetId] });
}
function syncDetailRows() {
var rows = $("#bootstrap-table").bootstrapTable("getData");
$.each(rows, function(index, row) {
var tr = $("#bootstrap-table tbody tr[data-index='" + index + "']");
row.disposalReason = tr.find("[name$='.disposalReason']").val() || "";
row.disposalRemark = tr.find("[name$='.disposalRemark']").val() || "";
});
}
function buildAssetCell(row, index) {
var hidden = $("<input>").attr({
type: "hidden",
name: "amsDisposalOrderItemList[" + index + "].assetId",
value: row.assetId
}).prop("outerHTML");
return hidden + $("<span>").text(row.assetCode).prop("outerHTML");
}
function buildInput(name, value, maxLength) {
return $("<input>").addClass("form-control").attr({
type: "text",
name: name,
maxlength: maxLength
}).val(value || "").prop("outerHTML");
}
</script>
</body>
</html>

@ -0,0 +1,207 @@
<!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('报废处置管理列表')" />
</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>
<input type="text" name="disposalNo"/>
</li>
<li>
<label>申请人:</label>
<input type="text" name="applicantName"/>
</li>
<li>
<label>申请部门:</label>
<input type="text" name="applyDeptName"/>
</li>
<li>
<label>处置方式:</label>
<input type="text" name="disposalMethod"/>
</li>
<li>
<label>资产编码:</label>
<input type="text" name="assetCode"/>
</li>
<li class="select-time">
<label>处置时间:</label>
<input type="text" class="time-input" id="startTime" placeholder="开始时间" name="params[beginDisposalTime]"/>
<span>-</span>
<input type="text" class="time-input" id="endTime" placeholder="结束时间" name="params[endDisposalTime]"/>
</li>
<li>
<label>单据状态:</label>
<select name="orderStatus" th:with="type=${@dict.getType('ams_disposal_status')}">
<option value="">所有</option>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><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-success" onclick="$.operate.add()" shiro:hasPermission="asset:disposal:add">
<i class="fa fa-plus"></i> 新增申请
</a>
<a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="asset:disposal:export">
<i class="fa fa-download"></i> 导出
</a>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var editFlag = [[${@permission.hasPermi('asset:disposal:edit')}]];
var removeFlag = [[${@permission.hasPermi('asset:disposal:remove')}]];
var submitFlag = [[${@permission.hasPermi('asset:disposal:submit')}]];
var confirmFlag = [[${@permission.hasPermi('asset:disposal:confirm')}]];
var rejectFlag = [[${@permission.hasPermi('asset:disposal:reject')}]];
var orderStatusDatas = [[${@dict.getType('ams_disposal_status')}]];
var prefix = ctx + "asset/disposal";
$(function() {
var options = {
url: prefix + "/list",
viewUrl: prefix + "/view/{id}",
createUrl: prefix + "/add",
updateUrl: prefix + "/edit/{id}",
removeUrl: prefix + "/remove",
exportUrl: prefix + "/export",
modalName: "报废处置单",
columns: [{
checkbox: true
},
{
field: 'orderId',
title: '单据ID',
visible: false
},
{
field: 'disposalNo',
title: '报废单号'
},
{
field: 'applicantName',
title: '申请人'
},
{
field: 'applyDeptName',
title: '申请部门'
},
{
field: 'disposalMethod',
title: '处置方式'
},
{
field: 'createTime',
title: '申请时间'
},
{
field: 'confirmUserName',
title: '确认人'
},
{
field: 'confirmTime',
title: '确认时间'
},
{
field: 'orderStatus',
title: '单据状态',
formatter: function(value, row, index) {
return $.table.selectDictLabel(orderStatusDatas, value);
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
// 查看详情按钮
actions.push('<a class="btn btn-info btn-xs" href="javascript:void(0)" onclick="$.operate.view(\'' + row.orderId + '\')"><i class="fa fa-eye"></i>查看</a> ');
// 草稿状态允许编辑、提交、删除;已驳回是终态,只允许查看。
if (row.orderStatus === 'DRAFT') {
actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.orderId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
actions.push('<a class="btn btn-warning btn-xs ' + submitFlag + '" href="javascript:void(0)" onclick="submitOrder(\'' + row.orderId + '\')"><i class="fa fa-send"></i>提交</a> ');
actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.orderId + '\')"><i class="fa fa-remove"></i>删除</a> ');
}
// 待确认状态允许确认报废和驳回
if (row.orderStatus === 'PENDING_CONFIRM') {
actions.push('<a class="btn btn-success btn-xs ' + confirmFlag + '" href="javascript:void(0)" onclick="confirmOrder(\'' + row.orderId + '\')"><i class="fa fa-check"></i>确认报废</a> ');
actions.push('<a class="btn btn-danger btn-xs ' + rejectFlag + '" href="javascript:void(0)" onclick="rejectOrder(\'' + row.orderId + '\')"><i class="fa fa-reply"></i>驳回</a> ');
}
return actions.join('');
}
}]
};
$.table.init(options);
});
function submitOrder(orderId) {
$.modal.confirm("确认要提交该报废申请吗?", function() {
$.post(prefix + "/submit/" + orderId, function(result) {
if (result.code == web_status.SUCCESS) {
$.modal.msgSuccess(result.msg);
$.table.refresh();
} else {
$.modal.alertError(result.msg);
}
});
});
}
function confirmOrder(orderId) {
$.modal.confirm("确认要同意并执行此报废处置吗?此操作将更改对应资产为已报废状态,并且不可回滚!", function() {
$.post(prefix + "/confirm/" + orderId, function(result) {
if (result.code == web_status.SUCCESS) {
$.modal.msgSuccess(result.msg);
$.table.refresh();
} else {
$.modal.alertError(result.msg);
}
});
});
}
function rejectOrder(orderId) {
$.modal.openPrompt("驳回原因", "请输入驳回原因", function(index, layero) {
var rejectReason = layero.find(".layui-layer-input").val();
if ($.common.isEmpty(rejectReason)) {
$.modal.msgWarning("驳回原因不能为空");
return false;
}
if (rejectReason.length > 500) {
$.modal.msgWarning("驳回原因长度不能超过500个字符");
return false;
}
$.post(prefix + "/reject/" + orderId, { rejectReason: rejectReason }, function(result) {
if (result.code == web_status.SUCCESS) {
$.modal.msgSuccess(result.msg);
layer.close(index);
$.table.refresh();
} else {
$.modal.alertError(result.msg);
}
});
});
}
</script>
</body>
</html>

@ -0,0 +1,250 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('修改报废处置申请')" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-disposal-edit" th:object="${amsDisposalOrder}">
<h4 class="form-header h4">基本信息</h4>
<input name="orderId" th:field="*{orderId}" type="hidden" />
<input type="hidden" name="applicantId" th:field="*{applicantId}" />
<input type="hidden" name="applyDeptId" th:field="*{applyDeptId}" />
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">报废单号:</label>
<div class="col-sm-8">
<input name="disposalNo" th:field="*{disposalNo}" class="form-control" type="text" readonly>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">单据状态:</label>
<div class="col-sm-8">
<select name="orderStatus" class="form-control" th:with="type=${@dict.getType('ams_disposal_status')}" th:field="*{orderStatus}" disabled>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人:</label>
<div class="col-sm-8">
<input name="applicantName" class="form-control" type="text" th:field="*{applicantName}" readonly>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请部门:</label>
<div class="col-sm-8">
<input name="applyDeptName" class="form-control" type="text" th:field="*{applyDeptName}" readonly>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label is-required">处置方式:</label>
<div class="col-sm-8">
<input name="disposalMethod" th:field="*{disposalMethod}" class="form-control" type="text" placeholder="如:报废、变卖、捐赠等" required max-length="64">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label">申请备注:</label>
<div class="col-sm-10">
<textarea name="remark" class="form-control" placeholder="请输入报废备注信息" max-length="500">[[*{remark}]]</textarea>
</div>
</div>
</div>
</div>
<h4 class="form-header h4">报废处置明细信息</h4>
<div class="row">
<div class="col-sm-12">
<div class="btn-group-sm" id="toolbar" role="group">
<button type="button" class="btn btn-success" onclick="selectAssets()"><i class="fa fa-plus"> 选择资产</i></button>
<button type="button" class="btn btn-danger" onclick="removeSelectedAssets()"><i class="fa fa-minus"> 移除资产</i></button>
</div>
<div class="col-sm-12 select-table table-striped" style="padding-top: 10px;">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "asset/disposal";
$("#form-disposal-edit").validate({
focusCleanup: true
});
function submitHandler() {
if ($.validate.form()) {
var data = $("#bootstrap-table").bootstrapTable("getData");
if (data.length === 0) {
$.modal.alertWarning("请选择需要报废的资产明细!");
return;
}
syncDetailRows();
$.operate.save(prefix + "/edit", $('#form-disposal-edit').serialize());
}
}
$(function() {
var data = [[${amsDisposalOrder.amsDisposalOrderItemList}]];
initDetailTable(data);
});
function initDetailTable(data) {
$.table.init({
data: data,
pagination: false,
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
sidePagination: "client",
columns: [
{ checkbox: true },
{
field: "assetId",
title: "资产编码",
formatter: function(value, row, index) {
return buildAssetCell(row, index);
}
},
{ field: "assetName", title: "资产名称" },
{ field: "categoryName", title: "资产类别" },
{ field: "specModel", title: "规格型号" },
{ field: "brand", title: "品牌" },
{
field: "disposalReason",
title: "报废原因",
formatter: function(value, row, index) {
return buildInput("amsDisposalOrderItemList[" + index + "].disposalReason", value, 500);
}
},
{
field: "disposalRemark",
title: "处置备注",
formatter: function(value, row, index) {
return buildInput("amsDisposalOrderItemList[" + index + "].disposalRemark", value, 500);
}
},
{
title: "操作",
align: "center",
formatter: function(value, row) {
return '<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="removeAsset(\'' + row.assetId + '\')"><i class="fa fa-remove"></i>删除</a>';
}
}
]
});
}
function selectAssets() {
syncDetailRows();
$.modal.openOptions({
title: "选择待报废资产",
url: prefix + "/selectAsset?orderId=" + $("[name='orderId']").val(),
width: "1200",
height: "680",
callBack: addSelectedAssets
});
}
function addSelectedAssets(index, layero) {
var selectedAssets = layero.find("iframe")[0].contentWindow.getSelectedAssets();
if (!selectedAssets || selectedAssets.length === 0) {
$.modal.alertWarning("请至少选择一条资产记录");
return;
}
syncDetailRows();
var existing = {};
$.each($("#bootstrap-table").bootstrapTable("getData"), function(i, row) {
existing[String(row.assetId)] = true;
});
$.each(selectedAssets, function(i, asset) {
if (!existing[String(asset.assetId)]) {
$("#bootstrap-table").bootstrapTable("insertRow", {
index: $("#bootstrap-table").bootstrapTable("getData").length,
row: buildDisposalItem(asset)
});
existing[String(asset.assetId)] = true;
}
});
$.modal.close(index);
}
function buildDisposalItem(asset) {
return {
assetId: asset.assetId,
assetCode: asset.assetCode,
assetName: asset.assetName,
categoryName: asset.categoryName,
specModel: asset.specModel,
brand: asset.brand,
disposalReason: "",
disposalRemark: ""
};
}
function removeSelectedAssets() {
var rows = $("#bootstrap-table").bootstrapTable("getSelections");
if (rows.length === 0) {
$.modal.alertWarning("请至少选择一条记录");
return;
}
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", {
field: "assetId",
values: $.map(rows, function(row) { return row.assetId; })
});
}
function removeAsset(assetId) {
syncDetailRows();
$("#bootstrap-table").bootstrapTable("remove", { field: "assetId", values: [assetId] });
}
function syncDetailRows() {
var rows = $("#bootstrap-table").bootstrapTable("getData");
$.each(rows, function(index, row) {
var tr = $("#bootstrap-table tbody tr[data-index='" + index + "']");
row.disposalReason = tr.find("[name$='.disposalReason']").val() || "";
row.disposalRemark = tr.find("[name$='.disposalRemark']").val() || "";
});
}
function buildAssetCell(row, index) {
var hidden = $("<input>").attr({
type: "hidden",
name: "amsDisposalOrderItemList[" + index + "].assetId",
value: row.assetId
}).prop("outerHTML");
return hidden + $("<span>").text(row.assetCode).prop("outerHTML");
}
function buildInput(name, value, maxLength) {
return $("<input>").addClass("form-control").attr({
type: "text",
name: name,
maxlength: maxLength
}).val(value || "").prop("outerHTML");
}
</script>
</body>
</html>

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('选择待报废资产')" />
</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><input type="text" name="assetCode"></li>
<li><label>资产名称:</label><input type="text" name="assetName"></li>
<li><label>资产类别:</label><input type="text" name="categoryName"></li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()">
<i class="fa fa-search"></i>&nbsp;搜索
</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()">
<i class="fa fa-refresh"></i>&nbsp;重置
</a>
</li>
</ul>
</div>
</form>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "asset/disposal";
var orderId = [[${orderId}]];
$(function() {
var url = prefix + "/availableAssetList";
if (orderId) {
url += "?orderId=" + orderId;
}
$.table.init({
url: url,
showSearch: false,
showRefresh: true,
showToggle: false,
showColumns: false,
modalName: "待报废资产",
columns: [
{ checkbox: true },
{ field: "assetCode", title: "资产编码" },
{ field: "assetName", title: "资产名称" },
{ field: "categoryName", title: "资产类别" },
{ field: "specModel", title: "规格型号" },
{ field: "brand", title: "品牌" },
{ field: "warehouseName", title: "当前仓库" },
{ field: "locationName", title: "当前位置" }
]
});
});
function getSelectedAssets() {
return $("#bootstrap-table").bootstrapTable("getSelections");
}
</script>
</body>
</html>

@ -0,0 +1,175 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('报废处置单详细')" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal" th:object="${amsDisposalOrder}">
<h4 class="form-header h4">基本信息</h4>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">报废单号:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{disposalNo}"></p>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">单据状态:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{@dict.getLabel('ams_disposal_status', orderStatus)}"></p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请人:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{applicantName}"></p>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请部门:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{applyDeptName}"></p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">处置方式:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{disposalMethod}"></p>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">申请时间:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{#dates.format(createTime, 'yyyy-MM-dd HH:mm:ss')}"></p>
</div>
</div>
</div>
</div>
<h4 class="form-header h4" th:if="*{confirmUserId != null}">确认信息</h4>
<div class="row" th:if="*{confirmUserId != null}">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认人:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{confirmUserName}"></p>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-4 control-label">确认时间:</label>
<div class="col-sm-8">
<p class="form-control-plaintext" th:text="*{#dates.format(confirmTime, 'yyyy-MM-dd HH:mm:ss')}"></p>
</div>
</div>
</div>
</div>
<h4 class="form-header h4" th:if="*{rejectReason != null and rejectReason != ''}">驳回信息</h4>
<div class="row" th:if="*{rejectReason != null and rejectReason != ''}">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label">驳回原因:</label>
<div class="col-sm-10">
<p class="form-control-plaintext text-danger" th:text="*{rejectReason}"></p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-2 control-label">申请备注:</label>
<div class="col-sm-10">
<p class="form-control-plaintext" th:text="*{remark}"></p>
</div>
</div>
</div>
</div>
<h4 class="form-header h4">报废处置明细信息</h4>
<div class="row">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
$(function() {
var data = [[${amsDisposalOrder.amsDisposalOrderItemList}]];
var options = {
data: data,
pagination: false,
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
sidePagination: "client",
columns: [
{
field: 'index',
title: '序号',
formatter: function (value, row, index) {
return index + 1;
}
},
{
field: 'assetCode',
title: '资产编码'
},
{
field: 'assetName',
title: '资产名称'
},
{
field: 'categoryName',
title: '资产类别'
},
{
field: 'specModel',
title: '规格型号'
},
{
field: 'brand',
title: '品牌'
},
{
field: 'disposalReason',
title: '报废原因',
formatter: function(value) {
return value || "-";
}
},
{
field: 'disposalRemark',
title: '处置备注',
formatter: function(value) {
return value || "-";
}
}]
};
$.table.init(options);
});
</script>
</body>
</html>

@ -0,0 +1,284 @@
package com.ruoyi.asset.service.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
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;
import com.ruoyi.asset.constant.DisposalOrderStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsDisposalOperateContext;
import com.ruoyi.asset.domain.AmsDisposalOrder;
import com.ruoyi.asset.domain.AmsDisposalOrderItem;
import com.ruoyi.asset.domain.AssetTransitionContext;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsDisposalOrderMapper;
import com.ruoyi.asset.service.IAssetStatusTransitionService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.service.ISysCodeRuleService;
/**
*
*
* @author Yangk
* @date 2026-06-17
*/
@ExtendWith(MockitoExtension.class)
class AmsDisposalOrderServiceImplTest
{
@Mock
private AmsDisposalOrderMapper amsDisposalOrderMapper;
@Mock
private AmsAssetMapper amsAssetMapper;
@Mock
private ISysCodeRuleService sysCodeRuleService;
@Mock
private IAssetStatusTransitionService assetStatusTransitionService;
@InjectMocks
private AmsDisposalOrderServiceImpl service;
/**
* 稿
*/
@Test
void insertShouldGenerateCodeAndFillSnapshots()
{
AmsDisposalOrder order = buildRequest();
order.setApplicantId(999L);
order.setApplicantName("前端篡改申请人");
order.setApplyDeptId(888L);
order.setApplyDeptName("前端篡改部门");
stubAsset(1L, "IN_STOCK");
when(sysCodeRuleService.nextCode("DISPOSAL_ORDER")).thenReturn("BF202606170001");
when(amsDisposalOrderMapper.countOtherActiveDisposalOrderByAssetId(eq(1L), eq(null))).thenReturn(0);
doAnswer(invocation -> {
AmsDisposalOrder inserted = invocation.getArgument(0);
inserted.setOrderId(100L);
return 1;
}).when(amsDisposalOrderMapper).insertAmsDisposalOrder(any(AmsDisposalOrder.class));
assertEquals(1, service.insertAmsDisposalOrder(order, adminContext()));
assertEquals("BF202606170001", order.getDisposalNo());
assertEquals(DisposalOrderStatus.DRAFT, order.getOrderStatus());
assertEquals(9L, order.getApplicantId());
assertEquals("管理员", order.getApplicantName());
assertEquals(3L, order.getApplyDeptId());
assertEquals("资产管理部", order.getApplyDeptName());
assertEquals("admin", order.getCreateBy());
assertNotNull(order.getCreateTime());
assertNull(order.getConfirmUserId());
// 校验明细快照被回填
AmsDisposalOrderItem item = order.getAmsDisposalOrderItemList().get(0);
assertEquals("A001", item.getAssetCode());
assertEquals("资产A", item.getAssetName());
assertEquals("CAT-A", item.getCategoryCode());
assertEquals("品牌A", item.getBrand());
}
/**
* 稿
*/
@Test
void insertShouldRejectIfAssetOccupied()
{
AmsDisposalOrder order = buildRequest();
stubAsset(1L, "IN_STOCK");
when(amsDisposalOrderMapper.countOtherActiveDisposalOrderByAssetId(eq(1L), eq(null))).thenReturn(1);
assertThrows(ServiceException.class, () -> service.insertAmsDisposalOrder(order, adminContext()));
}
/**
* 稿
*/
@Test
void insertShouldRejectIfAssetStatusInvalid()
{
AmsDisposalOrder order = buildRequest();
stubAsset(1L, "DISPOSED");
assertThrows(ServiceException.class, () -> service.insertAmsDisposalOrder(order, adminContext()));
}
/**
* 稿
*/
@Test
void updateShouldSuccessWhenStatusIsDraft()
{
AmsDisposalOrder current = buildRequest();
current.setOrderId(100L);
current.setDisposalNo("BF202606170001");
current.setOrderStatus(DisposalOrderStatus.DRAFT);
current.setApplicantId(9L);
current.setApplicantName("管理员");
current.setApplyDeptId(3L);
current.setApplyDeptName("资产管理部");
current.setCreateBy("admin");
current.setCreateTime(new Date());
when(amsDisposalOrderMapper.selectAmsDisposalOrderByOrderIdForUpdate(eq(100L))).thenReturn(current);
stubAsset(1L, "IN_STOCK");
when(amsDisposalOrderMapper.countOtherActiveDisposalOrderByAssetId(eq(1L), eq(100L))).thenReturn(0);
when(amsDisposalOrderMapper.updateAmsDisposalOrder(any(AmsDisposalOrder.class))).thenReturn(1);
AmsDisposalOrder updateReq = buildRequest();
updateReq.setOrderId(100L);
updateReq.setDisposalMethod("新处置方式");
updateReq.setApplicantId(999L);
updateReq.setApplicantName("前端篡改申请人");
assertEquals(1, service.updateAmsDisposalOrder(updateReq, adminContext()));
assertEquals(9L, updateReq.getApplicantId());
assertEquals("管理员", updateReq.getApplicantName());
assertEquals("admin", updateReq.getUpdateBy());
assertEquals(DisposalOrderStatus.DRAFT, updateReq.getParams().get("expectedOrderStatus"));
verify(amsDisposalOrderMapper).deleteAmsDisposalOrderItemByOrderId(eq(100L));
}
/**
*
*/
@Test
void submitShouldChangeStatusToPendingConfirm()
{
AmsDisposalOrder current = buildRequest();
current.setOrderId(100L);
current.setDisposalNo("BF202606170001");
current.setOrderStatus(DisposalOrderStatus.DRAFT);
when(amsDisposalOrderMapper.selectAmsDisposalOrderByOrderIdForUpdate(eq(100L))).thenReturn(current);
stubAsset(1L, "IN_STOCK");
when(amsDisposalOrderMapper.countOtherActiveDisposalOrderByAssetId(eq(1L), eq(100L))).thenReturn(0);
when(amsDisposalOrderMapper.updateAmsDisposalOrder(any(AmsDisposalOrder.class))).thenReturn(1);
assertEquals(1, service.submitDisposal(100L, adminContext()));
assertEquals(DisposalOrderStatus.PENDING_CONFIRM, current.getOrderStatus());
assertEquals(DisposalOrderStatus.DRAFT, current.getParams().get("expectedOrderStatus"));
}
/**
*
*/
@Test
void confirmShouldChangeStatusToDisposedDoneAndCallTransition()
{
AmsDisposalOrder current = buildRequest();
current.setOrderId(100L);
current.setDisposalNo("BF202606170001");
current.setOrderStatus(DisposalOrderStatus.PENDING_CONFIRM);
AmsDisposalOrderItem item = current.getAmsDisposalOrderItemList().get(0);
item.setItemId(500L);
when(amsDisposalOrderMapper.selectAmsDisposalOrderByOrderIdForUpdate(eq(100L))).thenReturn(current);
stubAsset(1L, "IN_STOCK");
when(amsDisposalOrderMapper.countOtherActiveDisposalOrderByAssetId(eq(1L), eq(100L))).thenReturn(0);
when(amsDisposalOrderMapper.updateAmsDisposalOrder(any(AmsDisposalOrder.class))).thenReturn(1);
assertEquals(1, service.confirmDisposal(100L, adminContext()));
assertEquals(DisposalOrderStatus.DISPOSED_DONE, current.getOrderStatus());
assertEquals(DisposalOrderStatus.PENDING_CONFIRM, current.getParams().get("expectedOrderStatus"));
assertNotNull(current.getConfirmTime());
assertNotNull(current.getDisposalTime());
ArgumentCaptor<AssetTransitionContext> contextCaptor = ArgumentCaptor.forClass(AssetTransitionContext.class);
verify(assetStatusTransitionService).confirmDisposal(eq(1L), contextCaptor.capture());
AssetTransitionContext transitionContext = contextCaptor.getValue();
assertEquals(100L, transitionContext.getSourceOrderId());
assertEquals("BF202606170001", transitionContext.getSourceOrderNo());
assertEquals(500L, transitionContext.getSourceItemId());
assertEquals(9L, transitionContext.getOperateUserId());
assertEquals("管理员", transitionContext.getOperateUserName());
}
/**
*
*/
@Test
void rejectShouldChangeStatusToRejected()
{
AmsDisposalOrder current = buildRequest();
current.setOrderId(100L);
current.setDisposalNo("BF202606170001");
current.setOrderStatus(DisposalOrderStatus.PENDING_CONFIRM);
when(amsDisposalOrderMapper.selectAmsDisposalOrderByOrderIdForUpdate(eq(100L))).thenReturn(current);
when(amsDisposalOrderMapper.updateAmsDisposalOrder(any(AmsDisposalOrder.class))).thenReturn(1);
assertEquals(1, service.rejectDisposal(100L, "资料不全", adminContext()));
assertEquals(DisposalOrderStatus.REJECTED, current.getOrderStatus());
assertEquals(DisposalOrderStatus.PENDING_CONFIRM, current.getParams().get("expectedOrderStatus"));
assertEquals("资料不全", current.getRejectReason());
}
// ========================================== 辅助构造方法 ==========================================
private AmsDisposalOrder buildRequest()
{
AmsDisposalOrder order = new AmsDisposalOrder();
order.setDisposalMethod("垃圾分类处置");
order.setRemark("常规报废");
AmsDisposalOrderItem item = new AmsDisposalOrderItem();
item.setAssetId(1L);
item.setDisposalReason("物理损坏");
item.setDisposalRemark("不可修复");
List<AmsDisposalOrderItem> items = new ArrayList<>();
items.add(item);
order.setAmsDisposalOrderItemList(items);
return order;
}
private AmsDisposalOperateContext adminContext()
{
return new AmsDisposalOperateContext(9L, "管理员", "admin", 3L, "资产管理部");
}
private void stubAsset(Long assetId, String status)
{
AmsAsset asset = new AmsAsset();
asset.setAssetId(assetId);
asset.setAssetCode("A001");
asset.setAssetName("资产A");
asset.setCategoryId(2L);
asset.setCategoryCode("CAT-A");
asset.setCategoryName("类别A");
asset.setSpecModel("型号A");
asset.setBrand("品牌A");
asset.setAssetStatus(status);
asset.setDelFlag("0");
when(amsAssetMapper.selectAmsAssetByAssetIdForUpdate(eq(assetId))).thenReturn(asset);
}
}
Loading…
Cancel
Save