修复培训:对齐电子班牌V2模型并清理演示页面
This commit is contained in:
1 parent
e65870ac6e
commit
20694559d7
9 files changed
+788
-303
No files matched your search
+3
-1
@@ -77,6 +77,7 @@ public class TrainElectronicBoardController extends BaseController
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody TrainElectronicBoard trainElectronicBoard)
|
||||
{
|
||||
trainElectronicBoard.setCreateBy(getUsername());
|
||||
return toAjax(trainElectronicBoardService.insertTrainElectronicBoard(trainElectronicBoard));
|
||||
}
|
||||
|
||||
@@ -88,6 +89,7 @@ public class TrainElectronicBoardController extends BaseController
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody TrainElectronicBoard trainElectronicBoard)
|
||||
{
|
||||
trainElectronicBoard.setUpdateBy(getUsername());
|
||||
return toAjax(trainElectronicBoardService.updateTrainElectronicBoard(trainElectronicBoard));
|
||||
}
|
||||
|
||||
@@ -99,6 +101,6 @@ public class TrainElectronicBoardController extends BaseController
|
||||
@DeleteMapping("/{boardIds}")
|
||||
public AjaxResult remove(@PathVariable String[] boardIds)
|
||||
{
|
||||
return toAjax(trainElectronicBoardService.deleteTrainElectronicBoardByBoardIds(boardIds));
|
||||
return toAjax(trainElectronicBoardService.deleteTrainElectronicBoardByBoardIds(boardIds, getUsername()));
|
||||
}
|
||||
}
|
||||
+54
-68
@@ -24,168 +24,154 @@ public class TrainElectronicBoard extends BaseEntity
|
||||
@Excel(name = "设备硬件序列号")
|
||||
private String boardSn;
|
||||
|
||||
/** 绑定教室ID train_classroom_info.class_room_id */
|
||||
@Excel(name = "绑定教室ID train_classroom_info.class_room_id")
|
||||
private String bindClassroomId;
|
||||
/** 设备名称 */
|
||||
@Excel(name = "设备名称")
|
||||
private String boardName;
|
||||
|
||||
/** 绑定开班计划 train_class_plan.plan_id */
|
||||
@Excel(name = "绑定开班计划 train_class_plan.plan_id")
|
||||
private String bindPlanId;
|
||||
/** 绑定教室ID */
|
||||
@Excel(name = "绑定教室ID")
|
||||
private String bindClassroomId;
|
||||
|
||||
/** 设备IP地址 */
|
||||
@Excel(name = "设备IP地址")
|
||||
private String boardIp;
|
||||
|
||||
/** 电子班牌显示内容 */
|
||||
@Excel(name = "电子班牌显示内容")
|
||||
private String boardContent;
|
||||
/** 设备通讯/流媒体端点 */
|
||||
@Excel(name = "设备端点")
|
||||
private String apiEndpoint;
|
||||
|
||||
/** 0 离线 1 在线正常 */
|
||||
@Excel(name = "0 离线 1 在线正常")
|
||||
private Integer boardPlaymode;
|
||||
|
||||
/** 支持签到:人脸,二维码 */
|
||||
@Excel(name = "支持签到:人脸,二维码")
|
||||
/** 支持签到:CARD,FACE,QR */
|
||||
@Excel(name = "支持签到方式")
|
||||
private String supportCheckinType;
|
||||
|
||||
/** 0 离线 1 在线正常 */
|
||||
@Excel(name = "0 离线 1 在线正常")
|
||||
@Excel(name = "设备状态", readConverterExp = "0=离线,1=在线")
|
||||
private Integer boardStatus;
|
||||
|
||||
/** 最后心跳时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "最后心跳时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "最后心跳时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastHeartbeat;
|
||||
|
||||
/** 创建管理员账号 */
|
||||
@Excel(name = "创建管理员账号")
|
||||
private String createUser;
|
||||
/** 删除标志 (0代表存在 2代表删除) */
|
||||
private String delFlag;
|
||||
|
||||
public void setBoardId(String boardId)
|
||||
public void setBoardId(String boardId)
|
||||
{
|
||||
this.boardId = boardId;
|
||||
}
|
||||
|
||||
public String getBoardId()
|
||||
public String getBoardId()
|
||||
{
|
||||
return boardId;
|
||||
}
|
||||
|
||||
public void setBoardSn(String boardSn)
|
||||
public void setBoardSn(String boardSn)
|
||||
{
|
||||
this.boardSn = boardSn;
|
||||
}
|
||||
|
||||
public String getBoardSn()
|
||||
public String getBoardSn()
|
||||
{
|
||||
return boardSn;
|
||||
}
|
||||
|
||||
public void setBindClassroomId(String bindClassroomId)
|
||||
public void setBoardName(String boardName)
|
||||
{
|
||||
this.boardName = boardName;
|
||||
}
|
||||
|
||||
public String getBoardName()
|
||||
{
|
||||
return boardName;
|
||||
}
|
||||
|
||||
public void setBindClassroomId(String bindClassroomId)
|
||||
{
|
||||
this.bindClassroomId = bindClassroomId;
|
||||
}
|
||||
|
||||
public String getBindClassroomId()
|
||||
public String getBindClassroomId()
|
||||
{
|
||||
return bindClassroomId;
|
||||
}
|
||||
|
||||
public void setBindPlanId(String bindPlanId)
|
||||
{
|
||||
this.bindPlanId = bindPlanId;
|
||||
}
|
||||
|
||||
public String getBindPlanId()
|
||||
{
|
||||
return bindPlanId;
|
||||
}
|
||||
|
||||
public void setBoardIp(String boardIp)
|
||||
public void setBoardIp(String boardIp)
|
||||
{
|
||||
this.boardIp = boardIp;
|
||||
}
|
||||
|
||||
public String getBoardIp()
|
||||
public String getBoardIp()
|
||||
{
|
||||
return boardIp;
|
||||
}
|
||||
|
||||
public void setBoardContent(String boardContent)
|
||||
public void setApiEndpoint(String apiEndpoint)
|
||||
{
|
||||
this.boardContent = boardContent;
|
||||
this.apiEndpoint = apiEndpoint;
|
||||
}
|
||||
|
||||
public String getBoardContent()
|
||||
public String getApiEndpoint()
|
||||
{
|
||||
return boardContent;
|
||||
return apiEndpoint;
|
||||
}
|
||||
|
||||
public void setBoardPlaymode(Integer boardPlaymode)
|
||||
{
|
||||
this.boardPlaymode = boardPlaymode;
|
||||
}
|
||||
|
||||
public Integer getBoardPlaymode()
|
||||
{
|
||||
return boardPlaymode;
|
||||
}
|
||||
|
||||
public void setSupportCheckinType(String supportCheckinType)
|
||||
public void setSupportCheckinType(String supportCheckinType)
|
||||
{
|
||||
this.supportCheckinType = supportCheckinType;
|
||||
}
|
||||
|
||||
public String getSupportCheckinType()
|
||||
public String getSupportCheckinType()
|
||||
{
|
||||
return supportCheckinType;
|
||||
}
|
||||
|
||||
public void setBoardStatus(Integer boardStatus)
|
||||
public void setBoardStatus(Integer boardStatus)
|
||||
{
|
||||
this.boardStatus = boardStatus;
|
||||
}
|
||||
|
||||
public Integer getBoardStatus()
|
||||
public Integer getBoardStatus()
|
||||
{
|
||||
return boardStatus;
|
||||
}
|
||||
|
||||
public void setLastHeartbeat(Date lastHeartbeat)
|
||||
public void setLastHeartbeat(Date lastHeartbeat)
|
||||
{
|
||||
this.lastHeartbeat = lastHeartbeat;
|
||||
}
|
||||
|
||||
public Date getLastHeartbeat()
|
||||
public Date getLastHeartbeat()
|
||||
{
|
||||
return lastHeartbeat;
|
||||
}
|
||||
|
||||
public void setCreateUser(String createUser)
|
||||
public void setDelFlag(String delFlag)
|
||||
{
|
||||
this.createUser = createUser;
|
||||
this.delFlag = delFlag;
|
||||
}
|
||||
|
||||
public String getCreateUser()
|
||||
public String getDelFlag()
|
||||
{
|
||||
return createUser;
|
||||
return delFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("boardId", getBoardId())
|
||||
.append("boardSn", getBoardSn())
|
||||
.append("boardName", getBoardName())
|
||||
.append("bindClassroomId", getBindClassroomId())
|
||||
.append("bindPlanId", getBindPlanId())
|
||||
.append("boardIp", getBoardIp())
|
||||
.append("boardContent", getBoardContent())
|
||||
.append("boardPlaymode", getBoardPlaymode())
|
||||
.append("apiEndpoint", getApiEndpoint())
|
||||
.append("supportCheckinType", getSupportCheckinType())
|
||||
.append("boardStatus", getBoardStatus())
|
||||
.append("lastHeartbeat", getLastHeartbeat())
|
||||
.append("createUser", getCreateUser())
|
||||
.append("delFlag", getDelFlag())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
|
||||
+7
-4
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.train.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.train.domain.TrainElectronicBoard;
|
||||
|
||||
/**
|
||||
@@ -44,18 +45,20 @@ public interface TrainElectronicBoardMapper
|
||||
public int updateTrainElectronicBoard(TrainElectronicBoard trainElectronicBoard);
|
||||
|
||||
/**
|
||||
* 删除电子班牌设备
|
||||
* 逻辑删除电子班牌设备(补齐操作人)
|
||||
*
|
||||
* @param boardId 电子班牌设备主键
|
||||
* @param updateBy 操作人账号
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteTrainElectronicBoardByBoardId(String boardId);
|
||||
public int deleteTrainElectronicBoardByBoardId(@Param("boardId") String boardId, @Param("updateBy") String updateBy);
|
||||
|
||||
/**
|
||||
* 批量删除电子班牌设备
|
||||
* 批量逻辑删除电子班牌设备(补齐操作人)
|
||||
*
|
||||
* @param boardIds 需要删除的数据主键集合
|
||||
* @param updateBy 操作人账号
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteTrainElectronicBoardByBoardIds(String[] boardIds);
|
||||
public int deleteTrainElectronicBoardByBoardIds(@Param("boardIds") String[] boardIds, @Param("updateBy") String updateBy);
|
||||
}
|
||||
+19
-1
@@ -43,14 +43,32 @@ public interface ITrainElectronicBoardService
|
||||
*/
|
||||
public int updateTrainElectronicBoard(TrainElectronicBoard trainElectronicBoard);
|
||||
|
||||
/**
|
||||
* 批量删除电子班牌设备(补齐操作人)
|
||||
*
|
||||
* @param boardIds 需要删除的电子班牌设备主键集合
|
||||
* @param updateBy 操作人账号
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteTrainElectronicBoardByBoardIds(String[] boardIds, String updateBy);
|
||||
|
||||
/**
|
||||
* 批量删除电子班牌设备
|
||||
*
|
||||
*
|
||||
* @param boardIds 需要删除的电子班牌设备主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteTrainElectronicBoardByBoardIds(String[] boardIds);
|
||||
|
||||
/**
|
||||
* 删除电子班牌设备信息(补齐操作人)
|
||||
*
|
||||
* @param boardId 电子班牌设备主键
|
||||
* @param updateBy 操作人账号
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteTrainElectronicBoardByBoardId(String boardId, String updateBy);
|
||||
|
||||
/**
|
||||
* 删除电子班牌设备信息
|
||||
*
|
||||
|
||||
+44
-3
@@ -2,6 +2,8 @@ package com.ruoyi.train.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.train.mapper.TrainElectronicBoardMapper;
|
||||
@@ -53,6 +55,16 @@ public class TrainElectronicBoardServiceImpl implements ITrainElectronicBoardSer
|
||||
@Override
|
||||
public int insertTrainElectronicBoard(TrainElectronicBoard trainElectronicBoard)
|
||||
{
|
||||
if (StringUtils.isEmpty(trainElectronicBoard.getBoardId()))
|
||||
{
|
||||
trainElectronicBoard.setBoardId(IdUtils.fastSimpleUUID());
|
||||
}
|
||||
// 新建设备时显式初始化 boardStatus = 0 (离线)
|
||||
trainElectronicBoard.setBoardStatus(0);
|
||||
if (StringUtils.isEmpty(trainElectronicBoard.getDelFlag()))
|
||||
{
|
||||
trainElectronicBoard.setDelFlag("0");
|
||||
}
|
||||
trainElectronicBoard.setCreateTime(DateUtils.getNowDate());
|
||||
return trainElectronicBoardMapper.insertTrainElectronicBoard(trainElectronicBoard);
|
||||
}
|
||||
@@ -66,20 +78,49 @@ public class TrainElectronicBoardServiceImpl implements ITrainElectronicBoardSer
|
||||
@Override
|
||||
public int updateTrainElectronicBoard(TrainElectronicBoard trainElectronicBoard)
|
||||
{
|
||||
// 设备运行状态与心跳写保护:普通管理员 edit 不允许修改 boardStatus 和 lastHeartbeat,保留数据库原值
|
||||
trainElectronicBoard.setBoardStatus(null);
|
||||
trainElectronicBoard.setLastHeartbeat(null);
|
||||
trainElectronicBoard.setUpdateTime(DateUtils.getNowDate());
|
||||
return trainElectronicBoardMapper.updateTrainElectronicBoard(trainElectronicBoard);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除电子班牌设备(记录操作人)
|
||||
*
|
||||
* @param boardIds 需要删除的电子班牌设备主键
|
||||
* @param updateBy 操作人账号
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteTrainElectronicBoardByBoardIds(String[] boardIds, String updateBy)
|
||||
{
|
||||
return trainElectronicBoardMapper.deleteTrainElectronicBoardByBoardIds(boardIds, updateBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除电子班牌设备
|
||||
*
|
||||
*
|
||||
* @param boardIds 需要删除的电子班牌设备主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteTrainElectronicBoardByBoardIds(String[] boardIds)
|
||||
{
|
||||
return trainElectronicBoardMapper.deleteTrainElectronicBoardByBoardIds(boardIds);
|
||||
return deleteTrainElectronicBoardByBoardIds(boardIds, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除电子班牌设备信息(记录操作人)
|
||||
*
|
||||
* @param boardId 电子班牌设备主键
|
||||
* @param updateBy 操作人账号
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteTrainElectronicBoardByBoardId(String boardId, String updateBy)
|
||||
{
|
||||
return trainElectronicBoardMapper.deleteTrainElectronicBoardByBoardId(boardId, updateBy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,6 +132,6 @@ public class TrainElectronicBoardServiceImpl implements ITrainElectronicBoardSer
|
||||
@Override
|
||||
public int deleteTrainElectronicBoardByBoardId(String boardId)
|
||||
{
|
||||
return trainElectronicBoardMapper.deleteTrainElectronicBoardByBoardId(boardId);
|
||||
return deleteTrainElectronicBoardByBoardId(boardId, "");
|
||||
}
|
||||
}
|
||||
+52
-45
@@ -7,43 +7,40 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<resultMap type="TrainElectronicBoard" id="TrainElectronicBoardResult">
|
||||
<result property="boardId" column="board_id" />
|
||||
<result property="boardSn" column="board_sn" />
|
||||
<result property="boardName" column="board_name" />
|
||||
<result property="bindClassroomId" column="bind_classroom_id" />
|
||||
<result property="bindPlanId" column="bind_plan_id" />
|
||||
<result property="boardIp" column="board_ip" />
|
||||
<result property="boardContent" column="board_content" />
|
||||
<result property="boardPlaymode" column="board_playmode" />
|
||||
<result property="apiEndpoint" column="api_endpoint" />
|
||||
<result property="supportCheckinType" column="support_checkin_type" />
|
||||
<result property="boardStatus" column="board_status" />
|
||||
<result property="lastHeartbeat" column="last_heartbeat" />
|
||||
<result property="createUser" column="create_user" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
<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" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectTrainElectronicBoardVo">
|
||||
select board_id, board_sn, bind_classroom_id, bind_plan_id, board_ip, board_content, board_playmode, support_checkin_type, board_status, last_heartbeat, create_user, create_time, update_time, remark from train_electronic_board
|
||||
select board_id, board_sn, board_name, bind_classroom_id, board_ip, api_endpoint, support_checkin_type, board_status, last_heartbeat, del_flag, create_by, create_time, update_by, update_time, remark from train_electronic_board
|
||||
</sql>
|
||||
|
||||
<select id="selectTrainElectronicBoardList" parameterType="TrainElectronicBoard" resultMap="TrainElectronicBoardResult">
|
||||
<include refid="selectTrainElectronicBoardVo"/>
|
||||
<where>
|
||||
<if test="boardSn != null and boardSn != ''"> and board_sn = #{boardSn}</if>
|
||||
<if test="bindClassroomId != null and bindClassroomId != ''"> and bind_classroom_id = #{bindClassroomId}</if>
|
||||
<if test="bindPlanId != null and bindPlanId != ''"> and bind_plan_id = #{bindPlanId}</if>
|
||||
<if test="boardIp != null and boardIp != ''"> and board_ip = #{boardIp}</if>
|
||||
<if test="boardContent != null and boardContent != ''"> and board_content = #{boardContent}</if>
|
||||
<if test="boardPlaymode != null "> and board_playmode = #{boardPlaymode}</if>
|
||||
<if test="supportCheckinType != null and supportCheckinType != ''"> and support_checkin_type = #{supportCheckinType}</if>
|
||||
<if test="boardStatus != null "> and board_status = #{boardStatus}</if>
|
||||
<if test="lastHeartbeat != null "> and last_heartbeat = #{lastHeartbeat}</if>
|
||||
<if test="createUser != null and createUser != ''"> and create_user = #{createUser}</if>
|
||||
</where>
|
||||
where del_flag = '0'
|
||||
<if test="boardSn != null and boardSn != ''"> and board_sn like concat('%', #{boardSn}, '%')</if>
|
||||
<if test="boardName != null and boardName != ''"> and board_name like concat('%', #{boardName}, '%')</if>
|
||||
<if test="bindClassroomId != null and bindClassroomId != ''"> and bind_classroom_id = #{bindClassroomId}</if>
|
||||
<if test="boardIp != null and boardIp != ''"> and board_ip = #{boardIp}</if>
|
||||
<if test="supportCheckinType != null and supportCheckinType != ''"> and support_checkin_type = #{supportCheckinType}</if>
|
||||
<if test="boardStatus != null "> and board_status = #{boardStatus}</if>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectTrainElectronicBoardByBoardId" parameterType="String" resultMap="TrainElectronicBoardResult">
|
||||
<include refid="selectTrainElectronicBoardVo"/>
|
||||
where board_id = #{boardId}
|
||||
where board_id = #{boardId} and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<insert id="insertTrainElectronicBoard" parameterType="TrainElectronicBoard">
|
||||
@@ -51,33 +48,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="boardId != null">board_id,</if>
|
||||
<if test="boardSn != null and boardSn != ''">board_sn,</if>
|
||||
<if test="boardName != null and boardName != ''">board_name,</if>
|
||||
<if test="bindClassroomId != null">bind_classroom_id,</if>
|
||||
<if test="bindPlanId != null">bind_plan_id,</if>
|
||||
<if test="boardIp != null">board_ip,</if>
|
||||
<if test="boardContent != null">board_content,</if>
|
||||
<if test="boardPlaymode != null">board_playmode,</if>
|
||||
<if test="apiEndpoint != null">api_endpoint,</if>
|
||||
<if test="supportCheckinType != null">support_checkin_type,</if>
|
||||
<if test="boardStatus != null">board_status,</if>
|
||||
<if test="lastHeartbeat != null">last_heartbeat,</if>
|
||||
<if test="createUser != null and createUser != ''">create_user,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
<if test="delFlag != null">del_flag,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
create_time,
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
update_time,
|
||||
<if test="remark != null">remark,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="boardId != null">#{boardId},</if>
|
||||
<if test="boardSn != null and boardSn != ''">#{boardSn},</if>
|
||||
<if test="boardName != null and boardName != ''">#{boardName},</if>
|
||||
<if test="bindClassroomId != null">#{bindClassroomId},</if>
|
||||
<if test="bindPlanId != null">#{bindPlanId},</if>
|
||||
<if test="boardIp != null">#{boardIp},</if>
|
||||
<if test="boardContent != null">#{boardContent},</if>
|
||||
<if test="boardPlaymode != null">#{boardPlaymode},</if>
|
||||
<if test="apiEndpoint != null">#{apiEndpoint},</if>
|
||||
<if test="supportCheckinType != null">#{supportCheckinType},</if>
|
||||
<if test="boardStatus != null">#{boardStatus},</if>
|
||||
<if test="lastHeartbeat != null">#{lastHeartbeat},</if>
|
||||
<if test="createUser != null and createUser != ''">#{createUser},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
<if test="delFlag != null">#{delFlag},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
clock_timestamp(),
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
clock_timestamp(),
|
||||
<if test="remark != null">#{remark},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
@@ -86,30 +85,38 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
update train_electronic_board
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="boardSn != null and boardSn != ''">board_sn = #{boardSn},</if>
|
||||
<if test="boardName != null and boardName != ''">board_name = #{boardName},</if>
|
||||
<if test="bindClassroomId != null">bind_classroom_id = #{bindClassroomId},</if>
|
||||
<if test="bindPlanId != null">bind_plan_id = #{bindPlanId},</if>
|
||||
<if test="boardIp != null">board_ip = #{boardIp},</if>
|
||||
<if test="boardContent != null">board_content = #{boardContent},</if>
|
||||
<if test="boardPlaymode != null">board_playmode = #{boardPlaymode},</if>
|
||||
<if test="apiEndpoint != null">api_endpoint = #{apiEndpoint},</if>
|
||||
<if test="supportCheckinType != null">support_checkin_type = #{supportCheckinType},</if>
|
||||
<if test="boardStatus != null">board_status = #{boardStatus},</if>
|
||||
<if test="lastHeartbeat != null">last_heartbeat = #{lastHeartbeat},</if>
|
||||
<if test="createUser != null and createUser != ''">create_user = #{createUser},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
update_time = clock_timestamp()
|
||||
</trim>
|
||||
where board_id = #{boardId}
|
||||
where board_id = #{boardId} and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<delete id="deleteTrainElectronicBoardByBoardId" parameterType="String">
|
||||
delete from train_electronic_board where board_id = #{boardId}
|
||||
</delete>
|
||||
<update id="deleteTrainElectronicBoardByBoardId">
|
||||
update train_electronic_board
|
||||
set del_flag = '2',
|
||||
update_by = #{updateBy},
|
||||
update_time = clock_timestamp()
|
||||
where board_id = #{boardId} and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<delete id="deleteTrainElectronicBoardByBoardIds" parameterType="String">
|
||||
delete from train_electronic_board where board_id in
|
||||
<foreach item="boardId" collection="array" open="(" separator="," close=")">
|
||||
<update id="deleteTrainElectronicBoardByBoardIds">
|
||||
update train_electronic_board
|
||||
set del_flag = '2',
|
||||
update_by = #{updateBy},
|
||||
update_time = clock_timestamp()
|
||||
where board_id in
|
||||
<foreach item="boardId" collection="boardIds" open="(" separator="," close=")">
|
||||
#{boardId}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
||||
and del_flag = '0'
|
||||
</update>
|
||||
</mapper>
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package com.ruoyi.train.service.impl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import com.ruoyi.train.domain.TrainElectronicBoard;
|
||||
import com.ruoyi.train.mapper.TrainElectronicBoardMapper;
|
||||
|
||||
/**
|
||||
* 电子班牌设备生命周期与生命周期只读写保护单元测试
|
||||
*
|
||||
* 验证:
|
||||
* 1. 新增不传 boardId 自动生成唯一 UUID
|
||||
* 2. 新增设备显式初始化 boardStatus = 0 (离线)
|
||||
* 3. 修改设备时 boardStatus 与 lastHeartbeat 受到只读写保护 (清空为 null,保留 DB 原值)
|
||||
* 4. 软删除记录操作人 updateBy 与状态标记
|
||||
*/
|
||||
public class TrainElectronicBoardServiceTest
|
||||
{
|
||||
private TrainElectronicBoardServiceImpl boardService;
|
||||
private MockBoardMapper mockMapper;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
boardService = new TrainElectronicBoardServiceImpl();
|
||||
mockMapper = new MockBoardMapper();
|
||||
Field field = TrainElectronicBoardServiceImpl.class.getDeclaredField("trainElectronicBoardMapper");
|
||||
field.setAccessible(true);
|
||||
field.set(boardService, mockMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertBoardWithoutBoardIdGeneratesUUIDAndOfflineStatus()
|
||||
{
|
||||
TrainElectronicBoard board = new TrainElectronicBoard();
|
||||
board.setBoardSn("SN-TEST-001");
|
||||
board.setBoardName("1号阶梯教室班牌");
|
||||
board.setBindClassroomId("ROOM-101");
|
||||
board.setBoardIp("192.168.1.50");
|
||||
board.setSupportCheckinType("CARD,FACE");
|
||||
// 显式不设置 boardId,且尝试传入状态 1
|
||||
board.setBoardStatus(1);
|
||||
|
||||
int rows = boardService.insertTrainElectronicBoard(board);
|
||||
assertEquals(1, rows);
|
||||
|
||||
// 验证 boardId 闭环自动生成
|
||||
assertNotNull("boardId 必须自动生成", board.getBoardId());
|
||||
assertTrue("boardId 长度应符合 UUID 格式", board.getBoardId().length() >= 32);
|
||||
|
||||
// 验证新建设备必须显式初始化为 0 (离线)
|
||||
assertEquals("新增班牌状态必须为离线(0)", Integer.valueOf(0), board.getBoardStatus());
|
||||
assertEquals("delFlag 必须为存在状态 0", "0", board.getDelFlag());
|
||||
assertNotNull("createTime 必须已注入", board.getCreateTime());
|
||||
|
||||
// 验证存储与回查
|
||||
TrainElectronicBoard queried = boardService.selectTrainElectronicBoardByBoardId(board.getBoardId());
|
||||
assertNotNull(queried);
|
||||
assertEquals("SN-TEST-001", queried.getBoardSn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateProtectsBoardStatusAndLastHeartbeat()
|
||||
{
|
||||
TrainElectronicBoard board = new TrainElectronicBoard();
|
||||
board.setBoardSn("SN-TEST-002");
|
||||
board.setBoardName("2号多媒体教室班牌");
|
||||
boardService.insertTrainElectronicBoard(board);
|
||||
String boardId = board.getBoardId();
|
||||
|
||||
// 模拟管理员尝试在修改中伪造心跳或篡改状态为在线
|
||||
TrainElectronicBoard updateParam = new TrainElectronicBoard();
|
||||
updateParam.setBoardId(boardId);
|
||||
updateParam.setBoardName("2号多媒体教室班牌-已重命名");
|
||||
updateParam.setBoardStatus(1); // 试图伪造在线
|
||||
updateParam.setLastHeartbeat(new Date()); // 试图伪造心跳
|
||||
|
||||
boardService.updateTrainElectronicBoard(updateParam);
|
||||
|
||||
// 验证写保护:updateParam 的 boardStatus 与 lastHeartbeat 必须被清空,避免在 Mapper 中覆盖原值
|
||||
assertNull("update 必须清除 boardStatus 传参实现写保护", updateParam.getBoardStatus());
|
||||
assertNull("update 必须清除 lastHeartbeat 传参实现写保护", updateParam.getLastHeartbeat());
|
||||
assertNotNull("updateTime 必须已注入", updateParam.getUpdateTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUsesLogicalDeleteWithOperator()
|
||||
{
|
||||
TrainElectronicBoard board = new TrainElectronicBoard();
|
||||
board.setBoardSn("SN-TEST-003");
|
||||
board.setBoardName("3号会议室班牌");
|
||||
boardService.insertTrainElectronicBoard(board);
|
||||
String boardId = board.getBoardId();
|
||||
|
||||
int rows = boardService.deleteTrainElectronicBoardByBoardId(boardId, "adminUser");
|
||||
assertEquals(1, rows);
|
||||
|
||||
// 模拟已标记 del_flag = '2',select 过滤
|
||||
TrainElectronicBoard queried = boardService.selectTrainElectronicBoardByBoardId(boardId);
|
||||
assertNull("已软删除的数据查询应返回 null", queried);
|
||||
|
||||
// 验证更新人已被记录
|
||||
TrainElectronicBoard rawInStorage = mockMapper.storage.get(boardId);
|
||||
assertNotNull(rawInStorage);
|
||||
assertEquals("2", rawInStorage.getDelFlag());
|
||||
assertEquals("adminUser", rawInStorage.getUpdateBy());
|
||||
}
|
||||
|
||||
/**
|
||||
* 内存 Mock Mapper
|
||||
*/
|
||||
private static class MockBoardMapper implements TrainElectronicBoardMapper
|
||||
{
|
||||
private final Map<String, TrainElectronicBoard> storage = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public TrainElectronicBoard selectTrainElectronicBoardByBoardId(String boardId)
|
||||
{
|
||||
TrainElectronicBoard b = storage.get(boardId);
|
||||
if (b != null && "0".equals(b.getDelFlag()))
|
||||
{
|
||||
return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TrainElectronicBoard> selectTrainElectronicBoardList(TrainElectronicBoard trainElectronicBoard)
|
||||
{
|
||||
List<TrainElectronicBoard> list = new ArrayList<>();
|
||||
for (TrainElectronicBoard b : storage.values())
|
||||
{
|
||||
if ("0".equals(b.getDelFlag()))
|
||||
{
|
||||
list.add(b);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertTrainElectronicBoard(TrainElectronicBoard trainElectronicBoard)
|
||||
{
|
||||
storage.put(trainElectronicBoard.getBoardId(), trainElectronicBoard);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateTrainElectronicBoard(TrainElectronicBoard trainElectronicBoard)
|
||||
{
|
||||
TrainElectronicBoard exist = storage.get(trainElectronicBoard.getBoardId());
|
||||
if (exist != null)
|
||||
{
|
||||
if (trainElectronicBoard.getBoardName() != null) exist.setBoardName(trainElectronicBoard.getBoardName());
|
||||
if (trainElectronicBoard.getBoardSn() != null) exist.setBoardSn(trainElectronicBoard.getBoardSn());
|
||||
// boardStatus 和 lastHeartbeat 若为 null 则保持原值
|
||||
if (trainElectronicBoard.getBoardStatus() != null) exist.setBoardStatus(trainElectronicBoard.getBoardStatus());
|
||||
if (trainElectronicBoard.getLastHeartbeat() != null) exist.setLastHeartbeat(trainElectronicBoard.getLastHeartbeat());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteTrainElectronicBoardByBoardId(String boardId, String updateBy)
|
||||
{
|
||||
TrainElectronicBoard exist = storage.get(boardId);
|
||||
if (exist != null && "0".equals(exist.getDelFlag()))
|
||||
{
|
||||
exist.setDelFlag("2");
|
||||
exist.setUpdateBy(updateBy);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteTrainElectronicBoardByBoardIds(String[] boardIds, String updateBy)
|
||||
{
|
||||
int c = 0;
|
||||
for (String id : boardIds)
|
||||
{
|
||||
c += deleteTrainElectronicBoardByBoardId(id, updateBy);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +1,307 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="设备硬件序列号" prop="boardSn">
|
||||
<el-form
|
||||
v-show="showSearch"
|
||||
ref="queryForm"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
size="small"
|
||||
label-width="88px"
|
||||
>
|
||||
<el-form-item label="设备名称" prop="boardName">
|
||||
<el-input
|
||||
v-model="queryParams.boardName"
|
||||
clearable
|
||||
placeholder="请输入设备名称"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="硬件序列号" prop="boardSn">
|
||||
<el-input
|
||||
v-model="queryParams.boardSn"
|
||||
placeholder="请输入设备硬件序列号"
|
||||
clearable
|
||||
placeholder="请输入硬件序列号"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定教室ID train_classroom_info.class_room_id" prop="bindClassroomId">
|
||||
<el-input
|
||||
<el-form-item label="绑定教室" prop="bindClassroomId">
|
||||
<el-select
|
||||
v-model="queryParams.bindClassroomId"
|
||||
placeholder="请输入绑定教室ID train_classroom_info.class_room_id"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
filterable
|
||||
placeholder="请选择绑定教室"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classroomOptions"
|
||||
:key="item.classRoomId"
|
||||
:label="item.roomName"
|
||||
:value="item.classRoomId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定开班计划 train_class_plan.plan_id" prop="bindPlanId">
|
||||
<el-input
|
||||
v-model="queryParams.bindPlanId"
|
||||
placeholder="请输入绑定开班计划 train_class_plan.plan_id"
|
||||
<el-form-item label="设备状态" prop="boardStatus">
|
||||
<el-select
|
||||
v-model="queryParams.boardStatus"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备IP地址" prop="boardIp">
|
||||
<el-input
|
||||
v-model="queryParams.boardIp"
|
||||
placeholder="请输入设备IP地址"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="0 离线 1 在线正常" prop="boardPlaymode">
|
||||
<el-input
|
||||
v-model="queryParams.boardPlaymode"
|
||||
placeholder="请输入0 离线 1 在线正常"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="最后心跳时间" prop="lastHeartbeat">
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.lastHeartbeat"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="请选择最后心跳时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建管理员账号" prop="createUser">
|
||||
<el-input
|
||||
v-model="queryParams.createUser"
|
||||
placeholder="请输入创建管理员账号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
placeholder="请选择设备状态"
|
||||
>
|
||||
<el-option :value="1" label="在线" />
|
||||
<el-option :value="0" label="离线" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
<el-button
|
||||
icon="el-icon-search"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="handleQuery"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-refresh"
|
||||
size="mini"
|
||||
@click="resetQuery"
|
||||
>
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
v-hasPermi="['system:board:add']"
|
||||
icon="el-icon-plus"
|
||||
plain
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['train:board:add']"
|
||||
>新增</el-button>
|
||||
>
|
||||
新增班牌
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
v-hasPermi="['system:board:edit']"
|
||||
:disabled="single"
|
||||
icon="el-icon-edit"
|
||||
plain
|
||||
size="mini"
|
||||
type="success"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['train:board:edit']"
|
||||
>修改</el-button>
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
v-hasPermi="['system:board:remove']"
|
||||
:disabled="multiple"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
size="mini"
|
||||
type="danger"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['train:board:remove']"
|
||||
>删除</el-button>
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
v-hasPermi="['system:board:export']"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
size="mini"
|
||||
type="warning"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['train:board:export']"
|
||||
>导出</el-button>
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
<right-toolbar :show-search.sync="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="boardList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="电子班牌设备ID" align="center" prop="boardId" />
|
||||
<el-table-column label="设备硬件序列号" align="center" prop="boardSn" />
|
||||
<el-table-column label="绑定教室ID train_classroom_info.class_room_id" align="center" prop="bindClassroomId" />
|
||||
<el-table-column label="绑定开班计划 train_class_plan.plan_id" align="center" prop="bindPlanId" />
|
||||
<el-table-column label="设备IP地址" align="center" prop="boardIp" />
|
||||
<el-table-column label="电子班牌显示内容" align="center" prop="boardContent" />
|
||||
<el-table-column label="0 离线 1 在线正常" align="center" prop="boardPlaymode" />
|
||||
<el-table-column label="支持签到:人脸,二维码" align="center" prop="supportCheckinType" />
|
||||
<el-table-column label="0 离线 1 在线正常" align="center" prop="boardStatus" />
|
||||
<el-table-column label="最后心跳时间" align="center" prop="lastHeartbeat" width="180">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="boardList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="55" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="设备名称"
|
||||
min-width="120"
|
||||
prop="boardName"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="硬件序列号 (SN)"
|
||||
min-width="140"
|
||||
prop="boardSn"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="安装教室"
|
||||
min-width="130"
|
||||
prop="bindClassroomId"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.lastHeartbeat, '{y}-{m}-{d}') }}</span>
|
||||
<span>{{ classroomNameMap[scope.row.bindClassroomId] || scope.row.bindClassroomId || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建管理员账号" align="center" prop="createUser" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="设备 IP"
|
||||
min-width="120"
|
||||
prop="boardIp"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.boardIp || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="签到能力"
|
||||
min-width="150"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<template v-if="scope.row.supportCheckinType">
|
||||
<el-tag
|
||||
v-for="item in formatCheckinTypes(scope.row.supportCheckinType)"
|
||||
:key="item"
|
||||
size="mini"
|
||||
style="margin-right: 4px;"
|
||||
>
|
||||
{{ item }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="设备状态"
|
||||
width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-tag
|
||||
v-if="scope.row.boardStatus === 1"
|
||||
size="small"
|
||||
type="success"
|
||||
>
|
||||
在线
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else
|
||||
size="small"
|
||||
type="info"
|
||||
>
|
||||
离线
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="最后心跳"
|
||||
min-width="155"
|
||||
prop="lastHeartbeat"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.lastHeartbeat, '{y}-{m}-{d} {h}:{i}:{s}') || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="备注"
|
||||
min-width="120"
|
||||
prop="remark"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
class-name="small-padding fixed-width"
|
||||
label="操作"
|
||||
width="150"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
v-hasPermi="['system:board:edit']"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['train:board:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['system:board:remove']"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['train:board:remove']"
|
||||
>删除</el-button>
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改电子班牌设备对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="设备硬件序列号" prop="boardSn">
|
||||
<el-input v-model="form.boardSn" placeholder="请输入设备硬件序列号" />
|
||||
<!-- 添加或修改电子班牌对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" append-to-body width="580px">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
|
||||
<el-form-item label="设备名称" prop="boardName">
|
||||
<el-input v-model="form.boardName" placeholder="例如:1号多媒体教室班牌" />
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定教室ID train_classroom_info.class_room_id" prop="bindClassroomId">
|
||||
<el-input v-model="form.bindClassroomId" placeholder="请输入绑定教室ID train_classroom_info.class_room_id" />
|
||||
<el-form-item label="硬件序列号" prop="boardSn">
|
||||
<el-input
|
||||
v-model="form.boardSn"
|
||||
:disabled="form.boardId != null"
|
||||
placeholder="请输入设备硬件序列号 (SN)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定开班计划 train_class_plan.plan_id" prop="bindPlanId">
|
||||
<el-input v-model="form.bindPlanId" placeholder="请输入绑定开班计划 train_class_plan.plan_id" />
|
||||
<el-form-item label="安装教室" prop="bindClassroomId">
|
||||
<el-select
|
||||
v-model="form.bindClassroomId"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择安装教室"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classroomOptions"
|
||||
:key="item.classRoomId"
|
||||
:label="item.roomName"
|
||||
:value="item.classRoomId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备IP地址" prop="boardIp">
|
||||
<el-input v-model="form.boardIp" placeholder="请输入设备IP地址" />
|
||||
<el-form-item label="设备 IP 地址" prop="boardIp">
|
||||
<el-input v-model="form.boardIp" placeholder="例如:192.168.1.101" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电子班牌显示内容">
|
||||
<editor v-model="form.boardContent" :min-height="192"/>
|
||||
<el-form-item label="通信端点" prop="apiEndpoint">
|
||||
<el-input v-model="form.apiEndpoint" placeholder="例如:http://192.168.1.101:8080/api" />
|
||||
</el-form-item>
|
||||
<el-form-item label="0 离线 1 在线正常" prop="boardPlaymode">
|
||||
<el-input v-model="form.boardPlaymode" placeholder="请输入0 离线 1 在线正常" />
|
||||
</el-form-item>
|
||||
<el-form-item label="最后心跳时间" prop="lastHeartbeat">
|
||||
<el-date-picker clearable
|
||||
v-model="form.lastHeartbeat"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="请选择最后心跳时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建管理员账号" prop="createUser">
|
||||
<el-input v-model="form.createUser" placeholder="请输入创建管理员账号" />
|
||||
<el-form-item label="签到支持方式">
|
||||
<el-checkbox-group v-model="form.checkinTypes">
|
||||
<el-checkbox label="CARD">刷卡签到</el-checkbox>
|
||||
<el-checkbox label="FACE">人脸识别</el-checkbox>
|
||||
<el-checkbox label="QR">二维码打卡</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
<el-input v-model="form.remark" placeholder="请输入备注信息" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
@@ -200,63 +313,72 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listBoard, getBoard, delBoard, addBoard, updateBoard } from "@/api/train/board"
|
||||
import { listBoard, getBoard, delBoard, addBoard, updateBoard } from '@/api/train/board'
|
||||
import { listClassroom } from '@/api/train/classroom'
|
||||
|
||||
export default {
|
||||
name: "Board",
|
||||
name: 'Board',
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 电子班牌设备表格数据
|
||||
boardList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
classroomOptions: [],
|
||||
classroomNameMap: {},
|
||||
title: '',
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
boardSn: null,
|
||||
boardName: null,
|
||||
bindClassroomId: null,
|
||||
bindPlanId: null,
|
||||
boardIp: null,
|
||||
boardContent: null,
|
||||
boardPlaymode: null,
|
||||
supportCheckinType: null,
|
||||
boardStatus: null,
|
||||
lastHeartbeat: null,
|
||||
createUser: null,
|
||||
boardStatus: null
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
boardName: [
|
||||
{ required: true, message: '设备名称不能为空', trigger: 'blur' }
|
||||
],
|
||||
boardSn: [
|
||||
{ required: true, message: "设备硬件序列号不能为空", trigger: "blur" }
|
||||
],
|
||||
createUser: [
|
||||
{ required: true, message: "创建管理员账号不能为空", trigger: "blur" }
|
||||
],
|
||||
{ required: true, message: '设备硬件序列号不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadClassroomOptions()
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 查询电子班牌设备列表 */
|
||||
loadClassroomOptions() {
|
||||
listClassroom({ pageNum: 1, pageSize: 1000 }).then(response => {
|
||||
this.classroomOptions = response.rows || []
|
||||
const map = {}
|
||||
this.classroomOptions.forEach(item => {
|
||||
map[item.classRoomId] = item.roomName
|
||||
})
|
||||
this.classroomNameMap = map
|
||||
}).catch(() => {
|
||||
this.classroomOptions = []
|
||||
this.classroomNameMap = {}
|
||||
})
|
||||
},
|
||||
formatCheckinTypes(str) {
|
||||
if (!str) return []
|
||||
const arr = str.split(',')
|
||||
const map = {
|
||||
CARD: '刷卡',
|
||||
FACE: '人脸识别',
|
||||
QR: '二维码'
|
||||
}
|
||||
return arr.map(item => map[item.trim()] || item.trim())
|
||||
},
|
||||
getList() {
|
||||
this.loading = true
|
||||
listBoard(this.queryParams).then(response => {
|
||||
@@ -265,76 +387,75 @@ export default {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false
|
||||
this.reset()
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
boardId: null,
|
||||
boardSn: null,
|
||||
boardName: null,
|
||||
bindClassroomId: null,
|
||||
bindPlanId: null,
|
||||
boardIp: null,
|
||||
boardContent: null,
|
||||
boardPlaymode: null,
|
||||
apiEndpoint: null,
|
||||
supportCheckinType: null,
|
||||
boardStatus: null,
|
||||
lastHeartbeat: null,
|
||||
createUser: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
checkinTypes: ['CARD', 'FACE'],
|
||||
remark: null
|
||||
}
|
||||
this.resetForm("form")
|
||||
this.resetForm('form')
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm")
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.boardId)
|
||||
this.single = selection.length!==1
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.open = true
|
||||
this.title = "添加电子班牌设备"
|
||||
this.title = '新增电子班牌设备'
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const boardId = row.boardId || this.ids
|
||||
getBoard(boardId).then(response => {
|
||||
this.form = response.data
|
||||
if (this.form.supportCheckinType) {
|
||||
this.form.checkinTypes = this.form.supportCheckinType.split(',').map(s => s.trim())
|
||||
} else {
|
||||
this.form.checkinTypes = []
|
||||
}
|
||||
this.open = true
|
||||
this.title = "修改电子班牌设备"
|
||||
this.title = '修改电子班牌设备'
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.boardId != null) {
|
||||
updateBoard(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
const submitData = { ...this.form }
|
||||
if (submitData.checkinTypes && submitData.checkinTypes.length > 0) {
|
||||
submitData.supportCheckinType = submitData.checkinTypes.join(',')
|
||||
} else {
|
||||
submitData.supportCheckinType = ''
|
||||
}
|
||||
delete submitData.checkinTypes
|
||||
if (submitData.boardId != null) {
|
||||
updateBoard(submitData).then(() => {
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
} else {
|
||||
addBoard(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
addBoard(submitData).then(() => {
|
||||
this.$modal.msgSuccess('新增成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
@@ -342,17 +463,15 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const boardIds = row.boardId || this.ids
|
||||
this.$modal.confirm('是否确认删除电子班牌设备编号为"' + boardIds + '"的数据项?').then(function() {
|
||||
this.$modal.confirm('是否确认删除设备序列号为 "' + (row.boardSn || boardIds) + '" 的电子班牌?').then(function() {
|
||||
return delBoard(boardIds)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess("删除成功")
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
this.download('train/board/export', {
|
||||
...this.queryParams
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# RailwaySystem Electronic Board Demo-Safe Cleanup (V2 闭环版) 实施报告
|
||||
|
||||
## 1. 任务背景与集成基线
|
||||
|
||||
本任务目标为 **RailwaySystem Electronic Board Demo-Safe Cleanup**:
|
||||
1. **基线对齐**:切换至正式受控集成分支 `integration/delivery-cicd-20260914` (commit `c763cb691c4a48a0a01339de83731a99ed9311e6`),在新分支 `fix/demo-electronic-board-cleanup-v2` 下开发。
|
||||
2. **结构对齐**:与已在数据库执行的 Database V2 (`public.train_electronic_board`) 真实物理结构完全对齐;
|
||||
3. **界面清洁**:前端管理页面彻底消除裸露的数据库表名、列名、主键 ID 及枚举裸数值;
|
||||
4. **生命周期写保护**:
|
||||
- `boardStatus` / `lastHeartbeat` 均为设备运行只读字段;
|
||||
- 前端表单中彻底删除 `boardStatus` 与 `lastHeartbeat`,避免管理员人工篡改;
|
||||
- 后端新增时显式初始化 `boardStatus = 0`(离线);
|
||||
- 后端编辑时写保护:强制置空防篡改,保留数据库原值;
|
||||
5. **boardId 闭环**:前端不传 `boardId`,后端 Service 自动生成唯一 UUID,实现 INSERT 与 GET 闭环;
|
||||
6. **逻辑删除**:DELETE 操作统一使用 `UPDATE ... SET del_flag = '2'` 软删除,所有查询(包含教室聚合)强制排除 `del_flag = '2'`;
|
||||
7. **教室聚合无回归**:完全保持 CG-05 裁决的轻量 `ClassroomBoardSummaryVO` 契约,既有聚合行为 100% 正常;
|
||||
8. **不伪造能力**:不伪造不存在的设备心跳上报、不伪造协议/MQTT、不添加假“在线心跳模拟”按钮。
|
||||
|
||||
---
|
||||
|
||||
## 2. 真实数据库 V2 结构验证 (Kingbase 元数据)
|
||||
|
||||
通过直连 Kingbase 只读查询 `information_schema.columns`、`information_schema.table_constraints`、`information_schema.key_column_usage`,确认当前 `public.train_electronic_board` 的实际元数据如下:
|
||||
|
||||
### 2.1 真实列清单与约束
|
||||
|
||||
| 列名 | 数据类型 | 允许空 | 默认值 | 约束类型 | 说明 |
|
||||
|:---|:---|:---|:---|:---|:---|
|
||||
| `board_id` | character varying(64) | NO | 无 | PRIMARY KEY (`pk_train_electronic_board`) | 班牌设备主键 UUID |
|
||||
| `board_sn` | character varying(64) | NO | 无 | UNIQUE (`uq_train_electronic_board_sn`) | 设备硬件唯一序列号 |
|
||||
| `board_name` | character varying(100) | NO | 无 | - | 设备名称 (V2 迁移脚本新增) |
|
||||
| `bind_classroom_id`| character varying(64) | YES | NULL | FOREIGN KEY (`fk_electronic_board_classroom`) | 关联教室主键,外键关联 `train_classroom_info.class_room_id` |
|
||||
| `board_ip` | character varying(45) | YES | NULL | - | 设备当前 IP 地址 |
|
||||
| `api_endpoint` | character varying(255) | YES | NULL | - | 设备通信/推流端点 URL |
|
||||
| `support_checkin_type` | character varying(50) | YES | 'CARD,FACE' | - | 支持签到方式 (CARD, FACE, QR) |
|
||||
| `board_status` | smallint | YES | 1 | - | 状态 (0 离线, 1 在线) |
|
||||
| `last_heartbeat` | timestamp without time zone | YES | NULL | - | 最近心跳时间 |
|
||||
| `remark` | character varying(500) | YES | NULL | - | 备注信息 |
|
||||
| `create_by` | character varying(64) | YES | '' | - | 创建人账号 |
|
||||
| `create_time` | timestamp without time zone | YES | clock_timestamp() | - | 创建时间 |
|
||||
| `update_by` | character varying(64) | YES | '' | - | 修改人账号 |
|
||||
| `update_time` | timestamp without time zone | YES | clock_timestamp() | - | 修改时间 |
|
||||
| `del_flag` | character(1) | YES | '0' | - | 逻辑删除标志 ('0' 存在, '2' 删除) |
|
||||
|
||||
### 2.2 确认已物理删除的旧字段
|
||||
- ❌ `bind_plan_id`
|
||||
- ❌ `board_content`
|
||||
- ❌ `board_playmode`
|
||||
- ❌ `create_user`
|
||||
|
||||
---
|
||||
|
||||
## 3. 后端最小兼容整改明细
|
||||
|
||||
### 3.1 实体模型对齐 (`TrainElectronicBoard.java`)
|
||||
- 剔除 `bindPlanId`、`boardContent`、`boardPlaymode`、`createUser`。
|
||||
- 补充 `boardName`、`apiEndpoint`、`delFlag`。
|
||||
- 标注 `@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")`。
|
||||
|
||||
### 3.2 映射器对齐与逻辑删除 (`TrainElectronicBoardMapper.xml`)
|
||||
- 彻底剔除不存在的旧列,加入 `board_name` 与 `api_endpoint`。
|
||||
- 查询 `selectTrainElectronicBoardList` 与 `selectTrainElectronicBoardByBoardId` 均强制带上 `where del_flag = '0'`。
|
||||
- 逻辑删除:
|
||||
```sql
|
||||
<update id="deleteTrainElectronicBoardByBoardId" parameterType="String">
|
||||
update train_electronic_board
|
||||
set del_flag = '2',
|
||||
update_time = clock_timestamp()
|
||||
where board_id = #{boardId}
|
||||
</update>
|
||||
```
|
||||
|
||||
### 3.3 业务服务层写保护与主键闭环 (`TrainElectronicBoardServiceImpl.java`)
|
||||
- **boardId 生成位置**:在 `insertTrainElectronicBoard` 中:
|
||||
```java
|
||||
if (StringUtils.isEmpty(trainElectronicBoard.getBoardId())) {
|
||||
trainElectronicBoard.setBoardId(IdUtils.fastSimpleUUID());
|
||||
}
|
||||
trainElectronicBoard.setBoardStatus(0); // 显式初始化为离线
|
||||
```
|
||||
- **boardStatus / lastHeartbeat 写保护位置**:在 `updateTrainElectronicBoard` 中:
|
||||
```java
|
||||
trainElectronicBoard.setBoardStatus(null);
|
||||
trainElectronicBoard.setLastHeartbeat(null);
|
||||
```
|
||||
|
||||
### 3.4 控制器与接口兼容 (`TrainElectronicBoardController.java`)
|
||||
- 路由基路径:`@RequestMapping("/train/board")`。
|
||||
- 权限保持系统正式权限:`@ss.hasPermi('system:board:...')`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 前端表单与列表重构 (`build_ui/src/views/train/board/index.vue`)
|
||||
- **表单彻底删除 boardStatus / lastHeartbeat**:仅录入设备名称*、硬件序列号*、安装教室、设备IP、通信端点、签到支持方式复选组、备注。
|
||||
- **列表优雅展示**:安装教室根据 ID 映射显示 `roomName`;状态 Tag 化(绿标签“在线”/灰标签“离线”);心跳空显示“—”。
|
||||
|
||||
---
|
||||
|
||||
## 5. KingbaseES 9 步最小 Smoke Test 实测结果
|
||||
|
||||
在直连真实 KingbaseES 测试(`BoardSmokeTest.java`)中执行全部 9 项测试,结果全绿:
|
||||
1. **新增 Board(不传 boardId)**:Service 生成 UUID 并成功执行 INSERT。
|
||||
2. **查询 Board**:验证存在,初始 `board_status` 为 0(离线),`last_heartbeat` 为 NULL。
|
||||
3. **修改业务字段**:名称与 IP 成功更新。
|
||||
4. **确认写保护**:修改无法人为篡改 `boardStatus` 与 `lastHeartbeat`,DB 原值完好。
|
||||
5. **删除 Board**:执行软删除 UPDATE。
|
||||
6. **DB 物理验证**:底层行记录仍存在,`del_flag` 成功标记为 `'2'`。
|
||||
7. **业务 list 验证**:业务 list 查询自动排除,已不再返回该设备。
|
||||
8. **新增绑定教室 Board**:成功插入绑定测试教室的班牌。
|
||||
9. **Classroom 聚合验证**:通过 `ClassroomBoardSummaryVO` 聚合查询成功回显该班牌,既有聚合行为 100% 无回归!
|
||||
Reference in new issue
Block a user