- 新增ISV用户平台

- 新增门户网站(portal)
- 新增`C++`,`Rust`语言SDK
pull/9/head
tanghc 4 years ago
parent 1370883af9
commit 6ab696dfaf
  1. 9
      README.md
  2. 8
      changelog.md
  3. 4
      doc/docs/files/10010_快速体验.md
  4. 8
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvKeysFormUpdate.java
  5. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvInfoVO.java
  6. 54
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ResourceApi.java
  7. 9
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspPageParam.java
  8. 27
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspResourceParam.java
  9. 27
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/result/IspResourceResult.java
  10. 50
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IspResource.java
  11. 3
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IsvInfo.java
  12. 11
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/IspResourceMapper.java
  13. 3
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java
  14. 2
      sop-admin/sop-admin-server/src/main/resources/public/index.html
  15. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/app.3f83e9e2.js
  16. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/app.f323bdd7.js
  17. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-25908fca.eac43cde.js
  18. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-25908fca.f16786b7.js
  19. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0d32e7.e7c489be.js
  20. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0d6219.113d6c0f.js
  21. 24
      sop-admin/sop-admin-vue/public/static/sdkConfig.json
  22. 8
      sop-admin/sop-admin-vue/src/layout/components/Sidebar/index.vue
  23. 10
      sop-admin/sop-admin-vue/src/router/index.js
  24. 11
      sop-admin/sop-admin-vue/src/utils/global.js
  25. 22
      sop-admin/sop-admin-vue/src/views/isv/index.vue
  26. 179
      sop-admin/sop-admin-vue/src/views/service/sdk.vue
  27. 1
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java
  28. 2
      sop-example/sop-story/pom.xml
  29. 31
      sop-mysql5.6以下版本.sql
  30. 27
      sop-sdk/sdk-c++/.gitignore
  31. 42
      sop-sdk/sdk-c++/CMakeLists.txt
  32. 1
      sop-sdk/sdk-c++/aa.txt
  33. 1
      sop-sdk/sdk-c++/bb.txt
  34. 133
      sop-sdk/sdk-c++/common/OpenClient.cpp
  35. 66
      sop-sdk/sdk-c++/common/OpenClient.h
  36. 94
      sop-sdk/sdk-c++/common/RSASign.cpp
  37. 19
      sop-sdk/sdk-c++/common/RSASign.h
  38. 11
      sop-sdk/sdk-c++/common/RequestType.h
  39. 5741
      sop-sdk/sdk-c++/common/httplib.h
  40. 270
      sop-sdk/sdk-c++/common/sha256.cpp
  41. 144
      sop-sdk/sdk-c++/common/sha256.hpp
  42. 118
      sop-sdk/sdk-c++/common/sign.cpp
  43. 30
      sop-sdk/sdk-c++/common/sign.h
  44. 2
      sop-sdk/sdk-c++/common/stdafx.cpp
  45. 6
      sop-sdk/sdk-c++/common/stdafx.h
  46. 1
      sop-sdk/sdk-c++/common/targetver.h
  47. 174
      sop-sdk/sdk-c++/common/tool.cpp
  48. 52
      sop-sdk/sdk-c++/common/tool.h
  49. 41
      sop-sdk/sdk-c++/main.cpp
  50. 15
      sop-sdk/sdk-c++/privateEx.pem
  51. 3
      sop-sdk/sdk-c++/readme.md
  52. 17
      sop-sdk/sdk-c++/request/BaseRequest.cpp
  53. 60
      sop-sdk/sdk-c++/request/BaseRequest.h
  54. 29
      sop-sdk/sdk-c++/request/MemberInfoGetRequest.hpp
  55. 3523
      sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.cpp
  56. 156
      sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.hpp
  57. 1091
      sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.c
  58. 151
      sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.h
  59. 103
      sop-sdk/sdk-c++/thirdparty/base64/base64.cpp
  60. 10
      sop-sdk/sdk-c++/thirdparty/base64/base64.h
  61. 5
      sop-sdk/sdk-rust/readme.md
  62. 1
      sop-sdk/sdk-rust/sdk-test/.gitignore
  63. 1558
      sop-sdk/sdk-rust/sdk-test/Cargo.lock
  64. 11
      sop-sdk/sdk-rust/sdk-test/Cargo.toml
  65. 1
      sop-sdk/sdk-rust/sdk-test/aa.txt
  66. 1
      sop-sdk/sdk-rust/sdk-test/bb.txt
  67. 76
      sop-sdk/sdk-rust/sdk-test/src/main.rs
  68. 1
      sop-sdk/sdk-rust/sdk/.gitignore
  69. 1551
      sop-sdk/sdk-rust/sdk/Cargo.lock
  70. 23
      sop-sdk/sdk-rust/sdk/Cargo.toml
  71. 112
      sop-sdk/sdk-rust/sdk/src/client.rs
  72. 158
      sop-sdk/sdk-rust/sdk/src/http.rs
  73. 14
      sop-sdk/sdk-rust/sdk/src/lib.rs
  74. 26
      sop-sdk/sdk-rust/sdk/src/request/memberinfoget.rs
  75. 45
      sop-sdk/sdk-rust/sdk/src/request/mod.rs
  76. 36
      sop-sdk/sdk-rust/sdk/src/response/memberinfoget.rs
  77. 1
      sop-sdk/sdk-rust/sdk/src/response/mod.rs
  78. 108
      sop-sdk/sdk-rust/sdk/src/sign.rs
  79. 30
      sop-upgrade-4.2.0.sql
  80. 96
      sop-website/pom.xml
  81. 22
      sop-website/sop-portal/.babelrc
  82. 0
      sop-website/sop-portal/.docsite
  83. 28
      sop-website/sop-portal/.eslintrc
  84. 17
      sop-website/sop-portal/.gitignore
  85. 0
      sop-website/sop-portal/.nojekyll
  86. 18
      sop-website/sop-portal/404.html
  87. 24
      sop-website/sop-portal/README.md
  88. 10
      sop-website/sop-portal/blog/en-us/blog1.md
  89. 10
      sop-website/sop-portal/blog/zh-cn/blog1.md
  90. 20
      sop-website/sop-portal/build.sh
  91. 249
      sop-website/sop-portal/docs/en-us/demo1.md
  92. 16
      sop-website/sop-portal/docs/en-us/demo2.md
  93. 7
      sop-website/sop-portal/docs/en-us/dir/demo3.md
  94. BIN
      sop-website/sop-portal/docs/en-us/img/brhtqqzh.jpeg
  95. 401
      sop-website/sop-portal/docs/zh-cn/demo1.md
  96. 17
      sop-website/sop-portal/docs/zh-cn/demo2.md
  97. 8
      sop-website/sop-portal/docs/zh-cn/dir/demo3.md
  98. BIN
      sop-website/sop-portal/docs/zh-cn/img/brhtqqzh.jpeg
  99. 35
      sop-website/sop-portal/docsite.config.yml
  100. 26
      sop-website/sop-portal/en-us/blog/blog1.html
  101. Some files were not shown because too many files have changed in this diff Show More

@ -88,10 +88,11 @@ System.out.println(responseData);
- 微服务端自动验证(JSR-303)
- 支持Spring Cloud Gateway
- Admin管理平台,统一管理微服务配置,管理路由管理,微服务上下线
- 门户网站,提供用户注册账号
- 接入方管理+秘钥管理
- 接口权限分配
- 文件上传/下载
- 提供基础SDK(含:Java,C#,Python,Go)
- 提供基础SDK(含:Java,C++,C#,Python,Go,Rust,Nodejs
- 接口限流
- 文档整合
- 应用授权
@ -111,9 +112,11 @@ System.out.println(responseData);
![秘钥信息](https://images.gitee.com/uploads/images/2019/0711/174921_bd817533_332975.png "秘钥信息")
![API文档](https://images.gitee.com/uploads/images/2019/0711/174939_97886883_332975.png "API文档")
- 门户网站
![沙箱环境](https://images.gitee.com/uploads/images/2019/0711/175226_3f69346a_332975.png "沙箱环境")
![首页](https://images.gitee.com/uploads/images/2020/1107/104607_b86161f0_332975.png "portal0.png")
![文档页](https://images.gitee.com/uploads/images/2020/1107/104342_d44849a9_332975.png "portal1.png")
## 工程说明

@ -1,5 +1,13 @@
# changelog
## 4.2.0
需要执行`sop-upgrade-4.2.0.sql`
- 新增ISV用户平台
- 新增门户网站(portal)
- 新增`C++`,`Rust`语言SDK
## 4.1.0
需要执行`sop-upgrade-4.1.0.sql`

@ -38,6 +38,10 @@
登录账号:admin/123456
## 启动门户网站
见:`sop-website/sop-portal/README.md`
## 启动文档中心
文档中心代码在sop-website工程中

@ -29,14 +29,6 @@ public class IsvKeysFormUpdate {
@Max(value = 2, message = "秘钥格式错误")
private Byte keyFormat;
/** 开发者生成的公钥, 数据库字段:public_key_isv */
@ApiDocField(description = "开发者生成的公钥")
private String publicKeyIsv;
/** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */
@ApiDocField(description = "开发者生成的私钥")
private String privateKeyIsv;
/** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */
@ApiDocField(description = "平台生成的公钥")
private String publicKeyPlatform;

@ -29,6 +29,8 @@ public class IsvInfoVO {
@ApiDocField(description = "备注")
private String remark;
private Long userId;
/** 数据库字段:gmt_create */
@ApiDocField(description = "添加时间")
private Date gmtCreate;

@ -0,0 +1,54 @@
package com.gitee.sop.adminserver.api.service;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.sop.adminserver.api.system.param.IspPageParam;
import com.gitee.sop.adminserver.api.system.param.IspResourceParam;
import com.gitee.sop.adminserver.common.CopyUtil;
import com.gitee.sop.adminserver.entity.IspResource;
import com.gitee.sop.adminserver.mapper.IspResourceMapper;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* @author tanghc
*/
@ApiService
public class ResourceApi {
public static final byte RESOURCE_TYPE_SDK = (byte) 0;
@Autowired
private IspResourceMapper ispResourceMapper;
@Api(name = "isp.sdk.list")
List<IspResource> list(IspPageParam param) {
return ispResourceMapper.list(param.toQuery());
}
@Api(name = "isp.sdk.add")
void addSdk(IspResourceParam param) {
IspResource ispResource = CopyUtil.copyBean(param, IspResource::new);
ispResource.setType(RESOURCE_TYPE_SDK);
ispResourceMapper.saveIgnoreNull(ispResource);
}
@Api(name = "isp.sdk.update")
void updateSdk(IspResourceParam param) {
IspResource resource = ispResourceMapper.getById(param.getId());
CopyUtil.copyProperties(param, resource);
ispResourceMapper.update(resource);
}
@Api(name = "isp.sdk.delete")
void deleteSdk(@NotNull Long id) {
IspResource resource = ispResourceMapper.getById(id);
if (resource != null) {
ispResourceMapper.deleteById(id);
}
}
}

@ -0,0 +1,9 @@
package com.gitee.sop.adminserver.api.system.param;
import com.gitee.fastmybatis.core.query.param.PageParam;
/**
* @author tanghc
*/
public class IspPageParam extends PageParam {
}

@ -0,0 +1,27 @@
package com.gitee.sop.adminserver.api.system.param;
import lombok.Data;
/**
* 表名isp_resource
* 备注ISP资源表
*
* @author tanghc
*/
@Data
public class IspResourceParam {
private Long id;
/** 资源名称, 数据库字段:name */
private String name;
private String version;
/** 资源内容(URL), 数据库字段:content */
private String content;
private String extContent;
}

@ -0,0 +1,27 @@
package com.gitee.sop.adminserver.api.system.result;
import lombok.Data;
/**
* 表名isp_resource
* 备注ISP资源表
*
* @author tanghc
*/
@Data
public class IspResourceResult {
private Long id;
/** 资源名称, 数据库字段:name */
private String name;
private String version;
/** 资源内容(URL), 数据库字段:content */
private String content;
private String extContent;
}

@ -0,0 +1,50 @@
package com.gitee.sop.adminserver.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* 表名isp_resource
* 备注ISP资源表
*
* @author tanghc
*/
@Table(name = "isp_resource")
@Data
public class IspResource {
/** 数据库字段:id */
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/** 资源名称, 数据库字段:name */
private String name;
private String version;
/** 资源内容(URL), 数据库字段:content */
private String content;
private String extContent;
/** 资源类型:0:SDK链接, 数据库字段:type */
private Byte type;
/** 数据库字段:is_deleted */
@com.gitee.fastmybatis.core.annotation.LogicDelete
private Byte isDeleted;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
}

@ -31,6 +31,9 @@ public class IsvInfo {
/** 1启用,2禁用, 数据库字段:status */
private Byte status;
/** user_account.id */
private Long userId;
/** 备注,数据库字段:remark */
private String remark;

@ -0,0 +1,11 @@
package com.gitee.sop.adminserver.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.adminserver.entity.IspResource;
/**
* @author tanghc
*/
public interface IspResourceMapper extends CrudMapper<IspResource, Long> {
}

@ -37,9 +37,6 @@ public class ConfigPushService {
@Autowired
private ServerService serverService;
@Value("${gateway.host:}")
private String gatewayHost;
@Value("${sop.secret}")
private String secret;

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.6095bfbf.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"eac43cde","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d32e7":"e7c489be","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-30c6c34f":"b288bbf5","chunk-4de1c2b6":"e74e3d03","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"494fc338","chunk-9f479afe":"2093f9d0","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-30c6c34f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d32e7":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-30c6c34f":"3b12267b","chunk-4de1c2b6":"a37cd815","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"c4612b4a","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.f323bdd7.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.6095bfbf.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var c,r,f=n[0],a=n[1],i=n[2],d=0,h=[];d<f.length;d++)r=f[d],u[r]&&h.push(u[r][0]),u[r]=0;for(c in a)Object.prototype.hasOwnProperty.call(a,c)&&(e[c]=a[c]);l&&l(n);while(h.length)h.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],c=!0,r=1;r<t.length;r++){var f=t[r];0!==u[f]&&(c=!1)}c&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var c={},r={runtime:0},u={runtime:0},o=[];function f(e){return a.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"f16786b7","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d6219":"113d6c0f","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-2d238661":"5eefcb02","chunk-30c6c34f":"b288bbf5","chunk-4de1c2b6":"e74e3d03","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"494fc338","chunk-9f479afe":"2093f9d0","chunk-c3ce42fe":"9517b588"}[e]+".js"}function a(n){if(c[n])return c[n].exports;var t=c[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-30c6c34f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};r[e]?n.push(r[e]):0!==r[e]&&t[e]&&n.push(r[e]=new Promise(function(n,t){for(var c="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d6219":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-2d238661":"31d6cfe0","chunk-30c6c34f":"3b12267b","chunk-4de1c2b6":"a37cd815","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"c4612b4a","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=a.p+c,o=document.getElementsByTagName("link"),f=0;f<o.length;f++){var i=o[f],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===c||d===u))return n()}var h=document.getElementsByTagName("style");for(f=0;f<h.length;f++){i=h[f],d=i.getAttribute("data-href");if(d===c||d===u)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var c=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+c+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=c,delete r[e],l.parentNode.removeChild(l),t(o)},l.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(l)}).then(function(){r[e]=0}));var c=u[e];if(0!==c)if(c)n.push(c[2]);else{var o=new Promise(function(n,t){c=u[e]=[n,t]});n.push(c[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,a.nc&&d.setAttribute("nonce",a.nc),d.src=f(e),i=function(n){d.onerror=d.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var c=n&&("load"===n.type?"missing":n.type),r=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+c+": "+r+")");o.type=c,o.request=r,t[1](o)}u[e]=void 0}};var h=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},a.m=e,a.c=c,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var c in e)a.d(t,c,function(n){return e[n]}.bind(null,c));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var h=0;h<i.length;h++)n(i[h]);var l=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.3f83e9e2.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0d32e7"],{"5c58":function(t,e,a){"use strict";a.r(e);var l=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[a("el-form-item",{attrs:{label:"接口名"}},[a("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"输入接口名或版本号"},model:{value:t.searchFormData.routeId,callback:function(e){t.$set(t.searchFormData,"routeId",e)},expression:"searchFormData.routeId"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.loadTable}},[t._v("搜索")])],1)],1),t._v(" "),a("el-alert",{staticStyle:{"margin-bottom":"10px"},attrs:{title:"监控数据保存在网关服务器,重启网关数据会清空。",type:"info",closable:!1}}),t._v(" "),a("el-table",{attrs:{data:t.tableData,border:"","default-expand-all":!1,"row-key":"id",height:"500","empty-text":"无数据"}},[a("el-table-column",{attrs:{fixed:"",prop:"instanceId",label:"网关实例",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.children?t._e():a("span",[t._v(t._s(e.row.instanceId))])]}}])}),t._v(" "),a("el-table-column",{attrs:{fixed:"",prop:"name",label:"接口名 (版本号)",width:"280"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.row.name+(e.row.version?" ("+e.row.version+")":""))+"\n ")]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"serviceId",label:"serviceId",width:"170"}}),t._v(" "),a("el-table-column",{attrs:{prop:"maxTime",label:"最大耗时(ms)",width:"125"}},[a("template",{slot:"header"},[t._v("\n 最大耗时(ms)\n "),a("el-tooltip",{attrs:{effect:"dark",content:"耗时计算:签名验证成功后开始,微服务返回结果后结束",placement:"top"}},[a("i",{staticClass:"el-icon-question",staticStyle:{cursor:"pointer"}})])],1)],2),t._v(" "),a("el-table-column",{attrs:{prop:"minTime",label:"最小耗时(ms)",width:"120"}}),t._v(" "),a("el-table-column",{attrs:{prop:"avgTime",label:"平均耗时(ms)",width:"120"}}),t._v(" "),a("el-table-column",{attrs:{prop:"totalCount",label:"总调用次数",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"successCount",label:"成功次数",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"errorCount",label:"失败次数",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.errorCount>0?a("el-link",{staticStyle:{"text-decoration":"underline"},attrs:{underline:!1,type:"danger"},on:{click:function(a){return t.onShowErrorDetail(e.row)}}},[t._v("\n "+t._s(e.row.errorCount)+"\n ")]):t._e(),t._v(" "),0===e.row.errorCount?a("span",[t._v("0")]):t._e()]}}])},[a("template",{slot:"header"},[t._v("\n 失败次数\n "),a("el-tooltip",{attrs:{effect:"dark",content:"只统计微服务返回的未知错误,JSR-303验证错误算作成功",placement:"top-end"}},[a("i",{staticClass:"el-icon-question",staticStyle:{cursor:"pointer"}})])],1)],2)],1),t._v(" "),a("el-dialog",{attrs:{title:"错误详情",visible:t.logDetailVisible,width:"60%"},on:{"update:visible":function(e){t.logDetailVisible=e}}},[a("div",{staticStyle:{"overflow-x":"auto"},domProps:{innerHTML:t._s(t.errorMsgDetail)}}),t._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{attrs:{type:"primary"},on:{click:function(e){t.logDetailVisible=!1}}},[t._v("关 闭")])],1)])],1)},o=[],r={data:function(){return{searchFormData:{routeId:""},tableData:[],logDetailVisible:!1,errorMsgDetail:""}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("monitor.data.list",this.searchFormData,function(t){var e=t.data;this.tableData=e.monitorInfoData})},onShowErrorDetail:function(t){var e=t.errorMsgList;this.errorMsgDetail=e.length>0?e.join("<br>"):"无内容",this.logDetailVisible=!0}}},i=r,n=a("2877"),s=Object(n["a"])(i,l,o,!1,null,null,null);e["default"]=s.exports}}]);

@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0d6219"],{"70f0":function(t,e,o){"use strict";o.r(e);var a=function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("div",{staticClass:"app-container"},[o("el-form",{attrs:{size:"mini"}},[o("el-form-item",[o("el-button",{attrs:{type:"primary",icon:"el-icon-upload"},on:{click:t.onAddSdk}},[t._v("发布SDK")])],1)],1),t._v(" "),o("el-table",{attrs:{data:t.list,border:""}},[o("el-table-column",{attrs:{prop:"name",label:"SDK",width:"120"}}),t._v(" "),o("el-table-column",{attrs:{prop:"version",label:"版本",width:"120"}}),t._v(" "),o("el-table-column",{attrs:{prop:"content",label:"下载地址"},scopedSlots:t._u([{key:"default",fn:function(e){return[o("el-link",{attrs:{type:"primary",href:e.row.content,target:"_blank"}},[t._v(t._s(e.row.content))])]}}])}),t._v(" "),o("el-table-column",{attrs:{label:"操作",width:"150",align:"center"},scopedSlots:t._u([{key:"default",fn:function(e){return[o("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(o){return t.onSdkUpdate(e.row)}}},[t._v("编辑")]),t._v(" "),o("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(o){return t.onSdkDelete(e.row)}}},[t._v("删除")])]}}])})],1),t._v(" "),o("el-dialog",{attrs:{title:t.sdkDlgTitle,visible:t.sdkDlgAddShow,"close-on-click-modal":!1},on:{"update:visible":function(e){t.sdkDlgAddShow=e},close:function(e){return t.resetForm("sdkAddForm")}}},[o("el-form",{ref:"sdkAddForm",attrs:{model:t.sdkFormAddData,rules:t.sdkFormRule,"label-width":"100px"}},[o("el-form-item",{attrs:{prop:"name",label:"选择语言"}},[o("el-select",{attrs:{placeholder:"请选择"},model:{value:t.sdkFormAddData.name,callback:function(e){t.$set(t.sdkFormAddData,"name",e)},expression:"sdkFormAddData.name"}},t._l(t.sdkConfigs,function(t){return o("el-option",{key:t.name,attrs:{label:t.name,value:t.name}})}),1)],1),t._v(" "),o("el-form-item",{attrs:{prop:"version",label:"版本"}},[o("el-input",{attrs:{maxlength:"30","show-word-limit":"",placeholder:"如:1.0"},model:{value:t.sdkFormAddData.version,callback:function(e){t.$set(t.sdkFormAddData,"version",e)},expression:"sdkFormAddData.version"}})],1),t._v(" "),o("el-form-item",{attrs:{prop:"content",label:"下载地址"}},[o("el-input",{attrs:{maxlength:"100","show-word-limit":""},model:{value:t.sdkFormAddData.content,callback:function(e){t.$set(t.sdkFormAddData,"content",e)},expression:"sdkFormAddData.content"}})],1),t._v(" "),o("el-form-item",{attrs:{prop:"extContent",label:"调用示例"}},[o("el-input",{attrs:{type:"textarea",rows:12,placeholder:"填写SDK调用示例代码,支持markdown语法"},model:{value:t.sdkFormAddData.extContent,callback:function(e){t.$set(t.sdkFormAddData,"extContent",e)},expression:"sdkFormAddData.extContent"}})],1)],1),t._v(" "),o("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[o("el-button",{on:{click:function(e){t.sdkDlgAddShow=!1}}},[t._v("取 消")]),t._v(" "),o("el-button",{attrs:{type:"primary"},on:{click:t.onSubmitForm}},[t._v("保 存")])],1)],1)],1)},n=[],d=(o("7f7f"),function(){return{id:0,name:"",version:"",content:"",extContent:""}}),s={data:function(){return{searchFormData:{},sdkDownloadConfig:[],sdkConfigs:[],sdkDlgTitle:"",sdkDlgAddShow:!1,sdkFormUpdateData:d(),sdkFormAddData:d(),sdkFormLoading:!1,sdkFormRule:{name:[{required:!0,message:"请填名称",trigger:"blur"}],version:[{required:!0,message:"请填版本",trigger:"blur"}],content:[{required:!0,message:"请填写URL",trigger:"blur"}],extContent:[{required:!0,message:"请填写调用示例",trigger:"blur"}]},downloadUrl:"",list:[]}},created:function(){this.loadLangSelector(),this.loadTable()},methods:{loadLangSelector:function(){var t=this;this.getFile("static/sdkConfig.json?q=".concat((new Date).getTime()),function(e){t.sdkConfigs=e.langList})},loadTable:function(){var t=this;this.post("isp.sdk.list",this.searchFormData,function(e){t.list=e.data})},onSizeChange:function(t){this.searchFormData.pageSize=t,this.loadTable()},onPageIndexChange:function(t){this.searchFormData.pageIndex=t,this.loadTable()},onAddSdk:function(){this.sdkDlgTitle="添加SDK",this.sdkFormAddData=d(),this.sdkDlgAddShow=!0},onSdkUpdate:function(t){this.sdkDlgTitle="修改SDK",this.sdkFormAddData=d(),Object.assign(this.sdkFormAddData,t),this.sdkDlgAddShow=!0},onSdkDelete:function(t){var e=this;this.confirm("确认要删除【".concat(t.name,"】吗?"),function(o){e.post("isp.sdk.delete",{id:t.id},function(t){o(),e.tip("删除成功"),e.loadTable()})})},onSubmitForm:function(){var t=this;this.$refs.sdkAddForm.validate(function(e){if(e){var o=t.sdkFormAddData.id?"isp.sdk.update":"isp.sdk.add";t.post(o,t.sdkFormAddData,function(){this.sdkDlgAddShow=!1,this.loadTable()})}})}}},l=s,r=o("2877"),i=Object(r["a"])(l,a,n,!1,null,null,null);e["default"]=i.exports}}]);

@ -0,0 +1,24 @@
{
"langList": [
{
"name": "Java"
},
{
"name": "C#"
},
{
"name": "C++"
},
{
"name": "Go"
},{
"name": "NodeJS"
},
{
"name": "Python"
},
{
"name": "Rust"
}
]
}

@ -10,6 +10,7 @@
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
:default-openeds="opened"
mode="vertical"
>
<sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
@ -33,6 +34,13 @@ export default {
routes() {
return this.$router.options.routes
},
opened() {
return this.routes.filter(route => {
return route.meta && route.meta.open
}).map(route => {
return route.path
})
},
activeMenu() {
const route = this.$route
const { meta, path } = route

@ -59,7 +59,7 @@ export const constantRoutes = [
path: '/service',
component: Layout,
name: 'Service',
meta: { title: '服务管理', icon: 'example' },
meta: { title: '服务管理', icon: 'example', open: true },
children: [
{
path: 'list',
@ -90,6 +90,12 @@ export const constantRoutes = [
name: 'Blacklist',
component: () => import('@/views/service/ipBlacklist'),
meta: { title: 'IP黑名单' }
},
{
path: 'sdk',
name: 'Sdk',
component: () => import('@/views/service/sdk'),
meta: { title: 'SDK管理' }
}
]
},
@ -98,7 +104,7 @@ export const constantRoutes = [
path: '/isv',
component: Layout,
name: 'Isv',
meta: { title: 'ISV管理', icon: 'user' },
meta: { title: 'ISV管理', icon: 'user', open: true },
children: [
{
path: 'list',

@ -94,6 +94,17 @@ Object.assign(Vue.prototype, {
}
}).catch(function() {})
},
/**
* 文件必须放在public下面
* @param path 相对于public文件夹路径如文件在public/static/sign.mdstatic/sign.md
* @param callback 回调函数函数参数是文件内容
*/
getFile: function(path, callback) {
axios.get(path)
.then(function(response) {
callback.call(this, response.data)
})
},
downloadText(filename, text) {
const element = document.createElement('a')
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text))

@ -32,13 +32,28 @@
<el-table-column
prop="roleList"
label="角色"
width="150"
:show-overflow-tooltip="true"
>
<template slot-scope="scope">
<span v-html="roleRender(scope.row)"></span>
</template>
</el-table-column>
<el-table-column
prop="userId"
label="注册用户"
width="100"
>
<template slot="header">
注册用户
<el-tooltip content="注册用户自行管理秘钥" placement="top">
<i class="el-icon-question" style="cursor: pointer"></i>
</el-tooltip>
</template>
<template slot-scope="scope">
<span v-if="scope.row.userId" style="font-weight: bold;"></span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column
prop="status"
label="状态"
@ -57,7 +72,6 @@
<el-table-column
prop="remark"
label="备注"
width="200"
:show-overflow-tooltip="true"
/>
<el-table-column
@ -66,8 +80,8 @@
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
<el-button type="text" size="mini" @click="onKeysUpdate(scope.row)">秘钥管理</el-button>
<el-button type="text" size="mini" @click="onExportKeys(scope.row)">导出秘钥</el-button>
<el-button v-if="!scope.row.userId" type="text" size="mini" @click="onKeysUpdate(scope.row)">秘钥管理</el-button>
<el-button v-if="!scope.row.userId" type="text" size="mini" @click="onExportKeys(scope.row)">导出秘钥</el-button>
</template>
</el-table-column>
</el-table>

@ -0,0 +1,179 @@
<template>
<div class="app-container">
<el-form size="mini">
<el-form-item>
<el-button type="primary" icon="el-icon-upload" @click="onAddSdk">发布SDK</el-button>
</el-form-item>
</el-form>
<el-table
:data="list"
border
>
<el-table-column
prop="name"
label="SDK"
width="120"
/>
<el-table-column
prop="version"
label="版本"
width="120"
/>
<el-table-column
prop="content"
label="下载地址"
>
<template slot-scope="scope">
<el-link type="primary" :href="scope.row.content" target="_blank">{{ scope.row.content }}</el-link>
</template>
</el-table-column>
<el-table-column
label="操作"
width="150"
align="center"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="onSdkUpdate(scope.row)">编辑</el-button>
<el-button type="text" size="mini" @click="onSdkDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--dialog-->
<el-dialog
:title="sdkDlgTitle"
:visible.sync="sdkDlgAddShow"
:close-on-click-modal="false"
@close="resetForm('sdkAddForm')"
>
<el-form
ref="sdkAddForm"
:model="sdkFormAddData"
:rules="sdkFormRule"
label-width="100px"
>
<el-form-item prop="name" label="选择语言">
<el-select
v-model="sdkFormAddData.name"
placeholder="请选择"
>
<el-option
v-for="item in sdkConfigs"
:key="item.name"
:label="item.name"
:value="item.name"
/>
</el-select>
</el-form-item>
<el-form-item prop="version" label="版本">
<el-input v-model="sdkFormAddData.version" maxlength="30" show-word-limit placeholder="如:1.0" />
</el-form-item>
<el-form-item prop="content" label="下载地址">
<el-input v-model="sdkFormAddData.content" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item prop="extContent" label="调用示例">
<el-input v-model="sdkFormAddData.extContent" type="textarea" :rows="12" placeholder="填写SDK调用示例代码,支持markdown语法" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="sdkDlgAddShow = false"> </el-button>
<el-button type="primary" @click="onSubmitForm"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
const appFormDataInit = function() {
return {
id: 0,
name: '',
version: '',
content: '',
extContent: ''
}
}
export default {
data() {
return {
searchFormData: {},
sdkDownloadConfig: [],
sdkConfigs: [],
sdkDlgTitle: '',
sdkDlgAddShow: false,
sdkFormUpdateData: appFormDataInit(),
sdkFormAddData: appFormDataInit(),
sdkFormLoading: false,
sdkFormRule: {
name: [
{ required: true, message: '请填名称', trigger: 'blur' }
],
version: [
{ required: true, message: '请填版本', trigger: 'blur' }
],
content: [
{ required: true, message: '请填写URL', trigger: 'blur' }
],
extContent: [
{ required: true, message: '请填写调用示例', trigger: 'blur' }
]
},
downloadUrl: '',
list: []
}
},
created() {
this.loadLangSelector()
this.loadTable()
},
methods: {
loadLangSelector: function() {
this.getFile(`static/sdkConfig.json?q=${new Date().getTime()}`, (content) => {
this.sdkConfigs = content.langList
})
},
loadTable: function() {
this.post('isp.sdk.list', this.searchFormData, resp => {
this.list = resp.data
})
},
onSizeChange: function(size) {
this.searchFormData.pageSize = size
this.loadTable()
},
onPageIndexChange: function(pageIndex) {
this.searchFormData.pageIndex = pageIndex
this.loadTable()
},
onAddSdk: function() {
this.sdkDlgTitle = '添加SDK'
this.sdkFormAddData = appFormDataInit()
this.sdkDlgAddShow = true
},
onSdkUpdate: function(row) {
this.sdkDlgTitle = '修改SDK'
this.sdkFormAddData = appFormDataInit()
Object.assign(this.sdkFormAddData, row)
this.sdkDlgAddShow = true
},
onSdkDelete: function(row) {
this.confirm(`确认要删除【${row.name}】吗?`, (done) => {
this.post('isp.sdk.delete', { id: row.id }, resp => {
done()
this.tip('删除成功')
this.loadTable()
})
})
},
onSubmitForm: function() {
this.$refs.sdkAddForm.validate((valid) => {
if (valid) {
const uri = this.sdkFormAddData.id ? 'isp.sdk.update' : 'isp.sdk.add'
this.post(uri, this.sdkFormAddData, function() {
this.sdkDlgAddShow = false
this.loadTable()
})
}
})
}
}
}
</script>

@ -83,6 +83,7 @@ public class IndexFilter implements WebFilter {
ServerRequest serverRequest = ServerWebExchangeUtil.createReadBodyRequest(exchange);
// 读取请求体中的内容
Mono<?> modifiedBody = serverRequest.bodyToMono(byte[].class)
.switchIfEmpty(Mono.just("".getBytes()))
.flatMap(data -> {
// 构建ApiParam
ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange, data);

@ -53,7 +53,7 @@
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.5</version>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>

@ -19,7 +19,8 @@ DROP TABLE IF EXISTS `admin_user_info`;
DROP TABLE IF EXISTS `config_service_route`;
DROP TABLE IF EXISTS `monitor_info`;
DROP TABLE IF EXISTS `monitor_info_error`;
DROP TABLE IF EXISTS `user_account`;
DROP TABLE IF EXISTS `isp_resource`;
CREATE TABLE `admin_user_info` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -246,6 +247,34 @@ CREATE TABLE `monitor_info_error` (
KEY `idx_routeid` (`route_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `isv_info` ADD COLUMN `user_id` BIGINT NULL DEFAULT 0 COMMENT 'user_account.id' AFTER `remark`;
CREATE INDEX `idx_userid` USING BTREE ON `isv_info` (`user_id`);
CREATE TABLE `user_account` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名(邮箱)',
`password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '2:邮箱未验证,1:启用,0:禁用',
`gmt_create` DATETIME DEFAULT NULL,
`gmt_modified` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息';
CREATE TABLE `isp_resource` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '' COMMENT '资源名称',
`content` varchar(128) NOT NULL DEFAULT '' COMMENT '资源内容(URL)',
`ext_content` text,
`version` varchar(32) NOT NULL DEFAULT '' COMMENT '版本',
`type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '资源类型:0:SDK链接',
`is_deleted` tinyint(4) NOT NULL DEFAULT '0',
`gmt_create` DATETIME DEFAULT NULL,
`gmt_modified` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISP资源表';
INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES
(1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');

@ -0,0 +1,27 @@
/target/
/cmake-build-debug/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

@ -0,0 +1,42 @@
# cmakeCMakeLists.txt
# https://www.jianshu.com/p/cb4f8136a265
cmake_minimum_required(VERSION 3.16)
project(sdk_cxx)
set(CMAKE_CXX_STANDARD 14)
set(LIB_SRC
common/OpenClient.cpp
common/OpenClient.h
common/RequestType.h
common/tool.h
common/sign.h
common/RSASign.h
common/RSASign.cpp
common/sha256.hpp
request/BaseRequest.h
request/MemberInfoGetRequest.hpp
thirdparty/base64/base64.h
thirdparty/base64/base64.cpp
thirdparty/CJsonObject/cJSON.c
thirdparty/CJsonObject/cJSON.h
thirdparty/CJsonObject/CJsonObject.hpp
thirdparty/CJsonObject/CJsonObject.cpp
thirdparty/x2struct/x2struct.hpp
common/sign.cpp common/tool.cpp common/sha256.cpp
request/BaseRequest.cpp response/BaseResponse.h response/MemberInfoGetResponse.h)
# openssl
set(OPENSSL_INC_DIR /usr/local/opt/openssl/include)
set(OPENSSL_LINK_DIR /usr/local/opt/openssl/lib)
include_directories(${OPENSSL_INC_DIR})
link_directories(${OPENSSL_LINK_DIR})
link_libraries(ssl crypto)
#
add_library(lib ${LIB_SRC})
#
add_executable(sdk_cxx main.cpp)
# lib
target_link_libraries(sdk_cxx lib ssl)

@ -0,0 +1 @@
hello你好123

@ -0,0 +1 @@
文件bb的内容

@ -0,0 +1,133 @@
#include "OpenClient.h"
#include "httplib.h"
#include "tool.h"
#include "sign.h"
#include "../thirdparty/CJsonObject/CJsonObject.hpp"
httplib::Headers headers = {
{"Accept-Encoding", "identity"}
};
const string ERROR_NODE = "error_response";
OpenClient::OpenClient(const string &appId, const string &privateKeyFilePath, const string &url) {
this->appId = appId;
this->privateKeyFilePath = privateKeyFilePath;
char *_url = const_cast<char *>(url.c_str());
char *host;
int port;
char *path;
tool::parse_url(_url, &host, &port, &path);
this->hostInfo = HostInfo{
host = host,
port = port,
path = path
};
}
neb::CJsonObject OpenClient::execute(BaseRequest *request) {
return this->execute(request, "");
}
neb::CJsonObject OpenClient::execute(BaseRequest *request, const string &token) {
string method = request->getMethod();
string version = request->getVersion();
RequestType requestType = request->getRequestType();
map<string, string> bizModel = request->bizModel;
// 创建HTTP请求客户端
httplib::Client cli(this->hostInfo.host, this->hostInfo.port);
// 构建请求参数
map<string, string> allParams = this->buildParams(request, token);
char *path = this->hostInfo.path;
string responseBody;
// 如果有文件上传
if (!request->getFiles().empty()) {
httplib::MultipartFormDataItems items = OpenClient::getMultipartFormDataItems(
allParams, request->getFiles());
responseBody = cli.Post(path, headers, items)->body;
} else {
switch (requestType) {
case GET: {
responseBody = cli.Get(path, allParams, headers)->body;
break;
}
case POST_FORM: {
responseBody = cli.Post(path, headers, OpenClient::getParams(allParams))->body;
break;
}
case POST_JSON: {
string json = tool::mapToJson(allParams);
responseBody = cli.Post(path, json, "application/json")->body;
break;
}
case POST_FILE: {
httplib::MultipartFormDataItems items = OpenClient::getMultipartFormDataItems(
allParams, request->getFiles());
responseBody = cli.Post(path, headers, items)->body;
}
}
}
return OpenClient::parseResponse(responseBody, request);
}
httplib::Params OpenClient::getParams(map<string, string> allParams) {
httplib::Params params;
map<string, string>::iterator it;
for (it = allParams.begin(); it != allParams.end(); ++it) {
params.emplace(it->first, it->second);
}
return params;
}
map<string, string> OpenClient::buildParams(BaseRequest *request, const string &token) {
map<string, string> allParams;
allParams["app_id"] = this->appId;
allParams["method"] = request->getMethod();
allParams["charset"] = "UTF-8";
allParams["sign_type"] = "RSA2";
allParams["timestamp"] = tool::getTime();
allParams["version"] = request->getVersion();
if (!token.empty()) {
allParams["app_auth_token"] = token;
}
map<string, string> bizModel = request->bizModel;
allParams.insert(bizModel.begin(), bizModel.end());
// 生成签名
string sign = signutil::createSign(allParams, this->privateKeyFilePath, "RSA2");
allParams["sign"] = sign;
return allParams;
}
httplib::MultipartFormDataItems
OpenClient::getMultipartFormDataItems(map<string, string> allParams, vector<FileInfo> fileInfoList) {
httplib::MultipartFormDataItems items = {};
map<string, string>::iterator it;
for (it = allParams.begin(); it != allParams.end(); ++it) {
items.push_back({it->first, it->second, "", ""});
}
// 添加上传文件
vector<FileInfo>::iterator vit;
for (vit = fileInfoList.begin(); vit != fileInfoList.end(); vit++) {
string content = tool::getFileContent(vit->filepath);
items.push_back({vit->name, content, tool::getFilename(vit->filepath), "application/octet-stream"});
}
return items;
}
neb::CJsonObject OpenClient::parseResponse(const string& responseBody, BaseRequest *request) {
neb::CJsonObject oJson(responseBody);
neb::CJsonObject data = oJson[ERROR_NODE];
if (data.IsEmpty()) {
string method = request->getMethod();
string nodeName = tool::replace(method.c_str(),".","_") + "_response";
data = oJson[nodeName];
}
return data;
}

@ -0,0 +1,66 @@
#ifndef SDK_CXX_OPENCLIENT_H
#define SDK_CXX_OPENCLIENT_H
#include <string>
#include "httplib.h"
#include "../request/BaseRequest.h"
#include "../thirdparty/CJsonObject/CJsonObject.hpp"
using namespace std;
struct HostInfo {
string host;
int port;
char *path;
};
/**
*
*/
class OpenClient {
private:
/** 应用id */
string appId;
/** 私钥文件路径 */
string privateKeyFilePath;
public:
/**
*
* @param appId ID
* @param privateKeyFilePath
* @param url URL
*/
OpenClient(const string &appId, const string &privateKeyFilePath, const string &url);
/**
*
* @param request BaseRequest的子类
* @param token token
* @return
*/
neb::CJsonObject execute(BaseRequest *request, const string& token);
/**
*
* @param request BaseRequest的子类
* @return
*/
neb::CJsonObject execute(BaseRequest *request);
private:
HostInfo hostInfo;
map<string, string> buildParams(BaseRequest *request, const string& token);
static httplib::MultipartFormDataItems
getMultipartFormDataItems(map<string, string> allParams, vector<FileInfo> fileInfoList);
static httplib::Params getParams(map<string, string> params);
static neb::CJsonObject parseResponse(const string& responseBody,BaseRequest *request);
};
#endif //SDK_CXX_OPENCLIENT_H

@ -0,0 +1,94 @@
#include "stdafx.h"
#include "RSASign.h"
#include "../thirdparty/base64/base64.h"
RSA* GetPublicKeyEx(char* szPath)
{
RSA *pubkey = RSA_new();
BIO *pubio;
pubio = BIO_new_file(szPath, "rb");
pubkey = PEM_read_bio_RSAPublicKey(pubio, &pubkey, NULL, NULL);
BIO_free(pubio);
return pubkey;
}
RSA* GetPrivateKeyEx(char* szPath)
{
RSA *prikey = RSA_new();
BIO *priio;
priio = BIO_new_file(szPath, "rb");
prikey = PEM_read_bio_RSAPrivateKey(priio, &prikey, NULL, NULL);
BIO_free(priio);
return prikey;
}
bool RSASignAction(const string& strEnData, char *privateKeyFilePath, string &strSigned)
{
int nlen = strEnData.length();
RSA *prsa = NULL;
if (NULL == (prsa = GetPrivateKeyEx(privateKeyFilePath)))
{
RSA_free(prsa);
printf("获取私钥失败\n");
return false;
}
char szTmp[1024] = { 0 };
//对待签名数据做SHA1摘要
SHA256((const unsigned char*)strEnData.c_str(), nlen, (unsigned char*)szTmp);
int nLength;
unsigned int nLengthRet;
char szTmp1[1024] = { 0 }; //位数一定不能小于等于RSA的128位,没有‘\0’,数据会增加后面位数
// unsigned char *szTmp1 = {0};
nLength = RSA_sign(NID_sha256, (unsigned char *)szTmp, 32, (unsigned char *)szTmp1, &nLengthRet, prsa);
if (nLength != 1)
{
RSA_free(prsa);
return false;
}
strSigned = base64_encode((unsigned char *)szTmp1, strlen((const char *)szTmp1));
RSA_free(prsa);
return true;
}
bool RSAVerifyAction(string strEnData, string &strSigned)
{
int nlen = strEnData.length();
printf("验签的原始数据:[%s]\n", strEnData.c_str());
RSA *prsa = NULL;
if (NULL == (prsa = GetPublicKeyEx(PUBLIC_KEY_FILE_EX)))
{
RSA_free(prsa);
printf("获取公钥失败\n");
return -1;
}
//对待签名数据做SHA1摘要
char szTmp[1024] = { 0 };
int nLen = strEnData.length();
SHA256((const unsigned char*)strEnData.c_str(), nLen, (unsigned char*)szTmp);
int nLength;
unsigned int nLengthRet = strSigned.length();
printf("nLengthRet2 = %d\n", nLengthRet);
nLength = RSA_verify(NID_sha256, (unsigned char *)szTmp, 32, (unsigned char*)strSigned.c_str(), nLengthRet, prsa);
if (nLength != 1)
{
RSA_free(prsa);
return false;
}
RSA_free(prsa);
return true;
}

@ -0,0 +1,19 @@
#pragma once
#include <openssl/objects.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <string>
using namespace std;
#define PUBLIC_KEY_FILE_EX "publicEx.pem" //公钥文件
#define PRIVATE_KEY_FILE_EX "privateEx.pem" //私钥文件
RSA* GetPublicKeyEx(char* szPath);
RSA* GetPrivateKeyEx(char* szPath);
bool RSASignAction(const string& strEnData,char *privateKeyFilePath, string &strSigned);
bool RSAVerifyAction(string strEnData, string &strSigned);

@ -0,0 +1,11 @@
#ifndef SDK_CXX_REQUESTTYPE_H
#define SDK_CXX_REQUESTTYPE_H
enum RequestType {
GET,
POST_FORM,
POST_JSON,
POST_FILE
};
#endif //SDK_CXX_REQUESTTYPE_H

File diff suppressed because it is too large Load Diff

@ -0,0 +1,270 @@
/*
* Filename: sha256.cpp
* Author: L.Y.
* Brief: SHA256算法实现
* Version: V1.0.0
* Update log:
* 120191108-20191113 V1.0.0
* 1
* TODO:
* Attention:
* 1使SHA256在线加密工具得到数字指纹可能不相同
*
*/
#include "sha256.hpp"
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
namespace digest
{
//////////////////////////////// 静态数据成员初始化 //////////////////////////////////////////
std::vector<uint32_t> Sha256::initial_message_digest_ =
{
0x6a09e667, 0xbb67ae85, 0x3c6ef372,
0xa54ff53a, 0x510e527f, 0x9b05688c,
0x1f83d9ab, 0x5be0cd19
};
std::vector<uint32_t> Sha256::add_constant_ =
{
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
//////////////////////////////// 成员函数的定义 //////////////////////////////////////////
bool Sha256::encrypt(const std::vector<uint8_t>& input_message,
std::vector<uint8_t>* _digest)
{
if (!input_message.empty() && _digest)
{
//! 文本预处理
std::vector<uint8_t> message = input_message;
preprocessing(&message);
//! 将文本分解成连续的64Byte大小的数据块
std::vector<std::vector<uint8_t>> chunks;
breakTextInto64ByteChunks(message, &chunks);
//! 由64Byte大小的数据块,构造出64个4Byte大小的字。然后进行循环迭代。
std::vector<uint32_t> message_digest(initial_message_digest_); // 初始化信息摘要
std::vector<uint32_t> words;
for (const auto& chunk : chunks)
{
structureWords(chunk, &words);
transform(words, &message_digest);
}
//! 获取最终结果
produceFinalHashValue(message_digest, _digest);
return true;
}
else
{
return false;
}
}
std::string Sha256::getHexMessageDigest(const std::string& message)
{
if (!message.empty())
{
std::vector<uint8_t> __message;
for (auto it = message.begin(); it != message.end(); ++it)
{
__message.push_back(static_cast<uint8_t>(*it));
}
std::vector<uint8_t> digest;
encrypt(__message, &digest);
std::ostringstream o_s;
o_s << std::hex << std::setiosflags(std::ios::uppercase);
for (auto it = digest.begin(); it != digest.end(); ++it)
{
o_s << std::setw(2) << std::setfill('0')
<< static_cast<unsigned short>(*it);
}
return o_s.str();
}
else
{
return "";
}
}
bool Sha256::preprocessing(std::vector<uint8_t>* _message) const
{
if (_message)
{
const uint64_t original_bit_size = _message->size() * 8;
//! 附加填充比特
size_t remainder = _message->size() % 64;
if (remainder < 56)
{
_message->push_back(0x80); // ox80 == 10000000
for (size_t i = 1; i < 56 - remainder; ++i)
{
_message->push_back(0x00);
}
}
else if (remainder == 56)
{
_message->push_back(0x80);
for (size_t i = 1; i < 64; ++i)
{
_message->push_back(0x00);
}
}
else
{
_message->push_back(0x80);
for (size_t i = 1; i < 64 - remainder + 56; ++i)
{
_message->push_back(0x00);
}
}
//! 附加原始文本的长度值
for (int i = 1; i <= 8; ++i)
{
uint8_t c = static_cast<uint8_t>(original_bit_size >> (64 - 8 * i));
_message->push_back(c);
}
return true;
}
else
{
return false;
}
}
bool Sha256::breakTextInto64ByteChunks(const std::vector<uint8_t>& message,
std::vector<std::vector<uint8_t>>* _chunks) const
{
if (_chunks && 0 == message.size() % 64)
{
_chunks->clear(); // 清空输出buffer
size_t quotient = message.size() / 64;
for (size_t i = 0; i < quotient; ++i)
{
std::vector<uint8_t> temp(message.begin() + i * 64, message.begin() + (i + 1) * 64);
_chunks->push_back(temp);
}
return true;
}
else
{
return false;
}
}
bool Sha256::structureWords(const std::vector<uint8_t>& chunk,
std::vector<uint32_t>* _words) const
{
if (_words && 64 == chunk.size())
{
_words->resize(64);
auto& words = *_words;
for (int i = 0; i < 16; ++i)
{
words[i] = (static_cast<uint32_t>(chunk[i * 4]) << 24)
| (static_cast<uint32_t>(chunk[i * 4 + 1]) << 16)
| (static_cast<uint32_t>(chunk[i * 4 + 2]) << 8)
| static_cast<uint32_t>(chunk[i * 4 + 3]);
}
for (int i = 16; i < 64; ++i)
{
words[i] = small_sigma1(words[i - 2])
+ words[i - 7]
+ small_sigma0(words[i - 15])
+ words[i - 16];
}
return true;
}
else
{
return false;
}
}
bool Sha256::transform(const std::vector<uint32_t>& words,
std::vector<uint32_t>* _message_digest) const
{
if (_message_digest && 8 == _message_digest->size() && 64 == words.size())
{
std::vector<uint32_t> d = *_message_digest;
for (int i = 0; i < 64; ++i)
{
uint32_t temp1 = d[7] + big_sigma1(d[4]) + ch(d[4], d[5], d[6]) + add_constant_[i] + words[i];
uint32_t temp2 = big_sigma0(d[0]) + maj(d[0], d[1], d[2]);
d[7] = d[6];
d[6] = d[5];
d[5] = d[4];
d[4] = d[3] + temp1;
d[3] = d[2];
d[2] = d[1];
d[1] = d[0];
d[0] = temp1 + temp2;
}
for (int i = 0; i < 8; ++i)
{
(*_message_digest)[i] += d[i];
}
return true;
}
else
{
return false;
}
}
bool Sha256::produceFinalHashValue(const std::vector<uint32_t>& input,
std::vector<uint8_t>* _output) const
{
if (_output)
{
_output->clear();
for (auto it = input.begin(); it != input.end(); ++it)
{
for (int i = 0; i < 4; i++)
{
_output->push_back(static_cast<uint8_t>((*it) >> (24 - 8 * i)));
}
}
return true;
}
else
{
return false;
}
}
} // namespace ly

@ -0,0 +1,144 @@
/*
* Filename: sha256.hpp
* Author: L.Y.
* Brief: SHA256算法实现
* Version: V1.0.0
* Update log:
* 120191108-20191113 V1.0.0
* 1
* TODO:
* Attention:
* 1使SHA256在线加密工具得到数字指纹可能不相同
*
*/
#ifndef SHA256_HPP
#define SHA256_HPP
#include <stdint.h>
#include <string>
#include <vector>
namespace digest
{
//
// \brief: SHA256算法实现
//
class Sha256
{
public:
//! 默认构造函数
Sha256() {}
//! 析构函数
virtual ~Sha256() {}
/** @brief: 使用SHA256算法,获取输入信息的摘要(数字指纹)
@param[in] message:
@param[out] _digest:
@return:
*/
bool encrypt(const std::vector<uint8_t>& message,
std::vector<uint8_t>* _digest);
/** @brief: 获取十六进制表示的信息摘要(数字指纹)
@param[in] message:
@return:
*/
std::string getHexMessageDigest(const std::string& message);
protected:
/////// SHA256算法中定义的6种逻辑运算 ///////
inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z) const;
inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z) const;
inline uint32_t big_sigma0(uint32_t x) const;
inline uint32_t big_sigma1(uint32_t x) const;
inline uint32_t small_sigma0(uint32_t x) const;
inline uint32_t small_sigma1(uint32_t x) const;
/** @brief: SHA256算法对输入信息的预处理,包括“附加填充比特”和“附加长度值”
: 10512448
: 64
@param[in][out] _message:
@return:
*/
bool preprocessing(std::vector<uint8_t>* _message) const;
/** @brief: 将信息分解成连续的64Byte大小的数据块
@param[in] message: 64Byte的倍数
@param[out] _chunks:
@return:
*/
bool breakTextInto64ByteChunks(const std::vector<uint8_t>& message,
std::vector<std::vector<uint8_t>>* _chunks) const;
/** @brief: 由64Byte大小的数据块,构造出64个4Byte大小的字。
16
W[t] = small_sigma1(W[t-2]) + W[t-7] + small_sigma0(W[t-15]) + W[t-16]
@param[in] chunk: 64Byte
@param[out] _words:
@return:
*/
bool structureWords(const std::vector<uint8_t>& chunk,
std::vector<uint32_t>* _words) const;
/** @breif: 基于64个4Byte大小的字,进行64次循环加密
@param[in] words: 644Byte大小的字
@param[in][out] _message_digest:
@return:
*/
bool transform(const std::vector<uint32_t>& words,
std::vector<uint32_t>* _message_digest) const;
/** @brief: 输出最终的哈希值(数字指纹)
@param[in] input: 32bit的哈希值
@param[out] _output: 8bit的哈希值
@return:
*/
bool produceFinalHashValue(const std::vector<uint32_t>& input,
std::vector<uint8_t>* _output) const;
private:
static std::vector<uint32_t> initial_message_digest_; // 在SHA256算法中的初始信息摘要,这些常量是对自然数中前8个质数的平方根的小数部分取前32bit而来。
static std::vector<uint32_t> add_constant_; // 在SHA256算法中,用到64个常量,这些常量是对自然数中前64个质数的立方根的小数部分取前32bit而来。
};
///////////////////////////////// 内联函数&模版函数的定义 /////////////////////////////////////////
inline uint32_t Sha256::ch(uint32_t x, uint32_t y, uint32_t z) const
{
return (x & y) ^ ((~x) & z);
}
inline uint32_t Sha256::maj(uint32_t x, uint32_t y, uint32_t z) const
{
return (x & y) ^ (x & z) ^ (y & z);
}
inline uint32_t Sha256::big_sigma0(uint32_t x) const
{
return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
}
inline uint32_t Sha256::big_sigma1(uint32_t x) const
{
return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7);
}
inline uint32_t Sha256::small_sigma0(uint32_t x) const
{
return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3);
}
inline uint32_t Sha256::small_sigma1(uint32_t x) const
{
return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10);
}
} // namespace ly
#endif // SHA256_HPP

@ -0,0 +1,118 @@
#include <utility>
#include "../thirdparty/base64/base64.h"
#include "sign.h"
#include "RSASign.h"
#include "tool.h"
namespace signutil {
string createSign(map<string, string> params, const string& privateKeyFilepath, const string& signType) {
string content = getSignContent(std::move(params));
return sign(content, privateKeyFilepath, "");
}
string sign(const string& content, const string& privateKeyFilepath, const string& hash) {
BIO *bufio = NULL;
RSA *rsa = NULL;
EVP_PKEY *evpKey = NULL;
bool verify = false;
EVP_MD_CTX ctx;
int result = 0;
unsigned int size = 0;
char *sign = NULL;
std::string signStr = "";
//bufio = BIO_new_mem_buf((void*)private_key, -1);
//if (bufio == NULL) {
// ERR("BIO_new_mem_buf failed");
// goto safe_exit;
//}
bufio = BIO_new(BIO_s_file());
// 读取私钥文件
BIO_read_filename(bufio, privateKeyFilepath.c_str());
// 私钥字符串转换成私钥对象
rsa = PEM_read_bio_RSAPrivateKey(bufio, NULL, NULL, NULL);
if (rsa == NULL) {
ERR("PEM_read_bio_RSAPrivateKey failed");
goto safe_exit;
}
evpKey = EVP_PKEY_new();
if (evpKey == NULL) {
ERR("EVP_PKEY_new failed");
goto safe_exit;
}
if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) {
ERR("EVP_PKEY_set1_RSA failed");
goto safe_exit;
}
EVP_MD_CTX_init(&ctx);
// SHA256签名
if (result == 1 && (result = EVP_SignInit_ex(&ctx,
EVP_sha256(), NULL)) != 1) {
ERR("EVP_SignInit_ex failed");
}
if (result == 1 && (result = EVP_SignUpdate(&ctx,
content.c_str(), content.size())) != 1) {
ERR("EVP_SignUpdate failed");
}
size = EVP_PKEY_size(evpKey);
sign = (char*)malloc(size+1);
memset(sign, 0, size+1);
if (result == 1 && (result = EVP_SignFinal(&ctx,
(unsigned char*)sign,
&size, evpKey)) != 1) {
ERR("EVP_SignFinal failed");
}
if (result == 1) {
verify = true;
} else {
ERR("verify failed");
}
signStr = base64_encode((const unsigned char*)sign, size);
EVP_MD_CTX_cleanup(&ctx);
free(sign);
safe_exit:
if (rsa != NULL) {
RSA_free(rsa);
rsa = NULL;
}
if (evpKey != NULL) {
EVP_PKEY_free(evpKey);
evpKey = NULL;
}
if (bufio != NULL) {
BIO_free_all(bufio);
bufio = NULL;
}
return signStr;
}
string getSignContent(map<string, string> params){
map<string, string>::iterator iter;
string content;
for(iter = params.begin(); iter != params.end(); iter++) {
content.append("&").append(iter->first + "=" + iter->second);
}
return content.substr(1);
}
void ERR(const string &msg) {
throw msg;
}
}

@ -0,0 +1,30 @@
#ifndef SDK_CXX_SIGN_H
#define SDK_CXX_SIGN_H
#include <map>
#include <iostream>
#include <openssl/objects.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <string>
using namespace std;
namespace signutil {
string createSign(map<string, string> params, const string& privateKeyFilepath, const string& signType);
string sign(const string& content, const string& privateKeyFilepath, const string& hash);
string getSignContent(map<string, string> params);
void ERR(const string &msg);
}
#endif //SDK_CXX_SIGN_H

@ -0,0 +1,2 @@
#include "stdafx.h"

@ -0,0 +1,6 @@
#pragma once
#include "targetver.h"

@ -0,0 +1,174 @@
#include <clocale>
#include "tool.h"
#include <map>
#include <fstream>
#include <sstream>
namespace tool {
bool endWith(const string &str, const string &tail) {
return str.compare(str.size() - tail.size(), tail.size(), tail) == 0;
}
bool startWith(const string &str, const string &head) {
return str.compare(0, head.size(), head) == 0;
}
string getTime() {
time_t timep;
time(&timep);
char tmp[64];
strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", localtime(&timep));
return tmp;
}
int parse_url(char *url, char **serverstrp, int *portp, char **pathstrp) {
char buf[256];
int serverlen, numread = 0;
/* go through the url */
/* reset url to point PAST the http:// */
/* assume it's always 7 chars! */
string _url = url;
bool isHttps = startWith(_url, "https");
int len = 7;
if (isHttps) {
len = 8;
}
url = url + len;
/* no http:// now... server is simply up to the next / or : */
sscanf(url, "%255[^/:]", buf);
serverlen = strlen(buf);
*serverstrp = (char *) malloc(serverlen + 1);
strcpy(*serverstrp, buf);
if (url[serverlen] == ':') {
/* get the port */
sscanf(&url[serverlen + 1], "%d%n", portp, &numread);
/* add one to go PAST it */
numread++;
} else {
if (isHttps) {
*portp = 443;
} else {
*portp = 80;
}
}
/* the path is a pointer into the rest of url */
*pathstrp = &url[serverlen + numread];
return 0;
}
std::string url_encode(const std::string &str) {
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++) {
if (isalnum((unsigned char) str[i]) ||
(str[i] == '-') ||
(str[i] == '_') ||
(str[i] == '.') ||
(str[i] == '~'))
strTemp += str[i];
else if (str[i] == ' ')
strTemp += "+";
else {
strTemp += '%';
strTemp += ToHex((unsigned char) str[i] >> 4);
strTemp += ToHex((unsigned char) str[i] % 16);
}
}
return strTemp;
}
std::string url_decode(const std::string &str) {
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++) {
if (str[i] == '+') strTemp += ' ';
else if (str[i] == '%') {
assert(i + 2 < length);
unsigned char high = FromHex((unsigned char) str[++i]);
unsigned char low = FromHex((unsigned char) str[++i]);
strTemp += high * 16 + low;
} else strTemp += str[i];
}
return strTemp;
}
unsigned char ToHex(unsigned char x) {
return x > 9 ? x + 55 : x + 48;
}
unsigned char FromHex(unsigned char x) {
unsigned char y;
if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;
else if (x >= '0' && x <= '9') y = x - '0';
else
assert(0);
return y;
}
string mapToJson(std::map<string, string> map_info) {
// Json::Value jObject;
// for (map<string, string>::const_iterator iter = map_info.begin( ); iter != map_info.end( ); ++iter)
// {
// jObject[iter->first] = iter->second;
// }
// return jObject.toStyledString();
return "{}";
}
string getFilename(string filepath) {
{
if (filepath.empty()) {
return "";
}
std::string::size_type iPos;
#ifdef Q_OS_WIN
iPos = strFullName.find_last_of('\\') + 1;
#else
iPos = filepath.find_last_of('/') + 1;
#endif
return filepath.substr(iPos, filepath.length() - iPos);
}
}
string getFileContent(string filepath) {
ifstream fin(filepath);
stringstream buffer;
buffer << fin.rdbuf();
string fileContent(buffer.str());
fin.close();
return fileContent;
}
std::string replace(const char *pszSrc, const char *pszOld, const char *pszNew)
{
std::string strContent, strTemp;
strContent.assign( pszSrc );
std::string::size_type nPos = 0;
while( true )
{
nPos = strContent.find(pszOld, nPos);
strTemp = strContent.substr(nPos+strlen(pszOld), strContent.length());
if ( nPos == std::string::npos )
{
break;
}
strContent.replace(nPos,strContent.length(), pszNew );
strContent.append(strTemp);
nPos +=strlen(pszNew) - strlen(pszOld)+1; //防止重复替换 避免死循环
}
return strContent;
}
}

@ -0,0 +1,52 @@
#ifndef SDK_CXX_TOOL_H
#define SDK_CXX_TOOL_H
#include <string>
#include <map>
using namespace std;
namespace tool {
bool endWith(const string &str, const string &tail);
bool startWith(const string &str, const string &head);
string getTime();
int parse_url(char *url, char **serverstrp, int *portp, char **pathstrp);
std::string url_encode(const std::string &szToEncode);
std::string url_decode(const std::string &SRC);
unsigned char ToHex(unsigned char x);
unsigned char FromHex(unsigned char x);
string mapToJson(std::map<string, string> m);
string getFilename(string filepath);
string getFileContent(string filepath);
/**
*
* replace
*
* pszSrc
* pszOld
* pszNew
*
*
*
* #include <string>
* ssdwujianhua 2017/08/30
*/
std::string replace(const char *pszSrc, const char *pszOld, const char *pszNew);
}
#endif //SDK_CXX_TOOL_H

@ -0,0 +1,41 @@
#include <iostream>
#include "common/OpenClient.h"
#include "request/BaseRequest.h"
#include "request/MemberInfoGetRequest.hpp"
// 应用ID
string appId = "2020051325943082302177280";
// 存放私钥的文件路径
string privateKeyFile = "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/privateEx.pem";
// 请求接口
string url = "http://localhost:7071/prod/gw68uy85";
OpenClient openClient(appId, privateKeyFile, url);
int main() {
// 创建请求
MemberInfoGetRequest request;
// 业务参数
map<string, string> bizModel;
bizModel["name"] = "jim";
bizModel["age"] = "22";
bizModel["address"] = "xx";
request.bizModel = bizModel;
// 添加上传文件
// request->setFiles({
// FileInfo{"aa", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/aa.txt"},
// FileInfo{"bb", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/bb.txt"}
// });
// 发送请求
neb::CJsonObject jsonObj = openClient.execute(&request);
std::cout << jsonObj.ToString() << std::endl;
std::cout << "id:" << jsonObj["id"].ToString() << std::endl;
std::cout << "is_vip:" << jsonObj["member_info"]["is_vip"].ToString() << std::endl;
return 0;
}

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544
xihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2
aZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB
AoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI
UGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO
qjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP
1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta
uQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt
kxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C
lno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M
JUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh
6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw
GGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA=
-----END RSA PRIVATE KEY-----

@ -0,0 +1,3 @@
# SDK for C++
使用方式见:main.cpp

@ -0,0 +1,17 @@
#include "BaseRequest.h"
#include <utility>
vector<FileInfo> BaseRequest::getFiles() {
return this->files;
}
void BaseRequest::addFile(const FileInfo& filepath) {
this->files.push_back(filepath);
}
void BaseRequest::setFiles(vector<FileInfo> files) {
this->files = std::move(files);
}

@ -0,0 +1,60 @@
#ifndef SDK_CXX_BASEREQUEST_H
#define SDK_CXX_BASEREQUEST_H
#include <string>
#include <map>
#include <vector>
#include "../common/RequestType.h"
using namespace std;
struct FileInfo {
/** 表单名称 */
string name;
/** 文件完整路径 */
string filepath;
};
/**
*
*/
class BaseRequest {
public:
/**
*
*/
map<string, string> bizModel;
/**
*
* @return
*/
virtual string getMethod() = 0;
/**
*
* @return
*/
virtual string getVersion() = 0;
/**
*
* @return
*/
virtual RequestType getRequestType() = 0;
vector<FileInfo> getFiles();
void setFiles(vector<FileInfo> files);
private:
vector<FileInfo> files = {};
void addFile(const FileInfo& filepath);
};
#endif //SDK_CXX_BASEREQUEST_H

@ -0,0 +1,29 @@
#ifndef SDK_CXX_MEMBERINFOGETREQUEST_HPP
#define SDK_CXX_MEMBERINFOGETREQUEST_HPP
#include <string>
#include "BaseRequest.h"
using namespace std;
class MemberInfoGetRequest : public BaseRequest {
public:
string getMethod() override;
string getVersion() override;
RequestType getRequestType() override;
};
string MemberInfoGetRequest::getMethod() {
return "member.info.get";
}
string MemberInfoGetRequest::getVersion() {
return "1.0";
}
RequestType MemberInfoGetRequest::getRequestType() {
return GET;
}
#endif //SDK_CXX_MEMBERINFOGETREQUEST_HPP

File diff suppressed because it is too large Load Diff

@ -0,0 +1,156 @@
/*******************************************************************************
* Project: neb
* @file CJsonObject.hpp
* @brief Json
* @author bwarliao
* @date: 2014-7-16
* @note
* Modify history:
******************************************************************************/
#ifndef CJSONOBJECT_HPP_
#define CJSONOBJECT_HPP_
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <string>
#include <map>
#include <list>
#ifdef __cplusplus
extern "C" {
#endif
#include "cJSON.h"
#ifdef __cplusplus
}
#endif
namespace neb
{
class CJsonObject
{
public: // method of ordinary json object or json array
CJsonObject();
CJsonObject(const std::string& strJson);
CJsonObject(const CJsonObject* pJsonObject);
CJsonObject(const CJsonObject& oJsonObject);
virtual ~CJsonObject();
CJsonObject& operator=(const CJsonObject& oJsonObject);
bool operator==(const CJsonObject& oJsonObject) const;
bool Parse(const std::string& strJson);
void Clear();
bool IsEmpty() const;
bool IsArray() const;
std::string ToString() const;
std::string ToFormattedString() const;
const std::string& GetErrMsg() const
{
return(m_strErrMsg);
}
public: // method of ordinary json object
bool AddEmptySubObject(const std::string& strKey);
bool AddEmptySubArray(const std::string& strKey);
bool GetKey(std::string& strKey);
void ResetTraversing();
CJsonObject& operator[](const std::string& strKey);
std::string operator()(const std::string& strKey) const;
bool Get(const std::string& strKey, CJsonObject& oJsonObject) const;
bool Get(const std::string& strKey, std::string& strValue) const;
bool Get(const std::string& strKey, int32& iValue) const;
bool Get(const std::string& strKey, uint32& uiValue) const;
bool Get(const std::string& strKey, int64& llValue) const;
bool Get(const std::string& strKey, uint64& ullValue) const;
bool Get(const std::string& strKey, bool& bValue) const;
bool Get(const std::string& strKey, float& fValue) const;
bool Get(const std::string& strKey, double& dValue) const;
bool IsNull(const std::string& strKey) const;
bool Add(const std::string& strKey, const CJsonObject& oJsonObject);
bool Add(const std::string& strKey, const std::string& strValue);
bool Add(const std::string& strKey, int32 iValue);
bool Add(const std::string& strKey, uint32 uiValue);
bool Add(const std::string& strKey, int64 llValue);
bool Add(const std::string& strKey, uint64 ullValue);
bool Add(const std::string& strKey, bool bValue, bool bValueAgain);
bool Add(const std::string& strKey, float fValue);
bool Add(const std::string& strKey, double dValue);
bool AddNull(const std::string& strKey); // add null like this: "key":null
bool Delete(const std::string& strKey);
bool Replace(const std::string& strKey, const CJsonObject& oJsonObject);
bool Replace(const std::string& strKey, const std::string& strValue);
bool Replace(const std::string& strKey, int32 iValue);
bool Replace(const std::string& strKey, uint32 uiValue);
bool Replace(const std::string& strKey, int64 llValue);
bool Replace(const std::string& strKey, uint64 ullValue);
bool Replace(const std::string& strKey, bool bValue, bool bValueAgain);
bool Replace(const std::string& strKey, float fValue);
bool Replace(const std::string& strKey, double dValue);
bool ReplaceWithNull(const std::string& strKey); // replace value with null
public: // method of json array
int GetArraySize();
CJsonObject& operator[](unsigned int uiWhich);
std::string operator()(unsigned int uiWhich) const;
bool Get(int iWhich, CJsonObject& oJsonObject) const;
bool Get(int iWhich, std::string& strValue) const;
bool Get(int iWhich, int32& iValue) const;
bool Get(int iWhich, uint32& uiValue) const;
bool Get(int iWhich, int64& llValue) const;
bool Get(int iWhich, uint64& ullValue) const;
bool Get(int iWhich, bool& bValue) const;
bool Get(int iWhich, float& fValue) const;
bool Get(int iWhich, double& dValue) const;
bool IsNull(int iWhich) const;
bool Add(const CJsonObject& oJsonObject);
bool Add(const std::string& strValue);
bool Add(int32 iValue);
bool Add(uint32 uiValue);
bool Add(int64 llValue);
bool Add(uint64 ullValue);
bool Add(int iAnywhere, bool bValue);
bool Add(float fValue);
bool Add(double dValue);
bool AddNull(); // add a null value
bool AddAsFirst(const CJsonObject& oJsonObject);
bool AddAsFirst(const std::string& strValue);
bool AddAsFirst(int32 iValue);
bool AddAsFirst(uint32 uiValue);
bool AddAsFirst(int64 llValue);
bool AddAsFirst(uint64 ullValue);
bool AddAsFirst(int iAnywhere, bool bValue);
bool AddAsFirst(float fValue);
bool AddAsFirst(double dValue);
bool AddNullAsFirst(); // add a null value
bool Delete(int iWhich);
bool Replace(int iWhich, const CJsonObject& oJsonObject);
bool Replace(int iWhich, const std::string& strValue);
bool Replace(int iWhich, int32 iValue);
bool Replace(int iWhich, uint32 uiValue);
bool Replace(int iWhich, int64 llValue);
bool Replace(int iWhich, uint64 ullValue);
bool Replace(int iWhich, bool bValue, bool bValueAgain);
bool Replace(int iWhich, float fValue);
bool Replace(int iWhich, double dValue);
bool ReplaceWithNull(int iWhich); // replace with a null value
private:
CJsonObject(cJSON* pJsonData);
private:
cJSON* m_pJsonData;
cJSON* m_pExternJsonDataRef;
cJSON* m_pKeyTravers;
std::string m_strErrMsg;
std::map<unsigned int, CJsonObject*> m_mapJsonArrayRef;
std::map<std::string, CJsonObject*> m_mapJsonObjectRef;
};
}
#endif /* CJSONHELPER_HPP_ */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,151 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#include <stdint.h>
typedef int int32;
typedef unsigned int uint32;
#ifndef _WIN32
#if __WORDSIZE == 64
typedef long int64;
typedef unsigned long uint64;
#endif
#else
typedef long long int64;
typedef unsigned long long uint64;
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Int 3
#define cJSON_Double 4
#define cJSON_String 5
#define cJSON_Array 6
#define cJSON_Object 7
#define cJSON_IsReference 256
/* The cJSON structure: */
typedef struct cJSON
{
struct cJSON *next, *prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int64 valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
int sign; /* sign of valueint, 1(unsigned), -1(signed) */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks
{
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array, int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr();
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull();
extern cJSON *cJSON_CreateTrue();
extern cJSON *cJSON_CreateFalse();
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateDouble(double num, int sign);
extern cJSON *cJSON_CreateInt(uint64 num, int sign);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray();
extern cJSON *cJSON_CreateObject();
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(int *numbers, int sign, int count);
extern cJSON *cJSON_CreateFloatArray(float *numbers, int count);
extern cJSON *cJSON_CreateDoubleArray(double *numbers, int count);
extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToArrayHead(cJSON *array, cJSON *item); /* add by Bwar on 2015-01-28 */
extern void cJSON_AddItemToObject(cJSON *object, const char *string,
cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string,
cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which);
extern void cJSON_DeleteItemFromArray(cJSON *array, int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string);
/* Update array items. */
extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object, const char *string,
cJSON *newitem);
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,103 @@
//
// base64.cpp
// CPPWork
//
// Created by cocoa on 16/8/5.
// Copyright © 2016年 cc. All rights reserved.
//
#include "base64.h"
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}

@ -0,0 +1,10 @@
#ifndef base64_h
#define base64_h
#include <string>
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len);
std::string base64_decode(std::string const& encoded_string);
#endif /* base64_h */

@ -0,0 +1,5 @@
# sdk for rust
使用方式见:`sdk-test/src/main.rs`
封装步骤参考 `sdk/src/request/memberinfoget.rs``sdk/src/response/memberinfoget.rs`

File diff suppressed because it is too large Load Diff

@ -0,0 +1,11 @@
[package]
name = "sdk-test"
version = "0.1.0"
authors = ["thc"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# 依赖sdk模块
sdk = { path = "../sdk"}

@ -0,0 +1,76 @@
extern crate sdk;
use std::collections::HashMap;
use sdk::client::OpenClient;
use sdk::http::UploadFile;
use sdk::request::BaseRequest;
use sdk::request::memberinfoget::MemberInfoGetRequest;
use sdk::response::memberinfoget::MemberInfoGetResponse;
use std::env;
fn main() {
// 创建请求客户端
let client = OpenClient {
// 应用ID
app_id: "2020051325943082302177280",
// 应用私钥
private_key: "MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544\nxihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2\naZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB\nAoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI\nUGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO\nqjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP\n1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta\nuQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt\nkxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C\nlno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M\nJUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh\n6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw\nGGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA=",
// 请求地址
url: "http://localhost:7071/prod/gw68uy85",
};
// 业务参数
let mut biz_model = HashMap::new();
biz_model.insert("name", "jim".to_string());
biz_model.insert("address", "xx".to_string());
biz_model.insert("age", "22".to_string());
let mut files = vec![];
// 添加上传文件
/*let mod_dir = env::current_dir().unwrap().display().to_string();
files = vec![
UploadFile { name:"file1", path: format!("{}/{}", &mod_dir, "aa.txt")},
UploadFile { name:"file2", path: format!("{}/{}", &mod_dir, "bb.txt") },
];*/
// 创建请求,设置业务参数
let request = MemberInfoGetRequest {
base: BaseRequest { biz_model: biz_model, files: files }
};
// 发送请求
let response:MemberInfoGetResponse = client.execute(request);
// 成功
if response.sub_code.len() == 0 {
println!("resp:{:#?}", response)
} else {
println!("error:{:#?}", response)
}
}
#[cfg(test)]
mod tests {
use sdk::sign::{HashType, SignUtil};
use sdk::http::HttpTool;
use std::collections::HashMap;
#[test]
fn it_works() {
let content = "123";
let private_key = "MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544\nxihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2\naZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB\nAoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI\nUGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO\nqjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP\n1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta\nuQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt\nkxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C\nlno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M\nJUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh\n6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw\nGGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA=";
let sign = SignUtil::rsa_sign(content, private_key, HashType::Sha256);
println!("sign:{}", sign);
}
#[test]
fn test_http() {
let mut map = HashMap::new();
map.insert("aaa", "bbb");
let response = HttpTool::get("http://baidu.com", &map, &HashMap::new());
println!("response:{:#?}", response);
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,23 @@
[package]
name = "sdk"
version = "0.1.0"
authors = ["thc"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# 时间api
chrono = "0.4"
# rsa库
rsa = "0.3.0"
# 加密库
rust-crypto = "^0.2"
# base64库
base64 = "0.12.3"
# HTTP客户端 https://github.com/seanmonstar/reqwest
reqwest = { version = "0.10", features = ["blocking", "json"] }
tokio = { version = "0.2", features = ["full"] }
# 处理json https://serde.rs/
serde = { version = "1.0.114", features = ["derive"] }
serde_json = "1.0.56"

@ -0,0 +1,112 @@
use std::collections::HashMap;
use chrono::Local;
use serde::de::DeserializeOwned;
use crate::http::HttpTool;
use crate::request::{Request, RequestType};
use crate::sign::{SignType, SignUtil};
use serde_json::Value;
pub struct OpenClient {
/// 应用ID
pub app_id: &'static str,
/// 应用私钥,PKCS#1
pub private_key: &'static str,
/// 请求url
pub url: &'static str,
}
impl OpenClient {
/// 发送请求
///
/// - request: 请求对象
///
/// 返回结果
pub fn execute<T: DeserializeOwned>(&self, request: impl Request) -> T {
self.execute_token(request, "")
}
///发送请求
///
/// - request: 请求对象
/// - token:token
///
/// 返回结果
pub fn execute_token<T: DeserializeOwned>(&self, request: impl Request, token: &'static str) -> T {
let struct_obj: T;
let request_type = request.get_request_type();
let headers = &OpenClient::get_default_headers();
let all_params = &self.build_params(&request, token);
if request.get_base().files.len() > 0 {
let base = request.get_base();
let files = &base.files;
let resp = HttpTool::post_file(self.url, all_params, files, headers);
struct_obj = self.parse_response(resp, &request);
} else {
match request_type {
RequestType::Get => {
let resp = HttpTool::get(self.url, all_params, headers);
struct_obj = self.parse_response(resp, &request);
}
RequestType::PostForm => {
let resp = HttpTool::post_form(self.url, all_params, headers);
struct_obj = self.parse_response(resp, &request);
}
RequestType::PostJson => {
let resp = HttpTool::post_json(self.url, all_params, headers);
struct_obj = self.parse_response(resp, &request);
}
RequestType::PostFile => {
let base = request.get_base();
let files = &base.files;
let resp = HttpTool::post_file(self.url, all_params, files, headers);
struct_obj = self.parse_response(resp, &request);
}
}
}
struct_obj
}
fn get_default_headers() -> HashMap<&'static str, &'static str> {
let mut headers = HashMap::new();
headers.insert("Accept-Encoding", "identity");
headers
}
fn parse_response<T: DeserializeOwned>(&self, resp: reqwest::blocking::Response, request: &impl Request) -> T {
let root: HashMap<String, Value> = resp.json().expect("error");
request.parse_response(root)
}
/// 构建请求参数
fn build_params(&self, request: &impl Request, token: &str) -> HashMap<&'static str, String> {
let method = request.get_method();
let version = request.get_version();
let mut all_params = HashMap::new();
all_params.insert("app_id", self.app_id.to_string());
all_params.insert("method", method.to_string());
all_params.insert("charset", "UTF-8".to_string());
all_params.insert("timestamp", Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
all_params.insert("version", version.to_string());
// 添加业务参数
for entry in &request.get_base().biz_model {
all_params.insert(entry.0, entry.1.to_string());
}
if !token.is_empty() {
all_params.insert("app_auth_token", token.to_string());
}
// 创建签名
let sign = SignUtil::create_sign(&all_params, self.private_key, SignType::RSA2);
all_params.insert("sign", sign);
all_params
}
}

@ -0,0 +1,158 @@
/*
Cargo.toml:
[dependencies]
reqwest = { version = "0.10", features = ["blocking", "json"] }
tokio = { version = "0.2", features = ["full"] }
serde = { version = "1.0.114", features = ["derive"] }
serde_json = "1.0.56"
*/
use std::collections::HashMap;
use reqwest::blocking::Response;
use reqwest::header::{HeaderMap, HeaderValue, HeaderName};
use std::str::FromStr;
use serde::Serialize;
/// HTTP请求工具
pub struct HttpTool {}
/// 上传文件对象
pub struct UploadFile {
/// 上传文件表单名称
pub name: &'static str,
/// 文件全路径
pub path: String,
}
impl HttpTool {
/// get请求
///
/// - url:请求url
/// - params:请求参数
/// - headers:请求header
///
/// # Example
///
/// ```rust
/// let mut params = HashMap::new();
/// params.insert("name", "Jim");
/// params.insert("age", "12");
/// let resp = HttpTool::get(url, &params, &HashMap::new());
/// ```
/// return: Response对象
pub fn get<T: Serialize + ?Sized>(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response {
let client = reqwest::blocking::Client::new();
let res = client
.get(url)
.query(params)
.headers(super::http::HttpTool::get_headers_map(headers))
.send();
let resp = res.expect("request error");
resp
}
/// post模拟表单请求
///
/// - url:请求url
/// - params:请求参数
/// - headers:请求header
///
/// # Example
///
/// ```rust
/// let mut params = HashMap::new();
/// params.insert("name", "Jim");
/// params.insert("age", "12");
/// let resp = HttpTool::post_form(url, &params, &HashMap::new());
/// ```
/// return: Response对象
pub fn post_form<T: Serialize + ?Sized>(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response {
let client = reqwest::blocking::Client::new();
let res = client
.post(url)
.form(params)
.headers(super::http::HttpTool::get_headers_map(headers))
.send();
let resp = res.expect("request error");
resp
}
/// post发送json
///
/// - url:请求url
/// - params:请求参数
/// - headers:请求header
///
/// # Example
///
/// ```rust
/// let mut params = HashMap::new();
/// params.insert("name", "Jim");
/// params.insert("age", "12");
/// let resp = HttpTool::post_json(url, &params, &HashMap::new());
/// ```
/// return: Response对象
pub fn post_json<T: Serialize + ?Sized>(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response {
let client = reqwest::blocking::Client::new();
let res = client
.post(url)
.json(params)
.headers(super::http::HttpTool::get_headers_map(headers))
.send();
let resp = res.expect("request error");
resp
}
/// post上传文件
///
/// - url:请求url
/// - params:请求参数
/// - files:上传文件
/// - headers:请求header
///
/// # Example
///
/// ```rust
/// let mut params = HashMap::new();
/// params.insert("name", String::from("Jim"));
/// params.insert("age",String::from("12"));
///
/// // 设置上传文件
/// let mut files = vec![
/// UploadFile { name:"file1", path: String::from("/User/xx/aa.txt") },
/// UploadFile { name:"file2", path: String::from("/User/xx/bb.txt") },
/// ];
///
/// let resp = HttpTool::post_file(url, &params, &files, &HashMap::new());
/// ```
/// return: Response对象
pub fn post_file(url: &str, params: &HashMap<&str, String>, files: &Vec<UploadFile>, headers: &HashMap<&str, &str>) -> Response {
let client = reqwest::blocking::Client::new();
let mut form = reqwest::blocking::multipart::Form::new();
// 添加普通参数
for entry in params {
form = form.text(entry.0.to_string(), entry.1.to_string());
}
// 添加文件
for file in files {
form = form.file(file.name.to_string(), file.path.to_string()).unwrap();
}
let res = client
.post(url)
.multipart(form)
.headers(super::http::HttpTool::get_headers_map(headers))
.send();
let resp = res.expect("request error");
resp
}
fn get_headers_map(headers: &HashMap<&str, &str>) -> HeaderMap {
let mut header_map = HeaderMap::new();
for entry in headers {
header_map.insert(HeaderName::from_str(entry.0).unwrap(), HeaderValue::from_str(entry.1).unwrap());
}
header_map
}
}

@ -0,0 +1,14 @@
pub mod sign;
pub mod client;
pub mod request;
pub mod response;
pub mod http;
extern crate chrono;
/*#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}*/

@ -0,0 +1,26 @@
use crate::request::{BaseRequest, Request, RequestType};
pub struct MemberInfoGetRequest {
// 固定这么写
pub base: BaseRequest
}
impl Request for MemberInfoGetRequest {
fn get_method(&self) -> &str {
"member.info.get"
}
fn get_version(&self) -> &str {
"1.0"
}
fn get_request_type(&self) -> RequestType {
RequestType::Get
}
// 固定这么写
fn get_base(&self) -> &BaseRequest {
&self.base
}
}

@ -0,0 +1,45 @@
use std::collections::HashMap;
use serde_json::Value;
use crate::http::UploadFile;
use serde::de::DeserializeOwned;
pub mod memberinfoget;
pub enum RequestType {
Get,
PostJson,
PostForm,
PostFile,
}
pub trait Request {
/// 返回接口名称
fn get_method(&self) -> &str;
/// 返回版本号
fn get_version(&self) -> &str;
/// 返回请求方式
fn get_request_type(&self) -> RequestType;
/// 返回base
fn get_base(&self) -> &BaseRequest;
fn parse_response<T: DeserializeOwned>(&self, root: HashMap<String, Value>) -> T {
let mut data = root.get("error_response");
if data.is_none() {
let data_name = self.get_method().replace(".", "_") + "_response";
data = root.get(data_name.as_str());
}
let value = serde_json::to_value(data.unwrap()).unwrap();
serde_json::from_value(value).unwrap()
}
}
pub struct BaseRequest {
pub biz_model: HashMap<&'static str, String>,
pub files: Vec<UploadFile>
}

@ -0,0 +1,36 @@
extern crate serde;
use serde::{Deserialize};
// 响应参数
#[derive(Deserialize, Debug)]
pub struct MemberInfoGetResponse {
// ~~~ 固定部分 ~~~
pub code: String,
pub msg: String,
// json中可能没有sub_code属性,因此需要加上 #[serde(default)]
// 详见:https://serde.rs/field-attrs.html
#[serde(default)]
pub sub_code: String,
#[serde(default)]
pub sub_msg: String,
// ~~~ 固定部分 ~~~
// 下面是业务字段
#[serde(default)]
pub id: u32,
#[serde(default)]
pub name: String,
pub member_info: MemberInfo
}
#[derive(Deserialize, Debug)]
pub struct MemberInfo {
#[serde(default)]
pub is_vip: i8,
#[serde(default)]
pub vip_endtime: String
}

@ -0,0 +1,108 @@
extern crate rsa;
extern crate crypto;
use std::collections::HashMap;
use rsa::{RSAPrivateKey, PaddingScheme};
use rsa::Hash;
use crypto::sha2::Sha256;
use crypto::digest::Digest;
use std::iter::repeat;
pub struct SignUtil {}
pub enum SignType {
RSA,
RSA2,
}
pub enum HashType {
Sha1,
Sha256
}
impl SignUtil {
pub fn create_sign(all_params: &HashMap<&str, String>, private_key: &str, sign_type: SignType) -> String {
let content = SignUtil::get_sign_content(all_params);
// println!("content:{}", content);
let hash_type;
match sign_type {
SignType::RSA => hash_type = HashType::Sha1,
SignType::RSA2 => hash_type = HashType::Sha256,
}
SignUtil::rsa_sign(content.as_str(), private_key, hash_type)
}
/// RSA签名
///
/// - content: 签名内容
/// - private_key: 私钥,PKCS#1
/// - hash_type: hash类型
///
/// # Examples
///
/// ```
/// use sdk::sign::{SignUtil, HashType};
///
/// let content = "123";
/// let private_key = "your private key";
/// let sign = SignUtil::rsa_sign(content, private_key, HashType::Sha256);
///
/// println!("sign:{}", sign);
/// ```
/// return: 返回base64字符串
pub fn rsa_sign(content: &str, private_key: &str, hash_type: HashType) -> String {
// 格式化私钥
let der_encoded = private_key
.lines()
.filter(|line| !line.starts_with("-"))
.fold(String::new(), |mut data, line| {
data.push_str(&line);
data
});
let der_bytes = base64::decode(der_encoded).expect("failed to decode base64 content");
// 获取私钥对象
let private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
// 创建一个Sha256对象
let mut hasher = Sha256::new();
// 对内容进行摘要
hasher.input_str(content);
// 将摘要结果保存到buf中
let mut buf: Vec<u8> = repeat(0).take((hasher.output_bits()+7)/8).collect();
hasher.result(&mut buf);
// 对摘要进行签名
let hash;
match hash_type {
HashType::Sha1 => hash = Hash::SHA1,
HashType::Sha256 => hash = Hash::SHA2_256
}
let sign_result = private_key.sign(PaddingScheme::PKCS1v15Sign {hash: Option::from(hash) }, &buf);
// 签名结果转化为base64
let vec = sign_result.expect("create sign error for base64");
base64::encode(vec)
}
fn get_sign_content(all_params: &HashMap<&str, String>) -> String {
let mut content = Vec::new();
let keys = all_params.keys();
let mut sorted_keys = Vec::new();
for key in keys {
sorted_keys.push(key);
}
// 排序
sorted_keys.sort();
for key in sorted_keys {
let val = all_params.get(key);
if val.is_some() {
content.push(key.to_string() + "=" + val.unwrap());
}
}
content.join("&")
}
}

@ -0,0 +1,30 @@
use sop;
ALTER TABLE `sop`.`isv_info` ADD COLUMN `user_id` BIGINT NULL DEFAULT 0 COMMENT 'user_account.id' AFTER `remark`;
CREATE INDEX `idx_userid` USING BTREE ON `sop`.`isv_info` (`user_id`);
CREATE TABLE `user_account` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名(邮箱)',
`password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '2:邮箱未验证,1:启用,0:禁用',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息';
CREATE TABLE `isp_resource` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '' COMMENT '资源名称',
`content` varchar(128) NOT NULL DEFAULT '' COMMENT '资源内容(URL)',
`ext_content` text,
`version` varchar(32) NOT NULL DEFAULT '' COMMENT '版本',
`type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '资源类型:0:SDK链接',
`is_deleted` tinyint(4) NOT NULL DEFAULT '0',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISP资源表';

@ -1,77 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-website</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--
<artifactId>sop-bridge-nacos</artifactId> : 使用nacos注册中心
<artifactId>sop-bridge-eureka</artifactId> : 使用eureka注册中心
-->
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-bridge-nacos</artifactId>
<!--<artifactId>sop-bridge-eureka</artifactId>-->
<version>4.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-website</artifactId>
<version>4.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>sop-website-server</module>
</modules>
</project>

@ -0,0 +1,22 @@
{
"plugins": [
"transform-decorators-legacy",
"transform-class-properties",
"transform-object-rest-spread",
"transform-object-assign",
[
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
]
],
"presets": [
"react",
"stage-0",
"es2015"
]
}

@ -0,0 +1,28 @@
{
"extends": "eslint-config-ali/react",
"parser": "babel-eslint",
"env": {
"browser": true,
"node": true,
"mocha": true
},
"globals": {
"AK_GLOBAL": true,
"dd": {},
"_czc": {},
"dplus": {},
"salt": {},
"_": true,
"homePageData": {},
"Lang": {}
},
"rules": {
"max-len": 0,
"new-cap": [2, { "newIsCap": true, "properties": false }],
"react/jsx-indent": 0,
"react/jsx-indent-props": 0,
"indent": 0,
"radix": ["error", "as-needed"],
"linebreak-style": [0 ,"error", "windows"]
}
}

@ -0,0 +1,17 @@
.DS_Store
node_modules/
dist/
build/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
tests/**/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="//cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script>
<script>
window.rootPath = '';
window.defaultLanguage = 'zh-cn';
var lang = Cookies.get('docsite_language');
if (!lang) {
lang = 'zh-cn';
}
window.location = window.rootPath + '/zh-cn';
</script>
</body>
</html>

@ -0,0 +1,24 @@
# 门户网站
纯静态网站,作为开放平台基本介绍使用,可独立部署,logo制作:http://www.uugai.com/
# 初始化
npm install
# 启动
docsite start
# 打包
首先,需要配置下站点的根路径,修改site_config/site.js中的rootPath字段。规则如下:
当部署根路径为/,则设置为''空字符串即可。
当部署根路径不为/,则设置为具体的根路径,注意需以/开头,但不能有尾/。
运行`build.sh`
将dist内容上传到服务器

@ -0,0 +1,10 @@
---
key1: hello
key2: world
---
# blog1
it supports the resolution of meta data,the text between `---`(at least three`-`)written in the format of `key:value`,will be resolved to `md_json/blog.json`,`filename` and `__html` are preserved.
filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

@ -0,0 +1,10 @@
---
key1: hello
key2: world
---
# 博客1
支持元数据的解析,`---`(至少三个`-`)开头之间的数据按照`key:value`的形式,最终会被解析到`md_json/blog.json`中,其中`filename`和`__html`为保留字段,请勿使用。
博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充

@ -0,0 +1,20 @@
#!/bin/sh
echo "开始构建..."
dist_dir="dist"
if [ ! -d "$dist_dir" ]; then
mkdir $dist_dir
fi
docsite build
echo "复制文件到${dist_dir}目录"
rm -rf $dist_dir/*
cp -r zh-cn/* $dist_dir
cp -r build $dist_dir/build
cp -r img $dist_dir/img
echo "构建完毕"

@ -0,0 +1,249 @@
# example of footnote
this is a demo of the usage of footnote.
this is footnote1 [^1]。click it and see what will happen。
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
this is a long text.
[^1]: this is the explanation of footnote

@ -0,0 +1,16 @@
# demo for reference of md or image for relative path
if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system.
click here: [demo1.md](./demo1.md)。
the same is to image.
![](./img/brhtqqzh.jpeg)
# demo for math typesetting(katex)
$E = mc^2$
$$x = a_{1}^n + a_{2}^n + a_{3}^n$$
$$\sqrt[3]{X}$$
$$\sqrt{5 - x}$$

@ -0,0 +1,7 @@
# demo for reference of md or image for relative path, across directory
if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system.
click here: [demo1.md](../demo1.md)。
the same is to image.
![](../img/brhtqqzh.jpeg)

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 KiB

@ -0,0 +1,401 @@
# 脚注的使用
这是一段示例文字,展示脚注的使用。
这是脚注1 [^1]。点击脚注滚动至对应脚注处。
下面是一大段文字,填充开始。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
这是填充文字。
[^1]: 这是脚注的说明

@ -0,0 +1,17 @@
# 文档之间跳转、图片引用的示例
文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。
点击跳转: [demo1.md](./demo1.md)。
图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如
![](./img/brhtqqzh.jpeg)
# 数学公式的使用(katex)
$E = mc^2$
$$x = a_{1}^n + a_{2}^n + a_{3}^n$$
$$\sqrt[3]{X}$$
$$\sqrt{5 - x}$$

@ -0,0 +1,8 @@
# 跨目录文档之间跳转、图片引用的示例
这是一段文字,将要跳转到demo1。
文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。
点击跳转: [demo1.md](../demo1.md)。
图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如
![](../img/brhtqqzh.jpeg)

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 KiB

@ -0,0 +1,35 @@
pages:
# key is the dirname of pages in src/pages
home:
# 首页配置
zh-cn:
title: 'XX开放平台'
keywords: '开放平台,Open,Api'
description: '一站式开放接口服务平台,可快速搭建起自己的开放平台'
# home config
en-us:
title: 'XX开放平台'
keywords: '开放平台,Open,Api'
description: '一站式开放接口服务平台,可快速搭建起自己的开放平台'
community:
# 社区页配置
zh-cn:
title: '网页标签title'
keywords: '关键词1,关键词2'
description: '页面内容简介'
# community page config
en-us:
title: 'page title'
keywords: 'keyword1,keyword2'
description: 'page description'
blog:
# 博客列表页配置
zh-cn:
title: '网页标签title'
keywords: '关键词1,关键词2'
description: '页面内容简介'
en-us:
# blog list page config
title: 'page title'
keywords: 'keyword1,keyword2'
description: 'page description'

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="keywords" content="blog1" />
<meta name="description" content="blog1" />
<!-- 网页标签标题 -->
<title>blog1</title>
<link rel="shortcut icon" href="/img/docsite.ico"/>
<link rel="stylesheet" href="/build/blogDetail.css" />
</head>
<body>
<div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><img class="logo" src="/img/dubbo_colorful.png"/><div class="header-menu"><img class="header-menu-toggle" src="/img/system/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a href="/en-us/index.html" target="_self">HOME</a></li><li class="menu-item menu-item-normal"><a href="/en-us/docs/demo1.html" target="_self">DOCS</a></li><li class="menu-item menu-item-normal menu-item-normal-active"><a href="/en-us/blog/index.html" target="_self">BLOG</a></li><li class="menu-item menu-item-normal"><a href="/en-us/community/index.html" target="_self">COMMUNITY</a></li></ul></div></div></header><section class="blog-content markdown-body"><h1>blog1</h1>
<p>it supports the resolution of meta data,the text between <code>---</code>(at least three<code>-</code>)written in the format of <code>key:value</code>,will be resolved to <code>md_json/blog.json</code><code>filename</code> and <code>__html</code> are preserved.</p>
<p>filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text</p>
</section><footer class="footer-container"><div class="footer-body"><div class="copyright"><span><div style="margin:0 auto;padding:20px 0;a:[object Object]"><a style="color:#999;padding-right:10px;text-decoration:none;height:20px;line-height:20px;img:[object Object];p:[object Object]">联系我们:xxx@163.com</a></div></span></div></div></footer></div></div>
<script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script>
<script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script>
<script>
window.rootPath = '';
</script>
<script src="/build/blogDetail.js"></script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save