npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@lingxiteam/service

v1.0.5

Published

服务编排框架

Downloads

335

Readme

服务编排框架及模块调整说明

为了提高服务编排模块的性能和代码可维护性,在新版本中我们提供了@lingxiteam/service这个包作为服务编排模块底层数据操作框架。该框架提供了一个通用的数据存储,修改以及响应的接口。

在新版本,我们将服务编排模块的代码调整至ServiceOrchestrator目录,业务相关的代码以及组件和资源都放置在此目录,以便于将来模块解耦。

快速开始

当前@lingxiteam/service还未发包,本地使用软连接使用,包的位置位于packages/service/src/

不建议直接修改包中的内容,在使用的过程中如果觉得框架有需要修改,需要先做评估。

项目的启动

  • 请确保你已经从git上拉取了该分支的全部代码,当前分支为opt-service-performance-11146372

  • 使用yarn start:app 运行主程序,同时切换到packages/service目录执行yarn watch

如果不出意外,此时项目应该可以正常启动了。

API参考及说明

ServiceProcessor

服务编排框架的核心处理类,负责数据的处理以及事件的分法。

const processor = new ServiceProcessor({ // 类实例化的时候可以传入默认数据到store中去
  baseInfo: {
    serviceName: `新增服务${getCurrentDateTime('MMDDhhmm')}`,
    serviceCode: `new_${createId(10)}`,
    serviceType: 'orchestration',
  },
  serviceParam: {},
  serviceNodeList: [
  	{ nodeCode: 'input', nodeName: '服务入参', nodeType: 'input', children: [] },
  	{ nodeCode: 'output', nodeName: '服务出参', nodeType: 'output', children: [] },
 	],
  nodeListStructure: [],
  nodeDictionary: {},
  selectedNodeCode: 'input',
  graphInstance: null,
  selectedNode: null,
  cache: {},
  addStepInfo: {},
  activeTab: null, // 'choreography',
  status: 0, // 0:未初始化 1: 初始化完成 >1 更新数据
}) 

实际上对于ServiceProcessor的实例化,也包含在框架中了,我们会将实例processor暴露出来,以供大家通过processor几个简单的API封装出更多的符合业务需求的工具代码。

ServiceProcessor实例方法使用说明
方法
getData方法支持多种方式取值
  • public getData(): any;

    • 参数: 不传入任何参数

    • 返回值: 返回整个store中的数据

  • public getData(key: string | string[]): any;

    • 参数: 传入从store后开始的路径字符串活路径字符串的数据

    • 返回值: 返回指定路径的数据或者指定多个路径的数据集合

    getData('baseInfo') // 返回store中baseInfo的最新值
    getData('baseInfo.serviceName') // 返回store中baseInfo.serviceName的最新值
    getData(['baseInfo.serviceCode', 'status']) // 返回对应值的数组[serviceCode新值, status新值]
  • public getData(selector: (store: any) => any): any;

    • 参数:使用函数(selector的方式)获取值,支持数组
    • 返回值:返回指定选择器的获取的数据或者数据集合
    getData(store => store.baseInfo) // 返回store中baseInfo的最新值
    getData(store => store.baseInfo.serviceName) // 返回store中baseInfo.serviceName的最新值
    // store不是固定的,可以进行简化
    getData(s => [s.serviceCode, s.status]) // 返回对应值的数组[serviceCode新值, status新值]
setData方法同样支持多中方式设置值
  • public setData(newData: object): void;

    • 参数: 不传入路径,只传入新的数据

    • 说明:将newData合并到store中

  • setData = (path: string | string[], value: any | ((currentValue: any) => any)): void

    • 参数:path路径或者路径的数组,value要传入的值或者一个回调函数
    const instance = {};
    setData('graphInstance', instance); // store下跟节点单个值设置
    setData('baseInfo.serviceType', 'rule'); // 根据路径设置值
    setData(['graphInstance', 'baseInfo.serviceType'], [instance, 'rule']); //保证顺序一致
      
    // 使用回调函数的方式设置值
    setData('status', (value) => {
      // value是当前仓库中status的最新值
      return value + 1; // 将其+1后更新原值
    });

通过以上getData和setData方法的组合使用,就可以组合出我们自己业务上需要用到的业务函数

举个🌰 删除画布中指定节点

  deleteServiceNode: (processor: any, params: any) => {
    const { path, nodeCode } = params;
    const [serviceNodeList, nodeDictionary] = 
          processor.getData((s: any) => [s.serviceNodeList, s.nodeDictionary]);
    deleteNodeByPath(serviceNodeList, path);
    delete nodeDictionary[nodeCode];
    processor.setData(['nodeDictionary', 'status'], ([_, num]) => {
      return [nodeDictionary, num + 1];
    });
  },
subscribe订阅数据更新
  • subscribe(selector: (store: any) => any, listener: Listener): Unsubscribe
    • 参数:
      • selector: 函数,从 store 中选择部分数据的函数。
      • listener: 函数,当选定的数据变化时调用的回调函数。
    • 返回值: Unsubscribe,调用此函数可取消订阅。
  • 示例1:单独订阅一个数据
  useEffect(() => {
    const unsubscribe = subscribe(store => store.serviceParam, (value) => {
      // 这里的value就是store中serviceParam的值
      // 当serviceParam发生了变化
      // 可以在这里做一些事情....
    });
    return () => unsubscribe();
  }, []);
  • 示例2:单独订阅一个数据,并触发当前页面更新
const [serviceParam, setServiceParam] = useState({});

 useEffect(() => {
    const unsubscribe = subscribe(store => store.serviceParam, setServiceParam); // v => setService(v)简写

    return () => unsubscribe();
  }, []);

 return (<MakeupParames serviceParam={serviceParam} />);
  • 示例3:单独订阅一个数据,并且通过比较新旧值进行再进行下一步动作
 useEffect(() => {
    const unsubscribe = subscribe(store => store.serviceParam, (currValue, prevValue) => {
		if(currValue.editStatus !== prevValue.editStatus) {
      // 可以精确控制新旧值中具体某个值发生了变化再执行某些业务
      // ...
    }
    });
    return () => unsubscribe();
  }, []);
  • 示例4:同时订阅多个数据,并且通过数据判断决定下一步动作
  useEffect(() => {
    const unsubscribe = subscribe(
      s => [s.selectedNodeCode, s.nodeDictionary], 
      ([code, nodeMap],[oldCode,oldNodeMap]) => {
				// 多个值的订阅,返回新值和就值的数组,和订阅顺序一致
  			// ...
    }); 
    return () => unsubscribe();
  }, []);

在大多数情况下,如果没有要手动控制组件的渲染,而是希望直接取回一个具备响应性的数据,按照示例2,需要写不少样板代码,因此我们提供了一个hooks,可以简化这些样板代码:

  • 示例5:通过getStateData直接获取状态值
// 可以对照示例2,代码简化了不少
const serviceParam = getStateData(store => store.serviceParam);

return (<MakeupParames serviceParam={serviceParam} />);

init = (newData: { [key: string]: any }): void

update = (newData: { [key: string]: any }): void

public setData(newData: object): void;

三者功能相同,都是直接往store里直接塞数据,区别是init不会触发事件监听,一般用于初始设置默认数据,后两者会触发监听。

destroy = (): void

销毁当前仓库以及事件监听,在组件卸载时可以调用,已达到快速回收内存的目的

ServiceProvider

在serviceProvider中会完成对ServiceProcessor的实例化,并且在Context中共享这个这个实例processor,通过这个实例,我们可以组合出更加贴近业务的通用代码,并且将其注册到框架中。ServiceProvider提供了模块内状态共享的能力,并且多个状态相互不受影响,比如打开多个服务标签。当标签被关闭后,会自动调用processor提供的destroy()方法销毁该实例内的数据和监听。

  render(): React.ReactNode {
    return (
      <ServiceContext.Provider
        value={{
          subscribe: this.processor.subscribe,
          setData: this.processor.setData,
          getData: this.processor.getData,
          ...this.executeInjectedMethods(),
        }}
      >
        {this.props.children}
      </ServiceContext.Provider>
    );
  }

通过上面的代码不难发现,Provider本身暴露的三个subscribegetDatasetData都是实例processor提供的,其中this.executeInjectedMethods()用户注入用户自定义函数,以满足开发需求。

下面介绍一些已经提供的自定义函数
  getDataByNodeCode: (processor: any, nodeCode: string) => {
    return processor.getData('nodeDictionary')[nodeCode];
  },
  setGraphInstance: (processor: any, instance: any) => {
    processor.setData('graphInstance', instance);
  },
  getGraphInstance: (processor: any) => {
    return processor.getData('graphInstance');
  },
  setScaleRatio: (processor: any, instance: any) => {
    processor.setData('scaleRatio', transZoomRatio(instance.zoom()));
  },
  getStateData: (_: unknown, selector: Function) => useStateData(selector),
添加画布节点的自定义函数
addServiceNode: (processor: any, params: any) => {
    const { nodeName, nodeType, sourceStep } = params;
    const { path } = sourceStep;
    const [serviceNodeList, nodeDictionary] = processor.getData((s: any) => [s.serviceNodeList, s.nodeDictionary]);
    const suffix = Math.floor(Math.random() * 10000 + 1);
    const nodeCode = `${nodeType}${suffix}`;
    const newPath = path.split('/');
    newPath.pop();

    const newNode = {
      nodeName,
      nodeType,
      nodeCode,
      path: `${newPath.join('/')}/${nodeCode}`,
      children: GATEWAY_NODES.includes(nodeType) ? [] : null,
    };

    nodeDictionary[nodeCode] = newNode;

    if (nodeType === 'ifElse') {
      newNode.children = [
        {
          nodeCode: `if-${suffix}-1`,
          nodeName: '分支1',
          nodeType: 'if',
          children: [],
        },
        {
          nodeCode: `if-${suffix}-else`,
          nodeName: '其他',
          nodeType: 'if',
          children: [],
          isElse: true,
        },
      ];

      newNode.children.forEach(c => {
        nodeDictionary[c.nodeCode] = { ...c, path: `${newNode.path}/${c.nodeCode}` };
      });
    }

    insertNodeAfterPath(serviceNodeList, path, newNode);
    nodeDictionary[nodeCode] = newNode;

    processor.setData(['nodeDictionary', 'status'], ([_, num]) => {
      return [nodeDictionary, num + 1];
    });
  
    return [newNode];
  },
    
  // 使用方法
  const newNode = addServiceNode(params);
删除画布节点的自定义函数
  deleteServiceNode: (processor: any, params: any) => {
    if (!params) return;
    const { path, nodeCode } = params;
    const [serviceNodeList, nodeDictionary] = processor.getData((s: any) => [s.serviceNodeList, s.nodeDictionary]);
    deleteNodeByPath(serviceNodeList, path);
    delete nodeDictionary[nodeCode];
    processor.setData(['nodeDictionary', 'status'], ([_, num]) => {
      return [nodeDictionary, num + 1];
    });
  },