houweichen преди 2 месеца
родител
ревизия
18b32c52d5
променени са 100 файла, в които са добавени 15136 реда и са изтрити 0 реда
  1. 21 0
      LICENSE
  2. 5 0
      README.md
  3. 24 0
      Roadmap.md
  4. BIN
      __MACOSX/spec/._api.md
  5. BIN
      __MACOSX/src/._.DS_Store
  6. BIN
      __MACOSX/src/Tools/._.DS_Store
  7. BIN
      __MACOSX/src/Tools/Wicture.DbRESTFul.PMT/._Dockerfile.txt
  8. BIN
      __MACOSX/src/Tools/Wicture.DbRESTFul.PMT/._TODO.txt
  9. BIN
      __MACOSX/src/Wicture.DbRESTFul/._.DS_Store
  10. BIN
      __MACOSX/src/ci/._.DS_Store
  11. 1 0
      _config.yml
  12. 263 0
      spec/api.md
  13. 44 0
      spec/assistant.md
  14. 0 0
      spec/cache.md
  15. 0 0
      spec/config.md
  16. 0 0
      spec/conventions.md
  17. 233 0
      spec/cri.md
  18. 159 0
      spec/csi.md
  19. 3 0
      spec/https.md
  20. 45 0
      spec/index.md
  21. 157 0
      spec/invoke.md
  22. 94 0
      spec/middleware.md
  23. 132 0
      spec/permission.md
  24. 39 0
      spec/procedure.md
  25. 388 0
      spec/query.md
  26. 215 0
      spec/repository.md
  27. 27 0
      spec/restful-lib.md
  28. 1 0
      spec/rw-control.md
  29. BIN
      src/.DS_Store
  30. 87 0
      src/DbRESTFul.sln
  31. 2143 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/API/common.json
  32. 385 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/API/pmt.json
  33. 896 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/API/test.json
  34. 418 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/CSI/common.json
  35. 63 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/CSI/pmt.json
  36. 124 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/CSI/test.json
  37. 20 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Controllers/TestController.cs
  38. 37 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Controllers/WeatherForecastController.cs
  39. 38 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Models/Models.cs
  40. 13 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Program.cs
  41. 30 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Properties/launchSettings.json
  42. 93 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Repositories/CommonRepository.cs
  43. 14 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Repositories/TestRepository.cs
  44. 37 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Service/UserIdentifier.cs
  45. 43 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Startup.cs
  46. 87 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Test/common.rest
  47. 15 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/WeatherForecast.cs
  48. 17 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Wicture.DbRESTFul.Samples.MySqlService.csproj
  49. 59 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/appsettings.json
  50. 40 0
      src/Samples/Wicture.DbRESTFul.Samples.MySqlService/nlog.config
  51. 98 0
      src/Tests/Wicture.DbRESTFul.Core.Test/ConfigurationTest.cs
  52. 28 0
      src/Tests/Wicture.DbRESTFul.Core.Test/Wicture.DbRESTFul.Core.Test.csproj
  53. 115 0
      src/Tests/Wicture.DbRESTFul.Core.Test/appsettings.json
  54. BIN
      src/Tools/.DS_Store
  55. 3 0
      src/Tools/Wicture.DbRESTFul.PMT/.bowerrc
  56. 67 0
      src/Tools/Wicture.DbRESTFul.PMT/API/api.json
  57. 1090 0
      src/Tools/Wicture.DbRESTFul.PMT/API/csi.json
  58. 3304 0
      src/Tools/Wicture.DbRESTFul.PMT/API/pmt.json
  59. 185 0
      src/Tools/Wicture.DbRESTFul.PMT/CSI/api.json
  60. 146 0
      src/Tools/Wicture.DbRESTFul.PMT/CSI/csi.json
  61. 77 0
      src/Tools/Wicture.DbRESTFul.PMT/CSI/pmt.json
  62. 95 0
      src/Tools/Wicture.DbRESTFul.PMT/CSI/user.json
  63. 68 0
      src/Tools/Wicture.DbRESTFul.PMT/Controllers/ApiSyncController.cs
  64. 41 0
      src/Tools/Wicture.DbRESTFul.PMT/Controllers/CsiSyncController.cs
  65. 17 0
      src/Tools/Wicture.DbRESTFul.PMT/Controllers/HomeController.cs
  66. 163 0
      src/Tools/Wicture.DbRESTFul.PMT/Controllers/ImportController.cs
  67. 261 0
      src/Tools/Wicture.DbRESTFul.PMT/Controllers/SpecController.cs
  68. 97 0
      src/Tools/Wicture.DbRESTFul.PMT/Database/structure.sql
  69. 46 0
      src/Tools/Wicture.DbRESTFul.PMT/Dockerfile.txt
  70. 100 0
      src/Tools/Wicture.DbRESTFul.PMT/Models/ACIModels.cs
  71. 13 0
      src/Tools/Wicture.DbRESTFul.PMT/Program.cs
  72. 27 0
      src/Tools/Wicture.DbRESTFul.PMT/Properties/launchSettings.json
  73. 48 0
      src/Tools/Wicture.DbRESTFul.PMT/Repositories/CSIRepository.cs
  74. 239 0
      src/Tools/Wicture.DbRESTFul.PMT/Repositories/DefaultApiRepository.cs
  75. 239 0
      src/Tools/Wicture.DbRESTFul.PMT/Repositories/DefaultCsiRepository.cs
  76. 76 0
      src/Tools/Wicture.DbRESTFul.PMT/Repositories/ImportRepository.cs
  77. 15 0
      src/Tools/Wicture.DbRESTFul.PMT/Repositories/ProjectRepository.cs
  78. 68 0
      src/Tools/Wicture.DbRESTFul.PMT/Repositories/UserRepository.cs
  79. 155 0
      src/Tools/Wicture.DbRESTFul.PMT/Scaffolding/ScaffoldingHelper.cs
  80. 39 0
      src/Tools/Wicture.DbRESTFul.PMT/Service/UserIdentifier.cs
  81. 18 0
      src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/ApiSpecGenerator.Partial.cs
  82. 747 0
      src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/ApiSpecGenerator.cs
  83. 57 0
      src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/DbSpecGenerator.Partial.cs
  84. 22 0
      src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/DbSpecGenerator.cs
  85. 30 0
      src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/IntroductionGenerator.cs
  86. 159 0
      src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/SpecHelper.cs
  87. 72 0
      src/Tools/Wicture.DbRESTFul.PMT/Startup.cs
  88. 18 0
      src/Tools/Wicture.DbRESTFul.PMT/TODO.txt
  89. 7 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/Home/About.cshtml
  90. 111 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/Home/ConfiguredApi.cshtml
  91. 17 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/Home/Contact.cshtml
  92. 168 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/Home/Index.cshtml
  93. 6 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/Shared/Error.cshtml
  94. 67 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/Shared/_Layout.cshtml
  95. 95 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/Spec/Index.cshtml
  96. 1 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/_ViewImports.cshtml
  97. 3 0
      src/Tools/Wicture.DbRESTFul.PMT/Views/_ViewStart.cshtml
  98. 22 0
      src/Tools/Wicture.DbRESTFul.PMT/Wicture.DbRESTFul.PMT.csproj
  99. 45 0
      src/Tools/Wicture.DbRESTFul.PMT/appsettings.json
  100. 41 0
      src/Tools/Wicture.DbRESTFul.PMT/bower.json

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 DawsonLiu Wicture
+
+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.

+ 5 - 0
README.md

@@ -0,0 +1,5 @@
+DbRESTFul项目支持从关系数据库直接暴露成RESTFul API服务。它采用ASP.NET Web API实现,数据库访问层采用Dapper。
+目前只支持MySQL数据库,但基于Dapper的实现,理论上可以支持其它主流数据库。
+支持基本CURD操作
+支持排序,分页,包含与排除字段查询
+DbRESTFUl中一个独立的项目,可以直接host,并通过:http://localhost:<port>/api

+ 24 - 0
Roadmap.md

@@ -0,0 +1,24 @@
+Roadmap
+
+1. Logging
+	- API
+	- Exception (exceptional)
+	- Performace
+
+2. Micorservice
+	- Dynamic configuration
+	- Service Package
+	- Version Management
+	- Upgrade online
+	- Configuration outside box
+
+3. DbRESTFul
+	- CCI Management
+	- RCI Management
+	- Config.json Watch and reload
+
+4. Stack (Standard)
+	- Message Queue
+	- Logging
+	- APM
+	- Monitoring

BIN
__MACOSX/spec/._api.md


BIN
__MACOSX/src/._.DS_Store


BIN
__MACOSX/src/Tools/._.DS_Store


BIN
__MACOSX/src/Tools/Wicture.DbRESTFul.PMT/._Dockerfile.txt


BIN
__MACOSX/src/Tools/Wicture.DbRESTFul.PMT/._TODO.txt


BIN
__MACOSX/src/Wicture.DbRESTFul/._.DS_Store


BIN
__MACOSX/src/ci/._.DS_Store


+ 1 - 0
_config.yml

@@ -0,0 +1 @@
+theme: jekyll-theme-merlot

+ 263 - 0
spec/api.md

@@ -0,0 +1,263 @@
+定义API调用接口(API)
+===================
+
+--------------------------------------
+定义API(Application Programming Interface)调用接口,是通过约定格式来配置传统WebAPI的Controller以及一些其他配置参数。使得开发人员可以节约成本并更方便的管理其开发接口。API(本文中后续的API统一指的是Dbrestful中的API)还可以模拟返回结果以及生成文档,这样可以使得接口在未开发完成时前端也可以根据接口文档并行开发。不必浪费时间等待后端接口开发完成。
+
+
+* * *
+[TOC]
+* * *
+
+## 配置API调用接口
+配置API调用接口可以通过文件直接配置或通过PMT平台页面配置管理,配置文件以`json`格式存储,通常,该配置代码文件存放在服务项目的API目录,也可以通过该项目的根目录下的[`config.json`](config.md)文件中指定。
+
+```json
+{
+  ……
+  "APIPath": "API/"
+  ……
+}
+```
+
+
+## API配置说明
+### API Schema
+API的详细定义,请参考`Wicture.DbRESTFul`项目中`Schema`目录下的`api-schema.json`文件。它的主要结构如下:
+```json
+{
+    "version": "",
+    "owner": "",
+    "updatedTime": "",
+    "name": "",
+    "module": "",
+    "url": "",
+    "useAbsoluteUrl": true,
+    "method": "",
+    "title": "",
+    "summary": "",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": ""
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "",
+          "name": "",
+          "description": ""
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "",
+          "type": "int",
+          "nullable": false,
+          "description": ""
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": ""
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "",
+          "schema": [
+            {
+              "name": "items",
+              "type": "array",
+              "nullable": false,
+              "description": "",
+              "schema": [
+                {
+                  "name": ",
+                  "type": "int",
+                  "nullable": false,
+                  "description": ""
+                },...]
+               }]
+      ]},
+    "mock": [
+      {
+        "input": {
+          "waitForStatus": "*",
+          "pageIndex": "*",
+          "pageSize": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "items": "",
+            "pagination": ""
+          }
+        }
+      }
+    ]
+  },
+```
+
+### 示例说明
+1. 假设我们需要配置API基本信息我可以这样配置:
+```json
+{
+    "version": "1.0",
+    "owner": "田成果",
+    "updatedTime": "2016-09-26T10:03:16",
+    "name": "ListOrders",
+    "module": "Order.Info",
+    "url": "/client/v1/order/list",
+    "useAbsoluteUrl": true,
+    "method": "GET",
+    "title": "获取订单列表",
+    "summary": "获取订单列表",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "OrderRepository.ListOrders"
+    },
+    //"implementation": {
+    //"type": "csi",
+    //"name": "ListOrders"
+    //},
+}
+```
+
+> 说明:
+> 1. `version`:`API`的版本号。
+> 2. `owner`:`API`的负责人。
+> 3. `name`:名称,必须全局唯一。
+> 4. `module`:`API`所属模块。
+> 5. `url`:`API`的访问地址。
+> 6. `useAbsoluteUrl`:是否绝对地址(否的时候,访问地址为API\模块名\url)
+> 7. `method`:HTTP请求类型(GET POST DELETE PUT ect)
+> 8. `useAbsoluteUrl`:是否绝对地址(否的时候,访问地址为API\模块名\url)
+> 9. `title`:接口标题
+> 10. `summary`:描述
+> 11. `note`:备注
+> 12. `allowAnonymous`:是否匿名访问,一般都要使用身份验证和权限控制,但总有部分接口是可以匿名访问的
+> 13. `useAbsoluteUrl`:是否绝对地址(否的时候,访问地址为API\模块名\url)
+>       
+> 14. `cache`: 缓存,暂时只支持redis
+> 15. `implemented`: 是否已实现,否的时候会返回mock中定义的返回结果。
+> 16. `implementation`: 实现方式,实现方式分为两种,一种为csi,一种为repository。(请参考`Wicture.DbRESTFul`项目中`docs`目录下的`CSI`和`repository`文件)
+
+
+2. 假设我们需要配置API请求信息我可以这样配置:
+```json
+{
+  "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "id",
+          "nullable": true
+          "description": "{id:1}"
+        }
+      ],
+      "body": [
+        {
+          "type": "object",
+          "name": "test"
+        }
+      ]
+    },
+}
+```
+> 说明:
+> 1. `parameter`:请求信息格式,分类两类:query以及body。
+> 2. `query`:`API`的URL参数。即在URL后跟上请求的定义参数。如:api/test?id=1
+> 3. `body`:`API`的表单参数。按raw方式请求,定义内容可以JSON
+> 4. `nullable`:表示是否必须填写,如是则必须有内容。
+
+
+3. 假设我们需要配置API返回以及模拟信息我可以这样配置:
+```json
+{
+  "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "",
+          "type": "int",
+          "nullable": false,
+          "description": ""
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": ""
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "",
+          "schema": [
+            {
+              "name": "items",
+              "type": "array",
+              "nullable": false,
+              "description": "",
+              "schema": [
+                {
+                  "name": ",
+                  "type": "int",
+                  "nullable": false,
+                  "description": ""
+                },...]
+               }]
+      ]},
+    "mock": [
+      {
+        "input": {
+          "waitForStatus": "*",
+          "pageIndex": "*",
+          "pageSize": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "items": "",
+            "pagination": ""
+          }
+        }
+      }
+}
+```
+> 说明:
+> 1. `result`:返回类型默认是json。
+> 2. `schema`:`result`中的`schema`指的是返回集合定义。可以是多个返回参数的集合。
+> 3. `nullable`:表示是否必须填写,如是则必须有内容。
+> 4. `type`:返回字段类型。string,int,object,array etc...
+> 5. `mock`:模拟返回信息,当`implemented`是false时才会生效。返回内容可以是多条。条件根据`input`设置匹配
+> 6. `input`:模拟的请求参数,为json格式。`*`表示所有参数,不然则按照定义的返回
+> 7. `output`:模拟返回字段,为json格式。
+
+
+## TODOs

+ 44 - 0
spec/assistant.md

@@ -0,0 +1,44 @@
+# Assistant
+
+
+## 版本更新(Version)
+
+Version | UpdatedBy | UpdatedDate | Note
+:------:|:---------:|:-----------:|------
+0.0.1   |刘合桃      |2015/09/24   |Draft
+* * *
+
+[TOC]
+
+* * *
+
+## 1. DbRESTFul.Assistant功能概述
+`DbRESTFul`是一套基于.NET WEB API 2.0的直接为数据库(目前仅支持MySQL)提供RESTFul API的数据服务层,支持三种:单表单视图的基本CRUD操作(query),存储过程调用(procedure)和配置代码调用(cci),且支持数据的读写分离,参数验证等。
+`DbRESTFul.Assistant`是一个基于Web的`DbRESTFul`开发支持工具,方便我们的后端开发人员可以在线快速的开发基于`DbRESTFul`的开发。具体提供如下几大功能:
+  > 1. 项目(数据库)管理
+  > 2. 对象(表,视图)管理
+  > 3. 存储过程管理
+  > 4. 配置查询(cci)管理
+  > 5. 试运行代码
+  > 6. 生成测试用例
+  > 7. Api接口管理
+  > 8. 接口文档
+  > 9. 数据库文档
+
+
+
+## 2. 项目(数据库)管理
+
+###2.1 项目(数据库)列表
+
+###2.2 创建项目(数据库)
+
+###2.3 项目(数据库)结构树
+
+
+
+
+
+
+
+## Review Notes:

+ 0 - 0
spec/cache.md


+ 0 - 0
spec/config.md


+ 0 - 0
spec/conventions.md


+ 233 - 0
spec/cri.md

@@ -0,0 +1,233 @@
+配置Redis代码调用(CRI)
+===================
+
+--------------------------------------
+配置Redis代码调用(Configured Redis Invocation),即client端通过`http/https`直接调用,操作`Redis`数据库。该功能适用于直接对`Redis`数据库的一些基本操作,开发人员可以将操作代码的`Redis`写到配置文件中,或者直接访问`api/v1/cri`接口调用。
+
+* * *
+[TOC]
+* * *
+
+
+## 配置说明
+开发人员可以通过修改项目的根目录下的[`config.json`](config.md)文件中,`CRI`子项的相关属性来设置适合项目的Redis数据库操作配置,具体格式如下:
+```json
+{
+    ……
+    "CRI": {
+        "ConnectionString": "trade.wicture.com:6379,password=Wicture@123",
+        "Path": "CRI/",
+        "UnwrapParameterName": "param",
+        "ApiOnly": false
+    }
+    ……
+}
+```
+> 说明:
+> 1. `ConnectionString`:Redis数据库的连接字符串。
+> 2. `Path`: `CRI`的配置文件目录。
+> 3. `UnwrapParameterName`: 定义Api的参数时,可以指定一个参数名称,将这个参数名称的值做为`param`的值,对于写操作时非常有用,否则会将该参数的名称一起包在一个对象存入Redis.
+> 4. `ApiOnly`: 是否只启用通过API定义的方式调用,否则可以通过直接通过`DbRESTFul`接口调用,但该方式没有安全控制。
+
+
+## 直接通过`DbRESTFul`接口`api/v1/cri`调用
+
+### 接口说明:
+
+简单直接对Redis数据库直接键值操作,可以直接通过下面的方式调用:
+
+* **请求URL:** `http://<host:port>/api/v1/cri`
+* **请求类型:** `POST`
+* **请求参数: ** 无
+* **表单参数: **
+```json
+{
+    "key": "String",
+    "method": "String",
+    "dbIndex": "Number",
+    "resultType": "String",
+    "param": "Object"
+}
+```
+* **参数说明: **
+|      name    |  type  | nullable |description
+|--------------|--------|----------|-------------
+|key           |string  |no        |Redis数据库的Key
+|method        |string  |no        |调用方法,请参考CRI支持方法。
+|dbIndex       |int     |yes       |Redis数据库Index,默认为:0
+|resultType    |string  |yes       |返回数据的类型,String类型或者Object类型,默认为:String
+|param         |object  |yes       |要操作的数据,通过在写入时需要指定,可以是string类型,也可以是object类型。
+
+* **请求返回数据:**
+```json
+{
+    "statusCode": "200",
+    "errorMessage": "",
+    "data": {}
+}
+```
+* **返回数据说明: **
+|      name    |  type  | nullable |description
+|--------------|--------|----------|-------------
+|statusCode    |string  |no        |状态码,200为正常,500为内部错误,其它为用户自定义错误
+|errorMessage  |string  |no        |如果无错误信息则为空,否则为错误信息。
+|data          |int     |no        |返回在数据,如果为请求的`resultType`为String则返回字符串,否则为对象类型。
+
+
+### 调用示例:
+假设我需要插入一个用户信息,则可以通过如下调用方式:
+```bash
+curl -X POST \
+    -H "Content-Type: application/json" \
+    -d '{
+        "dbIndex": 1,
+        "method": "StringSet",
+        "resultType": "Object",
+        "key": "Member_Dawson",
+        "param": {
+          "id": 10,
+          "name": "Dawson",
+          "age": 43
+        }
+    }' \
+    http://localhost:5000/api/v1/cri
+```
+返回结果:
+```json
+{
+    "statusCode": "200",
+    "errorMessage": null,
+    "data": {
+        "success": true
+    }
+}
+```
+
+查询该操作的结果:
+```bash
+curl -X POST \
+    -H "Content-Type: application/json" \
+    -d '{
+        "dbIndex": 1,
+        "method": "StringGet",
+        "resultType": "Object",
+        "key": "Member_Dawson"
+    }' \
+    http://localhost:5000/api/v1/cri
+```
+返回结果:
+```json
+{
+    "statusCode": "200",
+    "errorMessage": null,
+    "data": {
+        "id": 10,
+        "name": "Dawson",
+        "age": 43
+    }
+}
+```
+
+
+## 通过后端配置代码调用
+### CRI JSON配置说明
+CRI的详细定义,请参考`Wicture.DbRESTFul`项目中`Schema`目录下的`cri-schema.json`文件。它的主要结构如下:
+```json
+{
+    "name": "String",
+    "key": "String",
+    "method": "String",
+    "dbIndex": "Number",
+    "resultType": "String",
+    "param": "Oject"
+}
+```
+> 说明:通过后端配置代码调用,其配置结构与直接调用接口非常相似,只是要求给该配置加了一个`name`,以标识其唯一性。
+
+
+### 示例说明
+假设我们需要向`Redis`数据库`1`中插入下面的一个键为`Greeting`的`String`类型的值。我们可以这样做:
+1. 定义`CRI`,在项目的`CRI`目录下,新建一个`cri-file.json`的文件,然后加入:
+```json
+[{
+    "name": "SetGreeting",
+    "key": "SetGreeting",
+    "dbIndex": 1,
+    "method": "StringSet"
+}]
+```
+2. 定义相关API接口,将其实现方式改为`cri`,同时指定实现名称:`Greeting`
+```json
+{
+    ……
+    "module": "cri",
+    "url": "string/set",
+    "method": "POST",
+    "implementation": {
+        "type": "cri",
+        "name": "SetGreeting"
+    },
+    "parameter": {
+      "body": [
+        {
+          "name": "param",
+          "type": "string",
+          "nullable": false,
+          "description": "数据"
+        }
+      ]
+    }
+    ……
+}
+```
+3. 通过Api访问
+    - Url:`http://<host>:<port>/api/cri/string/set`
+    - Method: `POST`
+    - Body:
+    ```json
+    {
+        "param": {
+          "id": 10,
+          "name": "Dawson",
+          "age": 43
+        }
+    }
+    ```
+4. 假设我们设置`ApiOnly = false`,则可以通过该API来验证:
+	- Url: `http://<host>:<port>/api/v1/cri`
+    - Method: `GET`
+    - Request:
+    ```json
+    {
+        "dbIndex": 1,
+        "method": "StringGet",
+        "resultType": "Object",
+        "key": "SetGreeting"
+    }
+    ```
+    - Result:
+    ```json
+    {
+        "statusCode": "200",
+        "errorMessage": null,
+        "data": {
+            "id": 10,
+            "name": "Dawson",
+            "age": 43
+        }
+    }
+```
+
+
+## CRI支持方法 <a name='SupportedMethods'></a>
+CRI支持通用的Redis操作,基本代码可以在`DbRESTFul`项目的`Redis`目录下找到`RedisContext`类来做扩展,目前已支持的方法如下:
+   Method    |    Description
+-------------|-----------------------
+StringGet    |读取字符数据
+StringSet    |写入字符数据
+ListPop      |对应Redis的ListLeftPop操作
+ListRange    |对应Redis的ListRange操作
+ListPush     |对应Redis的ListRightPush操作
+KeyDelete    |删除键,对应Redis的KeyDelete操作
+SetAdd       |给Set添加项目,对应Redis的SetAdd操作
+……           |……

+ 159 - 0
spec/csi.md

@@ -0,0 +1,159 @@
+配置SQL代码调用(CSI)
+===================
+
+配置SQL代码调用(Configured SQL Invocation),即client端通过`http/https`直接调用后端配置好的`SQL`。该功能适用于复杂的业务场景,开发人员可以将复杂的`SQL`写到配置文件中,后台通过传进来的名称与参数调用。CSI是DbRESTFul的核心功能之一。
+
+* * *
+[TOC]
+* * *
+
+## 配置SQL代码文件
+配置SQL代码调用的代码文件以`json`格式存储,通常,该配置代码文件存放在服务项目的CSI目录,也可以通过该项目的根目录下的[`config.json`](config.md)文件中指定。
+
+```json
+{
+  ……
+  "CSIPath": "CSI/"
+  ……
+}
+```
+
+
+## CSI JSON配置说明
+### CSI Schema
+CSI的详细定义,请参考`Wicture.DbRESTFul`项目中`Schema`目录下的`csi-schema.json`文件。它的主要结构如下:
+```json
+{
+    "name": "",
+    "code": "",
+    "resultSet": "",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+}
+```
+
+### 示例说明<a name="示例说明"></a>
+假设我们需要查询指定项目下的所有接口定义。
+调用参数包括:
+
+|   name   |   type   | required |      note
+|----------|----------|----------|----------------
+|projectId | int      |    yes   | The project Id
+|module    | string   |    no    | The module name
+|keyword   | string   |    no    | The keyword for searching (name, module, owner, title)
+|orderBy   | string   |    no    | Sorting
+|pageIndex | int      |    yes   | pageIndex for pagination
+|pageSize  | int      |    yes   | pageSize for pagination
+
+对应的CSI可能是这样定义的:
+```json
+{
+    "name": "list_api_for_project",
+    "code": "SELECT * FROM api WHERE projectId = @projectId [AND `module`=@module] [AND (`name` LIKE CONCAT('%',@keyword,'%') OR `module` LIKE CONCAT('%',@keyword,'%') OR `owner` LIKE CONCAT('%',@keyword,'%') OR `title` LIKE CONCAT('%',@keyword,'%') )] @orderBy LIMIT @pageStart, @pageSize;",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+        "pagination": {
+            "size": "pageSize",
+            "count": "totalCount",
+            "page": "pageIndex"
+        },
+        "defaults": {
+            "orderBy": "id DESC"
+        },
+        "replace": [ "orderBy" ]
+    }
+}
+```
+返回结果请参考[`隐式对象代码`](#隐式对象代码)
+
+> 说明:
+> 1. `name`: 必须全局唯一,即整个应用程序级别的唯一性,否则应用程序启动加载时会抛异常。
+> 2. `code`为要执行的SQL语句,参数通过`@parameterName`的形式声名。
+> 3. 对于可选参数,将条件放入`[]`中,SQL被执行前,如果未指定该参数,则该条件将被忽略。
+> 4. `"resultSet": "M"`: 此CSI返回结果集是一个集合。
+> 5. `"queryOnly": true`: 该CSI将通过只读连接执行。
+> 6. `"requiredTransaction": false`: 该CSI不启用事务。
+> 7. 该CSI将通过`pagination` Middleware来作分页操作,具体请参考`Middleware`部分的说明。这里的`pageIndex`与`pageSize`参数就是为了使用该Middleware。
+> 8. 该CSI默认以`id DESC`排序,调用者也可以指定排序方式。
+> 9. 因`orderBy`为特殊定义,Dapper并不能像SQL参数一样处理,所以需要通过`replace`作执行前处理。
+> 10. 对于所有参数都是可选参数的情况,`where`后面需要加一个 ` 1=1 ` 的恒成立条件。
+
+
+### 支持代码方式说明
+1. 单次执行代码,即:`code`为字符串类型的SQL语句。如:
+```json
+{
+	"name": "GetApi",
+	"code": "SELECT * FROM api WHERE id = @id;",
+	"resultSet": "S",
+	"queryOnly": true,
+	"requiredTransaction": false
+}
+```
+通过`DbRESTFul`标准化输入的结果为:
+```json
+{
+	"statusCode": "200",
+	"errorMessage": "",
+	"data": {
+	    "id": 23,
+		"name": "GetUserInfo",
+		……
+	}
+}
+```
+
+2. 对象型代码,即:`code`为`object`对象,即,`key`: `sql`。,且。如:`code`的定义为:
+```json
+{
+	"name": "CreateCart",
+	"code": {
+        "user": "SELECT name, phone FROM user WHERE userId = @userId",
+        "order": "UPDATE order SET status = @status WHERE orderId = @orderId;SELECT @orderId AS orderId",
+        "cart": "INSERT INTO cart(sn, ammount, userId) VALUES(@sn, @ammount, @userId);SELECT LAST_INSERT_ID() AS cartId;"
+    },
+	"resultSet": "S,S,S",
+	"queryOnly": false,
+	"requiredTransaction": true
+}
+```
+通过`DbRESTFul`标准化输入的结果可能是:
+```json
+{
+	"statusCode": "200",
+	"errorMessage": "",
+	"data": {
+	    "user": { "name": "dawson", "phone": 13545245245 },
+		"order": { "orderId": 23 },
+		"cart": { "cartId": 324 }
+	}
+}
+```
+> 说明:
+> 1. 返回结果有多个时,通过`"resultSet": "S,S,S"`指定集合类型。
+> 2. 执行的结果对象与`code`定义一一对应。
+> 3. 如果`"requiredTransaction": true`的话,整个`code`对象将被放在一个`trancaction`内执行。
+
+3. 隐式对象代码<a name="隐式对象代码"></a>,如上面[`示例说明`](#示例说明)的例子,`code`虽然是一个字符串,但因为使用了`pagination` Middleware,实际上也会以对象的方式执行。其它返回结果如下:
+```json
+{
+	"statusCode": "200",
+	"errorMessage": "",
+	"data": {
+	    "items": [
+		    { "id": 32, "name": "GetUserInfo", …… },
+		    { "id": 33, "name": "UpdateUserInfo", …… },
+		    { "id": 34, "name": "DeleteUserInfo", …… },
+	    ],
+		"pagination": {
+		    "pageIndex": 2,
+			"pageSize": 10,
+			"totalCount": 69
+		}
+	}
+}
+```
+

+ 3 - 0
spec/https.md

@@ -0,0 +1,3 @@
+# https
+
+- http://www.asp.net/web-api/overview/security/working-with-ssl-in-web-api

+ 45 - 0
spec/index.md

@@ -0,0 +1,45 @@
+# DbRESTFul 功能简介
+
+`DbRESTFul`支持从关系数据库暴露成RESTFul API服务。Web前端,或者App端可以直接通过`http`或者`https`请求来访问,为快速开发单页面(SAP)网站和移动应用(Android、iOS及Windows  )提供高效的后端服务框架。它采用ASP.NET Web API实现,数据库访问层采用Dapper。
+
+- 目前只支持MySQL数据库,因为数据库的访问层基于Dapper的实现,理论上可以支持其它更多的主流数据库,如:SQL Server, Oracle, DB2等。
+- 它支持标准常用的单表或者视图的`CURD`操作。
+- 支持排序,分页,包含与排除字段查询。
+- 支持对存储过程的调用。
+- 复杂逻辑的SQL调用,可以通过后台SQL配置文件的方式实现。
+- 支持后端的数据验证,以保证数据的合法性。
+- 支持数据字段级别的数据访问权限控制。
+- 支持读写分离,以保证数据库访问的高效性。
+
+`DbRESTFul`是一个独立的ASP.NET MVC项目,可以直接部署在IIS上,并通过:`http://localhost:<port>/api/`来向外提供服务。
+
+
+# 目录
+
+* [`query`](query.html) - 单表或者单视图`CURD`操作
+* [`procedure`](procedure.html) - 存储过程调用
+* [`invoke`](invoke.html) - 配置过程调用
+* [`permission`](permission.html) - 访问权限控制
+* [`rw-control`](rw-control.html) - 读写分离
+* [`http/https`](https.html) - `SSL`支持
+* [`dbrestful.js`](dbrestful.js.html) - `SSL`支持
+
+# TODO
+
+ - 将所有表及其字段先读出来,用于异常处理(TableExsit),和Include与Exclude支持。
+ - 分析`parameters`的结构
+ - 测试驱动
+ - 支持访问控制
+ - 支持批量操作
+ - 支持更多数据库
+ - 支持存储过程
+ - 支持事务
+ - 优化api的url,`http://host<:port>/api/<tableName>/<parameter>`
+ - 优化,StringBuilder.Append
+ - 开放源码
+ - 字段类型判断(如string与datetime)
+ - 字符串特殊字符转义
+ - 查询都应该仅包含部分字段
+
+# 版本
+`0.0.1`

+ 157 - 0
spec/invoke.md

@@ -0,0 +1,157 @@
+配置SQL代码调用(CSI)
+===================
+
+--------------------------------------
+配置SQL代码调用(Configured SQL Invocation),即client端通过`http/https`直接调用后端配置好的`SQL`。该功能适用于复杂的业务场景,开发人员可以将复杂的`SQL`写到配置文件中,后台通过传进来的名称与参数调用。
+
+* * *
+[TOC]
+* * *
+
+## 配置SQL代码文件
+配置SQL代码调用的代码文件以`json`格式存储,通常,该配置代码文件存放在服务项目的CSI目录,也可以通过该项目的根目录下的`config.json`文件中指定。
+
+```json
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <appSettings>
+      <add key="CCIPath" value="~/cci" />
+    </appSettings>
+</configuration>
+```
+
+
+## CCI JSON配置
+在code配置中,使用`[]`来设置是否是可选SQL段,`[]`中必须包含`@Parameter`。在前端调用时,如果未指定某参数,则后端将忽略此字段判断。
+如果`where`后面只有一个可选条件,则需要加一个 `1=1` 条件。
+```json
+[
+    {
+        name: "query_user_info",
+        code: "SELECT * FROM user WHERE id > @userId [and id < @minUserId]",
+        resultSet: 'M',
+        requiredTransaction: true,
+        queryOnly: true,
+        validator: {'userId': ['required', 'int']}
+    },
+    {
+        name: "search_user_info",
+        code: {
+        	items: 'SELECT * FROM user WHERE name LIKE CONCAT("%" + @keyword + "%") LIMIT @pageSize, @pageStart',
+            pagination: 'SELECT count(*) as count, @pageSize as size, @pageIndex as index FROM user WHERE name LIKE CONCAT("%" + @keyword + "%")'
+        },
+        resultSet: "M,S",
+        requiredTransaction: true,
+        queryOnly: true,
+        validator: {
+        	'pageSize': ['required', 'int'],
+        	'pageIndex': ['required', 'int']
+        }
+    }
+]
+```
+## **说明:**
+> **支持代码方式**
+> 1. 单次执行代码,即:`code`为字符串类型的SQL语句。
+> 2. 对象型代码,即:`code`为`json`对象类型,即,`key`: `sql`。整个`code`对象将被放在一个`trancaction`内执行(如果指定了的话),且执行的结果将以对象的方式返回。如:`code`的定义为:
+> ```json
+> {
+>     users: 'SELECT name, phone FROM user WHERE score > @score',
+>     order: 'SELECT sn, ammount, userId FROM order WHERE orderId = @orderId',
+>     pagination: 'SELECT count(*) as count, @pageSize as size, @pageIndex as index FROM user WHERE score = @score'
+> }
+> ```
+> 返回值可能是:
+> ```json
+> {
+>     users: [{ name: 'dawson', phone: 13545245245}, ...],
+>     order: [{ sn: 'wa039234913', amount: 231.23, userId: 23}],
+>     pagination: [{ count: 34, size: 3, index: 2}]
+> }
+> ```
+> 3. 通过`resultSet`,指定执行结果是单条(`S`)还多条(`M`)记录。如:上面示例中,如果指定`resultSet: "M,S,S"`,则返回值为:
+> ```json
+> {
+>     users: [{ name: 'dawson', phone: 13545245245}, ...],
+>     order: { sn: 'wa039234913', amount: 231.23, userId: 23},
+>     pagination: { count: 34, size: 3, index: 2}
+> }
+> ```
+
+## 配置代码调用
+如:前端需要查询某一段时间内用户的姓名,电话,下单号与订单金额信息,可以通过下面的配置文件(内容)来调用:
+
+```json
+[{
+	'name': 'query_user_order_info',
+    'code': 'SELECT Users.UserName, Users.Phone, Orders.OrderNo, Orders.Amount
+             FROM Users
+             INNER JOIN Orders
+             ON Users.Id = Orders.UserId
+             ORDER BY Orders.CreatedDate
+             WHERE Orders.CreatedDate >= @startDate AND Orders.CreatedDate <= @endDate',
+    'requiredTransaction': true,
+	'queryOnly': true,
+     'validator': {
+     	'startDate': ['datetime', 'required'],
+     	'endDate': ['datetime', 'required']
+     }
+},{
+	'name': '……',
+    'code': '……',
+    'requiredTransaction': true,
+	'queryOnly': true,
+    'validator': {}
+}, ……]
+```
+
+**说明:**
+> 1. `name`: 在一个或者多个cci的`json`文件下,名称必须是唯一的,否则会出现异常
+> 2. `requiredTransaction`: 支持事务
+> 3. `queryOnly`: 支持读写分离,`true`,且配置好了读写分离连接,系统将自动通过只读连接处理查询
+> 4. `validator`: 支持数据验证,详情参考[数据验证](#validator)
+
+通过DbRESTFul调用该存储过程的方式为:
+* **请求URL:** `https://localhost:<port>/api/<version>/invoke`
+* **请求类型:** `post`
+* **请求参数:**
+```json
+{
+    'name': 'query_user_order_info',
+    'parameters': {
+        'startDate': '2015-02-15',
+        'endDate': '2015-04-15'
+    }
+}
+```
+* **请求返回数据:** `json`格式
+```json
+{
+    "hasError": false,    // 如果是true,说明操作有错误
+    "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+    "data": {}            // 调用sql的返回的数据
+}
+```
+
+
+## 数据验证 <a name='validator'></a>
+配置代码的调用支持数据的验证,通过`validator`来指定数据的验证规则,目前支持的规则如下:
+   规则名称   |    规则说明
+-------------|-----------------------
+required     |不能为空
+datetime     |类型为日期类型
+email        |邮件类型
+int          |int类型
+decimal      |数字类型(decimal, int, float, double)
+bool         |布尔类型(true, false)
+regex:\w     |正则表达式,`:`后接表达式
+maxLength:10 |最大长度为10
+minLength:5  |最小长度为5
+range:3,14   |数字区间,3到14之间
+in:a,b,23    |其中的值,'a', 'b'或'23'
+min:30       |输入值不能小于30
+max:50       |输入值不能大于50
+
+
+## TODOs
+- 考虑是否支持`json`格式的配置代码调用,即:后台的`query.dm`,另外调用。

+ 94 - 0
spec/middleware.md

@@ -0,0 +1,94 @@
+配置SQL功能中间件(Middleware)
+===================
+
+
+
+--------------------------------------
+配置SQL功能中间件(Middleware),即client端通过`http/https`直接调用后端配置好的`CSI`时,需要的辅助功能中间件。该功能适用于复杂的业务场景,开发人员无法通过复杂的`SQL`配置文件去实现时,后台通过传进来的名称与参数,结合预定义的功能组件,实现复杂的业务功能。Middleware是CSI配置的核心功能之一。
+
+* * *
+[TOC]
+* * *
+
+
+
+
+## 配置中间件
+中间件是CSI的辅助功能,通常,该配置存放在CSI的“middleWares”下,是以object形式的json对象。
+
+```json
+{
+   ......
+   "middleWares": {
+      "pagination": {
+          "size": "pageSize",
+          "count": "totalCount",
+          "page": "pageIndex"
+      }
+   }
+   ......
+}
+```
+
+## Middleware JSON配置说明
+### Middleware Schema
+Middleware的详细定义,请参考`Wicture.DbRESTFul`项目中`Schema`目录下的`middleware-schema.json`文件。它的主要结构如下:
+```json
+{
+    ......
+   "middleWares": {
+      "pagination": {
+          "size": "pageSize",
+          "count": "totalCount",
+          "page": "pageIndex"
+      },
+      "defaults": {
+          "orderBy": "id DESC"
+      },
+      "replace": [ "orderBy" ]
+   }
+   ......
+}
+```
+
+### 示例说明
+我们这里使用CSI中的查询示例。
+调用参数包括:
+
+|   name   |   type   | required |      note
+|----------|----------|----------|----------------
+|projectId | int      |    yes   | The project Id
+|module    | string   |    no    | The module name
+|keyword   | string   |    no    | The keyword for searching (name, module, owner, title)
+|orderBy   | string   |    no    | Sorting
+|pageIndex | int      |    yes   | pageIndex for pagination
+|pageSize  | int      |    yes   | pageSize for pagination
+
+对应的CSI可能是这样定义的:
+```json
+{
+    "name": "list_api_for_project",
+    "code": "SELECT * FROM api WHERE projectId = @projectId [AND `module`=@module] [AND (`name` LIKE CONCAT('%',@keyword,'%') OR `module` LIKE CONCAT('%',@keyword,'%') OR `owner` LIKE CONCAT('%',@keyword,'%') OR `title` LIKE CONCAT('%',@keyword,'%') )] @orderBy LIMIT @pageStart, @pageSize;",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+        "pagination": {
+            "size": "pageSize",
+            "count": "totalCount",
+            "page": "pageIndex"
+        },
+        "defaults": {
+            "orderBy": "id DESC"
+        },
+        "replace": [ "orderBy" ]
+    }
+}
+```
+
+> 说明:
+> 1. `Middleware`为CSI配置中需要的所有中间件,通过预定义的参数名称调用。
+> 2. 该CSI将通过`pagination` 中间件来作分页操作。这里的`size`、`count`和`page`三个参数就是为Middleware预定义的配置,分别代表页数、数据总条数、页码,`pageSize`、`totalCount`和`pageIndex`三个`“value”`是要返回的结果中对应的参数名称。
+> 3. `defaults`是默认配置的排序功能,该CSI默认以`id DESC`排序,调用者也可以通过传递`@orderBy`参数指定排序方式。
+> 4. 因`orderBy`为特殊定义,Dapper并不能像SQL参数一样处理,所以需要通过调用`replace`作执行前处理。
+

+ 132 - 0
spec/permission.md

@@ -0,0 +1,132 @@
+# 权限控制
+DbRESTFul中的API支持权限控制,其权限判断基于用户角色对指定表的`查询/插入/更新/删除`操作授权。
+
+##权限相关基础表
+1. 用户表(User)
+ 字段称  |  类型  | 是否为空 |    说明
+--------| ------| --------| ---------
+id		| int	| 否  	 | 用户的标识
+roleId  | int	| 是  	 | 用户所属角色
+……		| ……	| ……      | 其它字段
+
+2. 角色表(Role)
+ 字段称  |      类型    	| 是否为空 |    说明
+--------| ------------- | --------| ---------
+id		| int			| 否  	 | 角色的标识
+name  	| varchar(31)	| 否  	 | 角色名称
+……		| ……			| ……      | 其它字段
+
+3. 权限表(ApiPermission)
+     字段称	  |      类型    	 | 是否为空 |    说明
+------------	| ------------- |:-------:| ---------
+id				| int			| 否  	 | 权限的标识
+name			| varcahr		| 否  	 | 权限的名称
+roleId			| int			| 否  	 | 权限的标识
+type  			| smallint		| 否  	 | 权限操作类型(0:table or view, 1:store procedure, 2:configured code invocation)
+object  		| varchar(31)	| 否  	 | 待操作对象(table, view, sp, cci)
+flag			| int		    | 是      | 允许操作的二进制值(请参考说明)
+
+**说明**
+> `flag`为权限操作标识,判断的时候,通过`&`即可,如:`01010`表示`可插入`与`可删除`
+> - `00001`: `selectable`
+> - `00010`: `insertable`
+> - `00100`: `updatable`
+> - `01000`: `deletable`
+> - `10000`: `executable`
+> `type`:
+> - TableOrView = 0
+> - ConfiguredCodeInvocation = 1
+> - StoreProcedure = 2
+
+
+##权限判断
+当用户非法访问时,返回信息如下:
+```JSON
+{
+    "hasError": true,
+    "errorMessage": "此操作无权限!",
+    "data": null
+}
+```
+
+##权限编辑
+
+1. 获取角色
+    * **请求URL:** `https://localhost:<port>/api/<version>/permission`
+    * **请求类型:** `get`
+    * **请求参数:**
+    ```json
+    {
+        'id': 21 			// 用户的Id,可为不指定,如果指定,则加载用户所属角色,如果不指定,则加载所有的角色。
+    }
+    ```
+    * **请求返回数据:** `json`格式
+    ```json
+    {
+        "hasError": false,    // 如果是true,说明操作有错误
+        "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+        "data": [{}]          // Role 中的角色,如果没指定用户的Id,则所有用户角色都加载进来
+    }
+    ```
+
+2. 创建角色
+    * **请求URL:** `https://localhost:<port>/api/<version>/permission`
+    * **请求类型:** `post`
+    * **请求参数:**
+    ```json
+    {
+        'name': '超级管理员'    // 角色名称
+    }
+    ```
+    * **请求返回数据:** `json`格式
+    ```json
+    {
+        "hasError": false,    // 如果是true,说明操作有错误
+        "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+        "data": 1             // 执行影响行数
+    }
+    ```
+
+3. 设置用户的角色
+    * **请求URL:** `https://localhost:<port>/api/<version>/permission`
+    * **请求类型:** `put`
+    * **请求参数:**
+    ```json
+    {
+        'id': 21,			// 用户的Id
+        'roleId':2			// 角色
+    }
+    ```
+    * **请求返回数据:** `json`格式
+    ```json
+    {
+        "hasError": false,    // 如果是true,说明操作有错误
+        "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+        "data": 1             // 执行影响行数
+    }
+    ```
+
+4. 给角色授权
+    * **请求URL:** `https://localhost:<port>/api/<version>/permission`
+    * **请求类型:** `patch`
+    * **请求参数:**
+    ```json
+    {
+        'roleId': 21,			// 角色Id
+        'tableName':'order', 	// 表名
+        'selectEnabled': true,	// 是否能查询
+        'insertEnabled': true,	// 是否能插入
+        'updateEnabled': true,	// 是否能更新
+        'deleteEnabled': true 	// 是否能删除
+    }
+    ```
+    * **请求返回数据:** `json`格式
+    ```json
+    {
+        "hasError": false,    // 如果是true,说明操作有错误
+        "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+        "data": 1             // 执行影响行数
+    }
+    ```
+    * **说明:**
+    > 这个操作总是拿`roleId`与`tableName`去查询,如果已有记录,则更新`selectEnabled`等4个基本操作的权限,否则,创建一条新的授权记录。

+ 39 - 0
spec/procedure.md

@@ -0,0 +1,39 @@
+# 存储过程调用
+
+--------------------------------------
+
+
+[TOC]
+
+
+## 存储过程调用(PROCEDURE)
+如:数据库中有查询`user`分数大于指定参数的用户的存储过程:
+
+```sql
+CREATE PROCEDURE sp_queryUserWithScore
+(IN score INT)
+BEGIN
+  SELECT * FROM `user` WHERE `score` = score;
+END
+```
+
+通过DbRESTFul调用该存储过程的方式为:
+* **请求URL:** `https://localhost:<port>/api/<version>/procedure`
+* **请求类型:** `post`
+* **请求参数:**
+```json
+{
+    'name': 'sp_queryUserWithScore',
+    'parameters': {
+        'score': 80
+    }
+}
+```
+* **请求返回数据:** `json`格式
+```json
+{
+    "hasError": false,    // 如果是true,说明操作有错误
+    "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+    "data": {}            // 存储过程返回的数据
+}
+```

+ 388 - 0
spec/query.md

@@ -0,0 +1,388 @@
+# Query Api - 单表或者单视图`CURD`操作
+
+* * *
+
+[TOC]
+
+* * *
+
+<a name='select'></a>
+## 查询操作(SELECT)
+
+### 1. **简单查询**
+简单查询是指对一下数据库的表或者视图进行简单的查询操作。如:查询`user`表的所有数据:
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `GET`
+* **请求参数: ** 无
+* **表单参数: ** 无
+* **请求返回数据:** `json`格式的用户列表数据 <a name="usersResult"></a>
+```json
+{
+    "hasError": false,    // 如果是true,说明操作有错误
+    "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+    "data": [{            // 用户数组
+        "id": 1,
+        "name": "Dawson",
+        ...
+    }, ...]
+}
+```
+
+
+<a name="where"></a>
+### 2. **条件查询(Where)**
+如:查询`user`表中,`id > 5`,且`score <= 60`的用户:
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `GET`
+* **请求参数:** `parameters`为[`WHERE条件查询`](#whereCondition)结构的字符串:
+```json
+{
+    '$where': {
+        '$and': [{
+            '$gt': {
+                'id':5
+            }
+        }, {
+            '$lte': {
+                'score':60
+            }
+        }]
+    }
+}
+```
+* **表单参数: ** 无
+* **请求返回数据:** `json`格式的[用户列表数据](#usersResult)
+* **说明:**
+> 请求参数`parameters`为`json`形式的字符串,即通过`JSON.stringify(parameters)`来将对象转成字符串
+> 对于所有查询操作,都只描述`parameters`参数。
+
+
+<a name="filters"></a>
+### 3. **字段筛选(Filters)**
+如:查询`user`表的用户姓名`name`表示成`DisplayName`,年龄`age`与分数`score`字段:
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `GET`
+* **请求参数:** `parameters`为:
+```json
+{
+    '$where': {},                // 查询条件
+    "$includes": [{
+            "$name": "name",
+            "$alias": "DisplayName"
+        }, {
+            "$name": "age"       // 如果只指定`$name`,可以简写为字段名称,即:"age"
+        }
+        "score"
+    ]
+}
+```
+* **表单参数: ** 无
+* **请求返回数据:** `json`格式的[用户列表数据](#usersResul)
+* **说明:**
+> 1. 如果未指定`$includes`和`$excludes`,则返回全部字段
+> 2. 如果只想排除某些字段,可以通过`$excludes`来设置,如:`$excludes:['name', 'score']`
+> 3. `$excludes`后面的数组只能是字段名称
+> 4. `$includes`,后面是一个包括[字段对象](#fieldObject)的对象,或者如果只指定`$name`,可以简写为该字段名称字符串
+> 5. 字段筛选可与[`条件查询`](#whereCondition)一起使用
+
+
+### 4. **分页支持(Pagination)**
+如:查询所有用户,获取每页为10条记录,第3页的数据。
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `GET`
+* **请求参数:** `parameters`为:
+```json
+{
+    '$where': {},        // 查询条件
+    '$includes': [],     // 字段筛选
+    '$page': [10,3]      // 表示:`pageSize:10`, `pageIndex:3`
+}
+```
+* **表单参数: ** 无
+* **请求返回数据:** `json`格式数据的
+```json
+{
+    "hasError": false,     // 如果是true,说明操作有错误
+    "errorMessage": null,  // 如果hasError为true,则errorMessage为错误信息
+    "data": {
+        "items": [{        // 用户数组
+            "id": 1,
+            "name": "Dawson",
+            ...
+        }, ...],
+        "pagination": {
+        	"size": 10,	  // 每页10条记录
+            "index": 3,	  // 当前第3页
+            "count": 45	  // 记录总数
+        }
+    }
+}
+```
+
+### 5. **排序支持(OrderBy)**
+如:查询用户,按生日`birthday`升序排列,然后按分数`score`降序排列。
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `GET`
+* **请求参数:** `parameters`为:
+```json
+{
+    '$where': {}, 	  // 查询条件
+    "$order": [
+    	"birthday",
+    	{
+            "$name": "score",
+            "$desc": true
+        }
+    ]
+}
+```
+* **表单参数: ** 无
+* **请求返回数据:** 按要求排好序的`json`格式的[用户列表数据](#usersResult)
+* **说明:**
+>  默认是按升序进行排列,如`birthday`,并可写成字符串形式
+> `$order`数组内的设置会以[字段对象](#fieldObject)顺序应用,即,先`order by birthday asc, score desc`
+
+
+### 6. **分组支持(GroupBy)**
+如:查询用户,按年龄`age`和分数`score`分组。
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `GET`
+* **请求参数:** `parameters`为:
+```json
+{
+    '$where': {}, 	  // 查询条件
+    "$groupby": ['age']
+}
+```
+* **表单参数: ** 无
+* **请求返回数据:** 按要求分组的`json`格式数据
+```json
+{
+    "hasError": false,     // 如果是true,说明操作有错误
+    "errorMessage": null,  // 如果hasError为true,则errorMessage为错误信息
+    "data": [{
+            "age": 12,
+            "count": 5,
+            ...
+        }, ...]
+    }
+}
+```
+* **说明:**
+> `$groupby`可以通过`$includes`[字段筛选](#filters)中的[字段对象](#fieldObject)指定`$aggregation`[聚合指令](#aggregation)配合使用,以计算分组结果。
+
+
+### 7. **Having支持(Having)**
+如:查询学生分数表,所有考试的分数平均大于70的学生。
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `GET`
+* **请求参数:** `parameters`为:
+```json
+{
+    '$where': {}, 	  // 查询条件
+    "$groupby": 'name',
+    "$having": {
+        "$eq": {
+            "$key": {
+                "$name": "score",
+                "$aggregation": "$avg"
+            },
+            "$value": 70
+        }
+    }
+}
+```
+* **表单参数: ** 无
+* **请求返回数据:** 按要求分组的`json`格式数据
+* **说明:**
+> `$having`必须同时与`$aggregation`[聚合指令](#aggregation)配合使用。
+
+
+<a name='fieldObject'></a>
+## 字段对象
+`字段对象`表示某一个字段(此字段可能只有名称,或者还包含聚合函数、正序或倒序排序、别名),所有能包含的属性如下:
+```json
+{
+    "$name": "fieldName",		// 要查询的字段名,如果是复杂字段形式,此属性必须指定
+    "$distinct": true,		   // 是否要加 distinct 关键字
+    "$aggregation": "$avg",	  // 聚合操作
+    "$alias": "aliasName",		// 别名
+    "$desc": true				// 是否倒序
+}
+```
+
+**说明:**
+> 1. `$distinct` 只需指定一次
+> 2. `$aggregation` 为聚合操作,请参考[聚合指令](#aggregation)
+> 3. 如果只想查询指定的字段名称,可以直接使用该字段名称作为简写的字段对象形式
+> 4. 放在`$excludes`指令的字段对象,只能是其简写形式,即该字段的名称。
+> 5. `$alias` 只能使用在`$includes`中
+> 6. `$desc` 只能使用在`$orderby`中
+
+
+<a name="aggregation"></a>
+### 聚合指令
+聚合指令是指查询操作中,调用一些简单的数据库聚合函数,完全其对应的聚合操作。目前DbRESTFul支持的聚合指令有:
+
+  |指令名称 | 说明
+  |--------| -------
+  |$avg    | 求平均值
+  |$max    | 求最大值
+  |$min    | 求最小值
+  |$count  | 求记录条数
+  |$sum    | 求和
+
+
+<a name='whereCondition'></a>
+## `WHERE`条件查询结构
+`WHERE`条件查询通过`$where`关键属性,通过`$and`与`$or`组合嵌套[基本条件指令](#conditions)(如:`$eq`, `$gt`, `$in`),来构建查询对象,然后通过转成`json`字符串,传入后端调用。其实基本结构如下:
+```json
+{
+    '$where': {
+    	'$or': [
+            {
+            	'$and': [
+                    {'$gt': { 'id':5 }},		   // (id > 5 and
+                    {'$lte': { 'score':60}}		// score <= 60)
+            	]
+            },
+        	{'$lk': {'name': 'Daw'}}	 // or (name like '%Daw%')
+        ]
+    }
+}
+```
+
+
+<a name='conditions'></a>
+### 基本条件指令(必须用在where或having中)
+基本条件指令通过下面的结构来描述:
+```json
+{
+    '$eq': {
+        '$key': {
+            '$name': 'id',
+            "$aggregation": "$avg"
+        },
+        '$value': {'$name': 'parentId' }
+    }
+}
+```
+
+**说明:**
+> 1. `$value`可以是字段名,也可以直接指定值,如果是字段名,通过`{'$name': 'fieldName' }`来指定
+> 2. 如果`$value`为值,则后面直接指定该值,如:`'$value': 5` 或 `'$value': 'shanghai'`,如果是时间字段可以指定为时间字符串('yyyy-MM-dd HH:mm:ss'/'yyyy-MM-dd')
+> 3. 如果是简单的条件比较,`$key`与`$value`都可以缺省,即简化为:`{ '$eq': {'id': 5 } }`
+> 4. `$eq`为条件比较指令,下面是所有支持的条件比较指令说明:
+| 指令名称 | 			示例	 		| 				说明				|
+|----------|----------------------------|-----------------------------------|
+|$eq       |`{'$eq':{'id':5}}`   	    |表示:id = 5						|
+|$neq      |`{'$neq':{'id':5}}`  	    |表示:id <> 5						|
+|$gt       |`{'$gt':{'id':5}}`   	    |表示:id > 5						|
+|$gte      |`{'$gte':{'id':5}}`  	    |表示:id >= 5						|
+|$lt       |`{'$lt':{'id':5}}`   	    |表示:id < 5						|
+|$lte      |`{'$lte':{'id':5}}`  	    |表示:id <= 5						|
+|$in       |`{'$in':{'id':[4,5,15]}`    |表示:id in [4,5,15]				|
+|$nin      |`{'$nin':{'id':[4,5,15]}`   |表示:id not in [4,5,15]			|
+|$btn      |`{'$btn':{'id':[4,15]}` 	|表示:id between 4 and 15			|
+|$nbtn     |`{'$nbtn':{'id':[4,15]}`	|表示:id not between 4 and 15		|
+|$lk       |`{'$lk':{'name':'Daw'}`	    |表示:id like '%Daw%'				|
+|$lkl      |`{'$lkl':{'name':'Daw'}`	|表示:id like 'Daw%'				|
+|$lkr      |`{'$lkr':{'name':'Daw'}`	|表示:id like '%Daw'				|
+|$nlk      |`{'$nlk':{'name':'Daw'}`	|表示:id not like '%Daw%'			|
+|$nlkl     |`{'$nlkl':{'name':'Daw'}`   |表示:id not like 'Daw%'			|
+|$nlkr     |`{'$nlkr':{'name':'Daw'}`   |表示:id not like '%Daw'			|
+
+
+### 组合条件:
+条件组合通过`$and`可以将多个基本条件以`‘且’`的方式组合,通过`$or`来将多个基本条件以`‘或’`的方式组合,且`$and`和`$or`可以嵌套使用,如:
+```json
+{
+    '$and':[
+        {'$gt':{'id':5}},
+        {'$or': [
+            {'$lte':{'score': 60}},
+            {'$lk':{'name': 'Liu'}},
+        ]}
+    ]
+}
+```
+
+
+<a name='insert'></a>
+## 插入操作(Insert)
+可以插入多条数据,如:向`user`表插入一条数据
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `POST`
+* **请求参数:** 无
+* **表单参数: **
+```json
+{
+    data: [
+        { name: 'Dawson', age: 32, score: 89 },
+        { name: 'Ricky', age: 1, score: 12 }
+    ]
+}
+```
+* **请求返回数据:**  `json`格式
+```json
+{
+    hasError: false,      // 如果是true,说明操作有错误
+    errorMessage: null,   // 如果hasError为true,通常errorMessage为错误信息
+    data:null             // 暂无数据返回
+}
+```
+
+<a name='update'></a>
+## 更新操作(Update)
+如:更新`user`表中,id为3的数据
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `PUT`
+* **请求参数:** 无
+* **表单参数: **
+```json
+{
+    parameters: {
+        '$where': { '$eq': { 'id':3 }}
+    },
+    data: {
+        name: 'Dawson',
+        age: 12,
+        score: 89
+    }
+}
+```
+* **请求返回数据:** `json`格式
+```json
+{
+    hasError: false, 	// 如果是true,说明操作有错误
+    errorMessage: null, // 如果hasError为true,通常errorMessage为错误信息
+    data: 12 			// 执行更新操作影响的行数
+}
+```
+* **说明:**
+> `parameters`参数,请参考[查询操作](#where),如果不指定,或者查询结果是多条记录,则对每条记录作同样的更新。
+
+
+<a name='delete'></a>
+## 删除操作(Delete)
+如:删除`user`表中,`birthday < '2001-12-31'`的记录
+* **请求URL:** `https://localhost:<port>/dbrestful/api/query/user`
+* **请求类型:** `DELETE`
+* **请求参数:** `parameters`为:
+```json
+{
+    '$where': { '$lt': { 'birthday':'2001-12-31' }}
+}
+```
+* **请求返回数据:** `json`格式
+```json
+{
+    hasError: false, 	// 如果是true,说明操作有错误
+    errorMessage: null,  // 如果hasError为true,通常errorMessage为错误信息
+    data: 12 			// 执行更新操作影响的行数
+}
+```
+* **说明:**
+> `parameters`参数,请参考[查询操作](#where),如果不指定,或者查询结果是多条记录,则对每条记录作同样的更新。
+
+

+ 215 - 0
spec/repository.md

@@ -0,0 +1,215 @@
+配置SQL代码调用(CSI)
+===================
+
+--------------------------------------
+配置SQL代码调用(Configured SQL Invocation),即client端通过`http/https`直接调用后端配置好的`SQL`。该功能适用于复杂的业务场景,开发人员可以将复杂的`SQL`写到配置文件中,后台通过传进来的名称与参数调用。CSI是DbRESTFul的核心功能之一。
+
+* * *
+[TOC]
+* * *
+
+## 配置SQL代码文件
+配置SQL代码调用的代码文件以`json`格式存储,通常,该配置代码文件存放在服务项目的CSI目录,也可以通过该项目的根目录下的[`config.json`](config.md)文件中指定。
+
+```json
+{
+  ……
+  "CSIPath": "CSI/"
+  ……
+}
+```
+
+
+## CSI JSON配置说明
+### CSI Schema
+CSI的详细定义,请参考`Wicture.DbRESTFul`项目中`Schema`目录下的`csi-schema.json`文件。它的主要结构如下:
+```json
+{
+    "name": "",
+    "code": "",
+    "resultSet": "",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+}
+```
+
+### 示例说明
+假设我们需要查询指定项目下的所有接口定义。
+调用参数包括:
+
+|   name   |   type   | required |      note
+|----------|----------|----------|----------------
+|projectId | int      |    yes   | The project Id
+|module    | string   |    no    | The module name
+|keyword   | string   |    no    | The keyword for searching (name, module, owner, title)
+|orderBy   | string   |    no    | Sorting
+|pageIndex | int      |    yes   | pageIndex for pagination
+|pageSize  | int      |    yes   | pageSize for pagination
+
+对应的CSI可能是这样定义的:
+```json
+{
+    "name": "list_api_for_project",
+    "code": "SELECT * FROM api WHERE projectId = @projectId [AND `module`=@module] [AND (`name` LIKE CONCAT('%',@keyword,'%') OR `module` LIKE CONCAT('%',@keyword,'%') OR `owner` LIKE CONCAT('%',@keyword,'%') OR `title` LIKE CONCAT('%',@keyword,'%') )] @orderBy LIMIT @pageStart, @pageSize;",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+        "pagination": {
+            "size": "pageSize",
+            "count": "totalCount",
+            "page": "pageIndex"
+        },
+        "defaults": {
+            "orderBy": "id DESC"
+        },
+        "replace": [ "orderBy" ]
+    }
+}
+```
+
+> 说明:
+> 1. `code`为要执行的SQL语句,参数通过`@parameterName`的形式声名。
+> 2. 对于可选参数,将条件放入`[]`中,SQL被执行前,如果未指定该参数,则该条件将被忽略。
+> 3. `"resultSet": "M"`: 此CSI返回结果集是一个集合。
+> 4. `"queryOnly": true`: 该CSI将通过只读连接执行。
+> 5. `"requiredTransaction": false`: 该CSI不启用事务。
+> 6. 该CSI将通过`pagination` Middleware来作分页操作,具体请参考`Middleware`部分的说明。这里的`pageIndex`与`pageSize`参数就是为了使用该Middleware。
+> 7. 该CSI默认以`id DESC`排序,调用者也可以指定排序方式。
+> 8. 因`orderBy`为特殊定义,Dapper并不能像SQL参数一样处理,所以需要通过`replace`作执行前处理。
+
+
+在code配置中,
+如果`where`后面只有一个可选条件,则需要加一个 `1=1` 条件。
+```json
+[
+    {
+        name: "query_user_info",
+        code: "SELECT * FROM user WHERE id > @userId [and id < @minUserId]",
+        resultSet: 'M',
+        requiredTransaction: true,
+        queryOnly: true,
+        validator: {'userId': ['required', 'int']}
+    },
+    {
+        name: "search_user_info",
+        code: {
+        	items: 'SELECT * FROM user WHERE name LIKE CONCAT("%" + @keyword + "%") LIMIT @pageSize, @pageStart',
+            pagination: 'SELECT count(*) as count, @pageSize as size, @pageIndex as index FROM user WHERE name LIKE CONCAT("%" + @keyword + "%")'
+        },
+        resultSet: "M,S",
+        requiredTransaction: true,
+        queryOnly: true,
+        validator: {
+        	'pageSize': ['required', 'int'],
+        	'pageIndex': ['required', 'int']
+        }
+    }
+]
+```
+## **说明:**
+> **支持代码方式**
+> 1. 单次执行代码,即:`code`为字符串类型的SQL语句。
+> 2. 对象型代码,即:`code`为`json`对象类型,即,`key`: `sql`。整个`code`对象将被放在一个`trancaction`内执行(如果指定了的话),且执行的结果将以对象的方式返回。如:`code`的定义为:
+> ```json
+> {
+>     users: 'SELECT name, phone FROM user WHERE score > @score',
+>     order: 'SELECT sn, ammount, userId FROM order WHERE orderId = @orderId',
+>     pagination: 'SELECT count(*) as count, @pageSize as size, @pageIndex as index FROM user WHERE score = @score'
+> }
+> ```
+> 返回值可能是:
+> ```json
+> {
+>     users: [{ name: 'dawson', phone: 13545245245}, ...],
+>     order: [{ sn: 'wa039234913', amount: 231.23, userId: 23}],
+>     pagination: [{ count: 34, size: 3, index: 2}]
+> }
+> ```
+> 3. 通过`resultSet`,指定执行结果是单条(`S`)还多条(`M`)记录。如:上面示例中,如果指定`resultSet: "M,S,S"`,则返回值为:
+> ```json
+> {
+>     users: [{ name: 'dawson', phone: 13545245245}, ...],
+>     order: { sn: 'wa039234913', amount: 231.23, userId: 23},
+>     pagination: { count: 34, size: 3, index: 2}
+> }
+> ```
+
+## 配置代码调用
+如:前端需要查询某一段时间内用户的姓名,电话,下单号与订单金额信息,可以通过下面的配置文件(内容)来调用:
+
+```json
+[{
+	'name': 'query_user_order_info',
+    'code': 'SELECT Users.UserName, Users.Phone, Orders.OrderNo, Orders.Amount
+             FROM Users
+             INNER JOIN Orders
+             ON Users.Id = Orders.UserId
+             ORDER BY Orders.CreatedDate
+             WHERE Orders.CreatedDate >= @startDate AND Orders.CreatedDate <= @endDate',
+    'requiredTransaction': true,
+	'queryOnly': true,
+     'validator': {
+     	'startDate': ['datetime', 'required'],
+     	'endDate': ['datetime', 'required']
+     }
+},{
+	'name': '……',
+    'code': '……',
+    'requiredTransaction': true,
+	'queryOnly': true,
+    'validator': {}
+}, ……]
+```
+
+**说明:**
+> 1. `name`: 在一个或者多个cci的`json`文件下,名称必须是唯一的,否则会出现异常
+> 2. `requiredTransaction`: 支持事务
+> 3. `queryOnly`: 支持读写分离,`true`,且配置好了读写分离连接,系统将自动通过只读连接处理查询
+> 4. `validator`: 支持数据验证,详情参考[数据验证](#validator)
+
+通过DbRESTFul调用该存储过程的方式为:
+* **请求URL:** `https://localhost:<port>/api/<version>/invoke`
+* **请求类型:** `post`
+* **请求参数:**
+```json
+{
+    'name': 'query_user_order_info',
+    'parameters': {
+        'startDate': '2015-02-15',
+        'endDate': '2015-04-15'
+    }
+}
+```
+* **请求返回数据:** `json`格式
+```json
+{
+    "hasError": false,    // 如果是true,说明操作有错误
+    "errorMessage": null, // 如果hasError为true,则errorMessage为错误信息
+    "data": {}            // 调用sql的返回的数据
+}
+```
+
+
+## 数据验证 <a name='validator'></a>
+配置代码的调用支持数据的验证,通过`validator`来指定数据的验证规则,目前支持的规则如下:
+   规则名称   |    规则说明
+-------------|-----------------------
+required     |不能为空
+datetime     |类型为日期类型
+email        |邮件类型
+int          |int类型
+decimal      |数字类型(decimal, int, float, double)
+bool         |布尔类型(true, false)
+regex:\w     |正则表达式,`:`后接表达式
+maxLength:10 |最大长度为10
+minLength:5  |最小长度为5
+range:3,14   |数字区间,3到14之间
+in:a,b,23    |其中的值,'a', 'b'或'23'
+min:30       |输入值不能小于30
+max:50       |输入值不能大于50
+
+
+## TODOs
+- 考虑是否支持`json`格式的配置代码调用,即:后台的`query.dm`,另外调用。

+ 27 - 0
spec/restful-lib.md

@@ -0,0 +1,27 @@
+# RestFul 类库说明
+RestFul依赖于MVC4.0,通过WebAPI形式提供服务
+
+## 初始化应用
+新建MVC4.0项目,在`WebApiConfig.Register`方式中调用RestFul类库中的`Config.InitAPI(config)`方法,函数签名如下:
+```cs
+    void InitAPI(HttpConfiguration config, string apiPrefix = "api")
+```
+此InitAPI方法中,大致处理行为包含:
+1. 注册异常处理的过滤器,异常将通过`AjaxResult`进行返回
+2. 配置[路由规则](#route)
+3. JSON序列化时,将时间以`yyyy-MM-dd HH:mm:ss`形式返回
+
+### 路由规则<a name="route"></a>
+路由规则如下:**(其它业务逻辑不能与此路由冲突)**
+```cs
+config.Routes.MapHttpRoute(
+    name: "DbRestfulApi",
+    routeTemplate: "<apiPrefix>/{version}/{controller}/{tableName}",
+    defaults: new { controller = "Query", tableName = RouteParameter.Optional }
+);
+```
+
+## RestfulAPI调用
+调用时须传入当前用户的`token`信息,以验证用户权限
+调用路径为`<apiPrefix>/v1/{controller}/<tableName>`
+具体调用方式请参考文档: [query.md](query.html)

+ 1 - 0
spec/rw-control.md

@@ -0,0 +1 @@
+# 读写分离

BIN
src/.DS_Store


+ 87 - 0
src/DbRESTFul.sln

@@ -0,0 +1,87 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29409.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{A75FBDCB-939E-4B36-80B3-ABC648A0042C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{1B9480AA-91C4-4756-AA26-E2E06EB2CEA6}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{9CA632AB-EABF-4933-A5E5-92ED3F70BCA7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.DbRESTFul.LMT", "Tools\Wicture.DbRESTFul.LMT\Wicture.DbRESTFul.LMT.csproj", "{7C7C38F9-3054-4DBD-B207-DFE1FDB81792}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.DbRESTFul.PMT", "Tools\Wicture.DbRESTFul.PMT\Wicture.DbRESTFul.PMT.csproj", "{116C78DE-BCBC-4543-ACF6-F5461A4F5234}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{21F0C579-2C5D-4766-A829-211AC8C7DF2F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.DbRESTFul.Core.Test", "Tests\Wicture.DbRESTFul.Core.Test\Wicture.DbRESTFul.Core.Test.csproj", "{7990A3A0-FBE8-4076-B17C-5B95FBF1E523}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Legacy", "Legacy", "{A02C7D2D-04EB-42AE-A0B3-3D70BC43C3F7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.DbRESTFul.Core", "Legacy\Wicture.DbRESTFul.Core\Wicture.DbRESTFul.Core.csproj", "{4AA9C819-81CD-460C-9A79-C07EDDC3041A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.DbRESTFul.SQL", "Legacy\Wicture.DbRESTFul.SQL\Wicture.DbRESTFul.SQL.csproj", "{945804D9-862F-4101-8A85-A07EDAC672B5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.Microservice", "Legacy\Wicture.Microservice\Wicture.Microservice.csproj", "{C195F828-7705-4A81-BA82-759E1F4C47BE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.DbRESTFul", "Wicture.DbRESTFul\Wicture.DbRESTFul.csproj", "{84DC77B1-353B-4C5E-957A-54ACEC5C7E6A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wicture.DbRESTFul.Samples.MySqlService", "Samples\Wicture.DbRESTFul.Samples.MySqlService\Wicture.DbRESTFul.Samples.MySqlService.csproj", "{16A506D0-4C52-45B1-BC6C-0F0BA1F9DA38}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{7C7C38F9-3054-4DBD-B207-DFE1FDB81792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7C7C38F9-3054-4DBD-B207-DFE1FDB81792}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7C7C38F9-3054-4DBD-B207-DFE1FDB81792}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7C7C38F9-3054-4DBD-B207-DFE1FDB81792}.Release|Any CPU.Build.0 = Release|Any CPU
+		{116C78DE-BCBC-4543-ACF6-F5461A4F5234}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{116C78DE-BCBC-4543-ACF6-F5461A4F5234}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{116C78DE-BCBC-4543-ACF6-F5461A4F5234}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{116C78DE-BCBC-4543-ACF6-F5461A4F5234}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7990A3A0-FBE8-4076-B17C-5B95FBF1E523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7990A3A0-FBE8-4076-B17C-5B95FBF1E523}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7990A3A0-FBE8-4076-B17C-5B95FBF1E523}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7990A3A0-FBE8-4076-B17C-5B95FBF1E523}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4AA9C819-81CD-460C-9A79-C07EDDC3041A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4AA9C819-81CD-460C-9A79-C07EDDC3041A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4AA9C819-81CD-460C-9A79-C07EDDC3041A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4AA9C819-81CD-460C-9A79-C07EDDC3041A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{945804D9-862F-4101-8A85-A07EDAC672B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{945804D9-862F-4101-8A85-A07EDAC672B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{945804D9-862F-4101-8A85-A07EDAC672B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{945804D9-862F-4101-8A85-A07EDAC672B5}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C195F828-7705-4A81-BA82-759E1F4C47BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C195F828-7705-4A81-BA82-759E1F4C47BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C195F828-7705-4A81-BA82-759E1F4C47BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C195F828-7705-4A81-BA82-759E1F4C47BE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{84DC77B1-353B-4C5E-957A-54ACEC5C7E6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{84DC77B1-353B-4C5E-957A-54ACEC5C7E6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{84DC77B1-353B-4C5E-957A-54ACEC5C7E6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{84DC77B1-353B-4C5E-957A-54ACEC5C7E6A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{16A506D0-4C52-45B1-BC6C-0F0BA1F9DA38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{16A506D0-4C52-45B1-BC6C-0F0BA1F9DA38}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{16A506D0-4C52-45B1-BC6C-0F0BA1F9DA38}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{16A506D0-4C52-45B1-BC6C-0F0BA1F9DA38}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{7C7C38F9-3054-4DBD-B207-DFE1FDB81792} = {9CA632AB-EABF-4933-A5E5-92ED3F70BCA7}
+		{116C78DE-BCBC-4543-ACF6-F5461A4F5234} = {9CA632AB-EABF-4933-A5E5-92ED3F70BCA7}
+		{7990A3A0-FBE8-4076-B17C-5B95FBF1E523} = {21F0C579-2C5D-4766-A829-211AC8C7DF2F}
+		{4AA9C819-81CD-460C-9A79-C07EDDC3041A} = {A02C7D2D-04EB-42AE-A0B3-3D70BC43C3F7}
+		{945804D9-862F-4101-8A85-A07EDAC672B5} = {A02C7D2D-04EB-42AE-A0B3-3D70BC43C3F7}
+		{C195F828-7705-4A81-BA82-759E1F4C47BE} = {A02C7D2D-04EB-42AE-A0B3-3D70BC43C3F7}
+		{84DC77B1-353B-4C5E-957A-54ACEC5C7E6A} = {1B9480AA-91C4-4756-AA26-E2E06EB2CEA6}
+		{16A506D0-4C52-45B1-BC6C-0F0BA1F9DA38} = {A75FBDCB-939E-4B36-80B3-ABC648A0042C}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {BB41E6F1-2B6C-4320-B720-0DE90778D23B}
+	EndGlobalSection
+EndGlobal

+ 2143 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/API/common.json

@@ -0,0 +1,2143 @@
+[
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:45:58",
+    "name": "List_enums",
+    "module": "common",
+    "url": "enums",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取系统枚举值",
+    "summary": "获取系统枚举值",
+    "note": "目前系统支持的枚举类型有:\n1. 会员类型:memberType\n2. 配件类别:accessoryType",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "List_enums"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "category",
+          "type": "string",
+          "nullable": false,
+          "description": "目前系统支持的枚举类型有: 1. 会员类型:memberType 2. 配件类别:accessoryType",
+          "schema": null
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "名称",
+              "schema": null
+            },
+            {
+              "name": "value",
+              "type": "int",
+              "nullable": false,
+              "description": "值",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:39:57",
+    "name": "List_Areas",
+    "module": "common",
+    "url": "areas",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取区域信息",
+    "summary": "获取区域信息",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.ListAreas"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "code",
+              "type": "string",
+              "nullable": false,
+              "description": "代码",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "名称",
+              "schema": null
+            },
+            {
+              "name": "children",
+              "type": "object",
+              "nullable": false,
+              "description": "子项",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:46:08",
+    "name": "Help_Files",
+    "module": "common",
+    "url": "help",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取系统相关帮助",
+    "summary": "获取系统相关帮助",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.HelpFiles"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "type",
+          "type": "string",
+          "nullable": false,
+          "description": "获取数据的类型(关于我们:AboutUS、服务说明:ServiceDesc、购买须知:BuyInfo)",
+          "schema": null
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "",
+          "schema": [
+            {
+              "name": "title",
+              "type": "string",
+              "nullable": false,
+              "description": "标题",
+              "schema": null
+            },
+            {
+              "name": "url",
+              "type": "string",
+              "nullable": false,
+              "description": "Html页面地址",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:23:18",
+    "name": "List_All_Brand",
+    "module": "common",
+    "url": "brand/all",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "显示全部品牌",
+    "summary": "显示全部品牌",
+    "note": "首页和vin查询页面",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.ListAllBrand"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "brandCode",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "iconUrl",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "brandName",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "vinEnabled",
+              "type": "bool",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "epcEnabled",
+              "type": "bool",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "isPopular",
+              "type": "bool",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "vinLastCharCount",
+              "type": "int",
+              "nullable": true,
+              "description": "vin查询后几位"
+            },
+            {
+              "name": "pinYinCapital",
+              "type": "string",
+              "nullable": true,
+              "description": "拼音首字母"
+            },
+            {
+              "name": "comingSoon",
+              "type": "bool",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "isAppPopular",
+              "type": "bool",
+              "nullable": true,
+              "description": "是否app热门"
+            },
+            {
+              "name": "appModelEnabled",
+              "type": "bool",
+              "nullable": true,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2019-08-20T15:10:00",
+    "name": "Test_Controller_Route_GetStudent",
+    "module": "common",
+    "url": "test/getstudent",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "测试contorller路由API",
+    "summary": "测试contorller路由API",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "controller",
+      "name": "TestController.GetStudentInfo"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "schema": [
+            {
+              "schema": [
+                {
+                  "name": "pic",
+                  "type": "string",
+                  "nullable": true,
+                  "description": ""
+                },
+                {
+                  "name": "redirectUrl",
+                  "type": "string",
+                  "description": "/home"
+                }
+              ],
+              "name": "item",
+              "type": "object",
+              "nullable": true,
+              "description": ""
+            }
+          ],
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": ""
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:42:48",
+    "name": "Get_ImageCdn_BaseUrl",
+    "module": "common",
+    "url": "WeatherForecast",
+    "useAbsoluteUrl": true,
+    "method": "GET",
+    "title": "获取图片基地址",
+    "summary": "获取图片基地址",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "controller",
+      "name": "WeatherForecastController.Get"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "",
+          "schema": [
+            {
+              "name": "baseUrl",
+              "type": "string",
+              "nullable": true,
+              "description": "基地址"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:41:01",
+    "name": "List_Common_Feedback",
+    "module": "common",
+    "url": "feedback/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "常见反馈信息",
+    "summary": "常见反馈信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "List_Common_Feedback"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "item",
+              "type": "object",
+              "nullable": true,
+              "description": "",
+              "schema": [
+                {
+                  "name": "title",
+                  "type": "string",
+                  "nullable": true,
+                  "description": "常用反馈title"
+                },
+                {
+                  "name": "id",
+                  "type": "int",
+                  "nullable": true,
+                  "description": ""
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-07-12T16:56:03",
+    "name": "List_FQA",
+    "module": "common",
+    "url": "fqa",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取常见问题列表",
+    "summary": "获取常见问题列表",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.ListFQA"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "页数"
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "页码"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "question",
+              "type": "string",
+              "nullable": false,
+              "description": "标题",
+              "schema": null
+            },
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "id"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-07-12T16:56:03",
+    "name": "List_Common_Question",
+    "module": "common",
+    "url": "list/questions",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取常见问题列表",
+    "summary": "获取常见问题列表",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "List_Common_Question"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "页数"
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "页码"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "question",
+              "type": "string",
+              "nullable": false,
+              "description": "标题",
+              "schema": null
+            },
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "id"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:23:33",
+    "name": "Get_Question_Detail_RPC",
+    "module": "common",
+    "url": "fqa/detail",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "常见问题详细",
+    "summary": "常见问题详细",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "rpc",
+      "name": "Wicture.DbRESTFul.Samples.MySqlService.Get_Question_Detail"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "id"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "question",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "answer",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "createdAt",
+              "type": "datetime",
+              "nullable": false,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:23:33",
+    "name": "Get_Question_Detail_In_Repository",
+    "module": "common",
+    "url": "fqa/detail/by/repository",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "常见问题详细",
+    "summary": "常见问题详细",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.GetQuestionDetail"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "id"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "question",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "answer",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "createdAt",
+              "type": "datetime",
+              "nullable": false,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:23:33",
+    "name": "Get_Question_Detail_By_Rpc_Chian",
+    "module": "common",
+    "url": "fqa/detail/by/rpc/chian",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "常见问题详细",
+    "summary": "常见问题详细",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "rpc",
+      "name": "Wicture.DbRESTFul.Samples.MySqlService.Get_Question_Detail_In_Repository"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "id"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "question",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "answer",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "createdAt",
+              "type": "datetime",
+              "nullable": false,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:23:33",
+    "name": "Get_Question_Detail",
+    "module": "common",
+    "url": "fqa/detail/rpc",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "常见问题详细",
+    "summary": "常见问题详细",
+    "note": "",
+    "allowAnonymous": true,
+    "protocol": "RpcOnly",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_CommonQuestion_Detail"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "id"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "question",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "answer",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "createdAt",
+              "type": "datetime",
+              "nullable": false,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:41:16",
+    "name": "List_Brand_Common_Part",
+    "module": "common",
+    "url": "brand/commonpart",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "查询品牌常用件",
+    "summary": "查询品牌常用件",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "List_Brand_Common_Part"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "string",
+          "name": "brandCode",
+          "description": "品牌参数"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "keyword",
+              "type": "string",
+              "nullable": false,
+              "description": "0im3813d",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:42:26",
+    "name": "Get_Invoice_Min_Amount",
+    "module": "common",
+    "url": "invoice/min",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取最小开票金额",
+    "summary": "获取最小开票金额",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_Invoice_Min_Amount"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "min",
+              "type": "decimal",
+              "nullable": true,
+              "description": "最小开票金额"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T16:35:00",
+    "name": "Get_Collection_Share",
+    "module": "common",
+    "url": "collection/share",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取分享数据",
+    "summary": "获取分享数据",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_Collection_Share"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "string",
+          "name": "code",
+          "description": "分享码"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2019-06-20T18:44:59",
+    "name": "Create_Collection_ShareNew",
+    "module": "common",
+    "url": "collection/share/createprice",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建分享清单",
+    "summary": "创建分享清单",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.CreateShareWithPrice"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": false,
+          "description": "多个id 逗号隔开",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "price",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:29:25",
+    "name": "Get_SystemNotice",
+    "module": "common",
+    "url": "systemnotice/get",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取vin查询系统公告",
+    "summary": "获取vin查询系统公告",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_VinQuery_Latest_Ad"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "systemNotice",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-11-12T09:59:35",
+    "name": "DisableMember",
+    "module": "common",
+    "url": "member/disable",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "禁用账户",
+    "summary": "禁用账户",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "MemberRepository.DisableUser"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "memberId",
+          "description": "用户Id"
+        },
+        {
+          "type": "string",
+          "name": "key",
+          "nullable": false,
+          "description": "key"
+        },
+        {
+          "type": "string",
+          "name": "reason",
+          "description": "原因"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:40:33",
+    "name": "Get_Score_Rule",
+    "module": "common",
+    "url": "score/rule",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取邀请送积分数量",
+    "summary": "获取邀请送积分数量",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_Score_Rule"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "rule",
+              "type": "int",
+              "nullable": true,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-26T16:02:33",
+    "name": "App_Fqa",
+    "module": "common",
+    "url": "fqa/app",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "App获取常见问题",
+    "summary": "App获取常见问题",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.List_App_Fqa"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "question",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "answer",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "createdAt",
+              "type": "datetime",
+              "nullable": false,
+              "description": ""
+            },
+            {
+              "name": "summary",
+              "type": "string",
+              "nullable": false,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:25:49",
+    "name": "Get_ServiceDiscountNotice",
+    "module": "common",
+    "url": "service/notice",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取服务购买活动公告",
+    "summary": "获取服务购买活动公告",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_Service_Notice"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "serviceNotice",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:25:15",
+    "name": "List_App_Banner",
+    "module": "common",
+    "url": "app/banner",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "App首页banner大图",
+    "summary": "App首页banner大图",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "List_App_Banner"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "string",
+          "name": "version",
+          "nullable": true,
+          "description": "app version"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "schema": [
+            {
+              "schema": [
+                {
+                  "name": "pic",
+                  "type": "string",
+                  "nullable": true,
+                  "description": ""
+                },
+                {
+                  "name": "action",
+                  "type": "string",
+                  "description": "",
+                  "nullable": false
+                }
+              ],
+              "name": "items",
+              "type": "array",
+              "nullable": true,
+              "description": ""
+            }
+          ],
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集"
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2018-10-22T15:23:55",
+    "name": "Get_MemberInfo_By_InviteCode",
+    "module": "common",
+    "url": "member/byinvite",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "根据邀请码获取用户基本信息",
+    "summary": "根据邀请码获取用户基本信息",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_MemberInfo_By_InviteCode"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "string",
+          "name": "invitationCode",
+          "description": "邀请码"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": true,
+              "description": "",
+              "schema": []
+            },
+            {
+              "name": "displayName",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            },
+            {
+              "name": "mobile",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            },
+            {
+              "name": "companyName",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            },
+            {
+              "name": "iconUrl",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            }
+          ],
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集"
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "xiongkailing",
+    "updatedTime": "2018-11-20T13:29:44",
+    "name": "GetBrand",
+    "module": "common",
+    "url": "brand/get",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取单个品牌信息",
+    "summary": "获取单个品牌信息",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_Brand_Info"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "string",
+          "name": "brandCode"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "xiongkailing",
+    "updatedTime": "2018-12-25T18:36:51",
+    "name": "List_Home_Config",
+    "module": "common",
+    "url": "home/config",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "首页配置项",
+    "summary": "首页配置项",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "List_Home_Config"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "client",
+          "description": "0 web 1 app"
+        },
+        {
+          "type": "string",
+          "name": "version",
+          "description": "版本号"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "schema": [
+            {
+              "name": "config",
+              "type": "string",
+              "nullable": true,
+              "description": ""
+            }
+          ],
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "字符串"
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "xiongkailing",
+    "updatedTime": "2019-02-26T17:12:25",
+    "name": "Get_User_Guide",
+    "module": "common",
+    "url": "user/guide",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "用户指引",
+    "summary": "用户指引",
+    "note": "",
+    "allowAnonymous": true,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "Get_User_Guide"
+    },
+    "parameter": {
+      "query": [],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "schema": [],
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "字符串"
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "xiongkailing",
+    "updatedTime": "2019-08-15T18:25:23",
+    "name": "Create_WebAccess_Log",
+    "module": "common",
+    "url": "accesslog/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "保存web访问日志",
+    "summary": "保存web访问日志",
+    "note": null,
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.CreateWebAccessLog"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "type",
+          "type": "string",
+          "nullable": false,
+          "description": ""
+        },
+        {
+          "name": "info",
+          "type": "string",
+          "nullable": false,
+          "description": ""
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": true,
+              "description": "返回创建编号"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "xiongkailing",
+    "updatedTime": "2019-06-20T18:45:26",
+    "name": "Create_Collection_Share",
+    "module": "common",
+    "url": "collection/share/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建分享清单",
+    "summary": "创建分享清单",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CommonRepository.CreateShare"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "ids",
+          "type": "string",
+          "nullable": false,
+          "description": "多个id 逗号隔开",
+          "schema": []
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  }
+]

+ 385 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/API/pmt.json

@@ -0,0 +1,385 @@
+[
+  {
+    "version": "1.0",
+    "owner": "Dawson Liu",
+    "name": "CheckNameExsits",
+    "module": "pmt",
+    "url": "ca/check",
+    "method": "GET",
+    "title": "检测接口名称是否存在",
+    "summary": "检测接口名称是否存在",
+    "note": "",
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": false,
+    "implementation": {
+      "type": "csi",
+      "name": "CheckNameExsits"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": false,
+          "description": "接口标示",
+          "schema": null
+        },
+        {
+          "name": "name",
+          "type": "string",
+          "nullable": null,
+          "description": "接口名称",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口编号",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "version",
+              "type": "string",
+              "nullable": true,
+              "description": "接口版本",
+              "schema": null
+            },
+            {
+              "name": "owner",
+              "type": "string",
+              "nullable": true,
+              "description": "接口负责人",
+              "schema": null
+            },
+            {
+              "name": "module",
+              "type": "string",
+              "nullable": true,
+              "description": "所属模块",
+              "schema": null
+            },
+            {
+              "name": "url",
+              "type": "string",
+              "nullable": true,
+              "description": "接口访问地址",
+              "schema": null
+            },
+            {
+              "name": "method",
+              "type": "string",
+              "nullable": true,
+              "description": "接口请求方法",
+              "schema": null
+            },
+            {
+              "name": "title",
+              "type": "string",
+              "nullable": true,
+              "description": "接口标题",
+              "schema": null
+            },
+            {
+              "name": "summary",
+              "type": "string",
+              "nullable": true,
+              "description": "接口简述",
+              "schema": null
+            },
+            {
+              "name": "note",
+              "type": "string",
+              "nullable": true,
+              "description": "接口备注",
+              "schema": null
+            },
+            {
+              "name": "implemented",
+              "type": "bool",
+              "nullable": true,
+              "description": "是否已实现",
+              "schema": null
+            },
+            {
+              "name": "implementation",
+              "type": "string",
+              "nullable": true,
+              "description": "实现代码",
+              "schema": null
+            },
+            {
+              "name": "parameter",
+              "type": "string",
+              "nullable": true,
+              "description": "接口调用参数",
+              "schema": null
+            },
+            {
+              "name": "result",
+              "type": "string",
+              "nullable": true,
+              "description": "接口返回结果",
+              "schema": null
+            },
+            {
+              "name": "mock",
+              "type": "string",
+              "nullable": true,
+              "description": "接口模拟数据",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "projectId": "*",
+          "name": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "id": "",
+            "projectId": "",
+            "name": "",
+            "version": "",
+            "owner": "",
+            "module": "",
+            "url": "",
+            "method": "",
+            "title": "",
+            "summary": "",
+            "note": "",
+            "implemented": "",
+            "implementation": "",
+            "parameter": "",
+            "result": "",
+            "mock": "",
+            "updatedTime": ""
+          }
+        }
+      }
+    ],
+    "createdAt": "2020-02-26T18:10:44"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "ListProjectTables",
+    "module": "pmt",
+    "url": "project/tables",
+    "method": "POST",
+    "title": "获取对应数据库信息",
+    "summary": "获取对应数据库信息",
+    "note": "",
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": false,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultCsiRepository.LoadTablesForProject"
+    },
+    "parameter": {
+      "query": [],
+      "bodyType": "object",
+      "body": [
+        {
+          "name": "connectionString",
+          "type": "string",
+          "nullable": null,
+          "description": "链接字符串",
+          "schema": null
+        },
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": null,
+          "description": "所属项目",
+          "schema": null
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-02-26T18:10:45"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "CreateDefaultCsi",
+    "module": "pmt",
+    "url": "defaultcsi/create",
+    "method": "POST",
+    "title": "创建默认CSI",
+    "summary": "创建默认CSI",
+    "note": "",
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": false,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultCsiRepository.CreateCsis"
+    },
+    "parameter": {
+      "query": [],
+      "bodyType": "object",
+      "body": [
+        {
+          "name": "connectionString",
+          "type": "string",
+          "nullable": null,
+          "description": "链接字符串",
+          "schema": null
+        },
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": null,
+          "description": "所属项目",
+          "schema": null
+        },
+        {
+          "name": "module",
+          "type": "string",
+          "nullable": null,
+          "description": "所属模块",
+          "schema": null
+        },
+        {
+          "name": "apiData",
+          "type": "array",
+          "nullable": null,
+          "description": "api接口数据",
+          "schema": null
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-02-26T18:10:45"
+  }
+]

+ 896 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/API/test.json

@@ -0,0 +1,896 @@
+[
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "TestApi",
+    "module": "test",
+    "url": "test/service",
+    "method": "GET",
+    "title": "测试接口",
+    "summary": "测试服务是否可用",
+    "note": "测试服务是否可用,如果不可用,则不能发布",
+    "allowAnonymous": true,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListModules"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": null,
+          "description": "项目Id",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": null
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-02-22T12:25:13"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "GetApi_726d",
+    "module": "test",
+    "url": "api/get1",
+    "method": "GET",
+    "title": "获取接口信息表",
+    "summary": null,
+    "note": null,
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "GetApi"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "接口标识",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "返回的数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口标识",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": true,
+              "description": "项目Id",
+              "schema": null
+            },
+            {
+              "name": "moduleId",
+              "type": "int",
+              "nullable": true,
+              "description": "模块Id",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": true,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "ownerId",
+              "type": "int",
+              "nullable": true,
+              "description": "负责人Id",
+              "schema": null
+            },
+            {
+              "name": "minorVersion",
+              "type": "int",
+              "nullable": true,
+              "description": "最后版本号",
+              "schema": null
+            },
+            {
+              "name": "updatedAt",
+              "type": "dateTime",
+              "nullable": true,
+              "description": "最后更新时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-03-10T14:18:27"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "ListApi_7d57",
+    "module": "test",
+    "url": "api/list1",
+    "method": "GET",
+    "title": "获取接口信息表列表",
+    "summary": null,
+    "note": null,
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListApi"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "页数",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "页码",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "返回的数据",
+          "schema": [
+            {
+              "name": "items",
+              "type": "array",
+              "nullable": true,
+              "description": "当前页数据项目",
+              "schema": [
+                {
+                  "name": "id",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "接口标识",
+                  "schema": null
+                },
+                {
+                  "name": "projectId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "项目Id",
+                  "schema": null
+                },
+                {
+                  "name": "moduleId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "模块Id",
+                  "schema": null
+                },
+                {
+                  "name": "name",
+                  "type": "string",
+                  "nullable": true,
+                  "description": "接口名称",
+                  "schema": null
+                },
+                {
+                  "name": "ownerId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "负责人Id",
+                  "schema": null
+                },
+                {
+                  "name": "minorVersion",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "最后版本号",
+                  "schema": null
+                },
+                {
+                  "name": "updatedAt",
+                  "type": "dateTime",
+                  "nullable": true,
+                  "description": "最后更新时间",
+                  "schema": null
+                }
+              ]
+            },
+            {
+              "name": "pagination",
+              "type": "int",
+              "nullable": false,
+              "description": "分页信息",
+              "schema": [
+                {
+                  "name": "totalCount",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "总条数",
+                  "schema": null
+                },
+                {
+                  "name": "pageSize",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "页数",
+                  "schema": null
+                },
+                {
+                  "name": "pageIndex",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "页码",
+                  "schema": null
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-03-10T14:18:28"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "GetApi",
+    "module": "test",
+    "url": "api/get7",
+    "method": "GET",
+    "title": "获取接口信息表",
+    "summary": null,
+    "note": null,
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "GetApi"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "接口标识",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "返回的数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口标识",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": true,
+              "description": "项目Id",
+              "schema": null
+            },
+            {
+              "name": "moduleId",
+              "type": "int",
+              "nullable": true,
+              "description": "模块Id",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": true,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "ownerId",
+              "type": "int",
+              "nullable": true,
+              "description": "负责人Id",
+              "schema": null
+            },
+            {
+              "name": "minorVersion",
+              "type": "int",
+              "nullable": true,
+              "description": "最后版本号",
+              "schema": null
+            },
+            {
+              "name": "updatedAt",
+              "type": "dateTime",
+              "nullable": true,
+              "description": "最后更新时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-03-10T14:32:27"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "ListApi",
+    "module": "test",
+    "url": "api/list7",
+    "method": "GET",
+    "title": "获取接口信息表列表",
+    "summary": null,
+    "note": null,
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListApi"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "页数",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "页码",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "返回的数据",
+          "schema": [
+            {
+              "name": "items",
+              "type": "array",
+              "nullable": true,
+              "description": "当前页数据项目",
+              "schema": [
+                {
+                  "name": "id",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "接口标识",
+                  "schema": null
+                },
+                {
+                  "name": "projectId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "项目Id",
+                  "schema": null
+                },
+                {
+                  "name": "moduleId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "模块Id",
+                  "schema": null
+                },
+                {
+                  "name": "name",
+                  "type": "string",
+                  "nullable": true,
+                  "description": "接口名称",
+                  "schema": null
+                },
+                {
+                  "name": "ownerId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "负责人Id",
+                  "schema": null
+                },
+                {
+                  "name": "minorVersion",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "最后版本号",
+                  "schema": null
+                },
+                {
+                  "name": "updatedAt",
+                  "type": "dateTime",
+                  "nullable": true,
+                  "description": "最后更新时间",
+                  "schema": null
+                }
+              ]
+            },
+            {
+              "name": "pagination",
+              "type": "int",
+              "nullable": false,
+              "description": "分页信息",
+              "schema": [
+                {
+                  "name": "totalCount",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "总条数",
+                  "schema": null
+                },
+                {
+                  "name": "pageSize",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "页数",
+                  "schema": null
+                },
+                {
+                  "name": "pageIndex",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "页码",
+                  "schema": null
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-03-10T14:32:31"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "GetApi_2ce5",
+    "module": "test",
+    "url": "api/get5",
+    "method": "GET",
+    "title": "获取接口信息表",
+    "summary": "获取接口信息表",
+    "note": null,
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "GetApi_868c"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "接口标识",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "返回的数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口标识",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": true,
+              "description": "项目Id",
+              "schema": null
+            },
+            {
+              "name": "moduleId",
+              "type": "int",
+              "nullable": true,
+              "description": "模块Id",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": true,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "ownerId",
+              "type": "int",
+              "nullable": true,
+              "description": "负责人Id",
+              "schema": null
+            },
+            {
+              "name": "minorVersion",
+              "type": "int",
+              "nullable": true,
+              "description": "最后版本号",
+              "schema": null
+            },
+            {
+              "name": "updatedAt",
+              "type": "dateTime",
+              "nullable": true,
+              "description": "最后更新时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-03-10T14:18:51"
+  },
+  {
+    "version": "1.0.0",
+    "owner": "Dawson Liu",
+    "name": "ListApi_40cc",
+    "module": "test",
+    "url": "api/list6",
+    "method": "GET",
+    "title": "获取接口信息表列表",
+    "summary": "获取接口信息表列表",
+    "note": null,
+    "allowAnonymous": false,
+    "useAbsoluteUrl": false,
+    "deprecated": false,
+    "protocol": "httpAndRpc",
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListApi_39f6"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "页数",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "页码",
+          "schema": null
+        }
+      ],
+      "bodyType": "object",
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "返回的数据",
+          "schema": [
+            {
+              "name": "items",
+              "type": "array",
+              "nullable": true,
+              "description": "当前页数据项目",
+              "schema": [
+                {
+                  "name": "id",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "接口标识",
+                  "schema": null
+                },
+                {
+                  "name": "projectId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "项目Id",
+                  "schema": null
+                },
+                {
+                  "name": "moduleId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "模块Id",
+                  "schema": null
+                },
+                {
+                  "name": "name",
+                  "type": "string",
+                  "nullable": true,
+                  "description": "接口名称",
+                  "schema": null
+                },
+                {
+                  "name": "ownerId",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "负责人Id",
+                  "schema": null
+                },
+                {
+                  "name": "minorVersion",
+                  "type": "int",
+                  "nullable": true,
+                  "description": "最后版本号",
+                  "schema": null
+                },
+                {
+                  "name": "updatedAt",
+                  "type": "dateTime",
+                  "nullable": true,
+                  "description": "最后更新时间",
+                  "schema": null
+                }
+              ]
+            },
+            {
+              "name": "pagination",
+              "type": "int",
+              "nullable": false,
+              "description": "分页信息",
+              "schema": [
+                {
+                  "name": "totalCount",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "总条数",
+                  "schema": null
+                },
+                {
+                  "name": "pageSize",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "页数",
+                  "schema": null
+                },
+                {
+                  "name": "pageIndex",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "页码",
+                  "schema": null
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [],
+    "createdAt": "2020-03-10T14:18:52"
+  }
+]

+ 418 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/CSI/common.json

@@ -0,0 +1,418 @@
+[
+  {
+    "name": "Create_feedback",
+    "code": "INSERT INTO feedback( memberId, memberName, type, title, content, images, createdAt) VALUES ( @memberId, @memberName, @type, @title, @content, @images, NOW()); SELECT LAST_INSERT_ID() AS id;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {
+      "identity": {
+        "userId": "memberId",
+        "userName": "memberName"
+      }
+    }
+  },
+  {
+    "name": "List_enums",
+    "code": "select name, value from dictionary where category = @category;",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_brand_with_ids",
+    "code": "SELECT id, `name` FROM brand WHERE id in (@ids)",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "defaults": {
+        "ids": "0"
+      },
+      "replace": [
+        "ids"
+      ]
+    }
+  },
+  {
+    "name": "List_member_type",
+    "code": "SELECT dictionary.`name` as memberTypeName, dictionary.`value` as memberTypeId FROM dictionary where category='memberType' order by dictionary.`value`;",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_Brand_Common_Part",
+    "code": "SELECT partName as keyword From brand_common_part;",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Add_Brand_Common_Part",
+    "code": "INSERT INTO brand_common_part (brandCode,partName,partCode) VALUES (@brandCode,@partName,@partCode); SELECT ROW_COUNT();",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "Delete_Brand_Common_Part",
+    "code": "delete from brand_common_part where id=@id; select ROW_COUNT();",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "Update_Brand_Common_Part",
+    "code": "Update brand_common_part SET brandCode=@brandCode,partName=@partName,partCode=@partCode WHERE id=@id; SELECT ROW_COUNT();",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "List_brand_all",
+    "code": "SELECT name as brandName,code as brandCode,epcEnabled,vinEnabled,icon as iconUrl,isPopular,vinLastCharCount,pinYinCapital, if(brandId is null, 0 , 1) as isAppPopular,appModelEnabled FROM brand LEFT JOIN app_popular_brands ON brandId = brand.id ORDER BY pinYinCapital, brand.displayOrder",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Update_Query_Part_Statistic",
+    "code": "INSERT INTO query_statistic (memberId,times,searchCount,`date`,updatedAt) VALUE (@memberId,@times,@searchCount,date(now()),now()) ON DUPLICATE KEY UPDATE times=times+@times,searchCount=searchCount+@searchCount,updatedAt=now(); SELECT ROW_COUNT() as count;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {
+      "identity": {
+        "userId": "memberId"
+      }
+    }
+  },
+  {
+    "name": "List_Home_Banner",
+    "code": "SELECT id,pic,redirectUrl FROM web_banner WHERE enabled=1 ORDER BY displayOrder",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_Common_Question",
+    "code": "SELECT id, title as question from static_content where type=1 AND hided=0 ORDER BY displayOrder Limit @pageStart,@pageSize",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "page": "pageIndex",
+        "count": "totalCount"
+      }
+    }
+  },
+  {
+    "name": "List_Home_Func",
+    "code": "SELECT title,content,url FROM static_content where type=2 and hided=0 ORDER BY displayOrder;",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_Home_Advantage",
+    "code": "SELECT title,url as pic,content from static_content WHERE type=3 and hided=0 ORDER By displayOrder",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_ImageCdn_BaseUrl",
+    "code": "SELECT value as baseUrl FROM dictionary WHERE category='sysConfig' AND name='imageCdn';",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_Common_Feedback",
+    "code": "SELECT name as title,value as id from dictionary where category='feedbackType'",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_CommonQuestion_Detail",
+    "code": "SELECT id,title as question,content as answer,createdAt FROM static_content WHERE id=@id",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_Invoice_Min_Amount",
+    "code": "SELECT value as min FROM dictionary WHERE category='invoiceConfig' AND name='min' LIMIT 1;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Create_Collection_Share",
+    "code": "INSERT INTO part_collection_share (memberId,code,ids) VALUE (@memberId,@code,@ids); SELECT LAST_INSERT_ID() as id;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {
+      "identity": {
+        "userId": "memberId"
+      }
+    }
+  },
+  {
+    "name": "Create_Share_Relation",
+    "code": "INSERT INTO part_share_relation (colectionId,shareId) VALUES (@colectionId,@shareId); SELECT ROW_COUNTS() as count;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "Check_Share_Exist",
+    "code": "SELECT code, id from part_collection_share WHERE memberId=@memberId AND ids=@ids",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "identity": {
+        "userId": "memberId"
+      }
+    }
+  },
+  {
+    "name": "Get_part_collection_shares",
+    "code": "SELECT p.id,p.partCode,p.partName,p.buyCount,p.suitableModel,b.name as brandName,p.originalGroup,p.count,p.marketCode,p.marketName,p.brandCode from part_collection p JOIN brand b ON b.code = p.brandCode WHERE p.id in @cIds",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_VinQuery_Latest_Ad",
+    "code": "SELECT value as systemNotice from dictionary WHERE category = 'systemNotice' AND `name`='vinQueryNotice' limit 1",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "DisableUser",
+    "code": "UPDATE member SET disabled=1, updatedAt=now() WHERE id=@memberId AND 'AeQr4P3M'=@key; INSERT into member_disable_reason (memberId,reason) VALUES (@memberId,@reason); SELECT mobile from member where id=@memberId;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_ComingSoon",
+    "code": "SELECT value as brandCodes FROM dictionary WHERE category='sysConfig' AND name='comingSoon'",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_Score_Rule",
+    "code": "SELECT value as rule FROM dictionary WHERE category='scoreRelative' and name='inviteSend'",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_App_Fqa",
+    "code": "SELECT id, title as question,content as answer from static_content where type=4 AND hided=0 ORDER BY displayOrder",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_Collection_Share",
+    "code": "SELECT p.partCode,p.partName,p.count,p.buyCount,p.originalGroup,p.marketCode,p.marketName,b.icon,b.name as brandName,ps.price FROM part_collection_share s JOIN part_share_relation ps ON ps.shareId = s.id JOIN part_collection p ON p.id = ps.collectionId LEFT JOIN brand b ON b.code = p.brandCode WHERE s.`code` = @code",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_Service_Notice",
+    "code": "SELECT value as serviceNotice from dictionary WHERE category = 'systemNotice' AND `name`='discountNotice' limit 1",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_App_Banner",
+    "code": "SELECT url as pic,action from app_layout_banners WHERE enabled=1 AND ((startedAt < now()) OR (startedAt is null)) AND ((endedAt > now()) OR (endedAt is null)) [AND ((maxVersion > @version) OR (maxVersion is null))] [AND ((minVersion <= @version) OR (minVersion is null))] ORDER BY displayOrder;",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_Share_Details",
+    "code": "SELECT s.`code` as shareCode,c.partCode,c.partName,c.buyCount,c.originalGroup,b.icon as brandIcon,c.marketName,c.marketCode from part_collection_share s JOIN part_share_relation r ON r.shareId = s.id JOIN part_collection c ON c.id = r.collectionId JOIN brand b ON b.code = c.brandCode WHERE 1=1 AND s.`code` in @shareCodes;",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_MemberInfo_By_InviteCode",
+    "code": "SELECT id,displayName,companyName,concat(left(mobile,3),'****',right(mobile,4)) as mobile,iconUrl FROM member WHERE invitationCode=@invitationCode",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_Model_Index",
+    "code": {
+      "className": "SELECT className from model_index WHERE parentId = @parentId GROUP BY className ",
+      "items": "select m.id,m.name,count(c.className) > 0 as hasChildren,c.className as childrenClassName,b.icon from model_index m LEFT JOIN model_index c ON c.parentId = m.id LEFT JOIN brand b ON b.name = m.name WHERE m.parentId = @parentId group by m.id,m.name,c.className,b.icon",
+      "breadCrumb": "select t2.id,t2.`name`,t2.className from ( select @r AS _id, (SELECT @r:=parentId FROM model_index WHERE id = _id) AS parentId, @l := @l + 1 AS lvl from (select @r:=@parentId, @l:= 0) vars, model_index h where @r <> 0 ) t1 join model_index t2 ON t1._id = t2.id order BY t1.lvl desc"
+    },
+    "module": "common",
+    "resultSet": "S,M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_MaintainParts_List",
+    "code": {
+      "modelInfo": "select engine,displacement,transmission,driverType,year from model_info where indexId = @modeId",
+      "maintainParts": "SELECT id, partName, partCode, oesPrice, maintainTime, maintainUnitPrice, maintainPrice, replacementNo, `type`, usage, unit, level, iceTemperature, color, viscosity FROM model_mantain_part WHERE modelId=@modelId",
+      "cleanServices": "SELECT id,`name`,price FROM model_clean_service WHERE modelId=@modelId"
+    },
+    "module": "common",
+    "resultSet": "S,M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_model_mantain_part",
+    "code": "SELECT id, maintainDistance, maintainSpan FROM model_mantain_part WHERE modelId = @modelId",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_Brand_Info",
+    "code": "SELECT name, icon from brand where code = @brandCode;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "List_Home_Config",
+    "code": "SELECT config FROM home_config WHERE client=@client AND version=@version;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_User_Guide",
+    "code": "SELECT `value` as config FROM `dictionary` WHERE name='guide' and category='sysConfig';",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Get_BrandNew",
+    "code": "SELECT value as brandCodes FROM dictionary WHERE category='sysConfig' AND name='brandNew'",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "Create_web_access_log",
+    "code": "INSERT INTO web_access_log( `type`, info) VALUES ( @type, @info); SELECT LAST_INSERT_ID() AS id;",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "Create_ApprovePicture",
+    "code": "INSERT INTO `qpzs_main`.`approve_banpic`(`approvalId`, `picurl`) VALUES (@approvalId, @picurl); ",
+    "module": "common",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "List_HotPoints",
+    "code": "SELECT * FROM banner_hotpoint WHERE bannerId in @bannerIds;",
+    "module": "common",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  }
+]

+ 63 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/CSI/pmt.json

@@ -0,0 +1,63 @@
+[
+  {
+    "name": "GetProjectDbType",
+    "module": "pmt",
+    "code": "SELECT databaseType FROM project WHERE id = @projectId;",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": null,
+    "branch": null,
+    "description": null
+  },
+  {
+    "name": "GetProjectName",
+    "module": "pmt",
+    "code": "SELECT name FROM project WHERE id = @projectId;",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": null,
+    "branch": null,
+    "description": null
+  },
+  {
+    "name": "GetApi_1b1b",
+    "module": "pmt",
+    "code": "SELECT id, projectId, moduleId, name, ownerId, minorVersion, updatedAt FROM api WHERE id = @id",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": null,
+    "branch": null,
+    "description": "获取接口信息表"
+  },
+  {
+    "name": "ListApi_ee61",
+    "module": "pmt",
+    "code": "SELECT id, projectId, moduleId, name, ownerId, minorVersion, updatedAt FROM api WHERE 1 = 1 LIMIT @pageStart, @pageSize;",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      }
+    },
+    "branch": null,
+    "description": "获取接口信息表列表"
+  },
+  {
+    "name": "CreateApi_0b88",
+    "module": "pmt",
+    "code": "test",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middlewares": {},
+    "branch": null,
+    "description": null
+  }
+]

+ 124 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/CSI/test.json

@@ -0,0 +1,124 @@
+[
+  {
+    "name": "TestCsi",
+    "module": "test",
+    "code": "SELECT * FROM test",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middlewares": null,
+    "branch": null,
+    "description": "测试"
+  },
+  {
+    "name": "ListApi",
+    "module": "test",
+    "code": "SELECT id, projectId, moduleId, name, ownerId, minorVersion, updatedAt FROM api WHERE 1 = 1 LIMIT @pageStart, @pageSize;",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      }
+    },
+    "branch": null,
+    "description": "获取接口信息表列表"
+  },
+  {
+    "name": "CreateApi",
+    "module": "test",
+    "code": "INSERT INTO api(projectId, moduleId, name, ownerId, minorVersion, updatedAt) VALUES (@projectId, @moduleId, @name, @ownerId, @minorVersion, @updatedAt);SELECT LAST_INSERT_ID() AS id;",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middlewares": null,
+    "branch": null,
+    "description": "创建接口信息表"
+  },
+  {
+    "name": "UpdateApi",
+    "module": "test",
+    "code": "UPDATE api SET projectId = @projectId, moduleId = @moduleId, name = @name, ownerId = @ownerId, minorVersion = @minorVersion, updatedAt = @updatedAt WHERE id = @id; SELECT ROW_COUNT() AS count;",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middlewares": null,
+    "branch": null,
+    "description": "更新接口信息表"
+  },
+  {
+    "name": "DeleteApi",
+    "module": "test",
+    "code": "DELETE FROM api WHERE id in ids; SELECT ROW_COUNT() AS count;",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middlewares": null,
+    "branch": null,
+    "description": "删除接口信息表"
+  },
+  {
+    "name": "UpdateApi_6aa1",
+    "module": "test",
+    "code": "UPDATE api SET projectId = @projectId, moduleId = @moduleId, name = @name, ownerId = @ownerId, minorVersion = @minorVersion, updatedAt = @updatedAt WHERE id = @id; SELECT ROW_COUNT() AS count;",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middlewares": null,
+    "branch": null,
+    "description": "更新接口信息表"
+  },
+  {
+    "name": "DeleteApi_5602",
+    "module": "test",
+    "code": "DELETE FROM api WHERE id in ids; SELECT ROW_COUNT() AS count;",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middlewares": null,
+    "branch": null,
+    "description": "删除接口信息表"
+  },
+  {
+    "name": "GetApi_868c",
+    "module": "test",
+    "code": "SELECT id, projectId, moduleId, name, ownerId, minorVersion, updatedAt FROM api WHERE id = @id",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": null,
+    "branch": null,
+    "description": "获取接口信息表"
+  },
+  {
+    "name": "ListApi_39f6",
+    "module": "test",
+    "code": "SELECT id, projectId, moduleId, name, ownerId, minorVersion, updatedAt FROM api WHERE 1 = 1 LIMIT @pageStart, @pageSize;",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      }
+    },
+    "branch": null,
+    "description": "获取接口信息表列表"
+  },
+  {
+    "name": "GetApi",
+    "module": "test",
+    "code": "SELECT id, projectId, moduleId, name, ownerId, minorVersion, updatedAt FROM api WHERE id = @id",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middlewares": null,
+    "branch": null,
+    "description": "获取接口信息表"
+  }
+]

+ 20 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Controllers/TestController.cs

@@ -0,0 +1,20 @@
+using Microsoft.AspNetCore.Mvc;
+using Wicture.DbRESTFul.Samples.MySqlService.Repositories;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService.Controllers
+{
+    public class TestController : ControllerBase
+    {
+        private CommonRepository _commonRepository;
+
+        public TestController(CommonRepository commonRepository)
+        {
+            _commonRepository = commonRepository;
+        }
+
+        public object GetStudentInfo()
+        {
+            return new StandardResult() { Data = new { id = 1, name = "Ricky", age = 10 } };
+        }
+    }
+}

+ 37 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Controllers/WeatherForecastController.cs

@@ -0,0 +1,37 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Linq;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService.Controllers
+{
+    public class WeatherForecastController : ControllerBase
+    {
+        private static readonly string[] Summaries = new[]
+        {
+            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+        };
+
+        private readonly ILogger<WeatherForecastController> _logger;
+
+        public WeatherForecastController(ILogger<WeatherForecastController> logger)
+        {
+            _logger = logger;
+        }
+
+        public StandardResult Get()
+        {
+            var rng = new Random();
+
+            return new StandardResult
+            {
+                Data = Enumerable.Range(1, 5).Select(index => new WeatherForecast
+                {
+                    Date = DateTime.Now.AddDays(index),
+                    TemperatureC = rng.Next(-20, 55),
+                    Summary = Summaries[rng.Next(Summaries.Length)]
+                })
+            };
+        }
+    }
+}

+ 38 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Models/Models.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService
+{
+    public partial class Brand
+    {
+        public string brandCode { get; set; }
+        public string iconUrl { get; set; }
+        public string brandName { get; set; }
+        public int vinLastCharCount { get; set; }
+        public bool vinEnabled { get; set; }
+        public bool epcEnabled { get; set; }
+        public bool isPopular { get; set; }
+        public string pinYinCapital { get; set; }
+        public bool comingSoon { get; set; }
+        public bool isAppPopular { get; set; }
+        public bool appModelEnabled { get; set; }
+        public bool brandNew { get; set; }
+    }
+
+    public class Banner
+    {
+        public int Id { get; set; }
+        public string Pic { get; set; }
+        public string RedirectUrl { get; set; }
+    }
+
+    public class Question
+    {
+        public int Id { get; set; }
+        public string question { get; set; }
+        public string Answer { get; set; }
+        public DateTime createdAt { get; set; }
+    }
+}

+ 13 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Program.cs

@@ -0,0 +1,13 @@
+using NLog;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            LogManager.ThrowConfigExceptions = true;
+            Starter.Start<Startup>(args);
+        }
+    }
+}

+ 30 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Properties/launchSettings.json

@@ -0,0 +1,30 @@
+{
+  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:6359",
+      "sslPort": 0
+    }
+  },
+  "profiles": {
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "launchUrl": "weatherforecast",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "Wicture.DbRESTFul.Samples.MySqlService": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "launchUrl": "weatherforecast",
+      "applicationUrl": "http://localhost:5001",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 93 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Repositories/CommonRepository.cs

@@ -0,0 +1,93 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+using Wicture.DbRESTFul.Rpc.Client;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService.Repositories
+{
+    public class CommonRepository : DbRESTFulRepository
+    {
+        private readonly IRpcClient _rpcClient;
+        public CommonRepository(IRpcClient rpcClient)
+        {
+            _rpcClient = rpcClient;
+        }
+        /// <summary>
+        /// Async way method.
+        /// </summary>
+        public async Task<object> ListAreas()
+        {
+            return await Task.FromResult(new { id = 1, name = "上海" }); 
+        }
+
+        /// <summary>
+        /// Sync way method.
+        /// </summary>
+        public object HelpFiles()
+        {
+            return new { filePath = "test" }; 
+        }
+
+        /// <summary>
+        /// InvokeAsync and InvokeSingleOrDefaultAsync with shared connection.
+        /// </summary>
+        public async Task<object> ListAllBrand()
+        {
+            using var conn = ConnectionManager.GetConnection();
+            var brands = await base.InvokeAsync<Brand>("List_brand_all", new { }, conn);
+            string comingSoon = await base.InvokeSingleOrDefaultAsync<string>("Get_ComingSoon", new { }, conn);
+            string brandNew = await base.InvokeSingleOrDefaultAsync<string>("Get_BrandNew", new { }, conn);
+
+            if (!string.IsNullOrEmpty(comingSoon))
+            {
+                var brandCodes = comingSoon.Split(',');
+                brands.ForEach(t => { t.comingSoon = brandCodes.Contains(t.brandCode); });
+            }
+            if (!string.IsNullOrEmpty(brandNew))
+            {
+                var brandNews = brandNew.Split(',');
+                brands.ForEach(t => { t.brandNew = brandNews.Contains(t.brandCode); });
+            }
+            return brands;
+        }
+
+
+        /// <summary>
+        /// QuerySingleOrDefaultAsync and QuerySingleOrDefaultAsync with shared connection.
+        /// </summary>
+        public async Task<object> ListFQA(JToken param)
+        {
+            using var conn = ConnectionManager.GetConnection();
+            var csi = ResourceManager.Csis["List_Common_Question"];
+            var questions = await base.QueryAsync<Question>(csi.code.ToString(), param, conn);
+
+            csi = ResourceManager.Csis["Get_CommonQuestion_Detail"];
+            var question1 = await base.QuerySingleOrDefaultAsync<Question>(csi.code.ToString(), new { id = 26 }, conn);
+            var question2 = await base.QuerySingleOrDefaultAsync<Question>(csi.code.ToString(), new { id = 0 }, conn);
+
+            return new { questions, question1, question2 };
+        }
+
+        public UserIdentity GetUserInfo(string username, string password)
+        {
+            if (username == "Dawson" && password == "123456")
+            {
+                return new UserIdentity { From = "Unknown", Id = "0", Name = username };
+            }
+
+            throw new LogicalException("Invalid username and password.");
+        }
+
+        public async Task<object> GetQuestionDetail(JToken param)
+        {
+            var response = await _rpcClient.InvokeRpcAsync(Context, "Wicture.DbRESTFul.Samples.MySqlService", "Get_Question_Detail", param);
+            var result = response.GetData<JObject>();
+
+            result["Identity"] = Context.Identity.Name;
+
+            return result;
+        }
+    }
+}

+ 14 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Repositories/TestRepository.cs

@@ -0,0 +1,14 @@
+using Newtonsoft.Json.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService.Repositories
+{
+    public class TestRepository : DbRESTFulRepository
+    {
+        public async Task<object> PostArray(JToken param)
+        {
+            return new { result = param, success = true };
+        }
+    }
+}

+ 37 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Service/UserIdentifier.cs

@@ -0,0 +1,37 @@
+using Autofac;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Authentication;
+using Wicture.DbRESTFul.Samples.MySqlService.Repositories;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService.Service
+{
+    public class UserIdentifier : IUserIdentifier
+    {
+        private ILogger _logger;
+
+        public UserIdentifier(ILoggerFactory loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger("DbRESTFulApi");
+        }
+
+        public async Task AuthorizeAsync(ExecutionContext context)
+        {
+            _logger.LogInformation($"Authorize {context.Api.name} access for {context.Identity.Name}.");
+            context.Identity.IsAuthenticated = true;
+            await Task.CompletedTask;
+        }
+
+        public async Task<UserIdentity> IdentifyAsync(string username, string password, JToken param)
+        {
+            _logger.LogDebug($"Login for `{username}` with `{password}`.");
+
+            using (var scope = AutofacContainer.Container.BeginLifetimeScope())
+            {
+                var repository = scope.Resolve<CommonRepository>();
+                return await Task.FromResult(repository.GetUserInfo(username, password));
+            }
+        }
+    }
+}

+ 43 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Startup.cs

@@ -0,0 +1,43 @@
+using Autofac;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService
+{
+    public class Startup
+    {
+        public Startup(IConfiguration configuration)
+        {
+            Configuration = configuration;
+        }
+
+        public IConfiguration Configuration { get; }
+
+        // This method gets called by the runtime. Use this method to add services to the container.
+        public void ConfigureServices(IServiceCollection services)
+        {
+            services.AddDbRESTFul();
+        }
+
+        public void ConfigureContainer(ContainerBuilder builder)
+        {
+            builder.RegisterDbRESTFul();
+        }
+
+        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+        {
+            if (env.IsDevelopment())
+            {
+                app.UseDeveloperExceptionPage();
+            }
+
+            app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
+
+            app.UseDbRESTFul(o => o.EnableRpc = true);
+        }
+    }
+}

+ 87 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Test/common.rest

@@ -0,0 +1,87 @@
+### 1. List_enums: csi single result set test.
+GET http://localhost:5000/api/common/enums?category=memberType HTTP/1.1
+content-type: application/json
+
+### 2. ListAreas: repository async method test.
+GET http://localhost:5000/api/common/areas HTTP/1.1
+content-type: application/json
+
+### 3. HelpFiles: repository sync method test.
+GET http://localhost:5000/api/common/help?type=ServiceDesc HTTP/1.1
+content-type: application/json
+
+### 4. ListAllBrand: repository InvokeAsync and InvokeSingleOrDefaultAsync with shared connection
+GET http://localhost:5000/api/common/brand/all HTTP/1.1
+content-type: application/json
+
+### 5. GetStudentInfo: controller routing test.
+GET http://localhost:5000/api/common/test/getstudent HTTP/1.1
+content-type: application/json
+
+### 6. WeatherForecast: controller routing (useAbsoluteUrl) test.
+GET http://localhost:5000/WeatherForecast HTTP/1.1
+content-type: application/json
+
+### 7. Auth: token request (success) test with specified UserIdentifier.
+POST http://localhost:5000/token HTTP/1.1
+content-type: application/json
+
+{
+    "username": "Dawson",
+    "password": "123456"
+}
+
+### 8. Auth: token request (failed) test with specified UserIdentifier.
+POST http://localhost:5000/token HTTP/1.1
+content-type: application/json
+
+{
+    "username": "user",
+    "password": "abc123"
+}
+
+### 9. ListCommonFeedback: authorization test.
+GET http://localhost:5000/api/common/feedback/list HTTP/1.1
+content-type: application/json
+Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjAiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiRGF3c29uIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiIiwianRpIjoiOWQ1MGUwNmMtZGI5MS00N2ExLTg3NTUtZTJhOWVlMjZkZjdlIiwiaWF0IjoxNTc0NzU5NjgzNjkwLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3VzZXJkYXRhIjoibnVsbCIsIm5iZiI6MTU3NDc1OTY4MywiZXhwIjoxNTc0Nzg4NDgzLCJpc3MiOiJXaWN0dXJlIiwiYXVkIjoiRGJSRVNURnVsQXBpIn0.zc-tCsfdOyNbOPDizvsM2PpgS_v3n88crEyZgIo_EZs
+
+### 10. ListFQA: test QueryAsync and QuerySingleOrDefaultAsync
+GET http://localhost:5000/api/common/fqa?pageSize=10&pageIndex=1 HTTP/1.1
+content-type: application/json
+Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJZCI6IjAiLCJOYW1lIjoiRGF3c29uIiwianRpIjoiMTk5M2RhNGItZTgwYy00OTE2LWEwNjctMzk1NGFhNWM4ZTA1IiwiaWF0IjoxNTc0OTE5OTEwNDUxLCJuYmYiOjE1NzQ5MTk5MTAsImV4cCI6MTU3NDk0ODcxMCwiaXNzIjoiV2ljdHVyZSIsImF1ZCI6IkRiUkVTVEZ1bEFwaSJ9.Lxw1_3ieXmRu9O3EkVgAtdHH0Lpfz7_r6LgH6nwzO3o
+
+### 11. GetFQADetail: test Rpc
+GET http://localhost:5000/api/common/fqa/detail?id=18 HTTP/1.1
+content-type: application/json
+
+### 12. GetFQADetailByRepository: test Rpc in Repository and with auth
+GET http://localhost:5000/api/common/fqa/detail/by/repository?id=18 HTTP/1.1
+content-type: application/json
+Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjAiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiRGF3c29uIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiIiwianRpIjoiYTRhZmM0ZDUtY2M0Ny00NmFmLWIwODAtNWMyNDE1NzkyNTFkIiwiaWF0IjoxNTc0ODI1MjU5ODE2LCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3VzZXJkYXRhIjoibnVsbCIsIm5iZiI6MTU3NDgyNTI1OSwiZXhwIjoxNTc0ODU0MDU5LCJpc3MiOiJXaWN0dXJlIiwiYXVkIjoiRGJSRVNURnVsQXBpIn0.zWQdCQ4c7IfhuX4EJ5l1JZWlQtX4GZyuEuxxODMXJTs
+
+### 13. GetFQADetailByRpcChian: test Rpc in Repository call another rpc
+GET http://localhost:5000/api/common/fqa/detail/by/rpc/chian?id=20 HTTP/1.1
+content-type: application/json
+Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJZCI6IjAiLCJOYW1lIjoiRGF3c29uIiwianRpIjoiZThiNTNjMDgtZWE1MC00YWIzLWE1ODMtYWY3YTY2MWE1NDBiIiwiaWF0IjoxNTc0ODM5MDI4MTk5LCJuYmYiOjE1NzQ4MzkwMjgsImV4cCI6MTU3NDg2NzgyOCwiaXNzIjoiV2ljdHVyZSIsImF1ZCI6IkRiUkVTVEZ1bEFwaSJ9.ybiTu-s_Jwngrel-O1l_xaCDaqUF1EIo5aQPRK2eSEI
+
+### 14. GetFQADetail: test Rpc Only (expect to throw exception)
+GET http://localhost:5000/api/common/fqa/detail/rpc?id=18 HTTP/1.1
+content-type: application/json
+
+### 15. ListQuestionsWithPagination: test Pagination Csi Middleware
+GET http://localhost:5000/api/common/list/questions?pageSize=10&pageIndex=1 HTTP/1.1
+content-type: application/json
+Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJZCI6IjAiLCJOYW1lIjoiRGF3c29uIiwianRpIjoiMTk5M2RhNGItZTgwYy00OTE2LWEwNjctMzk1NGFhNWM4ZTA1IiwiaWF0IjoxNTc0OTE5OTEwNDUxLCJuYmYiOjE1NzQ5MTk5MTAsImV4cCI6MTU3NDk0ODcxMCwiaXNzIjoiV2ljdHVyZSIsImF1ZCI6IkRiUkVTVEZ1bEFwaSJ9.Lxw1_3ieXmRu9O3EkVgAtdHH0Lpfz7_r6LgH6nwzO3o
+
+### 16. ListBrandCommonPart: Array Body parameters.
+GET http://localhost:5000/api/test/post/array HTTP/1.1
+content-type: application/json
+
+[{
+    "id": 1,
+    "age": 12,
+    "name": "abc"
+},{
+    "id": 2,
+    "name": "efg"
+}]

+ 15 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/WeatherForecast.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace Wicture.DbRESTFul.Samples.MySqlService
+{
+    public class WeatherForecast
+    {
+        public DateTime Date { get; set; }
+
+        public int TemperatureC { get; set; }
+
+        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+
+        public string Summary { get; set; }
+    }
+}

+ 17 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/Wicture.DbRESTFul.Samples.MySqlService.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Wicture.DbRESTFul\Wicture.DbRESTFul.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Folder Include="Test\" />
+  </ItemGroup>
+
+  <ProjectExtensions><VisualStudio><UserProperties appsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
+
+</Project>

+ 59 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/appsettings.json

@@ -0,0 +1,59 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft": "Warning",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  },
+  "AllowedHosts": "*",
+  "API": {
+    "ShowFullException": true,
+    "Path": "API/",
+    "LogConfig": {
+      "Exception": true,
+      "Event": true,
+      "Token": true,
+      "Identity": true,
+      "Param": true,
+      "Result": true
+    }
+  },
+  "CSI": {
+    "Path": "CSI/",
+    "DbConnection": {
+      "DatabaseType": "MySQL",
+      "CommandTimeout": 5000,
+      "ReadConnectionString": "server=localhost;port=3306;user=abc;password=123456;database=testdb;charset=utf8;convert zero datetime=True;Allow User Variables=True;SslMode=None",
+      "WriteConnectionString": "server=localhost;port=3306;user=abc;password=123456;database=testdb;charset=utf8;convert zero datetime=True;Allow User Variables=True;SslMode=None"
+    },
+    "LogConfig": {
+      "Code": true,
+      "Param": true,
+      "Result": false
+    }
+  },
+  "Rpc": {
+    "Clients": [
+      {
+        "ServiceName": "Wicture.DbRESTFul.Samples.MySqlService",
+        "ServerAddress": "localhost:50000"
+      }
+    ],
+    "Server": {
+      "LogResult": true,
+      "Enabled": true,
+      "ServiceName": "Wicture.DbRESTFul.Samples.MySqlService",
+      "ServiceGuid": "6FECA33E-EA05-4F48-AE85-9F627C4AD603",
+      "Host": "0.0.0.0",
+      "Port": 50000
+    }
+  },
+  "DevTool": {
+    "Enabled": true,
+    "SyncOnLoading": true,
+    "ProjectName": "Wicture.DbRESTFul.PMT",
+    "PMTServiceUrl": "http://localhost:5000/",
+    "Branch": "dev"
+  }
+}

+ 40 - 0
src/Samples/Wicture.DbRESTFul.Samples.MySqlService/nlog.config

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      autoReload="true"
+      internalLogLevel="Info"
+      internalLogFile="c:\temp\internal-nlog.txt">
+
+  <!-- enable asp.net core layout renderers -->
+  <extensions>
+    <add assembly="NLog.Web.AspNetCore"/>
+  </extensions>
+
+  <!-- the targets to write to -->
+  <targets>
+    <target name="console" xsi:type="Console"
+          layout="${longdate}|${level:uppercase=true}|${logger}|${message}"
+          error="true"
+          detectConsoleAvailable="true" />
+    
+    <!-- write logs to file  -->
+    <target xsi:type="File" name="allfile" fileName="nlog-all-${shortdate}.log"
+            layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
+
+    <!-- another file log, only own logs. Uses some ASP.NET core renderers -->
+    <target xsi:type="File" name="ownFile-web" fileName="nlog-own-${shortdate}.log"
+            layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
+  </targets>
+
+  <!-- rules to map from logger name to target -->
+  <rules>
+    <!--All logs, including from Microsoft-->
+    <logger name="*" minlevel="Trace" writeTo="allfile" />
+    <!--All logs, including from Microsoft-->
+    <logger name="*" minlevel="Trace" writeTo="console" />
+    <!--Skip non-critical Microsoft logs and so log only own logs-->
+    <logger name="Microsoft.*" maxlevel="Info" final="true" />
+    <!-- BlackHole without writeTo -->
+    <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
+  </rules>
+</nlog>

+ 98 - 0
src/Tests/Wicture.DbRESTFul.Core.Test/ConfigurationTest.cs

@@ -0,0 +1,98 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Cache;
+using Wicture.DbRESTFul.Configuration;
+using Wicture.DbRESTFul.Resources.Api;
+using Wicture.DbRESTFul.Resources.Csi;
+
+namespace Wicture.DbRESTFul.Core.Test
+{
+    [TestClass]
+    public class ConfigurationTest
+    {
+        [TestMethod]
+        public void TypeTest()
+        {
+            var t1 = typeof(Task<int>);
+            var t2 = typeof(Task);
+            var t3 = typeof(CsiModel);
+            var t4 = typeof(Task<CsiModel>);
+
+            Task<object> m = Convert(Task.FromResult(new CsiModel()));
+
+            Assert.AreEqual(t1.IsGenericType, true);
+        }
+
+        private static async Task<object> Convert(Task task)
+        {
+            await task;
+            var voidTaskType = typeof(Task<>).MakeGenericType(Type.GetType("System.Threading.Tasks.VoidTaskResult"));
+            if (voidTaskType.IsAssignableFrom(task.GetType()))
+                throw new InvalidOperationException("Task does not have a return value (" + task.GetType().ToString() + ")");
+            var property = task.GetType().GetProperty("Result", BindingFlags.Public | BindingFlags.Instance);
+            if (property == null)
+                throw new InvalidOperationException("Task does not have a return value (" + task.GetType().ToString() + ")");
+            return property.GetValue(task);
+        }
+
+
+
+        [TestMethod]
+        public void CsiModelTest()
+        {
+            var csiModel = new CsiModel() { code = new JValue("test") };
+            var infos = csiModel.ListResultSetInfo();
+            Assert.AreEqual(infos.Count, 1);
+            Assert.AreEqual(infos.FirstOrDefault().MultipleRecords, true);
+            Assert.AreEqual(infos.FirstOrDefault().Sql, "test");
+
+            csiModel = new CsiModel() { code = new JValue("test"), resultSet = "S" };
+            infos = csiModel.ListResultSetInfo();
+            Assert.AreEqual(infos.Count, 1);
+            Assert.AreEqual(infos.FirstOrDefault().MultipleRecords, false);
+            Assert.AreEqual(infos.FirstOrDefault().Sql, "test");
+
+
+            csiModel = new CsiModel() { code = JObject.FromObject(new { a = "sql1", b = "sql2" }), resultSet = "S" };
+            infos = csiModel.ListResultSetInfo();
+            Assert.AreEqual(infos.Count, 2);
+            Assert.AreEqual(infos.FirstOrDefault().MultipleRecords, false);
+            Assert.AreEqual(infos.FirstOrDefault().Sql, "sql1");
+
+            Assert.AreEqual(infos.LastOrDefault().MultipleRecords, true);
+            Assert.AreEqual(infos.LastOrDefault().Sql, "sql2");
+        }
+
+        [TestMethod]
+        public void ObjectSectionConfigTest()
+        {
+            //ConfigurationManager.Load();
+
+            //var apiConfig = ConfigurationManager.GetSectionConfig<ApiSectionConfig>();
+
+            //Assert.AreEqual(apiConfig.SectionName, "API");
+            //Assert.AreEqual(apiConfig.ShowFullException, true);
+            //Assert.AreEqual(apiConfig.Path, "API/");
+        }
+
+
+        [TestMethod]
+        public void ListSectionConfigTest()
+        {
+            //ConfigurationManager.Load();
+
+            //var config = ConfigurationManager.GetSectionConfig<CacheSectionConfig>();
+
+            //Assert.AreEqual(config.SectionName, "CacheConfigs");
+            //Assert.AreEqual(config.Items.Count, 2);
+            //Assert.AreEqual(config.Items[0].Type, "redis");
+            //Assert.AreEqual(config.Items[1].Type, "memcached");
+        }
+    }
+}

+ 28 - 0
src/Tests/Wicture.DbRESTFul.Core.Test/Wicture.DbRESTFul.Core.Test.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
+    <PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
+    <PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
+    <PackageReference Include="coverlet.collector" Version="1.0.1" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Wicture.DbRESTFul\Wicture.DbRESTFul.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="appsettings.json">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
+</Project>

+ 115 - 0
src/Tests/Wicture.DbRESTFul.Core.Test/appsettings.json

@@ -0,0 +1,115 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft": "Warning",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  },
+  "AllowedHosts": "*",
+
+  "API": {
+    "ShowFullException": true,
+    "Path": "API/",
+    "LogConfig": {
+      "Exception": true,
+      "Event": true,
+      "Token": true,
+      "Identity": true,
+      "Param": true,
+      "Result": true
+    }
+  },
+  "CSI": {
+    "Path": "CSI/",
+    "DatabaseType": "MySQL",
+    "CommandTimeout": 5000,
+    "ReadDbConnectionString": "server=db.91qpzs.com;port=3306;user=accesslogadmin;password=Iale933MIeqPKLAd;database=qpzs_access_logs;charset=utf8;convert zero datetime=True;",
+    "WriteDbConnectionString": "server=db.91qpzs.com;port=3306;user=accesslogadmin;password=Iale933MIeqPKLAd;database=qpzs_access_logs;charset=utf8;convert zero datetime=True;",
+    "LogConfig": {
+      "Event": true,
+      "Code": false,
+      "Param": false,
+      "Result": false
+    }
+  },
+  "Rpc": {
+    "Enabled": false
+  },
+  "CacheConfigs": [
+    {
+      "Type": "redis",
+      "Config": {
+        "ConnectionString": "cluster1.91qpzs.com:6333,password=zRGEdH69LM7OZXG4,defaultDatabase=0,poolsize=10,ssl=false,writeBuffer=10240;cluster2.91qpzs.com:6333,password=zRGEdH69LM7OZXG4,defaultDatabase=0,poolsize=10,ssl=false,writeBuffer=10240;cluster3.91qpzs.com:6333,password=zRGEdH69LM7OZXG4,defaultDatabase=0,poolsize=10,ssl=false,writeBuffer=10240;"
+      }
+    },
+    {
+      "Type": "memcached",
+      "Config": {
+        "Address": "cluster1.91qpzs.com",
+        "Port": 10010
+      }
+    }
+  ],
+  "TokenProviderOptions": {
+    "SecretKey": "ekGgU7a96TQcx^4k&O&guGIniFD",
+    "Audience": "Microservice",
+    "Issuer": "Wicture",
+    "Expiration": "24:00:00",
+    "UseJwtCookie": false
+  },
+  "NLogConfig": [
+    {
+      "Target": {
+        "Name": "ApiLog",
+        "FileName": "${basedir}/log/api-${shortdate}.log",
+        "Layout": "${longdate}|${level}|${logger}|${message} ${exception}"
+      },
+      "Rule": {
+        "MinLevel": "Info",
+        "MaxLevel": "Fatal",
+        "LoggerNamePattern": "DbRESTFul",
+        "Final": true
+      }
+    }
+  ],
+  "ScheduleService": {
+    "Enabled": true,
+    "IntervalInMinutes": 1
+  },
+  "DevConfig": {
+    "Enabled": true,
+    "SyncOnLoading": true,
+    "ProjectName": "Wicture.Microservice.LogVision",
+    "PMTServiceUrl": "http://backend.wicture.com:8680"
+  },
+  "Consul": {
+    "Address": "http://localhost:8680"
+  },
+  //"LogVision": {
+  //  "TopicNamePrefix": "Wicture.Microservice.AccessLog",
+  //  "BufferSize": 3,
+  //  "FlushInterval": "00:01:00",
+  //  "IgnoredApiPaths": [],
+  //  "IgnoredCsiNames": [ "List_Model_Catalog", "Get_Model_Config" ]
+  //},
+  //"KafkaClientConfig": {
+  //  "BootstrapServers": "backend.wicture.com:8866"
+  //},
+  "GeelyOrLynkcoMaliciousSetting": {
+    "Enabled": true,
+    "StartMemberId": 10410
+  },
+  "UserForbiddenSettings": [
+    {
+      "Origins": [ "91autoparts", "" ],
+      "ServiceUrl": "https://service.91autoparts.com/api/common/member/disable?memberId=#ID#&key=AeQr4P3M&reason=#REASON#",
+      "HttpMethod": "GET",
+      "IdPlaceHolder": "#ID#",
+      "ReasonPlaceHolder": "#REASON#"
+    }
+  ],
+  "Variables": {
+    "RedisConnectionString": "172.19.229.23:6333,password=zRGEdH69LM7OZXG4,defaultDatabase=3,poolsize=10,ssl=false,writeBuffer=10240"
+  }
+}

BIN
src/Tools/.DS_Store


+ 3 - 0
src/Tools/Wicture.DbRESTFul.PMT/.bowerrc

@@ -0,0 +1,3 @@
+{
+  "directory": "wwwroot/lib"
+}

+ 67 - 0
src/Tools/Wicture.DbRESTFul.PMT/API/api.json

@@ -0,0 +1,67 @@
+[
+  {
+    "version": "1.0.0",
+    "owner": "chengong",
+    "updatedTime": "2016-12-26T11:35:43",
+    "name": "CheckAPINames",
+    "module": "api",
+    "url": "defaultapi/check",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "查询API名称是否存在",
+    "summary": null,
+    "note": "为了检查生成默认接口提示使用",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultApiRepository.CheckExistName"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "object",
+          "name": "apiData"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)"
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息"
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": true,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  }
+]

+ 1090 - 0
src/Tools/Wicture.DbRESTFul.PMT/API/csi.json

@@ -0,0 +1,1090 @@
+[
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-24T16:45:10",
+    "name": "ListAllCSI",
+    "module": "csi",
+    "url": "list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取所有CSI",
+    "summary": "列出所有的CSI信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "QueryCSI"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "keyword",
+          "type": "string",
+          "nullable": true,
+          "description": "名称搜索关键字",
+          "schema": null
+        },
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "每页显示几条",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "当前第几页",
+          "schema": null
+        },
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "项目标识"
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "CSI编号",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI描述",
+              "schema": null
+            },
+            {
+              "name": "code",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI格式化代码",
+              "schema": null
+            },
+            {
+              "name": "rawCode",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI编辑源码代码",
+              "schema": null
+            },
+            {
+              "name": "resultSet",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI返回结果集类型",
+              "schema": null
+            },
+            {
+              "name": "queryOnly",
+              "type": "bool",
+              "nullable": true,
+              "description": "是否只读",
+              "schema": null
+            },
+            {
+              "name": "requiredTransaction",
+              "type": "bool",
+              "nullable": false,
+              "description": "是否启用事务",
+              "schema": null
+            },
+            {
+              "name": "middleWares",
+              "type": "string",
+              "nullable": false,
+              "description": "CSI中间件"
+            },
+            {
+              "name": "createdBy",
+              "type": "int",
+              "nullable": false,
+              "description": "创建人标识"
+            },
+            {
+              "name": "createdByName",
+              "type": "string",
+              "nullable": false,
+              "description": "创建人名称"
+            },
+            {
+              "name": "updatedBy",
+              "type": "int",
+              "nullable": false,
+              "description": "修改人标识"
+            },
+            {
+              "name": "updatedByName",
+              "type": "string",
+              "nullable": false,
+              "description": "修改人名称"
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-24T17:56:42",
+    "name": "ImportCSI",
+    "module": "csi",
+    "url": "import",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "导入CSI",
+    "summary": "导入CSI定义,一般是json文件。",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "ImportRepository.ImportCSI"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "项目标识"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "成功导入的数量"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-28T16:52:40",
+    "name": "GetCSI",
+    "module": "csi",
+    "url": "get",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取CSI信息",
+    "summary": "获取CSI信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "GetCSI"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "id",
+          "description": "CSI标识"
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "CSI编号",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI描述",
+              "schema": null
+            },
+            {
+              "name": "code",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI格式化代码",
+              "schema": null
+            },
+            {
+              "name": "rawCode",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI编辑源码代码",
+              "schema": null
+            },
+            {
+              "name": "resultSet",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI返回结果集类型",
+              "schema": null
+            },
+            {
+              "name": "queryOnly",
+              "type": "bool",
+              "nullable": true,
+              "description": "是否只读",
+              "schema": null
+            },
+            {
+              "name": "requiredTransaction",
+              "type": "bool",
+              "nullable": false,
+              "description": "是否启用事务",
+              "schema": null
+            },
+            {
+              "name": "middleWares",
+              "type": "string",
+              "nullable": false,
+              "description": "CSI中间件"
+            },
+            {
+              "name": "createdBy",
+              "type": "int",
+              "nullable": false,
+              "description": "创建人标识"
+            },
+            {
+              "name": "createdByName",
+              "type": "string",
+              "nullable": false,
+              "description": "创建人名称"
+            },
+            {
+              "name": "updatedBy",
+              "type": "int",
+              "nullable": false,
+              "description": "修改人标识"
+            },
+            {
+              "name": "updatedByName",
+              "type": "string",
+              "nullable": false,
+              "description": "修改人名称"
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-30T13:41:58",
+    "name": "ListOwnerCSI",
+    "module": "csi",
+    "url": "owner/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取所有当前用户的CSI",
+    "summary": "列出当前用户的CSI信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListOwnerCSI"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "keyword",
+          "type": "string",
+          "nullable": true,
+          "description": "名称搜索关键字",
+          "schema": null
+        },
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "每页显示几条",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "当前第几页",
+          "schema": null
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "CSI编号",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI描述",
+              "schema": null
+            },
+            {
+              "name": "code",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI格式化代码",
+              "schema": null
+            },
+            {
+              "name": "rawCode",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI编辑源码代码",
+              "schema": null
+            },
+            {
+              "name": "resultSet",
+              "type": "string",
+              "nullable": true,
+              "description": "CSI返回结果集类型",
+              "schema": null
+            },
+            {
+              "name": "queryOnly",
+              "type": "bool",
+              "nullable": true,
+              "description": "是否只读",
+              "schema": null
+            },
+            {
+              "name": "requiredTransaction",
+              "type": "bool",
+              "nullable": false,
+              "description": "是否启用事务",
+              "schema": null
+            },
+            {
+              "name": "middleWares",
+              "type": "string",
+              "nullable": false,
+              "description": "CSI中间件"
+            },
+            {
+              "name": "createdBy",
+              "type": "int",
+              "nullable": false,
+              "description": "创建人标识"
+            },
+            {
+              "name": "createdByName",
+              "type": "string",
+              "nullable": false,
+              "description": "创建人名称"
+            },
+            {
+              "name": "updatedBy",
+              "type": "int",
+              "nullable": false,
+              "description": "修改人标识"
+            },
+            {
+              "name": "updatedByName",
+              "type": "string",
+              "nullable": false,
+              "description": "修改人名称"
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2017-01-05T16:58:41",
+    "name": "CreateCSI",
+    "module": "csi",
+    "url": "create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建CSI信息",
+    "summary": "创建CSI信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "CreateCSI"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "项目编号"
+        },
+        {
+          "type": "string",
+          "name": "name"
+        },
+        {
+          "type": "string",
+          "name": "code"
+        },
+        {
+          "type": "string",
+          "name": "rawCode"
+        },
+        {
+          "type": "string",
+          "name": "resultSet"
+        },
+        {
+          "type": "bool",
+          "name": "queryOnly"
+        },
+        {
+          "type": "bool",
+          "name": "requiredTransaction"
+        },
+        {
+          "type": "string",
+          "name": "middleWares"
+        },
+        {
+          "type": "string",
+          "name": "module"
+        },
+        {
+          "type": "int",
+          "name": "moduleId"
+        },
+        {
+          "type": "bool",
+          "name": "isCodeObject"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "id"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "projectId": "*",
+          "name": "*",
+          "code": "*",
+          "rawCode": "*",
+          "resultSet": "*",
+          "queryOnly": "*",
+          "requiredTransaction": "*",
+          "middleWares": "*",
+          "createdBy": "*",
+          "createdByName": "*",
+          "updatedBy": "*",
+          "updatedByName": "*",
+          "updatedTime": "*",
+          "module": "*",
+          "moduleId": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "id": ""
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2019-12-13T21:55:39",
+    "name": "UpdateCSI",
+    "module": "csi",
+    "url": "update",
+    "useAbsoluteUrl": false,
+    "method": "PUT",
+    "title": "更新CSI信息",
+    "summary": "更新CSI信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "UpdateCSI"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "int",
+          "name": "id"
+        },
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "项目编号"
+        },
+        {
+          "type": "string",
+          "name": "name"
+        },
+        {
+          "type": "string",
+          "name": "code"
+        },
+        {
+          "type": "string",
+          "name": "rawCode"
+        },
+        {
+          "type": "string",
+          "name": "resultSet"
+        },
+        {
+          "type": "bool",
+          "name": "queryOnly"
+        },
+        {
+          "type": "bool",
+          "name": "requiredTransaction"
+        },
+        {
+          "type": "string",
+          "name": "middleWares"
+        },
+        {
+          "type": "string",
+          "name": "module"
+        },
+        {
+          "type": "int",
+          "name": "moduleId"
+        },
+        {
+          "type": "bool",
+          "name": "isCodeObject"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "id"
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "id": "*",
+          "projectId": "*",
+          "name": "*",
+          "code": "*",
+          "rawCode": "*",
+          "resultSet": "*",
+          "queryOnly": "*",
+          "requiredTransaction": "*",
+          "middleWares": "*",
+          "createdBy": "*",
+          "createdByName": "*",
+          "updatedBy": "*",
+          "updatedByName": "*",
+          "updatedTime": "*",
+          "module": "*",
+          "moduleId": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "count": ""
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-09T16:23:57",
+    "name": "ExecuteCode",
+    "module": "csi",
+    "url": "exec",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "执行CSI代码",
+    "summary": "执行CSI代码",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "CSIRepository.ExecuteCode"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "ProjectId"
+        },
+        {
+          "type": "string",
+          "name": "code",
+          "description": "待执行的csi的code(sql代码)"
+        },
+        {
+          "type": "string",
+          "name": "params",
+          "description": "参数(json格式)"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "projectId": "*",
+          "code": "*",
+          "params": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {}
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-27T17:28:48",
+    "name": "CheckCSINameExsits",
+    "module": "csi",
+    "url": "check",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "检测CSI名称是否存在",
+    "summary": "检测CSI名称是否存在",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "CheckCSINameExsits"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": false,
+          "description": "项目标识",
+          "schema": null
+        },
+        {
+          "type": "string",
+          "name": "name",
+          "description": "CSI名称"
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口编号",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "projectId": "*",
+          "name": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "id": "2"
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-27T19:23:54",
+    "name": "DeleteCSI",
+    "module": "csi",
+    "url": "delete",
+    "useAbsoluteUrl": false,
+    "method": "DELETE",
+    "title": "删除CSI",
+    "summary": "删除CSI",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "DeleteCSI"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "string",
+          "name": "ids",
+          "description": "删除的id号"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "ids": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {}
+        }
+      }
+    ]
+  }
+]

+ 3304 - 0
src/Tools/Wicture.DbRESTFul.PMT/API/pmt.json

@@ -0,0 +1,3304 @@
+[
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2017-01-23T13:45:04",
+    "name": "ListAllApis",
+    "module": "pmt",
+    "url": "ca/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取所有接口",
+    "summary": "列出所有的接口信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "QueryApis"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": false,
+          "description": "所属项目",
+          "schema": null
+        },
+        {
+          "name": "keyword",
+          "type": "string",
+          "nullable": true,
+          "description": "名称搜索关键字",
+          "schema": null
+        },
+        {
+          "name": "module",
+          "type": "string",
+          "nullable": true,
+          "description": "模块名称",
+          "schema": null
+        },
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "每页显示几条",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "当前第几页",
+          "schema": null
+        },
+        {
+          "type": "string",
+          "name": "orderBy",
+          "nullable": true,
+          "description": "排序"
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "description",
+              "type": "string",
+              "nullable": true,
+              "description": "接口描述",
+              "schema": null
+            },
+            {
+              "name": "createdBy",
+              "type": "string",
+              "nullable": true,
+              "description": "创建人",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "keyword": "*",
+          "pageSize": "*",
+          "pageIndex": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "items": [
+              {
+                "id": 1,
+                "projectId": 1,
+                "name": "PuTuoShan",
+                "module": "普陀山统一信息发布平台",
+                "title": "刘合桃",
+                "summary": "普陀山统一信息发布平台",
+                "version": "刘合桃",
+                "url": "PuTuoShan",
+                "method": "普陀山统一信息发布平台",
+                "owner": "刘合桃",
+                "note": "PuTuoShan",
+                "implemented": "普陀山统一信息发布平台",
+                "implementation": "刘合桃",
+                "parameter": "PuTuoShan",
+                "result": "普陀山统一信息发布平台",
+                "mock": "刘合桃",
+                "updatedTime": 1452775192
+              }
+            ],
+            "pagination": {
+              "size": 10,
+              "count": 23,
+              "index": 2
+            }
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T22:19:28",
+    "name": "GetApi",
+    "module": "pmt",
+    "url": "ca/get",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取指定接口",
+    "summary": "获取指定接口信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "GetApi"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": true,
+          "description": "接口标示",
+          "schema": null
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口编号",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "version",
+              "type": "string",
+              "nullable": true,
+              "description": "接口版本",
+              "schema": null
+            },
+            {
+              "name": "owner",
+              "type": "string",
+              "nullable": true,
+              "description": "接口负责人",
+              "schema": null
+            },
+            {
+              "name": "module",
+              "type": "string",
+              "nullable": true,
+              "description": "所属模块",
+              "schema": null
+            },
+            {
+              "name": "url",
+              "type": "string",
+              "nullable": true,
+              "description": "接口访问地址",
+              "schema": null
+            },
+            {
+              "name": "method",
+              "type": "string",
+              "nullable": true,
+              "description": "接口请求方法",
+              "schema": null
+            },
+            {
+              "name": "title",
+              "type": "string",
+              "nullable": true,
+              "description": "接口标题",
+              "schema": null
+            },
+            {
+              "name": "summary",
+              "type": "string",
+              "nullable": true,
+              "description": "接口简述",
+              "schema": null
+            },
+            {
+              "name": "note",
+              "type": "string",
+              "nullable": true,
+              "description": "接口备注",
+              "schema": null
+            },
+            {
+              "name": "implemented",
+              "type": "bool",
+              "nullable": true,
+              "description": "是否已实现",
+              "schema": null
+            },
+            {
+              "name": "implementation",
+              "type": "string",
+              "nullable": true,
+              "description": "实现代码",
+              "schema": null
+            },
+            {
+              "name": "parameter",
+              "type": "string",
+              "nullable": true,
+              "description": "接口调用参数",
+              "schema": null
+            },
+            {
+              "name": "result",
+              "type": "string",
+              "nullable": true,
+              "description": "接口返回结果",
+              "schema": null
+            },
+            {
+              "name": "mock",
+              "type": "string",
+              "nullable": true,
+              "description": "接口模拟数据",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "keyword": "*",
+          "pageSize": "*",
+          "pageIndex": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "id": 1,
+            "projectId": 1,
+            "name": "PuTuoShan",
+            "module": "普陀山统一信息发布平台",
+            "title": "刘合桃",
+            "summary": "普陀山统一信息发布平台",
+            "version": "刘合桃",
+            "url": "PuTuoShan",
+            "method": "普陀山统一信息发布平台",
+            "owner": "刘合桃",
+            "note": "PuTuoShan",
+            "implemented": "普陀山统一信息发布平台",
+            "implementation": "刘合桃",
+            "parameter": "PuTuoShan",
+            "result": "普陀山统一信息发布平台",
+            "mock": "刘合桃",
+            "updatedTime": 1452775192
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2019-12-09T11:28:27",
+    "name": "CreateApi",
+    "module": "pmt",
+    "url": "ca/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建接口",
+    "summary": "创建接口信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "CreateApi"
+    },
+    "parameter": {
+      "query": null,
+      "body": [
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": false,
+          "description": "项目编号",
+          "schema": null
+        },
+        {
+          "name": "name",
+          "type": "string",
+          "nullable": false,
+          "description": "接口名称",
+          "schema": null
+        },
+        {
+          "name": "version",
+          "type": "string",
+          "nullable": true,
+          "description": "接口版本",
+          "schema": null
+        },
+        {
+          "name": "owner",
+          "type": "string",
+          "nullable": true,
+          "description": "接口负责人",
+          "schema": null
+        },
+        {
+          "name": "module",
+          "type": "string",
+          "nullable": true,
+          "description": "所属模块",
+          "schema": null
+        },
+        {
+          "name": "url",
+          "type": "string",
+          "nullable": true,
+          "description": "接口访问地址",
+          "schema": null
+        },
+        {
+          "name": "method",
+          "type": "string",
+          "nullable": true,
+          "description": "接口请求方法",
+          "schema": null
+        },
+        {
+          "name": "title",
+          "type": "string",
+          "nullable": true,
+          "description": "接口标题",
+          "schema": null
+        },
+        {
+          "name": "summary",
+          "type": "string",
+          "nullable": true,
+          "description": "接口简述",
+          "schema": null
+        },
+        {
+          "name": "note",
+          "type": "string",
+          "nullable": true,
+          "description": "接口备注",
+          "schema": null
+        },
+        {
+          "name": "implemented",
+          "type": "bool",
+          "nullable": true,
+          "description": "是否已实现",
+          "schema": null
+        },
+        {
+          "name": "implementation",
+          "type": "string",
+          "nullable": true,
+          "description": "实现代码",
+          "schema": null
+        },
+        {
+          "name": "parameter",
+          "type": "string",
+          "nullable": true,
+          "description": "接口调用参数",
+          "schema": null
+        },
+        {
+          "name": "result",
+          "type": "string",
+          "nullable": true,
+          "description": "接口返回结果",
+          "schema": null
+        },
+        {
+          "name": "mock",
+          "type": "string",
+          "nullable": true,
+          "description": "接口模拟数据",
+          "schema": null
+        },
+        {
+          "name": "protocol",
+          "type": "string",
+          "nullable": false,
+          "description": ""
+        },
+        {
+          "name": "deprecated",
+          "type": "bool",
+          "nullable": false,
+          "description": ""
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "操作影响行数",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2019-12-09T11:29:57",
+    "name": "UpdateApi",
+    "module": "pmt",
+    "url": "ca/update",
+    "useAbsoluteUrl": false,
+    "method": "PUT",
+    "title": "更新指定接口",
+    "summary": "更新指定接口信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "UpdateApi"
+    },
+    "parameter": {
+      "query": null,
+      "body": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "接口编号",
+          "schema": null
+        },
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": false,
+          "description": "项目编号",
+          "schema": null
+        },
+        {
+          "name": "name",
+          "type": "string",
+          "nullable": false,
+          "description": "接口名称",
+          "schema": null
+        },
+        {
+          "name": "version",
+          "type": "string",
+          "nullable": true,
+          "description": "接口版本",
+          "schema": null
+        },
+        {
+          "name": "owner",
+          "type": "string",
+          "nullable": true,
+          "description": "接口负责人",
+          "schema": null
+        },
+        {
+          "name": "module",
+          "type": "string",
+          "nullable": true,
+          "description": "所属模块",
+          "schema": null
+        },
+        {
+          "name": "url",
+          "type": "string",
+          "nullable": true,
+          "description": "接口访问地址",
+          "schema": null
+        },
+        {
+          "name": "method",
+          "type": "string",
+          "nullable": true,
+          "description": "接口请求方法",
+          "schema": null
+        },
+        {
+          "name": "title",
+          "type": "string",
+          "nullable": true,
+          "description": "接口标题",
+          "schema": null
+        },
+        {
+          "name": "summary",
+          "type": "string",
+          "nullable": true,
+          "description": "接口简述",
+          "schema": null
+        },
+        {
+          "name": "note",
+          "type": "string",
+          "nullable": true,
+          "description": "接口备注",
+          "schema": null
+        },
+        {
+          "name": "implemented",
+          "type": "bool",
+          "nullable": true,
+          "description": "是否已实现",
+          "schema": null
+        },
+        {
+          "name": "implementation",
+          "type": "string",
+          "nullable": true,
+          "description": "实现代码",
+          "schema": null
+        },
+        {
+          "name": "parameter",
+          "type": "string",
+          "nullable": true,
+          "description": "接口调用参数",
+          "schema": null
+        },
+        {
+          "name": "result",
+          "type": "string",
+          "nullable": true,
+          "description": "接口返回结果",
+          "schema": null
+        },
+        {
+          "name": "mock",
+          "type": "string",
+          "nullable": true,
+          "description": "接口模拟数据",
+          "schema": null
+        },
+        {
+          "name": "protocol",
+          "type": "string",
+          "nullable": true,
+          "description": "支持的协议"
+        },
+        {
+          "name": "deprecated",
+          "type": "bool",
+          "nullable": true,
+          "description": "是否弃用"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "操作影响行数",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "id": "*",
+          "projectId": "*",
+          "name": "*",
+          "module": "*",
+          "title": "*",
+          "summary": "*",
+          "version": "*",
+          "url": "*",
+          "method": "*",
+          "owner": "*",
+          "note": "*",
+          "implemented": "*",
+          "implementation": "*",
+          "parameter": "*",
+          "result": "*",
+          "mock": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "count": 1
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T22:19:28",
+    "name": "DeleteApi",
+    "module": "pmt",
+    "url": "ca/delete",
+    "useAbsoluteUrl": false,
+    "method": "DELETE",
+    "title": "删除指定接口",
+    "summary": "删除指定接口信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "DeleteApi"
+    },
+    "parameter": {
+      "query": null,
+      "body": [
+        {
+          "name": "ids",
+          "type": "string",
+          "nullable": false,
+          "description": "要删除的接口编号列表(以`,`分隔)",
+          "schema": null
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "操作影响行数",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "ids": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "count": 1
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1",
+    "owner": "dawson",
+    "updatedTime": "2017-01-08T20:45:51",
+    "name": "ListModules",
+    "module": "pmt",
+    "url": "ca/module/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取模块列表",
+    "summary": "获取项目中所有Api的模块列表",
+    "note": "获取项目中所有Api的模块列表",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListModules"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "projectId"
+        },
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "页数"
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "页码"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "items",
+              "type": "array",
+              "nullable": false,
+              "description": "模块列表",
+              "schema": [
+                {
+                  "name": "id",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "模块编号"
+                },
+                {
+                  "name": "name",
+                  "type": "string",
+                  "nullable": false,
+                  "description": "模块名称"
+                },
+                {
+                  "name": "description",
+                  "type": "string",
+                  "nullable": false,
+                  "description": "描述"
+                },
+                {
+                  "name": "projectId",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "项目标识"
+                }
+              ]
+            },
+            {
+              "name": "pagination",
+              "type": "object",
+              "nullable": false,
+              "description": "分页",
+              "schema": [
+                {
+                  "name": "totalCount",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "总记录"
+                },
+                {
+                  "name": "pageSize",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "每页条数"
+                },
+                {
+                  "name": "pageIndex",
+                  "type": "int",
+                  "nullable": false,
+                  "description": "当前第几页"
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "projectId": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": [
+            {
+              "name": "a"
+            },
+            {
+              "name": "b"
+            }
+          ]
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-04T14:31:20",
+    "name": "ListUsers",
+    "module": "pmt",
+    "url": "user/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取所有用户",
+    "summary": "列出所有的用户信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "QueryUsers"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "页数"
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "页码"
+        },
+        {
+          "type": "string",
+          "name": "keyword",
+          "description": "关键字",
+          "nullable": true
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "用户信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "用户标识",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "用户账号",
+              "schema": null
+            },
+            {
+              "name": "nickname",
+              "type": "string",
+              "nullable": false,
+              "description": "用户名称",
+              "schema": null
+            },
+            {
+              "name": "enabled",
+              "type": "bit",
+              "nullable": false,
+              "description": "用户是否启用",
+              "schema": null
+            },
+            {
+              "name": "privilege",
+              "type": "int",
+              "nullable": false,
+              "description": "权限等级",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "更新时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T23:04:01",
+    "name": "GetUser",
+    "module": "pmt",
+    "url": "user/get",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取指定用户",
+    "summary": "列出指定用户信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "GetUser"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "用户标识",
+          "schema": null
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "用户信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "用户标识",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "用户账号",
+              "schema": null
+            },
+            {
+              "name": "nickname",
+              "type": "string",
+              "nullable": false,
+              "description": "用户名称",
+              "schema": null
+            },
+            {
+              "name": "enabled",
+              "type": "bit",
+              "nullable": false,
+              "description": "用户是否启用",
+              "schema": null
+            },
+            {
+              "name": "privilege",
+              "type": "int",
+              "nullable": false,
+              "description": "权限等级",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "更新时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-04T15:44:46",
+    "name": "CreateUser",
+    "module": "pmt",
+    "url": "user/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建用户",
+    "summary": "创建的用户信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "UserRepository.CreateUser"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "name",
+          "type": "string",
+          "nullable": false,
+          "description": "用户账号",
+          "schema": null
+        },
+        {
+          "name": "password",
+          "type": "string",
+          "nullable": false,
+          "description": "用户密码",
+          "schema": null
+        },
+        {
+          "name": "nickname",
+          "type": "string",
+          "nullable": false,
+          "description": "用户名称",
+          "schema": null
+        },
+        {
+          "name": "enabled",
+          "type": "bit",
+          "nullable": false,
+          "description": "用户是否启用",
+          "schema": null
+        },
+        {
+          "name": "privilege",
+          "type": "int",
+          "nullable": false,
+          "description": "权限等级",
+          "schema": null
+        },
+        {
+          "type": "int",
+          "name": "roleId",
+          "description": "角色ID"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "int",
+          "nullable": true,
+          "description": "用户标识",
+          "schema": null
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "name": "*",
+          "password": "*",
+          "nickname": "*",
+          "enabled": "*",
+          "privilege": "*",
+          "roleId": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {}
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-04T16:45:08",
+    "name": "UpdateUser",
+    "module": "pmt",
+    "url": "user/update",
+    "useAbsoluteUrl": false,
+    "method": "PUT",
+    "title": "修改用户",
+    "summary": "修改的用户信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "UserRepository.UpdateUser"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "用户标识",
+          "schema": null
+        },
+        {
+          "name": "name",
+          "type": "string",
+          "nullable": false,
+          "description": "用户账号",
+          "schema": null
+        },
+        {
+          "name": "password",
+          "type": "string",
+          "nullable": true,
+          "description": "用户密码",
+          "schema": null
+        },
+        {
+          "name": "nickname",
+          "type": "string",
+          "nullable": false,
+          "description": "用户名称",
+          "schema": null
+        },
+        {
+          "name": "enabled",
+          "type": "bit",
+          "nullable": false,
+          "description": "用户是否启用",
+          "schema": null
+        },
+        {
+          "name": "privilege",
+          "type": "int",
+          "nullable": false,
+          "description": "权限等级",
+          "schema": null
+        },
+        {
+          "type": "int",
+          "name": "roleId",
+          "description": "角色ID"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "string",
+          "nullable": true,
+          "description": "无",
+          "schema": null
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T23:04:11",
+    "name": "ListAllProjects",
+    "module": "pmt",
+    "url": "project/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取所有项目",
+    "summary": "列出所有的项目信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "QueryProjects"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "keyword",
+          "type": "string",
+          "nullable": true,
+          "description": "名称搜索关键字",
+          "schema": null
+        },
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "每页显示几条",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "当前第几页",
+          "schema": null
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "项目信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "项目名称",
+              "schema": null
+            },
+            {
+              "name": "description",
+              "type": "string",
+              "nullable": true,
+              "description": "项目描述",
+              "schema": null
+            },
+            {
+              "name": "createdBy",
+              "type": "string",
+              "nullable": true,
+              "description": "创建人",
+              "schema": null
+            },
+            {
+              "name": "connectionString",
+              "type": "string",
+              "nullable": true,
+              "description": "数据库链接字符串",
+              "schema": null
+            },
+            {
+              "name": "introduction",
+              "type": "string",
+              "nullable": true,
+              "description": "项目简介",
+              "schema": null
+            },
+            {
+              "name": "intromarkdown",
+              "type": "string",
+              "nullable": true,
+              "description": "项目简介",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "keyword": "*",
+          "pageSize": "*",
+          "pageIndex": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "items": [
+              {
+                "id": 1,
+                "name": "PuTuoShan",
+                "description": "普陀山统一信息发布平台",
+                "createdBy": "刘合桃",
+                "connectionString": "server=wicture.com;user=root;password=Wicture@123;database=pmt;charset=utf8;convert zero datetime=True;",
+                "introduction": "统一信息发布平台。。。。。",
+                "intromarkdown": "统一信息发布平台。。。。。",
+                "updatedTime": 1452775192
+              },
+              {
+                "id": 2,
+                "name": "PuTuoShan",
+                "description": "普陀山统一信息发布平台",
+                "createdBy": "刘合桃",
+                "connectionString": "server=wicture.com;user=root;password=Wicture@123;database=pmt;charset=utf8;convert zero datetime=True;",
+                "introduction": "统一信息发布平台。。。。。",
+                "intromarkdown": "统一信息发布平台。。。。。",
+                "updatedTime": 1452775192
+              },
+              {
+                "id": 3,
+                "name": "PuTuoShan",
+                "description": "普陀山统一信息发布平台",
+                "createdBy": "刘合桃",
+                "connectionString": "server=wicture.com;user=root;password=Wicture@123;database=pmt;charset=utf8;convert zero datetime=True;",
+                "introduction": "统一信息发布平台。。。。。",
+                "intromarkdown": "统一信息发布平台。。。。。",
+                "updatedTime": 1452775192
+              }
+            ],
+            "pagination": {
+              "size": 10,
+              "count": 23,
+              "index": 2
+            }
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T23:04:11",
+    "name": "GetProject",
+    "module": "pmt",
+    "url": "project/get",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取指定项目",
+    "summary": "获取指定项目信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "GetProject"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": true,
+          "description": "项目标示",
+          "schema": null
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "项目信息数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "项目名称",
+              "schema": null
+            },
+            {
+              "name": "description",
+              "type": "string",
+              "nullable": true,
+              "description": "项目描述",
+              "schema": null
+            },
+            {
+              "name": "createdBy",
+              "type": "string",
+              "nullable": true,
+              "description": "创建人",
+              "schema": null
+            },
+            {
+              "name": "connectionString",
+              "type": "string",
+              "nullable": true,
+              "description": "数据库链接字符串",
+              "schema": null
+            },
+            {
+              "name": "introduction",
+              "type": "string",
+              "nullable": true,
+              "description": "项目简介",
+              "schema": null
+            },
+            {
+              "name": "intromarkdown",
+              "type": "string",
+              "nullable": true,
+              "description": "项目简介",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "keyword": "*",
+          "pageSize": "*",
+          "pageIndex": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "id": 1,
+            "name": "PuTuoShan",
+            "description": "普陀山统一信息发布平台",
+            "createdBy": "刘合桃",
+            "connectionString": "server=wicture.com;user=root;password=Wicture@123;database=pmt;charset=utf8;convert zero datetime=True;",
+            "introduction": "统一信息发布平台。。。。。",
+            "intromarkdown": "统一信息发布平台。。。。。",
+            "updatedTime": 1452775192
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T23:04:11",
+    "name": "CreateProject",
+    "module": "pmt",
+    "url": "project/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建项目",
+    "summary": "创建新的项目",
+    "note": "创建项目信息的接口。",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "CreateProject"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "name",
+          "type": "string",
+          "nullable": false,
+          "description": "项目名称",
+          "schema": null
+        },
+        {
+          "name": "description",
+          "type": "string",
+          "nullable": false,
+          "description": "项目描述",
+          "schema": null
+        },
+        {
+          "name": "createdBy",
+          "type": "string",
+          "nullable": false,
+          "description": "创建人",
+          "schema": null
+        },
+        {
+          "name": "connectionString",
+          "type": "string",
+          "nullable": true,
+          "description": "数据库链接字符串",
+          "schema": null
+        },
+        {
+          "name": "intromarkdown",
+          "type": "string",
+          "nullable": true,
+          "description": "项目简介",
+          "schema": null
+        },
+        {
+          "name": "introduction",
+          "type": "string",
+          "nullable": true,
+          "description": "项目简介",
+          "schema": null
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "name": "*",
+          "description": "*",
+          "createdBy": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": [
+            {
+              "count": 1
+            }
+          ]
+        }
+      }
+    ]
+  },
+  {
+    "version": "1",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T23:04:11",
+    "name": "UpdateProject",
+    "module": "pmt",
+    "url": "project/update",
+    "useAbsoluteUrl": false,
+    "method": "PUT",
+    "title": "修改项目",
+    "summary": "修改指定的项目",
+    "note": "修改项目信息的接口。",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "UpdateProject"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "项目标识",
+          "schema": null
+        },
+        {
+          "name": "name",
+          "type": "string",
+          "nullable": false,
+          "description": "项目名称",
+          "schema": null
+        },
+        {
+          "name": "description",
+          "type": "string",
+          "nullable": false,
+          "description": "项目描述",
+          "schema": null
+        },
+        {
+          "name": "createdBy",
+          "type": "string",
+          "nullable": false,
+          "description": "创建人",
+          "schema": null
+        },
+        {
+          "name": "connectionString",
+          "type": "string",
+          "nullable": true,
+          "description": "数据库链接字符串",
+          "schema": null
+        },
+        {
+          "name": "intromarkdown",
+          "type": "string",
+          "nullable": true,
+          "description": "项目简介",
+          "schema": null
+        },
+        {
+          "name": "introduction",
+          "type": "string",
+          "nullable": true,
+          "description": "项目简介",
+          "schema": null
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "name": "*",
+          "description": "*",
+          "createdBy": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": [
+            {
+              "count": 1
+            }
+          ]
+        }
+      }
+    ]
+  },
+  {
+    "version": "1",
+    "owner": "dawson",
+    "updatedTime": "2016-08-13T23:04:11",
+    "name": "DeleteProject",
+    "module": "pmt",
+    "url": "project/delete",
+    "useAbsoluteUrl": false,
+    "method": "DELETE",
+    "title": "删除项目",
+    "summary": "删除指定的项目",
+    "note": "删除项目信息的接口。",
+    "allowAnonymous": false,
+    "cache": {},
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "DeleteProject"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "id",
+          "type": "int",
+          "nullable": false,
+          "description": "项目标识",
+          "schema": null
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "name": "*",
+          "description": "*",
+          "createdBy": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": [
+            {
+              "count": 1
+            }
+          ]
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-03T19:00:26",
+    "name": "CheckNameExsits",
+    "module": "pmt",
+    "url": "ca/check",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "检测接口名称是否存在",
+    "summary": "检测接口名称是否存在",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "CheckNameExsits"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "projectId",
+          "type": "int",
+          "nullable": false,
+          "description": "接口标示",
+          "schema": null
+        },
+        {
+          "type": "string",
+          "name": "name",
+          "description": "接口名称"
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口编号",
+              "schema": null
+            },
+            {
+              "name": "projectId",
+              "type": "int",
+              "nullable": false,
+              "description": "项目编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "version",
+              "type": "string",
+              "nullable": true,
+              "description": "接口版本",
+              "schema": null
+            },
+            {
+              "name": "owner",
+              "type": "string",
+              "nullable": true,
+              "description": "接口负责人",
+              "schema": null
+            },
+            {
+              "name": "module",
+              "type": "string",
+              "nullable": true,
+              "description": "所属模块",
+              "schema": null
+            },
+            {
+              "name": "url",
+              "type": "string",
+              "nullable": true,
+              "description": "接口访问地址",
+              "schema": null
+            },
+            {
+              "name": "method",
+              "type": "string",
+              "nullable": true,
+              "description": "接口请求方法",
+              "schema": null
+            },
+            {
+              "name": "title",
+              "type": "string",
+              "nullable": true,
+              "description": "接口标题",
+              "schema": null
+            },
+            {
+              "name": "summary",
+              "type": "string",
+              "nullable": true,
+              "description": "接口简述",
+              "schema": null
+            },
+            {
+              "name": "note",
+              "type": "string",
+              "nullable": true,
+              "description": "接口备注",
+              "schema": null
+            },
+            {
+              "name": "implemented",
+              "type": "bool",
+              "nullable": true,
+              "description": "是否已实现",
+              "schema": null
+            },
+            {
+              "name": "implementation",
+              "type": "string",
+              "nullable": true,
+              "description": "实现代码",
+              "schema": null
+            },
+            {
+              "name": "parameter",
+              "type": "string",
+              "nullable": true,
+              "description": "接口调用参数",
+              "schema": null
+            },
+            {
+              "name": "result",
+              "type": "string",
+              "nullable": true,
+              "description": "接口返回结果",
+              "schema": null
+            },
+            {
+              "name": "mock",
+              "type": "string",
+              "nullable": true,
+              "description": "接口模拟数据",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "projectId": "*",
+          "name": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "id": "",
+            "projectId": "",
+            "name": "",
+            "version": "",
+            "owner": "",
+            "module": "",
+            "url": "",
+            "method": "",
+            "title": "",
+            "summary": "",
+            "note": "",
+            "implemented": "",
+            "implementation": "",
+            "parameter": "",
+            "result": "",
+            "mock": "",
+            "updatedTime": ""
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-04T15:11:49",
+    "name": "ListRoles",
+    "module": "pmt",
+    "url": "roles/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取所有角色",
+    "summary": "获取所有角色",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListRoles"
+    },
+    "parameter": {
+      "query": [],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "用户信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "标识",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "名称",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-11-30T11:19:42",
+    "name": "ListOwnerApis",
+    "module": "pmt",
+    "url": "ca/owner/list",
+    "useAbsoluteUrl": false,
+    "method": "GET",
+    "title": "获取当前用户接口",
+    "summary": "获取当前用户接口",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "ListOwnerApis"
+    },
+    "parameter": {
+      "query": [
+        {
+          "name": "keyword",
+          "type": "string",
+          "nullable": true,
+          "description": "名称搜索关键字",
+          "schema": null
+        },
+        {
+          "name": "module",
+          "type": "string",
+          "nullable": true,
+          "description": "模块名称",
+          "schema": null
+        },
+        {
+          "name": "pageSize",
+          "type": "int",
+          "nullable": false,
+          "description": "每页显示几条",
+          "schema": null
+        },
+        {
+          "name": "pageIndex",
+          "type": "int",
+          "nullable": false,
+          "description": "当前第几页",
+          "schema": null
+        }
+      ],
+      "body": null
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "array",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "接口编号",
+              "schema": null
+            },
+            {
+              "name": "name",
+              "type": "string",
+              "nullable": false,
+              "description": "接口名称",
+              "schema": null
+            },
+            {
+              "name": "description",
+              "type": "string",
+              "nullable": true,
+              "description": "接口描述",
+              "schema": null
+            },
+            {
+              "name": "createdBy",
+              "type": "string",
+              "nullable": true,
+              "description": "创建人",
+              "schema": null
+            },
+            {
+              "name": "updatedTime",
+              "type": "int",
+              "nullable": false,
+              "description": "最后修改时间",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "keyword": "*",
+          "pageSize": "*",
+          "pageIndex": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "items": [
+              {
+                "id": 1,
+                "projectId": 1,
+                "name": "PuTuoShan",
+                "module": "普陀山统一信息发布平台",
+                "title": "刘合桃",
+                "summary": "普陀山统一信息发布平台",
+                "version": "刘合桃",
+                "url": "PuTuoShan",
+                "method": "普陀山统一信息发布平台",
+                "owner": "刘合桃",
+                "note": "PuTuoShan",
+                "implemented": "普陀山统一信息发布平台",
+                "implementation": "刘合桃",
+                "parameter": "PuTuoShan",
+                "result": "普陀山统一信息发布平台",
+                "mock": "刘合桃",
+                "updatedTime": 1452775192
+              }
+            ],
+            "pagination": {
+              "size": 10,
+              "count": 23,
+              "index": 2
+            }
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2019-11-28T18:39:23",
+    "name": "CreateModule",
+    "module": "pmt",
+    "url": "ca/module/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建当前项目模块",
+    "summary": "创建当前项目模块",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "CreateModule"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "string",
+          "name": "name"
+        },
+        {
+          "type": "int",
+          "name": "projectId"
+        },
+        {
+          "type": "string",
+          "name": "description"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "id",
+              "type": "int",
+              "nullable": false,
+              "description": "操作行数",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "name": "*",
+          "projectId": "*",
+          "description": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "id": 170
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-01T15:07:07",
+    "name": "DeleteModule",
+    "module": "pmt",
+    "url": "ca/module/delete",
+    "useAbsoluteUrl": false,
+    "method": "DELETE",
+    "title": "删除模块",
+    "summary": "删除模块",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "DeleteModule"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "int",
+          "name": "id"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "操作行数",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": [
+      {
+        "input": {
+          "id": "*"
+        },
+        "output": {
+          "statusCode": 200,
+          "errorMessage": "",
+          "data": {
+            "count": ""
+          }
+        }
+      }
+    ]
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-26T11:40:03",
+    "name": "CheckAPINames",
+    "module": "pmt",
+    "url": "defaultapi/check",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "查询API名称是否存在",
+    "summary": "查询API名称是否存在",
+    "note": "为了检查生成默认接口提示使用",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultApiRepository.CheckExistName"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "string",
+          "name": "connectionString",
+          "description": "链接字符串"
+        },
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "所属项目"
+        },
+        {
+          "type": "string",
+          "name": "module",
+          "description": "所属模块"
+        },
+        {
+          "type": "array",
+          "name": "apiData",
+          "description": "api接口数据"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-26T12:32:31",
+    "name": "CheckCSINames",
+    "module": "pmt",
+    "url": "defaultcsi/check",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "查询API名称是否存在",
+    "summary": "查询CSI名称是否存在",
+    "note": "为了检查生成默认接口提示使用",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultCsiRepository.CheckExistName"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "string",
+          "name": "connectionString",
+          "description": "链接字符串"
+        },
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "所属项目"
+        },
+        {
+          "type": "string",
+          "name": "module",
+          "description": "所属模块"
+        },
+        {
+          "type": "array",
+          "name": "apiData",
+          "description": "api接口数据"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "object",
+              "nullable": false,
+              "description": ""
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-22T14:33:25",
+    "name": "ListProjectTables",
+    "module": "pmt",
+    "url": "project/tables",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "获取对应数据库信息",
+    "summary": "获取对应数据库信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultCsiRepository.LoadTablesForProject"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "string",
+          "name": "connectionString",
+          "description": "链接字符串"
+        },
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "所属项目"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2017-06-08T17:42:31",
+    "name": "CreateDefaultApi",
+    "module": "pmt",
+    "url": "defaultapi/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建默认API",
+    "summary": "创建默认API",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultApiRepository.CreateApis"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "string",
+          "name": "connectionString",
+          "description": "链接字符串"
+        },
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "所属项目"
+        },
+        {
+          "type": "string",
+          "name": "module",
+          "description": "所属模块"
+        },
+        {
+          "type": "string",
+          "name": "apiData",
+          "description": "api接口数据"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-22T14:35:45",
+    "name": "CreateDefaultCsi",
+    "module": "pmt",
+    "url": "defaultcsi/create",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "创建默认CSI",
+    "summary": "创建默认CSI",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "DefaultCsiRepository.CreateCsis"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "string",
+          "name": "connectionString",
+          "description": "链接字符串"
+        },
+        {
+          "type": "int",
+          "name": "projectId",
+          "description": "所属项目"
+        },
+        {
+          "type": "string",
+          "name": "module",
+          "description": "所属模块"
+        },
+        {
+          "type": "array",
+          "name": "apiData",
+          "description": "api接口数据"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": []
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2016-12-30T09:46:27",
+    "name": "DeleteUser",
+    "module": "pmt",
+    "url": "user/delete",
+    "useAbsoluteUrl": false,
+    "method": "DELETE",
+    "title": "删除用户",
+    "summary": "删除用户信息",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "DeleteUser"
+    },
+    "parameter": {
+      "query": [
+        {
+          "type": "int",
+          "name": "id",
+          "description": "用户标识"
+        }
+      ],
+      "body": []
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "string",
+          "nullable": true,
+          "description": "无",
+          "schema": null
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2017-01-05T11:50:39",
+    "name": "SetUserPasswrod",
+    "module": "pmt",
+    "url": "user/password/set",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "设置密码",
+    "summary": "设置密码",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "repository",
+      "name": "UserRepository.SetUserPasswrod"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "name": "password",
+          "type": "string",
+          "nullable": false,
+          "description": "用户密码",
+          "schema": null
+        },
+        {
+          "name": "newPassword",
+          "type": "string",
+          "nullable": false,
+          "description": "用户名称",
+          "schema": null
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "int",
+          "nullable": true,
+          "description": "用户标识",
+          "schema": null
+        }
+      ]
+    },
+    "mock": []
+  },
+  {
+    "version": "1.0",
+    "owner": "dawson",
+    "updatedTime": "2017-11-07T09:17:05",
+    "name": "UpdateModule",
+    "module": "pmt",
+    "url": "ca/module/update",
+    "useAbsoluteUrl": false,
+    "method": "POST",
+    "title": "更新当前项目模块",
+    "summary": "更新当前项目模块",
+    "note": "",
+    "allowAnonymous": false,
+    "cache": {
+      "enabled": false,
+      "type": "redis",
+      "expiration": 300
+    },
+    "implemented": true,
+    "implementation": {
+      "type": "csi",
+      "name": "UpdateModule"
+    },
+    "parameter": {
+      "query": [],
+      "body": [
+        {
+          "type": "int",
+          "name": "id"
+        },
+        {
+          "type": "string",
+          "name": "name"
+        },
+        {
+          "type": "string",
+          "name": "description"
+        }
+      ]
+    },
+    "result": {
+      "type": "json",
+      "schema": [
+        {
+          "name": "statusCode",
+          "type": "int",
+          "nullable": false,
+          "description": "错误码(默认为200,无错误信息)",
+          "schema": null
+        },
+        {
+          "name": "errorMessage",
+          "type": "string",
+          "nullable": true,
+          "description": "错误信息",
+          "schema": null
+        },
+        {
+          "name": "data",
+          "type": "object",
+          "nullable": true,
+          "description": "接口信息数据集",
+          "schema": [
+            {
+              "name": "count",
+              "type": "int",
+              "nullable": false,
+              "description": "操作行数",
+              "schema": null
+            }
+          ]
+        }
+      ]
+    },
+    "mock": []
+  }
+]

+ 185 - 0
src/Tools/Wicture.DbRESTFul.PMT/CSI/api.json

@@ -0,0 +1,185 @@
+[
+  {
+    "name": "QueryApis",
+    "code": "SELECT * FROM api WHERE projectId = @projectId [AND `module`=@module] [AND (`name` LIKE CONCAT('%',@keyword,'%') OR `module` LIKE CONCAT('%',@keyword,'%') OR `owner` LIKE CONCAT('%',@keyword,'%') OR `title` LIKE CONCAT('%',@keyword,'%') )] @orderBy LIMIT @pageStart, @pageSize;",
+    "module": "api",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      },
+      "defaults": {
+        "orderBy": "id DESC"
+      },
+      "replace": [
+        "orderBy"
+      ]
+    }
+  },
+  {
+    "name": "GetApi",
+    "code": "SELECT * FROM api WHERE id = @id;",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "CreateApi",
+    "code": "INSERT INTO api(projectId, module,moduleId, name, title, summary, version, url, method, owner, note, implemented, implementation, parameter, result, mock, cache, allowAnonymous,deprecated, protocol, exceptions, useAbsoluteUrl, updatedTime,ownerId) VALUES(@projectId, @module,@moduleId, @name, @title, @summary, @version, @url, @method, @userName, @note, @implemented, @implementation, @parameter, @result, @mock, @cache, @allowAnonymous, @deprecated, @protocol, @exceptions, @useAbsoluteUrl, UNIX_TIMESTAMP(), @userId); SELECT LAST_INSERT_ID();",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {
+      "identity": {
+        "userId": "userId",
+        "userName": "userName"
+      }
+    }
+  },
+  {
+    "name": "DeleteAndCreateApi",
+    "code": "DELETE FROM api WHERE name = @name; INSERT INTO api(projectId, module,moduleId, name, title, summary, version, url, method, owner, note, implemented, implementation, parameter, result, mock, cache, allowAnonymous,deprecated, protocol, exceptions, useAbsoluteUrl, updatedTime,ownerId) VALUES(@projectId, @module,@moduleId, @name, @title, @summary, @version, @url, @method, @userName, @note, @implemented, @implementation, @parameter, @result, @mock, @cache, @allowAnonymous,@deprecated, @protocol, @exceptions, @useAbsoluteUrl, UNIX_TIMESTAMP(), @userId); SELECT LAST_INSERT_ID();",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {
+      "identity": {
+        "userId": "userId",
+        "userName": "userName"
+      }
+    }
+  },
+  {
+    "name": "UpdateApi",
+    "code": "UPDATE api SET name = @name, module = @module, moduleId = @moduleId, title = @title, summary = @summary, version = @version, url = @url, method = @method, owner = @owner, note = @note, implemented = @implemented, implementation = @implementation, parameter = @parameter, result = @result, mock = @mock,cache = @cache,allowAnonymous = @allowAnonymous, deprecated = @deprecated, protocol = @protocol, exceptions = @exceptions, useAbsoluteUrl = @useAbsoluteUrl, updatedTime = UNIX_TIMESTAMP(NOW()) WHERE id = @id;",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "DeleteApi",
+    "code": "DELETE FROM api WHERE id in @ids;",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": {
+      "replace": [
+        "ids"
+      ]
+    }
+  },
+  {
+    "name": "ListModules",
+    "code": "SELECT * FROM module WHERE projectId = @projectId LIMIT @pageStart, @pageSize;",
+    "module": "api",
+    "resultSet": "M",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      }
+    }
+  },
+  {
+    "name": "ImportApis",
+    "code": "INSERT INTO api(projectId, module, name, title, summary, version, url, method, owner, note, implemented, allowAnonymous, useAbsoluteUrl,deprecated, protocol, exceptions, implementation, parameter, result, mock, updatedTime) VALUES(@projectId, @module, @name, @title, @summary, @version, @url, @method, @owner, @note, @implemented, @allowAnonymous, useAbsoluteUrl, @deprecated, @protocol, @exceptions, @implementation, @parameter, @result, @mock, UNIX_TIMESTAMP(NOW()));",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "CheckNameExsits",
+    "code": "SELECT id FROM api WHERE name = @name AND projectId = @projectId;",
+    "module": "api",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "ListOwnerApis",
+    "code": "SELECT a.*,p.`name` as projectName FROM api a LEFT JOIN project p ON a.projectId=p.id WHERE a.ownerId = @userId [AND a.module=@module] [AND (a.name LIKE CONCAT('%',@keyword,'%') OR a.module LIKE CONCAT('%',@keyword,'%') OR a.title LIKE CONCAT('%',@keyword,'%') )] ORDER BY projectId,updatedTime LIMIT @pageStart, @pageSize;",
+    "module": "api",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      },
+      "defaults": {
+        "orderBy": "id DESC"
+      },
+      "identity": {
+        "userId": "userId"
+      }
+    }
+  },
+  {
+    "name": "UpdateModule",
+    "code": "UPDATE module SET name = @name, description = @description WHERE id = @id; SELECT ROW_COUNT() AS count;",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "DeleteModule",
+    "code": "DELETE FROM module WHERE id=@id AND (SELECT COUNT(*) FROM project WHERE id=@id)=0;",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "CheckAPINames",
+    "code": "SELECT count(id) AS count FROM `api` WHERE `name` IN @apiNames AND projectId = @projectId;",
+    "module": "api",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "replace": [
+        "apiNames"
+      ]
+    }
+  },
+  {
+    "name": "ListModulesByProjectName",
+    "code": "SELECT module.id, module.`name`, module.projectId FROM module INNER JOIN project ON module.projectId = project.id WHERE project.`name` = @projectName;",
+    "module": "api",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "ListApiByModules",
+    "code": "SELECT a.version, a.owner, from_unixtime(a.updatedTime) as updatedTime,a.name,a.module,a.url,a.useAbsoluteUrl,a.method,a.title,a.summary,a.note,a.allowAnonymous,a.cache,a.implemented,a.implementation,a.parameter,a.result,a.mock FROM api AS a LEFT JOIN project AS p ON p.id = a.projectId WHERE p.id = @projectId [AND a.moduleId = @moduleId]",
+    "module": "api",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  }
+]

+ 146 - 0
src/Tools/Wicture.DbRESTFul.PMT/CSI/csi.json

@@ -0,0 +1,146 @@
+[
+  {
+    "name": "QueryCSI",
+    "code": "SELECT c.*, cu.nickname AS createdByName, uu.nickname AS updatedByName FROM csi AS c JOIN user AS cu ON cu.id = c.createdBy JOIN user AS uu ON uu.id = c.updatedBy WHERE projectId = @projectId [AND c.name LIKE CONCAT('%',@keyword,'%')] @orderBy LIMIT @pageStart, @pageSize;",
+    "module": "csi",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      },
+      "defaults": {
+        "orderBy": "c.updatedTime DESC"
+      },
+      "replace": [
+        "orderBy"
+      ]
+    }
+  },
+  {
+    "name": "GetCSI",
+    "code": "SELECT c.*, cu.nickname AS createdByName, uu.nickname AS updatedByName FROM csi AS c JOIN user AS cu ON cu.id = c.createdBy JOIN user AS uu ON uu.id = c.updatedBy WHERE c.id = @id;",
+    "module": "csi",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "CreateCSI",
+    "code": "INSERT INTO csi( projectId, name, code, isCodeObject, rawCode, resultSet, queryOnly, module, moduleId, requiredTransaction, middleWares, createdBy, updatedBy, updatedTime) VALUES( @projectId, @name, @code, @isCodeObject, @rawCode, @resultSet, @queryOnly, @module, @moduleId, @requiredTransaction, @middleWares, @userId, @userId, UNIX_TIMESTAMP(NOW()) ); SELECT LAST_INSERT_ID();",
+    "module": "csi",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {
+      "identity": {
+        "userId": "userId",
+        "userName": "createUser"
+      }
+    }
+  },
+  {
+    "name": "UpdateCSI",
+    "code": "UPDATE csi SET name = @name, code = @code, rawCode = @rawCode, resultSet = @resultSet, queryOnly = @queryOnly, requiredTransaction = @requiredTransaction, middleWares = @middleWares, moduleId = @moduleId , module = @module, updatedBy = @updatedBy, isCodeObject = @isCodeObject, updatedTime = UNIX_TIMESTAMP(NOW()) WHERE id = @id;",
+    "module": "csi",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": {
+      "identity": {
+        "userId": "updatedBy",
+        "userName": "updatedAt"
+      }
+    }
+  },
+  {
+    "name": "DeleteCSI",
+    "code": "DELETE FROM csi WHERE id in @ids;",
+    "module": "csi",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": {
+      "replace": [
+        "ids"
+      ]
+    }
+  },
+  {
+    "name": "ImportCSIs",
+    "code": "INSERT INTO csi(projectId, name, module, code, rawCode, resultSet, queryOnly, requiredTransaction, middleWares, createdBy, updatedBy, updatedTime) VALUES(@projectId, @name, @module, @code, @rawCode, @resultSet, @queryOnly, @requiredTransaction, @middleWares, @createdBy, @updatedBy, UNIX_TIMESTAMP(NOW()));SELECT LAST_INSERT_ID();",
+    "module": "csi",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": null
+  },
+  {
+    "name": "CheckCSINameExsits",
+    "code": "SELECT id FROM csi WHERE name = @name AND projectId = @projectId;",
+    "module": "csi",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "ListOwnerCSI",
+    "code": "SELECT c.*, cu.nickname AS createdByName, uu.nickname AS updatedByName ,p.`name` as projectName FROM csi AS c JOIN user AS cu ON cu.id = c.createdBy JOIN user AS uu ON uu.id = c.updatedBy LEFT JOIN project p ON c.projectId=p.id WHERE c.createdBy = @userId [AND c.name LIKE CONCAT('%',@keyword,'%')] @orderBy LIMIT @pageStart, @pageSize;",
+    "module": "csi",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      },
+      "defaults": {
+        "orderBy": "id DESC"
+      },
+      "identity": {
+        "userId": "userId"
+      },
+      "replace": [
+        "orderBy"
+      ]
+    }
+  },
+  {
+    "name": "CheckCSINames",
+    "code": "SELECT count(id) AS count FROM `csi` WHERE `name` IN @csiNames AND projectId = @projectId;",
+    "module": "csi",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "replace": [
+        "csiNames"
+      ]
+    }
+  },
+  {
+    "name": "GetCSIIdByName",
+    "code": "select id from csi where name = @name and projectId = @projectId",
+    "module": "csi",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "ListCsiByModules",
+    "code": "SELECT `name`, `code`, module, resultSet, queryOnly, requiredTransaction, middleWares FROM csi WHERE projectId = @projectId [AND moduleId = @moduleId]",
+    "module": "csi",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {}
+  }
+]

+ 77 - 0
src/Tools/Wicture.DbRESTFul.PMT/CSI/pmt.json

@@ -0,0 +1,77 @@
+[
+  {
+    "name": "CreateModule",
+    "code": "INSERT INTO module(name, projectId, description) VALUES(@name, @projectId, @description); SELECT LAST_INSERT_ID();",
+    "module": "pmt",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": {}
+  },
+  {
+    "name": "QueryProjects",
+    "code": "SELECT * FROM project WHERE 1 = 1 [AND `name` LIKE CONCAT('%',@keyword,'%')] @orderBy LIMIT @pageStart, @pageSize;",
+    "module": "pmt",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      },
+      "defaults": {
+        "orderBy": "id DESC"
+      },
+      "replace": [
+        "orderBy"
+      ]
+    }
+  },
+  {
+    "name": "GetProject",
+    "code": "SELECT * FROM project WHERE id = @id;",
+    "module": "pmt",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "CreateProject",
+    "code": "INSERT INTO project(name, description, createdBy, connectionString, introduction, intromarkdown, createdTime) VALUES(@name, @description, @createdBy, @connectionString, @introduction, @intromarkdown, UNIX_TIMESTAMP(NOW()));SELECT LAST_INSERT_ID() AS id;",
+    "module": "pmt",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": null
+  },
+  {
+    "name": "UpdateProject",
+    "code": "UPDATE project SET name = @name, description = @description, createdBy = @createdBy, connectionString = @connectionString, introduction = @introduction, intromarkdown = @intromarkdown, databaseType=@databaseType WHERE id = @id;",
+    "module": "pmt",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": {}
+  },
+  {
+    "name": "DeleteProject",
+    "code": "DELETE FROM project WHERE id = @id;",
+    "module": "pmt",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "GetProjectDbType",
+    "code": "SELECT databaseType FROM project WHERE id = @projectId;",
+    "module": "pmt",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  }
+]

+ 95 - 0
src/Tools/Wicture.DbRESTFul.PMT/CSI/user.json

@@ -0,0 +1,95 @@
+[
+  {
+    "name": "CreateUser",
+    "code": "INSERT INTO `user`(name, nickname, password, enabled, privilege, updatedTime, roleId) VALUES(@name, @nickname, @password, @enabled,@privilege, UNIX_TIMESTAMP(NOW()), @roleId);SELECT LAST_INSERT_ID() AS id;",
+    "module": "user",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "UpdateUser",
+    "code": "UPDATE `user` SET nickname = @nickname, [password = @password, ]enabled = @enabled, privilege = @privilege, updatedTime = UNIX_TIMESTAMP(NOW()), roleId = @roleId WHERE id = @id;",
+    "module": "user",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": null
+  },
+  {
+    "name": "GetUser",
+    "code": "SELECT id, name, nickname, enabled, privilege, updatedTime, password FROM `user` WHERE 1=1 [ AND name=@name] [ AND id = @id];",
+    "module": "user",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "LoginUser",
+    "code": "SELECT id, name, nickname, enabled, privilege, updatedTime, (select name from role where id=user.roleId) as roleName FROM `user` WHERE name = @username AND password = @password;",
+    "module": "user",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "CheckUser",
+    "code": "SELECT COUNT(*) FROM `user` WHERE name = @name;",
+    "module": "user",
+    "resultSet": "S",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "QueryUsers",
+    "code": "SELECT us.id, us.name, us.nickname, us.enabled, us.privilege, us.updatedTime, us.roleId, rl.name AS roleName FROM `user` us LEFT JOIN role rl ON us.roleId = rl.id WHERE 1 = 1 [AND (us.`name` LIKE CONCAT('%',@keyword,'%') OR us.`nickname` LIKE CONCAT('%',@keyword,'%'))] @orderBy LIMIT @pageStart, @pageSize;",
+    "module": "user",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": {
+      "pagination": {
+        "size": "pageSize",
+        "count": "totalCount",
+        "page": "pageIndex"
+      },
+      "defaults": {
+        "orderBy": "us.id DESC"
+      },
+      "replace": [
+        "orderBy"
+      ]
+    }
+  },
+  {
+    "name": "ListRoles",
+    "code": "SELECT id, name FROM `role` WHERE isActive = 1 ORDER BY displayOrder;",
+    "module": "user",
+    "resultSet": "M",
+    "queryOnly": true,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "DeleteUser",
+    "code": "DELETE FROM `user` WHERE id = @id;",
+    "module": "user",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": false,
+    "middleWares": null
+  },
+  {
+    "name": "SetUserPassword",
+    "code": "UPDATE `user` SET password = @password, updatedTime = UNIX_TIMESTAMP(NOW()) WHERE id = @id;",
+    "module": "user",
+    "resultSet": "S",
+    "queryOnly": false,
+    "requiredTransaction": true,
+    "middleWares": null
+  }
+]

+ 68 - 0
src/Tools/Wicture.DbRESTFul.PMT/Controllers/ApiSyncController.cs

@@ -0,0 +1,68 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+
+namespace Wicture.DbRESTFul.PMT.Controllers
+{
+    [Route("dbrestful/devtool/api")]
+    public class ApiSyncController : Controller
+    {
+        private static readonly List<string> ObjectProperties = new List<string> { "cache", "implementation", "parameter", "result", "mock" };
+        private readonly ILogger _logger;
+        private readonly DbRESTFulRepository _repository;
+
+        public ApiSyncController(ILoggerFactory loggerFactory, DbRESTFulRepository repository) 
+        {
+            _logger = loggerFactory.CreateLogger("PMT");
+            _repository = repository;
+        }
+
+        [HttpGet]
+        [Route("modules")]
+        public async Task<object> ListModules(string projectName)
+        {
+            return await _repository.InvokeAsync("ListModulesByProjectName", new { projectName });
+        }
+
+        [HttpGet]
+        [Route("sync")]
+        public async Task<object> SyncAllConfiguredAPIs(int projectId, int? moduleId)
+        {
+            if (projectId < 1) throw new ArgumentException(nameof(projectId));
+
+            var param = moduleId != null ? JObject.FromObject(new { projectId, moduleId }) : JObject.FromObject(new { projectId });
+            IEnumerable<dynamic> data = await _repository.InvokeAsync("ListApiByModules", param);
+
+            var result = new JObject();
+
+            foreach (IEnumerable<dynamic> items in data.GroupBy(i => i.module))
+            {
+                var module = items.FirstOrDefault().module ?? "other";
+                result[module] = FillAPIsToFile(items.ToArray());
+            }
+            
+            return result;
+        }
+
+        private static JArray FillAPIsToFile(params dynamic[] rawApis)
+        {
+            var apis = new JArray();
+            foreach (var item in rawApis)
+            {
+                JObject ca = JObject.FromObject(item);
+                ca["implemented"] = ca.Value<bool?>("implemented") ?? false;
+                ca["useAbsoluteUrl"] = ca.Value<bool?>("useAbsoluteUrl") ?? false;
+                ca["allowAnonymous"] = ca.Value<bool?>("allowAnonymous") ?? false;
+                ObjectProperties.ForEach(p => ca[p] = JToken.Parse(ca.Value<string>(p) ?? "{}"));
+                apis.Add(ca);
+            }
+
+            return apis;
+        }
+    }
+}

+ 41 - 0
src/Tools/Wicture.DbRESTFul.PMT/Controllers/CsiSyncController.cs

@@ -0,0 +1,41 @@
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+
+namespace Wicture.DbRESTFul.PMT.Controllers
+{
+    [Route("dbrestful/devtool/csi")]
+    public class CsiSyncController : Controller
+    {
+        private readonly DbRESTFulRepository _repository;
+
+        public CsiSyncController(DbRESTFulRepository repository)
+        {
+            _repository = repository;
+        }
+
+        [HttpGet]
+        [Route("sync")]
+        public async Task<object> SyncAllCSIs(int projectId, int? moduleId)
+        {
+            if (projectId < 1) throw new ArgumentException(nameof(projectId));
+
+            var param = moduleId != null ? JObject.FromObject(new { projectId, moduleId }) : JObject.FromObject(new { projectId });
+            IEnumerable<dynamic> data = await _repository.InvokeAsync("ListCsiByModules", param);
+
+            var result = new JObject();
+
+            foreach (IEnumerable<dynamic> items in data.GroupBy(i => i.module))
+            {
+                var module = items.FirstOrDefault().module ?? "other";
+                result[module] = JToken.FromObject(items);
+            }
+
+            return result;
+        }
+    }
+}

+ 17 - 0
src/Tools/Wicture.DbRESTFul.PMT/Controllers/HomeController.cs

@@ -0,0 +1,17 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Wicture.DbRESTFul.PMT.Controllers
+{
+    public class HomeController : Controller
+    {
+        public IActionResult Index()
+        {
+            return View();
+        }
+
+        public IActionResult Error()
+        {
+            return View();
+        }
+    }
+}

+ 163 - 0
src/Tools/Wicture.DbRESTFul.PMT/Controllers/ImportController.cs

@@ -0,0 +1,163 @@
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+using Wicture.DbRESTFul.Resources.Api;
+using Wicture.DbRESTFul.Resources.Csi;
+
+namespace Wicture.DbRESTFul.PMT
+{
+    [Route("api")]
+    public class ImportController : Controller
+    {
+        private readonly DbRESTFulRepository _repository;
+
+        public ImportController(DbRESTFulRepository repository)
+        {
+            _repository = repository;
+        }
+
+        [HttpPost("ca/import")]
+        public async Task<object> ImportApi(int projectId)
+        {
+            if (projectId <= 0)
+            {
+                return new StandardResult(ResultCode.LogicError, "No projectId specified.");
+            }
+
+            if (Request.Form.Files.Count == 0)
+            {
+                return new StandardResult(ResultCode.LogicError, "No file uploaded.");
+            }
+
+            int total = 0;
+            int replace = 0;
+
+            IEnumerable<dynamic> modules = await _repository.InvokeAsync("ListModulesForProject", new { projectId });
+
+            foreach (var file in Request.Form.Files)
+            {
+                using (StreamReader reader = new StreamReader(file.OpenReadStream()))
+                {
+                    var text = reader.ReadToEnd();
+                    var apis = JsonConvert.DeserializeObject<List<ApiModel>>(text);
+
+                    foreach (var api in apis)
+                    {
+                        var checkResult = await _repository.InvokeAsync("CheckNameExsits", new { projectId, api.name }) as IEnumerable<dynamic>;
+                        if (checkResult.Any())
+                        {
+                            replace++;
+                            await _repository.InvokeAsync("DeleteApi", new { ids = checkResult.Select(d => d.id).OfType<int>().ToList() });
+
+                            // TODO: 改成提示用户是否要覆盖。
+                            //return AjaxResult.SetError($"API:[{api.name}] is already exsiting.", ResultCode.LogicError);
+                        }
+                    }
+
+                    await _repository.InvokeAsync("ImportApis", apis.Select(ca => BeParameterized(projectId, ca, modules)));
+                    total += apis.Count;
+                }
+            }
+
+            return new StandardResult { Data = new { total, replace } };
+        }
+
+        private object BeParameterized(int projectId, ApiModel api, IEnumerable<dynamic> modules)
+        {
+            int? moduleId = modules == null ? null : modules.FirstOrDefault(a => a.name == api.module)?.id;
+
+            return new
+            {
+                projectId,
+                api.version,
+                api.owner,
+                api.createdAt,
+                api.name,
+                api.module,
+                moduleId,
+                api.url,
+                useAbsoluteUrl = api.useAbsoluteUrl ? 1 : 0,
+                api.method,
+                api.title,
+                api.summary,
+                api.note,
+                allowAnonymous = api.allowAnonymous ? 1 : 0,
+                cache = JsonConvert.SerializeObject(api.cache),
+
+                implemented = api.implemented ? 1 : 0,
+                implementation = JsonConvert.SerializeObject(api.implementation),
+                parameter = JsonConvert.SerializeObject(api.parameter),
+                result = JsonConvert.SerializeObject(api.result),
+                mock = JsonConvert.SerializeObject(api.mock)
+            };
+        }
+
+        [HttpPost("csi/import")]
+        public async Task<object> ImportCSI(int projectId)
+        {
+            if (projectId <= 0)
+            {
+                return new StandardResult(ResultCode.LogicError, "No projectId specified.");
+            }
+
+            if (Request.Form.Files.Count == 0)
+            {
+                return new StandardResult(ResultCode.LogicError, "No file uploaded.");
+            }
+
+            int total = 0;
+            int replace = 0;
+
+            IEnumerable<dynamic> modules = await _repository.InvokeAsync("ListModulesForProject", new { projectId });
+
+            foreach (var file in Request.Form.Files)
+            {
+                using (StreamReader reader = new StreamReader(file.OpenReadStream()))
+                {
+                    var text = reader.ReadToEnd();
+                    var csiList = JsonConvert.DeserializeObject<List<CsiModel>>(text);
+
+                    foreach (var api in csiList)
+                    {
+                        var checkResult = await _repository.InvokeAsync("CheckCSINameExsits", new { projectId, api.name }) as IEnumerable<dynamic>;
+                        if (checkResult.Any())
+                        {
+                            replace++;
+                            await _repository.InvokeAsync("DeleteCSI", new { ids = checkResult.Select(d => d.id).OfType<int>().ToList() });
+                        }
+                    }
+
+                    await _repository.InvokeAsync("ImportCSIs", csiList.Select(csi => BeParameterized(projectId, csi, modules)));
+                    total += csiList.Count;
+                }
+            }
+
+            return new StandardResult { Data = new { total, replace } };
+        }
+
+        private object BeParameterized(int projectId, CsiModel csi, IEnumerable<dynamic> modules)
+        {
+            int? moduleId = modules == null ? null : modules.FirstOrDefault(a => a.name == csi.module)?.id;
+
+            return new
+            {
+                projectId,
+                csi.name,
+                moduleId,
+                code = JsonConvert.SerializeObject(csi.code),
+                rawCode = JsonConvert.SerializeObject(csi.code),
+                csi.resultSet,
+                csi.queryOnly,
+                csi.requiredTransaction,
+                middleWares = JsonConvert.SerializeObject(csi.middlewares),
+                updatedBy = _repository.Context.Identity.Id,
+                createdBy = _repository.Context.Identity.Id
+            };
+        }
+    }
+
+}

+ 261 - 0
src/Tools/Wicture.DbRESTFul.PMT/Controllers/SpecController.cs

@@ -0,0 +1,261 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+using Wicture.DbRESTFul.PMT.SpecGenerators;
+
+namespace Wicture.DbRESTFul.PMT.Controllers
+{
+    public class SpecController : Controller
+    {
+        private readonly string docRoot = string.Empty;
+        private readonly DbRESTFulRepository _repository;
+
+        public SpecController(IWebHostEnvironment env, DbRESTFulRepository repository)
+        {
+            _repository = repository;
+            docRoot = Path.Combine(env.ContentRootPath, @"doc/");
+        }
+
+        [HttpGet]
+        [Route("api/spec/tables")]
+        public object LoadTablesForProject(string connectionString,int projectId)
+        {
+            var helper = new SpecHelper(connectionString, projectId);
+            var file = helper.ListTables().Select(t => new { name = t.name, title = t.title });
+            return new StandardResult { Data = file };
+        }
+
+        /// <summary>
+        /// 生成数据库文档
+        /// </summary>
+        /// <param name="config">
+        /// {
+        ///     "moduleName": "文档的模块名称",
+        ///     "projectName": "项目名称",
+        ///     "tables": [
+        ///         {   
+        ///             "name": "",
+        ///             "title": ""
+        ///         }
+        ///     ...]
+        /// }
+        /// </param>
+        /// <returns></returns>
+        [HttpPost]
+        [Route("api/spec/db")]
+        public async Task<object> GenerateDbSpec()
+        {
+            using var reader = new StreamReader(Request.Body);
+            var body = await reader.ReadToEndAsync();
+            JObject config = JObject.Parse(body);
+
+            var generator = new DbSpecGenerator(config);
+            var file = generator.Generate(docRoot);
+            return new StandardResult { Data = file };
+        }
+
+        /// <summary>
+        /// 生成项目简介文档
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [Route("api/spec/project/introduction")]
+        public async Task<object> GenerateProjectIntroducation(int id)
+        {
+            var project = await _repository.InvokeAsync("GetProject", new { id });
+            var generator = new IntroductionGenerator(project.name, project.introduction);
+            var file = generator.Generate(docRoot);
+            return new StandardResult { Data = file };
+        }
+
+        /// <summary>
+        /// 生成Api文档
+        /// </summary>
+        /// <param name="config">
+        /// {
+        ///     "projectName": "项目名称",
+        ///     "moduleName": "文档的模块名称",
+        ///     "moduleTitle": "文档的标题",
+        ///     "all": true/false "是否全部更新(按模块)"
+        /// }
+        /// </param>
+        /// <returns></returns>
+        [HttpPost]
+        [Route("api/spec/ca")]
+        public async Task<object> GenerateApiSpec()
+        {
+            try
+            {
+                using var reader = new StreamReader(Request.Body);
+                var body = await reader.ReadToEndAsync();
+                JObject config = JObject.Parse(body);
+
+                // TODO: 可以考虑通过一个按钮,直接刷新当前文档。
+                var helper = new SpecHelper(_repository.ConnectionManager.DbConnection.ReadConnectionString);
+                var projectName = config.Value<string>("projectName");
+
+                int count = 0;
+                if (config.Value<bool?>("all") == true)
+                {
+                    var modules = helper.ListModules(projectName);
+
+                    foreach (var item in modules)
+                    {
+                        var apis = helper.ListAPIForProject(projectName, item.moduleName);
+
+                        var generator = new ApiSpecGenerator(projectName, item.moduleName, item.moduleTitle, apis);
+                        count += generator.Generate(docRoot);
+                    }
+                }
+                else
+                {
+                    var moduleName = config.Value<string>("moduleName");
+                    var moduleTitle = config.Value<string>("moduleTitle");
+
+                    var apis = helper.ListAPIForProject(projectName, moduleName);
+
+                    var generator = new ApiSpecGenerator(projectName, moduleName, moduleTitle, apis);
+                    count = generator.Generate(docRoot);
+                }
+
+                return new StandardResult { Data = new { count = count } };
+            }
+            catch(Exception ex)
+            {
+                return new StandardResult(500, "生成API文件失败: " + ex);
+            }
+        }
+
+        [HttpGet]
+        [Route("api/spec/list")]
+        public object ListSpec(string projectName)
+        {
+            var result = new List<object>();
+
+            var specDir = docRoot + projectName;
+            if(Directory.Exists(specDir))
+            {
+                result.AddRange(Directory.GetFiles(specDir).Select(f => new { fileName = Path.GetFileNameWithoutExtension(f), type = "项目简介", filePath = f }));
+            }
+
+            var dbSpecDir = specDir + "/Database/";
+            if (Directory.Exists(dbSpecDir))
+            {
+                result.AddRange(Directory.GetFiles(dbSpecDir).Select(f => new { fileName = Path.GetFileNameWithoutExtension(f), type = "数据库文档", filePath = f }));
+            }
+
+            var apiSpecDir = specDir + "/Api/";
+            if (Directory.Exists(apiSpecDir))
+            {
+                result.AddRange(Directory.GetFiles(apiSpecDir).Select(f => new { fileName = Path.GetFileNameWithoutExtension(f), type = "接口文档", filePath = f }));
+            }
+
+            var otherSpecDir = specDir + "/Others/";
+            if (Directory.Exists(otherSpecDir))
+            {
+                result.AddRange(Directory.GetFiles(otherSpecDir).Select(f => new { fileName = Path.GetFileNameWithoutExtension(f), type = "其它", filePath = f }));
+            }
+
+            return new StandardResult { Data = result };
+        }
+
+        [HttpPost("api/spec/import")]
+        public object Import(string projectName)
+        {
+            if (string.IsNullOrEmpty(projectName)) return new StandardResult(ResultCode.LogicError, "No projectName specified.");
+            if (Request.Form.Files.Count == 0) return new StandardResult(ResultCode.LogicError, "No file uploaded.");
+
+            foreach (var file in Request.Form.Files)
+            {
+                using (StreamReader reader = new StreamReader(file.OpenReadStream()))
+                {
+                    var text = reader.ReadToEnd();
+                    var dir = docRoot + projectName + "/Others/";
+                    if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
+
+                    var filePath = docRoot + projectName + "/Others/" + file.FileName;
+
+                    System.IO.File.WriteAllText(filePath, text);
+                }
+            }
+
+            return new StandardResult { Data = new { success = true } };
+        }
+
+        /// <summary>
+        /// 删除指定路径的文档。
+        /// </summary>
+        /// <param name="filePath"></param>
+        /// <returns></returns>
+        [HttpDelete]
+        [Route("api/spec/delete")]
+        public object DeleteSpec(string filePath)
+        {
+            var result = new { success = System.IO.File.Exists(filePath) };
+
+            if (System.IO.File.Exists(filePath))
+            {
+                System.IO.File.Delete(filePath);
+            }
+
+            return new StandardResult { Data = result };
+        }
+
+        public IActionResult Index([FromQuery]string project)
+        {
+            ViewData["projectName"] = project;
+
+            var getStamp = new Func<string>(() => "?s="+DateTime.Now.Ticks);
+
+            if (string.IsNullOrEmpty(project))
+            {
+                var projects = Directory.GetDirectories(docRoot).Select(p => Path.GetFileName(p));
+                ViewData["projects"] = projects;
+            }
+            else
+            {
+                var dbSpecDir = docRoot + project + "/Database/";
+                if (Directory.Exists(dbSpecDir))
+                {
+                    var dbSpecs = Directory.GetFiles(dbSpecDir);
+                    ViewData["dbSpecs"] = dbSpecs.Select(f => f.Replace(docRoot, "doc/").Replace("\\", "/") + getStamp());
+                }
+                else
+                {
+                    ViewData["dbSpecs"] = new List<string>();
+                }
+
+                var apiSpecDir = docRoot + project + "/Api/";
+                if (Directory.Exists(apiSpecDir))
+                {
+                    var apiSpecs = Directory.GetFiles(apiSpecDir);
+                    ViewData["apiSpecs"] = apiSpecs.Select(f => f.Replace(docRoot, "doc/").Replace("\\", "/") + getStamp());
+                }
+                else
+                {
+                    ViewData["apiSpecs"] = new List<string>();
+                }
+
+                var otherSpecDir = docRoot + project + "/Others/";
+                if (Directory.Exists(otherSpecDir))
+                {
+                    var otherSpecs = Directory.GetFiles(otherSpecDir);
+                    ViewData["otherSpecs"] = otherSpecs.Select(f => f.Replace(docRoot, "doc/").Replace("\\", "/") + getStamp());
+                }
+                else
+                {
+                    ViewData["otherSpecs"] = new List<string>();
+                }
+            }
+
+            return View();
+        }        
+    }
+}

+ 97 - 0
src/Tools/Wicture.DbRESTFul.PMT/Database/structure.sql

@@ -0,0 +1,97 @@
+/*
+Navicat MySQL Data Transfer
+
+Source Server         : 139.196.37.201_wictureDev
+Source Server Version : 50520
+Source Host           : 139.196.37.201:3306
+Source Database       : pmt
+
+Target Server Type    : MYSQL
+Target Server Version : 50520
+File Encoding         : 65001
+
+Date: 2016-08-30 18:39:28
+*/
+
+SET FOREIGN_KEY_CHECKS=0;
+
+-- ----------------------------
+-- Table structure for api
+-- ----------------------------
+DROP TABLE IF EXISTS `api`;
+CREATE TABLE `api` (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '接口标识',
+  `projectId` int(11) DEFAULT NULL COMMENT '项目标识',
+  `name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '接口名称',
+  `owner` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '负责人',
+  `version` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '版本',
+  `module` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '所属模块',
+  `url` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '相对路由地址',
+  `useAbsoluteUrl` bit(1) DEFAULT NULL COMMENT '是否使用绝对访问路径',
+  `method` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'HTTP请求方法',
+  `title` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '接口标题',
+  `summary` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '接口描述',
+  `allowAnonymous` bit(1) DEFAULT NULL COMMENT '是否允许匿名访问',
+  `cache` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '缓存设置',
+  `note` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '接口备注',
+  `implemented` bit(1) DEFAULT NULL COMMENT '接口是否实现',
+  `implementation` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '接口实现说明',
+  `parameter` text COLLATE utf8_unicode_ci COMMENT '接口参数定义',
+  `result` text COLLATE utf8_unicode_ci COMMENT '接口返回值定义',
+  `mock` text COLLATE utf8_unicode_ci COMMENT '接口模拟数据定义',
+  `updatedTime` int(10) DEFAULT NULL,
+  `ownerId` int(11) DEFAULT NULL,
+  `createdBy` int(11) DEFAULT NULL,
+  `updatedBy` int(11) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=546 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='接口信息表';
+
+-- ----------------------------
+-- Table structure for csi
+-- ----------------------------
+DROP TABLE IF EXISTS `csi`;
+CREATE TABLE `csi` (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'csi标识',
+  `projectId` int(11) DEFAULT NULL COMMENT '项目标识',
+  `name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'csi名称',
+  `code` text COLLATE utf8_unicode_ci COMMENT 'csi代码',
+  `resultSet` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'csi结果集定义',
+  `readOnly` bit(1) DEFAULT NULL COMMENT '是否只读',
+  `requiredTransaction` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '是否需要事务支持',
+  `middleWare` text COLLATE utf8_unicode_ci COMMENT 'csi的中间件支持',
+  `createdBy` int(11) DEFAULT NULL,
+  `updatedTime` int(11) DEFAULT NULL,
+  `updatedBy` int(11) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='csi信息表';
+
+-- ----------------------------
+-- Table structure for project
+-- ----------------------------
+DROP TABLE IF EXISTS `project`;
+CREATE TABLE `project` (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '项目标识',
+  `name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '项目名称',
+  `description` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '项目描述',
+  `createdBy` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '创建人',
+  `connectionString` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '数据库链接字符串',
+  `intromarkdown` text COLLATE utf8_unicode_ci,
+  `introduction` text COLLATE utf8_unicode_ci COMMENT '项目简介',
+  `createdTime` int(10) DEFAULT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='项目信息表';
+
+-- ----------------------------
+-- Table structure for user
+-- ----------------------------
+DROP TABLE IF EXISTS `user`;
+CREATE TABLE `user` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `nickname` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `password` varchar(126) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `enabled` bit(1) DEFAULT NULL,
+  `updatedTime` int(11) DEFAULT NULL,
+  `privilege` int(11) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

+ 46 - 0
src/Tools/Wicture.DbRESTFul.PMT/Dockerfile.txt

@@ -0,0 +1,46 @@
+FROM microsoft/dotnet:1.0.0-core
+
+ENV CONSUL_VERSION 0.6.4
+
+RUN apt-get update && apt-get install -y unzip
+RUN apt-get install -y supervisor
+
+ADD https://releases.hashicorp.com/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip /tmp/consul.zip
+RUN cd /bin && \
+    unzip /tmp/consul.zip && \
+    chmod +x /bin/consul && \
+    mkdir -p {/data/consul,/etc/consul.d} && \
+    rm /tmp/consul.zip
+
+ADD /consul/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
+COPY /consul/server /etc/consul.d
+
+    
+# Set the Working Directory
+WORKDIR /app
+
+# Configure the listening port to 80
+ENV ASPNETCORE_URLS http://*:80
+EXPOSE 80
+
+# Server RPC is used for communication between Consul clients and servers for 
+# internal request forwarding.
+EXPOSE 8300
+
+# Serf LAN and WAN (WAN is used only by Consul servers) are used for gossip between
+# Consul agents. LAN is within the datacenter and WAN is between just the Consul
+# servers in all datacenters.
+EXPOSE 8301 8301/udp 8302 8302/udp
+
+# CLI, HTTP, and DNS (both TCP and UDP) are the primary interfaces that applications
+# use to interact with Consul.
+EXPOSE 8400 8500 8600 8600/udp
+
+# Copy the app
+COPY /app /app
+COPY /API /app/API
+COPY /CSI /app/CSI
+COPY /CRI /app/CRI
+
+# Start the app
+ENTRYPOINT ["/usr/bin/supervisord"]

+ 100 - 0
src/Tools/Wicture.DbRESTFul.PMT/Models/ACIModels.cs

@@ -0,0 +1,100 @@
+using System.Collections.Generic;
+
+namespace Wicture.DbRESTFul.PMT.Models
+{
+    public class ACIModels
+    {
+    }
+
+    public class Parameter
+    {
+        public string name { get; set; }
+        public string type { get; set; }
+        public bool nullable { get; set; } = false;
+        public string description { get; set; }
+    }
+    public class ApiCreateData
+    {
+        public bool allowAnonymous { get; set; } = false;
+        public string Cache { get; set; } = "{\"enabled\":false,\"type\":\"redis\",\"expiration\":300}";
+        public string implementation { get; set; }
+        public bool implemented { get; set; } = true;
+        public string method { get; set; }
+        public string mock { get; set; } = "[]";
+        public string module { get; set; }
+        public int moduleId { get; set; }
+        public string Name { get; set; }
+        public string note { get; set; }
+        public string owner { get; set; }
+        public string parameter { get; set; }
+        public int projectId { get; set; }
+        public string result { get; set; }
+        public string summary { get; set; }
+        public string title { get; set; }
+        public string url { get; set; }
+        public bool useAbsoluteUrl { get; set; } = false;
+        public string version { get; set; } = "1.0.0";
+        public string protocol { get; set; }
+        public bool deprecated { get; set; } = false;
+    }
+
+    public class CsiCreateData
+    {
+        public string code { get; set; } = "";
+        public bool isCodeObject { get; set; } = false;
+        public string middleWares { get; set; } = "{}";
+        public int moduleId { get; set; }
+        public string module { get; set; }
+        public string name { get; set; }
+        public bool queryOnly { get; set; }
+        public int projectId { get; set; }
+        public string rawCode { get; set; }
+        public bool requiredTransaction { get; set; }
+        public string resultSet { get; set; }
+    }
+
+    public class Module
+    {
+        public int ModuleId { get; set; }
+        public string ModuleName { get; set; }
+    }
+    public class Methods
+    {
+        public Method Title { get; set; }
+    }
+    public enum Method
+    {
+        Get,
+        List,
+        Create,
+        Update,
+        Delete
+    }
+    public class ACIData
+    {
+        public string TableName { get; set; }
+        public string ColumnName { get; set; }
+        public string Key { get; set; }
+    }
+
+    public class DefaultACIColumn
+    {
+        public string title { get; set; }
+        public string table { get; set; }
+        public string name { get; set; }
+        public string type { get; set; }
+        public string description { get; set; }
+        public string @default { get; set; }
+        public bool primary_key { get; set; }
+        public bool nullable { get; set; }
+    }
+
+    public class DefaultACITable
+    {
+        public string title { get; set; }
+        public string name { get; set; }
+        public string description { get; set; }
+
+        public List<DefaultACIColumn> columns { get; set; }
+    }
+}

+ 13 - 0
src/Tools/Wicture.DbRESTFul.PMT/Program.cs

@@ -0,0 +1,13 @@
+using NLog;
+
+namespace Wicture.DbRESTFul.PMT
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            LogManager.ThrowConfigExceptions = true;
+            Starter.Start<Startup>(args);
+        }
+    }
+}

+ 27 - 0
src/Tools/Wicture.DbRESTFul.PMT/Properties/launchSettings.json

@@ -0,0 +1,27 @@
+{
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:56053/",
+      "sslPort": 0
+    }
+  },
+  "profiles": {
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "Wicture.DbRESTFul.PMT": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "launchUrl": "http://localhost:5000",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 48 - 0
src/Tools/Wicture.DbRESTFul.PMT/Repositories/CSIRepository.cs

@@ -0,0 +1,48 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+using Wicture.DbRESTFul.Resources.Csi;
+
+namespace Wicture.DbRESTFul.PMT.Repositories
+{
+    public class CSIRepository : DbRESTFulRepository
+    {
+        public async Task<object> ExecuteCode(JToken param)
+        {
+            var projectId = param.Value<int>("projectId");
+            if (projectId <= 0) throw new ArgumentException("No projectId specified.", nameof(projectId));
+
+            var pmtConnection = ConnectionManager.DbConnection;
+
+            try
+            {
+                var project = await base.InvokeAsync("GetProject", new { id = projectId });
+                var dbtype = (DatabaseType)project.databaseType;
+
+                ConnectionManager.DbConnection = BuildConnection(project);
+
+                var parameters = JToken.Parse(param.Value<string>("params"));
+                return await base.QueryAsync<dynamic>(param.Value<string>("code"), parameters);
+            }
+            catch (Exception ex)
+            {
+                throw ex;
+            }
+            finally
+            {
+                ConnectionManager.DbConnection = pmtConnection;
+            }
+        }
+
+        private DbConnection BuildConnection(dynamic project)
+        {
+            return new DbConnection
+            {
+                DatabaseType = (DatabaseType)project.databaseType,
+                ReadConnectionString = project.connectionString,
+                WriteConnectionString = project.connectionString,
+            };
+        }
+    }
+}

+ 239 - 0
src/Tools/Wicture.DbRESTFul.PMT/Repositories/DefaultApiRepository.cs

@@ -0,0 +1,239 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+using Wicture.DbRESTFul.PMT.Models;
+using Wicture.DbRESTFul.PMT.Scaffolding;
+
+namespace Wicture.DbRESTFul.PMT.Repositories
+{
+    public class DefaultApiRepository : DbRESTFulRepository
+    {
+        public async Task<object> CheckExistName(JToken param)
+        {
+            var apiData = param["apiData"].ToObject<List<ACIData>>();
+            var tableName = string.Join("-", apiData.Select(s => s.TableName).Distinct().ToArray());
+            var methods = param.Value<JArray>("selectedMethods").ToObject<List<Methods>>();
+            List<string> apiNames = new List<string>();
+            foreach (var item in methods)
+            {
+                apiNames.Add($"{item.Title.ToString()}_{tableName}");
+            }
+            return await base.InvokeAsync("CheckAPINames", new { apiNames, projectId = param.Value<int>("projectId") });
+        }
+
+        public object LoadTablesForProject(JToken param)
+        {
+            string connectionString = param.Value<string>("connectionString");
+            int projectId = param.Value<int>("projectId");
+            var helper = new ScaffoldingHelper(connectionString, projectId);
+            var table = helper.ListTables().Select(t => new { title = t.name, key = 1, children = t.columns });
+            return table;
+        }
+
+        public async Task<object> CreateApis(JToken param)
+        {
+            var projectId = param.Value<int>("projectId");
+            var methods = param.Value<JArray>("selectedMethods").ToObject<List<Methods>>();
+            var module = param["module"].ToObject<Module>();
+            var apiData = param["apiData"].ToObject<List<ACIData>>();
+            var helper = new ScaffoldingHelper(param.Value<string>("connectionString"), param.Value<int>("projectId"));
+            var tableNames = apiData.Select(s => s.TableName).Distinct().ToArray();
+            var columNames = apiData.Where(w => w.TableName == "").Select(s => s.ColumnName);
+            var tables = helper.ListTables().Where(w => (tableNames.Contains(w.name)));
+            List<DefaultACIColumn> allColumns = new List<DefaultACIColumn>();
+            tables.ForEach(f =>
+            {
+                var columns = new List<DefaultACIColumn>();
+                f.columns.ForEach(cf =>
+                {
+                    if (apiData.Where(w => w.TableName == f.name).Select(s => s.ColumnName).Contains(cf.name))
+                        columns.Add(cf);
+                });
+                allColumns.AddRange(columns);
+            });
+
+            using (var cnn = ConnectionManager.GetConnection(false))
+            {
+                cnn.Open();
+                var transaction = cnn.BeginTransaction();
+                try
+                {
+                    foreach (var item in methods)
+                    {
+                        var data = this.ParamIntegration(projectId, module, item.Title, string.Join("-", tableNames), allColumns);
+                        await base.InvokeAsync("DeleteAndCreateApi", data, cnn, transaction);
+                    }
+
+                    transaction.Commit();
+                }
+                catch (Exception)
+                {
+                    transaction.Rollback();
+                    throw;
+                }
+            }
+            return null;
+        }
+
+        #region Private Methods
+        private object ParamIntegration(int projectId, Module module, Method method, string tableName, List<DefaultACIColumn> columns)
+        {
+            ApiCreateData result = new ApiCreateData();
+            switch (method)
+            {
+                case Method.Get:
+                    {
+                        result.method = "GET";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.Name = $"Get_{tableName}";
+                        result.parameter = JsonConvert.SerializeObject(new { query = DefaultApiColumnToParameter(columns.Where(w => (w.primary_key))), body = new object[] { } });
+                        result.result = DefaultGetReturnApi(DefaultApiColumnToParameter(columns));
+                        result.url = $"{tableName.ToLower()}/get";
+                        result.projectId = projectId;
+                        result.title = $"获取详情:{tableName}";
+                    }
+                    break;
+                case Method.List:
+                    {
+                        result.method = "GET";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.Name = $"List_{tableName}";
+                        result.parameter = "{\"query\":[{\"name\":\"pageSize\",\"type\":\"int\",\"nullable\":false,\"description\":\"页数\"},{\"name\":\"pageIndex\",\"type\":\"int\",\"nullable\":false,\"description\":\"页码\"}],\"body\":[]}";
+                        result.result = DefaultListReturnApi(DefaultApiColumnToParameter(columns));
+                        result.url = $"{tableName.ToLower()}/list";
+                        result.projectId = projectId;
+                        result.title = $"获取列表:{tableName}";
+                    }
+                    break;
+                case Method.Create:
+                    {
+                        result.method = "POST";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.Name = $"Create_{tableName}";
+                        result.parameter = JsonConvert.SerializeObject(new { query = new object[] { }, body = DefaultApiColumnToParameter(columns) });
+                        result.result = DefaultGetReturnApi(new object[] { new { name = "id", type = "int", nullable = true, description = "返回创建编号" } });
+                        result.url = $"{tableName.ToLower()}/create";
+                        result.projectId = projectId;
+                        result.title = $"创建:{tableName}";
+                    }
+                    break;
+                case Method.Update:
+                    {
+                        result.method = "PUT";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.Name = $"Update_{tableName}";
+                        result.parameter = JsonConvert.SerializeObject(new { query = new object[] { }, body = DefaultApiColumnToParameter(columns) });
+                        result.result = DefaultGetReturnApi(new object[] { new { name = "count", type = "int", nullable = true, description = "更新数量" } });
+                        result.url = $"{tableName.ToLower()}/update";
+                        result.projectId = projectId;
+                        result.title = $"更新:{tableName}";
+                    }
+                    break;
+                case Method.Delete:
+                    {
+                        result.method = "DELETE";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.Name = $"Delete_{tableName}";
+                        result.parameter = JsonConvert.SerializeObject(new { query = DefaultApiColumnToParameter(columns.Where(w => (w.primary_key))), body = new object[] { } });
+                        result.result = DefaultGetReturnApi(new object[] { new { name = "count", type = "int", nullable = true, description = "删除数量" } });
+                        result.url = $"{tableName.ToLower()}/delete";
+                        result.projectId = projectId;
+                        result.title = $"删除:{tableName}";
+                    }
+                    break;
+            }
+
+            result.implementation = "{\"type\":\"csi\",\"name\":\"" + result.Name + "\"}";
+            result.summary = result.title;
+            result.deprecated = false;
+            result.protocol = "HttpAndRpc";
+
+            return result;
+        }
+
+        private IEnumerable<Parameter> DefaultApiColumnToParameter(IEnumerable<DefaultACIColumn> columns)
+        {
+            foreach (var item in columns)
+            {
+                yield return new Parameter
+                {
+                    name = item.name,
+                    type = TransferType(item.type),
+                    nullable = item.nullable,
+                    description = item.description
+                };
+            }
+        }
+
+        /// <summary>
+        /// 根据查询类型转换目前支持类型
+        /// </summary>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        private string TransferType(string type)
+        {
+            var result = type;
+            if (type.Contains("char") || type.Contains("uniqueidentifier") || type.Contains("text"))
+            {
+                result = "string";
+            }
+            if (type.Contains("int"))
+            {
+                result = "int";
+            }
+            if (type.Contains("decimal") || type.Contains("double"))
+            {
+                result = "decimal";
+            }
+            if (type.Contains("bit"))
+            {
+                result = "bit";
+            }
+            if (result != "object" && result != "array" && result != "string" && result != "int" && result != "bool" && result != "decimal" && result != "number" && result != "datetime" && result != "bit")
+            {
+                result = "object";
+            }
+            return result;
+        }
+
+        private string DefaultGetReturnApi(object schemaObj)
+        {
+            return JsonConvert.SerializeObject(new
+            {
+                type = "json",
+                schema = new object[]{
+                    new { name = "statusCode", type = "int", nullable = false, description = "错误码(默认为200,无错误信息)" },
+                    new { name = "errorMessage", type = "string", nullable = true, description = "错误信息" },
+                    new { name = "data", type = "object", nullable = true, description = "接口信息数据集", schema = schemaObj }
+                }
+            });
+        }
+
+        private string DefaultListReturnApi(object schemaObj)
+        {
+            return DefaultGetReturnApi(
+                new object[]
+                {
+                    new { name = "items", type = "array", nullable = false, description = "数据集", schema = schemaObj },
+                    new { name = "pagination", type = "object", nullable = false, description = "分页数据",
+                        schema = new object[]
+                        {
+                            new { name = "totalCount", type = "int", nullable = false, description = "总条数" },
+                            new { name = "pageSize", type = "int", nullable = false, description = "页数" },
+                            new { name = "pageIndex", type = "int", nullable = false, description = "页码" }
+                        }
+                    }
+                });
+        }
+        #endregion
+    }
+}

+ 239 - 0
src/Tools/Wicture.DbRESTFul.PMT/Repositories/DefaultCsiRepository.cs

@@ -0,0 +1,239 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+using Wicture.DbRESTFul.PMT.Models;
+using Wicture.DbRESTFul.PMT.Scaffolding;
+
+namespace Wicture.DbRESTFul.PMT.Repositories
+{
+    public class DefaultCsiRepository : DbRESTFulRepository
+    {
+        public async Task<object> CheckExistName(JToken param)
+        {
+            var apiData = param["apiData"].ToObject<List<ACIData>>();
+            var tableName = string.Join("-", apiData.Select(s => s.TableName).Distinct().ToArray());
+            var methods = param.Value<JArray>("selectedMethods").ToObject<List<Methods>>();
+            var csiNames = new List<string>();
+            foreach (var item in methods)
+            {
+                csiNames.Add($"{item.Title}_{tableName}");
+            }
+            return await base.InvokeAsync("CheckCSINames", new { csiNames, projectId = param.Value<int>("projectId") });
+        }
+
+        public object LoadTablesForProject(JToken param)
+        {
+            var connectionString = param.Value<string>("connectionString");
+            var projectId = param.Value<int>("projectId");
+            var helper = new ScaffoldingHelper(connectionString, projectId);
+            var table = helper.ListTables().Select(t => new { title = t.name, key = 1, children = t.columns });
+            return table;
+        }
+
+        public async Task<object> CreateCsis(JToken param)
+        {
+            var projectId = param.Value<int>("projectId");
+            var methods = param.Value<JArray>("selectedMethods").ToObject<List<Methods>>();
+            var module = param["module"].ToObject<Module>();
+            var apiData = param["apiData"].ToObject<List<ACIData>>();
+            var helper = new ScaffoldingHelper(param.Value<string>("connectionString"), param.Value<int>("projectId"));
+            var tableNames = apiData.Select(s => s.TableName).Distinct().ToArray();
+            var tables = helper.ListTables().Where(w => (tableNames.Contains(w.name)));
+            var allColumns = new List<DefaultACIColumn>();
+            tables.ForEach(f =>
+            {
+                var columns = new List<DefaultACIColumn>();
+                f.columns.ForEach(cf =>
+                {
+                    if (apiData.Where(w => w.TableName == f.name).Select(s => s.ColumnName).Contains(cf.name))
+                        columns.Add(cf);
+                });
+                allColumns.AddRange(columns);
+            });
+
+            using (var cnn = ConnectionManager.GetConnection(false))
+            {
+                cnn.Open();
+                var transaction = cnn.BeginTransaction();
+                try
+                {
+                    int databaseType = (await InvokeSingleOrDefaultAsync<dynamic>("GetProjectDbType", new { projectId }, cnn)).databaseType;
+                    foreach (var item in methods)
+                    {
+                        var data = this.ParamIntegration(projectId, module, item.Title, string.Join("-", tableNames), allColumns, databaseType);
+                        await base.InvokeAsync("CreateCSI", data, cnn, transaction);
+                    }
+
+                    transaction.Commit();
+                }
+                catch (Exception)
+                {
+                    transaction.Rollback();
+                    throw;
+                }
+            }
+            return null;
+        }
+
+        #region Private Methods
+        private object ParamIntegration(int projectId, Module module, Method method, string tableName, IReadOnlyCollection<DefaultACIColumn> columns, int databaseType = 0)
+        {
+            var result = new CsiCreateData();
+            var allColumnNames = columns.Select(c => c.name);
+            switch (method)
+            {
+                case Method.Get:
+                    {
+                        var primaryKey = string.Join(", ", columns.Where(w => (w.primary_key)).Select(c => c.name));
+
+                        result.code = $"SELECT {string.Join(", ", allColumnNames)} FROM { tableName } WHERE { primaryKey } = @{ primaryKey };";
+                        result.rawCode = $"SELECT {string.Join(", \n\t", allColumnNames)} \nFROM { tableName } \nWHERE { primaryKey } = @{ primaryKey };";
+                        result.name = $"Get_{ tableName }";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.projectId = projectId;
+                        result.queryOnly = true;
+                        result.requiredTransaction = false;
+                        result.resultSet = "S";
+                    }
+                    break;
+                case Method.List:
+                    {
+                        result.name = $"List_{ tableName }";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.projectId = projectId;
+                        result.queryOnly = true;
+                        result.requiredTransaction = false;
+                        result.resultSet = "M";
+
+                        // MYSQL 0
+                        if (databaseType == 0)
+                        {
+                            result.code = $"SELECT {string.Join(", ", allColumnNames)} FROM { tableName } WHERE 1 = 1 LIMIT @pageStart, @pageSize;";
+                            result.rawCode = $"SELECT {string.Join(", \n\t", allColumnNames)} \nFROM { tableName } \nWHERE 1 = 1 \nLIMIT @pageStart, @pageSize;";
+                            result.middleWares = "{\"pagination\":{\"size\":\"pageSize\",\"count\":\"totalCount\",\"page\":\"pageIndex\"}}";
+                        }
+
+                        // SQLSERVER 1
+                        else
+                        {
+                            var primaryKey = string.Join(", ", columns.Where(w => (w.primary_key)).Select(c => c.name));
+
+                            result.code = $"SELECT Top @pageSize * FROM(SELECT ROW_NUMBER() OVER(ORDER BY t.{ primaryKey }) AS RowNumber, t.* FROM { tableName } t) P WHERE RowNumber > ((@pageIndex - 1) * @pageSize);";
+                            result.rawCode = $"SELECT Top @pageSize * FROM\n\t(SELECT ROW_NUMBER() OVER(ORDER BY t.{ primaryKey }) AS RowNumber, t.* FROM { tableName } t) P \nWHERE RowNumber > ((@pageIndex - 1) * @pageSize);";
+                            result.middleWares = JsonConvert.SerializeObject(new
+                            {
+                                pagination = new
+                                {
+                                    size = "pageSize",
+                                    count = "totalCount",
+                                    page = "pageIndex"
+                                },
+                                replace = new object[]
+                                {
+                                    "pageSize"
+                                }
+                            });
+                        }
+                    }
+                    break;
+                case Method.Create:
+                    {
+                        var columnNames = string.Join(", ", columns.Where(w => (!w.primary_key)).Select(c => c.name));
+                        var columnValues = string.Join(", ", columns.Where(w => (!w.primary_key)).Select(c => "@" + c.name));
+                        var rawColumnNames = string.Join(", \n\t", columns.Where(w => (!w.primary_key)).Select(c => c.name));
+                        var rawColumnValues = string.Join(", \n\t", columns.Where(w => (!w.primary_key)).Select(c => "@" + c.name));
+
+                        result.name = $"Create_{ tableName }";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.projectId = projectId;
+                        result.queryOnly = false;
+                        result.requiredTransaction = true;
+                        result.resultSet = "S";
+
+                        // MYSQL 0
+                        if (databaseType == 0)
+                        {
+                            result.code = $"INSERT INTO { tableName }({ columnNames }) VALUES ({ columnValues });SELECT LAST_INSERT_ID() AS id;";
+                            result.rawCode = $"INSERT INTO { tableName }(\n\t{ rawColumnNames }) \nVALUES (\n\t{ rawColumnValues });\nSELECT LAST_INSERT_ID() AS id;";
+                        }
+
+                        // SQLSERVER 1
+                        else
+                        {
+                            result.code = $"INSERT INTO { tableName }({ columnNames }) VALUES ({ columnValues });SELECT @@IDENTITY AS id;";
+                            result.rawCode = $"INSERT INTO { tableName }(\n\t{ rawColumnNames }) \nVALUES (\n\t{ rawColumnValues });\nSELECT @@IDENTITY AS id;";
+                        }
+                    }
+                    break;
+                case Method.Update:
+                    {
+                        var primaryKey = string.Join(", ", columns.Where(w => (w.primary_key)).Select(c => c.name));
+                        var nameValues = string.Join(", ", columns.Where(w => (!w.primary_key)).Select(c => $"{c.name} = @{c.name}"));
+                        var rawNameValues = string.Join(", ", columns.Where(w => (!w.primary_key)).Select(c => $"\n\t{c.name} = @{c.name}"));
+
+                        result.name = $"Update_{ tableName }";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.projectId = projectId;
+                        result.queryOnly = false;
+                        result.requiredTransaction = true;
+                        result.resultSet = "S";
+
+                        // MYSQL 0
+                        if (databaseType == 0)
+                        {
+                            result.code = $"UPDATE { tableName } SET { nameValues } WHERE { primaryKey } = @{ primaryKey }; SELECT ROW_COUNT() AS count;";
+                            result.rawCode = $"Update { tableName } SET { rawNameValues } \nWHERE { primaryKey } = @{ primaryKey }; \nSELECT ROW_COUNT() AS count;";
+                        }
+
+                        // SQLSERVER 1
+                        else
+                        {
+                            result.code = $"UPDATE { tableName } SET { nameValues } WHERE { primaryKey } = @{ primaryKey }; SELECT @@ROWCOUNT AS count;";
+                            result.rawCode = $"Update { tableName } SET { rawNameValues } \nWHERE { primaryKey } = @{ primaryKey }; \nSELECT @@ROWCOUNT AS count;";
+                           
+                        }
+                    }
+                    break;
+                case Method.Delete:
+                    {
+                        var primaryKey = string.Join(", ", columns.Where(w => (w.primary_key)).Select(c => c.name));
+
+                        result.name = $"Delete_{ tableName }";
+                        result.module = module.ModuleName;
+                        result.moduleId = module.ModuleId;
+                        result.projectId = projectId;
+                        result.queryOnly = false;
+                        result.requiredTransaction = true;
+                        result.resultSet = "S";
+
+                        // MYSQL 0
+                        if (databaseType == 0)
+                        {
+                            result.code = $"DELETE FROM { tableName } WHERE { primaryKey } = @{ primaryKey }; SELECT ROW_COUNT() AS count;";
+                            result.rawCode = $"DELETE FROM { tableName } \nWHERE { primaryKey } = @{ primaryKey }; \nSELECT ROW_COUNT() AS count;";
+                        }
+
+                        // SQLSERVER 1
+                        else
+                        {
+                            result.code = $"DELETE FROM { tableName } WHERE { primaryKey } = @{ primaryKey }; SELECT @@ROWCOUNT AS count;";
+                            result.rawCode = $"DELETE FROM { tableName } \nWHERE { primaryKey } = @{ primaryKey }; \nSELECT @@ROWCOUNT AS count;";
+                        }
+                    }
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(method), method, null);
+            }
+            return result;
+        }
+        #endregion
+    }
+}

+ 76 - 0
src/Tools/Wicture.DbRESTFul.PMT/Repositories/ImportRepository.cs

@@ -0,0 +1,76 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+using Wicture.DbRESTFul.Resources.Csi;
+
+namespace Wicture.DbRESTFul.PMT.Repositories
+{
+    public class ImportRepository : DbRESTFulRepository
+    {
+        public async Task<object> ImportCSI(JToken param)
+        {
+            var httpContext = (Context as HttpApiExecutionContext).HttpContext;
+
+            var projectId = param.Value<int>("projectId");
+            if (projectId <= 0) throw new ArgumentException("No projectId specified.", nameof(projectId));
+            if (httpContext.Request.Form.Files.Count == 0) throw new Exception("No file uploaded.");
+
+            int total = 0;
+            int replace = 0;
+            
+            IEnumerable<dynamic> modules = await base.InvokeAsync("ListModulesForProject", new { projectId });
+
+            foreach (var file in httpContext.Request.Form.Files)
+            {
+                using (StreamReader reader = new StreamReader(file.OpenReadStream()))
+                {
+                    var text = reader.ReadToEnd();
+                    var csiList = JsonConvert.DeserializeObject<List<CsiModel>>(text);
+
+                    foreach (var api in csiList)
+                    {
+                        IEnumerable<dynamic> checkResult = await base.InvokeAsync("CheckCSINameExsits", new { projectId, api.name });
+                        if (checkResult.Any())
+                        {
+                            replace++;
+                            await base.InvokeAsync("DeleteCSI", new { ids = checkResult.Select(d => d.id).OfType<int>().ToList() });
+                        }
+                    }
+
+                    await base.InvokeAsync("ImportCSIs", csiList.Select(csi => BeParameterized(projectId, csi, modules)));
+                    total += csiList.Count;
+                }
+            }
+
+            return new { total, replace };
+        }
+
+        private object BeParameterized(int projectId, CsiModel csi, IEnumerable<dynamic> modules)
+        {
+            int? moduleId = modules == null ? null : modules.FirstOrDefault(a => a.name == csi.module)?.id;
+            var code = csi.code is JValue ? csi.code.ToString() : csi.code.ToString(Formatting.None);
+
+            return new
+            {
+                projectId,
+                csi.name,
+                module = csi.module ?? "default",
+                moduleId,
+                code,
+                rawCode = code,
+                csi.resultSet,
+                csi.queryOnly,
+                csi.requiredTransaction,
+                middleWares = JsonConvert.SerializeObject(csi.middlewares),
+                updatedBy = Context.Identity.Id,
+                createdBy = Context.Identity.Id
+            };
+        }
+    }
+}

+ 15 - 0
src/Tools/Wicture.DbRESTFul.PMT/Repositories/ProjectRepository.cs

@@ -0,0 +1,15 @@
+using Newtonsoft.Json.Linq;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+
+namespace Wicture.DbRESTFul.PMT.Repositories
+{
+    public class ProjectRepository : DbRESTFulRepository
+    {
+        public async Task<object> ListUsers(JToken param)
+        {
+            var sql = "SELECT * FROM user";
+            return await base.InvokeAsync(sql, param);
+        }
+    }
+}

+ 68 - 0
src/Tools/Wicture.DbRESTFul.PMT/Repositories/UserRepository.cs

@@ -0,0 +1,68 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Infrastructure.Repository;
+
+namespace Wicture.DbRESTFul.PMT.Repositories
+{
+    public class UserRepository : DbRESTFulRepository
+    {
+        public async Task<object> CreateUser(JToken param)
+        {
+            using var md5 = MD5.Create();
+            var password = md5.ComputeHash(Encoding.UTF8.GetBytes(param.Value<string>("password")));
+            param["password"] = Convert.ToBase64String(password);
+
+            var user = await base.InvokeAsync("GetUser", new { name = param.Value<string>("name") });
+            if (user != null)
+            {
+                return new { id = 0 };
+            }
+
+            return await base.InvokeAsync("CreateUser", param);
+        }
+
+        public async Task<object> UpdateUser(JToken param)
+        {
+            if (param.Value<string>("password") != null && param.Value<string>("password") != "")
+            {
+                using var md5 = MD5.Create();
+                var password = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(param.Value<string>("password")));
+                param["password"] = Convert.ToBase64String(password);
+            }
+
+            return await base.InvokeAsync("UpdateUser", param);
+        }
+
+        public async Task<object> SetUserPasswrod(JToken param)
+        {
+            using var md5 = MD5.Create();
+            var password = md5.ComputeHash(Encoding.UTF8.GetBytes(param.Value<string>("password")));
+            var user = await base.InvokeAsync("GetUser", new { id = Context.Identity.Id });
+            if (user.password == Convert.ToBase64String(password))
+            {
+                var newPassword = Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes(param.Value<string>("newPassword"))));
+
+                await base.InvokeAsync("SetUserPassword", new { id = Context.Identity.Id, password = newPassword });
+                return JToken.FromObject(new { count = 1 });//修改成功
+            }
+            return JToken.FromObject(new { count = 0 });//原密码不正确
+        }
+
+        public async Task<UserIdentity> GetUserIdentity(string username, string password, string tag = null)
+        {
+            using var md5 = MD5.Create();
+            var pwd = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
+
+            var user = await base.InvokeAsync("LoginUser", new { username, password = Convert.ToBase64String(pwd) });
+            if (user != null)
+            {
+                return new UserIdentity { Id = user.id.ToString(), Name = user.name, Alias = user.nickname, Role = user.roleName };
+            }
+
+            return null;
+        }
+    }
+}

+ 155 - 0
src/Tools/Wicture.DbRESTFul.PMT/Scaffolding/ScaffoldingHelper.cs

@@ -0,0 +1,155 @@
+using Dapper;
+using MySql.Data.MySqlClient;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.SqlClient;
+using System.IO;
+using System.Linq;
+using Wicture.DbRESTFul.Configuration;
+using Wicture.DbRESTFul.PMT.Models;
+using Wicture.DbRESTFul.Resources.Csi;
+
+namespace Wicture.DbRESTFul.PMT.Scaffolding
+{
+    public class CSIDbSource
+    {
+        public string projectName { get; set; }
+        public string moduleName { get; set; }
+        public List<DefaultACITable> tables { get; set; }
+
+        public DatabaseType databaseType { get; set; }
+
+        public IDbConnection GetConnection(string connectionString, DatabaseType DatabaseType = DatabaseType.MySQL)
+        {
+            if (DatabaseType == DatabaseType.SQLServer) return new SqlConnection(connectionString) as IDbConnection;
+            else return new MySqlConnection(connectionString) as IDbConnection;
+        }
+    }
+
+
+    public class ScaffoldingHelper
+    {
+        private readonly string databaseName = string.Empty;
+        private readonly string query_table_names = "SELECT `TABLE_NAME` AS `name`, `TABLE_COMMENT` AS description FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = '{0}' AND `TABLE_TYPE` = 'BASE TABLE';";
+        private readonly string query_table_names_sqlserver = "SELECT st.name,sep.VALUE  [description] FROM 	sys.tables st  LEFT JOIN sys.extended_properties sep ON st.object_id = sep.major_id AND sep.minor_id=0;";
+        private readonly string query_all_column_names = "SELECT `TABLE_NAME` AS `table`, `COLUMN_NAME` AS `name`, `COLUMN_NAME` AS `title`, `COLUMN_TYPE` AS `type`, (CASE `IS_NULLABLE` WHEN 'NO' THEN 0 ELSE 1 END) AS nullable, (CASE `COLUMN_KEY` WHEN 'PRI' THEN 1 ELSE 0 END) AS primary_key, `COLUMN_DEFAULT` AS `default`,`COLUMN_COMMENT` AS description FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = '{0}';";
+        private readonly string query_all_column_names_sqlserver = "SELECT st.name [table], sc.is_nullable nullable, sc.name [name], sc.name title, si.is_primary_key primary_key, sep.VALUE description, TYPE_NAME(sc.system_type_id) [type], 0 [default] FROM sys.tables st INNER JOIN sys.columns sc ON st.object_id = sc.object_id LEFT JOIN sys.extended_properties sep ON st.object_id = sep.major_id AND sc.column_id = sep.minor_id LEFT JOIN sys.sysindexkeys sik ON sik.id = sc.object_id AND sik.colid = sc.column_id LEFT JOIN sys.indexes si ON si.object_id = sik.id AND si.index_id = sik.indid LEFT JOIN sys.objects so ON so.name = si.name;";
+        private readonly string query_api_for_project = "SELECT a.* FROM api AS a LEFT JOIN project AS p ON p.id = a.projectId WHERE p.name = @projectName;";
+        private readonly string get_databaseType = "SELECT databaseType FROM project WHERE id=@projectId;";
+        private static readonly List<string> ObjectProperties = new List<string> { "cache", "implementation", "parameter", "result", "mock" };
+
+        private readonly CSIDbSource source;
+        private readonly string pmtConncetString = "";
+        private readonly string projectConncetString = "";
+
+        public ScaffoldingHelper(string connectionString, int projectId = -1)
+        {
+            source = new CSIDbSource();
+            projectConncetString = connectionString;
+            using (var conn = source.GetConnection(connectionString))
+            {
+                databaseName = conn.Database;
+            }
+
+            pmtConncetString = ConfigurationManager.Settings.GetConfig<CsiSectionConfig>("CSI").DbConnection.ReadConnectionString;
+            if (projectId != -1)
+            {
+                try
+                {
+                    using (var conn = source.GetConnection(pmtConncetString))
+                    {
+                        var databaseType = SqlMapper.Query<int?>(conn, get_databaseType, new { projectId = projectId }).FirstOrDefault();
+                        source.databaseType = databaseType == 1 ? DatabaseType.SQLServer : DatabaseType.MySQL;
+                    }
+                }
+                catch (Exception ex)
+                {
+                    LoggerManager.Logger.Error(ex, "Get database type failed.");
+                    throw ex;
+                }
+            }
+        }
+
+        public List<DefaultACITable> ListTables()
+        {
+            try
+            {
+                using (var conn = source.GetConnection(projectConncetString, source.databaseType))
+                {
+                    if (conn.State == ConnectionState.Closed) conn.Open();
+                    //choose sqls
+                    var query_all_columns = source.databaseType == DatabaseType.MySQL ? query_all_column_names : query_all_column_names_sqlserver;
+                    var query_all_tables = source.databaseType == DatabaseType.MySQL ? query_table_names : query_table_names_sqlserver;
+                    //get columns
+                    var columns = SqlMapper.Query<DefaultACIColumn>(conn, string.Format(query_all_columns, databaseName)).ToList();
+                    //get tables
+                    var data = SqlMapper.Query<DefaultACITable>(conn, string.Format(query_all_tables, databaseName));
+                    //make result for return
+                    data.ForEach(t =>
+                    {
+                        t.title = t.description;
+                        t.columns = columns.Where(c => c.table == t.name).ToList();
+                    });
+                    return data.ToList();
+                }
+            }
+            catch (Exception ex)
+            {
+                LoggerManager.Logger.Error(ex, "Get list tables failed.");
+                throw new Exception("获取项目数据库表信息出错。", ex);
+            }
+        }
+
+        public List<JObject> ListAPIForProject(string projectName, string moduleName)
+        {
+            var result = new List<JObject>();
+            try
+            {
+                using (var conn = source.GetConnection(pmtConncetString))
+                {
+                    var param = new { projectName = projectName };
+                    IEnumerable<dynamic> apis = SqlMapper.Query(conn, query_api_for_project, param);
+
+                    foreach (var item in apis)
+                    {
+                        // If moduleName is not specified, list all.
+                        if (string.IsNullOrEmpty(moduleName) || item.module == moduleName)
+                        {
+                            JObject ca = JObject.FromObject(item);
+                            ObjectProperties.ForEach(p => ca[p] = JToken.Parse(ca.Value<string>(p) ?? "{}"));
+                            result.Add(ca);
+                        }
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                LoggerManager.Logger.Error(ex, "Get list api for project failed.");
+                throw new Exception("获取项目接口信息出错。", ex);
+            }
+
+            return result;
+        }
+
+        public void GenerateCSI(DefaultACITable dACITable)
+        {
+            var TableName = char.ToUpper(dACITable.name[0]) + dACITable.name.Substring(1);
+            var tableName = dACITable.name;
+            var primaryKey = dACITable.columns.FirstOrDefault(c => c.primary_key)?.name;
+            var columnNames = string.Join(", ", dACITable.columns.Select(c => c.name));
+            var columnValues = string.Join(", ", dACITable.columns.Select(c => "@" + c.name));
+            var nameValues = string.Join(", ", dACITable.columns.Select(c => $"{c.name} = @{c.name}"));
+
+            var text = File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Template//template.json"));
+
+            var result = text.Replace("#TableName#", TableName)
+                            .Replace("#tableName#", tableName)
+                            .Replace("#primaryKey#", primaryKey)
+                            .Replace("#columnNames#", columnNames)
+                            .Replace("#columnValues#", columnValues)
+                            .Replace("#nameValues#", nameValues);
+        }
+    }
+}

+ 39 - 0
src/Tools/Wicture.DbRESTFul.PMT/Service/UserIdentifier.cs

@@ -0,0 +1,39 @@
+using Autofac;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Wicture.DbRESTFul.Authentication;
+using Wicture.DbRESTFul.PMT.Repositories;
+
+namespace Wicture.DbRESTFul.PMT.Service
+{
+    public class UserIdentifier : IUserIdentifier
+    {
+        private ILogger _logger;
+
+        public UserIdentifier(ILoggerFactory loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger("DbRESTFulApi");
+        }
+
+        public async Task AuthorizeAsync(ExecutionContext context)
+        {
+            _logger.LogInformation($"Authorize {context.Api.name} access for {context.Identity.Name}.");
+            context.Identity.IsAuthenticated = true;
+            await Task.CompletedTask;
+        }
+
+        public async Task<UserIdentity> IdentifyAsync(string username, string password, JToken param, ExecutionContext context = null)
+        {
+            _logger.LogDebug($"Login for `{username}` with `{password}`.");
+
+            using (var scope = AutofacContainer.Container.BeginLifetimeScope())
+            {
+                var repository = scope.Resolve<UserRepository>();
+                return await repository.GetUserIdentity(username, password);
+            }
+        }
+    }
+}

Файловите разлики са ограничени, защото са твърде много
+ 18 - 0
src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/ApiSpecGenerator.Partial.cs


+ 747 - 0
src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/ApiSpecGenerator.cs

@@ -0,0 +1,747 @@
+// ------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version: 14.0.0.0
+//  
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+// ------------------------------------------------------------------------------
+namespace Wicture.DbRESTFul.PMT.SpecGenerators
+{
+    using System.Collections.Generic;
+    using Newtonsoft.Json.Linq;
+    using System;
+    using System.Reflection;
+    using Newtonsoft.Json;
+    using Wicture.DbRESTFul.Resources.Api;
+
+    /// <summary>
+    /// Class to produce the template output
+    /// </summary>
+
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")]
+    public partial class ApiSpecGenerator : ApiSpecGeneratorBase
+    {
+        /// <summary>
+        /// Create the template output
+        /// </summary>
+        public virtual string TransformText()
+        {
+            this.Write("\r\n");
+            this.Write("\r\n");
+
+            string[] tableTile = { "名称", "参数类型", "是否可空", "说明" };
+            string[] tableTileErrorCode = { "错误码", "错误描述" };
+            this.Write("\r\n<!--header-->\r\n<!DOCTYPE html>\r\n<html>\r\n");
+            this.Write(this.ToStringHelper.ToStringWithCulture(HeaderGenerator(styleName)));
+            this.Write("\r\n<body class=\"markdown ");
+            this.Write(this.ToStringHelper.ToStringWithCulture(styleName));
+            this.Write("\">\r\n\t\t\t\r\n<!--title-->\r\n");
+            this.Write(this.ToStringHelper.ToStringWithCulture(string.Format("<h1 id=\"服务接口({0})\">服务接口({0})</h1>", apis[0]["module"])));
+            this.Write("\r\n\r\n<!--navigation-->\r\n");
+            string navigations = "版本更新(Version)";
+            foreach (var item in apis)
+            {
+                navigations += "," + item["title"] + "(" + item["name"] + ")";
+            }
+
+            this.Write("\r\n<hr class=\"page\">\r\n");
+            this.Write(this.ToStringHelper.ToStringWithCulture(TOCGenerator(navigations.Split(','))));
+            this.Write("\r\n<hr class=\"page\">\r\n\r\n<!--version title-->\r\n<h2 id=\"版本更新(Version)\">版本更新(Version)" +
+                    "</h2>\r\n<!--version table-->\r\n");
+
+            String[] titleVersion = { "Version", "UpdatedBy", "UpdatedDate", "Note" };
+
+            List<string[]> titleInfo = new List<string[]>();
+
+            titleInfo.Add(new string[] { "0.0.1", "Wicture", DateTime.Now.ToString("yyyy/MM/dd"), "Auto Generated" });
+            this.Write(this.ToStringHelper.ToStringWithCulture(TableGenerator(titleVersion, titleInfo)));
+
+            this.Write("\r\n\r\n\r\n");
+            string[] bodyNavigation = { "功能定义", "版本", "负责人", "最后更新时间", "是否实现", "是否支持匿名", "URL", "类型", "Request参数", "Body参数", "返回结果", "说明", "错误码" };
+
+            this.Write("\r\n");
+            foreach (var item in apis)
+            {
+                this.Write("\t\t\t\r\n<!--body title-->\r\n");
+                this.Write(this.ToStringHelper.ToStringWithCulture(string.Format("<h2 id=\"{0}({1})\">{0}({1})</h2>", item.Value<string>("title"), item.Value<string>("name"))));
+                this.Write("\r\n\r\n\t");
+                var bodyContext = "<ul>";
+                this.Write("\r\n\t");
+                var data = item["result"].Value<Newtonsoft.Json.Linq.JArray>("schema");
+                this.Write("\t");
+                var result = new Newtonsoft.Json.Linq.JObject();
+                this.Write("\t\t");
+
+                //若mock对应的output有值,则显示对应output;若没有,则显示默认的请求参数
+                var outputObj = item.Value<JObject>("output");
+                if (outputObj != null) result = outputObj;
+
+                foreach (Newtonsoft.Json.Linq.JObject obj in data)
+                {
+                    this.Write("\t\t\t");
+                    if (obj.Value<string>("name") == "data")
+                    {
+                        this.Write("\t\t\t\t");
+                        var shceme = obj.Value<Newtonsoft.Json.Linq.JArray>("schema");
+                        this.Write("\t\t\t\t");
+                        var value = new Newtonsoft.Json.Linq.JObject();
+                        this.Write("\t\t\t\t");
+
+                        if (shceme != null)
+                        {
+                            foreach (var row in shceme)
+                            {
+                                this.Write("\t\t\t\t\t");
+                                if (row["schema"] == null || !row["schema"].HasValues)
+                                {
+                                    value.Add(row.Value<string>("name"), " ");
+                                }
+                                else
+                                {
+                                    JToken property = null;
+                                    if (row.Value<string>("type") == "object") property = TransformSchema(row.Value<JArray>("schema"));
+                                    else if (row.Value<string>("type") == "array") property = new JArray { TransformSchema(row.Value<JArray>("schema")) };
+
+                                    if (property != null) value.Add(row.Value<string>("name"), property);
+                                }
+                                this.Write("\t\t\t\t");
+                            }
+                        }
+
+                        this.Write("\t\t\t\t");
+
+                        if (outputObj == null)
+                        {
+                            if (obj.Value<string>("type") == "array") result.Add(obj.Value<string>("name"), new JArray() { value });
+                            else result.Add(obj.Value<string>("name"), value);
+                        }
+
+                        this.Write("\t\t\t");
+                    }
+                    else
+                    {
+                        this.Write("\t\t\t\t");
+                        if (outputObj == null)
+                        {
+                            if (obj["schema"] == null || !obj["schema"].HasValues) { result.Add(obj.Value<string>("name"), " "); }
+                            else { result.Add(obj.Value<string>("name"), TransformSchema(obj.Value<JArray>("schema"))); }
+                        }
+                        this.Write("\t\t\t");
+                    }
+
+                    this.Write("\t\t");
+                }
+
+                this.Write("\t\t\t\t\r\n\r\n\t<!--build body-->\r\n\t");
+
+                foreach (var nav in bodyNavigation)
+                {
+                    this.Write("\t\t");
+                    bodyContext += "<li><strong>" + nav + "</strong>:";
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+
+                    if (nav == "功能定义")
+                    {
+                        this.Write("\t\t\t<!--功能定义-->\r\n\t\t\t");
+                        bodyContext += " " + item.Value<string>("summary");
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+                    if (nav == "版本")
+                    {
+                        this.Write("\t\t\t<!--版本-->\t\r\n\t\t\t");
+                        bodyContext += " <code>" + item.Value<string>("version") + "</code>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+
+                    if (nav == "负责人")
+                    {
+                        this.Write("\t\t\t<!--负责人-->\t\r\n\t\t\t");
+                        bodyContext += " <code>" + item.Value<string>("owner") + "</code>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+
+                    if (nav == "最后更新时间")
+                    {
+                        this.Write("\t\t\t<!--最后更新时间-->\r\n\t\t\t");
+                        bodyContext += " <code>" + DateTime.Now.ToString("yyyy/MM/dd") + "</code>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+
+                    if (nav == "是否实现")
+                    {
+                        this.Write("\t\t\t<!--是否实现-->\r\n\t\t\t");
+                        bodyContext += " <code>" + (item.Value<bool>("implemented") ? " 是" : " 否") + "</code>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+
+                    if (nav == "是否支持匿名")
+                    {
+                        this.Write("\t\t\t<!--是否支持匿名-->\r\n\t\t\t");
+                        bodyContext += " <code>" + (item.Value<bool?>("allowAnonymous") == true ? " 是" : " 否") + "</code>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+                    
+                    if (nav == "URL")
+                    {
+                        this.Write("\t\t\t<!--URL-->\r\n\t\t\t");
+                        if (item.Value<int?>("useAbsoluteUrl") == 1)
+                            bodyContext += " <code>" + "http://host&lt:port&gt" + item.Value<string>("url") + "</code>";
+                        else
+                            bodyContext += " <code>" + "http://host&lt:port&gt/api/" + item.Value<string>("module") + "/" + item.Value<string>("url") + "</code>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\r\n\t\t");
+
+                    if (nav == "类型")
+                    {
+                        this.Write("\t\t\t<!--类型-->\r\n\t\t\t");
+                        bodyContext += " <code>" + item.Value<string>("method") + "</code>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\r\n\t\t");
+
+                    var parameter = item["parameter"] as JObject;
+
+                    this.Write("\t\t\r\n\t\t");
+
+                    if (nav == "Request参数")
+                    {
+                        this.Write("\t\t\t");
+                        if (parameter != null && parameter.Value<JArray>("query") != null && parameter.Value<JArray>("query").Count > 0)
+                        {
+                            this.Write("\t\t\t\t");
+                            var parameterStr = "";
+                            this.Write("\t\t\t\t");
+                            var tableBody1 = new List<string[]>();
+                            this.Write("\t\t\t\t");
+
+                            foreach (var column in parameter["query"])
+                            {
+                                this.Write("\t\t\t\t\t");
+                                parameterStr += column.Value<string>("name") + "=&lt" + column.Value<string>("name") + "&gt"+"&";
+                                this.Write("\t\t\t\t\t");
+                                var row = new[] { column.Value<string>("name"), column.Value<string>("type"), (column.Value<bool>("nullable") ? "是" : "否"), column.Value<string>("description") };
+                                this.Write("\t\t\t\t\t");
+                                tableBody1.Add(row);
+                                this.Write("\t\t\t\t");
+                            }
+
+                            this.Write("\r\n\t\t\t\t<!--*Request参数-->\r\n\t\t\t\t");
+                            bodyContext += "<ul>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "<li>" + "参数:" + "<br>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "<code>" + "?" + parameterStr.TrimEnd('&') + "</code>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</li>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "<li>" + "参数说明:" + "<br>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += TableGenerator(tableTile, tableBody1);
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</li>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</ul>";
+                            this.Write("\t\t\t");
+                        }
+                        else
+                        {
+                            bodyContext += "无";
+                        }
+
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\r\n\t\t");
+
+                    if (nav == "Body参数")
+                    {
+                        this.Write("\t\t\t");
+
+                        if (parameter != null && parameter.Value<JArray>("body") != null && parameter.Value<JArray>("body").Count > 0)
+                        {
+                            this.Write("\t\t\t\t<!--*Body参数-->\r\n\t\t\t\t");
+                            var parameterObj = new Newtonsoft.Json.Linq.JObject();
+                            this.Write("\t\t\t\t");
+                            var tableBody2 = new List<string[]>();
+                            this.Write("\t\t\t\t");
+
+                            //若mock对应的input有值,则显示对应input;若没有,则显示默认的请求参数
+                            var inputObj = item.Value<JObject>("input");
+                            if (inputObj != null) parameterObj = inputObj;
+
+                            foreach (var column in parameter["body"])
+                            {
+                                this.Write("\t\t\t\t\t");
+                                if (inputObj == null) parameterObj.Add(column.Value<string>("name"), "");
+                                this.Write("\t\t\t\t\t");
+                                var row = new[] { column.Value<string>("name"), column.Value<string>("type"), (column.Value<bool>("nullable") ? "是" : "否"), column.Value<string>("description") };
+                                this.Write("\t\t\t\t\t");
+                                tableBody2.Add(row);
+                                this.Write("\t\t\t\t");
+                                SchemaRecursion(column, tableBody2);
+                            }
+
+
+                            this.Write("\t\t\t\t");
+                            bodyContext += "<ul>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "<li>" + "参数:" + "<br>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += JsonGeneratetor(parameterObj);
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</li>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "<li>" + "参数说明:" + "<br>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += TableGenerator(tableTile, tableBody2);
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</li>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</ul>";
+                            this.Write("\t\t\t");
+                        }
+                        else
+                        {
+                            bodyContext += "无";
+                        }
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+                    if (nav == "返回结果")
+                    {
+                        this.Write("\t\t\t<!--*返回结果-->\r\n\t\t\t");
+                        bodyContext += "<ul>";
+                        this.Write("\t\t\t");
+                        bodyContext += "<li>" + "返回结果:" + "<br>";
+                        this.Write("\t\t\t");
+                        bodyContext += JsonGeneratetor(result);
+                        this.Write("\t\t\t");
+                        bodyContext += "</li>";
+                        this.Write("\t\t\t");
+                        bodyContext += "<li>" + "参数说明:" + "<br>";
+                        this.Write("\t\t\t");
+                        bodyContext += TableGenerator(tableTile, ListResultSchema(item));
+                        this.Write("\t\t\t");
+                        bodyContext += "</li>";
+                        this.Write("\t\t\t");
+                        bodyContext += "</ul>";
+                        this.Write("\t\t");
+                    }
+                    this.Write("\r\n\t\t");
+
+                    if (nav == "说明")
+                    {
+                        this.Write("\t\t\t<!--*说明-->\r\n\t\t\t");
+                        bodyContext += " <blockquote>";
+                        this.Write("\t\t\t");
+                        bodyContext += item.Value<string>("note")?.Replace("\n\r", "<br >");
+                        this.Write("\t\t\t");
+                        bodyContext += "<blockquote>";
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\t\t\t\t\t\r\n\t\t");
+
+                    var exceptions = item["exceptions"];
+                    if (nav == "错误码")
+                    {
+                        this.Write("\t\t\t");
+                        if (exceptions != null&&!string.IsNullOrEmpty(exceptions.ToString()))
+                        {
+                            this.Write("\t\t\t\t");
+                            var tableBody2 = new List<string[]>();
+                            this.Write("\t\t\t\t");
+
+                            this.Write("\r\n\t\t\t\t<!--*错误码-->\r\n\t\t\t\t");
+                            bodyContext += "<ul>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "<li>" + "错误码:" + "<br>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += TableGenerator(tableTileErrorCode, tableBody2);
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</li>";
+                            this.Write("\t\t\t\t");
+                            bodyContext += "</ul>";
+                            this.Write("\t\t\t");
+                        }
+                        else
+                        {
+                            bodyContext += "无";
+                        }
+
+                        this.Write("\t\t");
+                    }
+
+                    this.Write("\r\n\t\t");
+                    bodyContext += "</li>";
+                    this.Write("\t");
+                }
+
+                this.Write("\r\n\t");
+                bodyContext += "</ul>";
+                this.Write("\t");
+                this.Write(this.ToStringHelper.ToStringWithCulture(bodyContext));
+                this.Write("\r\n");
+            }
+
+            this.Write("\r\n<!--footer-->\r\n");
+            this.Write(this.ToStringHelper.ToStringWithCulture(FooterGenerator()));
+            this.Write("\r\n</body>\r\n</html>\r\n\r\n\r\n\r\n\r\n\t\t\t\r\n");
+            return this.GenerationEnvironment.ToString();
+        }
+
+        string JsonGeneratetor(Newtonsoft.Json.Linq.JObject jsonItem, string spacName = "default")
+        {
+            return string.Format("<pre class=\"json hljs\"><code class=\"json\" >{0}</code></pre>", jsonItem.ToString());
+        }
+
+        string TOCGenerator(string[] items, string spacName = "default")
+        {
+            var toc = "<ul>";
+            foreach (string item in items)
+            {
+                toc += string.Format("<li><span class=\"title\"><a href=\"#{0}\" title=\"{0}\">{0}</a></span></li>", item);
+            }
+            toc += "</ul>";
+            return toc;
+        }
+
+
+        string TableGenerator(string[] titles, List<string[]> body)
+        {
+            var table = "<table>";
+            var theader = "<thead><tr>";
+            var tbody = "<tbody>";
+            //build table head
+            foreach (var item in titles)
+            {
+                var column = string.Format("<th>{0}</th>", item);
+                theader += column;
+            }
+            theader += "</tr></thead>";
+            table += theader;
+
+            //build table body
+            foreach (var row in body)
+            {
+                tbody += "<tr>";
+                foreach (var column in row)
+                {
+                    tbody += string.Format("<td>{0}</td>", column);
+                }
+                tbody += "</tr>";
+            }
+            tbody += "</tbody>";
+            table += tbody;
+
+            return table += "</table>";
+        }
+
+        string HeaderGenerator(string styleName)
+        {
+            var style = styles[styleName];
+            var header = "<head>    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />    <title>" + moduleTitle + "</title>    <meta name=\"generator\" content=\"Haroopad 0.13.1\" />    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">      <style>" + style + "</style>  </head>";
+            return header;
+        }
+
+        string FooterGenerator()
+        {
+            var footer = "<footer style=\"position:fixed; font-size:.8em; text-align:right; bottom:0px; margin-left:-25px; height:20px; width:100%;\">Generated by <a href=\"http://www.wicture.com\" target=\"_blank\">Wicture</a></footer>";
+            return footer;
+        }
+
+        JObject TransformSchema(JArray data)
+        {
+            JObject result = new JObject();
+            if (data != null)
+            {
+                foreach (var item in data)
+                {
+                    if (item["schema"] != null && item["schema"].HasValues)
+                    {
+                        result.Add(item.Value<string>("name"), new JArray { TransformSchema(item.Value<JArray>("schema")) });
+                    }
+                    else
+                    {
+                        result.Add(item.Value<string>("name"), " ");
+                    }
+                }
+            }
+            return result;
+        }
+
+        void SchemaRecursion(JToken column, List<string[]> tableBody)
+        {
+            if (column["schema"] != null && column["schema"].HasValues)
+            {
+                foreach (var rowItem in column.Value<Newtonsoft.Json.Linq.JArray>("schema"))
+                {
+                    this.Write("\t\t\t\t\t");
+                    var row = new[] { rowItem.Value<string>("name"), rowItem.Value<string>("type"), (rowItem.Value<bool>("nullable") ? "是" : "否"), rowItem.Value<string>("description") };
+                    this.Write("\t\t\t\t\t");
+                    tableBody.Add(row);
+                    this.Write("\t\t\t\t");
+                    SchemaRecursion(rowItem, tableBody);
+                    this.Write("\t\t\t\t");
+                }
+            }
+        }
+    }
+
+    #region Base class
+    /// <summary>
+    /// Base class for this transformation
+    /// </summary>
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")]
+    public class ApiSpecGeneratorBase
+    {
+        #region Fields
+        private global::System.Text.StringBuilder generationEnvironmentField;
+        private global::System.Collections.Generic.List<int> indentLengthsField;
+        private string currentIndentField = "";
+        private bool endsWithNewline;
+        private global::System.Collections.Generic.IDictionary<string, object> sessionField;
+        #endregion
+        #region Properties
+        /// <summary>
+        /// The string builder that generation-time code is using to assemble generated output
+        /// </summary>
+        protected System.Text.StringBuilder GenerationEnvironment
+        {
+            get
+            {
+                if ((this.generationEnvironmentField == null))
+                {
+                    this.generationEnvironmentField = new global::System.Text.StringBuilder();
+                }
+                return this.generationEnvironmentField;
+            }
+            set
+            {
+                this.generationEnvironmentField = value;
+            }
+        }
+        /// <summary>
+        /// A list of the lengths of each indent that was added with PushIndent
+        /// </summary>
+        private System.Collections.Generic.List<int> indentLengths
+        {
+            get
+            {
+                if ((this.indentLengthsField == null))
+                {
+                    this.indentLengthsField = new global::System.Collections.Generic.List<int>();
+                }
+                return this.indentLengthsField;
+            }
+        }
+        /// <summary>
+        /// Gets the current indent we use when adding lines to the output
+        /// </summary>
+        public string CurrentIndent
+        {
+            get
+            {
+                return this.currentIndentField;
+            }
+        }
+        /// <summary>
+        /// Current transformation session
+        /// </summary>
+        public virtual global::System.Collections.Generic.IDictionary<string, object> Session
+        {
+            get
+            {
+                return this.sessionField;
+            }
+            set
+            {
+                this.sessionField = value;
+            }
+        }
+        #endregion
+        #region Transform-time helpers
+        /// <summary>
+        /// Write text directly into the generated output
+        /// </summary>
+        public void Write(string textToAppend)
+        {
+            if (string.IsNullOrEmpty(textToAppend))
+            {
+                return;
+            }
+            // If we're starting off, or if the previous text ended with a newline,
+            // we have to append the current indent first.
+            if (((this.GenerationEnvironment.Length == 0)
+                        || this.endsWithNewline))
+            {
+                this.GenerationEnvironment.Append(this.currentIndentField);
+                this.endsWithNewline = false;
+            }
+            // Check if the current text ends with a newline
+            if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
+            {
+                this.endsWithNewline = true;
+            }
+            // This is an optimization. If the current indent is "", then we don't have to do any
+            // of the more complex stuff further down.
+            if ((this.currentIndentField.Length == 0))
+            {
+                this.GenerationEnvironment.Append(textToAppend);
+                return;
+            }
+            // Everywhere there is a newline in the text, add an indent after it
+            textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
+            // If the text ends with a newline, then we should strip off the indent added at the very end
+            // because the appropriate indent will be added when the next time Write() is called
+            if (this.endsWithNewline)
+            {
+                this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
+            }
+            else
+            {
+                this.GenerationEnvironment.Append(textToAppend);
+            }
+        }
+        /// <summary>
+        /// Write text directly into the generated output
+        /// </summary>
+        public void WriteLine(string textToAppend)
+        {
+            this.Write(textToAppend);
+            this.GenerationEnvironment.AppendLine();
+            this.endsWithNewline = true;
+        }
+        /// <summary>
+        /// Write formatted text directly into the generated output
+        /// </summary>
+        public void Write(string format, params object[] args)
+        {
+            this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+        }
+        /// <summary>
+        /// Write formatted text directly into the generated output
+        /// </summary>
+        public void WriteLine(string format, params object[] args)
+        {
+            this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+        }
+        /// <summary>
+        /// Increase the indent
+        /// </summary>
+        public void PushIndent(string indent)
+        {
+            if ((indent == null))
+            {
+                throw new global::System.ArgumentNullException("indent");
+            }
+            this.currentIndentField = (this.currentIndentField + indent);
+            this.indentLengths.Add(indent.Length);
+        }
+        /// <summary>
+        /// Remove the last indent that was added with PushIndent
+        /// </summary>
+        public string PopIndent()
+        {
+            string returnValue = "";
+            if ((this.indentLengths.Count > 0))
+            {
+                int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
+                this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
+                if ((indentLength > 0))
+                {
+                    returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
+                    this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
+                }
+            }
+            return returnValue;
+        }
+        /// <summary>
+        /// Remove any indentation
+        /// </summary>
+        public void ClearIndent()
+        {
+            this.indentLengths.Clear();
+            this.currentIndentField = "";
+        }
+        #endregion
+        #region ToString Helpers
+        /// <summary>
+        /// Utility class to produce culture-oriented representation of an object as a string.
+        /// </summary>
+        public class ToStringInstanceHelper
+        {
+            private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
+            /// <summary>
+            /// Gets or sets format provider to be used by ToStringWithCulture method.
+            /// </summary>
+            public System.IFormatProvider FormatProvider
+            {
+                get
+                {
+                    return this.formatProviderField;
+                }
+                set
+                {
+                    if ((value != null))
+                    {
+                        this.formatProviderField = value;
+                    }
+                }
+            }
+            /// <summary>
+            /// This is called from the compile/run appdomain to convert objects within an expression block to a string
+            /// </summary>
+            public string ToStringWithCulture(object objectToConvert)
+            {
+                if ((objectToConvert == null))
+                {
+                    throw new global::System.ArgumentNullException("objectToConvert");
+                }
+                System.Type t = objectToConvert.GetType();
+                System.Reflection.MethodInfo method = t.GetTypeInfo().GetMethod("ToString", new System.Type[] {
+                            typeof(System.IFormatProvider)});
+                if ((method == null))
+                {
+                    return objectToConvert.ToString();
+                }
+                else
+                {
+                    return ((string)(method.Invoke(objectToConvert, new object[] {
+                                this.formatProviderField })));
+                }
+            }
+        }
+        private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
+        /// <summary>
+        /// Helper to produce culture-oriented representation of an object as a string
+        /// </summary>
+        public ToStringInstanceHelper ToStringHelper
+        {
+            get
+            {
+                return this.toStringHelperField;
+            }
+        }
+        #endregion
+    }
+    #endregion
+}

+ 57 - 0
src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/DbSpecGenerator.Partial.cs

@@ -0,0 +1,57 @@
+using Newtonsoft.Json.Linq;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Wicture.DbRESTFul.PMT.SpecGenerators
+{
+    partial class DbSpecGenerator
+    {
+        private readonly DbSpecSource source;
+        public DbSpecGenerator(JObject config)
+        {
+            List<TableSpec> tables = GetTables(config.Value<string>("connectionString"), config.Value<JArray>("tables"),config.Value<int>("projectId"));
+
+            source = new DbSpecSource
+            {
+                moduleName = config.Value<string>("moduleName"),
+                projectName = config.Value<string>("projectName"),
+                tables = tables
+            };
+        }
+
+        private List<TableSpec> GetTables(string connectionString, JArray tables,int projectId)
+        {
+            SpecHelper manager = new SpecHelper(connectionString,projectId);
+            var result = new List<TableSpec>();
+
+            manager.ListTables().ForEach(t =>
+            {
+                var item = tables.OfType<JObject>().FirstOrDefault(i => i.Value<string>("name") == t.name);
+                if (item != null)
+                {
+                    t.title = item.Value<string>("title");
+                    t.title = string.IsNullOrEmpty(t.title) ? t.name : t.title;
+                    result.Add(t);
+                }
+            });
+
+            return result;
+        }
+
+        public string Generate(string path)
+        {
+            var fileName = string.Format(@"{0}/{1}/Database/{2}.html", path, source.projectName, source.moduleName);
+
+            if (!Directory.Exists(Path.GetDirectoryName(fileName)))
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(fileName));
+            }
+
+            var text = this.TransformText();
+            File.WriteAllText(fileName, text);
+
+            return fileName;
+        }
+    }
+}

Файловите разлики са ограничени, защото са твърде много
+ 22 - 0
src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/DbSpecGenerator.cs


+ 30 - 0
src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/IntroductionGenerator.cs

@@ -0,0 +1,30 @@
+using System.IO;
+
+namespace Wicture.DbRESTFul.PMT.SpecGenerators
+{
+    public class IntroductionGenerator
+    {
+        public string ProjectName { get; set; }
+        public string Introduction { get; set; }
+
+        public IntroductionGenerator(string project, string introduction)
+        {
+            ProjectName = project;
+            Introduction = introduction;
+        }
+
+        public string Generate(string path)
+        {
+            var fileName = string.Format("{0}/{1}/introduction.html", path, ProjectName);
+
+            if (!Directory.Exists(Path.GetDirectoryName(fileName)))
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(fileName));
+            }
+
+            File.WriteAllText(fileName, Introduction, System.Text.Encoding.UTF8);
+
+            return fileName;
+        }
+    }
+}

+ 159 - 0
src/Tools/Wicture.DbRESTFul.PMT/SpecGenerators/SpecHelper.cs

@@ -0,0 +1,159 @@
+using Dapper;
+using MySql.Data.MySqlClient;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using Wicture.DbRESTFul.Configuration;
+using Wicture.DbRESTFul.Resources.Csi;
+
+namespace Wicture.DbRESTFul.PMT.SpecGenerators
+{
+    public class ColumnSpec
+    {
+        public string table { get; set; }
+        public string name { get; set; }
+        public string type { get; set; }
+        public string description { get; set; }
+        public string @default { get; set; }
+        public bool primary_key { get; set; }
+        public bool nullable { get; set; }
+    }
+
+    public class TableSpec
+    {
+        public string title { get; set; }
+        public string name { get; set; }
+        public string description { get; set; }
+
+        public List<ColumnSpec> columns { get; set; }
+    }
+
+    public class DbSpecSource
+    {
+        public string projectName { get; set; }
+        public string moduleName { get; set; }
+        public List<TableSpec> tables { get; set; }
+
+        public DatabaseType databaseType { get; set; }
+
+        public IDbConnection GetConnection(string connectionString, DatabaseType DatabaseType = DatabaseType.MySQL)
+        {
+            if (DatabaseType == DatabaseType.SQLServer) return new SqlConnection(connectionString) as IDbConnection;
+            else return new MySqlConnection(connectionString) as IDbConnection;
+        }
+    }
+
+
+    public class SpecHelper
+    {
+        private readonly string databaseName = string.Empty;
+        private readonly string query_table_names = "SELECT `TABLE_NAME` AS `name`, `TABLE_COMMENT` AS description FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = '{0}' AND `TABLE_TYPE` = 'BASE TABLE';";
+        private readonly string query_table_names_sqlserver = "SELECT st.name,sep.VALUE  [description] FROM 	sys.tables st  LEFT JOIN sys.extended_properties sep ON st.object_id = sep.major_id AND sep.minor_id=0;";
+        private readonly string query_all_column_names = "SELECT `TABLE_NAME` AS `table`, `COLUMN_NAME` AS `name`, `COLUMN_TYPE` AS `type`, (CASE `IS_NULLABLE` WHEN 'NO' THEN 0 ELSE 1 END) AS nullable, (CASE `COLUMN_KEY` WHEN 'PRI' THEN 1 ELSE 0 END) AS primary_key, `COLUMN_DEFAULT` AS `default`,`COLUMN_COMMENT` AS description FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = '{0}';";
+        private readonly string query_all_column_names_sqlserver = "SELECT st.name [table],	sc.is_nullable [nullable],	sc.name [name],	sep.VALUE [description],	TYPE_NAME(sc.system_type_id) [type],0 [default]  FROM 	sys.tables st INNER JOIN sys.columns sc ON st.object_id = sc.object_id LEFT JOIN sys.extended_properties sep ON st.object_id = sep.major_id AND sc.column_id = sep.minor_id;";
+        private readonly string query_api_for_project = "SELECT a.* FROM api AS a LEFT JOIN project AS p ON p.id = a.projectId WHERE p.name = @projectName;";
+        private readonly string query_module_for_project = "select m.`name` as moduleName, m.description as moduleTitle from module as m join project as p on p.id = m.projectId where p.`name` = @projectName;";
+        private readonly string get_databaseType = "SELECT databaseType FROM project WHERE id=@projectId;";
+        private static readonly List<string> ObjectProperties = new List<string> { "cache", "implementation", "parameter", "result", "mock" };
+
+        private readonly DbSpecSource source;
+        private string pmtConncetString = "";
+        private string projectConncetString = "";
+
+
+        public SpecHelper(string connectionString, int projectId = -1)
+        {
+            source = new DbSpecSource();
+            projectConncetString = connectionString;
+            using (var conn = source.GetConnection(connectionString))
+            {
+                databaseName = conn.Database;
+            }
+
+            pmtConncetString = ConfigurationManager.Settings.GetConfig<CsiSectionConfig>("CSI").DbConnection.ReadConnectionString;
+
+            if (projectId != -1)
+            {
+                using (var conn = source.GetConnection(pmtConncetString))
+                {
+                    var databaseType = SqlMapper.Query<int?>(conn, get_databaseType, new { projectId = projectId }).FirstOrDefault();
+                    source.databaseType = databaseType == 1 ? DatabaseType.SQLServer : DatabaseType.MySQL;
+                }
+            }
+        }
+
+        public List<TableSpec> ListTables()
+        {
+            using (var conn = source.GetConnection(projectConncetString, source.databaseType))
+            {
+                if (conn.State == ConnectionState.Closed) conn.Open();
+                //choose sqls
+                var query_all_columns = source.databaseType == DatabaseType.MySQL ? query_all_column_names : query_all_column_names_sqlserver;
+                var query_all_tables = source.databaseType == DatabaseType.MySQL ? query_table_names : query_table_names_sqlserver;
+                //get columns
+                var columns = SqlMapper.Query<ColumnSpec>(conn, string.Format(query_all_columns, databaseName)).ToList();
+                //get tables
+                var data = SqlMapper.Query<TableSpec>(conn, string.Format(query_all_tables, databaseName));
+                //make result for return
+                data.ForEach(t =>
+                {
+                    t.title = t.description;
+                    t.columns = columns.Where(c => c.table == t.name).ToList();
+                });
+                return data.ToList();
+            }
+        }
+
+        public List<JObject> ListAPIForProject(string projectName, string moduleName)
+        {
+            var result = new List<JObject>();
+            using (var conn = source.GetConnection(pmtConncetString))
+            {
+                var param = new { projectName = projectName };
+                IEnumerable<dynamic> apis = SqlMapper.Query(conn, query_api_for_project, param);
+
+                foreach (var item in apis)
+                {
+                    // If moduleName is not specified, list all.
+                    if (string.IsNullOrEmpty(moduleName) || item.module == moduleName)
+                    {
+                        JObject ca = JObject.FromObject(item);
+                        ObjectProperties.ForEach(p =>
+                        {
+                            ca[p] = JToken.Parse(ca.Value<string>(p) ?? "{}");
+
+                            //处理mock属性,将其input与output参数取出来
+                            if (p == "mock")
+                            {
+                                var mockerArr = ca.Value<JArray>(p) ?? new JArray();
+                                //var mockerArr = JArray.FromObject(ca.Value<string>(p));
+
+                                if (mockerArr != null && mockerArr.Count > 0)
+                                {
+                                    ca["input"] = mockerArr[0].Value<JObject>("input");
+                                    ca["output"] = mockerArr[0].Value<JObject>("output");
+                                }
+                            }
+                        }
+                        );
+                        result.Add(ca);
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        public dynamic ListModules(string projectName)
+        {
+            using (var conn = source.GetConnection(pmtConncetString))
+            {
+                var param = new { projectName = projectName };
+                return SqlMapper.Query(conn, query_module_for_project, param);
+            }
+        }
+    }
+}

+ 72 - 0
src/Tools/Wicture.DbRESTFul.PMT/Startup.cs

@@ -0,0 +1,72 @@
+using Autofac;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Hosting;
+using System.IO;
+
+namespace Wicture.DbRESTFul.PMT
+{
+    public class Startup
+    {
+        public Startup(IConfiguration configuration)
+        {
+            Configuration = configuration;
+        }
+
+        public IConfiguration Configuration { get; }
+
+        // This method gets called by the runtime. Use this method to add services to the container.
+        public void ConfigureServices(IServiceCollection services)
+        {
+            services.AddControllersWithViews().AddNewtonsoftJson();
+            services.AddDbRESTFul();
+        }
+
+        public void ConfigureContainer(ContainerBuilder builder)
+        {
+            builder.RegisterDbRESTFul();
+        }
+
+        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+        {
+            if (env.IsDevelopment())
+            {
+                app.UseDeveloperExceptionPage();
+            }
+
+            if (!env.IsDevelopment()) app.UseExceptionHandler("/Home/Error");
+            else
+            {
+                app.UseDeveloperExceptionPage();
+            }
+
+            app.UseStaticFiles();
+            app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
+            var docPath = Path.Combine(env.ContentRootPath, "doc/");
+            if (!Directory.Exists(docPath)) Directory.CreateDirectory(docPath);
+
+            app.UseFileServer(new FileServerOptions()
+            {
+                FileProvider = new PhysicalFileProvider(docPath),
+                RequestPath = new PathString("/doc"),
+                EnableDirectoryBrowsing = false
+            });
+
+            app.UseRouting();
+
+            app.UseEndpoints(endpoints =>
+            {
+                endpoints.MapControllerRoute(
+                    name: "default",
+                    pattern: "{controller=Home}/{action=Index}/{id?}");
+            });
+
+            app.UseDbRESTFul();
+        }
+    }
+}

+ 18 - 0
src/Tools/Wicture.DbRESTFul.PMT/TODO.txt

@@ -0,0 +1,18 @@
+TODO:
+1. Check if api name exists in the Create/Edit/Clone api.
+2. Tool for creating param & data schema (from database).
+3. Tool for CSI to Generate SQL.
+
+
+
+
+Reference:
+1. https://www.youtube.com/watch?v=huvBEB3suoo
+
+2. Testing
+	b) Test case management
+3. Service management
+	a) Services list
+	b) Service upload and version management
+	c) Service deployment
+	d) Service Monitoring

+ 7 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/Home/About.cshtml

@@ -0,0 +1,7 @@
+@{
+    ViewData["Title"] = "About";
+}
+<h2>@ViewData["Title"].</h2>
+<h3>@ViewData["Message"]</h3>
+
+<p>Use this area to provide additional information.</p>

+ 111 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/Home/ConfiguredApi.cshtml

@@ -0,0 +1,111 @@
+@{
+    Layout = "";
+}
+
+<!DOCTYPE html>
+<html lang="en" ng-app="pmt" class="fixed js flexbox flexboxlegacy no-touch csstransforms csstransforms3d no-overflowscrolling no-mobile-device custom-scroll sidebar-left-xs">
+
+<head>
+    <!-- Basic -->
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+
+    <title>Wicture 开发管理平台 | Api管理</title>
+    <meta name="keywords" content="HTML5 Admin Template" />
+    <meta name="description" content="Porto Admin - Responsive HTML5 Template">
+    <meta name="author" content="okler.net">
+
+    <!-- Mobile Metas -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+
+    <!-- Vendor CSS -->
+    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
+    <link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.css" />
+    <link rel="stylesheet" href="~/lib/bootstrap-datepicker/dist/css/bootstrap-datepicker3.css" />
+    <!-- Specific Page Vendor CSS -->
+    <link rel="stylesheet" href="~/lib/jquery-ui/themes/ui-lightness/jquery-ui.css" />
+    <link rel="stylesheet" href="~/lib/bootstrap-multiselect/dist/css/bootstrap-multiselect.css" />
+    <link rel="stylesheet" href="~/lib/nanoscroller/bin/css/nanoscroller.css" />
+    <link rel="stylesheet" href="~/lib/magnific-popup/dist/magnific-popup.css" />
+    <link rel="stylesheet" href="~/lib/angularjs-toaster/toaster.css">
+    <link rel="stylesheet" href="~/lib/codemirror/lib/codemirror.css">
+    <link rel="stylesheet" href="~/lib/codemirror/theme/twilight.css" />
+
+    <!-- Theme CSS -->
+    <link rel="stylesheet" href="~/css/theme.css" />
+
+    <!-- Theme Custom CSS -->
+    <link rel="stylesheet" href="~/css/theme-custom.css">
+    <link rel="stylesheet" href="~/css/site.css">
+
+    <!-- Head Libs -->
+    <script src="~/js/modernizr.js"></script>
+</head>
+
+<body>
+    <section class="body">
+        <div ng-include="'/views/includes/header.html'" ng-controller="headerCtrl"></div>
+        <div class="inner-wrapper">
+            <div ng-include="'/views/includes/navigation.html'" ng-controller="navigationCtrl"></div>
+            <section role="main" class="content-body">
+                <div ui-view="main"></div>
+            </section>
+        </div>
+    </section>
+
+    <toaster-container toaster-options="{'close-button':{ 'toast-warning': true, 'toast-error': false } }"></toaster-container>
+
+    <!-- Vendor -->
+    <script src="~/lib/jquery/dist/jquery.js"></script>
+    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
+    <script src="~/lib/bootstrap-datepicker/js/bootstrap-datepicker.js"></script>
+    <script src="~/lib/nanoscroller/bin/javascripts/jquery.nanoscroller.min.js"></script>
+    <script src="~/lib/magnific-popup/dist/jquery.magnific-popup.js"></script>
+    <script src="~/lib/bootbox.js/bootbox.js"></script>
+    <script src="~/lib/codemirror/lib/codemirror.js"></script>
+
+    <!-- Specific Page Vendor -->
+    <script src="~/lib/jquery-ui/jquery-ui.min.js"></script>
+    <script src="~/lib/appear/jquery.appear.js"></script>
+    <script src="~/lib/bootstrap-multiselect/dist/js/bootstrap-multiselect.js"></script>
+
+    <!-- Theme Base, Components and Settings -->
+    <script src="~/js/admin/theme.js"></script>
+
+    <!-- Theme Custom -->
+    <script src="~/js/admin/theme.custom.js"></script>
+
+    <!-- Theme Initialization Files -->
+    <script src="~/js/theme.init.js"></script>./lib
+
+
+    <script type='text/javascript' src='~/lib/angular/angular.js'></script>
+    <script type='text/javascript' src='~/lib/angular-resource/angular-resource.js'></script>
+    <script type='text/javascript' src='~/lib/angular-animate/angular-animate.js'></script>
+    <script type='text/javascript' src='~/lib/angular-route/angular-route.js'></script>
+    <script type='text/javascript' src='~/lib/ui-router/release/angular-ui-router.js'></script>
+    <script type='text/javascript' src='~/lib/moment/moment.js'></script>
+    <script type='text/javascript' src='~/lib/bootstrap-daterangepicker/daterangepicker.js'></script>
+    <script type='text/javascript' src='~/lib/angular-daterangepicker/js/angular-daterangepicker.js'></script>
+    <script type='text/javascript' src='~/lib/ng-table/dist/ng-table.js'></script>
+    <script type='text/javascript' src='~/lib/angular-bootstrap/ui-bootstrap.js'></script>
+    <script type='text/javascript' src='~/lib/angular-bootstrap/ui-bootstrap-tpls.js'></script>
+    <script type='text/javascript' src='~/lib/angular-swx-session-storage/release/swx-session-storage.js'></script>
+    <script type='text/javascript' src='~/lib/angularjs-toaster/toaster.js'></script>
+    <script type='text/javascript' src='~/lib/angular-bootstrap-datetimepicker/src/js/datetimepicker.js'></script>
+    <script type='text/javascript' src='~/lib/angularjs-toaster/toaster.js'></script>
+    <script type='text/javascript' src="~/lib/angular-ui-codemirror/ui-codemirror.js"></script>
+
+    <!-- build:appjs -->
+    <script type='text/javascript' src='~/js/app.js'></script>
+    <script type='text/javascript' src='~/js/directives.js'></script>
+    <script type='text/javascript' src='~/js/routes.js'></script>
+    <script type='text/javascript' src='~/js/controllers.js'></script>
+
+    <script type='text/javascript' src='~/js/core/project.js'></script>
+    <script type='text/javascript' src='~/js/core/api.js'></script>
+
+    <!-- endbuild -->
+</body>
+</html>
+

+ 17 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/Home/Contact.cshtml

@@ -0,0 +1,17 @@
+@{
+    ViewData["Title"] = "Contact";
+}
+<h2>@ViewData["Title"].</h2>
+<h3>@ViewData["Message"]</h3>
+
+<address>
+    One Microsoft Way<br />
+    Redmond, WA 98052-6399<br />
+    <abbr title="Phone">P:</abbr>
+    425.555.0100
+</address>
+
+<address>
+    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br />
+    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
+</address>

+ 168 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/Home/Index.cshtml

@@ -0,0 +1,168 @@
+@{
+    Layout = "";
+}
+
+<!DOCTYPE html>
+<html lang="en" ng-app="pmt" class="fixed js flexbox flexboxlegacy no-touch csstransforms csstransforms3d no-overflowscrolling no-mobile-device custom-scroll sidebar-left-xs">
+
+<head>
+    <!-- Basic -->
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+
+    <title>Wicture 开发管理平台 | Api管理</title>
+    <meta name="keywords" content="HTML5 Admin Template" />
+    <meta name="description" content="Porto Admin - Responsive HTML5 Template">
+    <meta name="author" content="okler.net">
+
+    <!-- Mobile Metas -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+
+    <!-- Vendor CSS -->
+    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
+    <link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.css" />
+    <link rel="stylesheet" href="~/lib/bootstrap-datepicker/dist/css/bootstrap-datepicker3.css" />
+    <link rel="stylesheet" href="~/lib/jquery.fancytree/dist/skin-win8/ui.fancytree.css" />
+    <!-- Specific Page Vendor CSS -->
+    <link rel="stylesheet" href="~/lib/jquery-ui/themes/ui-lightness/jquery-ui.css" />
+    <link rel="stylesheet" href="~/lib/bootstrap-multiselect/dist/css/bootstrap-multiselect.css" />
+    <link rel="stylesheet" href="~/lib/nanoscroller/dist/css/nanoscroller.css" />
+    <link rel="stylesheet" href="~/lib/magnific-popup/dist/magnific-popup.css" />
+    <link rel="stylesheet" href="~/lib/angularjs-toaster/toaster.css">
+    <link rel="stylesheet" href="~/lib/codemirror/lib/codemirror.css">
+    <link rel="stylesheet" href="~/lib/codemirror/theme/twilight.css" />
+
+    <link rel="stylesheet" href="~/lib/editor.md/css/editormd.css" />
+    <link rel="stylesheet" type="text/css" href="~/lib/angular-ui-layout/src/ui-layout.css" />
+    <!-- Theme CSS -->
+    <link rel="stylesheet" href="~/css/theme.css" />
+
+    <!-- Theme Custom CSS -->
+    <link rel="stylesheet" href="~/css/theme-custom.css">
+    <link rel="stylesheet" href="~/css/site.css">
+
+    <!-- Head Libs -->
+    <script src="~/js/modernizr.js"></script>
+</head>
+
+<body>
+    <section class="body" ng-if="!!currentUser">
+        <div ng-include="'/views/includes/header.html'" ng-controller="headerCtrl"></div>
+        <div class="inner-wrapper">
+            <div ng-include="'/views/includes/navigation.html'" ng-controller="navigationCtrl"></div>
+            <section role="main" class="content-body">
+                <div ui-view="main"></div>
+            </section>
+        </div>
+        <div class="modal fade" id="setUserPasswordModal" tabindex="-1" role="dialog" aria-labelledby="formModalLabel" aria-hidden="true" ng-controller="headerCtrl">
+            <div class="modal-dialog">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+                        <h4 class="modal-title" id="formModalLabel">修改密码</h4>
+                    </div>
+                    <form class="form-horizontal form-bordered" id="setUserPasswordForm" name="setUserPasswordForm">
+                        <div class="modal-body">
+                            <div class="form-group">
+                                <label class="col-md-3 control-label" for="user-nickname">原密码</label>
+                                <div class="col-md-6">
+                                     <input id="user-nickname" type="password" placeholder="原密码" required="required" ng-model="userModel.password" class="form-control" required>
+                                </div>
+                            </div>
+                            <div class="form-group">
+                                <label class="col-md-3 control-label" for="user-name">新密码</label>
+                                <div class="col-md-6">
+                                    <input id="user-newPassword" type="password" placeholder="新密码" ng-model="userModel.newPassword" class="form-control" required>
+                                </div>
+                            </div>
+                            <div class="form-group">
+                                <label class="col-md-3 control-label" for="user-password">重复新密码</label>
+                                <div class="col-md-6">
+                                    <input id="user-replyNewPassword" type="password" placeholder="重复新密码" ng-model="userModel.replyNewPassword" class="form-control" required>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="modal-footer">
+                            <button type="button" class="btn btn-primary" ng-disabled="setUserPasswordForm.$invalid" ng-click="save()"><i class="fa fa-check"></i> 保存</button>
+                            <button type="button" class="btn btn-default" data-dismiss="modal"><i class="fa fa-close"></i> 取消</button>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </section>
+    <section role="main" class="body" ng-if="!currentUser">
+        <div ui-view="login"></div>
+    </section>
+
+    <toaster-container toaster-options="{'close-button':{ 'toast-warning': true, 'toast-error': false }, 'animation-class': 'toast-bottom-center' }"></toaster-container>
+
+    
+
+    <!-- Vendor -->
+    <script src="~/lib/jquery/dist/jquery.js"></script>
+    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
+    <script src="~/lib/bootstrap-datepicker/js/bootstrap-datepicker.js"></script>
+    <script src="~/lib/nanoscroller/dist/javascripts/jquery.nanoscroller.min.js"></script>
+    <script src="~/lib/magnific-popup/dist/jquery.magnific-popup.js"></script>
+    <script src="~/lib/bootbox.js/bootbox.js"></script>
+    <script src="~/lib/codemirror/lib/codemirror.js"></script>
+    <script src="~/lib/lodash/lodash.min.js"></script>
+
+    <!-- Specific Page Vendor -->
+    <script src="~/lib/jquery-ui/jquery-ui.min.js"></script>
+    <script src="~/lib/appear/jquery.appear.js"></script>
+    <script src="~/lib/bootstrap-multiselect/dist/js/bootstrap-multiselect.js"></script>
+
+    <!-- Theme Base, Components and Settings -->
+    <script src="~/js/admin/theme.js"></script>
+
+    <!-- Theme Custom -->
+    <script src="~/js/admin/theme.custom.js"></script>
+
+    <!-- Theme Initialization Files -->
+    <script src="~/js/theme.init.js"></script>
+
+
+    <script type='text/javascript' src='~/lib/angular/angular.js'></script>
+    <script type='text/javascript' src='~/lib/angular-resource/angular-resource.js'></script>
+    <script type='text/javascript' src='~/lib/angular-animate/angular-animate.js'></script>
+    <script type='text/javascript' src='~/lib/angular-route/angular-route.js'></script>
+    <script type='text/javascript' src='~/lib/ui-router/release/angular-ui-router.js'></script>
+    <script type='text/javascript' src='~/lib/moment/moment.js'></script>
+    <script type='text/javascript' src='~/lib/bootstrap-daterangepicker/daterangepicker.js'></script>
+    <script type='text/javascript' src='~/lib/angular-daterangepicker/js/angular-daterangepicker.js'></script>
+    <script type='text/javascript' src='~/lib/ng-table/dist/ng-table.js'></script>
+    <script type='text/javascript' src='~/lib/angular-bootstrap/ui-bootstrap.js'></script>
+    <script type='text/javascript' src='~/lib/angular-bootstrap/ui-bootstrap-tpls.js'></script>
+    <script type='text/javascript' src='~/lib/angular-swx-session-storage/release/swx-session-storage.js'></script>
+    <script type='text/javascript' src='~/lib/angularjs-toaster/toaster.js'></script>
+    <script type='text/javascript' src='~/lib/angular-bootstrap-datetimepicker/src/js/datetimepicker.js'></script>
+    <script type='text/javascript' src='~/lib/angularjs-toaster/toaster.js'></script>
+    <script type='text/javascript' src="~/lib/angular-ui-codemirror/ui-codemirror.js"></script>
+    <script type='text/javascript' src="~/lib/editor.md/editormd.min.js"></script>
+    <script type='text/javascript' src="~/lib/ng-file-upload/ng-file-upload-all.min.js"></script>
+    <script type='text/javascript' src='~/lib/angular-cookies/angular-cookies.js'></script>
+    <script type='text/javascript' src="~/lib/angularLocalStorage/dist/angularLocalStorage.min.js"></script>
+    <script type="text/javascript" src="~/lib/angular-ui-layout/src/ui-layout.js"></script>
+    <script type="text/javascript" src="~/lib/ace-builds/src-min-noconflict/ace.js"></script>
+    <script type="text/javascript" src="~/lib/ace-builds/src-min-noconflict/ext-language_tools.js"></script>
+    <script type="text/javascript" src="~/lib/angular-ui-ace/ui-ace.js"></script>
+    <script src="~/lib/jquery.fancytree/dist/jquery.fancytree.min.js"></script>
+
+    <!-- build:appjs -->
+    <script type='text/javascript' src='~/js/app.js'></script>
+    <script type='text/javascript' src='~/js/directives.js'></script>
+    <script type='text/javascript' src='~/js/routes.js'></script>
+    <script type='text/javascript' src='~/js/controllers.js'></script>
+
+    <script type='text/javascript' src='~/js/sys/login.js'></script>
+    <script type='text/javascript' src='~/js/core/project.js'></script>
+    <script type='text/javascript' src='~/js/core/api.js'></script>
+    <script type='text/javascript' src='~/js/sys/user.js'></script>
+    <script type='text/javascript' src='~/js/core/csi.js'></script>
+
+    <!-- endbuild -->
+</body>
+</html>
+

+ 6 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/Shared/Error.cshtml

@@ -0,0 +1,6 @@
+@{
+    ViewData["Title"] = "Error";
+}
+
+<h1 class="text-danger">Error.</h1>
+<h2 class="text-danger">An error occurred while processing your request.</h2>

+ 67 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/Shared/_Layout.cshtml

@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>@ViewData["Title"] - Wicture.DbRESTFul.PMT</title>
+
+    <environment names="Development">
+        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
+        <link rel="stylesheet" href="~/css/site.css" />
+    </environment>
+    <environment names="Staging,Production">
+        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/css/bootstrap.min.css"
+              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
+              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
+        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
+    </environment>
+</head>
+<body>
+    <div class="navbar navbar-inverse navbar-fixed-top">
+        <div class="container">
+            <div class="navbar-header">
+                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
+                    <span class="sr-only">Toggle navigation</span>
+                    <span class="icon-bar"></span>
+                    <span class="icon-bar"></span>
+                    <span class="icon-bar"></span>
+                </button>
+                <a asp-controller="Home" asp-action="Index" class="navbar-brand">Wicture.DbRESTFul.PMT</a>
+            </div>
+            <div class="navbar-collapse collapse">
+                <ul class="nav navbar-nav">
+                    <li><a asp-controller="Home" asp-action="Index">Home</a></li>
+                    <li><a asp-controller="Home" asp-action="About">About</a></li>
+                    <li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
+                </ul>
+            </div>
+        </div>
+    </div>
+    <div class="container body-content">
+        @RenderBody()
+        <hr />
+        <footer>
+            <p>&copy; 2016 - Wicture.DbRESTFul.PMT</p>
+        </footer>
+    </div>
+
+    <environment names="Development">
+        <script src="~/lib/jquery/dist/jquery.js"></script>
+        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
+        <script src="~/js/site.js" asp-append-version="true"></script>
+    </environment>
+    <environment names="Staging,Production">
+        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
+                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
+                asp-fallback-test="window.jQuery">
+        </script>
+        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/bootstrap.min.js"
+                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
+                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
+        </script>
+        <script src="~/js/site.min.js" asp-append-version="true"></script>
+    </environment>
+
+    @RenderSection("scripts", required: false)
+</body>
+</html>

+ 95 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/Spec/Index.cshtml

@@ -0,0 +1,95 @@
+@{
+    Layout = "";
+}
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <title>文档 -《@ViewData["projectName"]》</title>
+
+    <link rel="stylesheet" href="./css/spec.css" type="text/css">
+    <!--<link rel="stylesheet" href="lib/css/documentation.css" type="text/css">-->
+    <!--<link rel="stylesheet" href="./lib/jquery.layout/dist/layout-default-latest.css" type="text/css">-->
+    <!--[if lte IE 7]>
+        <style type="text/css"> body { font-size: 85%; } </style>
+    <![endif]-->
+    <!-- REQUIRED scripts for layout widget -->
+    <script type="text/javascript" src="./lib/jquery/dist/jquery.1.21.1.min.js"></script>
+    <script type="text/javascript" src="./lib/jquery-ui/jquery-ui.min.js"></script>
+    <script type="text/javascript" src="./lib/jquery.layout/dist/jquery.layout-latest.js"></script>
+</head>
+<body>
+
+    @if (ViewData["projects"] != null)
+    {
+        <ul>
+        @foreach (var item in ViewData["projects"] as IEnumerable<string>)
+        {
+            <li><a href="?project=@item">@item</a></li>
+        }
+        </ul>
+    }
+    else
+    {
+    <div class="ui-layout-north">
+        <div id="logo">@ViewData["projectName"] 项目文档</div>
+        <div id="navigation">
+            <a href="javascript:;" class="current">首页</a>
+            <a href="javascript:;">数据库文档</a>
+            <a href="javascript:;">Api文档</a>
+            <a href="javascript:;">项目简介</a>
+            <a href="javascript:;">关于</a>
+        </div>
+    </div>
+
+    <iframe id="mainFrame" name="mainFrame" class="ui-layout-center content" 
+            width="100%" height="100%" frameborder="0" scrolling="auto" src="~/doc/@ViewData["projectName"]/introduction.html"></iframe>
+
+    <!--
+        =======================
+        *	TABLE OF CONTENTS  *
+        =======================
+    -->
+    <div class="ui-layout-west" style="display: none;">
+        <div class="header">文档目录</div>
+        <div class="ui-layout-content" style="overflow: auto;">
+            <ul id="TOC">
+                <li><a target="mainFrame" href="~/doc/@ViewData["projectName"]/introduction.html">项目简介</a></li>
+                <li>
+                    <span class="tocBtn"></span><a href="#">数据库文档</a>
+                    <ul>
+                        @foreach (var item in ViewData["dbSpecs"] as IEnumerable<string>)
+                        {
+                            <li><a target="mainFrame" href="~/@item">@System.IO.Path.GetFileNameWithoutExtension(item)</a></li>
+                        }
+                    </ul>
+                </li>
+                <li>
+                    <span class="tocBtn"></span><a href="#">接口文档</a>
+                    <ul>
+                        @foreach (var item in ViewData["apiSpecs"] as IEnumerable<string>)
+                        {
+                            <li><a target="mainFrame" href="~/@item">@System.IO.Path.GetFileNameWithoutExtension(item)</a></li>
+                        }
+                    </ul>
+                </li>
+                <li>
+                    <span class="tocBtn"></span><a href="#">其它文档</a>
+                    <ul>
+                        @foreach (var item in ViewData["otherSpecs"] as IEnumerable<string>)
+                        {
+                            <li><a target="mainFrame" href="~/@item">@System.IO.Path.GetFileNameWithoutExtension(item)</a></li>
+                        }
+                    </ul>
+                </li>
+            </ul>
+        </div>
+    </div>
+
+    <script type="text/javascript" src="./js/spec.js"></script>
+    }
+
+</body>
+</html>

+ 1 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/_ViewImports.cshtml

@@ -0,0 +1 @@
+@using Wicture.DbRESTFul.PMT

+ 3 - 0
src/Tools/Wicture.DbRESTFul.PMT/Views/_ViewStart.cshtml

@@ -0,0 +1,3 @@
+@{
+    Layout = "_Layout";
+}

+ 22 - 0
src/Tools/Wicture.DbRESTFul.PMT/Wicture.DbRESTFul.PMT.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <AssemblyName>Wicture.DbRESTFul.PMT</AssemblyName>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Content Remove="nlog.config" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Include="nlog.config">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Wicture.DbRESTFul\Wicture.DbRESTFul.csproj" />
+  </ItemGroup>
+
+</Project>

+ 45 - 0
src/Tools/Wicture.DbRESTFul.PMT/appsettings.json

@@ -0,0 +1,45 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft": "Warning",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  },
+  "AllowedHosts": "*",
+  "API": {
+    "ShowFullException": true,
+    "Path": "API/",
+    "LogConfig": {
+      "Exception": true,
+      "Event": true,
+      "Token": true,
+      "Identity": true,
+      "Param": true,
+      "Result": true
+    }
+  },
+  "CSI": {
+    "Path": "CSI/",
+    "DbConnection": {
+      "DatabaseType": "MySQL",
+      "CommandTimeout": 5000,
+      "ReadConnectionString": "server=localhost;port=3306;user=root;password=root;database=pmt2;charset=utf8;convert zero datetime=True;Allow User Variables=True;SslMode=None",
+      "WriteConnectionString": "server=localhost;port=3306;user=root;password=root;database=pmt2;charset=utf8;convert zero datetime=True;Allow User Variables=True;SslMode=None"
+    },
+    "LogConfig": {
+      "Code": true,
+      "Param": true,
+      "Result": false
+    }
+  },
+  "DevTool": {
+    "Enabled": true,
+    "Path": "/dev",
+    "SyncOnLoading": false,
+    "ProjectName": "Wicture.DbRESTFul.PMT",
+    "PMTServiceUrl": "http://localhost:5000"
+  },
+  "Variables": {
+  }
+}

+ 41 - 0
src/Tools/Wicture.DbRESTFul.PMT/bower.json

@@ -0,0 +1,41 @@
+{
+  "name": "pmt",
+  "private": true,
+  "dependencies": {
+    "bootstrap": "3.3.5",
+    "jquery": "2.1.4",
+    "jquery-validation": "1.14.0",
+    "jquery-validation-unobtrusive": "3.2.4",
+    "angular": "1.5.8",
+    "angular-ui-codemirror": "~0.3.0",
+    "angular-bootstrap": "1.0.3",
+    "angular-bootstrap-datetimepicker": "0.4.0",
+    "angular-daterangepicker": "0.2.2",
+    "angular-resource": "~1.5.8",
+    "angular-route": "~1.5.8",
+    "angularjs-toaster": "0.4.181",
+    "font-awesome": "4.5.0",
+    "ng-table": "0.8.3",
+    "ui-router": "0.2.15",
+    "modernizr": "3.0.0",
+    "bootstrap-datepicker": "1.4.1",
+    "bootstrap-multiselect": "~0.9.13",
+    "jquery-ui": "1.11.4",
+    "appear": "~0.3.6",
+    "nanoscroller": "~0.8.7",
+    "magnific-popup": "1.0.0",
+    "bootbox.js": "4.4.0",
+    "jquery.layout": "*",
+    "lodash": "3.10.1",
+    "editor.md": "1.5.0",
+    "ng-file-upload": "~12.0.4",
+    "angular-cookies": "1.4.14",
+    "angularLocalStorage": "0.3.2",
+    "angular-swx-session-storage": "^1.0.2",
+    "angular-ui-layout": "^1.4.2",
+    "angular-ui-ace": "^0.2.3"
+  },
+  "resolutions": {
+    "angular": "1.5.8"
+  }
+}

Някои файлове не бяха показани, защото твърде много файлове са промени