feat(ops): 新增安保工单核心服务与派单策略

包含安保工单 CRUD(创建/确认/完单)、区域人员绑定服务、
区域分配策略 SecurityAreaAssignStrategy、调度策略
SecurityScheduleStrategy,以及安保扩展查询处理器。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-03-11 17:33:09 +08:00
parent d53d1c4584
commit 784c2ed387
9 changed files with 730 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
package com.viewsh.module.ops.security.service.area;
import com.viewsh.module.ops.security.dal.dataobject.area.OpsAreaSecurityUserDO;
import java.util.List;
/**
* 区域-安保人员绑定服务
*
* @author lzh
*/
public interface OpsAreaSecurityUserService {
/**
* 查询区域绑定的启用安保人员
*
* @param areaId 区域ID
* @return 安保人员列表
*/
List<OpsAreaSecurityUserDO> listByAreaId(Long areaId);
/**
* 绑定安保人员到区域
*
* @param areaId 区域ID
* @param userId 用户ID
* @param userName 用户姓名
* @param teamId 班组ID
* @param sort 排序值
* @return 绑定记录ID
*/
Long bindUser(Long areaId, Long userId, String userName, Long teamId, Integer sort);
/**
* 更新绑定信息
*
* @param id 绑定记录ID
* @param enabled 是否启用
* @param sort 排序值
* @param teamId 班组ID
*/
void updateBinding(Long id, Boolean enabled, Integer sort, Long teamId);
/**
* 解除绑定
*
* @param id 绑定记录ID
*/
void unbindUser(Long id);
}

View File

@@ -0,0 +1,94 @@
package com.viewsh.module.ops.security.service.area;
import com.viewsh.module.ops.security.dal.dataobject.area.OpsAreaSecurityUserDO;
import com.viewsh.module.ops.security.dal.mysql.area.OpsAreaSecurityUserMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.viewsh.module.ops.enums.ErrorCodeConstants.*;
/**
* 区域-安保人员绑定服务实现
*
* @author lzh
*/
@Slf4j
@Service
public class OpsAreaSecurityUserServiceImpl implements OpsAreaSecurityUserService {
@Resource
private OpsAreaSecurityUserMapper areaSecurityUserMapper;
@Override
public List<OpsAreaSecurityUserDO> listByAreaId(Long areaId) {
return areaSecurityUserMapper.selectListByAreaId(areaId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long bindUser(Long areaId, Long userId, String userName, Long teamId, Integer sort) {
// 唯一性校验
OpsAreaSecurityUserDO existing = areaSecurityUserMapper.selectByAreaIdAndUserId(areaId, userId);
if (existing != null) {
throw exception(SECURITY_AREA_USER_DUPLICATE);
}
OpsAreaSecurityUserDO record = OpsAreaSecurityUserDO.builder()
.areaId(areaId)
.userId(userId)
.userName(userName)
.teamId(teamId)
.enabled(true)
.sort(sort != null ? sort : 0)
.build();
try {
areaSecurityUserMapper.insert(record);
} catch (DuplicateKeyException e) {
throw exception(SECURITY_AREA_USER_DUPLICATE);
}
log.info("绑定安保人员到区域: areaId={}, userId={}, userName={}", areaId, userId, userName);
return record.getId();
}
@Override
public void updateBinding(Long id, Boolean enabled, Integer sort, Long teamId) {
OpsAreaSecurityUserDO existing = areaSecurityUserMapper.selectById(id);
if (existing == null) {
throw exception(SECURITY_AREA_USER_NOT_FOUND);
}
OpsAreaSecurityUserDO update = new OpsAreaSecurityUserDO();
update.setId(id);
if (enabled != null) {
update.setEnabled(enabled);
}
if (sort != null) {
update.setSort(sort);
}
if (teamId != null) {
update.setTeamId(teamId);
}
areaSecurityUserMapper.updateById(update);
log.info("更新安保人员绑定: id={}, enabled={}, sort={}", id, enabled, sort);
}
@Override
public void unbindUser(Long id) {
OpsAreaSecurityUserDO existing = areaSecurityUserMapper.selectById(id);
if (existing == null) {
throw exception(SECURITY_AREA_USER_NOT_FOUND);
}
areaSecurityUserMapper.deleteById(id);
log.info("解除安保人员绑定: id={}, areaId={}, userId={}", id, existing.getAreaId(), existing.getUserId());
}
}

View File

@@ -0,0 +1,76 @@
package com.viewsh.module.ops.security.service.dispatch;
import cn.hutool.core.collection.CollUtil;
import com.viewsh.module.ops.core.dispatch.DispatchEngine;
import com.viewsh.module.ops.core.dispatch.model.AssigneeRecommendation;
import com.viewsh.module.ops.core.dispatch.model.OrderDispatchContext;
import com.viewsh.module.ops.core.dispatch.strategy.AssignStrategy;
import com.viewsh.module.ops.enums.WorkOrderTypeEnum;
import com.viewsh.module.ops.security.dal.dataobject.area.OpsAreaSecurityUserDO;
import com.viewsh.module.ops.security.dal.mysql.area.OpsAreaSecurityUserMapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 安保工单区域分配策略
* <p>
* 根据区域绑定的安保人员随机分配。
*
* @author lzh
*/
@Slf4j
@Component
public class SecurityAreaAssignStrategy implements AssignStrategy {
private static final String STRATEGY_NAME = "security_area_user";
private static final String BUSINESS_TYPE = WorkOrderTypeEnum.SECURITY.getType();
@Resource
private DispatchEngine dispatchEngine;
@Resource
private OpsAreaSecurityUserMapper areaSecurityUserMapper;
@PostConstruct
public void init() {
dispatchEngine.registerAssignStrategy(BUSINESS_TYPE, this);
log.info("注册安保分配策略: {}", STRATEGY_NAME);
}
@Override
public String getName() {
return STRATEGY_NAME;
}
@Override
public String getSupportedBusinessType() {
return BUSINESS_TYPE;
}
@Override
public AssigneeRecommendation recommend(OrderDispatchContext context) {
Long areaId = context.getAreaId();
if (areaId == null) {
log.warn("安保派单缺少区域ID: orderId={}", context.getOrderId());
return AssigneeRecommendation.none();
}
List<OpsAreaSecurityUserDO> users = areaSecurityUserMapper.selectListByAreaId(areaId);
if (CollUtil.isEmpty(users)) {
log.info("区域 {} 无绑定安保人员,工单 {} 等待手动分配", areaId, context.getOrderId());
return AssigneeRecommendation.none();
}
// 选择 sort 值最小的人员sort 越小优先级越高,由 Mapper 已按 sort ASC 排序)
OpsAreaSecurityUserDO chosen = users.get(0);
AssigneeRecommendation recommendation = AssigneeRecommendation.of(
chosen.getUserId(), chosen.getUserName(), 50, "区域排序优先分配");
recommendation.setAreaId(areaId);
return recommendation;
}
}

View File

@@ -0,0 +1,59 @@
package com.viewsh.module.ops.security.service.dispatch;
import com.viewsh.module.ops.core.dispatch.DispatchEngine;
import com.viewsh.module.ops.core.dispatch.model.DispatchDecision;
import com.viewsh.module.ops.core.dispatch.model.DispatchPath;
import com.viewsh.module.ops.core.dispatch.model.OrderDispatchContext;
import com.viewsh.module.ops.core.dispatch.strategy.ScheduleStrategy;
import com.viewsh.module.ops.enums.WorkOrderTypeEnum;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 安保工单调度策略
* <p>
* 安保工单调度相对简单:
* - 有可用人员 → 直接派单
* - 人员忙碌 → 入队等待
*
* @author lzh
*/
@Slf4j
@Component
public class SecurityScheduleStrategy implements ScheduleStrategy {
private static final String STRATEGY_NAME = "security_schedule";
private static final String BUSINESS_TYPE = WorkOrderTypeEnum.SECURITY.getType();
@Resource
private DispatchEngine dispatchEngine;
@PostConstruct
public void init() {
dispatchEngine.registerScheduleStrategy(BUSINESS_TYPE, this);
log.info("注册安保调度策略: {}", STRATEGY_NAME);
}
@Override
public String getName() {
return STRATEGY_NAME;
}
@Override
public String getSupportedBusinessType() {
return BUSINESS_TYPE;
}
@Override
public DispatchDecision decide(OrderDispatchContext context) {
// 安保工单默认直接派单
// DispatchEngine 会根据执行人状态自动选择路径
return DispatchDecision.builder()
.path(DispatchPath.DIRECT_DISPATCH)
.reason("安保工单直接派单")
.build();
}
}

View File

@@ -0,0 +1,37 @@
package com.viewsh.module.ops.security.service.securityorder;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 安保工单人工完单请求 DTOService 层内部使用)
*
* @author lzh
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SecurityOrderCompleteReqDTO {
@NotNull(message = "工单ID不能为空")
private Long orderId;
@NotBlank(message = "处理结果不能为空")
private String result;
private List<String> resultImgUrls;
/**
* 操作人ID由 Controller 层填充)
*/
@NotNull(message = "操作人ID不能为空")
private Long operatorId;
}

View File

@@ -0,0 +1,47 @@
package com.viewsh.module.ops.security.service.securityorder;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 安保工单创建请求 DTOService 层内部使用)
*
* @author lzh
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SecurityOrderCreateReqDTO {
@NotBlank(message = "工单标题不能为空")
private String title;
private String description;
private Integer priority;
@NotNull(message = "区域ID不能为空")
private Long areaId;
private String location;
// ==================== 告警来源 ====================
private String alarmId;
private String alarmType;
private String cameraId;
private String roiId;
private String imageUrl;
private String sourceType;
}

View File

@@ -0,0 +1,102 @@
package com.viewsh.module.ops.security.service.securityorder;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO;
import com.viewsh.module.ops.security.dal.mysql.workorder.OpsOrderSecurityExtMapper;
import com.viewsh.module.ops.service.OrderDetailVO;
import com.viewsh.module.ops.service.OrderExtQueryHandler;
import com.viewsh.module.ops.service.OrderSummaryVO;
import com.viewsh.module.ops.enums.WorkOrderTypeEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 安保工单扩展查询处理器
* <p>
* 实现 OrderExtQueryHandler 接口,为工单中心查询提供安保扩展信息加载能力
*
* @author lzh
*/
@Slf4j
@Component
public class SecurityOrderExtQueryHandler implements OrderExtQueryHandler {
@Resource
private OpsOrderSecurityExtMapper securityExtMapper;
private static final String ORDER_TYPE_SECURITY = WorkOrderTypeEnum.SECURITY.getType();
@Override
public boolean supports(String orderType) {
return ORDER_TYPE_SECURITY.equals(orderType);
}
@Override
public void enrichWithExtInfo(OrderSummaryVO vo, Long orderId) {
OpsOrderSecurityExtDO securityExt = securityExtMapper.selectByOpsOrderId(orderId);
if (securityExt != null) {
vo.setExtInfo(buildExtInfoMap(securityExt));
}
}
@Override
public OrderDetailVO buildDetailVO(OpsOrderDO order) {
OpsOrderSecurityExtDO securityExt = securityExtMapper.selectByOpsOrderId(order.getId());
OrderDetailVO vo = OrderDetailVO.builder()
.id(order.getId())
.orderCode(order.getOrderCode())
.orderType(order.getOrderType())
.sourceType(order.getSourceType())
.title(order.getTitle())
.description(order.getDescription())
.priority(order.getPriority())
.status(order.getStatus())
.areaId(order.getAreaId())
.location(order.getLocation())
.urgentReason(order.getUrgentReason())
.assigneeId(order.getAssigneeId())
.assigneeName(order.getAssigneeName())
.inspectorId(order.getInspectorId())
.startTime(order.getStartTime())
.endTime(order.getEndTime())
.qualityScore(order.getQualityScore())
.qualityComment(order.getQualityComment())
.responseSeconds(order.getResponseSeconds())
.completionSeconds(order.getCompletionSeconds())
.createTime(order.getCreateTime())
.updateTime(order.getUpdateTime())
.build();
if (securityExt != null) {
vo.setExtInfo(buildExtInfoMap(securityExt));
}
return vo;
}
/**
* 构建安保工单扩展信息 Map列表/详情统一使用)
*/
private Map<String, Object> buildExtInfoMap(OpsOrderSecurityExtDO ext) {
Map<String, Object> extInfo = new LinkedHashMap<>(16);
extInfo.put("alarmId", ext.getAlarmId());
extInfo.put("alarmType", ext.getAlarmType());
extInfo.put("cameraId", ext.getCameraId());
extInfo.put("roiId", ext.getRoiId());
extInfo.put("imageUrl", ext.getImageUrl());
extInfo.put("assignedUserId", ext.getAssignedUserId());
extInfo.put("assignedUserName", ext.getAssignedUserName());
extInfo.put("assignedTeamId", ext.getAssignedTeamId());
extInfo.put("result", ext.getResult());
extInfo.put("resultImgUrls", ext.getResultImgUrls());
extInfo.put("dispatchedTime", ext.getDispatchedTime());
extInfo.put("confirmedTime", ext.getConfirmedTime());
extInfo.put("completedTime", ext.getCompletedTime());
return extInfo;
}
}

View File

@@ -0,0 +1,55 @@
package com.viewsh.module.ops.security.service.securityorder;
import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO;
/**
* 安保工单服务接口
* <p>
* 提供安保工单的创建、确认、自动完单、人工完单等能力
*
* @author lzh
*/
public interface SecurityOrderService {
/**
* 创建安保工单(对外接口,由外部告警系统调用)
* <p>
* 流程:创建主表 + 扩展表 → 自动派单
*
* @param createReq 创建请求
* @return 工单ID
*/
Long createSecurityOrder(SecurityOrderCreateReqDTO createReq);
/**
* 确认工单(安保人员确认接单)
*
* @param orderId 工单ID
* @param userId 安保人员user_id
*/
void confirmOrder(Long orderId, Long userId);
/**
* 自动完单(对方系统调用,无需提交结果)
*
* @param orderId 工单ID
* @param remark 备注
*/
void autoCompleteOrder(Long orderId, String remark);
/**
* 人工完单(安保人员提交处理结果)
*
* @param req 完单请求(包含 result + resultImgUrls
*/
void manualCompleteOrder(SecurityOrderCompleteReqDTO req);
/**
* 根据工单ID查询安保扩展信息
*
* @param opsOrderId 工单ID
* @return 扩展信息
*/
OpsOrderSecurityExtDO getSecurityExt(Long opsOrderId);
}

View File

@@ -0,0 +1,209 @@
package com.viewsh.module.ops.security.service.securityorder;
import cn.hutool.core.util.StrUtil;
import com.viewsh.module.ops.core.event.OrderEventPublisher;
import com.viewsh.module.ops.core.event.OrderCreatedEvent;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
import com.viewsh.module.ops.enums.OperatorTypeEnum;
import com.viewsh.module.ops.enums.PriorityEnum;
import com.viewsh.module.ops.enums.SourceTypeEnum;
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import com.viewsh.module.ops.enums.WorkOrderTypeEnum;
import com.viewsh.module.ops.infrastructure.code.OrderCodeGenerator;
import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain;
import com.viewsh.module.ops.infrastructure.log.enumeration.LogModule;
import com.viewsh.module.ops.infrastructure.log.enumeration.LogType;
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecord;
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder;
// 注意confirm/complete 的业务日志由 SecurityOrderEventListener 统一记录
// 本类仅记录 CREATE 日志(创建不经过状态变更事件)
import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.viewsh.module.ops.enums.ErrorCodeConstants.*;
import com.viewsh.module.ops.infrastructure.id.OrderIdGenerator;
import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO;
import com.viewsh.module.ops.security.dal.mysql.workorder.OpsOrderSecurityExtMapper;
import com.viewsh.module.ops.service.fsm.OrderStateMachine;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 安保工单服务实现
*
* @author lzh
*/
@Slf4j
@Service
public class SecurityOrderServiceImpl implements SecurityOrderService {
@Resource
private OpsOrderMapper opsOrderMapper;
@Resource
private OpsOrderSecurityExtMapper securityExtMapper;
@Resource
private OrderIdGenerator orderIdGenerator;
@Resource
private OrderCodeGenerator orderCodeGenerator;
@Resource
private OrderEventPublisher orderEventPublisher;
@Resource
private OpsBusAreaMapper opsBusAreaMapper;
@Resource
private OrderStateMachine orderStateMachine;
@Resource
private EventLogRecorder eventLogRecorder;
private static final String BUSINESS_TYPE = WorkOrderTypeEnum.SECURITY.getType();
@Override
@Transactional(rollbackFor = Exception.class)
public Long createSecurityOrder(SecurityOrderCreateReqDTO createReq) {
// 0. 校验区域是否存在
if (opsBusAreaMapper.selectById(createReq.getAreaId()) == null) {
throw exception(AREA_NOT_FOUND);
}
// 1. 生成ID和编号
Long orderId = orderIdGenerator.generate();
String orderCode = orderCodeGenerator.generate(BUSINESS_TYPE);
// 2. 确定来源类型
String sourceType = StrUtil.isNotBlank(createReq.getSourceType())
? createReq.getSourceType()
: (StrUtil.isNotBlank(createReq.getAlarmId()) ? SourceTypeEnum.ALARM.getType() : SourceTypeEnum.MANUAL.getType());
// 3. 构建主表记录
OpsOrderDO order = OpsOrderDO.builder()
.id(orderId)
.orderCode(orderCode)
.orderType(BUSINESS_TYPE)
.sourceType(sourceType)
.title(createReq.getTitle())
.description(createReq.getDescription())
.priority(createReq.getPriority() != null ? createReq.getPriority() : PriorityEnum.P2.getPriority())
.status(WorkOrderStatusEnum.PENDING.getStatus())
.areaId(createReq.getAreaId())
.location(createReq.getLocation())
.build();
opsOrderMapper.insert(order);
// 4. 构建扩展表记录
OpsOrderSecurityExtDO securityExt = OpsOrderSecurityExtDO.builder()
.opsOrderId(orderId)
.alarmId(createReq.getAlarmId())
.alarmType(createReq.getAlarmType())
.cameraId(createReq.getCameraId())
.roiId(createReq.getRoiId())
.imageUrl(createReq.getImageUrl())
.build();
securityExtMapper.insert(securityExt);
// 5. 发布工单创建事件(触发自动派单)
OrderCreatedEvent event = OrderCreatedEvent.builder()
.orderId(orderId)
.orderType(BUSINESS_TYPE)
.orderCode(orderCode)
.title(createReq.getTitle())
.areaId(createReq.getAreaId())
.priority(order.getPriority())
.createTime(order.getCreateTime())
.build();
orderEventPublisher.publishOrderCreated(event);
// 6. 记录业务日志
eventLogRecorder.record(EventLogRecord.builder()
.module(LogModule.SECURITY)
.domain(EventDomain.DISPATCH)
.eventType(LogType.ORDER_CREATED.getCode())
.message("安保工单创建")
.targetId(orderId)
.targetType("order")
.build());
log.info("创建安保工单成功: orderId={}, orderCode={}, alarmId={}, areaId={}",
orderId, orderCode, createReq.getAlarmId(), createReq.getAreaId());
return orderId;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void confirmOrder(Long orderId, Long userId) {
OpsOrderDO order = getOrderOrThrow(orderId);
validateOrderType(order);
// 状态转换DISPATCHED → CONFIRMED扩展表时间 + 业务日志由 EventListener 统一记录)
orderStateMachine.transition(order, WorkOrderStatusEnum.CONFIRMED,
OperatorTypeEnum.SECURITY_GUARD, userId, "安保人员确认接单");
log.info("安保工单确认: orderId={}, userId={}", orderId, userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void autoCompleteOrder(Long orderId, String remark) {
OpsOrderDO order = getOrderOrThrow(orderId);
validateOrderType(order);
// 状态转换 → COMPLETED扩展表时间 + 业务日志由 EventListener 统一记录,主表 endTime 由状态机统一设置)
orderStateMachine.transition(order, WorkOrderStatusEnum.COMPLETED,
OperatorTypeEnum.SYSTEM, null,
StrUtil.isNotBlank(remark) ? remark : "系统自动完单");
log.info("安保工单自动完单: orderId={}", orderId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void manualCompleteOrder(SecurityOrderCompleteReqDTO req) {
OpsOrderDO order = getOrderOrThrow(req.getOrderId());
validateOrderType(order);
// 状态转换 → COMPLETED扩展表 completedTime + 业务日志由 EventListener 统一记录,主表 endTime 由状态机统一设置)
orderStateMachine.transition(order, WorkOrderStatusEnum.COMPLETED,
OperatorTypeEnum.SECURITY_GUARD, req.getOperatorId(), "安保人员提交处理结果");
// 更新扩展表:结果 + 图片
OpsOrderSecurityExtDO extUpdate = new OpsOrderSecurityExtDO();
extUpdate.setOpsOrderId(req.getOrderId());
extUpdate.setResult(req.getResult());
if (req.getResultImgUrls() != null && !req.getResultImgUrls().isEmpty()) {
extUpdate.setResultImgUrls(cn.hutool.json.JSONUtil.toJsonStr(req.getResultImgUrls()));
}
securityExtMapper.insertOrUpdateSelective(extUpdate);
log.info("安保工单人工完单: orderId={}", req.getOrderId());
}
@Override
public OpsOrderSecurityExtDO getSecurityExt(Long opsOrderId) {
return securityExtMapper.selectByOpsOrderId(opsOrderId);
}
// ==================== 内部方法 ====================
private OpsOrderDO getOrderOrThrow(Long orderId) {
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
throw exception(SECURITY_ORDER_NOT_FOUND);
}
return order;
}
private void validateOrderType(OpsOrderDO order) {
if (!BUSINESS_TYPE.equals(order.getOrderType())) {
throw exception(SECURITY_ORDER_TYPE_MISMATCH);
}
}
}