当前位置: 首页 > news >正文

企业网站的建站步骤北京做公司网站的公司

企业网站的建站步骤,北京做公司网站的公司,赚钱做任务的网站有哪些,网页设计代码中字体的颜色如何改优质博文#xff1a;IT-BLOG-CN 一、LiteFlow 简介 LiteFlow是一个轻量且强大的国产规则引擎框架#xff0c;可用于复杂的组件化业务的编排领域。帮助系统变得更加丝滑且灵活。利用LiteFlow#xff0c;你可以将瀑布流式的代码#xff0c;转变成以组件为核心概念的代码结构…优质博文IT-BLOG-CN 一、LiteFlow 简介 LiteFlow是一个轻量且强大的国产规则引擎框架可用于复杂的组件化业务的编排领域。帮助系统变得更加丝滑且灵活。利用LiteFlow你可以将瀑布流式的代码转变成以组件为核心概念的代码结构这种结构的好处是可以任意编排组件与组件之间是解耦的组件可以用脚本来定义组件之间的流转全靠规则来驱动。LiteFlow拥有开源规则引擎最为简单的DSL语法。 LiteFlow官网 LiteFlow于2020年正式开源2021年获得开源中国年度最受欢迎开源软件殊荣。于2022年获得Gitee最有价值开源项目GVP荣誉。是一个正处在高速发展中的开源项目。LiteFlow是一个由社区驱动的项目拥有一个2500多人的使用者社区。虽然相比Acitiviti、Flowable来说LiteFlow的知名度要低得多功能也没有这些知名成熟引擎那么强大但LiteFlow还是有诸多优点能够满足你绝大部分的场景。这些优点包括 【1】规则多样化 规则支持xml、json、yml三种规则文件写法方式。 【2】使用便捷 引几个jar包、实现几个接口、写一个流程编排文件就能运行。 【3】编排丰富 支持串行、并行、选择、循环、异常处理、嵌套等各种编排方式。 【4】事件监听 支持事件触发和状态变化监听可以方便地扩展和定制工作流处理逻辑。 【5】异步超时 支持异步执行和超时控制可以提高系统的并发处理能力和稳定性。 【6】支持脚本 支持各种主流脚本语言。 【7】配置源丰富 支持将流程定义放到ZK/DB/Etcd/Nacos/Redis/Apollo和自定义扩展等。相当于可以实现动态配置更改。 【8】定制化 高度可定制化用户可以根据自己的需求自由扩展和定制LiteFlow的各种组件和功能。 【9】支持众多脚本语言 LiteFlow的脚本组件支持众多脚本语言Groovy/JavaScript/QLExpress/Python/Lua/Aviator/Java完全和Java打通你可以用脚本来实现任何逻辑。 【10】优雅热刷新机制 规则变化无需重启您的应用即时改变应用的规则。高并发下不会因为刷新规则导致正在执行的规则有任何错乱。 【11】支持广泛 不管你的项目是不是基于SpringbootSpring还是任何其他java框架构建LiteFlow都能游刃有余。 【12】上下文隔离机制 可靠的上下文隔离机制你无需担心高并发情况下的数据串流。 【13】性能卓越 框架本身几乎不消耗额外性能性能取决你的组件执行效率。 【14】自带简单监控 框架内自带一个命令行的监控能够知道每个组件的运行耗时排行。 适合使用的这项技术的系统 在每个公司的系统中总有一些拥有复杂业务逻辑的系统这些系统承载着核心业务逻辑几乎每个需求都和这些核心业务有关这些核心业务业务逻辑冗长涉及内部逻辑运算缓存操作持久化操作外部资源调取内部其他系统RPC调用等等。时间一长项目几经易手维护成本就会越来越高。各种硬代码判断分支条件越来越多。代码的抽象复用率也越来越低各个模块之间的耦合度很高。一小段逻辑的变动会影响到其他模块需要进行完整回归测试来验证。如要灵活改变业务流程的顺序则要进行代码大改动进行抽象重新写方法。实时热变更业务流程几乎很难实现。 如何打破僵局LiteFlow为解耦逻辑而生为编排而生在使用LiteFlow之后你会发现打造一个低耦合灵活的系统会变得易如反掌 二、LiteFlow 原理 如果你要对复杂业务逻辑进行新写或者重构用LiteFlow最合适不过。它是一个编排式的规则引擎框架组件编排帮助解耦业务代码让每一个业务片段都是一个组件。 LiteFlow的核心是“流程即代码”即将业务流程和代码结构紧密耦合在一起。LiteFlow采用基于XML文件的流程定义方式通过定义流程节点和连线来描述整个工作流程。每个流程节点都对应着Java代码中的一个方法而连线则对应着方法之间的调用关系。这样一来我们就可以非常直观地看到整个业务流程的处理过程而且在修改流程时也更加方便快捷。 组件可实时热更替也可以给编排好的逻辑流里实时增加一个组件从而改变你的业务逻辑。 编排语法强大到可以编排出任何你想要的逻辑流程例如 三、使用场景 LiteFlow适用于哪些场景 LiteFlow适用于拥有复杂逻辑的业务比如说价格引擎下单流程等这些业务往往都拥有很多步骤这些步骤完全可以按照业务粒度拆分成一个个独立的组件进行装配复用变更。使用LiteFlow你会得到一个灵活度高扩展性很强的系统。因为组件之间相互独立也可以避免改一处而动全身的这样的风险。 LiteFlow不适用于哪些场景 LiteFlow不适合角色任务之间的流转类似于审批流A审批完应该是B审批然后再流转到C角色。这里申明下LiteFlow只做基于逻辑的流转而不做基于角色任务的流转。如果你想做基于角色任务的流转推荐使用flowableactiviti这2个框架。 四、JDK支持情况 LiteFlow要求的最低的JDK版本为8支持JDK8~JDK17所有的版本。如果你使用JDK11以上确保LiteFlow的版本为v2.10.6及其以上版本。因为LiteFlow从v2.10.6开始对JDK11和JDK17进行了详细的用例测试通过了全部的900多个测试用例。而在v2.10.6以下版本在JDK11以上是未经过测试用例保障的。特别需要注意的是如果你使用JDK11及其以上的版本请确保jvm参数加上以下参数 --add-opens java.base/sun.reflect.annotationALL-UNNAMED五、Springboot 整合流程 LiteFlow要求的Springboot的最低的版本是2.0。支持的范围是Springboot 2.X ~ Springboot 3.X。如果你使用了最新的Springboot 3.X相应的JDK版本也要切换为JDK17。 LiteFlow提供了liteflow-spring-boot-starter依赖包提供自动装配功能 dependencygroupIdcom.yomahub/groupIdartifactIdliteflow-spring-boot-starter/artifactIdversion2.11.3/version /dependency组件定义 在依赖了以上jar包后你需要定义并实现一些组件确保SpringBoot会扫描到这些组件并注册进上下文。 Component(a) public class ACmp extends NodeComponent {Overridepublic void process() {//do your business} }以此类推再分别定义b,c组件 Component(b) public class BCmp extends NodeComponent {Overridepublic void process() {//do your business} }Component(c) public class CCmp extends NodeComponent {Overridepublic void process() {//do your business} }SpringBoot配置文件 然后在你的SpringBoot的application.properties或者application.yml里添加配置(这里以yaml为例properties也是一样的) liteflow:#规则文件路径rule-source: config/flow.el.xml#-----------------以下非必须-----------------#liteflow是否开启默认为trueenable: true#liteflow的banner打印是否开启默认为trueprint-banner: true#zkNode的节点只有使用zk作为配置源的时候才起作用默认为/lite-flow/flowzk-node: /lite-flow/flow#上下文的最大数量槽默认值为1024slot-size: 1024#FlowExecutor的execute2Future的线程数默认为64main-executor-works: 64#FlowExecutor的execute2Future的自定义线程池BuilderLiteFlow提供了默认的Buildermain-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultMainExecutorBuilder#自定义请求ID的生成类LiteFlow提供了默认的生成类request-id-generator-class: com.yomahub.liteflow.flow.id.DefaultRequestIdGenerator#并行节点的线程池BuilderLiteFlow提供了默认的Builderthread-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultWhenExecutorBuilder#异步线程最长的等待时间(只用于when)默认值为15000when-max-wait-time: 15000#异步线程最长的等待时间(只用于when)默认值为MILLISECONDS毫秒when-max-wait-time-unit: MILLISECONDS#when节点全局异步线程池最大线程数默认为16when-max-workers: 16#并行循环子项线程池最大线程数默认为16parallelLoop-max-workers: 16#并行循环子项线程池等待队列数默认为512parallelLoop-queue-limit: 512#并行循环子项的线程池BuilderLiteFlow提供了默认的BuilderparallelLoop-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultParallelLoopExecutorBuilder#when节点全局异步线程池等待队列数默认为512when-queue-limit: 512#是否在启动的时候就解析规则默认为trueparse-on-start: true#全局重试次数默认为0retry-count: 0#是否支持不同类型的加载方式混用默认为falsesupport-multiple-type: false#全局默认节点执行器node-executor-class: com.yomahub.liteflow.flow.executor.DefaultNodeExecutor#是否打印执行中过程中的日志默认为trueprint-execution-log: true#是否开启本地文件监听默认为falseenable-monitor-file: false#简易监控配置选项monitor:#监控是否开启默认不开启enable-log: false#监控队列存储大小默认值为200queue-limit: 200#监控一开始延迟多少执行默认值为300000毫秒也就是5分钟delay: 300000#监控日志打印每过多少时间执行一次默认值为300000毫秒也就是5分钟period: 300000规则文件的定义 同时你得在resources下的config/flow.el.xml中定义规则SpringBoot在启动时会自动装载规则文件。 ?xml version1.0 encodingUTF-8? flowchain namechain1THEN(a, b, c);/chain /flow执行 声明启动类 SpringBootApplication //把你定义的组件扫入Spring上下文中 ComponentScan({com.xxx.xxx.cmp}) public class LiteflowExampleApplication {public static void main(String[] args) {SpringApplication.run(LiteflowExampleApplication.class, args);} }然后你就可以在Springboot任意被Spring托管的类中拿到flowExecutor进行执行链路这个DefaultContext是默认的上下文用户可以用最自己的任意Bean当做上下文传入如果需要传入自己的上下文则需要传用户Bean的Class属性 Component public class YourClass{Resourceprivate FlowExecutor flowExecutor;public void testConfig(){LiteflowResponse response flowExecutor.execute2Resp(chain1, arg);} }六、数据上下文 在执行器执行流程时会分配数据上下文实例给这个请求。不同请求的数据上下文实例是完全隔离的。里面存放着此请求所有的用户数据。不同的组件之间是不传递参数的所有的数据交互都是通过这个数据上下文来实现的。数据上下文这个概念在LiteFlow框架中非常重要你所有的业务数据都是放在数据上下文中。要做到可编排一定是消除每个组件差异性的。如果每个组件出参入参都不一致那就没法编排了。 LiteFlow对此有独特的设计理念平时我们写瀑布流的程序时A调用B那A一定要把B所需要的参数传递给B而在LiteFlow框架体系中每个组件的定义中是不需要接受参数的也无任何返回的。每个组件只需要从数据上下文中获取自己关心的数据即可而不用关心此数据是由谁提供的同样的每个组件也只要把自己执行所产生的结果数据放到数据上下文中即可也不用关心此数据到底是提供给谁用的。这样一来就从数据层面一定程度的解耦了。从而达到可编排的目的。关于这个理念也在LiteFlow简介中的设计原则有提到过给了一个形象的例子大家可以再去看看。 一旦在数据上下文中放入数据整个链路中的任一节点都是可以取到的。 默认上下文 LiteFlow提供了一个默认的数据上下文的实现DefaultContext。这个默认的实现其实里面主要存储数据的容器就是一个Map。你可以通过DefaultContext中的setData方法放入数据通过getData方法获得数据。 ::: warning DefaultContext虽然可以用但是在实际业务中用这个会存在大量的弱类型存取数据的时候都要进行强转颇为不方便。所以官方建议你自己去实现自己的数据上下文。 ::: 自定义上下文 在一个流程中总会有一些初始的参数比如订单号用户Id等等一些的初始参数。这时候需要通过以下方法的第二个参数传入 //参数为流程ID无初始流程入参上下文类型为默认的DefaultContext public LiteflowResponse execute2Resp(String chainId) //第一个参数为流程ID第二个参数为流程入参。上下文类型为默认的DefaultContext public LiteflowResponse execute2Resp(String chainId, Object param); //第一个参数为流程ID第二个参数为流程入参后面可以传入多个上下文class public LiteflowResponse execute2Resp(String chainId, Object param, Class?... contextBeanClazzArray) //第一个参数为流程ID第二个参数为流程入参后面可以传入多个上下文的Bean public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray)你可以用你自己的任意的Bean当做上下文进行传入。LiteFlow对上下文的Bean没有任何要求。自己定义的上下文实质上就是一个最简单的值对象自己定义的上下文因为是强类型更加贴合业务。你可以像这样进行传入 LiteflowResponse response flowExecutor.execute2Resp(chain1, 流程初始参数, CustomContext.class);传入之后LiteFlow会在调用时进行初始化给这个上下文分配唯一的实例。你在组件之中可以这样去获得这个上下文实例 LiteflowComponent(yourCmpId) public class YourCmp extends NodeComponent {Overridepublic void process() {CustomContext context this.getContextBean(CustomContext.class);//或者你也可以用这个方法去获取上下文实例和上面是等价的//CustomContext context this.getFirstContextBean();...} }多上下文 LiteFlow在新版本中支持了多上下文在执行的时候同时初始化你传入的多个上下文。在组件里也可以根据class类型很方便的拿到。你可以像这样进行传入 LiteflowResponse response flowExecutor.execute2Resp(chain1, 流程初始参数, OrderContext.class, UserContext.class, SignContext.class);在组件之中可以这样去获得这个上下文实例 LiteflowComponent(yourCmpId) public class YourCmp extends NodeComponent {Overridepublic void process() {OrderContext orderContext this.getContextBean(OrderContext.class);UserContext userContext this.getContextBean(UserContext.class);SignContext signContext this.getContextBean(SignContext.class);//如果你只想获取第一个上下文第一个上下文是OrderContext那么也可以用这个方法//OrderContext orderContext this.getFirstContextBean();...} }用初始化好的上下文传入 LiteFlow从2.8.4版本开始允许用户传入一个或多个已经初始化好的bean作为上下文而不是传入class对象。在拿到FlowExecutor之后你可以像如下一样传入已经初始化好的bean作为上下文当然也支持多上下文这里只演示单上下文 OrderContext orderContext new OrderContext(); orderContext.setOrderNo(SO11223344); LiteflowResponse response flowExecutor.execute2Resp(chain1, null, orderContext);::: warning 框架并不支持上下文bean和class混传你要么都传bean要么都传class。 ::: 七、异步Future public FutureLiteflowResponse execute2Future(String chainId, Object param, Class?... contextBeanClazzArray)如果调用这个方法那就是无阻塞的想要拿到response请用得到的future.get()就可以了。同时主执行器在这个模式下的线程数和线程池也可以自定义具体配置如下LiteFlow已经设置了预设值你也可自己定义。 liteflow.main-executor-works64 liteflow.main-executor-classcom.yomahub.liteflow.thread.LiteFlowDefaultMainExecutorBuilder如果你定义了自定义线程池你需新建一个类然后实现ExecutorBuilder接口 public class CustomThreadBuilder implements ExecutorBuilder {Overridepublic ExecutorService buildExecutor() {return Executors.newCachedThreadPool();} }八、规则写法 串行编排 如果你要依次执行a,b,c,d四个组件你可以用THEN关键字需要注意的是THEN必须大写。 chain namechain1THEN(a, b, c, d); /chain并行编排 如果你要并行执行a,b,c三个组件你可以用WHEN关键字需要注意的是WHEN必须大写。 chain namechain1WHEN(a, b, c); /chain和串行嵌套起来让我们把THEN和WHEN结合起来用看一个示例b,c,d默认并行都执行完毕后才会执行e。 chain namechain1THEN(a,WHEN(b, c, d),e); /chain上面的示例应该很好理解吧那么再看一个示例 chain namechain1THEN(a,WHEN(b, THEN(c, d)),e); /chain忽略错误 WHEN关键字提供了一个子关键字ignoreError(默认为false)来提供忽略错误的特性用法如下 chain namechain1THEN(a,WHEN(b, c, d).ignoreError(true),e); /chain设b,c,d中任一一个节点有异常那么最终e仍旧会被执行。 任一节点先执行完则忽略其他 WHEN关键字提供了一个子关键字any(默认为false)用来提供并行流程中任一条分支先执行完即忽略其他分支继续执行的特性。用法如下假设e节点先执行完那么不管其他分支是否执行完会立马执行节点f。 chain namechain1THEN(a,WHEN(b, THEN(c, d), e).any(true),f); /chain指定任意节点先执行完则忽略其他 LiteFlow从v2.11.1开始支持了并行编排中指定节点的执行则忽略其他WHEN关键字新增子关键字must(不可为空)可用于指定需等待执行的任意节点可以为1个或者多个若指定的所有节点率先完成则继续往下执行忽略同级别的其他任务用法如下must指定了b,c则bc是一定会被执行完毕了如果bc执行完毕了后d还未执行完则忽略直接执行下一个组件f。 chain namechain1THEN(a,WHEN(b, c, d).must(b, c),f); /chain以上是单节点的用法must还可以指定一个或多个表达式。比如WHEN里有一个嵌套的THEN如果需要指定这个表达式则需要给这个表达式设置一个idmust里需要指定这个id需要注意的是must里指定id需要用引号括起来。 chain namechain1THEN(a,WHEN(b, THEN(c, d).id(t1), e).must(b, t1),f); /chain开启WHEN线程池隔离 目前liteflow设计里when线程池如果你不单独设置自定义线程池那么就会用默认的线程池。而这个线程池是所有的when共同一个。LiteFlow从2.11.1开始提供一个liteflow.when-thread-pool-isolate参数默认为false如果设为true则会开启WHEN的线程池隔离机制这意味着每一个when都会有单独的线程池。这个特性对于运行复杂的嵌套when时是可以提升运行速度的且规避掉一些锁的问题。 你可以如下配置来开启 liteflow.when-thread-pool-isolatetrue选择编排 我们在写业务逻辑的时候通常会碰到选择性问题即如果返回结果1则进入A流程如果返回结果2则进入B流程如果返回结果3则进入C流程。在有些流程定义中也被定义为排他网关。这个通过LiteFLow的表达式也非常容易实现你可以用SWITCH...TO的组合关键字注意的是SWITCH必须大写to大小写均可。 如果根据组件a来选择执行b,c,d中的一个你可以如下声明 LiteflowComponent(a) public class ACmp extends NodeSwitchComponent {Overridepublic String processSwitch() throws Exception {System.out.println(Acomp executed!);return c;} }DEFAULT关键字 LiteFlow从2.9.5开始对选择编排新增了一个DEFAULT关键字。用法为SWITCH...TO...DEFAULT。比如如下表达式 chain namechain1SWITCH(x).TO(a, b, c).DEFAULT(y); /chain如上表达式的x如果返回非a,b,c中的一个则默认选择到y。当然DEFAULT里面也可以是一个表达式。 选择编排中的id语法 接下来展示一个SWITCH中套THEN和WHEN的例子。如果你阅读过选择组件这一章就应该知道LiteFlow通过选择组件的返回来确定该选择什么。那么如果SWITCH中套一个THEN那么选择组件如果要选择这个THEN应该返回什么呢LiteFlow中规定每个表达式都可以有一个id值你可以设置id值来设置一个表达式的id值。然后在选择组件里返回这个id即可。用法如下 chain namechain1THEN(a,SWITCH(b).to(c, THEN(d, e).id(t1)),f); /chain如果你想选择THEN这个表达式那么你可以在选择节点里返回t1: LiteflowComponent(b) public class BCmp extends NodeSwitchComponent {Overridepublic String processSwitch() throws Exception {//do your bizreturn t1;} }选择编排中的tag语法 事实上除了给表达式赋值id属性之外你还可以给表达式赋值tag属性。用法如下 chain namechain1THEN(a,SWITCH(b).to(c, THEN(d, e).tag(t1)),f); /chain如果你想选择THEN这个表达式那么你可以在选择节点里返回: LiteflowComponent(b) public class BCmp extends NodeSwitchComponent {Overridepublic String processSwitch() throws Exception {return tag:t1;//以下这种也是可以的return :t1;} }条件编排 条件编排是选择编排一个变种选择编排是根据逻辑去选择多个子项中的一项。而条件编排只有真和假2个子项这处理某些业务的过程中非常有用。其实简单来说条件编排就是编程语言中的if else。只不过在LiteFlow EL语法中有一些不一样的用法。以下IF和ELIF的第一个参数要求定义条件组件。 IF的二元表达式 其中x为条件节点为真的情况下执行链路就为x-a-b为假链路就为x-b。 chain namechain1THEN(IF(x, a),b); /chainComponent(x) public class XCmp extends NodeIfComponent {Overridepublic boolean processIf() throws Exception {//do your bizreturn true;} }IF的三元表达式 其中x为条件节点为真的情况下执行链路就为x-a-c为假链路就为x-b-c。 chain namechain1THEN(IF(x, a, b),c); /chainELIF表达式 ELIF关键字的用法其实和java语言的else if类似可以跟多个和IF二元表达式参数一样一般最后还会跟个ELSE用于多重条件的判断 chain namechain1IF(x1, a).ELIF(x2, b).ELIF(x3, c).ELIF(x4, d).ELSE(THEN(m, n)); /chain循环编排 FOR循环 FOR循环表达式用于固定次数的循环通常的用法为 chain namechain1FOR(5).DO(THEN(a, b)); /chain上述表达式表示把a-b这个链路固定循环了5次。如果你在定义规则的时候并不确定要循环几次要在代码运行的时候才知道。那你也可以这样定义 chain namechain1FOR(f).DO(THEN(a, b)); /chain其中f这个节点需要为次数循环组件返回一个int循环次数f节点的定义需要继承NodeForComponent需要实现processFor方法 LiteflowComponent(f) public class FCmp extends NodeForComponent {Overridepublic int processFor() throws Exception {//这里根据业务去返回for的结果} }循环下标获取关键字FOR...DO...中DO里面的任意java组件都可以通过this.getLoopIndex()来获得下标。在脚本中通过_meta.loopIndex来获取。 WHILE循环 chain namechain1WHILE(w).DO(THEN(a, b)); /chain其中w这个节点需要为条件循环组件返回一个布尔值为true则继续循环 LiteflowComponent(w) public class WCmp extends NodeWhileComponent {Overridepublic boolean processWhile() throws Exception {//这里根据业务去返回while的结果} }循环下标获取关键字WHILE...DO...中DO里面的任意节点都可以通过this.getLoopIndex()来获得下标。在脚本中通过_meta.loopIndex来获取。 ITERATOR迭代循环 chain namechain1ITERATOR(x).DO(THEN(a, b)); /chain其中x这个节点需要为迭代循环组件返回一个迭代器x节点的定义需要继承NodeIteratorComponent需要实现processIterator方法 LiteflowComponent(x) public class XCmp extends NodeIteratorComponent {Overridepublic Iterator? processIterator() throws Exception {ListString list ListUtil.toList(jack, mary, tom);return list.iterator();} }BREAK LiteFlow同样也支持BREAK语法代表退出循环。BREAK关键字可以跟在FOR和WHILE后面通常用法为 chain namechain1FOR(f).DO(THEN(a, b)).BREAK(c); /chainchain namechain1WHILE(w).DO(THEN(a, b)).BREAK(c); /chain其中c这个节点需要为退出循环组件返回一个布尔值为true则退出循环。c节点的定义需要继承NodeBreakComponent需要实现processBreak方法 LiteflowComponent(c) public class CCmp extends NodeBreakComponent {Overridepublic boolean processBreak() throws Exception {//这里根据业务去返回break的结果} }BREAK关键字是在每次循环的末尾进行判断的。
http://www.huolong8.cn/news/51030/

相关文章:

  • 网站建设这个工作怎么样贺州市八步区乡镇建设局网站
  • 德尔普网站建设北京市保障性住建设投资中心网站
  • 网站中的搜索框怎么做哪里有软件开发培训机构
  • xp网站建设网店装修图
  • 百度如何网站淘宝运营培训班
  • 房地产网站模版唐山正规做网站的公司
  • 大连 网站开发500个企点qq大概多少钱
  • 网站logo上传个人博客页面
  • 一般网站后台地址网站的四大要素
  • 石油网站建设价格唐山公司网站制作
  • 网站主要内容包括什么白云区做网站公司
  • 苏州公司建站成都软件培训机构排名前十
  • 阿里巴巴的电子商务网站建设辽宁建设安装集团有限公司网站
  • 西宁网站优化百度站长工具app
  • 微信手机官方网站常州推广网络营销多少钱
  • 竞猜网站开发多少钱惠州做网站多少钱
  • 外包网站开发合同范本春哥seo博客
  • 购物网站后台订单处理流程东莞网络营销班
  • 大学生做爰网站阜阳讯拓网站建设
  • 青海省建设厅官方网站建设云现在网站还用asp做
  • 盘锦门户网站制作做网站的项目实施方案
  • 免费建微网站2023年又开始封控了吗
  • 自适应网站制作公司什么是html5网站
  • 无刷新网站太原seo关键词优化
  • 中小企业网站开发河南关键词seo
  • 游戏网站建设系统介绍温州seo平台
  • 此网站域名即将过期淘宝网站做多久
  • python做网站的多吗调研报告万能模板
  • 网站保护等级是企业必须做的么陕西政务服务网
  • 黑色门户网站源码在国外做黄皮网站违法么