34 Star 355 Fork 122

lykan / kstry-core

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

    🟢 项目主页

    🟢 Kstry 使用场景

    🟢 Kstry 概念介绍

    🟢 Kstry 使用文档

    🟢 Kstry 流程配置台

    🟢 Kstry 使用demo

    🟢 功能测试

为什么说Kstry是业务架构首选框架?

Kstry有如下使用场景:

一、流程编排

Kstry可以将原本存在于代码中错综复杂的方法调用关系以可视化流程图的形式更直观的展示出来。框架可以隔离各个业务模型的独自演进过程并屏蔽期间的相互影响,与此同时还提供了模型与模型间关系的动态化编排机制

流程编排演示

流程编排演示代码地址

支持动态化流程编排

二、并发框架

框架中可以通过配置open-async=true属性使并行网关、包含网关后面的流程并发执行。可以仅仅将Kstry作为并发框架应用在项目中,其提供的灵活性高于CompletableFuture

需要实现的异步流程图:

异步流程

代码方式定义上述流程:

@Bean
public ProcessLink testAsyncFlowProcess() {
    StartProcessLink processLink = StartProcessLink.build(ProcessConfig::testAsyncFlowProcess);
    InclusiveJoinPoint inclusive01 = processLink
            .nextService(CalculateService::atomicInc).name("Task01").build()
            .nextInclusive(processLink.inclusive().openAsync().build());
    InclusiveJoinPoint inclusive04 = processLink
            .nextService(CalculateService::atomicInc).name("Task04").build()
            .nextInclusive(processLink.inclusive().openAsync().build());

    processLink.inclusive().build().joinLinks(
                    inclusive01.nextService(CalculateService::atomicInc).name("Task02").build(),
                    processLink.inclusive().build().joinLinks(
                            inclusive01.nextService(CalculateService::atomicInc).name("Task03").build(),
                            inclusive04.nextService(CalculateService::atomicInc).name("Task05").build()
                    ).nextService(CalculateService::atomicInc).name("Task07").build(),
                    inclusive04.nextService(CalculateService::atomicInc).name("Task06").build()
            ).nextService(CalculateService::atomicInc).name("Task08").build()
            .end();
    return processLink;
}

服务节点方法定义:

@TaskService
public void atomicInc(@ReqTaskParam(reqSelf = true) AtomicInteger atomicInteger) {
    int i = atomicInteger.incrementAndGet();
    System.out.println("atomicInc... " + i);
    try {
        TimeUnit.MILLISECONDS.sleep(200);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

流程执行:

@Test
public void testAsyncFlowDemo() {
    // 配置文件调用
    AtomicInteger atomicInteger = new AtomicInteger();
    StoryRequest<Void> fireRequest = ReqBuilder.returnType(Void.class)
            .trackingType(TrackingTypeEnum.SERVICE_DETAIL).request(atomicInteger).startId("async-flow-demo")
            .build();
    TaskResponse<Void> result = storyEngine.fire(fireRequest);
    Assert.assertTrue(result.isSuccess());
    Assert.assertEquals(8, atomicInteger.intValue());

    // 代码流程调用
    atomicInteger = new AtomicInteger();
    fireRequest = ReqBuilder.returnType(Void.class)
            .trackingType(TrackingTypeEnum.SERVICE_DETAIL).request(atomicInteger).startProcess(ProcessConfig::testAsyncFlowProcess)
            .build();
    result = storyEngine.fire(fireRequest);
    Assert.assertTrue(result.isSuccess());
    Assert.assertEquals(8, atomicInteger.intValue());
}

testAsyncFlowProcess

如上所示的流程执行顺序如下:

    🟢 Task01执行完之后并发执行Task02、Task03

    🟢 Task04执行完之后并发执行Task05、Task06

    🟢 Task07将等待Task03和Task05都完成后执行

    🟢 当Task02、Task06、Task07都执行完之后再执行Task08

    🟢 最后结束流程

三、规则判断

可以使用框架提供的服务节点、网关、带判断条件的有向线段等组件配置规则流程图,再有动态流程能力的支持,完全可以实现一套动态化的规则判断

AND逻辑:

AND逻辑

节点方法定义及执行:

// 节点方法定义
@NoticeResult
@TaskService
public int plusCalculate(@VarTaskParam int a, @VarTaskParam int b) {
    return a + b;
}

// 执行
@Test
public void testRuleAndFlowDemo() {
    RuleJudgeRequest ruleJudgeRequest = new RuleJudgeRequest();
    ruleJudgeRequest.setA(10);
    ruleJudgeRequest.setB(5);
    ruleJudgeRequest.setC(15);
    StoryRequest<Integer> fireRequest = ReqBuilder.returnType(Integer.class)
            .trackingType(TrackingTypeEnum.SERVICE_DETAIL).varScopeData(ruleJudgeRequest).startId("test-rule-and-flow-demo")
            .build();
    TaskResponse<Integer> result = storyEngine.fire(fireRequest);
    Assert.assertTrue(result.isSuccess());
    Assert.assertEquals(15, (int) result.getResult());
}

testRuleAndFlowDemo

    🟢 判断多个条件都满足时候执行目标动作,否则结束流程

OR逻辑:

OR逻辑

testRuleElseIfFlowDemo

    🟢 排他网关有多个出度表达式被解析成true时,会选择第一个为true的分支继续向下执行,其他的将会被忽略,所以后面出度只要加上判断表达式便可以实现OR的逻辑

    🟢 可以结合o{数字}: 表达式格式定义表达式的同时指定后面出度的判断顺序,从而实现if... else if... else if... else...的逻辑

    🟢 也可以实现多个前置条件均未满足时执行默认逻辑的流程,这个可以理解成是NONE的语法

满足N个条件时继续:

满足N个条件

testRuleCompletedCountFlowDemo

    🟢 可以使用包含网关来配置当网关入度任务执行完成几个时流程便可以继续向下执行。指定的数量应该大于0且小于等于网关入度任务数

四、微服务整合

利用框架本身的编排能力再结合RPC、HTTP等客户端可以实现对微服务能力的编排。结合StoryBus和其上的task-params属性、自定义指令、类型转换器等可以轻松实现服务间的参数传递与转换

以HTTP为例可以先实现一个服务节点方法:

@TaskService(name = "http-post")
public void httpPostAction(ScopeDataOperator dataOperator,
                           @TaskParam("url") String url,
                           @TaskParam("result") ResultProperty result,
                           @TaskParam("data") Map<String, Object> data,
                           @TaskParam("header") Map<String, String> header) {
    if (StringUtils.isBlank(url)) {
        return;
    }
    try {
        HttpPost httpPost = new HttpPost(url);
        if (data == null) {
            data = Maps.newHashMap();
        }
        httpPost.setEntity(new StringEntity(JSON.toJSONString(data), ContentType.APPLICATION_JSON));
        if (MapUtils.isNotEmpty(header)) {
            header.forEach((k, v) -> {
                if (StringUtils.isAnyBlank(k, v)) {
                    return;
                }
                httpPost.setHeader(k, v);
            });
        }
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String r = EntityUtils.toString(response.getEntity());
        log.info("HttpActionService httpPostAction success. url: {}, header: {}, data: {}, response: {}, result: {}", url, header, data, response, r);
        if (StringUtils.isBlank(r)) {
            return;
        }
        if (result == null) {
            return;
        }
        noticeResult(dataOperator, result, r);
    } catch (Exception e) {
        log.error("HttpActionService httpPostAction error. url: {}, header: {}, data: {}", url, header, data, e);
        throw new BusinessException("-100", e.getMessage(), e);
    }
}

private void noticeResult(ScopeDataOperator dataOperator, ResultProperty resultProperty, String result) {
    if (StringUtils.isBlank(resultProperty.getTarget()) || !ElementParserUtil.isValidDataExpression(resultProperty.getTarget())) {
        return;
    }
    JSONObject jsonObject = JSON.parseObject(result);
    Object resData = jsonObject.get("data");
    if (resData != null) {
        jsonObject.put("data", typeConverterProcessor.convert(resultProperty.getConverter(), resData, Optional.ofNullable(resultProperty.getType())
                        .filter(StringUtils::isNotBlank).map(className -> {
                            try {
                                return Class.forName(className);
                            } catch (Exception e) {
                                log.error("HttpActionService convert. type invalid. type: {}", className, e);
                            }
                            return null;
                        }).orElse(null)
                ).getValue()
        );
    }
    dataOperator.setData(resultProperty.getTarget(), jsonObject);
}

@Data
public class ResultProperty {

    private String target; // 指定返回结果通知到StoryBus中的什么位置

    private String converter; // 指定类型转换器

    private String type; // 指定结果类型
}

HttpActionService

编排微服务流程:

配置微服务流程

    🟢 流程中首先会执行登录操作

{
    "url": "http://127.0.0.1:8787/login", // 访问的URL
    "result": {
        "target": "var.login", // 结果通知位置
        "type": "java.util.HashMap" // 返回结果类型
    },
    "data": {
        "username": "admin", // POST请求体数据,可以常量也可以变量
        "password": "admin"
    }
}

    🟢 第二步做资源查询

{
    "url": "http://127.0.0.1:8787/queryStudent",
    "result": {
        "target": "var.student",
        "converter": "map-to-student" // 查询到的结果使用类型转换器转换成Student对象
    },
    "header": {
        "Authorization": "@var.login.data.token" // 从登录结果中拿到token放到header中用来鉴权
    },
    "data": {
        "id": "@req.id"
    }
}

将上面流程编排中查询学生分数信息的流程用微服务编排来实现:

分布式分数查询

    🟢 如果有需要可以结合子流程拦截器实现自定义的分布式事务

    🟢 框架还支持Reactor方式的服务节点方法定义,结合asyncHttpClient可以做到发送请求后立即释放工作线程,等请求收到响应触发回调任务后再驱动流程继续向后执行

@TaskService(name = "async-http-post")
public Mono<Void> asyncHttpPostAction(ScopeDataOperator dataOperator,
                                      @TaskParam("url") String url,
                                      @TaskParam("result") ResultProperty result,
                                      @TaskParam("data") Map<String, Object> data,
                                      @TaskParam("header") Map<String, String> header) {
    if (StringUtils.isBlank(url)) {
        return Mono.empty();
    }
    try {
        SimpleRequestBuilder requestBuilder = SimpleRequestBuilder.post(url);
        if (MapUtils.isNotEmpty(header)) {
            header.forEach((k, v) -> {
                if (StringUtils.isAnyBlank(k, v)) {
                    return;
                }
                requestBuilder.setHeader(k, v);
            });
        }
        if (data == null) {
            data = Maps.newHashMap();
        }
        requestBuilder.setBody(JSON.toJSONString(data), ContentType.APPLICATION_JSON);
        SimpleHttpRequest request = requestBuilder.build();

        Pair<CompletableFuture<SimpleHttpResponse>, FutureCallback<SimpleHttpResponse>> futureCallbackPair = getFutureCallbackPair();
        asyncHttpClient.execute(request, futureCallbackPair.getValue());
        return Mono.fromFuture(futureCallbackPair.getKey()).mapNotNull(response -> {
            String r = null;
            try {
                r = response.getBodyText();
                log.info("HttpActionService async httpPostAction success. url: {}, header: {}, data: {}, response: {}, result: {}", url, header, request.getBody().getBodyText(), response, r);
                if (StringUtils.isBlank(r)) {
                    return null;
                }
                noticeResult(dataOperator, result, r);
                return null;
            } catch (Exception e) {
                log.error("HttpActionService async httpPostAction error. url: {}, header: {}, data: {}, response: {}, result: {}", url, header, request.getBody().getBodyText(), response, r);
                throw new RuntimeException(e);
            }
        });
    } catch (Exception e) {
        log.error("HttpActionService async httpPostAction error. url: {}, header: {}, data: {}", url, header, data, e);
        throw new BusinessException("-100", e.getMessage(), e);
    }
}

五、微服务动态判断

规则引擎加微服务调用可以作为微服务动态判断使用

一个决策是否要去上学的例子:

SOP流程图

     无需定义服务节点,在排他网关上配置前置指令^c-async-http-post,就会在网关执行前进行HTTP接口调用,示例中一共发送了三次请求,task-params依次配置和请求日志如下:

{
    "url": "http://127.0.0.1:8787/askWeek",
    "result": {
        "target": "var.askWeek"
    },
    "data": {
        
    }
}
// HttpActionService async httpPostAction success. url: http://127.0.0.1:8787/askWeek, header: null, data: {}, response: 200 null HTTP/1.1, result: {"success":true,"code":0,"msg":"success","data":5}

{
    "url": "http://127.0.0.1:8787/askRain",
    "result": {
        "target": "var.askRain"
    },
    "data": {
        
    }
}
// HttpActionService async httpPostAction success. url: http://127.0.0.1:8787/askRain, header: null, data: {}, response: 200 null HTTP/1.1, result: {"success":true,"code":0,"msg":"success","data":false}

{
    "url": "http://127.0.0.1:8787/askHungry",
    "result": {
        "target": "var.askHungry"
    },
    "data": {
        
    }
}
// HttpActionService async httpPostAction success. url: http://127.0.0.1:8787/askHungry, header: null, data: {}, response: 200 null HTTP/1.1, result: {"success":true,"code":0,"msg":"success","data":false}

     比如在判断“饿不饿”时,就可以使用条件表达式,var.askHungry.data(是)、!var.askHungry.data(否)来判断和决策

     “去上学”服务节点的task-params属性配置:

{
    "askWeek":"@var.askWeek.data",
    "askRain":"@var.askRain.data",
    "askHungry":"@var.askHungry.data"
}

     “去上学”服务节点方法定义及日志

@NoticeResult
@TaskService
public boolean gotoSchool(int askWeek, boolean askRain, boolean askHungry) {
    log.info("gotoSchool. askWeek: {}, askRain: {}, askHungry: {}", askWeek, askRain, askHungry);
    return true;
}
// gotoSchool. askWeek: 5, askRain: false, askHungry: false

askGotoSchool

六、数据字典

任何系统中,前端界面无可避免都有展示数据的诉求,在权限允许的情况下,Kstry可以做到通过零编码纯配置的方式查询并返回给前端整个微服务架构中任意服务的指定结果字段

沿用上面微服务整合中查询学生分数的例子

分布式分数查询

     接口实际返回的结果如下:

{
    "success": true,
    "code": 0,
    "msg": "success",
    "data": {
        "student": {
            "id": 66,
            "name": "张一",
            "address": "XX省XX市XX区",
            "idCard": "133133199401012345",
            "birthday": "1994-01-01"
        },
        "scoreInfos": [
            {
                "score": 99,
                "studentId": 66,
                "studyYear": "2013-1-2",
                "course": "语文",
                "classId": 1,
                "classInfo": {
                    "id": 1,
                    "name": "班级1"
                }
            },
            {
                "score": 88,
                "studentId": 66,
                "studyYear": "2013-1-2",
                "course": "数学",
                "classId": 1,
                "classInfo": {
                    "id": 1,
                    "name": "班级1"
                }
            },
            // 此处省略...
        ]
    }
}

     流程配置无需改动,只变更调用方式:

@PostMapping("/scoreQuery5")
public Mono<R<Map<String, Object>>> scoreQuery5(@RequestBody List<String> keys) {
    QueryScoreRequest request = new QueryScoreRequest();
    request.setStudentId(77L);
    request.setNeedScore(true);
    StoryRequest<Map<String, Object>> fireRequest = ReqBuilder.<Map<String, Object>>resultType(Map.class)
            .recallStoryHook(WebUtil::recallStoryHook).trackingType(TrackingTypeEnum.SERVICE_DETAIL).request(request).startId("http-student-score-query-process")
            .resultBuilder((res, query) -> {
                Map<String, Object> map = Maps.newHashMap();
                keys.forEach(key -> map.put(key, query.getData(key)));
                return map;
            }).build();
    Mono<Map<String, Object>> fireAsync = storyEngine.fireAsync(fireRequest);
    return WebUtil.dataDecorate(request, fireAsync);
}

scoreQuery5

    🟢 resultBuilder是流程执行完成之后,允许对结果进行加工处理的回调函数。其中有两个参数:

        🔷 res: 流程中实际返回的结果

        🔷 query: ScopeDataOperator对象

     如上的改造后,客户端就可以获取指定的数据结果了:

获取指定的数据结果

七、平台能力

框架提供的平台型能力可以根据请求信息或其他定义装载有不同权限的角色,使用不同角色请求同一流程时,提供的实际服务可以做到千人千面

定义通用流程

RBAC流程

定义服务节点方法

@TaskComponent(name = "orderService")
public class InnerOrderService implements OrderService {

    @Override
    @TaskService
    @NoticeVar(target = CommonFields.F.price)
    public long calculatePrice(long goodsId) {
        System.out.println("InnerOrderService calculatePrice...");
        return 100L;
    }

    @Override
    @TaskService
    @NoticeVar(target = CommonFields.F.lockStockResult)
    public boolean lockStock(long goodsId) {
        System.out.println("InnerOrderService lockStock...");
        return true;
    }

    @Override
    @NoticeResult
    @TaskService
    public long geneOrderId(long price, long goodsId) {
        System.out.println("InnerOrderService geneOrderId...");
        return 2987;
    }
}

普通执行方式

@Test
public void testRbacFlowDemo() {
    InScopeData varScopeData = new InScopeData(ScopeTypeEnum.VARIABLE);
    varScopeData.put(CommonFields.F.goodsId, 10);

    StoryRequest<Long> fireRequest = ReqBuilder.returnType(Long.class)
            .trackingType(TrackingTypeEnum.SERVICE_DETAIL).varScopeData(varScopeData).startId("test-rbac-flow-demo")
            .build();
    TaskResponse<Long> result = storyEngine.fire(fireRequest);
    Assert.assertTrue(result.isSuccess());
    Assert.assertEquals(100L, varScopeData.get(CommonFields.F.price));
    Assert.assertEquals(2987L, (long) result.getResult());
}

// 日志打印
// InnerOrderService calculatePrice...
// InnerOrderService lockStock...
// InnerOrderService geneOrderId...

testRbacFlowDemo

定义扩展能力点

@TaskComponent(name = "orderService", scanSuper = false)
public class ExternalOrderService extends InnerOrderService {

    @Override
    @NoticeVar(target = CommonFields.F.price)
    @TaskService(ability = "external")
    public long calculatePrice(long goodsId) {
        System.out.println("ExternalOrderService calculatePrice...");
        return 200L;
    }

    @Override
    @NoticeVar(target = CommonFields.F.lockStockResult)
    @TaskService(ability = "external")
    public boolean lockStock(long goodsId) {
        System.out.println("ExternalOrderService lockStock...");
        return false;
    }
}

    🟢 @TaskService中的ability = "external"代表为calculatePrice服务节点新增external的扩展点

定义角色并分配权限

@Component
public class AllocateRoleConfig implements DynamicRole {

    @Override
    public Optional<Role> getRole(String key) {
        if (Objects.equals("test-rbac-flow-demo@external-business-id", key)) {
            ServiceTaskRole serviceTaskRole = new ServiceTaskRole();
            serviceTaskRole.addPermission(PermissionUtil.permissionList("r:calculatePrice@external, r:lockStock@external"));
            return Optional.of(serviceTaskRole);
        }
        return Optional.empty();
    }
}

    🟢 创建实例实现DynamicRole接口并交给Spring容器管理,便可以实现角色动态分配器

    🟢 请求处理时如果startId和businessId都能匹配上,之后会创建带有自定义权限的角色

匹配角色执行扩展业务

@Test
public void testRbacFlowDemo() {
    InScopeData varScopeData = new InScopeData(ScopeTypeEnum.VARIABLE);
    varScopeData.put(CommonFields.F.goodsId, 10);

    StoryRequest<Long> fireRequest = ReqBuilder.returnType(Long.class)
            .businessId("external-business-id")
            .trackingType(TrackingTypeEnum.SERVICE_DETAIL).varScopeData(varScopeData).startId("test-rbac-flow-demo")
            .build();
    TaskResponse<Long> result = storyEngine.fire(fireRequest);
    Assert.assertTrue(result.isSuccess());
    Assert.assertEquals(200L, varScopeData.get(CommonFields.F.price));
    Assert.assertEquals(2987L, (long) result.getResult());
}
// 日志打印
// ExternalOrderService calculatePrice...
// ExternalOrderService lockStock...
// InnerOrderService geneOrderId...

执行服务节点时,如果根据角色匹配到了扩展业务会跳过默认业务点直接执行扩展业务,如果未匹配到时可以默认节点兜底

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

Kstry业务架构首选。具有可视化、业务隔离、轻量级框架等特点。使用场景是流程编排、并发编程、规则判断、微服务整合、微服务动态判断、数据字典、平台能力建设等。项目主页:http://kstry.cn 展开 收起
Java 等 5 种语言
Apache-2.0
取消

发行版 (17)

全部

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/kstry/kstry-core.git
git@gitee.com:kstry/kstry-core.git
kstry
kstry-core
kstry-core
master

搜索帮助