feat: 完成消息企业隔离表结构、接口设计以及部分实现

This commit is contained in:
songyuanlun 2024-01-12 10:23:38 +08:00
parent 544b76f5ff
commit 5ce054cbd3
18 changed files with 725 additions and 3 deletions

View File

@ -0,0 +1,29 @@
package cn.axzo.msg.center.inside.notices.service.impl;
import cn.axzo.msg.center.api.GeneralMessageApi;
import cn.axzo.msg.center.api.request.GeneralMessagePushReq;
import cn.axzo.msg.center.api.response.ReachResp;
import cn.axzo.msg.center.message.domain.param.GeneralMessagePushParam;
import cn.axzo.msg.center.message.service.CommonMessageRecordService;
import cn.azxo.framework.common.model.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
/**
* @author syl
* @date 2024/1/8
*/
@Slf4j
@RestController
public class GeneralMessageApiImpl implements GeneralMessageApi {
@Autowired
private CommonMessageRecordService commonMessageRecordService;
@Override
public CommonResponse<ReachResp> reachGeneralMsg(GeneralMessagePushReq param) {
// // TODO: 2024/1/10
ReachResp result = commonMessageRecordService.reach(GeneralMessagePushParam.from(param));
return CommonResponse.success(result);
}
}

View File

@ -0,0 +1,83 @@
package cn.axzo.msg.center.message.domain.param;
import cn.axzo.msg.center.api.request.GeneralMessagePushReq;
import cn.axzo.msg.center.api.request.PendingParam;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author syl
* @date 2023/12/28
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class GeneralMessagePushParam {
/**
* 发起者
* 若业务事件涉及到待办消息发起者信息必传
*/
private PersonDTO initiator;
/**
* 接收者列表
*/
private List<PersonDTO> receivers;
/**
* 业务事件code
* oms->消息模板 配置
*/
private String bizEventCode;
/**
* 关联业务唯一标识
* 例如: 请假申请的编号
*/
private String bizCode;
/**
* 消息所属组织类型
*/
private OrganizationTypeEnum orgType;
/**
* 所属工作台ID
* orgType = PROJECT 项目时必传参数
*/
private Long workspaceId;
/**
* 所属企业ID
* 备注如果是工人则所在企业可以传0其它均必传真实企业
*/
private Long ouId;
/**
* 业务扩展参数-JSON字符串格式
*/
private JSONObject bizExtParams;
/**
* 路由参数-JSON字符串格式
*/
private JSONObject routerParams;
/**
* 待办消息特有参数
*/
private PendingParam pendingParam;
public static GeneralMessagePushParam from(GeneralMessagePushReq request) {
return BeanUtil.copyProperties(request, GeneralMessagePushParam.class);
}
}

View File

@ -1,5 +1,9 @@
package cn.axzo.msg.center.message.send.processor;
import cn.axzo.msg.center.common.utils.PlaceholderResolver;
import com.alibaba.fastjson.JSONObject;
import java.util.Objects;
/**
* @author syl
* @date 2023/12/18
@ -11,4 +15,20 @@ public interface ISendProcessor {
default void send(SendContext context) {
}
default void checkReach(ReachContext context) {
}
default void reach(ReachContext context) {
}
default void save(ReachContext context) {
}
default String parseString(String string, JSONObject params) {
if (Objects.isNull(params)) {
return string;
}
return PlaceholderResolver.getDefaultResolver().resolveByMap(string, params);
}
}

View File

@ -2,17 +2,24 @@ package cn.axzo.msg.center.message.send.processor;
import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.msg.center.api.request.GeneralMessageReq;
import cn.axzo.msg.center.dal.CommonMessageRecordDao;
import cn.axzo.msg.center.domain.entity.CommonMessageRecord;
import cn.axzo.msg.center.domain.entity.MessageBaseTemplate;
import cn.axzo.msg.center.domain.entity.MessageRecord;
import cn.axzo.msg.center.domain.enums.BizActionCategory;
import cn.axzo.msg.center.message.domain.dto.SendImMessageDTO;
import cn.axzo.msg.center.message.domain.param.GeneralMessagePushParam;
import cn.axzo.msg.center.message.service.BizEventMappingService;
import cn.axzo.msg.center.message.service.GeneralMessageService;
import cn.axzo.msg.center.message.service.MessageSendTwiceRecordService;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.GeneralMessageStateEnum;
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest;
import cn.axzo.msg.center.service.pending.response.PushMessageReturnDTO;
import cn.axzo.msg.center.utils.PersonIdentityUtil;
import cn.axzo.msg.center.utils.UUIDUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@ -45,6 +52,9 @@ public class NotificationProcessor implements ISendProcessor {
@Autowired
private BizEventMappingService bizEventMappingService;
@Autowired
private CommonMessageRecordDao commonMessageRecordDao;
@Override
public void check(SendContext context) {
AssertUtil.notNull(context.getMessageParam(), "通知消息的参数错误,请检查");
@ -138,4 +148,62 @@ public class NotificationProcessor implements ISendProcessor {
.orElseGet(JSONObject::new))
.build();
}
@Override
public void checkReach(ReachContext context) {
AssertUtil.notNull(context.getMessageParam(), "通知消息的参数错误,请检查");
AssertUtil.notNull(context.getReachConfig(), "触达配置不能为空");
AssertUtil.isTrue(BizActionCategory.NOTIFICATION.getCode().equals(context.getReachConfig()
.getCategory()), "消息动作不匹配");
}
@Override
public void reach(ReachContext context) {
// 检查
this.checkReach(context);
log.info("NotificationProcessor new send im message. bizCode= [{}]"
+ ", templateCode= [{}]", context.getBizEvent().getBizCode(),
context.getReachConfig().getTemplateCode());
}
@Override
public void save(ReachContext context) {
this.checkReach(context);
MessageBaseTemplate template = bizEventMappingService.checkTemplateCodeByBizEvent(context.getReachConfig());
context.setTemplate(template);
List<CommonMessageRecord> records = convertRecords(context);
commonMessageRecordDao.saveBatch(records);
List<PushMessageReturnDTO> msgReturnList = records.stream()
.map(r -> PushMessageReturnDTO.builder().identityCode(r.getIdentityCode()).build())
.collect(Collectors.toList());
context.setMsgReturnList(msgReturnList);
}
private List<CommonMessageRecord> convertRecords(ReachContext context) {
MessageBaseTemplate template = context.getTemplate();
GeneralMessagePushParam messageParam = context.getMessageParam();
return messageParam.getReceivers().stream().map(r -> {
CommonMessageRecord.CommonMessageRecordBuilder builder = CommonMessageRecord.builder()
.identityCode(UUIDUtil.uuidString());
if (Objects.nonNull(messageParam.getInitiator())) {
builder.senderPersonId(messageParam.getInitiator().getId());
}
builder.receiverPersonId(r.getId());
builder.bizEventCode(messageParam.getBizEventCode());
builder.templateCode(template.getCode());
builder.title(parseString(template.getTitle(), messageParam.getBizExtParams()));
builder.content(parseString(template.getContent(), messageParam.getBizExtParams()));
builder.orgType(messageParam.getOrgType());
builder.workspaceId(messageParam.getWorkspaceId());
builder.ouId(messageParam.getOuId());
builder.state(GeneralMessageStateEnum.HAS_BEEN_SENT);
builder.bizCode(messageParam.getBizCode());
builder.routerParams(messageParam.getRouterParams());
builder.bizExtParams(messageParam.getBizExtParams());
return builder.build();
}).collect(Collectors.toList());
}
}

View File

@ -54,7 +54,8 @@ public class PendingProcessor implements ISendProcessor {
+ ", templateCode= [{}]", context.getBizEvent().getBizCode(),
context.getReachConfig().getTemplateCode());
// todo 由于之前业务方这期未做改造而之前对外提供的参数只满足消息待办这块的发送需要定义待办相关的业务属性
// 由于之前业务方这期未做改造而之前对外提供的参数只满足消息待办这块的发送需要定义待办相关的业务属性
// 迁移到新接口
// PendingMessagePushRequest request = buildSendRequest(context);
// pendingMessageNewService.push(PendingMessagePushParam.from(request));
}
@ -87,4 +88,17 @@ public class PendingProcessor implements ISendProcessor {
.bizExtParams(null)
.build();
}
@Override
public void checkReach(ReachContext context) {
}
@Override
public void reach(ReachContext context) {
// 等后续im具有待办消息的能力再接入目前统一走待办中心
}
@Override
public void save(ReachContext context) {
}
}

View File

@ -0,0 +1,49 @@
package cn.axzo.msg.center.message.send.processor;
import cn.axzo.msg.center.domain.entity.BizEventMapping;
import cn.axzo.msg.center.domain.entity.MessageBaseTemplate;
import cn.axzo.msg.center.message.domain.param.GeneralMessagePushParam;
import cn.axzo.msg.center.service.bizevent.request.ReachDto;
import cn.axzo.msg.center.service.pending.response.PushMessageReturnDTO;
import cn.axzo.msg.center.service.pending.response.PushPendingMessageDTO;
import java.util.Collections;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Builder.Default;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author syl
* @date 2024/01/10
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReachContext {
/**
* 业务事件映射
*/
private BizEventMapping bizEvent;
private ReachDto reachConfig;
private MessageBaseTemplate template;
/**
* IM 消息发送参数
*/
private GeneralMessagePushParam messageParam;
/**
* 待办返回参数
*/
@Default
List<PushPendingMessageDTO> pendingList = Collections.emptyList();
/**
* 通知返回参数
*/
@Default
List<PushMessageReturnDTO> msgReturnList = Collections.emptyList();
}

View File

@ -1,6 +1,7 @@
package cn.axzo.msg.center.message.service;
import cn.axzo.msg.center.domain.entity.BizEventMapping;
import cn.axzo.msg.center.domain.entity.MessageBaseTemplate;
import cn.axzo.msg.center.message.domain.param.BizEventMappingQueryParam;
import cn.axzo.msg.center.message.domain.param.BizEventSaveOrUpdateParam;
import cn.axzo.msg.center.service.bizevent.request.BizEventMappingPageResponse;
@ -25,5 +26,5 @@ public interface BizEventMappingService {
Optional<BizEventMapping> getByBizCode(String bizCode);
void checkTemplateCodeByBizEvent(ReachDto reachConfig);
MessageBaseTemplate checkTemplateCodeByBizEvent(ReachDto reachConfig);
}

View File

@ -0,0 +1,13 @@
package cn.axzo.msg.center.message.service;
import cn.axzo.msg.center.api.response.ReachResp;
import cn.axzo.msg.center.message.domain.param.GeneralMessagePushParam;
/**
* @author syl
* @date 2024/1/8
*/
public interface CommonMessageRecordService {
ReachResp reach(GeneralMessagePushParam param);
}

View File

@ -125,7 +125,7 @@ public class BizEventMappingServiceImpl implements BizEventMappingService {
}
@Override
public void checkTemplateCodeByBizEvent(ReachDto config) {
public MessageBaseTemplate checkTemplateCodeByBizEvent(ReachDto config) {
BizActionCategory category = BizActionCategory.getCategory(config.getCategory());
Optional<MessageBaseTemplate> templateOpt = messageBaseTemplateDao.lambdaQuery()
.eq(MessageBaseTemplate::getCode, config.getTemplateCode())
@ -134,6 +134,7 @@ public class BizEventMappingServiceImpl implements BizEventMappingService {
.oneOpt();
AssertUtil.isTrue(templateOpt.isPresent(), String.format("消息模板code不存在或类型不匹配: %s",
config.getTemplateCode()));
return templateOpt.get();
}
public LambdaQueryChainWrapper<BizEventMapping> build(BizEventMappingQueryParam bo) {

View File

@ -0,0 +1,80 @@
package cn.axzo.msg.center.message.service.impl;
import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.msg.center.api.response.ReachResp;
import cn.axzo.msg.center.domain.entity.BizEventMapping;
import cn.axzo.msg.center.domain.enums.BizActionCategory;
import cn.axzo.msg.center.message.domain.param.GeneralMessagePushParam;
import cn.axzo.msg.center.message.send.processor.ISendProcessor;
import cn.axzo.msg.center.message.send.processor.ReachContext;
import cn.axzo.msg.center.message.service.BizEventMappingService;
import cn.axzo.msg.center.message.service.CommonMessageRecordService;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import jodd.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
/**
* @author syl
* @date 2024/1/8
*/
@Slf4j
@Service
public class CommonMessageRecordServiceImpl implements CommonMessageRecordService {
@Value("${msg-im-server.partition-size:500}")
private Integer partitionSize;
private final ThreadFactory asyncReachThreadFactory = ThreadFactoryBuilder.create()
.setDaemon(true).setNameFormat("REACH_IM_MESSAGE_%d").get();
private final ExecutorService reachExecutorService = new ThreadPoolExecutor(5,
10, 5, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1024), asyncReachThreadFactory);
@Autowired
private BizEventMappingService bizEventMappingService;
@Autowired
private ApplicationContext applicationContext;
@Override
public ReachResp reach(GeneralMessagePushParam param) {
String bizEventCode = param.getBizEventCode();
Optional<BizEventMapping> bizEventOpt = bizEventMappingService.getByBizCode(bizEventCode);
AssertUtil.isTrue(bizEventOpt.isPresent(), String.format("业务事件映射%s不存在", bizEventCode));
BizEventMapping bizEvent = bizEventOpt.get();
AssertUtil.notEmpty(bizEvent.getReachConfig(), String.format("业务事件映射%s无业务动作配置", bizEventCode));
log.info("start to send reach message. bizCode:[{}]", bizEventCode);
ReachResp resp = new ReachResp();
bizEvent.getReachConfig().forEach(r -> {
BizActionCategory category = BizActionCategory.getCategory(r.getCategory());
ISendProcessor processor = applicationContext.getBean(category.getProcessor(), ISendProcessor.class);
ReachContext reachContext = ReachContext.builder()
.bizEvent(bizEvent).reachConfig(r)
.messageParam(param)
.build();
processor.save(reachContext);
switch (category) {
case NOTIFICATION:
// resp.setMsgReturnList(reachContext.getMsgReturnList());
break;
case PENDING:
resp.setPendingList(reachContext.getPendingList());
break;
default:
}
CompletableFuture.runAsync(() -> processor.reach(reachContext), reachExecutorService);
});
return resp;
}
}

View File

@ -0,0 +1,23 @@
package cn.axzo.msg.center.api;
import cn.axzo.msg.center.api.request.GeneralMessagePushReq;
import cn.axzo.msg.center.api.response.ReachResp;
import cn.azxo.framework.common.model.CommonResponse;
import javax.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author syl
* @date 2023/12/28
*/
public interface GeneralMessageApi {
/**
* 业务触达
* 消息待办等
*/
@RequestMapping(value = "api/message/reach", method = RequestMethod.POST)
CommonResponse<ReachResp> reachGeneralMsg(@RequestBody @Valid GeneralMessagePushReq param);
}

View File

@ -0,0 +1,85 @@
package cn.axzo.msg.center.api.request;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import com.alibaba.fastjson.JSONObject;
import java.util.List;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author syl
* @date 2023/12/28
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class GeneralMessagePushReq {
/**
* 发起者
* 若业务事件涉及到待办消息发起者信息必传
*/
// @NotNull(message = "发起者不能为空")
private PersonDTO initiator;
/**
* 接收者列表
*/
@NotNull(message = "接收者列表不能为空")
private List<PersonDTO> receivers;
/**
* 业务事件code
* oms->消息模板 配置
*/
@NotBlank(message = "业务事件code不能为空")
private String bizEventCode;
/**
* 关联业务唯一标识
* 例如: 请假申请的编号
*/
@NotBlank(message = "关联业务唯一标识不能为空")
private String bizCode;
/**
* 消息所属组织类型
*/
@NotNull(message = "工作台类型不能为空")
private OrganizationTypeEnum orgType;
/**
* 所属工作台ID
* orgType = PROJECT 项目时必传参数
*/
// @NotNull(message = "消息所属工作台ID不能为空")
private Long workspaceId;
/**
* 所属企业ID
* 备注如果是工人则所在企业可以传0其它均必传真实企业
*/
@NotNull(message = "消息所属企业ID不能为空")
private Long ouId;
/**
* 业务扩展参数-JSON格式
*/
private JSONObject bizExtParams;
/**
* 路由参数-JSON格式
*/
private JSONObject routerParams;
/**
* 待办消息特有参数
*/
private PendingParam pendingParam;
}

View File

@ -0,0 +1,52 @@
package cn.axzo.msg.center.api.request;
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
import cn.axzo.msg.center.service.enums.BizFinalStateEnum;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 待办消息参数
*
* @author syl
* @date 2024/1/3
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PendingParam {
/**
* 业务类型
* 待办消息必传
*/
private BizCategoryEnum bizCategory;
/**
* 关联业务主键
*/
// private String bizCode;
/**
* 流程类代办的流程结点编码
*/
private String subBizCode;
/**
* 业务描述eg:流程结点描述
*/
private String bizDesc;
/**
* 业务标签
*/
private String bizFlag;
/**
* 业务终态可为空
*/
private BizFinalStateEnum bizFinalState;
/**
* 待办的截止时间
*/
private Date deadline;
}

View File

@ -0,0 +1,33 @@
package cn.axzo.msg.center.api.response;
import cn.axzo.msg.center.service.pending.response.PushPendingMessageDTO;
import java.util.Collections;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Builder.Default;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author syl
* @date 2024/1/10
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ReachResp {
/**
* 待办返回参数
*/
@Default
List<PushPendingMessageDTO> pendingList = Collections.emptyList();
/**
* 通知返回参数
*/
@Default
List<PushPendingMessageDTO> msgReturnList = Collections.emptyList();
}

View File

@ -0,0 +1,18 @@
package cn.axzo.msg.center.service.pending.response;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PushMessageReturnDTO {
/**
* 消息的唯一标识
*/
private String identityCode;
}

View File

@ -0,0 +1,12 @@
package cn.axzo.msg.center.dal;
import cn.axzo.msg.center.dal.mapper.CommonMessageRecordMapper;
import cn.axzo.msg.center.domain.entity.CommonMessageRecord;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class CommonMessageRecordDao extends ServiceImpl<CommonMessageRecordMapper, CommonMessageRecord> {
}

View File

@ -0,0 +1,14 @@
package cn.axzo.msg.center.dal.mapper;
import cn.axzo.msg.center.domain.entity.CommonMessageRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author syl
* @date 2024/01/08
*/
@Mapper
public interface CommonMessageRecordMapper extends BaseMapper<CommonMessageRecord> {
}

View File

@ -0,0 +1,127 @@
package cn.axzo.msg.center.domain.entity;
import cn.axzo.msg.center.service.enums.GeneralMessageStateEnum;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 消息通知记录
*
* @author syl
* @date 2024/01/10
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName(value = "common_message_record", autoResultMap = true)
public class CommonMessageRecord {
/**
* 消息的唯一标识
*/
@TableField("identity_code")
private String identityCode;
/**
* 发送者自然人 ID
*/
@TableField("sender_person_id")
private Long senderPersonId;
/**
* 接收者自然人 ID
*/
@TableField("receiver_person_id")
private Long receiverPersonId;
/**
* 业务事件code
*/
@TableField("biz_event_code")
private String bizEventCode;
/**
* 模板编码
*/
private String templateCode;
/**
* 消息标题
*/
private String title;
/**
* 消息内容
*/
private String content;
/**
* 消息所属组织类型
*/
private OrganizationTypeEnum orgType;
/**
* 项目部ID
*/
@TableField("workspace_id")
private Long workspaceId;
/**
* 企业ID
*/
@TableField("ou_id")
private Long ouId;
/**
* 消息状态
*/
private GeneralMessageStateEnum state;
/**
* 关联业务主键
*/
private String bizCode;
/**
* 路由参数留存
*/
@TableField(typeHandler = FastjsonTypeHandler.class)
private JSONObject routerParams;
/**
* 业务扩展参数
*/
@TableField(typeHandler = FastjsonTypeHandler.class)
private JSONObject bizExtParams;
/**
* 推送重试次数
*/
private Integer retryCount;
/**
* 推送最终失败原因
*/
private String failCause;
/**
* id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 创建时间
*/
private Date createAt;
/**
* 修改时间
*/
private Date updateAt;
/**
* 是否删除 正常-0删除-主键ID
*/
private Long isDelete;
}