From 08bf664117fc38a7b3c7829a7bc401f601c961f8 Mon Sep 17 00:00:00 2001 From: tanghc Date: Sat, 23 Mar 2019 18:01:48 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=96=E5=86=99=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +- doc/README.md | 9 ++ doc/docs/README.md | 1 + doc/docs/_config.yml | 1 + doc/docs/_coverpage.md | 12 ++ doc/docs/_navbar.md | 3 + doc/docs/_sidebar.md | 6 + doc/docs/files/1001_快速体验.md | 20 +++ doc/docs/files/1002_新增接口.md | 140 ++++++++++++++++++ doc/docs/files/1003_业务参数校验.md | 72 +++++++++ doc/docs/files/1004_错误处理.md | 91 ++++++++++++ doc/docs/index.html | 68 +++++++++ doc/docs/请不要删除其它文件.txt | 0 doc/pom.xml | 49 ++++++ doc/run.sh | 1 + doc/server.js | 55 +++++++ .../java/com/gitee/sop/doc/SidebarTest.java | 39 +++++ pom.xml | 1 + .../zuul/result/ZuulResultExecutor.java | 18 ++- .../DefaultGlobalExceptionHandler.java | 4 +- .../exception/ServiceException.java | 7 + .../message/ServiceErrorEnum.java | 5 +- .../param/ServiceParamValidator.java | 2 +- sop-story/sop-story-web/pom.xml | 6 + .../sop/bookweb/config/OpenServiceConfig.java | 8 + .../bookweb/controller/AlipayController.java | 2 - .../controller/JSR303DemoController.java | 18 +++ .../controller/StoryDemoController.java | 21 +++ .../ThrowExceptionDemoController.java | 33 +++++ .../bookweb/controller/param/GoodsParam.java | 19 +++ .../controller/param/GoodsUpdateParam.java | 9 ++ .../sop/bookweb/message/GoodsErrorEnum.java | 20 +++ .../resources/i18n/isp/bizerror_en.properties | 9 +- .../i18n/isp/bizerror_zh_CN.properties | 10 ++ .../i18n/isp/goods_error_en.properties | 2 + .../i18n/isp/goods_error_zh_CN.properties | 5 + .../com/gitee/sop/AlipayClientPostTest.java | 8 - .../java/com/gitee/sop/JSR303DemoTest.java | 58 ++++++++ .../java/com/gitee/sop/StoryDemoTest.java | 87 +++++++++++ .../com/gitee/sop/TaobaoClientPostTest.java | 8 - .../src/test/java/com/gitee/sop/TestBase.java | 7 + .../com/gitee/sop/ThrowExceptionDemoTest.java | 58 ++++++++ 42 files changed, 968 insertions(+), 33 deletions(-) create mode 100644 doc/README.md create mode 100644 doc/docs/README.md create mode 100644 doc/docs/_config.yml create mode 100644 doc/docs/_coverpage.md create mode 100644 doc/docs/_navbar.md create mode 100644 doc/docs/_sidebar.md create mode 100644 doc/docs/files/1001_快速体验.md create mode 100644 doc/docs/files/1002_新增接口.md create mode 100644 doc/docs/files/1003_业务参数校验.md create mode 100644 doc/docs/files/1004_错误处理.md create mode 100644 doc/docs/index.html create mode 100644 doc/docs/请不要删除其它文件.txt create mode 100644 doc/pom.xml create mode 100644 doc/run.sh create mode 100644 doc/server.js create mode 100644 doc/src/main/java/com/gitee/sop/doc/SidebarTest.java create mode 100644 sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/JSR303DemoController.java create mode 100644 sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/StoryDemoController.java create mode 100644 sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/ThrowExceptionDemoController.java create mode 100644 sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsParam.java create mode 100644 sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsUpdateParam.java create mode 100644 sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/message/GoodsErrorEnum.java create mode 100644 sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_en.properties create mode 100644 sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_zh_CN.properties create mode 100644 sop-test/src/test/java/com/gitee/sop/JSR303DemoTest.java create mode 100644 sop-test/src/test/java/com/gitee/sop/StoryDemoTest.java create mode 100644 sop-test/src/test/java/com/gitee/sop/ThrowExceptionDemoTest.java diff --git a/README.md b/README.md index 3b67493c..266a929c 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ SOP封装了开放平台大部分功能包括:签名验证、统一异常处 > 运行环境:JDK8,Maven3,Zookeeper +- doc:开发文档 - sop-registry:注册中心,eureka实现 - sop-gateway:网关,统一访问入口,Spring Cloud Zuul实现,可切换成Spring Cloud Gateway - sop-common:公共模块,封装常用功能,包含签名校验、错误处理等功能 @@ -64,14 +65,6 @@ SOP封装了开放平台大部分功能包括:签名验证、统一异常处 - sop-book:微服务示例,book服务,也是Consumer,调用story提供的服务 - sop-test:接口调用测试用例 -## 快速开始 - -- 安装并启动zookeeper,[安装教程](http://zookeeper.apache.org/doc/r3.4.13/zookeeperStarted.html) -- IDE打开项目(IDEA下可以打开根pom.xml,然后open as project) -- 启动注册中心,sop-registry(运行SopRegistryApplication.java) -- 启动微服务:sop-story-web(运行SopStoryApplication.java) -- 启动网关:sop-gateway(运行SopGatewayApplication.java) -- 找到sop-test,打开测试用例,进行接口调用测试,运行com.gitee.sop.AlipayClientPostTest.testPost() ## 相关文档 diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 00000000..1268ba28 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,9 @@ +# 开发文档 + +配合gitee pages服务使用,gitee pages服务指定docs目录。 + +## 本地查看开发文档 + +- 前提:先安装好npm,[npm安装教程](https://blog.csdn.net/zhangwenwu2/article/details/52778521)。建议使用淘宝镜像。 +- 安装docsify,执行npm命令`npm i docsify-cli -g --registry=https://registry.npm.taobao.org` +- cd到当前目录,运行命令`docsify serve docs`,然后访问:`http://localhost:3000`即可查看。 diff --git a/doc/docs/README.md b/doc/docs/README.md new file mode 100644 index 00000000..b09d74d2 --- /dev/null +++ b/doc/docs/README.md @@ -0,0 +1 @@ +# 开发文档 diff --git a/doc/docs/_config.yml b/doc/docs/_config.yml new file mode 100644 index 00000000..aa7fd0af --- /dev/null +++ b/doc/docs/_config.yml @@ -0,0 +1 @@ +include: [_navbar,_sidebar] \ No newline at end of file diff --git a/doc/docs/_coverpage.md b/doc/docs/_coverpage.md new file mode 100644 index 00000000..55969331 --- /dev/null +++ b/doc/docs/_coverpage.md @@ -0,0 +1,12 @@ +![logo](_media/icon.svg) + +# docsify 4.6.10 + +> A magical documentation site generator. + +* Simple and lightweight (~19kB gzipped) +* No statically built html files +* Multiple themes + +[GitHub](https://github.com/QingWei-Li/docsify/) +[Get Started](#docsify) diff --git a/doc/docs/_navbar.md b/doc/docs/_navbar.md new file mode 100644 index 00000000..deff3845 --- /dev/null +++ b/doc/docs/_navbar.md @@ -0,0 +1,3 @@ +- 关于 + - [帮助](/zh-cn/) + - [API](/) diff --git a/doc/docs/_sidebar.md b/doc/docs/_sidebar.md new file mode 100644 index 00000000..965e5749 --- /dev/null +++ b/doc/docs/_sidebar.md @@ -0,0 +1,6 @@ +* 文档目录 + + * [错误处理](files/1004_错误处理.md?t=1553335182242) + * [新增接口](files/1002_新增接口.md?t=1553335182268) + * [快速体验](files/1001_快速体验.md?t=1553335182268) + * [业务参数校验](files/1003_业务参数校验.md?t=1553335182268) diff --git a/doc/docs/files/1001_快速体验.md b/doc/docs/files/1001_快速体验.md new file mode 100644 index 00000000..a9f01d79 --- /dev/null +++ b/doc/docs/files/1001_快速体验.md @@ -0,0 +1,20 @@ +# 快速体验 + +> 运行环境:JDK8,Maven3,Zookeeper + +- 安装并启动zookeeper,[安装教程](http://zookeeper.apache.org/doc/r3.4.13/zookeeperStarted.html) +- IDE打开项目(IDEA下可以打开根pom.xml,然后open as project) +- 启动注册中心,sop-registry(运行SopRegistryApplication.java) +- 启动微服务:sop-story-web(运行SopStoryApplication.java) +- 启动网关:sop-gateway(运行SopGatewayApplication.java) +- 找到sop-test,打开测试用例,进行接口调用测试,运行com.gitee.sop.AlipayClientPostTest.testPost() + +确保注册中心先启动 + +## 使用admin + +- 找到`sop-admin/sop-admin-server`工程,运行`com.gitee.sop.adminserver.SopAdminServerApplication.java` +- 找到`sop-admin/sop-admin-front/index.html`文件,在IDEA下直接右键--Run'index.html' +- 如果没有用到IDEA,则需要把sop-admin-front放到静态服务器中然后访问index.html + + diff --git a/doc/docs/files/1002_新增接口.md b/doc/docs/files/1002_新增接口.md new file mode 100644 index 00000000..c83314ca --- /dev/null +++ b/doc/docs/files/1002_新增接口.md @@ -0,0 +1,140 @@ +# 新增接口 + +以story服务为例,新增一个获取故事内容接口 + +- 在controller下新建一个类,StoryDemoController.java +- 加上`@RestController`注解 + +```java +@RestController +public class StoryDemoController { + +} +``` + +- 新增一个接口 + +```java +@ApiMapping(value = "story.demo.get") +public Story getStory() { + Story story = new Story(); + story.setId(1); + story.setName("白雪公主"); + return story; +} +``` + +这里的`@ApiMapping`注解作用同`@RequestMapping`注解,可以理解为是它的扩展 + +value就是接口名,对应客户端的`method`参数 + +如果要加上版本号,指定`version`参数:`@ApiMapping(value = "story.demo.get", version = "2.0")` + +- 重启story服务,这样接口就可以使用了。 + +## 测试接口 + +- 在sop-test工程下新建一个测试用例,`StoryDemoTest`,继承TestBase + +```java +public class StoryDemoTest extends TestBase { + + String url = "http://localhost:8081/api"; // zuul + String appId = "alipay_test"; + // 私钥 + String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ="; + + + @Test + public void testDemo() throws Exception { + // 公共请求参数 + Map params = new HashMap(); + params.put("app_id", appId); + // 这里对应@ApiMapping.value属性 + params.put("method", "story.demo.get"); + params.put("format", "json"); + params.put("charset", "utf-8"); + params.put("sign_type", "RSA2"); + params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + // 这里对应@ApiMapping.version属性 + params.put("version", "1.0"); + + // 业务参数 + Map bizContent = new HashMap<>(); + + params.put("biz_content", JSON.toJSONString(bizContent)); + + System.out.println("----------- 请求信息 -----------"); + System.out.println("请求参数:" + buildParamQuery(params)); + System.out.println("商户秘钥:" + privateKey); + String content = AlipaySignature.getSignContent(params); + System.out.println("待签名内容:" + content); + String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8"); + System.out.println("签名(sign):" + sign); + + params.put("sign", sign); + + System.out.println("----------- 返回结果 -----------"); + String responseData = post(url, params);// 发送请求 + System.out.println(responseData); + } + +} +``` + +- 请求成功后,控制台会打印: + +``` +----------- 请求信息 ----------- +请求参数:charset=utf-8&biz_content={}&method=story.demo.get&format=json&app_id=alipay_test&sign_type=RSA2&version=1.0×tamp=2019-03-23 15:41:22 +商户秘钥:MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ= +待签名内容:app_id=alipay_test&biz_content={}&charset=utf-8&format=json&method=story.demo.get&sign_type=RSA2×tamp=2019-03-23 15:41:22&version=1.0 +签名(sign):YMbxTPdovi6htcn1K3USTS6/Tbg6MOAMigG6x/kG0kQFCYH8ljvxXzcY86UT056nUG3OXxnj0xkw07eV6E03HMlu7bn3/jrT3PCcV3YguhA92aWz720x2xJWdfXY13OUPS9VOCC9zIVxu6EBD+PoZ7ojYChYvOfCR5I8bR/oOc0ZLjK63PWTBdf0eFS4sybXzRf81uNLMROsMhmBDDy0Fhml3ml77qzWBIpsmq5ECZ+89rMPbkNhAUcnFAe7ik7xZIL6WcUhAOhKVa8ZQK1GMjoGnAbGRed1FbuOHZGubgffg4/vMqrY10Bcy6h9jt/zK5w9L3HVgK3aPgQlfP16Gg== +----------- 返回结果 ----------- +{"story_demo_get_response":{"msg":"Success","code":"10000","name":"白雪公主","id":1},"sign":"YMbxTPdovi6htcn1K3USTS6/Tbg6MOAMigG6x/kG0kQFCYH8ljvxXzcY86UT056nUG3OXxnj0xkw07eV6E03HMlu7bn3/jrT3PCcV3YguhA92aWz720x2xJWdfXY13OUPS9VOCC9zIVxu6EBD+PoZ7ojYChYvOfCR5I8bR/oOc0ZLjK63PWTBdf0eFS4sybXzRf81uNLMROsMhmBDDy0Fhml3ml77qzWBIpsmq5ECZ+89rMPbkNhAUcnFAe7ik7xZIL6WcUhAOhKVa8ZQK1GMjoGnAbGRed1FbuOHZGubgffg4/vMqrY10Bcy6h9jt/zK5w9L3HVgK3aPgQlfP16Gg=="} +``` + +## 开放现有接口 + +如果想把现有项目中的接口开放出去,提供给客户调用,具体操作如下: + +以springboot项目为例,springmvc目前暂不支持,以后可以支持。 + +- 将现有项目注册到注册中心 +- 在现有接口方法上加上一个注解`@ApiAbility`,如下面这个接口 + +```java +// 遗留接口具备开放平台能力 +@ApiAbility +@RequestMapping("getStory2") +public Story getStory2_0() { + Story story = new Story(); + story.setId(1); + story.setName("海底小纵队(默认版本号)"); + return story; +} +``` + +这种情况下,老接口依然能正常访问,同时开放平台也能访问进来。 + +**注意** 此时的开放接口对应的接口名为:类@RequestMapping.value + "." + 方法@RequestMapping.value + +举个列子: + +```java +@RequestMapping("goods") +public class MyController { + @ApiAbility + @RequestMapping("listGoods") + public Object fun() { + + } +} +``` + +fun接口对应的路径为:`/goods/listGoods` + +那么对应开放平台的接口名会转换成:`goods.listGoods`,客户端的method参数要填`goods.listGoods` + +当然也可以直接把@RequestMapping替换成`@ApiMapping`并指定接口名,这样的话不能兼容以前的访问形式。 + diff --git a/doc/docs/files/1003_业务参数校验.md b/doc/docs/files/1003_业务参数校验.md new file mode 100644 index 00000000..99599216 --- /dev/null +++ b/doc/docs/files/1003_业务参数校验.md @@ -0,0 +1,72 @@ +# 业务参数校验 + +业务参数校验采用JSR-303方式,关于JSR-303介绍可以参考这篇博文:[JSR 303 - Bean Validation 介绍及最佳实践](https://www.ibm.com/developerworks/cn/java/j-lo-jsr303/) + +在参数中使用注解即可,框架会自动进行验证。如下面一个添加商品接口,它的参数是GoodsParam + +```java +@ApiMapping(value = "goods.add") +public void addGoods(GoodsParam param) { + ... +} +``` +在GoodsParam中添加JSR-303注解: + +```java +@Data +public class GoodsParam { + @NotEmpty(message = "商品名称不能为空") + private String goods_name; +} +``` + +如果不传商品名称则返回 + +``` +{"goods_add_response":{"msg":"Success","code":"10000","sub_msg":"商品名称不能为空","sub_code":"isv.invalid-parameter"},"sign":"Eh3Z5CxDCHsb4MyYFVxsPSmBpwVi1LISJdOkrzglxXoqG7RVyEOt4ef1kNpznUvMI3FDQU1suR7Rsmx6NjGdEVS6NSH2Kt0d8TFBRpLhWz8hApnxOtgzqMqbYeMuJie7X5gF6m8hTnvuuxF21IrkixMe+lyBcXw7dk0C3w1SwdEZkHQ+xC+M4bLqAZt5/3kl79/FWSMFJWHiZmg5YeEi8e8XhYCNcz+xlJRJL0x2Y87fFxqSY0UYWNxbQHgdVI8xRfn1n31nzkcLxiAtTh4LPtNRrG7w7absK/C1Oi/vczuBlFeq2EWUsYVWOVpKiJifUwvYVUUsztSLElzplzOjbg=="} + +``` + +## 参数校验国际化 + +国际化的配置方式如下: + +```java +@NotEmpty(message = "{goods.remark.notNull}") +private String goods_remark; +``` + +国际化资源文件`bizerror_en.properties`中添加: +``` +goods.remark.notNull=The goods_remark can not be null +``` + +bizerror_zh_CN.properties中添加: + +``` +# 商品备注不能为空 +goods.remark.notNull=\u5546\u54c1\u5907\u6ce8\u4e0d\u80fd\u4e3a\u7a7a +``` + +## 参数校验国际化传参 + +下面校验商品评论的长度,要求大于等于3且小于等于20。数字3和20要填充到国际化资源中去。 + +``` +// 传参的格式:{xxx}=value1,value2... +@Length(min = 3, max = 20, message = "{goods.comment.length}=3,20") +private String goods_comment; +``` + +bizerror_en.properties: +``` +goods.comment.length=The goods_comment length must >= {0} and <= {1} +``` + +bizerror_zh_CN.properties中添加: + +``` +# 商品评论长度必须在{0}和{1}之间 +goods.comment.length=\u5546\u54c1\u8bc4\u8bba\u957f\u5ea6\u5fc5\u987b\u5728{0}\u548c{1}\u4e4b\u95f4 +``` +这样value1,value2会分别填充到{0},{1}中 diff --git a/doc/docs/files/1004_错误处理.md b/doc/docs/files/1004_错误处理.md new file mode 100644 index 00000000..e2c3df63 --- /dev/null +++ b/doc/docs/files/1004_错误处理.md @@ -0,0 +1,91 @@ +# 错误处理 + +SOP对错误处理已经封装好了,简单做法是`throw ServiceException`,在最顶层的Controller会做统一处理。例如: + +```java +if(StringUtils.isEmpty(param.getGoods_name())) { + throw new ServiceException("goods_name不能为null"); +} +``` + +为了保证编码风格的一致性,推荐统一使用ServiceException + +## i18n国际化 + +SOP支持国际化消息。通过Request对象中的getLocale()来决定具体返回那种语言,客户端通过设置Accept-Language头部来决定返回哪种语言,中文是zh,英文是en。 + +SOP通过模块化来管理国际化消息,这样做的好处结构清晰,维护方便。下面就来讲解如何设置国际化消息。 + +以story服务为例,假设我们要对商品模块进行设置,步骤如下: + +- 在`resource/i18n/isp`目录下新建goods_error_zh_CN.properties属性文件 + +属性文件的文件名有规律, **i18n/isp/goods_error** 表示模块路径, **_zh_CN.properties** 表示中文错误消息。如果要使用英文错误,则新建一个`goods_error_en.properties`即可。 + +- 在goods_error_zh_CN.properties中配置错误信息 + +``` +# 商品名字不能为空 +isp.goods_error_100=\u5546\u54C1\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +``` + +isp.goods_error_为固定前缀,100为错误码,这两个值后续会用到。 + +接下来是把属性文件加载到国际化容器当中。 + +- 添加国际化配置,在OpenServiceConfig中的static块中添加,代码如下: + +```java +@Configuration +public class OpenServiceConfig extends AlipayServiceConfiguration { + + static { + ServiceConfig.getInstance().getI18nModules().add("i18n/isp/goods_error"); + } +} +``` + +- 新建一个枚举用来定义错误 + +```java +// 按模块来定义异常消息,团队开发可以分开进行 +public enum GoodsErrorEnum { + /** 参数错误 */ + NO_GOODS_NAME("100"), + ; + private ServiceErrorMeta errorMeta; + + StoryErrorEnum(String subCode) { + this.errorMeta = new ServiceErrorMeta("isp.goods_error_", subCode); + } + + public ServiceErrorMeta getErrorMeta() { + return errorMeta; + } +} + +``` + +接下来就可以使用了 + +```java +if (StringUtils.isEmpty(param.getGoods_name())) { + throw GoodsErrorEnum.NO_GOODS_NAME.getErrorMeta().getException(); +} +``` + +### 国际化消息传参 + +即代码中变量传入到properties文件中去,做法是采用{0},{1}占位符。0代表第一个参数,1表示第二个参数。 + +``` +# 商品名称太短,不能小于{0}个字 +isp.goods_error_101=\u5546\u54C1\u540D\u79F0\u592A\u77ED\uFF0C\u4E0D\u80FD\u5C0F\u4E8E{0}\u4E2A\u5B57 +``` + +```java +if (param.getGoods_name().length() <= 3) { + throw GoodsErrorEnum.LESS_GOODS_NAME_LEN.getErrorMeta().getException(3); +} +``` +直接放进getException(Object... params)方法参数中,因为是可变参数,可随意放。 \ No newline at end of file diff --git a/doc/docs/index.html b/doc/docs/index.html new file mode 100644 index 00000000..30b1d454 --- /dev/null +++ b/doc/docs/index.html @@ -0,0 +1,68 @@ + + + + + + SOP开发文档 + + + + + + + + + + + + + + +
加载中 ...
+ + + + + + + + + + + \ No newline at end of file diff --git a/doc/docs/请不要删除其它文件.txt b/doc/docs/请不要删除其它文件.txt new file mode 100644 index 00000000..e69de29b diff --git a/doc/pom.xml b/doc/pom.xml new file mode 100644 index 00000000..d80c9855 --- /dev/null +++ b/doc/pom.xml @@ -0,0 +1,49 @@ + + 4.0.0 + com.gitee.sop + doc + 1.0.0-SNAPSHOT + + + + 1.8 + UTF-8 + UTF-8 + + + + + junit + junit + 4.8 + test + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + diff --git a/doc/run.sh b/doc/run.sh new file mode 100644 index 00000000..8354ccf3 --- /dev/null +++ b/doc/run.sh @@ -0,0 +1 @@ +docsify serve docs diff --git a/doc/server.js b/doc/server.js new file mode 100644 index 00000000..785e4575 --- /dev/null +++ b/doc/server.js @@ -0,0 +1,55 @@ +const liveServer = require('live-server') +const isSSR = !!process.env.SSR +const middleware = [] + +if (isSSR) { + const Renderer = require('./packages/docsify-server-renderer/build.js') + const renderer = new Renderer({ + template: ` + + + + + docsify + + + + + + + + + `, + config: { + name: 'docsify', + repo: 'docsifyjs/docsify', + basePath: 'https://docsify.js.org/', + loadNavbar: true, + loadSidebar: true, + subMaxLevel: 3, + auto2top: true, + alias: { + '/de-de/changelog': '/changelog', + '/zh-cn/changelog': '/changelog', + '/changelog': + 'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG' + } + }, + path: './' + }) + + middleware.push(function(req, res, next) { + if (/\.(css|js)$/.test(req.url)) { + return next() + } + renderer.renderToString(req.url).then(html => res.end(html)) + }) +} + +const params = { + port: 3000, + watch: ['lib', 'docs', 'themes'], + middleware +} + +liveServer.start(params) diff --git a/doc/src/main/java/com/gitee/sop/doc/SidebarTest.java b/doc/src/main/java/com/gitee/sop/doc/SidebarTest.java new file mode 100644 index 00000000..2994bb25 --- /dev/null +++ b/doc/src/main/java/com/gitee/sop/doc/SidebarTest.java @@ -0,0 +1,39 @@ +package com.gitee.sop.doc; + +import java.io.File; +import java.io.FileOutputStream; + +/** + * 生成_sidebar.md文件,直接运行即可 + * @author tanghc + */ +public class SidebarTest { + + static String format = " * [%s](files/%s?t=%s)\r\n"; + static String sidebar_format = "* 文档目录\r\n\r\n%s"; + + public static void main(String[] args) throws Exception { + String path = SidebarTest.class.getClassLoader().getResource("").getPath(); + String root = path.substring(0, path.indexOf("doc")) + "doc"; + String fileDir = root + "/docs/files"; + File dir = new File(fileDir); + File[] files = dir.listFiles(); + StringBuilder output = new StringBuilder(); + for (File file : files) { + String filename = file.getName(); + String title = filename.substring(filename.indexOf("_") + 1, filename.length() - 3); + String line = String.format(format, title, filename, System.currentTimeMillis()); + output.append(line); + } + + String sidebarContent = String.format(sidebar_format, output.toString()); + + System.out.println(sidebarContent); + + String sidebarFilepath = root + "/docs/_sidebar.md"; + + FileOutputStream out = new FileOutputStream(new File(sidebarFilepath)); + out.write(sidebarContent.getBytes()); + out.close(); + } +} diff --git a/pom.xml b/pom.xml index f366065a..a6afca47 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ 一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台 + doc sop-common sop-admin sop-registry diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java index 753782fc..c9b32ace 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java @@ -8,13 +8,17 @@ import com.gitee.sop.gatewaycommon.message.Error; import com.gitee.sop.gatewaycommon.message.ErrorEnum; import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter; import com.gitee.sop.gatewaycommon.zuul.ZuulContext; +import com.netflix.util.Pair; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import javax.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.List; import java.util.Map; +import java.util.Optional; /** * @author tanghc @@ -24,9 +28,19 @@ public class ZuulResultExecutor extends BaseExecutorAdapter> bizHeaders = requestContext.getZuulResponseHeaders(); + Optional> first = bizHeaders.stream() + .filter(header -> { + return SopConstants.X_BIZ_ERROR_CODE.equals(header.first()); + }).findFirst(); + + Pair header = first.orElseGet(() -> { + return new Pair(HttpStatus.OK.name(), String.valueOf(HttpStatus.OK.value())); + }); + + String bizErrorCode = header.second(); if (bizErrorCode != null) { code = Integer.valueOf(bizErrorCode); } diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java index 0f4a9249..47b9e9be 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java @@ -24,7 +24,7 @@ public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler { @ResponseBody public Object error(HttpServletRequest request, HttpServletResponse response) { ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder(); - return serviceResultBuilder.buildError(request, response, new RuntimeException("系统繁忙")); + return serviceResultBuilder.buildError(request, response, new ServiceException("系统繁忙")); } @ExceptionHandler(ServiceException.class) @@ -37,7 +37,7 @@ public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler { @ResponseBody public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { log.error("系统错误", exception); - return this.processError(request, response, new RuntimeException("系统错误")); + return this.processError(request, response, new ServiceException("系统繁忙")); } /** diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ServiceException.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ServiceException.java index e97efcd4..762dda8a 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ServiceException.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ServiceException.java @@ -1,6 +1,7 @@ package com.gitee.sop.servercommon.exception; import com.gitee.sop.servercommon.message.ServiceError; +import com.gitee.sop.servercommon.message.ServiceErrorEnum; import com.gitee.sop.servercommon.message.ServiceErrorImpl; /** @@ -15,6 +16,12 @@ public class ServiceException extends RuntimeException { this.error = new ServiceErrorImpl(subCode, subMsg); } + public ServiceException(String subMsg) { + super(subMsg); + String subCode = ServiceErrorEnum.ISV_COMMON_ERROR.getErrorMeta().getError().getSub_code(); + this.error = new ServiceErrorImpl(subCode, subMsg); + } + public ServiceException(ServiceError error) { super(error.toString()); this.error = error; diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java index 4849db4e..56244f9c 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java @@ -6,7 +6,10 @@ package com.gitee.sop.servercommon.message; public enum ServiceErrorEnum { /** 系统繁忙 */ ISP_UNKNOW_ERROR("isp.unknow-error"), - ISP_PARAM_ERROR("isv.invalid-parameter"), + /** 参数错误 */ + ISV_PARAM_ERROR("isv.invalid-parameter"), + /** 通用错误 */ + ISV_COMMON_ERROR("isv.common-error"), ; private ServiceErrorMeta errorMeta; diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java index 62f3b57a..80a3bd65 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java @@ -44,7 +44,7 @@ public class ServiceParamValidator implements ParamValidator { } private RuntimeException getValidateBizParamException(String errorMsg) { - String subCode = ServiceErrorEnum.ISP_PARAM_ERROR.getErrorMeta().getSubCode(); + String subCode = ServiceErrorEnum.ISV_PARAM_ERROR.getErrorMeta().getSubCode(); String[] msgToken = errorMsg.split(EQ); String msg = msgToken[0]; if (msg.startsWith(LEFT_TOKEN) && msg.endsWith(RIGHT_TOKEN)) { diff --git a/sop-story/sop-story-web/pom.xml b/sop-story/sop-story-web/pom.xml index 90aca859..e03b1d40 100644 --- a/sop-story/sop-story-web/pom.xml +++ b/sop-story/sop-story-web/pom.xml @@ -44,6 +44,12 @@ spring-boot-starter-test test + + org.projectlombok + lombok + 1.18.4 + provided + diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/config/OpenServiceConfig.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/config/OpenServiceConfig.java index 7a852369..4aeba862 100644 --- a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/config/OpenServiceConfig.java +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/config/OpenServiceConfig.java @@ -1,9 +1,12 @@ package com.gitee.sop.bookweb.config; +import com.gitee.sop.servercommon.bean.ServiceConfig; import com.gitee.sop.servercommon.configuration.AlipayServiceConfiguration; import com.gitee.sop.servercommon.configuration.TaobaoServiceConfiguration; import org.springframework.context.annotation.Configuration; +import javax.annotation.PostConstruct; + /** * 使用支付宝开放平台功能 * @author tanghc @@ -11,6 +14,11 @@ import org.springframework.context.annotation.Configuration; @Configuration public class OpenServiceConfig extends AlipayServiceConfiguration { + + static { + ServiceConfig.getInstance().getI18nModules().add("i18n/isp/goods_error"); + } + } /** diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/AlipayController.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/AlipayController.java index 3c36bfce..ab433fd0 100644 --- a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/AlipayController.java +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/AlipayController.java @@ -1,9 +1,7 @@ package com.gitee.sop.bookweb.controller; -import com.gitee.sop.bookweb.message.StoryErrorEnum; import com.gitee.sop.servercommon.annotation.ApiMapping; import com.gitee.sop.story.api.domain.Story; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/JSR303DemoController.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/JSR303DemoController.java new file mode 100644 index 00000000..f04340d5 --- /dev/null +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/JSR303DemoController.java @@ -0,0 +1,18 @@ +package com.gitee.sop.bookweb.controller; + +import com.gitee.sop.bookweb.controller.param.GoodsParam; +import com.gitee.sop.servercommon.annotation.ApiMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 演示参数验证 + * @author tanghc + */ +@RestController +public class JSR303DemoController { + + @ApiMapping(value = "goods.add") + public Object addGoods(GoodsParam param) { + return param; + } +} diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/StoryDemoController.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/StoryDemoController.java new file mode 100644 index 00000000..fbc8a31f --- /dev/null +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/StoryDemoController.java @@ -0,0 +1,21 @@ +package com.gitee.sop.bookweb.controller; + +import com.gitee.sop.servercommon.annotation.ApiMapping; +import com.gitee.sop.story.api.domain.Story; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author tanghc + */ +@RestController +public class StoryDemoController { + + @ApiMapping(value = "story.demo.get") + public Story getStory() { + Story story = new Story(); + story.setId(1); + story.setName("白雪公主"); + return story; + } + +} diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/ThrowExceptionDemoController.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/ThrowExceptionDemoController.java new file mode 100644 index 00000000..8fe8b7db --- /dev/null +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/ThrowExceptionDemoController.java @@ -0,0 +1,33 @@ +package com.gitee.sop.bookweb.controller; + +import com.gitee.sop.bookweb.controller.param.GoodsUpdateParam; +import com.gitee.sop.bookweb.message.GoodsErrorEnum; +import com.gitee.sop.servercommon.annotation.ApiMapping; +import com.gitee.sop.servercommon.exception.ServiceException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.RestController; + +/** + * 演示如何抛出异常 + * @author tanghc + */ +@RestController +public class ThrowExceptionDemoController { + + @ApiMapping(value = "goods.update") + public Object updateGoods(GoodsUpdateParam param) { + // 方式1 + if ("iphone6".equals(param.getGoods_name())) { + throw new ServiceException("不能更新iphone6"); + } + // 方式2,国际化 + if (StringUtils.isEmpty(param.getGoods_name())) { + throw GoodsErrorEnum.NO_GOODS_NAME.getErrorMeta().getException(); + } + // 方式3,国际化传参 + if (param.getGoods_name().length() <= 3) { + throw GoodsErrorEnum.LESS_GOODS_NAME_LEN.getErrorMeta().getException(3); + } + return param; + } +} diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsParam.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsParam.java new file mode 100644 index 00000000..333cd59d --- /dev/null +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsParam.java @@ -0,0 +1,19 @@ +package com.gitee.sop.bookweb.controller.param; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; + +@Data +public class GoodsParam { +// @NotEmpty(message = "商品名称不能为空") + private String goods_name; + + @NotEmpty(message = "{goods.remark.notNull}") + private String goods_remark; + + // 传参的格式:{xxx}=value1,value2... + @Length(min = 3, max = 20, message = "{goods.comment.length}=3,20") + private String goods_comment; +} \ No newline at end of file diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsUpdateParam.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsUpdateParam.java new file mode 100644 index 00000000..3aab1d06 --- /dev/null +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/controller/param/GoodsUpdateParam.java @@ -0,0 +1,9 @@ +package com.gitee.sop.bookweb.controller.param; + +import lombok.Data; + +@Data +public class GoodsUpdateParam { + private String goods_name; + +} \ No newline at end of file diff --git a/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/message/GoodsErrorEnum.java b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/message/GoodsErrorEnum.java new file mode 100644 index 00000000..77a3265a --- /dev/null +++ b/sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/message/GoodsErrorEnum.java @@ -0,0 +1,20 @@ +package com.gitee.sop.bookweb.message; + +import com.gitee.sop.servercommon.message.ServiceErrorMeta; + +public enum GoodsErrorEnum { + /** 参数错误 */ + NO_GOODS_NAME("100"), + /** 参数长度太短 */ + LESS_GOODS_NAME_LEN("101"), + ; + private ServiceErrorMeta errorMeta; + + GoodsErrorEnum(String subCode) { + this.errorMeta = new ServiceErrorMeta("isp.goods_error_", subCode); + } + + public ServiceErrorMeta getErrorMeta() { + return errorMeta; + } +} \ No newline at end of file diff --git a/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_en.properties b/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_en.properties index 76ef051e..48d134c8 100644 --- a/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_en.properties +++ b/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_en.properties @@ -1,3 +1,10 @@ # 错误配置 -isp.error_isv.invalid-parameter=Invalid parameter, {0} \ No newline at end of file +# 系统配置 +isp.error_isv.common-error=The system is busy. +isp.error_isv.invalid-parameter=Invalid parameter, {0} + +# ==== 参数配置 ==== + +goods.remark.notNull=The goods_remark can not be null +goods.comment.length=The goods_comment length must >= {0} and <= {1} \ No newline at end of file diff --git a/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_zh_CN.properties b/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_zh_CN.properties index 4c215059..4ac48428 100644 --- a/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_zh_CN.properties +++ b/sop-story/sop-story-web/src/main/resources/i18n/isp/bizerror_zh_CN.properties @@ -1,4 +1,14 @@ # 错误配置 +# 系统繁忙 +isp.error_isv.common-error=\u7cfb\u7edf\u7e41\u5fd9 # 参数无效 isp.error_isv.invalid-parameter=\u53c2\u6570\u65e0\u6548, {0} + +# ==== 参数配置 ==== + +# 商品备注不能为空 +goods.remark.notNull=\u5546\u54c1\u5907\u6ce8\u4e0d\u80fd\u4e3a\u7a7a +# 商品评论长度必须在{0}和{1}之间 +goods.comment.length=\u5546\u54c1\u8bc4\u8bba\u957f\u5ea6\u5fc5\u987b\u5728{0}\u548c{1}\u4e4b\u95f4 + diff --git a/sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_en.properties b/sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_en.properties new file mode 100644 index 00000000..195c7d2b --- /dev/null +++ b/sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_en.properties @@ -0,0 +1,2 @@ +isp.goods_error_100=the goods_name can NOT be null +isp.goods_error_101=the goods_name must bigger than {0} \ No newline at end of file diff --git a/sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_zh_CN.properties b/sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_zh_CN.properties new file mode 100644 index 00000000..af1a43bc --- /dev/null +++ b/sop-story/sop-story-web/src/main/resources/i18n/isp/goods_error_zh_CN.properties @@ -0,0 +1,5 @@ +# 商品名字不能为空 +isp.goods_error_100=\u5546\u54C1\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +# 商品名称太短,不能小于{0}个字 +isp.goods_error_101=\u5546\u54C1\u540D\u79F0\u592A\u77ED\uFF0C\u4E0D\u80FD\u5C0F\u4E8E{0}\u4E2A\u5B57 \ No newline at end of file diff --git a/sop-test/src/test/java/com/gitee/sop/AlipayClientPostTest.java b/sop-test/src/test/java/com/gitee/sop/AlipayClientPostTest.java index 89794588..5b7b2306 100644 --- a/sop-test/src/test/java/com/gitee/sop/AlipayClientPostTest.java +++ b/sop-test/src/test/java/com/gitee/sop/AlipayClientPostTest.java @@ -151,12 +151,4 @@ biz_content String 是 请求参数的集合,最大长度不限,除公共 System.out.println(responseData); } - private String buildParamQuery(Map params) { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : params.entrySet()) { - sb.append("&").append(entry.getKey()).append("=").append(entry.getValue()); - } - return sb.toString().substring(1); - } - } diff --git a/sop-test/src/test/java/com/gitee/sop/JSR303DemoTest.java b/sop-test/src/test/java/com/gitee/sop/JSR303DemoTest.java new file mode 100644 index 00000000..b582f23f --- /dev/null +++ b/sop-test/src/test/java/com/gitee/sop/JSR303DemoTest.java @@ -0,0 +1,58 @@ +package com.gitee.sop; + +import com.alibaba.fastjson.JSON; +import com.gitee.sop.alipay.AlipaySignature; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author tanghc + */ +public class JSR303DemoTest extends TestBase { + + String url = "http://localhost:8081/api"; // zuul + String appId = "alipay_test"; + // 支付宝私钥 + String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ="; + + @Test + public void testGoodsAdd() throws Exception { + // 公共请求参数 + Map params = new HashMap(); + params.put("app_id", appId); + params.put("method", "goods.add"); + params.put("format", "json"); + params.put("charset", "utf-8"); + params.put("sign_type", "RSA2"); + params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + params.put("version", "1.0"); + + // 业务参数 + Map bizContent = new HashMap<>(); + // 对应校验规则查看GoodsParam.java + bizContent.put("goods_name", "iphone6"); + bizContent.put("goods_remark", "iphone6"); + bizContent.put("goods_comment", "1"); + + params.put("biz_content", JSON.toJSONString(bizContent)); + + System.out.println("----------- 请求信息 -----------"); + System.out.println("请求参数:" + buildParamQuery(params)); + System.out.println("商户秘钥:" + privateKey); + String content = AlipaySignature.getSignContent(params); + System.out.println("待签名内容:" + content); + String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8"); + System.out.println("签名(sign):" + sign); + + params.put("sign", sign); + + System.out.println("----------- 返回结果 -----------"); + String responseData = post(url, params);// 发送请求 + System.out.println(responseData); + } + +} diff --git a/sop-test/src/test/java/com/gitee/sop/StoryDemoTest.java b/sop-test/src/test/java/com/gitee/sop/StoryDemoTest.java new file mode 100644 index 00000000..3270af29 --- /dev/null +++ b/sop-test/src/test/java/com/gitee/sop/StoryDemoTest.java @@ -0,0 +1,87 @@ +package com.gitee.sop; + +import com.alibaba.fastjson.JSON; +import com.gitee.sop.alipay.AlipaySignature; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author tanghc + */ +public class StoryDemoTest extends TestBase { + + String url = "http://localhost:8081/api"; // zuul + String appId = "alipay_test"; + // 支付宝私钥 + String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ="; + + @Test + public void testDemo() throws Exception { + // 公共请求参数 + Map params = new HashMap(); + params.put("app_id", appId); + params.put("method", "story.demo.get"); + params.put("format", "json"); + params.put("charset", "utf-8"); + params.put("sign_type", "RSA2"); + params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + params.put("version", "1.0"); + + // 业务参数 + Map bizContent = new HashMap<>(); + + params.put("biz_content", JSON.toJSONString(bizContent)); + + System.out.println("----------- 请求信息 -----------"); + System.out.println("请求参数:" + buildParamQuery(params)); + System.out.println("商户秘钥:" + privateKey); + String content = AlipaySignature.getSignContent(params); + System.out.println("待签名内容:" + content); + String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8"); + System.out.println("签名(sign):" + sign); + + params.put("sign", sign); + + System.out.println("----------- 返回结果 -----------"); + String responseData = post(url, params);// 发送请求 + System.out.println(responseData); + } + + @Test + public void testGoodsAdd() throws Exception { + // 公共请求参数 + Map params = new HashMap(); + params.put("app_id", appId); + params.put("method", "goods.add"); + params.put("format", "json"); + params.put("charset", "utf-8"); + params.put("sign_type", "RSA2"); + params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + params.put("version", "1.0"); + + // 业务参数 + Map bizContent = new HashMap<>(); + bizContent.put("goods_name", "iphone6"); + + params.put("biz_content", JSON.toJSONString(bizContent)); + + System.out.println("----------- 请求信息 -----------"); + System.out.println("请求参数:" + buildParamQuery(params)); + System.out.println("商户秘钥:" + privateKey); + String content = AlipaySignature.getSignContent(params); + System.out.println("待签名内容:" + content); + String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8"); + System.out.println("签名(sign):" + sign); + + params.put("sign", sign); + + System.out.println("----------- 返回结果 -----------"); + String responseData = post(url, params);// 发送请求 + System.out.println(responseData); + } + +} diff --git a/sop-test/src/test/java/com/gitee/sop/TaobaoClientPostTest.java b/sop-test/src/test/java/com/gitee/sop/TaobaoClientPostTest.java index 124bcc7b..6aed2e60 100644 --- a/sop-test/src/test/java/com/gitee/sop/TaobaoClientPostTest.java +++ b/sop-test/src/test/java/com/gitee/sop/TaobaoClientPostTest.java @@ -66,12 +66,4 @@ sign String 是 API输入参数签名结果,签名算法参照下面的介绍 System.out.println(responseData); } - private String buildParamQuery(Map params) { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : params.entrySet()) { - sb.append("&").append(entry.getKey()).append("=").append(entry.getValue()); - } - return sb.toString().substring(1); - } - } diff --git a/sop-test/src/test/java/com/gitee/sop/TestBase.java b/sop-test/src/test/java/com/gitee/sop/TestBase.java index 22cc2559..8e57a00b 100644 --- a/sop-test/src/test/java/com/gitee/sop/TestBase.java +++ b/sop-test/src/test/java/com/gitee/sop/TestBase.java @@ -78,5 +78,12 @@ public class TestBase extends TestCase { return null; } + protected String buildParamQuery(Map params) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + sb.append("&").append(entry.getKey()).append("=").append(entry.getValue()); + } + return sb.toString().substring(1); + } } diff --git a/sop-test/src/test/java/com/gitee/sop/ThrowExceptionDemoTest.java b/sop-test/src/test/java/com/gitee/sop/ThrowExceptionDemoTest.java new file mode 100644 index 00000000..1d785fd8 --- /dev/null +++ b/sop-test/src/test/java/com/gitee/sop/ThrowExceptionDemoTest.java @@ -0,0 +1,58 @@ +package com.gitee.sop; + +import com.alibaba.fastjson.JSON; +import com.gitee.sop.alipay.AlipaySignature; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author tanghc + */ +public class ThrowExceptionDemoTest extends TestBase { + + String url = "http://localhost:8081/api"; // zuul + String appId = "alipay_test"; + // 支付宝私钥 + String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ="; + + @Test + public void testGoodsAdd() throws Exception { + // 公共请求参数 + Map params = new HashMap(); + params.put("app_id", appId); + params.put("method", "goods.update"); + params.put("format", "json"); + params.put("charset", "utf-8"); + params.put("sign_type", "RSA2"); + params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + params.put("version", "1.0"); + + // 业务参数 + Map bizContent = new HashMap<>(); + // 对应校验规则查看GoodsParam.java + bizContent.put("goods_name", "1"); + bizContent.put("goods_remark", "iphone6"); + bizContent.put("goods_comment", "1111"); + + params.put("biz_content", JSON.toJSONString(bizContent)); + + System.out.println("----------- 请求信息 -----------"); + System.out.println("请求参数:" + buildParamQuery(params)); + System.out.println("商户秘钥:" + privateKey); + String content = AlipaySignature.getSignContent(params); + System.out.println("待签名内容:" + content); + String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8"); + System.out.println("签名(sign):" + sign); + + params.put("sign", sign); + + System.out.println("----------- 返回结果 -----------"); + String responseData = post(url, params);// 发送请求 + System.out.println(responseData); + } + +}