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}
+ ${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);
+ }
+
+}