pbv5msopc 3 months ago
parent 2cdbde7910
commit 2447347288

@ -0,0 +1,114 @@
package com.example.controller;
import com.example.common.Result;
import com.example.entity.Account;
import com.example.entity.CarrierDestruction;
import com.example.service.CarrierDestructionService;
import com.example.utils.TokenUtils;
import com.github.pagehelper.PageInfo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* Controller
*/
@RestController
@RequestMapping("/destruction")
public class CarrierDestructionController {
@Resource
private CarrierDestructionService destructionService;
/**
*
*/
@PostMapping("/submit")
public Result submit(@RequestBody CarrierDestruction destruction) {
// 获取当前用户信息
Account currentUser = TokenUtils.getCurrentUser();
destruction.setApplicantId(currentUser.getId());
destruction.setApplicantName(currentUser.getName());
destructionService.submitApplication(destruction);
return Result.success();
}
/**
*
*/
@PostMapping("/approve")
public Result approve(@RequestBody CarrierDestruction destruction) {
// 获取当前用户信息
Account currentUser = TokenUtils.getCurrentUser();
destructionService.approve(
destruction.getId(),
destruction.getStatus(),
destruction.getApprovalOpinion(),
currentUser.getId(),
currentUser.getName()
);
return Result.success();
}
/**
* ID
*/
@DeleteMapping("/delete/{id}")
public Result deleteById(@PathVariable Integer id) {
destructionService.deleteById(id);
return Result.success();
}
/**
* ID
*/
@GetMapping("/selectById/{id}")
public Result selectById(@PathVariable Integer id) {
CarrierDestruction destruction = destructionService.selectById(id);
return Result.success(destruction);
}
/**
*
*/
@GetMapping("/selectAll")
public Result selectAll(CarrierDestruction destruction) {
List<CarrierDestruction> list = destructionService.selectAll(destruction);
return Result.success(list);
}
/**
*
*/
@GetMapping("/selectPage")
public Result selectPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer applicantId,
@RequestParam(required = false) String status) {
PageInfo<CarrierDestruction> page = destructionService.selectPage(pageNum, pageSize, applicantId, status);
return Result.success(page);
}
/**
*
*/
@GetMapping("/myApplications")
public Result myApplications(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Account currentUser = TokenUtils.getCurrentUser();
PageInfo<CarrierDestruction> page = destructionService.selectPage(pageNum, pageSize, currentUser.getId(), null);
return Result.success(page);
}
/**
*
*/
@GetMapping("/countPending")
public Result countPending() {
int count = destructionService.countPending();
return Result.success(count);
}
}

@ -0,0 +1,164 @@
package com.example.entity;
import java.io.Serializable;
/**
*
*/
public class CarrierDestruction implements Serializable {
private static final long serialVersionUID = 1L;
/** 主键ID */
private Integer id;
/** 载体ID */
private Integer carrierId;
/** 载体名称(冗余) */
private String carrierName;
/** 载体序列号(冗余) */
private String carrierSerialNo;
/** 申请人ID */
private Integer applicantId;
/** 申请人姓名(冗余) */
private String applicantName;
/** 销毁原因 */
private String reason;
/** 状态PENDING-待审批/APPROVED-已通过/REJECTED-已拒绝) */
private String status;
/** 审批人ID */
private Integer approverId;
/** 审批人姓名(冗余) */
private String approverName;
/** 审批意见 */
private String approvalOpinion;
/** 审批时间 */
private String approvalTime;
/** 创建时间 */
private String createTime;
/** 更新时间 */
private String updateTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCarrierId() {
return carrierId;
}
public void setCarrierId(Integer carrierId) {
this.carrierId = carrierId;
}
public String getCarrierName() {
return carrierName;
}
public void setCarrierName(String carrierName) {
this.carrierName = carrierName;
}
public String getCarrierSerialNo() {
return carrierSerialNo;
}
public void setCarrierSerialNo(String carrierSerialNo) {
this.carrierSerialNo = carrierSerialNo;
}
public Integer getApplicantId() {
return applicantId;
}
public void setApplicantId(Integer applicantId) {
this.applicantId = applicantId;
}
public String getApplicantName() {
return applicantName;
}
public void setApplicantName(String applicantName) {
this.applicantName = applicantName;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Integer getApproverId() {
return approverId;
}
public void setApproverId(Integer approverId) {
this.approverId = approverId;
}
public String getApproverName() {
return approverName;
}
public void setApproverName(String approverName) {
this.approverName = approverName;
}
public String getApprovalOpinion() {
return approvalOpinion;
}
public void setApprovalOpinion(String approvalOpinion) {
this.approvalOpinion = approvalOpinion;
}
public String getApprovalTime() {
return approvalTime;
}
public void setApprovalTime(String approvalTime) {
this.approvalTime = approvalTime;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
}

@ -0,0 +1,47 @@
package com.example.mapper;
import com.example.entity.CarrierDestruction;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Mapper
*/
public interface CarrierDestructionMapper {
/**
*
*/
int insert(CarrierDestruction destruction);
/**
* ID
*/
int deleteById(Integer id);
/**
*
*/
int updateById(CarrierDestruction destruction);
/**
* ID
*/
CarrierDestruction selectById(Integer id);
/**
*
*/
List<CarrierDestruction> selectAll(CarrierDestruction destruction);
/**
* ID
*/
List<CarrierDestruction> selectByApplicantId(@Param("applicantId") Integer applicantId);
/**
*
*/
List<CarrierDestruction> selectByStatus(@Param("status") String status);
}

@ -0,0 +1,142 @@
package com.example.service;
import cn.hutool.core.date.DateUtil;
import com.example.entity.Carrier;
import com.example.entity.CarrierDestruction;
import com.example.entity.Reminder;
import com.example.mapper.CarrierDestructionMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
/**
* Service
*/
@Service
public class CarrierDestructionService {
@Resource
private CarrierDestructionMapper destructionMapper;
@Resource
private CarrierService carrierService;
@Resource
private ReminderService reminderService;
/**
*
*/
public void submitApplication(CarrierDestruction destruction) {
// 查询载体信息
Carrier carrier = carrierService.selectById(destruction.getCarrierId());
if (carrier == null) {
throw new RuntimeException("载体不存在");
}
// 设置载体信息(冗余)
destruction.setCarrierName(carrier.getName());
destruction.setCarrierSerialNo(carrier.getSerialNo());
destruction.setStatus("PENDING");
destructionMapper.insert(destruction);
}
/**
*
*/
@Transactional(rollbackFor = Exception.class)
public void approve(Integer id, String status, String opinion, Integer approverId, String approverName) {
CarrierDestruction destruction = destructionMapper.selectById(id);
if (destruction == null) {
throw new RuntimeException("申请不存在");
}
if (!"PENDING".equals(destruction.getStatus())) {
throw new RuntimeException("该申请已处理");
}
// 更新审批信息
destruction.setStatus(status);
destruction.setApproverId(approverId);
destruction.setApproverName(approverName);
destruction.setApprovalOpinion(opinion);
destruction.setApprovalTime(DateUtil.now());
destructionMapper.updateById(destruction);
// 如果审批通过,删除载体
if ("APPROVED".equals(status)) {
carrierService.deleteById(destruction.getCarrierId());
}
// 发送通知给申请人
sendNotification(destruction);
}
/**
*
*/
private void sendNotification(CarrierDestruction destruction) {
String statusText = "APPROVED".equals(destruction.getStatus()) ? "已通过" : "已拒绝";
String content = String.format(
"您提交的载体销毁申请(载体:%s序列号%s%s。审批意见%s",
destruction.getCarrierName(),
destruction.getCarrierSerialNo(),
statusText,
destruction.getApprovalOpinion() != null ? destruction.getApprovalOpinion() : "无"
);
Reminder reminder = new Reminder();
reminder.setReceiverId(destruction.getApplicantId());
reminder.setReceiverName(destruction.getApplicantName());
reminder.setContent(content);
reminder.setIsRead(0);
reminderService.add(reminder);
}
/**
* ID
*/
public void deleteById(Integer id) {
destructionMapper.deleteById(id);
}
/**
* ID
*/
public CarrierDestruction selectById(Integer id) {
return destructionMapper.selectById(id);
}
/**
*
*/
public List<CarrierDestruction> selectAll(CarrierDestruction destruction) {
return destructionMapper.selectAll(destruction);
}
/**
*
*/
public PageInfo<CarrierDestruction> selectPage(Integer pageNum, Integer pageSize,
Integer applicantId, String status) {
PageHelper.startPage(pageNum, pageSize);
CarrierDestruction query = new CarrierDestruction();
query.setApplicantId(applicantId);
query.setStatus(status);
List<CarrierDestruction> list = destructionMapper.selectAll(query);
return PageInfo.of(list);
}
/**
*
*/
public int countPending() {
List<CarrierDestruction> list = destructionMapper.selectByStatus("PENDING");
return list.size();
}
}

@ -0,0 +1,96 @@
<?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.example.mapper.CarrierDestructionMapper">
<sql id="Base_Column_List">
id, carrier_id as carrierId, carrier_name as carrierName, carrier_serial_no as carrierSerialNo,
applicant_id as applicantId, applicant_name as applicantName, reason, status,
approver_id as approverId, approver_name as approverName, approval_opinion as approvalOpinion,
approval_time as approvalTime, create_time as createTime, update_time as updateTime
</sql>
<!-- 新增销毁申请 -->
<insert id="insert" parameterType="com.example.entity.CarrierDestruction" useGeneratedKeys="true" keyProperty="id">
INSERT INTO carrier_destruction
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="carrierId != null">carrier_id,</if>
<if test="carrierName != null">carrier_name,</if>
<if test="carrierSerialNo != null">carrier_serial_no,</if>
<if test="applicantId != null">applicant_id,</if>
<if test="applicantName != null">applicant_name,</if>
<if test="reason != null">reason,</if>
<if test="status != null">status,</if>
<if test="approverId != null">approver_id,</if>
<if test="approverName != null">approver_name,</if>
<if test="approvalOpinion != null">approval_opinion,</if>
<if test="approvalTime != null">approval_time,</if>
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
<if test="carrierId != null">#{carrierId},</if>
<if test="carrierName != null">#{carrierName},</if>
<if test="carrierSerialNo != null">#{carrierSerialNo},</if>
<if test="applicantId != null">#{applicantId},</if>
<if test="applicantName != null">#{applicantName},</if>
<if test="reason != null">#{reason},</if>
<if test="status != null">#{status},</if>
<if test="approverId != null">#{approverId},</if>
<if test="approverName != null">#{approverName},</if>
<if test="approvalOpinion != null">#{approvalOpinion},</if>
<if test="approvalTime != null">#{approvalTime},</if>
</trim>
</insert>
<!-- 根据ID删除 -->
<delete id="deleteById">
DELETE FROM carrier_destruction WHERE id = #{id}
</delete>
<!-- 更新销毁申请 -->
<update id="updateById" parameterType="com.example.entity.CarrierDestruction">
UPDATE carrier_destruction
<set>
<if test="status != null">status = #{status},</if>
<if test="approverId != null">approver_id = #{approverId},</if>
<if test="approverName != null">approver_name = #{approverName},</if>
<if test="approvalOpinion != null">approval_opinion = #{approvalOpinion},</if>
<if test="approvalTime != null">approval_time = #{approvalTime},</if>
</set>
WHERE id = #{id}
</update>
<!-- 根据ID查询 -->
<select id="selectById" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
WHERE id = #{id}
</select>
<!-- 查询所有销毁申请 -->
<select id="selectAll" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
<where>
<if test="carrierId != null">AND carrier_id = #{carrierId}</if>
<if test="applicantId != null">AND applicant_id = #{applicantId}</if>
<if test="status != null and status != ''">AND status = #{status}</if>
</where>
ORDER BY create_time DESC
</select>
<!-- 根据申请人ID查询 -->
<select id="selectByApplicantId" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
WHERE applicant_id = #{applicantId}
ORDER BY create_time DESC
</select>
<!-- 根据状态查询 -->
<select id="selectByStatus" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
WHERE status = #{status}
ORDER BY create_time DESC
</select>
</mapper>

@ -0,0 +1,96 @@
<?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.example.mapper.CarrierDestructionMapper">
<sql id="Base_Column_List">
id, carrier_id as carrierId, carrier_name as carrierName, carrier_serial_no as carrierSerialNo,
applicant_id as applicantId, applicant_name as applicantName, reason, status,
approver_id as approverId, approver_name as approverName, approval_opinion as approvalOpinion,
approval_time as approvalTime, create_time as createTime, update_time as updateTime
</sql>
<!-- 新增销毁申请 -->
<insert id="insert" parameterType="com.example.entity.CarrierDestruction" useGeneratedKeys="true" keyProperty="id">
INSERT INTO carrier_destruction
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="carrierId != null">carrier_id,</if>
<if test="carrierName != null">carrier_name,</if>
<if test="carrierSerialNo != null">carrier_serial_no,</if>
<if test="applicantId != null">applicant_id,</if>
<if test="applicantName != null">applicant_name,</if>
<if test="reason != null">reason,</if>
<if test="status != null">status,</if>
<if test="approverId != null">approver_id,</if>
<if test="approverName != null">approver_name,</if>
<if test="approvalOpinion != null">approval_opinion,</if>
<if test="approvalTime != null">approval_time,</if>
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
<if test="carrierId != null">#{carrierId},</if>
<if test="carrierName != null">#{carrierName},</if>
<if test="carrierSerialNo != null">#{carrierSerialNo},</if>
<if test="applicantId != null">#{applicantId},</if>
<if test="applicantName != null">#{applicantName},</if>
<if test="reason != null">#{reason},</if>
<if test="status != null">#{status},</if>
<if test="approverId != null">#{approverId},</if>
<if test="approverName != null">#{approverName},</if>
<if test="approvalOpinion != null">#{approvalOpinion},</if>
<if test="approvalTime != null">#{approvalTime},</if>
</trim>
</insert>
<!-- 根据ID删除 -->
<delete id="deleteById">
DELETE FROM carrier_destruction WHERE id = #{id}
</delete>
<!-- 更新销毁申请 -->
<update id="updateById" parameterType="com.example.entity.CarrierDestruction">
UPDATE carrier_destruction
<set>
<if test="status != null">status = #{status},</if>
<if test="approverId != null">approver_id = #{approverId},</if>
<if test="approverName != null">approver_name = #{approverName},</if>
<if test="approvalOpinion != null">approval_opinion = #{approvalOpinion},</if>
<if test="approvalTime != null">approval_time = #{approvalTime},</if>
</set>
WHERE id = #{id}
</update>
<!-- 根据ID查询 -->
<select id="selectById" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
WHERE id = #{id}
</select>
<!-- 查询所有销毁申请 -->
<select id="selectAll" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
<where>
<if test="carrierId != null">AND carrier_id = #{carrierId}</if>
<if test="applicantId != null">AND applicant_id = #{applicantId}</if>
<if test="status != null and status != ''">AND status = #{status}</if>
</where>
ORDER BY create_time DESC
</select>
<!-- 根据申请人ID查询 -->
<select id="selectByApplicantId" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
WHERE applicant_id = #{applicantId}
ORDER BY create_time DESC
</select>
<!-- 根据状态查询 -->
<select id="selectByStatus" resultType="com.example.entity.CarrierDestruction">
SELECT <include refid="Base_Column_List"/>
FROM carrier_destruction
WHERE status = #{status}
ORDER BY create_time DESC
</select>
</mapper>

@ -18,6 +18,7 @@ com\example\controller\WebController.class
com\example\common\config\WebConfig.class
com\example\common\enums\StatusEnum.class
com\example\controller\DoctorController.class
com\example\mapper\CarrierDestructionMapper.class
com\example\task\OverdueCheckTask.class
com\example\mapper\DepartmentMapper.class
com\example\service\ReminderService.class
@ -30,6 +31,7 @@ com\example\entity\Department.class
com\example\controller\CarrierController.class
com\example\service\UserService.class
com\example\service\RecordService.class
com\example\entity\CarrierDestruction.class
com\example\entity\Notice.class
com\example\controller\ApplicationController.class
com\example\controller\NoticeController.class
@ -37,6 +39,7 @@ com\example\common\config\CorsConfig.class
com\example\mapper\DoctorMapper.class
com\example\controller\RegistrationController.class
com\example\entity\Plan.class
com\example\service\CarrierDestructionService.class
com\example\entity\Carrier.class
com\example\mapper\ReminderMapper.class
com\example\controller\UserController.class
@ -67,6 +70,7 @@ com\example\entity\Reminder.class
com\example\controller\FileController.class
com\example\service\RegistrationService.class
com\example\service\ApplicationService.class
com\example\controller\CarrierDestructionController.class
com\example\mapper\CarrierMapper.class
com\example\controller\DepartmentController.class
com\example\entity\Reserve.class

@ -1,12 +1,14 @@
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\mapper\UserMapper.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\service\StatisticsService.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\RecordController.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\service\CarrierDestructionService.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\UserController.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\service\PlanService.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\entity\Doctor.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\service\ApplicationService.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\common\config\WebConfig.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\service\CarrierService.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\CarrierDestructionController.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\task\OverdueCheckTask.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\listener\RecordExcelListener.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\service\AdminService.java
@ -19,6 +21,7 @@ D:\asoftware\SRMS\src\springboot\src\main\java\com\example\mapper\DepartmentMapp
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\mapper\ReserveMapper.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\CarrierController.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\PlanController.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\mapper\CarrierDestructionMapper.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\mapper\NoticeMapper.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\entity\Reminder.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\FileController.java
@ -42,6 +45,7 @@ D:\asoftware\SRMS\src\springboot\src\main\java\com\example\exception\CustomExcep
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\service\UserService.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\mapper\AdminMapper.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\NoticeController.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\entity\CarrierDestruction.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\mapper\CabinetMapper.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\controller\ApplicationController.java
D:\asoftware\SRMS\src\springboot\src\main\java\com\example\entity\Cabinet.java

@ -0,0 +1,21 @@
-- 创建载体销毁申请表
CREATE TABLE IF NOT EXISTS `carrier_destruction` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`carrier_id` int(11) NOT NULL COMMENT '载体ID',
`carrier_name` varchar(100) DEFAULT NULL COMMENT '载体名称(冗余)',
`carrier_serial_no` varchar(100) DEFAULT NULL COMMENT '载体序列号(冗余)',
`applicant_id` int(11) NOT NULL COMMENT '申请人ID',
`applicant_name` varchar(50) DEFAULT NULL COMMENT '申请人姓名(冗余)',
`reason` text NOT NULL COMMENT '销毁原因',
`status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT '状态PENDING-待审批/APPROVED-已通过/REJECTED-已拒绝)',
`approver_id` int(11) DEFAULT NULL COMMENT '审批人ID',
`approver_name` varchar(50) DEFAULT NULL COMMENT '审批人姓名(冗余)',
`approval_opinion` varchar(500) DEFAULT NULL COMMENT '审批意见',
`approval_time` timestamp NULL DEFAULT NULL COMMENT '审批时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_carrier_id` (`carrier_id`),
KEY `idx_applicant_id` (`applicant_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='载体销毁申请表';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -38,6 +38,8 @@ const routes = [
{ path: 'carrier/ledger', name: 'CarrierLedger', meta: { name: '载体台账' }, component: () => import('../views/manager/CarrierLedger') },
{ path: 'carrier/entry', name: 'CarrierEntry', meta: { name: '载体登记入库' }, component: () => import('../views/manager/CarrierEntry') },
{ path: 'cabinet', name: 'Cabinet', meta: { name: '保密柜管理' }, component: () => import('../views/manager/Cabinet') },
{ path: 'destruction/apply', name: 'CarrierDestructionApply', meta: { name: '载体销毁申请' }, component: () => import('../views/manager/CarrierDestructionApply') },
{ path: 'destruction/approval', name: 'CarrierDestructionApproval', meta: { name: '销毁申请审批' }, component: () => import('../views/manager/CarrierDestructionApproval') },
{ path: 'approval', name: 'ApprovalCenter', meta: { name: '审批中心' }, component: () => import('../views/manager/ApprovalCenter') },
{ path: 'application/use', name: 'ApplyUse', meta: { name: '申请使用载体' }, component: () => import('../views/manager/ApplyUse') },
{ path: 'reminder', name: 'Reminder', meta: { name: '我的提醒' }, component: () => import('../views/manager/Reminder') },

@ -93,6 +93,12 @@
<el-menu-item index="/cabinet" v-if="user.role === 'DOCTOR'">
<i class="el-icon-box"></i>保密柜管理
</el-menu-item>
<el-menu-item index="/destruction/apply" v-if="user.role === 'DOCTOR'">
<i class="el-icon-delete"></i>载体销毁申请
</el-menu-item>
<el-menu-item index="/destruction/approval" v-if="user.role === 'ADMIN'">
<i class="el-icon-document-checked"></i>销毁申请审批
</el-menu-item>
<el-menu-item index="/approval" v-if="user.role === 'ADMIN'">
<i class="el-icon-s-check"></i>审批中心
</el-menu-item>

@ -0,0 +1,219 @@
<template>
<div>
<div style="margin-bottom: 20px">
<el-button type="primary" @click="handleAdd">
<i class="el-icon-plus"></i> 提交销毁申请
</el-button>
</div>
<div class="search">
<el-select v-model="status" placeholder="请选择状态" style="width: 150px" clearable>
<el-option label="待审批" value="PENDING" />
<el-option label="已通过" value="APPROVED" />
<el-option label="已拒绝" value="REJECTED" />
</el-select>
<el-button type="primary" style="margin-left: 10px" @click="load(1)"></el-button>
<el-button type="info" style="margin-left: 10px" @click="reset"></el-button>
</div>
<div class="table">
<el-table :data="tableData" stripe>
<el-table-column prop="id" label="ID" width="60" sortable></el-table-column>
<el-table-column prop="carrierName" label="载体名称" width="150"></el-table-column>
<el-table-column prop="carrierSerialNo" label="序列号" width="150"></el-table-column>
<el-table-column prop="reason" label="销毁原因" min-width="200" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 'PENDING'" type="warning" size="small"></el-tag>
<el-tag v-else-if="scope.row.status === 'APPROVED'" type="success" size="small">已通过</el-tag>
<el-tag v-else-if="scope.row.status === 'REJECTED'" type="danger" size="small">已拒绝</el-tag>
</template>
</el-table-column>
<el-table-column prop="approverName" label="审批人" width="120"></el-table-column>
<el-table-column prop="approvalOpinion" label="审批意见" width="150" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" label="申请时间" width="160"></el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="handleView(scope.row)"></el-button>
<el-button v-if="scope.row.status === 'PENDING'" size="mini" type="danger" @click="handleDelete(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-size="pageSize"
layout="total, prev, pager, next"
:total="total">
</el-pagination>
</div>
</div>
<!-- 提交申请对话框 -->
<el-dialog title="提交销毁申请" :visible.sync="formDialogVisible" width="600px" @close="resetForm">
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
<el-form-item label="选择载体" prop="carrierId">
<el-select v-model="form.carrierId" placeholder="请选择载体" filterable style="width: 100%">
<el-option
v-for="carrier in carrierList"
:key="carrier.id"
:label="`${carrier.name} - ${carrier.serialNo}`"
:value="carrier.id">
<span style="float: left">{{ carrier.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ carrier.serialNo }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="销毁原因" prop="reason">
<el-input
v-model="form.reason"
type="textarea"
:rows="5"
placeholder="请详细说明载体销毁的原因"
maxlength="500"
show-word-limit>
</el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="formDialogVisible = false">取消</el-button>
<el-button type="primary" @click="save"></el-button>
</div>
</el-dialog>
<!-- 查看详情对话框 -->
<el-dialog title="销毁申请详情" :visible.sync="detailDialogVisible" width="700px">
<el-descriptions :column="2" border>
<el-descriptions-item label="申请ID">{{ currentRow.id }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag v-if="currentRow.status === 'PENDING'" type="warning" size="small"></el-tag>
<el-tag v-else-if="currentRow.status === 'APPROVED'" type="success" size="small">已通过</el-tag>
<el-tag v-else-if="currentRow.status === 'REJECTED'" type="danger" size="small">已拒绝</el-tag>
</el-descriptions-item>
<el-descriptions-item label="载体名称">{{ currentRow.carrierName }}</el-descriptions-item>
<el-descriptions-item label="载体序列号">{{ currentRow.carrierSerialNo }}</el-descriptions-item>
<el-descriptions-item label="申请人">{{ currentRow.applicantName }}</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ currentRow.createTime }}</el-descriptions-item>
<el-descriptions-item label="销毁原因" :span="2">
<div style="white-space: pre-wrap">{{ currentRow.reason }}</div>
</el-descriptions-item>
<el-descriptions-item label="审批人">{{ currentRow.approverName || '—' }}</el-descriptions-item>
<el-descriptions-item label="审批时间">{{ currentRow.approvalTime || '—' }}</el-descriptions-item>
<el-descriptions-item label="审批意见" :span="2">
<div style="white-space: pre-wrap">{{ currentRow.approvalOpinion || '—' }}</div>
</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'CarrierDestructionApply',
data() {
return {
status: '',
tableData: [],
pageNum: 1,
pageSize: 10,
total: 0,
formDialogVisible: false,
detailDialogVisible: false,
form: {},
currentRow: {},
carrierList: [],
rules: {
carrierId: [{ required: true, message: '请选择载体', trigger: 'change' }],
reason: [{ required: true, message: '请填写销毁原因', trigger: 'blur' }]
}
}
},
created() {
this.load(1)
},
methods: {
async load(pageNum) {
if (pageNum) this.pageNum = pageNum
const res = await this.$request.get('/destruction/myApplications', {
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
status: this.status
}
})
this.tableData = res.data?.list || []
this.total = res.data?.total || 0
},
reset() {
this.status = ''
this.load(1)
},
async handleAdd() {
//
const res = await this.$request.get('/carrier/selectAll', {
params: { status: 'IN_STOCK' }
})
this.carrierList = res.data || []
this.formDialogVisible = true
},
async save() {
this.$refs.formRef.validate(async (valid) => {
if (valid) {
await this.$request.post('/destruction/submit', this.form)
this.$message.success('申请提交成功')
this.formDialogVisible = false
this.load(1)
}
})
},
handleView(row) {
this.currentRow = row
this.detailDialogVisible = true
},
handleDelete(id) {
this.$confirm('确定要撤回此申请吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await this.$request.delete('/destruction/delete/' + id)
this.$message.success('撤回成功')
this.load(this.pageNum)
})
},
handleCurrentChange(pageNum) {
this.load(pageNum)
},
resetForm() {
this.form = {}
this.$refs.formRef && this.$refs.formRef.resetFields()
}
}
}
</script>
<style scoped>
.search {
padding: 10px;
background-color: #fff;
margin-bottom: 10px;
border-radius: 5px;
}
.table {
padding: 10px;
background-color: #fff;
border-radius: 5px;
}
.pagination {
margin-top: 10px;
text-align: right;
}
</style>

@ -0,0 +1,227 @@
<template>
<div>
<div style="margin-bottom: 15px; padding: 15px; background: #fff3cd; border-left: 4px solid #ff9800; border-radius: 4px;">
<i class="el-icon-warning" style="color: #ff9800; margin-right: 8px;"></i>
<span style="color: #856404; font-weight: bold;">待审批申请{{ pendingCount }} </span>
</div>
<div class="search">
<el-select v-model="status" placeholder="请选择状态" style="width: 150px" clearable>
<el-option label="待审批" value="PENDING" />
<el-option label="已通过" value="APPROVED" />
<el-option label="已拒绝" value="REJECTED" />
</el-select>
<el-button type="primary" style="margin-left: 10px" @click="load(1)"></el-button>
<el-button type="info" style="margin-left: 10px" @click="reset"></el-button>
</div>
<div class="table">
<el-table :data="tableData" stripe>
<el-table-column prop="id" label="ID" width="60" sortable></el-table-column>
<el-table-column prop="carrierName" label="载体名称" width="150"></el-table-column>
<el-table-column prop="carrierSerialNo" label="序列号" width="150"></el-table-column>
<el-table-column prop="applicantName" label="申请人" width="120"></el-table-column>
<el-table-column prop="reason" label="销毁原因" min-width="200" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 'PENDING'" type="warning" size="small"></el-tag>
<el-tag v-else-if="scope.row.status === 'APPROVED'" type="success" size="small">已通过</el-tag>
<el-tag v-else-if="scope.row.status === 'REJECTED'" type="danger" size="small">已拒绝</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="申请时间" width="160"></el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="handleView(scope.row)"></el-button>
<el-button v-if="scope.row.status === 'PENDING'" size="mini" type="success" @click="handleApprove(scope.row, 'APPROVED')"></el-button>
<el-button v-if="scope.row.status === 'PENDING'" size="mini" type="danger" @click="handleApprove(scope.row, 'REJECTED')"></el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-size="pageSize"
layout="total, prev, pager, next"
:total="total">
</el-pagination>
</div>
</div>
<!-- 审批对话框 -->
<el-dialog :title="approvalTitle" :visible.sync="approvalDialogVisible" width="600px">
<el-descriptions :column="1" border style="margin-bottom: 20px">
<el-descriptions-item label="载体名称">{{ currentRow.carrierName }}</el-descriptions-item>
<el-descriptions-item label="载体序列号">{{ currentRow.carrierSerialNo }}</el-descriptions-item>
<el-descriptions-item label="申请人">{{ currentRow.applicantName }}</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ currentRow.createTime }}</el-descriptions-item>
<el-descriptions-item label="销毁原因">
<div style="white-space: pre-wrap">{{ currentRow.reason }}</div>
</el-descriptions-item>
</el-descriptions>
<el-form :model="approvalForm" :rules="approvalRules" ref="approvalFormRef" label-width="100px">
<el-form-item label="审批意见" prop="approvalOpinion">
<el-input
v-model="approvalForm.approvalOpinion"
type="textarea"
:rows="4"
:placeholder="approvalForm.status === 'APPROVED' ? '请填写审批意见(可选)' : '请说明拒绝原因'"
maxlength="500"
show-word-limit>
</el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="approvalDialogVisible = false">取消</el-button>
<el-button :type="approvalForm.status === 'APPROVED' ? 'success' : 'danger'" @click="submitApproval">
确认{{ approvalForm.status === 'APPROVED' ? '通过' : '拒绝' }}
</el-button>
</div>
</el-dialog>
<!-- 查看详情对话框 -->
<el-dialog title="销毁申请详情" :visible.sync="detailDialogVisible" width="700px">
<el-descriptions :column="2" border>
<el-descriptions-item label="申请ID">{{ currentRow.id }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag v-if="currentRow.status === 'PENDING'" type="warning" size="small"></el-tag>
<el-tag v-else-if="currentRow.status === 'APPROVED'" type="success" size="small">已通过</el-tag>
<el-tag v-else-if="currentRow.status === 'REJECTED'" type="danger" size="small">已拒绝</el-tag>
</el-descriptions-item>
<el-descriptions-item label="载体名称">{{ currentRow.carrierName }}</el-descriptions-item>
<el-descriptions-item label="载体序列号">{{ currentRow.carrierSerialNo }}</el-descriptions-item>
<el-descriptions-item label="申请人">{{ currentRow.applicantName }}</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ currentRow.createTime }}</el-descriptions-item>
<el-descriptions-item label="销毁原因" :span="2">
<div style="white-space: pre-wrap">{{ currentRow.reason }}</div>
</el-descriptions-item>
<el-descriptions-item label="审批人">{{ currentRow.approverName || '—' }}</el-descriptions-item>
<el-descriptions-item label="审批时间">{{ currentRow.approvalTime || '—' }}</el-descriptions-item>
<el-descriptions-item label="审批意见" :span="2">
<div style="white-space: pre-wrap">{{ currentRow.approvalOpinion || '—' }}</div>
</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'CarrierDestructionApproval',
data() {
return {
status: 'PENDING',
tableData: [],
pageNum: 1,
pageSize: 10,
total: 0,
pendingCount: 0,
approvalDialogVisible: false,
detailDialogVisible: false,
currentRow: {},
approvalForm: {},
approvalRules: {
approvalOpinion: [
{ required: false, message: '请填写审批意见', trigger: 'blur' }
]
}
}
},
computed: {
approvalTitle() {
return this.approvalForm.status === 'APPROVED' ? '审批通过' : '审批拒绝'
}
},
created() {
this.load(1)
this.loadPendingCount()
},
methods: {
async load(pageNum) {
if (pageNum) this.pageNum = pageNum
const res = await this.$request.get('/destruction/selectPage', {
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
status: this.status
}
})
this.tableData = res.data?.list || []
this.total = res.data?.total || 0
},
async loadPendingCount() {
const res = await this.$request.get('/destruction/countPending')
this.pendingCount = res.data || 0
},
reset() {
this.status = 'PENDING'
this.load(1)
},
handleApprove(row, status) {
this.currentRow = row
this.approvalForm = {
id: row.id,
status: status,
approvalOpinion: ''
}
this.approvalDialogVisible = true
},
async submitApproval() {
this.$refs.approvalFormRef.validate(async (valid) => {
if (valid) {
const confirmText = this.approvalForm.status === 'APPROVED'
? '确定通过此销毁申请吗?通过后将删除该载体数据!'
: '确定拒绝此销毁申请吗?'
this.$confirm(confirmText, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await this.$request.post('/destruction/approve', this.approvalForm)
this.$message.success('审批成功')
this.approvalDialogVisible = false
this.load(this.pageNum)
this.loadPendingCount()
})
}
})
},
handleView(row) {
this.currentRow = row
this.detailDialogVisible = true
},
handleCurrentChange(pageNum) {
this.load(pageNum)
}
}
}
</script>
<style scoped>
.search {
padding: 10px;
background-color: #fff;
margin-bottom: 10px;
border-radius: 5px;
}
.table {
padding: 10px;
background-color: #fff;
border-radius: 5px;
}
.pagination {
margin-top: 10px;
text-align: right;
}
</style>

@ -0,0 +1,367 @@
# 载体销毁申请功能说明
## 功能概述
实现涉密载体销毁的完整流程:
1. 保密室工作人员提交销毁申请并说明原因
2. 审批领导审批(通过/拒绝)
3. 审批通过后自动删除载体数据并释放保密柜空间
4. 自动发送消息通知给申请人
## 功能流程
### 1. 工作人员提交申请
**操作路径:** 业务管理 > 载体销毁申请
**步骤:**
1. 点击"提交销毁申请"按钮
2. 选择要销毁的载体(只能选择在库载体)
3. 填写销毁原因
4. 提交申请
**状态:** PENDING待审批
### 2. 领导审批
**操作路径:** 业务管理 > 销毁申请审批
**步骤:**
1. 查看待审批的销毁申请列表
2. 点击"查看"查看详细信息
3. 点击"通过"或"拒绝"
4. 填写审批意见
5. 确认审批
**审批通过APPROVED**
- 自动删除载体数据
- 释放保密柜空间current_count - 1
- 发送通知给申请人
**审批拒绝REJECTED**
- 载体数据保留
- 发送通知给申请人
### 3. 消息通知
审批完成后,系统自动发送通知给申请人:
**通过通知:**
```
您提交的载体销毁申请载体XXX序列号XXX已通过。
审批意见XXX
```
**拒绝通知:**
```
您提交的载体销毁申请载体XXX序列号XXX已拒绝。
审批意见XXX
```
## 数据库设计
### carrier_destruction 表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int | 主键ID |
| carrier_id | int | 载体ID |
| carrier_name | varchar(100) | 载体名称(冗余) |
| carrier_serial_no | varchar(100) | 载体序列号(冗余) |
| applicant_id | int | 申请人ID |
| applicant_name | varchar(50) | 申请人姓名(冗余) |
| reason | text | 销毁原因 |
| status | varchar(20) | 状态PENDING/APPROVED/REJECTED |
| approver_id | int | 审批人ID |
| approver_name | varchar(50) | 审批人姓名(冗余) |
| approval_opinion | varchar(500) | 审批意见 |
| approval_time | timestamp | 审批时间 |
| create_time | timestamp | 创建时间 |
| update_time | timestamp | 更新时间 |
## 后端实现
### 文件结构
```
src/main/java/com/example/
├── entity/
│ └── CarrierDestruction.java # 实体类
├── mapper/
│ └── CarrierDestructionMapper.java # Mapper接口
├── service/
│ └── CarrierDestructionService.java # 业务逻辑
└── controller/
└── CarrierDestructionController.java # 控制器
src/main/resources/mapper/
└── CarrierDestructionMapper.xml # MyBatis映射
```
### 主要接口
| 接口 | 方法 | 说明 |
|------|------|------|
| /destruction/submit | POST | 提交销毁申请 |
| /destruction/approve | POST | 审批申请 |
| /destruction/selectPage | GET | 分页查询申请 |
| /destruction/myApplications | GET | 查询我的申请 |
| /destruction/countPending | GET | 查询待审批数量 |
| /destruction/delete/{id} | DELETE | 撤回申请 |
### 核心业务逻辑
#### 提交申请
```java
public void submitApplication(CarrierDestruction destruction) {
// 1. 查询载体信息
Carrier carrier = carrierService.selectById(destruction.getCarrierId());
// 2. 设置载体信息(冗余)
destruction.setCarrierName(carrier.getName());
destruction.setCarrierSerialNo(carrier.getSerialNo());
destruction.setStatus("PENDING");
// 3. 插入申请记录
destructionMapper.insert(destruction);
}
```
#### 审批申请
```java
@Transactional
public void approve(Integer id, String status, String opinion,
Integer approverId, String approverName) {
// 1. 查询申请
CarrierDestruction destruction = destructionMapper.selectById(id);
// 2. 更新审批信息
destruction.setStatus(status);
destruction.setApproverId(approverId);
destruction.setApproverName(approverName);
destruction.setApprovalOpinion(opinion);
destruction.setApprovalTime(DateUtil.now());
destructionMapper.updateById(destruction);
// 3. 如果审批通过,删除载体
if ("APPROVED".equals(status)) {
carrierService.deleteById(destruction.getCarrierId());
}
// 4. 发送通知
sendNotification(destruction);
}
```
## 前端实现
### 页面结构
```
src/views/manager/
├── CarrierDestructionApply.vue # 工作人员申请页面
└── CarrierDestructionApproval.vue # 领导审批页面
```
### 工作人员申请页面
**功能:**
- 查看我的申请列表
- 提交新的销毁申请
- 查看申请详情
- 撤回待审批的申请
**状态筛选:**
- 待审批PENDING
- 已通过APPROVED
- 已拒绝REJECTED
### 领导审批页面
**功能:**
- 查看所有销毁申请
- 审批通过/拒绝
- 查看申请详情
- 显示待审批数量提醒
**审批操作:**
- 通过:填写审批意见(可选)
- 拒绝:必须说明拒绝原因
## 权限配置
### 保密室工作人员DOCTOR
**菜单:** 业务管理 > 载体销毁申请
**权限:**
- ✅ 提交销毁申请
- ✅ 查看自己的申请
- ✅ 撤回待审批的申请
- ❌ 审批申请
### 审批领导ADMIN
**菜单:** 业务管理 > 销毁申请审批
**权限:**
- ✅ 查看所有申请
- ✅ 审批申请(通过/拒绝)
- ✅ 查看申请详情
- ❌ 提交申请
## 部署步骤
### 1. 执行数据库脚本
```bash
mysql -u root -p your_database < 创建载体销毁申请表.sql
```
### 2. 编译后端
```bash
cd d:\asoftware\SRMS\src\springboot
mvn clean compile
```
### 3. 重启后端服务
完全停止并重新启动SpringBoot应用。
### 4. 刷新前端
**Ctrl + F5** 强制刷新浏览器。
## 使用场景
### 场景1正常销毁流程
1. **工作人员**:发现某载体已损坏,需要销毁
2. **提交申请**:选择载体,填写原因"载体已损坏,无法继续使用"
3. **领导审批**:查看申请,确认情况,点击"通过"
4. **系统处理**
- 删除载体数据
- 释放保密柜空间
- 发送通知给工作人员
5. **工作人员**:收到通知,进行实际销毁操作
### 场景2拒绝销毁
1. **工作人员**:提交销毁申请
2. **领导审批**:认为载体还可以使用,点击"拒绝"
3. **填写意见**"载体状态良好,暂不需要销毁"
4. **系统处理**
- 载体数据保留
- 发送通知给工作人员
5. **工作人员**:收到通知,了解拒绝原因
### 场景3撤回申请
1. **工作人员**:提交申请后发现选错了载体
2. **撤回申请**:在待审批状态下点击"撤回"
3. **系统处理**:删除申请记录
## 注意事项
1. **只能销毁在库载体**
- 使用中的载体无法提交销毁申请
- 系统只显示状态为"IN_STOCK"的载体
2. **审批通过后无法撤销**
- 载体数据已删除
- 保密柜空间已释放
- 操作不可逆
3. **待审批申请可撤回**
- 只有状态为"PENDING"的申请可以撤回
- 已审批的申请无法撤回
4. **自动释放保密柜**
- 审批通过后,系统自动调用 `carrierService.deleteById()`
- 该方法会自动释放保密柜空间current_count - 1
5. **消息通知**
- 审批完成后自动发送
- 申请人可在"我的提醒"中查看
- 支持弹窗提醒
## 测试步骤
### 1. 测试提交申请
1. 使用DOCTOR账号登录
2. 进入"载体销毁申请"
3. 点击"提交销毁申请"
4. 选择一个在库载体
5. 填写销毁原因
6. 提交
**验证:**
- ✅ 申请列表中出现新申请
- ✅ 状态为"待审批"
### 2. 测试审批通过
1. 使用ADMIN账号登录
2. 进入"销毁申请审批"
3. 找到刚才提交的申请
4. 点击"通过"
5. 填写审批意见
6. 确认
**验证:**
- ✅ 申请状态变为"已通过"
- ✅ 载体台账中该载体已删除
- ✅ 保密柜的当前存储数量减1
- ✅ 申请人收到通知
### 3. 测试审批拒绝
1. 提交新的销毁申请
2. 使用ADMIN账号审批
3. 点击"拒绝"
4. 填写拒绝原因
5. 确认
**验证:**
- ✅ 申请状态变为"已拒绝"
- ✅ 载体数据保留
- ✅ 保密柜数量不变
- ✅ 申请人收到通知
### 4. 测试撤回申请
1. 提交新的销毁申请
2. 在待审批状态下点击"撤回"
3. 确认撤回
**验证:**
- ✅ 申请从列表中消失
- ✅ 载体数据保留
## 扩展功能建议
1. **批量销毁**
- 支持一次申请销毁多个载体
- 批量审批
2. **销毁记录**
- 保留销毁历史记录
- 不删除申请记录,只标记为已销毁
3. **实物销毁确认**
- 审批通过后,工作人员确认实物已销毁
- 上传销毁证明照片
4. **统计报表**
- 销毁数量统计
- 按密级统计
- 按时间统计
5. **审批流程优化**
- 多级审批
- 会签机制
- 审批时限提醒
Loading…
Cancel
Save