调整待办分页接口

This commit is contained in:
黎润豪 2026-03-10 17:39:06 +08:00
parent a682dd6f59
commit 272443062c
9 changed files with 196 additions and 72 deletions

View File

@ -4,8 +4,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.aiccs.aiccs.abnormal.entity.EPriPerson;
import com.chinaweal.aiccs.aiccs.abnormal.service.EPriPersonService;
import com.chinaweal.aiccs.aiccs.abnormal.entity.TSWrit;
import com.chinaweal.aiccs.aiccs.abnormal.service.EPriPersonService;
import com.chinaweal.aiccs.aiccs.abnormal.service.TSWritService;
import com.chinaweal.aiccs.aiccs.auditing.entity.TSOpinion;
import com.chinaweal.aiccs.aiccs.auditing.service.TSOpinionService;
@ -20,15 +20,16 @@ import com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetail;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetailTemp;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.dto.*;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.vo.ApprovalPageVo;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.vo.PrintTSWritVo;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.vo.TaskDetailVo;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.vo.TaskSubmitResultVo;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.vo.PrintTSWritVo;
import com.chinaweal.aiccs.aiccs.seriousillegal.service.IEOtIlldisdetailService;
import com.chinaweal.aiccs.aiccs.seriousillegal.service.IEOtIlldisdetailTempService;
import com.chinaweal.aiccs.aiccs.system.entity.TRBaseCode;
import com.chinaweal.aiccs.aiccs.system.service.TRBaseCodeService;
import com.chinaweal.aiccs.common.base.controller.BaseController;
import com.chinaweal.aiccs.common.constant.BaseDataConstant;
import com.chinaweal.aiccs.common.constant.CommonConstants;
import com.chinaweal.aiccs.common.util.FlowUtil;
import com.chinaweal.aiccs.common.util.StringUtils;
import com.chinaweal.aiccs.common.util.WorkFlowUtil;
@ -49,7 +50,6 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -413,18 +413,17 @@ public class EOtIlldisdetailController extends BaseController {
@ApiOperation("查询外单位严重违法列入待办列表")
@PostMapping("/enterTaskList")
public RestResult<IPage<TSTaskList>> enterTaskList(HttpServletRequest request,
@RequestBody BaseQuery<?> baseQuery) {
@RequestBody BaseQuery<TaskListTodoParam> baseQuery) {
AICUser user = getLoginUser(request);
if (user == null) {
return RestResult.error(ResultCode.USER_NOT_LOGGED_IN);
}
TSTaskList taskListModel = new TSTaskList();
taskListModel.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_ENTER);
taskListModel.setSignuserid(user.getPrimaryKey());
TaskListTodoParam param = baseQuery.getEntity(TaskListTodoParam.class);
param.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_ENTER);
param.setCurrentUserId(user.getPrimaryKey());
IPage<TSTaskList> result = taskListService.searchTstaskByPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()),
taskListModel, "0", user.getRegionID());
IPage<TSTaskList> result = eOtIlldisdetailTempService.getTaskListTodoPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()), param);
return RestResult.ok(result);
}
@ -434,22 +433,18 @@ public class EOtIlldisdetailController extends BaseController {
*/
@ApiOperation("查询外单位严重违法列入已办记录")
@PostMapping("/enterFinishedList")
public RestResult<IPage<TSTaskList>> enterFinishedList(HttpServletRequest request,
@RequestBody BaseQuery<?> baseQuery) {
public RestResult<IPage<EOtIlldisdetailTemp>> enterFinishedList(HttpServletRequest request,
@RequestBody BaseQuery<TaskListTodoParam> baseQuery) {
AICUser user = getLoginUser(request);
if (user == null) {
return RestResult.error(ResultCode.USER_NOT_LOGGED_IN);
}
TSTaskList taskListModel = new TSTaskList();
taskListModel.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_ENTER);
taskListModel.setSignuserid(user.getPrimaryKey());
TaskListTodoParam param = baseQuery.getEntity(TaskListTodoParam.class);
param.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_ENTER);
param.setCurrentUserId(user.getPrimaryKey());
IPage<TSTaskList> result = taskListService.searchTstaskByPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()),
taskListModel, "1", user.getRegionID());
return RestResult.ok(result);
return RestResult.ok(eOtIlldisdetailTempService.getTodoHistoryPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()), param));
}
/**
@ -458,18 +453,17 @@ public class EOtIlldisdetailController extends BaseController {
@ApiOperation("查询外单位严重违法移出待办列表")
@PostMapping("/removeTaskList")
public RestResult<IPage<TSTaskList>> removeTaskList(HttpServletRequest request,
@RequestBody BaseQuery<?> baseQuery) {
@RequestBody BaseQuery<TaskListTodoParam> baseQuery) {
AICUser user = getLoginUser(request);
if (user == null) {
return RestResult.error(ResultCode.USER_NOT_LOGGED_IN);
}
TSTaskList taskListModel = new TSTaskList();
taskListModel.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_REMOVE);
taskListModel.setSignuserid(user.getPrimaryKey());
TaskListTodoParam param = baseQuery.getEntity(TaskListTodoParam.class);
param.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_REMOVE);
param.setCurrentUserId(user.getPrimaryKey());
IPage<TSTaskList> result = taskListService.searchTstaskByPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()),
taskListModel, "0", user.getRegionID());
IPage<TSTaskList> result = eOtIlldisdetailTempService.getTaskListTodoPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()), param);
return RestResult.ok(result);
}
@ -479,22 +473,18 @@ public class EOtIlldisdetailController extends BaseController {
*/
@ApiOperation("查询外单位严重违法移出已办记录")
@PostMapping("/removeFinishedList")
public RestResult<IPage<TSTaskList>> removeFinishedList(HttpServletRequest request,
@RequestBody BaseQuery<?> baseQuery) {
public RestResult<IPage<EOtIlldisdetailTemp>> removeFinishedList(HttpServletRequest request,
@RequestBody BaseQuery<TaskListTodoParam> baseQuery) {
AICUser user = getLoginUser(request);
if (user == null) {
return RestResult.error(ResultCode.USER_NOT_LOGGED_IN);
}
TSTaskList taskListModel = new TSTaskList();
taskListModel.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_REMOVE);
taskListModel.setSignuserid(user.getPrimaryKey());
TaskListTodoParam param = baseQuery.getEntity(TaskListTodoParam.class);
param.setBustype(BaseDataConstant.SERILLEGAL_TYPE_OT_REMOVE);
param.setCurrentUserId(user.getPrimaryKey());
IPage<TSTaskList> result = taskListService.searchTstaskByPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()),
taskListModel, "1", user.getRegionID());
return RestResult.ok(result);
return RestResult.ok(eOtIlldisdetailTempService.getTodoHistoryPage(new Page<>(baseQuery.getCurrent(), baseQuery.getSize()), param));
}
/**
@ -573,12 +563,12 @@ public class EOtIlldisdetailController extends BaseController {
throw new BusinessException("待办记录不存在");
}
// 查询业务数据
List<EOtIlldisdetailTemp> bizList = eOtIlldisdetailTempService.listByBizSeq(taskListModel.getBizseqid());
TaskDetailVo result = new TaskDetailVo();
result.setTaskList(taskListModel);
result.setBizList(bizList);
result.setBizInfo(eOtIlldisdetailTempService.lambdaQuery()
.eq(StringUtils.equals(taskListModel.getBustype(), BaseDataConstant.SERILLEGAL_TYPE_OT_ENTER), EOtIlldisdetailTemp::getInBizseq, taskListModel.getBizseqid())
.eq(StringUtils.equals(taskListModel.getBustype(), BaseDataConstant.SERILLEGAL_TYPE_OT_REMOVE), EOtIlldisdetailTemp::getOutBizseq, taskListModel.getBizseqid())
.ne(EOtIlldisdetailTemp::getDstate, CommonConstants.DELETED_2).one());
return RestResult.ok(result);
}

View File

@ -0,0 +1,32 @@
package com.chinaweal.aiccs.aiccs.seriousillegal.entity.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 待办查询参数
* @author lroyia
* @since 2026/3/10 16:44
**/
@Data
public class TaskListTodoParam implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 业务类型
*/
private String bustype;
/**
* 业务类型多个
*/
private List<String> bustypeIn;
/**
* 当前用户id
*/
private String currentUserId;
}

View File

@ -21,6 +21,6 @@ public class TaskDetailVo {
@ApiModelProperty("待办信息")
private TSTaskList taskList;
@ApiModelProperty("业务数据列表")
private List<EOtIlldisdetailTemp> bizList;
@ApiModelProperty("业务数据信息")
private EOtIlldisdetailTemp bizInfo;
}

View File

@ -1,7 +1,11 @@
package com.chinaweal.aiccs.aiccs.seriousillegal.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chinaweal.aiccs.aiccs.business.entity.TSTaskList;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetailTemp;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.dto.TaskListTodoParam;
import org.apache.ibatis.annotations.Param;
/**
* <p>
@ -13,4 +17,21 @@ import com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetailTemp;
*/
public interface EOtIlldisdetailTempMapper extends BaseMapper<EOtIlldisdetailTemp> {
/**
* 查询待办分页
*
* @param page 分页参数
* @param param 查询参数
* @return 分页结果
*/
IPage<TSTaskList> selectTaskListTodoPage(IPage<TSTaskList> page, @Param("param") TaskListTodoParam param);
/**
* 查询待办历史记录
*
* @param page 分页大小
* @param param 查询参数
* @return 分页结果
*/
IPage<EOtIlldisdetailTemp> selectTodoHistoryPage(IPage<EOtIlldisdetailTemp> page, @Param("param") TaskListTodoParam param);
}

View File

@ -1,9 +1,11 @@
package com.chinaweal.aiccs.aiccs.seriousillegal.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.chinaweal.aiccs.aiccs.auditing.entity.TSOpinion;
import com.chinaweal.aiccs.aiccs.business.entity.TSTaskList;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetailTemp;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.dto.TaskListTodoParam;
import com.chinaweal.aicorg.model.AICUser;
import java.util.List;
@ -20,6 +22,7 @@ public interface IEOtIlldisdetailTempService extends IService<EOtIlldisdetailTem
/**
* 根据业务号查询Temp表记录
*
* @param bizSeq 业务号
* @return Temp表记录列表
*/
@ -27,20 +30,22 @@ public interface IEOtIlldisdetailTempService extends IService<EOtIlldisdetailTem
/**
* 根据工作流ID查询Temp表记录
*
* @param workflowId 工作流ID
* @param isEnter 是否是列入业务true-列入false-移出
* @param isEnter 是否是列入业务true-列入false-移出
* @return Temp表记录列表
*/
List<EOtIlldisdetailTemp> listByWorkflowId(String workflowId, boolean isEnter);
/**
* 处理列入业务审批流程
* @param user 当前用户
* @param taskListModel 待办信息
* @param tempRecords 业务记录列表
* @param opinionModel 审批意见
* @param nextNodeID 下一节点ID
* @param nextPerformerids 下一处理人ID数组
*
* @param user 当前用户
* @param taskListModel 待办信息
* @param tempRecords 业务记录列表
* @param opinionModel 审批意见
* @param nextNodeID 下一节点ID
* @param nextPerformerids 下一处理人ID数组
* @param nextPerformerNames 下一处理人姓名数组
* @return 待办信息
*/
@ -50,12 +55,13 @@ public interface IEOtIlldisdetailTempService extends IService<EOtIlldisdetailTem
/**
* 处理移出业务审批流程
* @param user 当前用户
* @param taskListModel 待办信息
* @param tempRecords 业务记录列表
* @param opinionModel 审批意见
* @param nextNodeID 下一节点ID
* @param nextPerformerids 下一处理人ID数组
*
* @param user 当前用户
* @param taskListModel 待办信息
* @param tempRecords 业务记录列表
* @param opinionModel 审批意见
* @param nextNodeID 下一节点ID
* @param nextPerformerids 下一处理人ID数组
* @param nextPerformerNames 下一处理人姓名数组
* @return 待办信息
*/
@ -63,4 +69,21 @@ public interface IEOtIlldisdetailTempService extends IService<EOtIlldisdetailTem
List<EOtIlldisdetailTemp> tempRecords, TSOpinion opinionModel,
String nextNodeID, String[] nextPerformerids, String[] nextPerformerNames);
/**
* 待办查询
*
* @param page 分页
* @param param 查询参数
* @return 查询结果
*/
IPage<TSTaskList> getTaskListTodoPage(IPage<TSTaskList> page, TaskListTodoParam param);
/**
* 查询待办历史记录
*
* @param page 分页大小
* @param param 查询参数
* @return 分页结果
*/
IPage<EOtIlldisdetailTemp> getTodoHistoryPage(IPage<EOtIlldisdetailTemp> page, TaskListTodoParam param);
}

View File

@ -1,6 +1,5 @@
package com.chinaweal.aiccs.aiccs.seriousillegal.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaweal.aiccs.aiccs.business.entity.TSTaskList;
import com.chinaweal.aiccs.aiccs.business.service.TSTaskListService;
@ -10,7 +9,6 @@ import com.chinaweal.aiccs.aiccs.seriousillegal.entity.dto.RemoveAcceptDto;
import com.chinaweal.aiccs.aiccs.seriousillegal.mapper.EOtIlldisdetailMapper;
import com.chinaweal.aiccs.aiccs.seriousillegal.service.IEOtIlldisdetailService;
import com.chinaweal.aiccs.aiccs.seriousillegal.service.IEOtIlldisdetailTempService;
import com.chinaweal.aiccs.common.constant.BaseDataConstant;
import com.chinaweal.aiccs.common.util.FlowUtil;
import com.chinaweal.aiccs.common.util.HandleWorkFlowUtil;
import com.chinaweal.aicorg.model.AICUser;
@ -93,16 +91,6 @@ public class EOtIlldisdetailServiceImpl extends ServiceImpl<EOtIlldisdetailMappe
throw new IllegalArgumentException("业务记录列表不能为空");
}
// 保存到Temp表
for (EOtIlldisdetailTemp tempRecord : bizList) {
if (tempRecord.getIllid() == null) {
tempRecord.setIllid(java.util.UUID.randomUUID().toString().replace("-", ""));
}
tempRecord.setDstate("0"); // 0插入
tempRecord.setInBizseq(taskListModel.getBizseqid());
eOtIlldisdetailTempService.saveOrUpdate(tempRecord);
}
// 设置业务名称
StringBuilder entName = new StringBuilder();
for (int i = 0; i < bizList.size(); i++) {
@ -117,10 +105,22 @@ public class EOtIlldisdetailServiceImpl extends ServiceImpl<EOtIlldisdetailMappe
// 创建待办并启动工作流
if (taskListModel.getTasklistid() == null) {
taskListModel.setAreaCode(aicUser.getRegionID());
taskListModel = HandleWorkFlowUtil.createTaskList(taskListModel, aicUser);
taskListService.startWorkFlow(taskListModel, aicUser, FlowUtil.SERIOUS_ILLEGAL_WORKFLOW);
}
// 保存到Temp表
for (EOtIlldisdetailTemp tempRecord : bizList) {
if (tempRecord.getIllid() == null) {
tempRecord.setIllid(java.util.UUID.randomUUID().toString().replace("-", ""));
}
tempRecord.setDstate("0"); // 0插入
tempRecord.setInWorkflowId(taskListModel.getWorkflowid());
tempRecord.setInBizseq(taskListModel.getBizseqid());
eOtIlldisdetailTempService.saveOrUpdate(tempRecord);
}
return taskListModel;
}
@ -162,13 +162,8 @@ public class EOtIlldisdetailServiceImpl extends ServiceImpl<EOtIlldisdetailMappe
// 设置业务状态
tempRecord.setDstate("1"); // 1更新
tempRecord.setOutBizseq(taskListModel.getBizseqid());
bizList.add(tempRecord);
}
// 保存到Temp表
for (EOtIlldisdetailTemp tempRecord : bizList) {
eOtIlldisdetailTempService.saveOrUpdate(tempRecord);
bizList.add(tempRecord);
}
// 设置业务名称
@ -185,10 +180,18 @@ public class EOtIlldisdetailServiceImpl extends ServiceImpl<EOtIlldisdetailMappe
// 创建待办并启动工作流
if (taskListModel.getTasklistid() == null) {
taskListModel.setAreaCode(aicUser.getRegionID());
taskListModel = HandleWorkFlowUtil.createTaskList(taskListModel, aicUser);
taskListService.startWorkFlow(taskListModel, aicUser, FlowUtil.SERIOUS_ILLEGAL_WORKFLOW);
}
// 保存到Temp表
for (EOtIlldisdetailTemp tempRecord : bizList) {
tempRecord.setOutWorkflowId(taskListModel.getWorkflowid());
eOtIlldisdetailTempService.saveOrUpdate(tempRecord);
}
return taskListModel;
}
@ -223,6 +226,7 @@ public class EOtIlldisdetailServiceImpl extends ServiceImpl<EOtIlldisdetailMappe
// 创建待办并启动工作流
if (taskListModel.getTasklistid() == null) {
taskListModel.setAreaCode(aicUser.getRegionID());
taskListModel = HandleWorkFlowUtil.createTaskList(taskListModel, aicUser);
taskListService.startWorkFlow(taskListModel, aicUser, FlowUtil.SERIOUS_ILLEGAL_WORKFLOW);
}

View File

@ -2,6 +2,7 @@ package com.chinaweal.aiccs.aiccs.seriousillegal.service.impl;
import com.alibaba.excel.util.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaweal.aiccs.aiccs.abnormal.entity.TSWrit;
import com.chinaweal.aiccs.aiccs.abnormal.service.TSWritService;
@ -13,6 +14,7 @@ import com.chinaweal.aiccs.aiccs.business.service.WorkFlowService;
import com.chinaweal.aiccs.aiccs.inspect.service.TSWorkNoService;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetail;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetailTemp;
import com.chinaweal.aiccs.aiccs.seriousillegal.entity.dto.TaskListTodoParam;
import com.chinaweal.aiccs.aiccs.seriousillegal.mapper.EOtIlldisdetailTempMapper;
import com.chinaweal.aiccs.aiccs.seriousillegal.service.IEOtIlldisdetailService;
import com.chinaweal.aiccs.aiccs.seriousillegal.service.IEOtIlldisdetailTempService;
@ -227,4 +229,14 @@ public class EOtIlldisdetailTempServiceImpl extends ServiceImpl<EOtIlldisdetailT
throw new BusinessException("处理审批流程失败:" + e.getMessage());
}
}
@Override
public IPage<TSTaskList> getTaskListTodoPage(IPage<TSTaskList> page, TaskListTodoParam param) {
return baseMapper.selectTaskListTodoPage(page, param);
}
@Override
public IPage<EOtIlldisdetailTemp> getTodoHistoryPage(IPage<EOtIlldisdetailTemp> page, TaskListTodoParam param) {
return baseMapper.selectTodoHistoryPage(page, param);
}
}

View File

@ -207,6 +207,9 @@
and v.bustype = #{tsTaskListModel.bustype}
and exists(select 1 from TSAfterAuction where bizseqid = v.BizSeqID)
</if>
<if test='tsTaskListModel.bustype == "80" or tsTaskListModel.bustype == "81" '> <!--外单位列严移严-->
and v.bustype = #{tsTaskListModel.bustype}
</if>
</if>
<if test="flag != null and flag != ''">
<if test="flag=='abnormal'">

View File

@ -2,4 +2,43 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chinaweal.aiccs.aiccs.seriousillegal.mapper.EOtIlldisdetailTempMapper">
<select id="selectTaskListTodoPage" resultType="com.chinaweal.aiccs.aiccs.business.entity.TSTaskList">
select ta.*
from tstasklist ta LEFT JOIN TSTwfProcessNode n on ta.WorkflowID = n.processid
where n.status = 0 and ta.BUSSTATUS in ('0','-1')
<if test="param != null">
<if test="param.bustype != null and param.bustype != ''">
and ta.bustype = #{param.bustype}
</if>
<if test="param.bustypeIn != null and !bustypeIn.isEmpty()">
and ta.bustype in
<foreach collection="param.bustypeIn" open="(" close=")" item="each" separator=",">
#{each}
</foreach>
</if>
<if test="param.currentUserId != null and param.currentUserId != ''">
and n.CURRUSERID = #{param.currentUserId}
</if>
</if>
ORDER BY ta.ISDEADLINE desc, ta.LAUPTIME DESC
</select>
<select id="selectTodoHistoryPage"
resultType="com.chinaweal.aiccs.aiccs.seriousillegal.entity.EOtIlldisdetailTemp">
select ot.*
from E_OT_ILLDISDETAIL_TEMP ot
<if test='param.bustype eq "80"'>
left join tstasklist ta on ot.IN_WORKFLOW_ID = ta.WORKFLOWID
</if>
<if test='param.bustype eq "81"'>
left join tstasklist ta on ot.OUT_WORKFLOW_ID = ts.WORKFLOWID
</if>
<where>
<if test="param.currentUserId != null and param.currentUserId != ''">
and exists(select 1 from TSTWFPROCESSNODE twf
where ta.WORKFLOWID = twf.PROCESSID
and twf.CURRUSERID = #{param.currentUserId})
</if>
</where>
ORDER BY ta.LAUPTIME DESC
</select>
</mapper>