├── .gitignore ├── DDL.sql ├── LICENSE ├── README.md ├── balance-mng ├── balance-mng-bootstrap │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── io │ │ │ └── sofastack │ │ │ └── balance │ │ │ └── manage │ │ │ └── BalanceMngApplication.java │ │ └── resources │ │ └── application.properties ├── balance-mng-facade │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── sofastack │ │ └── balance │ │ └── manage │ │ ├── facade │ │ └── BalanceMngFacade.java │ │ └── model │ │ └── Balance.java ├── balance-mng-impl │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── sofastack │ │ └── balance │ │ └── manage │ │ ├── impl │ │ └── BalanceMngImpl.java │ │ └── mapper │ │ └── BalanceMngMapper.java └── pom.xml ├── mvnw ├── mvnw.cmd ├── pom.xml └── stock-mng ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src └── main ├── java └── io │ └── sofastack │ └── stockmng │ ├── StockMngApplication.java │ ├── controller │ ├── BookStoreController.java │ └── impl │ │ └── BookStoreControllerImpl.java │ ├── facade │ └── StockMngFacade.java │ ├── impl │ ├── DatabaseSeed.java │ └── StockMngImpl.java │ ├── mapper │ └── StockMngMapper.java │ └── model │ ├── BalanceResponse.java │ ├── ProductInfo.java │ └── Success.java └── resources ├── application.properties └── static ├── index.html └── static ├── css ├── 2.06645d11.chunk.css ├── 2.06645d11.chunk.css.map ├── main.5e80f7fb.chunk.css └── main.5e80f7fb.chunk.css.map └── js ├── 2.9c512073.chunk.js ├── 2.9c512073.chunk.js.map ├── main.b36ca635.chunk.js ├── main.b36ca635.chunk.js.map ├── runtime~main.a8a9905a.js └── runtime~main.a8a9905a.js.map /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | bin 3 | bak 4 | .pmd 5 | .project 6 | .settings 7 | .classpath 8 | .idea.xml 9 | .idea 10 | *.class 11 | *.bak 12 | *.iml 13 | *.ipr 14 | *.iws 15 | .DS_Store 16 | nb-configuration.xml 17 | coverage-report 18 | logs 19 | *.log -------------------------------------------------------------------------------- /DDL.sql: -------------------------------------------------------------------------------- 1 | # ========================== 2 | # balance db 3 | CREATE DATABASE balance_db; 4 | USE balance_db; 5 | DROP TABLE IF EXISTS `balance_tb`; 6 | CREATE TABLE `balance_tb` 7 | ( 8 | `id` int(11) NOT NULL AUTO_INCREMENT, 9 | `user_name` varchar(255) DEFAULT NULL, 10 | `balance` decimal(16, 2) DEFAULT 0.00, 11 | PRIMARY KEY (`id`), 12 | UNIQUE KEY (`user_name`) 13 | ) ENGINE = InnoDB 14 | DEFAULT CHARSET = utf8; 15 | 16 | # ========================== 17 | # stock db 18 | CREATE database stock_db; 19 | USE stock_db; 20 | DROP TABLE IF EXISTS `stock_tb`; 21 | CREATE TABLE `stock_tb` 22 | ( 23 | `id` int(11) NOT NULL AUTO_INCREMENT, 24 | `product_code` varchar(255) DEFAULT NULL, 25 | `name` varchar(255) DEFAULT NULL, 26 | `description` text, 27 | `price` decimal(10, 2) DEFAULT 0.00, 28 | `count` int(11) DEFAULT 0, 29 | `user_name` varchar(255) DEFAULT NULL, 30 | PRIMARY KEY (`id`), 31 | UNIQUE KEY (`id`, `user_name`) 32 | ) ENGINE = InnoDB 33 | DEFAULT CHARSET = utf8; 34 | 35 | DROP TABLE IF EXISTS `order_tb`; 36 | CREATE TABLE `order_tb` 37 | ( 38 | `id` int(11) NOT NULL AUTO_INCREMENT, 39 | `user_name` varchar(255) DEFAULT NULL, 40 | `product_code` varchar(255) DEFAULT NULL, 41 | `count` int(11) DEFAULT 0, 42 | PRIMARY KEY (`id`) 43 | ) ENGINE = InnoDB 44 | DEFAULT CHARSET = utf8; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 使用 SOFAStack 快速构建微服务 2 | 3 | ## 前置条件 4 | 注意:您需要自行部署后端环境依赖,并修改示例中的服务依赖地址即可使用。 5 | 6 | - [必选]部署注册中心:https://www.sofastack.tech/projects/sofa-registry/server-quick-start/ 7 | - [必须]部署数据库:本地自行搭建数据库,然后导入 [DDL.sql](https://github.com/sofastack-guides/kc-sofastack-demo/blob/master/DDL.sql) 8 | - [可选]部署LookoutServer:https://www.sofastack.tech/projects/sofa-lookout/quick-start-metrics-server/ 9 | - [可选]部署Zipkin:https://zipkin.io/pages/quickstart.html 10 | 11 | ## 实验内容 12 | 13 | 本实验基于 SOFAStack 快速构建一个微服务,主要包括以下几个部分: 14 | 15 | * 使用 SOFABoot + SOFARPC 发布服务 16 | * 使用 SOFABoot + SOFARPC 调用服务 17 | * 通过 ZipKin 查看 SOFATracer 上报的 Tracer 信息 18 | * 通过 SOFALookout 查看上报的 Metrics 信息 19 | 20 | ## 架构图 21 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*FiVrSoXTfsAAAAAAAAAAAABkARQnAQ) 22 | 23 | ## 任务 24 | 25 | #### 1、任务准备 26 | 27 | 从 github 上将 demo 工程克隆到本地 28 | ```bash 29 | git clone https://github.com/sofastack-guides/kc-sofastack-demo.git 30 | ``` 31 | 32 | 然后将工程导入到 IDEA 或者 eclipse。导入之后界面如下: 33 | 34 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*vVDNR7FRmQsAAAAAAAAAAABkARQnAQ) 35 | 36 | * balance-mng:余额管理系统,提供扣减余额服务 37 | * stock-mng:库存管理系统,提供扣减库存服务 38 | 39 | #### 2、引入依赖 40 | 41 | 将下面的依赖引入到 balance-mng 和 stock-mng 工程模块的 pom.xml 文件中。 42 | ```xml 43 | 44 | 45 | com.alipay.sofa 46 | rpc-sofa-boot-starter 47 | 48 | 49 | 50 | com.alipay.sofa 51 | tracer-sofa-boot-starter 52 | 53 | 54 | 55 | com.alipay.sofa 56 | registry-client-all 57 | 58 | 59 | 60 | com.alipay.sofa 61 | runtime-sofa-boot-starter 62 | 63 | 64 | 65 | com.alipay.sofa.lookout 66 | lookout-sofa-boot-starter 67 | 68 | ``` 69 | 70 | balance-mng 工程需要将依赖引入 balance-mng/balance-mng-impl/pom.xml 文件: 71 | 72 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*R475S7L1T3gAAAAAAAAAAABkARQnAQ) 73 | 74 | stock-mng 工程直接将依赖引入 stock-mng/pom.xml 文件: 75 | 76 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*z5mtSLaTuN4AAAAAAAAAAABkARQnAQ) 77 | 78 | #### 3、添加配置 79 | 80 | 将如下配置复制到 balance-mng 和 stock-mng 工程模块的 application.properties 中。 81 | ```properties 82 | # 1、添加服务注册中心地址 83 | com.alipay.sofa.rpc.registry.address=sofa://localhost:9603 84 | # 2、添加 tracer 数据上报的服务端 zipkin 地址 85 | # 如果上面前置条件未搭建 tracer,可以不配置 86 | com.alipay.sofa.tracer.zipkin.base-url=http://localhost:9411 87 | # 3、添加 metrics 数据上报的服务端地址 88 | # 如果上面前置条件未搭建 lookout-server,可以不配置 89 | com.alipay.sofa.lookout.agent-host-address=localhost 90 | ``` 91 | 92 | balance-mng 工程需要将配置添加至 balance-mng/balance-mng-bootstrap/src/main/resources/application.properties 文件: 93 | 94 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*aI0nT4hu2sYAAAAAAAAAAABkARQnAQ) 95 | 96 | 另外数据库配置修改为自己的数据库信息: 97 | ``` 98 | # database config 99 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 100 | spring.datasource.url=jdbc:mysql://localhost:3306/stock_db 101 | spring.datasource.username=root 102 | spring.datasource.password=root 103 | ``` 104 | 105 | stock-mng 工程需要将配置添加至 stock-mng/src/main/resources/application.properties 文件: 106 | 107 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*MVm1TIODuNYAAAAAAAAAAABkARQnAQ) 108 | 109 | 另外数据库配置修改为自己的数据库信息: 110 | ``` 111 | # database config 112 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 113 | spring.datasource.url=jdbc:mysql://localhost:3306/stock_db 114 | spring.datasource.username=root 115 | spring.datasource.password=root 116 | ``` 117 | 118 | #### 4、修改 unique id 119 | 由于所有人共用一套服务发现,为区分不同用户发布的服务,需要为服务增加 unique id。 120 | 121 | KubeCon workshop 会给每个用户准备一个 SOFAStack 账号,格式为 user0@sofastack.io 到 user99@sofastack.io,去掉 @sofastack.io 部分,账户前半部分的 user0 至 user99 即可作为 unique id。 122 | 123 | > 注意:balance-mng 和 stock-mng 里的 unique id 需要一致。 124 | 125 | balance-mng 工程需要在 balance-mng/balance-mng-bootstrap/src/main/resources/application.properties 文件修改: 126 | 127 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*6tsSQoNqZKQAAAAAAAAAAABkARQnAQ) 128 | 129 | stock-mng 工程需要在 stock-mng/src/main/resources/application.properties 文件修改: 130 | 131 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*0dF6R6oKJTUAAAAAAAAAAABkARQnAQ) 132 | 133 | #### 5、发布 SOFARPC 服务 134 | 135 | 在 BalanceMngImpl 类上加上 @SofaService 注解 和 @Service 注解,将其发布成一个 SOFARPC 服务: 136 | 137 | ```java 138 | import org.springframework.stereotype.Service; 139 | import com.alipay.sofa.runtime.api.annotation.SofaService; 140 | import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; 141 | 142 | @Service 143 | @SofaService(interfaceType = BalanceMngFacade.class, uniqueId = "${service.unique.id}", bindings = { @SofaServiceBinding(bindingType = "bolt") }) 144 | ``` 145 | 146 | 增加之后的 BalanceMngImpl 类如下图所示: 147 | 148 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*Hq4HSrGX3YsAAAAAAAAAAABkARQnAQ) 149 | 150 | 在 StockMngImpl 类上加上 @SofaService 注解 和 @Service 注解,将其发布成一个 SOFARPC 服务: 151 | 152 | ```java 153 | import org.springframework.stereotype.Service; 154 | import com.alipay.sofa.runtime.api.annotation.SofaService; 155 | import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; 156 | 157 | @Service 158 | @SofaService(interfaceType = StockMngFacade.class, uniqueId = "${service.unique.id}", bindings = { @SofaServiceBinding(bindingType = "bolt") }) 159 | ``` 160 | 161 | 增加之后的 StockMngImpl 类如下图所示: 162 | 163 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*s36WT6dxHcsAAAAAAAAAAABkARQnAQ) 164 | 165 | #### 6、引用 SOFARPC 服务 166 | 167 | 在 BookStoreControllerImpl 类中的 stockMngFacade 变量上方加 @SofaReference 注解,用于引用 SOFARPC 服务: 168 | 169 | ```java 170 | import com.alipay.sofa.runtime.api.annotation.SofaReference; 171 | import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding; 172 | 173 | @SofaReference(interfaceType = StockMngFacade.class, uniqueId = "${service.unique.id}", binding = @SofaReferenceBinding(bindingType = "bolt")) 174 | ``` 175 | 176 | 在 BookStoreControllerImpl 类中的 balanceMngFacade 变量上方加 @SofaReference 注解,用于引用 SOFARPC 服务: 177 | 178 | ```java 179 | import com.alipay.sofa.runtime.api.annotation.SofaReference; 180 | import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding; 181 | 182 | @SofaReference(interfaceType = BalanceMngFacade.class, uniqueId = "${service.unique.id}", binding = @SofaReferenceBinding(bindingType = "bolt")) 183 | ``` 184 | 185 | 增加之后的 BookStoreControllerImpl 类如下图所示: 186 | 187 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*L2d6RLa8XzkAAAAAAAAAAABkARQnAQ) 188 | 189 | #### 7、实验验证 190 | 191 | 运行 BalanceMngApplication 和 StockMngApplication 即可启动应用。应用启动之后,通过浏览器访问:[http://localhost:8080](http://localhost:8080) 即可正常操作页面: 192 | 193 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*s_pATp7OFmAAAAAAAAAAAABkARQnAQ) 194 | 195 | 浏览器访问 [http://localhost:9411](http://localhost:9411),查看链路数据上报以链路关系图: 196 | 197 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*rUxWQJ2tARAAAAAAAAAAAABkARQnAQ) 198 | 199 | 浏览器访问 [http://localhost:9090](http://localhost:9090) 即可查看上报 metrics: 200 | 201 | ![pic](https://gw.alipayobjects.com/mdn/rms_c69e1f/afts/img/A*k1kVS5N4oCQAAAAAAAAAAABkARQnAQ) 202 | 203 | * `jvm.threads.totalStarted{app="stock_mng"}`:可以查看 JVM 启动线程数 204 | * `rpc.consumer.service.stats.total_count.count{app="stock_mng"}`:可以查看 stock_mng 应用的调用次数 205 | 206 | 关于 SOFALookout 的更多用法,请参考: [https://www.sofastack.tech/sofa-lookout/docs/Home](https://www.sofastack.tech/sofa-lookout/docs/Home) 207 | 208 | ## 更多 209 | 210 | - [下载本次 Demo 幻灯片](https://gw.alipayobjects.com/os/basement_prod/b16fd217-b82b-436e-8b0d-452e636e072b.pdf)。 211 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-bootstrap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | balance-mng 7 | io.sofastack 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | balance-mng-bootstrap 13 | 14 | 15 | 16 | io.sofastack 17 | balance-mng-impl 18 | 19 | 20 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-bootstrap/src/main/java/io/sofastack/balance/manage/BalanceMngApplication.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.balance.manage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author yuanyuan 8 | * @since 2019/6/10 9 | */ 10 | @SpringBootApplication 11 | public class BalanceMngApplication { 12 | public static void main(String[] args) { 13 | SpringApplication.run(BalanceMngApplication.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-bootstrap/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=balance_mng 2 | 3 | # set_logging_level 4 | logging.level.*=INFO 5 | 6 | # set_color 7 | spring.output.ansi.enabled=ALWAYS 8 | 9 | # logging path 10 | logging.path=./logs 11 | 12 | # bolt & server port 13 | com.alipay.sofa.rpc.bolt.port=22200 14 | server.port=9080 15 | 16 | # database config 17 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 18 | spring.datasource.url=jdbc:mysql://localhost:3306/balance_db?characterEncoding=utf8 19 | spring.datasource.username=root 20 | spring.datasource.password=root 21 | 22 | # service unique id 23 | service.unique.id=user99 24 | 25 | ### 在此添加配置 26 | 27 | 28 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-facade/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | balance-mng 7 | io.sofastack 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | balance-mng-facade 13 | 14 | 15 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-facade/src/main/java/io/sofastack/balance/manage/facade/BalanceMngFacade.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.balance.manage.facade; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * @author yuanyuan 7 | * @since 2019/6/10 8 | */ 9 | public interface BalanceMngFacade { 10 | 11 | /** 12 | * 13 | * 添加一条用户记录 14 | * 15 | * @param userName 用户名 16 | */ 17 | void createUser(String userName); 18 | 19 | /** 20 | * 返回用户余额 21 | * 22 | * @param userName 用户名 23 | * @return 24 | */ 25 | BigDecimal queryBalance(String userName); 26 | 27 | /** 28 | * 减少用户余额 29 | * 30 | * @param userName 用户名 31 | * @param amount 减少数目 32 | */ 33 | void minusBalance(String userName, BigDecimal amount); 34 | } 35 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-facade/src/main/java/io/sofastack/balance/manage/model/Balance.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.balance.manage.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * @author yuanyuan 7 | * @since 2019/6/10 8 | */ 9 | public class Balance { 10 | private int id; 11 | private String userName; 12 | private BigDecimal balance; 13 | 14 | public int getId() { 15 | return id; 16 | } 17 | 18 | public void setId(int id) { 19 | this.id = id; 20 | } 21 | 22 | public String getUserName() { 23 | return userName; 24 | } 25 | 26 | public void setUserName(String userName) { 27 | this.userName = userName; 28 | } 29 | 30 | public BigDecimal getBalance() { 31 | return balance; 32 | } 33 | 34 | public void setBalance(BigDecimal balance) { 35 | this.balance = balance; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-impl/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | balance-mng 7 | io.sofastack 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | balance-mng-impl 13 | 14 | 15 | 16 | io.sofastack 17 | balance-mng-facade 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-jdbc 22 | 23 | 24 | mysql 25 | mysql-connector-java 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-impl/src/main/java/io/sofastack/balance/manage/impl/BalanceMngImpl.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.balance.manage.impl; 2 | 3 | import io.sofastack.balance.manage.facade.BalanceMngFacade; 4 | import io.sofastack.balance.manage.mapper.BalanceMngMapper; 5 | import io.sofastack.balance.manage.model.Balance; 6 | import javax.annotation.Resource; 7 | import java.math.BigDecimal; 8 | 9 | /** 10 | * @author yuanyuan 11 | * @since 2019/6/10 12 | */ 13 | public class BalanceMngImpl implements BalanceMngFacade { 14 | @Resource 15 | private BalanceMngMapper balanceMngMapper; 16 | 17 | @Override 18 | public void createUser(String userName) { 19 | Balance balance = balanceMngMapper.userExists(userName); 20 | if (balance == null) { 21 | balanceMngMapper.createUser(userName); 22 | } 23 | } 24 | 25 | @Override 26 | public BigDecimal queryBalance(String userName) { 27 | Balance balance = balanceMngMapper.queryBalance(userName); 28 | if (balance == null) { 29 | throw new RuntimeException("user name does not exist"); 30 | } 31 | return balance.getBalance(); 32 | } 33 | 34 | @Override 35 | public void minusBalance(String userName, BigDecimal amount) { 36 | balanceMngMapper.minusBalance(userName, amount); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /balance-mng/balance-mng-impl/src/main/java/io/sofastack/balance/manage/mapper/BalanceMngMapper.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.balance.manage.mapper; 2 | 3 | import io.sofastack.balance.manage.model.Balance; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | import java.math.BigDecimal; 7 | 8 | @Mapper 9 | public interface BalanceMngMapper { 10 | @Select("select * from balance_tb where user_name = #{userName}") 11 | Balance userExists(@Param("userName") String userName); 12 | 13 | @Insert("insert into balance_tb (user_name, balance) values (#{userName}, 1000000)") 14 | void createUser(@Param("userName") String userName); 15 | 16 | @Select("select * from balance_tb where user_name = #{userName}") 17 | Balance queryBalance(@Param("userName") String userName); 18 | 19 | @Update("update balance_tb set balance = balance - #{amount} where user_name = #{userName}") 20 | void minusBalance(@Param("userName") String userName, @Param("amount") BigDecimal amount); 21 | } 22 | -------------------------------------------------------------------------------- /balance-mng/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | balance-mng-facade 8 | balance-mng-impl 9 | balance-mng-bootstrap 10 | 11 | 12 | kc-sofastack-demo 13 | io.sofastack 14 | 0.0.1-SNAPSHOT 15 | 16 | io.sofastack 17 | balance-mng 18 | 0.0.1-SNAPSHOT 19 | balance-mng 20 | Demo project for Spring Boot 21 | 22 | 23 | 1.8 24 | 25 | 26 | 27 | 28 | 29 | io.sofastack 30 | balance-mng-facade 31 | ${project.version} 32 | 33 | 34 | io.sofastack 35 | balance-mng-impl 36 | ${project.version} 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | stock-mng 8 | balance-mng 9 | 10 | 11 | com.alipay.sofa 12 | sofaboot-dependencies 13 | 3.1.4 14 | 15 | 16 | io.sofastack 17 | kc-sofastack-demo 18 | 0.0.1-SNAPSHOT 19 | kc-sofastack-demo 20 | Demo project for SofaStack 21 | 22 | 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | 38 | org.mybatis.spring.boot 39 | mybatis-spring-boot-starter 40 | 1.3.1 41 | 42 | 43 | 44 | 45 | 46 | 47 | com.alipay.sofa 48 | registry-client-all 49 | 5.2.0 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-surefire-plugin 59 | 2.21.0 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /stock-mng/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | 8 | -------------------------------------------------------------------------------- /stock-mng/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /stock-mng/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /stock-mng/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | kc-sofastack-demo 7 | io.sofastack 8 | 0.0.1-SNAPSHOT 9 | 10 | io.sofastack 11 | stock-mng 12 | 0.0.1-SNAPSHOT 13 | stock-mng 14 | Demo project for Spring Boot 15 | 16 | 17 | 1.8 18 | 19 | 20 | 21 | 22 | io.sofastack 23 | balance-mng-facade 24 | ${project.version} 25 | 26 | 27 | mysql 28 | mysql-connector-java 29 | 30 | 31 | com.alibaba 32 | fastjson 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-jdbc 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-web 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/StockMngApplication.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class StockMngApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(StockMngApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/controller/BookStoreController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Alipay.com Inc. 3 | * Copyright (c) 2004-2019 All Rights Reserved. 4 | */ 5 | package io.sofastack.stockmng.controller; 6 | 7 | import io.sofastack.stockmng.model.BalanceResponse; 8 | import io.sofastack.stockmng.model.ProductInfo; 9 | import io.sofastack.stockmng.model.Success; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestMethod; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * 19 | * @author yuanyuan 20 | * @Since 2019/6/10 21 | */ 22 | 23 | @RequestMapping("/") 24 | public interface BookStoreController { 25 | 26 | /** 27 | * 查询商品信息 28 | * 29 | */ 30 | @RequestMapping(value = "/query", method = RequestMethod.POST) 31 | @ResponseBody 32 | List query(@RequestBody String body); 33 | 34 | /** 35 | * 查询排序后的商品信息(演示动态模块) 36 | */ 37 | @RequestMapping(value = "/querySorted", method = RequestMethod.POST) 38 | @ResponseBody 39 | List querySorted(@RequestBody String body); 40 | 41 | /** 42 | * 购买 43 | * 44 | */ 45 | @RequestMapping(value = "/purchase", method = RequestMethod.POST) 46 | @ResponseBody 47 | Success purchase(@RequestBody String body); 48 | 49 | /** 50 | * BalanceMng的RPC代理为Web创建用户 51 | * 52 | */ 53 | @RequestMapping(value = "/createUser", method = RequestMethod.POST) 54 | @ResponseBody 55 | Success createUser(@RequestBody String body); 56 | 57 | /** 58 | * BalanceMng的RPC代理为Web查询余额 59 | * 60 | */ 61 | @RequestMapping(value = "/queryBalance", method = RequestMethod.POST) 62 | @ResponseBody 63 | BalanceResponse queryBalance(@RequestBody String body); 64 | } -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/controller/impl/BookStoreControllerImpl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Alipay.com Inc. 3 | * Copyright (c) 2004-2019 All Rights Reserved. 4 | */ 5 | package io.sofastack.stockmng.controller.impl; 6 | 7 | import com.alibaba.fastjson.JSON; 8 | import com.alibaba.fastjson.JSONObject; 9 | import io.sofastack.balance.manage.facade.BalanceMngFacade; 10 | import io.sofastack.stockmng.controller.BookStoreController; 11 | import io.sofastack.stockmng.facade.StockMngFacade; 12 | import io.sofastack.stockmng.model.BalanceResponse; 13 | import io.sofastack.stockmng.model.ProductInfo; 14 | import io.sofastack.stockmng.model.Success; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import org.springframework.stereotype.Controller; 18 | import org.springframework.web.bind.annotation.RequestBody; 19 | 20 | import java.math.BigDecimal; 21 | import java.util.List; 22 | 23 | /** 24 | * @author yuanyuan 25 | * @Since 2019/6/10 26 | */ 27 | @Controller 28 | public class BookStoreControllerImpl implements BookStoreController { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(BookStoreControllerImpl.class); 31 | 32 | private StockMngFacade stockMngFacade; 33 | 34 | private BalanceMngFacade balanceMngFacade; 35 | 36 | @Override 37 | public List query(String body) { 38 | JSONObject obj = JSON.parseObject(body); 39 | String userName = obj.getString("userName"); 40 | return stockMngFacade.query(userName); 41 | } 42 | 43 | @Override 44 | public List querySorted(String body) { 45 | //TODO(guolei.sgl): SOFA 动态模块 46 | JSONObject obj = JSON.parseObject(body); 47 | String userName = obj.getString("userName"); 48 | return stockMngFacade.query(userName); 49 | } 50 | 51 | @Override 52 | public Success purchase(String body) { 53 | 54 | JSONObject obj = JSON.parseObject(body); 55 | String userName = obj.getString("userName"); 56 | String productCode = obj.getString("productCode"); 57 | int count = obj.getInteger("count"); 58 | 59 | BigDecimal productPrice = stockMngFacade.queryProductPrice(productCode, userName); 60 | if (productPrice == null) { 61 | throw new RuntimeException("product code does not exist"); 62 | } 63 | if (count <= 0) { 64 | throw new RuntimeException("purchase count should not be negative"); 65 | } 66 | LOGGER.info("purchase begin ... "); 67 | stockMngFacade.createOrder(userName, productCode, count); 68 | stockMngFacade.minusStockCount(userName, productCode, count); 69 | balanceMngFacade.minusBalance(userName, productPrice.multiply(new BigDecimal(count))); 70 | LOGGER.info("purchase end"); 71 | Success success = new Success(); 72 | success.setSuccess("true"); 73 | return success; 74 | } 75 | 76 | @Override 77 | public Success createUser(@RequestBody String body) { 78 | JSONObject obj = JSON.parseObject(body); 79 | String userName = obj.getString("userName"); 80 | balanceMngFacade.createUser(userName); 81 | Success success = new Success(); 82 | success.setSuccess("true"); 83 | return success; 84 | } 85 | 86 | @Override 87 | public BalanceResponse queryBalance(@RequestBody String body) { 88 | JSONObject obj = JSON.parseObject(body); 89 | String userName = obj.getString("userName"); 90 | BalanceResponse balance = new BalanceResponse(); 91 | balance.setBalance((balanceMngFacade.queryBalance(userName))); 92 | return balance; 93 | } 94 | } -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/facade/StockMngFacade.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng.facade; 2 | 3 | import io.sofastack.stockmng.model.ProductInfo; 4 | import org.apache.ibatis.annotations.Param; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.List; 8 | 9 | /** 10 | * @author yuanyuan 11 | * @since 2019/6/10 12 | */ 13 | public interface StockMngFacade { 14 | /** 15 | * 查询商品信息 16 | * 17 | */ 18 | List query(String userName); 19 | 20 | BigDecimal queryProductPrice(String productCode, String userName); 21 | 22 | boolean createOrder(String userName, String productCode, int count); 23 | 24 | boolean minusStockCount(String userName, String productCode, int count); 25 | } 26 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/impl/DatabaseSeed.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng.impl; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class DatabaseSeed { 6 | public static String[] name = new String[] { 7 | "未来架构: 从服务化到云原生", 8 | "Cloud Native Go: 构建基于Go和React的云原生Web应用与微服务", 9 | "云原生Java: Spring Boot、Spring Cloud与Cloud Foundry弹性系统设计", 10 | "Python云原生: 构建应对海量用户数据的高可扩展Web应用", 11 | "深入浅出Istio: Service Mesh快速入门与实践" 12 | }; 13 | 14 | public static BigDecimal[] price = new BigDecimal[] { 15 | new BigDecimal(99), 16 | new BigDecimal(69), 17 | new BigDecimal(128), 18 | new BigDecimal(89), 19 | new BigDecimal(79), 20 | }; 21 | 22 | public static String[] description = new String[] { 23 | "{\n" 24 | + " \"description\": " 25 | + "\"
互联网架构不断演化,经历了从集中式架构到分布式架构,再到云原生架构的过程。云原生因能解决传统应用升级缓慢、架构臃肿、无法快速迭代等问题而成了未来云端应用的目标。《未来架构:从服务化到云原生》首先介绍架构演化过程及云原生的概念,让读者对基础概念有一个准确的了解,接着阐述分布式、服务化、可观察性、容器调度、Service Mesh、云数据库等技术体系及原理,并介绍相关的SkyWalking、Dubbo、Spring Cloud、Kubernetes、Istio等开源解决方案,最后深度揭秘开源分布式数据库生态圈ShardingSphere的设计、实现,以及进入Apache基金会的历程,非常适合架构师、云计算从业人员阅读、学习。

张亮

京东数科数据研发负责人,Apache Sharding-Sphere发起人兼PPMC成员。热爱分享,拥抱开源,主张代码优雅化,擅长以Java为主的分布式架构及以Kubernetes和Mesos为主的云平台的构建。ShardingSphere已进入Apache软件基金会,是京东集团首个进入Apache的开源项目,也是Apache首个分布式数据库中间件。

吴晟

Apache SkyWalking创始人及PPMC成员,Apache ShardingSphere原型作者及PPMC成员,Apache Zipkin贡献者,Apache孵化器导师,CNCF基金会OpenTracing标准化委员会成员,W3C Trace Context规范贡献者。擅长分布式架构、性能监控与诊断、分布式追踪、云原生监控等领域。

敖小剑

具有十七年软件开发经验,资深码农,微服务专家,Cloud Native拥护者,敏捷实践者,ServiceMesh布道师,ServicelMesher中文社区联合创始人。专注于基础架构建设,对微服务、云计算等相关技术有着深入研究和独到见解。

宋净超

蚂蚁金服云原生布道师,ServiceMesher中文社区联合创始人,Kubemetes社区成员,Istio社区成员,《Cloud Native Go》《Python云原生》《云原生Java》等图书译者。

\",\n" 28 | + " \"author\": \"张亮, 吴晟, 敖小剑, 宋净超\",\n" 29 | + " \"image_url\": \"http://reserved-antcloud-cnshnfpub-opsware-v2.oss-cn-shanghai.aliyuncs" 30 | + ".com/fas/books/1.png?OSSAccessKeyId=RZU9wKztYEqaBQGB&Expires=1647166160&Signature=xCS%2FpJtY8" 31 | + "%2FVcdbLqfjHUp6z%2FoOw%3D\"\n" 32 | + "}\n", 33 | 34 | "{\n" 35 | + " \"description\": \"

本书旨在向开发人员展示如何构建适用于大流量、高并发场景下的云原生Web应用。本书从搭建开发测试环境开始,逐步介绍使用Go" 36 | + "语言构建微服务的方法,通过引入CI/CD流程和Wercker、Docker等工具将应用推送到云中。结合微服务构建中的后端服务、数据服务、事件溯源和CQRS模式、基于React和Flux的UI" 37 | + "设计等,本书最后构建了一个基于Web的RPG游戏World of FluxCraft,可以作为使用Go构建云原生Web应用的参考,适合于云计算与Go语言编程从业者们阅读。

1" 38 | + ".云原生是云计算时代的发展趋势和必然结果

《Cloud Native " 39 | + "Go:构建基于Go和React的云原生Web应用与微服务》通过一个云原生应用项目的构建,为大家介绍了云原生的道与术,引导读者了解云原生理念的产生、应用场景、优势。

2" 40 | + ".集现今诸多热点技术之大成

《Cloud Native Go:构建基于Go和React的云原生Web应用与微服务》在构建云原生项目时,涉及Docker、持续集成、微服务、DevOps" 41 | + "、事件溯源与CQRS等众多备受关注的技术热点,无疑会让读者受益匪浅。

3.Go语言助理云开发完美实现

Go" 42 | + "语言以其简单优雅、快速安全、支持高并发等特性,成为云计算时代的最优语言。《Cloud Native " 43 | + "Go:构建基于Go和React的云原生Web应用与微服务》将带领读者正确认识Go语言,掌握用Go构建应用程序的方法。

4.流程完整,示例具体详细

《Cloud Native " 44 | + "Go:构建基于Go和React的云原生Web应用与微服务》从搭建平台开始,逐步带领读者开发一个完整的云上项目。其中的每一环节都有详细讲解。示例具有代表性,代码详细,帮助读者轻松掌握云原生开发的关键。

\",\n" 46 | + " \"author\": \"Kevin Hoffman, 宋净超\",\n" 47 | + " \"image_url\": \"http://reserved-antcloud-cnshnfpub-opsware-v2.oss-cn-shanghai.aliyuncs" 48 | + ".com/fas/books/2.png?OSSAccessKeyId=RZU9wKztYEqaBQGB&Expires=1647166182&Signature" 49 | + "=U4ep6Dsh4w8TqW6tLlDLoEopIm4%3D\"\n" 50 | + "}\n", 51 | 52 | "{\n" 53 | + " \"description\": \"

无论是传统IT行业,还是互联网行业,都正处于行业历史上最剧烈的变革中 :大量的系统正在从传统的IT架构转向基于云的架构, " 54 | + "开发模式也正在从开发和运维分工的传统模式,逐渐转向统一的“DevOps”模式。Java技术已经进入了新的生命周期,大量被用于构建现代的、基于云的应用程序。 " 55 | + "本书详细阐述了开发云原生应用程序的机遇和挑战,明确指出了成功实现的方向,并且重点介绍了微服务框架Spring Boot。Spring Boot可以轻松创建任何粒度的 " 56 | + "Spring服务,并部署到现代的容器环境中。本书主要面向正在使用 Spring Boot、SpringCloud和Cloud Foundry, 以便更快、更好地构建软件的Java/JVM " 57 | + "开发人员。本书一共分为4个部分共15章。第1章和第2章介绍了云原生思想产生的背景,然后介绍了Spring Foundry。第3章介绍了如何配置Spring " 58 | + "Boot应用程序。第4章介绍了如何测试Spring应用程序,从如何测试最简单的组件到测试分布式系统。第5章介绍了可以将应用程序迁移到Cloud " 59 | + "Foundry等云平台的轻量级重构方式。第6章介绍了如何使用Spring构建HTTP和RESTful服务。第7章介绍了在分布式系统中控制请求进出的常用方法。第8章介绍了如何构建一个响应外部请求的服务。第9" 60 | + "章介绍了如何使用Spring Data在Spring中管理数据。这为领域驱动的思想奠定了基础。第10章介绍了如何使用Spring中事件驱动、消息中心化的能力,来集成分布式服务和数据。第11" 61 | + "章介绍了如何利用云平台(如Cloud Foundry)的能力来处理长期运行的工作。第12章介绍了在分布式系统中管理状态的一些方法。第13章介绍了如何构建具备可观测性和可操作性的系统。第14" 62 | + "章介绍了如何构建类似于Cloud Foundry平台的服务代理。第15章介绍了持续交付背后的思想。

1. 基础知识

了解云原生思维背后的动机;配置和测试Spring " 63 | + "Boot应用程序;将您的传统应用程序迁移至云端

2. 微服务

使用Spring构建HTTP和RESTful服务;在分布式系统中路由请求;建立更接近数据的边缘服务

3. " 64 | + "数据整合

使用Spring Data管理数据,并将分布式服务与——Spring对事件驱动、以消息传递为中心架构的支持——集成起来

4. " 65 | + "生产

让您的系统可观察;使用服务代理来连接有状态的服务;了解持续交付背后的重要思想

\",\n" 66 | + " \"author\": \"Josh Long, 张若飞, 宋净超\",\n" 67 | + " \"image_url\": \"http://reserved-antcloud-cnshnfpub-opsware-v2.oss-cn-shanghai.aliyuncs" 68 | + ".com/fas/books/3.png?OSSAccessKeyId=RZU9wKztYEqaBQGB&Expires=1647166193&Signature" 69 | + "=WfYCQ1S8XLuqj7oCGxHYTH3VNdc%3D\"\n" 70 | + "}\n", 71 | 72 | "{\n" 73 | + " \"description\": \"

《Python云原生:构建应对海量用户数据的高可扩展Web应用》以一个应用开发贯穿始终,从云原生和微服务的概念原理讲起,使用Python" 74 | + "构建云原生应用,并使用React构建Web视图。为了应对大规模的互联网流量,使用了Flux构建UI和事件溯源及CQRS模式。考虑到Web应用的安全性,《Python" 75 | + "云原生:构建应对海量用户数据的高可扩展Web应用》对此也给出了解决方案。书中对于关键步骤进行了详细讲解并给出运行结果。读者可以利用Docker容器、CI/CD工具,敏捷构建和发布本书示例中的应用到AWS" 76 | + "、Azure这样的公有云平台上,再利用平台工具对基础设施和应用的运行进行持续监控。

云原生是云计算时代的发展趋势和必然结果

云原生将持续领航云时代架构理念

用Python语言进行开发

易如门,易掌握,集现今诸多热点技术之大成

流程完整,示例具体详细

一个实际开发案例贯穿始终,全面开放代码

\",\n" 79 | + " \"author\": \"Manish Sethi, 宋净超\",\n" 80 | + " \"image_url\": \"http://reserved-antcloud-cnshnfpub-opsware-v2.oss-cn-shanghai.aliyuncs" 81 | + ".com/fas/books/4.png?OSSAccessKeyId=RZU9wKztYEqaBQGB&Expires=1647178695&Signature" 82 | + "=9l4JHy7eMQVj3T3mXgRpAN9wdPI%3D\"\n" 83 | + "}\n", 84 | 85 | "{\n" 86 | + " \"description\": \"

Google联合IBM、Lyft推出的Istio,一经问世就受到了人们的普遍关注,其热度迅速攀升,成为Service " 87 | + "Mesh(服务网格)方案的代表项目。本书整理了Istio中的部分概念和案例,以快速入门的形式,对Istio的基础用法一一进行讲解,并在书末给出一些试用方面的建议。
在本书中,前3" 88 | + "章从微服务和服务网格的简短历史开始,讲述了服务网格的诞生过程、基本特性及Istio的核心功能,若对这些内容已经有所了解,则可以直接从第4章开始阅读;第4、5章分别讲解了Istio的配置和部署过程;第6" 89 | + "章至第9章,通过多个场景来讲解Istio的常用功能;第10章结合了笔者的实践经验,为读者提供了Istio的一系列试用建议。本书没有采用官方复杂的Book " 90 | + "Info应用案例,而是采用客户端+简单HTTP服务端的案例,读者随时都能在短时间内启动一个小的测试。
本书面向对服务网格技术感兴趣,并希望进一步了解和学习Istio" 91 | + "的中高级技术人员,假设读者已经了解Kubernetes的相关概念并能够在Kubernetes上熟练部署和管理微服务。若希望全面、深入地学习Kubernetes,可参考《Kubernetes " 92 | + "权威指南:从Docker到Kubernetes实践全接触》和《Kubernetes 权威指南:企业级容器云实战》。

快速入门Service " 93 | + "Mesh和实践

手把手快速入门Service Mesh和实践,并根据Istio 1.1版本的升级,将源码及内容同步更新至GitHub

作者为Kubernetes " 94 | + "权威指南作者之一

作者为Kubernetes 权威指南作者之一,Istio、Kubernetes项目成员,Istio" 95 | + ".io主要贡献者之一

知名大咖热评

知名大咖敖小剑、马全一、张琦及《Kubernetes 权威指南》作者龚正等热评!

\",\n" 96 | + " \"author\": \"崔秀龙\",\n" 97 | + " \"image_url\": \"http://reserved-antcloud-cnshnfpub-opsware-v2.oss-cn-shanghai.aliyuncs" 98 | + ".com/fas/books/5.png?OSSAccessKeyId=RZU9wKztYEqaBQGB&Expires=1647179151&Signature=LIY" 99 | + "%2F56jF8Out5eHsxAU1hkUOp7o%3D\"\n" 100 | + "}\n", 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/impl/StockMngImpl.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng.impl; 2 | 3 | import io.sofastack.stockmng.facade.StockMngFacade; 4 | import io.sofastack.stockmng.mapper.StockMngMapper; 5 | import io.sofastack.stockmng.model.ProductInfo; 6 | import javax.annotation.Resource; 7 | import java.math.BigDecimal; 8 | import java.util.List; 9 | 10 | /** 11 | * @author yuanyuan 12 | * @since 2019/6/10 13 | */ 14 | public class StockMngImpl implements StockMngFacade { 15 | @Resource 16 | private StockMngMapper stockMngMapper; 17 | 18 | @Override 19 | public List query(String userName) { 20 | Integer stockRecordCount = stockMngMapper.getStockRecordCountForUserName(userName); 21 | if (stockRecordCount == null) { 22 | initUser(userName); 23 | } 24 | 25 | return stockMngMapper.query(userName); 26 | } 27 | 28 | private void initUser(String userName) { 29 | for (int i = 0; i < 5; i++) { 30 | stockMngMapper.insertStockRecord( 31 | "0000" + (i + 1), 32 | DatabaseSeed.name[i], 33 | DatabaseSeed.description[i], 34 | DatabaseSeed.price[i], 35 | 10000, 36 | userName 37 | ); 38 | } 39 | } 40 | 41 | @Override 42 | public BigDecimal queryProductPrice(String productCode, String userName) { 43 | return stockMngMapper.queryProductPrice(productCode, userName); 44 | } 45 | 46 | @Override 47 | public boolean createOrder(String userName, String productCode, int count) { 48 | return stockMngMapper.createOrder(userName, productCode, count) > 0; 49 | } 50 | 51 | @Override 52 | public boolean minusStockCount(String userName, String productCode, int count) { 53 | return stockMngMapper.minusStockCount(userName, productCode, count) > 0; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/mapper/StockMngMapper.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng.mapper; 2 | 3 | import io.sofastack.stockmng.model.ProductInfo; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.List; 8 | 9 | @Mapper 10 | public interface StockMngMapper { 11 | @Select( 12 | "select stock_tb.product_code as productCode, stock_tb.count as stockCount, name, price, sum(user_orders.count) as ownedCount, description\n" + 13 | "from stock_tb left outer join (select product_code, count from order_tb where user_name = #{userName}) as user_orders\n" + 14 | " on stock_tb.product_code = user_orders.product_code where stock_tb.user_name=#{userName} \n" + 15 | "group by productCode, stockCount, name, price, description;") 16 | List query(@Param("userName") String userName); 17 | 18 | @Select("select price from stock_tb where product_code = #{productCode} and user_name = #{userName}") 19 | BigDecimal queryProductPrice(@Param("productCode") String productCode, @Param("userName") String userName); 20 | 21 | @Select("select sum(1) from stock_tb where user_name = #{userName}") 22 | Integer getStockRecordCountForUserName(@Param("userName") String userName); 23 | 24 | @Insert( 25 | "insert into stock_tb (product_code, name, description, price, count, user_name) values (#{productCode}, " 26 | + "#{name}, #{description}, #{price}, #{count}, #{userName})") 27 | void insertStockRecord(@Param("productCode") String productCode, @Param("name") String name, 28 | @Param("description") String description, @Param("price") BigDecimal price, 29 | @Param("count") Integer count, @Param("userName") String userName); 30 | 31 | @Insert("insert into order_tb (user_name, product_code, count) values (#{userName}, #{productCode}, #{count})") 32 | int createOrder(@Param("userName") String userName, @Param("productCode") String productCode, @Param("count") int count); 33 | 34 | @Update("update stock_tb set count=count - #{count} where product_code=#{productCode} and user_name=#{userName}") 35 | int minusStockCount(@Param("userName") String userName, @Param("productCode") String productCode, 36 | @Param("count") int count); 37 | } 38 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/model/BalanceResponse.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class BalanceResponse { 6 | 7 | private BigDecimal balance; 8 | 9 | public BigDecimal getBalance() { 10 | return balance; 11 | } 12 | 13 | public void setBalance(BigDecimal balance) { 14 | this.balance = balance; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/model/ProductInfo.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class ProductInfo { 6 | 7 | private String productCode; 8 | private BigDecimal price; 9 | private String name; 10 | private Integer ownedCount; 11 | private Integer stockCount; 12 | private String description; 13 | 14 | public Integer getStockCount() { 15 | return stockCount; 16 | } 17 | 18 | public void setStockCount(Integer stockCount) { 19 | this.stockCount = stockCount; 20 | } 21 | 22 | 23 | public String getDescription() { 24 | return description; 25 | } 26 | 27 | public void setDescription(String description) { 28 | this.description = description; 29 | } 30 | 31 | public String getProductCode() { 32 | return productCode; 33 | } 34 | 35 | public void setProductCode(String productCode) { 36 | this.productCode = productCode; 37 | } 38 | 39 | public BigDecimal getPrice() { 40 | return price; 41 | } 42 | 43 | public void setPrice(BigDecimal price) { 44 | this.price = price; 45 | } 46 | 47 | public String getName() { 48 | return name; 49 | } 50 | 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | 55 | public Integer getOwnedCount() { 56 | return ownedCount; 57 | } 58 | 59 | public void setOwnedCount(Integer ownedCount) { 60 | this.ownedCount = ownedCount; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /stock-mng/src/main/java/io/sofastack/stockmng/model/Success.java: -------------------------------------------------------------------------------- 1 | package io.sofastack.stockmng.model; 2 | 3 | public class Success { 4 | 5 | private String success; 6 | 7 | public String getSuccess() { 8 | return success; 9 | } 10 | 11 | public void setSuccess(String success) { 12 | this.success = success; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stock-mng/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=stock_mng 2 | 3 | # set_logging_level 4 | logging.level.*=INFO 5 | 6 | # set_color 7 | spring.output.ansi.enabled=ALWAYS 8 | 9 | # logging path 10 | logging.path=./logs 11 | 12 | # static 13 | spring.mvc.static-path-pattern=/** 14 | 15 | # database config 16 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 17 | spring.datasource.url=jdbc:mysql://localhost:3306/stock_db?characterEncoding=utf8 18 | spring.datasource.username=root 19 | spring.datasource.password=root 20 | 21 | # service unique id 22 | service.unique.id=user99 23 | 24 | ### 在此添加配置 25 | 26 | 27 | -------------------------------------------------------------------------------- /stock-mng/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 库存管理系统
-------------------------------------------------------------------------------- /stock-mng/src/main/resources/static/static/css/main.5e80f7fb.chunk.css: -------------------------------------------------------------------------------- 1 | body{background:#f0f2f5;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overflow-y:scroll}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.layout .logo{font-size:20px;margin:16px 24px 16px 0;float:left;color:#fff;text-align:center;line-height:30px}.stock-item{border:1px solid #eee;border-radius:3px;padding:10px 10px 10px 74px;background:#fff;-webkit-transition:opacity .35s;transition:opacity .35s;position:relative;margin-bottom:10px;overflow:hidden;opacity:1}.stock-item .item-description{overflow-y:scroll;opacity:1;position:absolute;top:96px;-webkit-transition:opacity 1.05s;transition:opacity 1.05s;left:0;width:100%;background:#fcfcfc;height:calc(100% - 96px);padding:10px}.stock-item .item-description::-webkit-scrollbar{display:none}.stock-item .item-description.hidden{opacity:0}.stock-item .item-description.with-image{width:70%}.stock-item img.item-image{position:absolute;right:8px;top:96px;width:calc(30% - 16px);height:calc(100% - 96px);background-position:50%;background-size:contain;background-repeat:no-repeat}.stock-item.hidden{opacity:0;pointer-events:none}.stock-item .main{padding-right:140px}.stock-item .main>*{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.stock-item .main .name{font-weight:700;font-size:20px;margin-bottom:4px;cursor:pointer}.stock-item .main .name .book-name{padding-left:5px}.stock-item .main .name .author{font-size:14px;padding-left:8px;font-weight:lighter;font-style:italic}.stock-item .main .name .return-icon{-webkit-transition:all .35s;transition:all .35s;display:inline-block;margin-left:-2px;margin-right:-2px;width:0;padding-right:0;color:#ccc;white-space:nowrap;overflow:hidden;vertical-align:-8px}.stock-item .main .price{font-weight:500}.stock-item .main .main-img{position:absolute;left:12px;top:11px;width:50px;cursor:pointer}.stock-item .main .purchased{opacity:.7;position:relative}.stock-item .main .purchased .stock-count{font-size:10px;opacity:.5;padding-left:4px}.stock-item .main .purchased .ant-spin{position:absolute;margin-top:2px}.stock-item.enlarged .main .name .return-icon{width:24px}.stock-item .side{position:absolute;right:10px;top:10px}.stock-item .side .purchase-button.disabled{pointer-events:none}.stock-item .side .purchase-button,.stock-item .side .purchase-button>button{display:block;width:100%}.number-component{margin-bottom:4px}.number-component .input{width:60px;vertical-align:.5px;text-align:center}.number-component button{line-height:1.5}.number-component>span{padding:0 4px}@media only screen and (max-width:800px){.stock-item img.item-image{display:none!important}.stock-item .item-description.with-image{width:100%}}.balance-component{position:fixed;bottom:0;height:80px;width:100%;background:#40a9ff;color:#fff;font-size:30px;font-weight:200;padding:16px 30px}.balance-component span{font-weight:700;padding-left:10px}.ant-layout-footer{padding-bottom:60px}.spin-div{padding:20px;text-align:center}.username-display{position:absolute;right:50px;top:80px;color:#aaa}.username-display .username-text{padding-left:4px;vertical-align:-4px;font-size:10px;width:120px;overflow:hidden;text-overflow:ellipsis;display:inline-block;white-space:nowrap}.username-display .reload-btn{cursor:pointer;padding-left:4px;padding-right:2px} 2 | /*# sourceMappingURL=main.5e80f7fb.chunk.css.map */ -------------------------------------------------------------------------------- /stock-mng/src/main/resources/static/static/css/main.5e80f7fb.chunk.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["/Users/zhongle.wk/dev/demo/src/index.scss"],"names":[],"mappings":"AAIA,KACE,kBAAmB,CACnB,QAAS,CACT,mIAEU,CACV,kCAAmC,CACnC,iCAAkC,CAClC,iBAAkB,CAGpB,KACE,uEACS,CAGX,cACE,cAAe,CACf,uBAAwB,CACxB,UAAW,CACX,UAAY,CACZ,iBAAkB,CAClB,gBAAiB,CAGnB,YACE,qBAAsB,CACtB,iBAAkB,CAElB,2BAAkB,CAClB,eAAmB,CACnB,+BAjCwB,CAiCxB,uBAjCwB,CAkCxB,iBAAkB,CAClB,kBAAmB,CACnB,eAAgB,CAEhB,SAAU,CAXZ,8BAcI,iBAAkB,CAClB,SAAU,CACV,iBAAkB,CAClB,QAAS,CACT,gCAAyC,CAAzC,wBAAyC,CACzC,MAAO,CACP,UAAW,CACX,kBAAmB,CACnB,wBAAyB,CACzB,YAAa,CAvBjB,iDA0BM,YAAa,CA1BnB,qCA8BM,SAAU,CA9BhB,yCAkCM,SAAU,CAlChB,2BAuCI,iBAAkB,CAClB,SAAU,CACV,QAAS,CACT,sBAAuB,CACvB,wBAAyB,CACzB,uBAAkC,CAClC,uBAAwB,CACxB,2BAA4B,CA9ChC,mBAkDI,SAAU,CACV,mBAAoB,CAnDxB,kBA0DI,mBAAoB,CA1DxB,oBA6DM,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB,CA/D7B,wBAmEM,eAAiB,CACjB,cAAe,CACf,iBAAkB,CAClB,cAAe,CAtErB,mCAyEQ,gBAAiB,CAzEzB,gCA6EQ,cAAe,CACf,gBAAiB,CACjB,mBAAoB,CACpB,iBAAkB,CAhF1B,qCAoFQ,2BA/GkB,CA+GlB,mBA/GkB,CAgHlB,oBAAqB,CACrB,gBAAiB,CACjB,iBAAkB,CAClB,OAAQ,CACR,eAAgB,CAChB,UAAW,CACX,kBAAmB,CACnB,eAAgB,CAChB,mBAAoB,CA7F5B,yBAkGM,eAAgB,CAlGtB,4BAsGM,iBAAkB,CAClB,SAAU,CACV,QAAS,CACT,UAAW,CACX,cAAe,CA1GrB,6BA8GM,UAAW,CACX,iBAAkB,CA/GxB,0CAkHQ,cAAe,CACf,UAAY,CACZ,gBAAiB,CApHzB,uCAwHQ,iBAAkB,CAClB,cAAe,CAzHvB,8CAgIM,UAAW,CAhIjB,kBAqII,iBAAkB,CAClB,UAAW,CACX,QAAS,CAvIb,4CA0IM,mBAAoB,CA1I1B,6EA8IM,aAAc,CACd,UAAW,CAOjB,kBACE,iBAAkB,CADpB,yBAII,UAAW,CACX,mBAAqB,CACrB,iBAAkB,CANtB,yBAUI,eAAgB,CAVpB,uBAcI,aAAc,CAKlB,yCACE,2BACE,sBAAwB,CAE1B,yCACE,UAAW,CACZ,CAIH,mBACE,cAAe,CACf,QAAW,CACX,WAAY,CACZ,UAAW,CACX,kBAAmB,CACnB,UAAY,CACZ,cAAe,CACf,eAAgB,CAChB,iBAAkB,CATpB,wBAYI,eAAiB,CACjB,iBAAkB,CAItB,mBACE,mBAAoB,CAGtB,UACE,YAAa,CACb,iBAAkB,CAGpB,kBACE,iBAAkB,CAClB,UAAW,CACX,QAAS,CACT,UAAW,CAJb,iCAOI,gBAAiB,CACjB,mBAAoB,CACpB,cAAe,CACf,WAAY,CACZ,eAAgB,CAChB,sBAAuB,CACvB,oBAAqB,CACrB,kBAAmB,CAdvB,8BAkBI,cAAe,CACf,gBAAiB,CACjB,iBAAkB","file":"main.5e80f7fb.chunk.css","sourcesContent":["@import '~antd/dist/antd.css';\n\n$transitionDuration: 0.35s;\n\nbody {\n background: #f0f2f5;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n overflow-y: scroll;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n\n.layout .logo {\n font-size: 20px;\n margin: 16px 24px 16px 0;\n float: left;\n color: white;\n text-align: center;\n line-height: 30px;\n}\n\n.stock-item {\n border: 1px solid #EEE;\n border-radius: 3px;\n padding: 10px;\n padding-left: 74px;\n background: #FFFFFF;\n transition: opacity $transitionDuration;\n position: relative;\n margin-bottom: 10px;\n overflow: hidden;\n\n opacity: 1;\n\n .item-description {\n overflow-y: scroll;\n opacity: 1;\n position: absolute;\n top: 96px;\n transition: opacity $transitionDuration*3;\n left: 0;\n width: 100%;\n background: #FCFCFC;\n height: calc(100% - 96px);\n padding: 10px;\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n &.hidden {\n opacity: 0;\n }\n\n &.with-image {\n width: 70%;\n }\n }\n\n img.item-image {\n position: absolute;\n right: 8px;\n top: 96px;\n width: calc(30% - 16px);\n height: calc(100% - 96px);\n background-position: center center;\n background-size: contain;\n background-repeat: no-repeat;\n }\n\n &.hidden {\n opacity: 0;\n pointer-events: none;\n }\n\n &.enlarged {\n }\n\n .main {\n padding-right: 140px;\n\n & > * {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .name {\n font-weight: bold;\n font-size: 20px;\n margin-bottom: 4px;\n cursor: pointer;\n\n .book-name {\n padding-left: 5px;\n }\n\n .author {\n font-size: 14px;\n padding-left: 8px;\n font-weight: lighter;\n font-style: italic;\n }\n\n .return-icon {\n transition: all $transitionDuration;\n display: inline-block;\n margin-left: -2px;\n margin-right: -2px;\n width: 0;\n padding-right: 0;\n color: #CCC;\n white-space: nowrap;\n overflow: hidden;\n vertical-align: -8px;\n }\n }\n\n .price {\n font-weight: 500;\n }\n\n .main-img {\n position: absolute;\n left: 12px;\n top: 11px;\n width: 50px;\n cursor: pointer;\n }\n\n .purchased {\n opacity: .7;\n position: relative;\n\n .stock-count {\n font-size: 10px;\n opacity: 0.5;\n padding-left: 4px;\n }\n\n .ant-spin {\n position: absolute;\n margin-top: 2px;\n }\n }\n }\n\n &.enlarged {\n .main .name .return-icon {\n width: 24px;\n }\n }\n\n .side {\n position: absolute;\n right: 10px;\n top: 10px;\n\n .purchase-button.disabled {\n pointer-events: none;\n }\n\n .purchase-button, .purchase-button > button {\n display: block;\n width: 100%;\n }\n }\n\n}\n\n\n.number-component {\n margin-bottom: 4px;\n\n .input {\n width: 60px;\n vertical-align: 0.5px;\n text-align: center;\n }\n\n button {\n line-height: 1.5;\n }\n\n & > span {\n padding: 0 4px;\n }\n}\n\n\n@media only screen and (max-width: 800px) {\n .stock-item img.item-image {\n display: none !important;\n }\n .stock-item .item-description.with-image {\n width: 100%;\n }\n}\n\n\n.balance-component {\n position: fixed;\n bottom: 0px;\n height: 80px;\n width: 100%;\n background: #40a9ff;\n color: white;\n font-size: 30px;\n font-weight: 200;\n padding: 16px 30px;\n\n span {\n font-weight: bold;\n padding-left: 10px;\n }\n}\n\n.ant-layout-footer {\n padding-bottom: 60px;\n}\n\n.spin-div {\n padding: 20px;\n text-align: center;\n}\n\n.username-display {\n position: absolute;\n right: 50px;\n top: 80px;\n color: #AAA;\n\n .username-text {\n padding-left: 4px;\n vertical-align: -4px;\n font-size: 10px;\n width: 120px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n white-space: nowrap;\n }\n\n .reload-btn {\n cursor: pointer;\n padding-left: 4px;\n padding-right: 2px;\n }\n}\n"]} -------------------------------------------------------------------------------- /stock-mng/src/main/resources/static/static/js/main.b36ca635.chunk.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{120:function(e,t,a){e.exports=a(210)},125:function(e,t,a){},210:function(e,t,a){"use strict";a.r(t);var n=a(0),r=a.n(n),c=a(6),s=a.n(c),o=(a(125),a(9)),i=a.n(o),l=a(13),u=a(18),d=a(19),m=a(22),p=a(20),h=a(23),f=a(213),g=a(216),v=a(214),b=a(211),E=a(2),y=a.n(E),k=a(10),w=a(212),C=a(39),N=a(215),x=function(e){return Math.min(99,Math.max(0,e))};var O=function(e){function t(){var e,a;Object(u.a)(this,t);for(var n=arguments.length,r=new Array(n),c=0;c=0&&r.a.createElement("span",{className:"stock-count"},"\u5e93\u5b58",this.props.stockCount||0,"\u4ef6"))),r.a.createElement("div",{className:"side"},r.a.createElement(O,{value:this.state.orderCount,onChange:function(t){return e.setState({orderCount:t})},disabled:this.state.loading}),r.a.createElement(w.a,{placement:"left",title:"\u786e\u5b9a\u8981\u8ba2\u8d2d".concat(this.state.orderCount,"\u4ef6 ").concat(this.props.name," \u5417\uff1f (\u603b\u4ef7").concat((this.state.orderCount*this.props.price).toFixed(2),"\u5143)"),className:this.state.orderCount?"":"disabled",onConfirm:this.order},r.a.createElement(C.a,{className:"purchase-button",type:"primary",disabled:!this.state.orderCount,loading:this.state.loading},"\u8ba2\u8d2d"))),r.a.createElement("div",{className:y()({hidden:!this.props.enlarged,"item-description":!0,"with-image":!!this.props.imageUrl})},this.props.description?r.a.createElement("div",{dangerouslySetInnerHTML:{__html:this.props.description}}):r.a.createElement("div",{style:{opacity:.7}},"\u5546\u54c1\u6682\u65e0\u8be6\u7ec6\u8bf4\u660e")),this.props.enlarged&&!!this.props.imageUrl&&r.a.createElement("img",{src:"",alt:"",className:"item-image",style:{backgroundImage:"url('".concat(this.props.imageUrl,"')")}}))}}]),t}(n.Component),I=function(e){function t(e,a){var n;return Object(u.a)(this,t),(n=Object(m.a)(this,Object(p.a)(t).call(this,e,a))).updateCurrent=function(){n.state.current!==n.props.balance&&(Math.abs(n.state.current-n.props.balance)<.01?n.setState({current:n.props.balance}):n.setState({current:n.state.current+(n.props.balance-n.state.current)/10}))},n.state={current:n.props.balance},setInterval(n.updateCurrent,10),n}return Object(h.a)(t,e),Object(d.a)(t,[{key:"render",value:function(){return r.a.createElement("div",{className:"balance-component"},"\u53ef\u7528\u4f59\u989d ",null===this.props.balance?r.a.createElement("span",null,"..."):r.a.createElement("span",null,"\xa5 ",(+this.state.current).toFixed(2).replace(/\d(?=(\d{3})+\.)/g,"$&,")))}}]),t}(n.Component),U=a(66),B=function(e){function t(e,a){var n;return Object(u.a)(this,t),(n=Object(m.a)(this,Object(p.a)(t).call(this,e,a))).switchUser=function(){delete localStorage.userName,window.location.reload()},n.state={userName:localStorage.userName},setInterval(function(){n.setState({userName:localStorage.userName})},1e3),n}return Object(h.a)(t,e),Object(d.a)(t,[{key:"render",value:function(){return this.state.userName?r.a.createElement("div",{className:"username-display"},r.a.createElement(k.a,{type:"user"}),r.a.createElement(U.a,{title:"\u7528\u6237\u6807\u8bc6\u7b26: "+this.state.userName,placement:"bottomRight"},r.a.createElement("span",{className:"username-text"},this.state.userName)),r.a.createElement(w.a,{placement:"topLeft",title:"\u786e\u5b9a\u8981\u5207\u6362\u7528\u6237\u5417",onConfirm:this.switchUser},r.a.createElement(k.a,{type:"reload",className:"reload-btn"}))):null}}]),t}(n.Component),q=a(49),F=a.n(q);function H(e){if(!1===e.success)throw new Error("result.success===false")}function M(e){return"/"===window.backendUrl&&(window.backendUrl=window.location.origin),window.backendUrl.endsWith("/")&&(window.backendUrl=window.backendUrl.substr(0,window.backendUrl.length-1)),e.startsWith("/")||(e="/"+e),window.backendUrl+e}window.backendUrl=window.backendUrl||"/";var P={createUser:function(){var e=Object(l.a)(i.a.mark(function e(t){return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,F.a.post(M("/createUser"),{userName:t});case 3:H(e.sent.data),e.next=11;break;case 7:throw e.prev=7,e.t0=e.catch(0),g.a.error("Failed to create user: "+e.t0.message),e.t0;case 11:case"end":return e.stop()}},e,null,[[0,7]])}));return function(t){return e.apply(this,arguments)}}(),query:function(){var e=Object(l.a)(i.a.mark(function e(t){var a;return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,F.a.post(M("/query"),{userName:t});case 3:return H(a=e.sent.data),e.abrupt("return",a);case 8:throw e.prev=8,e.t0=e.catch(0),g.a.error("Failed to query stock: "+e.t0.message),e.t0;case 12:case"end":return e.stop()}},e,null,[[0,8]])}));return function(t){return e.apply(this,arguments)}}(),querySorted:function(){var e=Object(l.a)(i.a.mark(function e(t){var a;return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,F.a.post(M("/querySorted"),{userName:t});case 3:return H(a=e.sent.data),e.abrupt("return",a);case 8:throw e.prev=8,e.t0=e.catch(0),g.a.error("Failed to query stock: "+e.t0.message),e.t0;case 12:case"end":return e.stop()}},e,null,[[0,8]])}));return function(t){return e.apply(this,arguments)}}(),queryBalance:function(){var e=Object(l.a)(i.a.mark(function e(t){var a;return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,F.a.post(M("/queryBalance"),{userName:t});case 3:return H(a=e.sent.data),e.abrupt("return",a);case 8:throw e.prev=8,e.t0=e.catch(0),g.a.error("Failed to query balance: "+e.t0.message),e.t0;case 12:case"end":return e.stop()}},e,null,[[0,8]])}));return function(t){return e.apply(this,arguments)}}(),purchase:function(){var e=Object(l.a)(i.a.mark(function e(t,a,n){var r;return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,F.a.post(M("/purchase"),{userName:t,productCode:a,count:n});case 3:return H(r=e.sent.data),e.abrupt("return",r);case 8:throw e.prev=8,e.t0=e.catch(0),g.a.error("Failed to purchase: "+e.t0.message),e.t0;case 12:case"end":return e.stop()}},e,null,[[0,8]])}));return function(t,a,n){return e.apply(this,arguments)}}()},_=a(118),z=a(116),T=a.n(z),D={footerText:"Alipay \xa92019 / a KubeCon DEMO",initUser:function(){var e=Object(l.a)(i.a.mark(function e(){var t;return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(localStorage.userName){e.next=6;break}return t=T()(),e.next=5,P.createUser(t);case 5:localStorage.userName=t;case 6:case"end":return e.stop()}},e)}));return function(){return e.apply(this,arguments)}}(),convertItems:function(e){return e.map(function(e){var t=Object(_.a)({},e);try{var a=JSON.parse(t.description);t.description=a.description,t.image_url=a.image_url,t.author=a.author}catch(n){}return t})}},L=f.a.Header,A=f.a.Content,J=f.a.Footer,K=function(e){function t(e,a){var n;return Object(u.a)(this,t),(n=Object(m.a)(this,Object(p.a)(t).call(this,e,a))).resizeHandler=function(){n.recalcEnlargedElementStyle&&n.recalcEnlargedElementStyle()},n.refreshStockItems=Object(l.a)(i.a.mark(function e(){var t;return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,P.query(localStorage.userName);case 2:(t=e.sent).forEach(function(e){return e.loading=!1}),n.setState({stockItems:D.convertItems(t)});case 5:case"end":return e.stop()}},e)})),n.refreshBalance=Object(l.a)(i.a.mark(function e(){var t;return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,P.queryBalance(localStorage.userName);case 2:t=e.sent,n.setState({balance:t.balance});case 4:case"end":return e.stop()}},e)})),n.onOrder=function(){var e=Object(l.a)(i.a.mark(function e(t,a){return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,P.purchase(localStorage.userName,t.productCode,a);case 2:g.a.success("\u606d\u559c\uff0c\u8ba2\u8d2d\u6210\u529f"),n.setState({balance:n.state.balance-t.price*a}),t.loading=!0,n.setState({}),n.refreshStockItems(),n.refreshBalance();case 8:case"end":return e.stop()}},e)}));return function(t,a){return e.apply(this,arguments)}}(),n.state={balance:null,stockItems:null,enlargedProductCode:null},n}return Object(h.a)(t,e),Object(d.a)(t,[{key:"componentDidMount",value:function(){var e=Object(l.a)(i.a.mark(function e(){return i.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,D.initUser();case 2:return this.refreshBalance(),e.next=5,this.refreshStockItems();case 5:window.addEventListener("resize",this.resizeHandler);case 6:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.resizeHandler)}},{key:"render",value:function(){var e=this;return r.a.createElement(f.a,{className:"layout"},r.a.createElement(L,null,r.a.createElement("div",{className:"logo"},"KubeCon DEMO")),r.a.createElement(A,{style:{padding:"0 50px"}},r.a.createElement(v.a,{style:{margin:"16px 0"}},r.a.createElement(v.a.Item,null,"Home"),r.a.createElement(v.a.Item,null,"\u5e93\u5b58\u7ba1\u7406")),r.a.createElement(B,null),r.a.createElement("div",{style:{background:"#fff",padding:24,minHeight:280},id:"stock-item-list"},!this.state.stockItems&&r.a.createElement("div",{className:"spin-div"},r.a.createElement(b.a,null)),!!this.state.stockItems&&this.state.stockItems.map(function(t){return r.a.createElement(S,{key:t.productCode,name:t.name,description:t.description,author:t.author,imageUrl:t.image_url,ownedCount:t.ownedCount,stockCount:t.stockCount,price:t.price,loading:t.loading,enlarged:e.state.enlargedProductCode===t.productCode,hidden:!!e.state.enlargedProductCode&&e.state.enlargedProductCode!==t.productCode,onEnlarge:function(){return e.onEnlarge(t.productCode)},onShrink:function(){return e.onShrink()},onOrder:function(a){return e.onOrder(t,a)}})}))),r.a.createElement(I,{balance:this.state.balance}),r.a.createElement(J,{style:{textAlign:"center"}},D.footerText))}},{key:"onEnlarge",value:function(e){var t=this;this.setState({enlargedProductCode:e},function(){for(var e=document.getElementById("stock-item-list"),a=e.getBoundingClientRect(),n=-1,r={},c=0;c=0){n=c;var o=s.y-a.y-24,i=a.y+a.height-s.y-s.height-24;r[c]="margin-top:".concat(o,"px;margin-bottom:").concat(i,"px;height:").concat(s.height,"px;")}else e.children[c].className.indexOf("hidden")>=0&&(r[c]="position:absolute; width:".concat(s.width,"px; top:").concat(s.y,"px"))}Object.keys(r).forEach(function(t){e.children[t].style=r[t]}),n>=0&&(t.recalcEnlargedElementStyle=function(){var t=.35.toFixed(2)+"s",r=document.body.clientHeight-a.y-window.scrollY-48-105;e.children[n].style="transition: opacity ".concat(t,", margin-top ").concat(t,", margin-bottom ").concat(t,", height ").concat(t,";margin-top:0; margin-bottom:0; height:").concat(r,"px")},setTimeout(t.recalcEnlargedElementStyle,350/3))})}},{key:"onShrink",value:function(){this.recalcEnlargedElementStyle=null;for(var e=document.getElementById("stock-item-list"),t=0;t Math.min(99, Math.max(0, val));\n\nfunction isInteger(value) {\n return !isNaN(value) && parseInt(value) === +value;\n}\n\nclass Number extends Component {\n onChange = e => {\n if (!e.target.value) {\n this.props.onChange(0);\n return;\n }\n if (!isInteger(e.target.value)) {\n return;\n }\n this.props.onChange(normalizeVal(+e.target.value));\n };\n\n increment = (delta) => {\n let val = this.props.value + delta;\n if (isNaN(val)) {\n val = 0;\n }\n this.props.onChange(normalizeVal(val));\n };\n\n render() {\n return
\n \n \n \n \n \n
\n }\n\n}\n\nexport default Number;","import React, {Component} from 'react';\nimport classnames from \"classnames\";\nimport {Button, Popconfirm, Spin, Icon} from 'antd';\nimport Number from \"./Number\";\n\nconst noop = () => {\n};\n\nclass StockItem extends Component {\n\n order = async () => {\n this.setState({loading: true});\n try {\n await this.props.onOrder(this.state.orderCount);\n } catch {\n }\n this.setState({orderCount: 0, loading: false});\n };\n\n constructor(props, context) {\n super(props, context);\n this.state = {\n orderCount: 0,\n loading: false,\n };\n }\n\n render() {\n return
\n
\n
\n \n \n \n {this.props.name}\n {this.props.author}\n
\n
\n 单价: ¥{(+this.props.price).toFixed(2)}\n
\n\n {!!this.props.imageUrl &&
\n \"\"\n
}\n\n {this.props.loading ?
\n  \n \n
:\n
\n 已购: {this.props.ownedCount || 0} 件\n {this.props.stockCount >= 0 &&\n 库存{this.props.stockCount || 0}件}\n
}\n
\n
\n this.setState({orderCount})}\n disabled={this.state.loading}\n />\n \n 订购\n \n
\n\n
\n {!!this.props.description ?\n
\n :\n
\n 商品暂无详细说明\n
}\n
\n {this.props.enlarged && !!this.props.imageUrl &&\n \"\"}\n
\n }\n}\n\nexport default StockItem;","import React, {Component} from 'react';\n\nclass Balance extends Component {\n\n updateCurrent = () => {\n if (this.state.current === this.props.balance) {\n return;\n }\n if (Math.abs(this.state.current - this.props.balance) < 0.01) {\n this.setState({\n current: this.props.balance\n })\n } else {\n this.setState({\n current: this.state.current + (this.props.balance - this.state.current) / 10\n })\n }\n };\n\n constructor(props, context) {\n super(props, context);\n this.state = {current: this.props.balance};\n\n setInterval(this.updateCurrent, 10);\n }\n\n render() {\n return
\n 可用余额 {this.props.balance === null ? ... :\n ¥ {(+this.state.current).toFixed(2).replace(/\\d(?=(\\d{3})+\\.)/g, '$&,')}}\n
\n }\n\n}\n\nexport default Balance;","import React, {Component} from 'react';\nimport {Icon, Popconfirm, Tooltip} from \"antd\";\n\nclass UserName extends Component {\n\n constructor(props, context) {\n super(props, context);\n this.state = {\n userName: localStorage['userName']\n };\n setInterval(() => {\n this.setState({userName: localStorage['userName']})\n }, 1000);\n }\n\n switchUser = () => {\n delete localStorage[\"userName\"];\n window.location.reload();\n };\n\n render() {\n if (!this.state.userName) {\n return null;\n }\n return
\n \n \n {this.state.userName}\n \n \n \n \n
\n }\n}\n\nexport default UserName;","import axios from 'axios';\nimport {message} from \"antd\";\n\nwindow.backendUrl = window.backendUrl || \"/\";\n\nfunction assertResult(result) {\n if (result.success === false) {\n throw new Error(\"result.success===false\");\n }\n}\n\nfunction getBackendUrl(path) {\n if (window.backendUrl === '/') {\n window.backendUrl = window.location.origin;\n }\n if (window.backendUrl.endsWith(\"/\")) {\n window.backendUrl = window.backendUrl.substr(0, window.backendUrl.length - 1);\n }\n\n if (!path.startsWith(\"/\")) {\n path = \"/\" + path;\n }\n\n return window.backendUrl + path;\n}\n\nexport default {\n async createUser(userName) {\n try {\n const result = (await axios.post(getBackendUrl(\"/createUser\"), {userName})).data;\n assertResult(result);\n } catch (e) {\n message.error(\"Failed to create user: \" + e.message);\n throw e;\n }\n },\n async query(userName) {\n try {\n const result = (await axios.post(getBackendUrl(\"/query\"), {userName})).data;\n assertResult(result);\n return result;\n } catch (e) {\n message.error(\"Failed to query stock: \" + e.message);\n throw e;\n }\n },\n async querySorted(userName) {\n try {\n const result = (await axios.post(getBackendUrl(\"/querySorted\"), {userName})).data;\n assertResult(result);\n return result;\n } catch (e) {\n message.error(\"Failed to query stock: \" + e.message);\n throw e;\n }\n },\n async queryBalance(userName) {\n try {\n const result = (await axios.post(getBackendUrl(\"/queryBalance\"), {userName})).data;\n assertResult(result);\n return result;\n } catch (e) {\n message.error(\"Failed to query balance: \" + e.message);\n throw e;\n }\n },\n async purchase(userName, productCode, count) {\n try {\n const result = (await axios.post(getBackendUrl(\"/purchase\"), {userName, productCode, count})).data;\n assertResult(result);\n return result;\n } catch (e) {\n message.error(\"Failed to purchase: \" + e.message);\n throw e;\n }\n },\n}","import uuid from \"uuid/v4\";\nimport Service from \"./Service\";\n\nexport default {\n footerText: \"Alipay ©2019 / a KubeCon DEMO\",\n async initUser() {\n const userName = localStorage[\"userName\"];\n if (!userName) {\n let userName = uuid();\n await Service.createUser(userName);\n localStorage[\"userName\"] = userName;\n // message.success(\"已成功初始化新的用户。\");\n }\n },\n convertItems: function (items) {\n return items.map(a => {\n let item = {...a};\n\n try {\n let parsedDescription = JSON.parse(item.description);\n item.description = parsedDescription.description;\n item.image_url = parsedDescription.image_url;\n item.author = parsedDescription.author;\n } catch (e) {\n }\n return item;\n });\n }\n\n};","import React, {Component} from 'react';\nimport {Breadcrumb, Layout, message, Spin} from 'antd';\nimport StockItem from \"./StockItem\";\nimport Balance from \"./Balance\";\nimport UserName from \"./UserName\";\nimport Service from './Service'\nimport common from \"./common\";\n\nconst {Header, Content, Footer} = Layout;\n\nclass App extends Component {\n\n constructor(props, context) {\n super(props, context);\n this.state = {balance: null, stockItems: null, enlargedProductCode: null};\n }\n\n resizeHandler = () => {\n if (this.recalcEnlargedElementStyle) {\n this.recalcEnlargedElementStyle();\n }\n };\n\n async componentDidMount() {\n await common.initUser();\n this.refreshBalance();\n await this.refreshStockItems();\n\n window.addEventListener(\"resize\", this.resizeHandler);\n }\n\n componentWillUnmount() {\n window.removeEventListener(\"resize\", this.resizeHandler);\n\n }\n\n refreshStockItems = async () => {\n let items = await Service.query(localStorage[\"userName\"]);\n items.forEach(item => item.loading = false);\n this.setState({stockItems: common.convertItems(items)});\n };\n\n refreshBalance = async () => {\n let result = await Service.queryBalance(localStorage[\"userName\"]);\n this.setState({balance: result.balance});\n };\n\n onOrder = async (item, count) => {\n await Service.purchase(localStorage[\"userName\"], item.productCode, count);\n message.success(\"恭喜,订购成功\");\n this.setState({balance: this.state.balance - item.price * count});\n item.loading = true;\n this.setState({});\n this.refreshStockItems();\n this.refreshBalance();\n };\n\n render() {\n return (\n \n
\n
KubeCon DEMO
\n
\n \n \n Home\n 库存管理\n \n \n
\n {!this.state.stockItems &&
}\n {!!this.state.stockItems\n && this.state.stockItems.map(item =>\n
\n
\n\n \n
{common.footerText}
\n
\n );\n }\n\n onEnlarge(productCode) {\n this.setState({enlargedProductCode: productCode}, () => {\n const CONTAINER_PADDING = 24;\n const FOOTER_HEIGHT = 105;\n const TRANSITION_DURATION = 350;\n const list = document.getElementById(\"stock-item-list\");\n const listRect = list.getBoundingClientRect();\n let enlargeIndex = -1;\n const styleToApply = {};\n for (let i = 0; i < list.children.length; i++) {\n let rect = list.children[i].getBoundingClientRect();\n if (list.children[i].className.indexOf(\"enlarged\") >= 0) {\n enlargeIndex = i;\n let marginTop = rect.y - listRect.y - CONTAINER_PADDING;\n let marginBottom = listRect.y + listRect.height - rect.y - rect.height - CONTAINER_PADDING;\n styleToApply[i] = `margin-top:${marginTop}px;margin-bottom:${marginBottom}px;height:${rect.height}px;`;\n } else if (list.children[i].className.indexOf(\"hidden\") >= 0) {\n styleToApply[i] = `position:absolute; width:${rect.width}px; top:${rect.y}px`;\n }\n }\n\n Object.keys(styleToApply).forEach(key => {\n list.children[key].style = styleToApply[key];\n });\n\n if (enlargeIndex >= 0) {\n this.recalcEnlargedElementStyle = () => {\n const TRANSITION_DURATION_TEXT = (TRANSITION_DURATION / 1000).toFixed(2) + \"s\";\n let height = document.body.clientHeight - listRect.y - window.scrollY - CONTAINER_PADDING * 2 - FOOTER_HEIGHT;\n list.children[enlargeIndex].style = `transition: opacity ${TRANSITION_DURATION_TEXT}, margin-top ${TRANSITION_DURATION_TEXT}, margin-bottom ${TRANSITION_DURATION_TEXT}, height ${TRANSITION_DURATION_TEXT};margin-top:0; margin-bottom:0; height:${height}px`;\n };\n setTimeout(this.recalcEnlargedElementStyle, TRANSITION_DURATION / 3);\n }\n });\n }\n\n onShrink() {\n this.recalcEnlargedElementStyle = null;\n const list = document.getElementById(\"stock-item-list\");\n for (let i = 0; i < list.children.length; i++) {\n list.children[i].style = \"\";\n }\n this.setState({enlargedProductCode: null});\n }\n}\n\nexport default App;","import React, {Component} from 'react';\nimport {Breadcrumb, Layout, Spin} from 'antd';\nimport common from './common'\nimport Service from \"./Service\";\n\nconst {Header, Content, Footer} = Layout;\n\nclass SortedListApp extends Component {\n\n constructor(props, context) {\n super(props, context);\n this.state = {balance: null, stockItems: null, enlargedProductCode: null};\n }\n\n async componentDidMount() {\n await common.initUser();\n await this.refreshStockItems();\n }\n\n refreshStockItems = async () => {\n let items = await Service.querySorted(localStorage[\"userName\"]);\n items.forEach(item => item.loading = false);\n this.setState({stockItems: common.convertItems(items)}, () => console.log(this.state));\n };\n\n render() {\n return (\n \n
\n
KubeCon DEMO
\n
\n \n \n Home\n 库存管理\n \n
\n {!this.state.stockItems &&
}\n {!!this.state.stockItems\n && this.state.stockItems.map(item =>\n
\n
\n
\n {item.name}\n
\n {!!item.image_url &&
\n \"\"\n
}\n {!!item.author &&
\n {item.author}\n
}\n
\n
)}\n
\n
\n
{common.footerText}
\n
\n );\n }\n}\n\nexport default SortedListApp;","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.scss';\nimport App from './App';\nimport SortedListApp from './SortedListApp';\n\nwindow.backendUrl = \"http://localhost:8080\";\n\nif (window.location.hash === '#list') {\n ReactDOM.render(, document.getElementById('root'));\n} else {\n ReactDOM.render(, document.getElementById('root'));\n}\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /stock-mng/src/main/resources/static/static/js/runtime~main.a8a9905a.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c