客户生命周期流程设计

需求:

  记录一个客户从注册到注销经历的各个生命周期节点,给与查询展示

设计:

  1、生命周期节点分为以下几个流程:注册、授信、资料获取...(先开发部分流程,剩余用...表示)流程只有特定流程有顺序,有些流程是交叉进行的,比如授信和资料获取

  2、生命周期节点主要信息是:描述、所属流程、失败原因、触发时间    节点有:开始节点、结束节点、流程唯一节点    默认一个流程内节点是可重复的,例如,审核拒绝->补件->审核拒绝->补件->审核成功这种操作     流程唯一节点是在这样的场景:决策提交前,每2分钟查询一次最新征信数据等,不管成功失败都查询  最新不通过无法提交决策,这种数据要求展示最新一次的节点数据,不是连接操作。 有些节点是各个流程公用的,例如税务授权,授信、提额都需要税务授权,针对这种目前是作为公共节点,直挂用户,下一个流程产生时挂载到流程

  

设计图:

 

 

 

 

 

 

 

 

数据库表:

-- 生命周期表 begin
CREATE TABLE `supply_chain_dsc`.`scp_dsc_cust_life_cycle_process_node_item` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `item_no` varchar(20) NOT NULL COMMENT '节点条目编码',
  `process_no` varchar(20) DEFAULT NULL COMMENT '所属流程编码',
  `node_no` varchar(20) NOT NULL COMMENT '节点编码',
  `node_desc` varchar(50) NOT NULL COMMENT '节点描述',
  `result_no` varchar(50) NOT NULL COMMENT '节点结果',
  `result_desc` varchar(100) DEFAULT NULL COMMENT '节点条目描述',
  `is_start_node` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否开始节点:0-否、1-是',
  `is_complete_node` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否完成节点:0-否、1-是',
  `is_process_unique`  tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否流程唯一:0-否、1-是',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除:0-否、1-是',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户生命周期流程节点表';

CREATE TABLE `supply_chain_dsc`.`scp_dsc_cust_life_cycle_process_node_record` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `process_record_id` bigint(11) DEFAULT NULL COMMENT '进程记录id',
  `life_cycle_record_id` bigint(20) NOT NULL COMMENT '生命周期记录id',
  `process_no` varchar(20) NOT NULL COMMENT '进程编码',
  `process_node_no` varchar(20) NOT NULL COMMENT '进程节点编码',
  `process_node_desc` varchar(50) NOT NULL COMMENT '进程节点描述',
  `process_node_item_no` varchar(20) DEFAULT NULL COMMENT '流程节点条目编码',
  `process_node_result` varchar(50) DEFAULT NULL COMMENT '流程节点结果',
  `completed_time` datetime NOT NULL COMMENT '完成时间',
  `fail_reason` varchar(500) DEFAULT NULL COMMENT '失败原因',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注字段',
  `is_invalid` tinyint(1) DEFAULT '0' COMMENT '是否失效:0-否、1-是',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='生命周期进程节点记录表';

CREATE TABLE `supply_chain_dsc`.`scp_dsc_cust_life_cycle_process_record` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `life_cycle_record_id` int(11) NOT NULL COMMENT '用户生命周期id',
  `process_special_business_data` varchar(2000) DEFAULT '' COMMENT '流程专属业务数据,json。 不同流程结构不一致',
  `process_no` varchar(20) NOT NULL COMMENT '流程编码',
  `process_desc` varchar(50) NOT NULL COMMENT '流程描述',
  `is_invalid` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否失效:0-否、1-是',
  `is_completed` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否完成:0-否、1-是',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户生命周期流程记录表';

CREATE TABLE `supply_chain_dsc`.`scp_dsc_cust_life_cycle_record` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` varchar(12) NOT NULL COMMENT '用户id',
  `certificate_no` varchar(30) DEFAULT NULL COMMENT '证件号码',
  `certificate_type` varchar(2) DEFAULT NULL COMMENT '证件类型 01-身份证号、02-社会信用代码',
  `project_id` varchar(50) DEFAULT NULL COMMENT '项目id',
  `project_name` varchar(64) DEFAULT NULL COMMENT '项目名称',
  `cust_name` varchar(128) DEFAULT NULL COMMENT '客户名称',
  `related_party_name` varchar(128) DEFAULT NULL COMMENT '关联方名称',
  `process_node_item_no` varchar(20) NOT NULL COMMENT '当前流程节点编码',
  `process_no` varchar(20) NOT NULL COMMENT '当前流程编码',
  `process_node_desc` varchar(50) DEFAULT NULL COMMENT '当前节点描述',
  `is_show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否展示:0-否、1-是',
  `register_time` datetime NOT NULL COMMENT '注册时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户生命周期记录表';

 

主要代码:

1、接收对象转存储对象

/**
 * 生命周期节点结果Mq接收
 * @author zhen
 * @date 2022/11/28 9:16
 * @description
 */
@Data
public class LifeCycleNodeResultMqEvent implements Serializable {
    /**
     * 节点Item编码 必填
     */
    private String processNodeItemNo;

    /**
     * 完成时间: yyyy-MM-dd HH:mm:ss  必填
     */
    private String completeTime;

    /**
     * 失败原因
     */
    private String failReason;

    /**
     * 项目id 除了注册流程指定节点之外,必填
     */
    private String projectId;

    /**
     * 证件类型: 01-身份证号、02-社会信用代码  除了注册流程指定节点之外,必填
     */
    private String certificateType;

    /**
     * 证件号 除了注册流程指定节点之外,必填
     */
    private String certificateNo;

    /**
     * 节点属性json
     */
    private String processPropertyJson;
}

/**
 * 注册流程属性event
 * @author zhen
 * @date 2022/11/28 9:35
 * @description
 */
@Data
public class LifeCycleRegisterProcessPropertyEvent implements Serializable {

    /**
     * 手机号
     */
    private String phone;
    /**
     * 用户id
     */
    private String userId;
    /**
     * 身份证号
     */
    private String idCardNo;

    /**
     * 用户姓名
     */
    private String userName;

    /**
     * 企业名称
     */
    private String enterpriseName;

}

/**
 * 注册流程数据Template
 * @author zhen
 * @date 2022/11/28 9:35
 * @description
 */
@Data
public class LifeCycleRegisterProcessDataTemplate implements Serializable {

    /**
     * 手机号
     */
    private String phone;
    /**
     * 用户id
     */
    private String userId;
    /**
     * 身份证号
     */
    private String idCardNo;

    /**
     * 用户姓名
     */
    private String userName;

}

 

核心代码:

@Override
    @Transactional(rollbackFor = Exception.class)
    public void receiptProcessNodeResult(LifeCycleNodeResultMqEvent event) {

        CustLifeCycleProcessNodeItem nodeItem = lifeCycleProcessNodeItemService.findByNodeItemNo(event.getProcessNodeItemNo());

        if (nodeItem == null) {
            log.info("【客户生命周期】根据节点item编号找不到对应的属性节点 nodeItemNo:{}", event.getProcessNodeItemNo());
            return;
        }

        if(ProcessSpecialNodeItemEnum.isValidValue(nodeItem.getItemNo())){
            nodeSpecialDeal(nodeItem, event);
        }else {
            normalNodeDeal(nodeItem, event);
        }

    }


    private String transProcessDataEventValueToTemplateValue(String eventValueJson, String processNo){
        if (StringUtils.isEmpty(eventValueJson) || "{}".equals(eventValueJson)){
            return "{}";
        }
        ProcessTemplateTransEnum templateTransEnum = ProcessTemplateTransEnum.getByValue(processNo);
        if (templateTransEnum != null) {
            try{
                Class srcClazz = Class.forName(templateTransEnum.getSrcClazzName());
                Class targetClazz = Class.forName(templateTransEnum.getSrcClazzName());
                Object srcObj = JSON.parseObject(eventValueJson, srcClazz);
                Object targetObject = targetClazz.newInstance();
                BeanUtils.copyProperties(srcObj, targetObject);
                return JSON.toJSONString(targetObject);
            } catch (IllegalAccessException|InstantiationException|ClassNotFoundException e) {
                log.error("对象转换异常", e);
                throw new RuntimeException("客户生命周期流程对象转换异常");
            }

        }
        return "{}";
    }

    private void nodeSpecialDeal(CustLifeCycleProcessNodeItem nodeItem, LifeCycleNodeResultMqEvent event){
        Class<LifeCycleNodeItemReceiptBusinessImpl> clazz = LifeCycleNodeItemReceiptBusinessImpl.class;
        try{
            Method invokeMethod = clazz.getMethod("n" + nodeItem.getItemNo() + "Deal", CustLifeCycleProcessNodeItem.class, LifeCycleNodeResultMqEvent.class);
            invokeMethod.invoke(this, nodeItem, event);
        }catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
            log.error("【LEVEL:LOW】找不到处理此特殊节点结果的执行器, 节点结果编码:{}", nodeItem.getItemNo());
            return;
        }
    }

    /**
     * 常用节点处理
     * @param nodeItem
     * @param event
     */
    private void normalNodeDeal(CustLifeCycleProcessNodeItem nodeItem, LifeCycleNodeResultMqEvent event) {

        CustLifeCycleRecord nodeLifeCycleRecord = getLifeCycleRecordByBusinessPk(event);
        if (nodeLifeCycleRecord == null) {
            return;
        }

        normalNodeDealUseLifeCycle(nodeLifeCycleRecord, nodeItem, event);
    }

    /**
     * 已知生命周期情况下常用节点处理
     * @param nodeLifeCycleRecord
     * @param nodeItem
     * @param event
     */
    private void normalNodeDealUseLifeCycle(CustLifeCycleRecord nodeLifeCycleRecord, CustLifeCycleProcessNodeItem nodeItem, LifeCycleNodeResultMqEvent event){
        //生命周期记录更新
        normalLifeCycleUpdate(nodeLifeCycleRecord, nodeItem);

        CustLifeCycleProcessRecord processRecord = lifeCycleProcessRecordService.selectCurrentProcessRecord(nodeLifeCycleRecord.getId(), nodeItem.getProcessNo());

        //节点保存
        nodeItemSave(nodeLifeCycleRecord.getId(), processRecord == null ? null : processRecord.getId(), nodeItem, event);
    }

    /**
     * 公用节点挂载
     * @param processRecord
     */
    private void commonNodeMount(CustLifeCycleProcessRecord processRecord ){
        List<CustLifeCycleProcessNodeRecord> nodeRecordList = lifeCycleProcessNodeRecordService.selectUnMountNodeRecordList(processRecord.getLifeCycleRecordId());
        for (CustLifeCycleProcessNodeRecord nodeRecord : nodeRecordList) {
            nodeRecord.setProcessNo(processRecord.getProcessNo());
            nodeRecord.setProcessRecordId(processRecord.getId());
        }
        lifeCycleProcessNodeRecordService.updateBatchById(nodeRecordList);
    }

    /**
     * 常用的生命周期更新操作
     * @param lifeCycleRecord
     * @param nodeItem
     */
    private void normalLifeCycleUpdate(CustLifeCycleRecord lifeCycleRecord, CustLifeCycleProcessNodeItem nodeItem){
        lifeCycleRecord.setProcessNo(nodeItem.getProcessNo());
        lifeCycleRecord.setProcessNodeDesc(nodeItem.getNodeDesc());
        lifeCycleRecord.setProcessNodeItemNo(nodeItem.getItemNo());
        lifeCycleRecord.setUpdateTime(LocalDateTime.now());
        lifeCycleRecordService.updateById(lifeCycleRecord);
    }

    /**
     * 常用的保存节点操作
     * @param lifeCycleRecordId
     * @param processRecordId
     * @param nodeItem
     * @param event
     */
    private void nodeItemSave(Long lifeCycleRecordId, Long processRecordId, CustLifeCycleProcessNodeItem nodeItem, LifeCycleNodeResultMqEvent event) {
        CustLifeCycleProcessRecord processRecord = null;
        if (processRecordId == null) {
            if (nodeItem.getIsStartNode() == 1) {
                //创建流程记录
                processRecord = buildNormalProcessRecord(lifeCycleRecordId, nodeItem);
                processRecord.setProcessSpecialBusinessData(transProcessDataEventValueToTemplateValue(event.getProcessPropertyJson(), nodeItem.getProcessNo()));
                lifeCycleProcessRecordService.save(processRecord);
            }
        }else {
            processRecord = lifeCycleProcessRecordService.getById(processRecordId);
        }
        if (processRecord != null){
            processRecordId = processRecord.getId();
            //公用节点挂载
            commonNodeMount(processRecord);
            /*
             * 完成节点特殊处理
             */
            if (1 == nodeItem.getIsCompleteNode()) {
                //0 -否、1-是 公用节点不可能是完成节点,因此,不会出现空指针
                processRecord.setIsCompleted(1);
                lifeCycleProcessRecordService.updateById(processRecord);
            }
        }


        if(nodeItem.getIsProcessUnique() == 1){
            //保存最新记录的节点操作就死update最新信息
            CustLifeCycleProcessNodeRecord processNoRecord = lifeCycleProcessNodeRecordService.findProcessNoRecord(lifeCycleRecordId, processRecordId, nodeItem.getItemNo());
            if (processNoRecord != null){
                CustLifeCycleProcessNodeRecord nodeRecord = buildNormalUpdateProcessNodeRecord(processNoRecord, event);
                lifeCycleProcessNodeRecordService.updateById(nodeRecord);
            }else {
                CustLifeCycleProcessNodeRecord nodeRecord = buildNormalProcessNodeRecord(lifeCycleRecordId, processRecordId, nodeItem, event);
                lifeCycleProcessNodeRecordService.save(nodeRecord);
            }
        }else {
            CustLifeCycleProcessNodeRecord nodeRecord = buildNormalProcessNodeRecord(lifeCycleRecordId, processRecordId, nodeItem, event);
            lifeCycleProcessNodeRecordService.save(nodeRecord);
        }

    }
View Code

 

 

 

 

 

 

  

posted @ 2022-12-06 17:34  guodaxia  阅读(81)  评论(0编辑  收藏  举报