Quellcode durchsuchen

接口文档支持call方法

jlutt@163.com vor 2 Jahren
Ursprung
Commit
f94a4307a2
23 geänderte Dateien mit 1290 neuen und 291 gelöschten Zeilen
  1. 69 0
      conf/Rest接口Call InvokeCallParams方法文档示例和说明.xml
  2. 69 0
      conf/apiFiles/expressOut.xml
  3. 20 67
      conf/script/1000/expressApi/BE_Express_CallBack_VerifySign_SFTC.groovy
  4. 1 1
      conf/script/1000/expressApi/BE_Express_PreCreateOrder_SFTC.groovy
  5. 2 2
      conf/script/1000/orderApi/BE_Order_Sync_RiderLocation_DYLK.groovy
  6. 0 13
      ddBusiness/src/main/java/com/dderp/business/service/flycat/DouyinServiceImpl.java
  7. 0 1
      ddCommon/src/main/java/com/dderp/common/api/flycat/DouyinService.java
  8. 13 0
      ddCommon/src/main/java/com/dderp/common/entity/base/InvokeCallParams.java
  9. 5 0
      ddCommon/src/main/java/com/dderp/common/entity/order/BusinessOrder.java
  10. 4 1
      ddCommon/src/main/java/com/dderp/common/servlet/apidoc/ApiDocServlet.java
  11. 25 0
      ddCommon/src/main/java/com/dderp/common/tdoc/ApiParam.java
  12. 59 0
      ddCommon/src/main/java/com/dderp/common/tdoc/TAppCallParam.java
  13. 491 195
      ddCommon/src/main/java/com/dderp/common/tdoc/TAppDoc.java
  14. 25 0
      ddCommon/src/main/java/com/dderp/common/tdoc/TAppMethod.java
  15. 1 0
      ddWebCore/src/main/java/com/dderp/webcore/rest/PlatformRest.java
  16. 1 0
      ddWebCore/src/main/java/com/dderp/webcore/rest/flycat/ExpressOutRest.java
  17. 5 10
      ddWebCore/src/main/java/com/dderp/webcore/servlet/ExpressCallServlet.java
  18. 1 1
      pom.xml
  19. 361 0
      root/api/api-template.html
  20. 6 0
      root/api/bootstrap.min.css
  21. 7 0
      root/api/bootstrap.min.js
  22. 4 0
      root/api/jquery.min.js
  23. 121 0
      root/apilogin.html

+ 69 - 0
conf/Rest接口Call InvokeCallParams方法文档示例和说明.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<apis>
+    <api>
+        <!-- 接口名称 -->
+        <methodName>测试api</methodName>
+        <!-- 排序 -->
+        <sort>10</sort>
+        <!-- 使用的脚本业务名称 不要BE_ -->
+        <businessMethod>Express_aaaa</businessMethod>
+        <!-- 是否需要token,留着空表示不需要,需要token的写上获取token的方法,不能不要该节点 -->
+        <tokenFrom></tokenFrom>
+        <!-- 是否需要分页,留着空表示不需要,随便写的啥表示需要分页参数,不能不要该节点 -->
+        <pageFrom>1</pageFrom>
+
+        <!--
+            参数信息,paramOnly和params是互斥的,有的时候我们需要的参数是单个类的json就完事,但是又懒得一个一个写param,可以只用paramOnly标识,里面输入带包类名,
+            然后在类里面用@ApiPlce("脚本业务名称")标记上需要的字段即可,不能不要该节点,使用params时,paramOnly写空即可。
+        -->
+        <paramOnly></paramOnly>
+        <!--
+            参数信息,
+        -->
+        <params>
+            <param>
+                <!-- 参数说明 -->
+                <paramComment>参数1</paramComment>
+                <!-- 参数名称 -->
+                <paramKey>keyWord1</paramKey>
+                <!-- 参数类型 "Integer", "Long", "Double", "Float", "Boolean", "String" 为基础类型 -->
+                <paramKind>Integer</paramKind>
+            </param>
+
+            <param>
+                <paramComment>参数2</paramComment>
+                <paramKey>keyWord2</paramKey>
+                <paramKind>String</paramKind>
+            </param>
+
+            <param>
+                <paramComment>参数3</paramComment>
+                <paramKey>keyWord3</paramKey>
+                <!-- 参数类型 为类时,文档会读取注解@ApiPlce("脚本业务名称")的字段生成参数表 -->
+                <paramKind>com.dderp.common.entity.order.BusinessOrder</paramKind>
+            </param>
+
+            <param>
+                <paramComment>参数4</paramComment>
+                <paramKey>keyWord4</paramKey>
+                <paramKind>com.dderp.common.entity.order.OrderAfterSaleBill</paramKind>
+            </param>
+        </params>
+
+        <!-- 类说明,文档会生成类的说明表格 -->
+        <webApiBean>com.dderp.common.entity.order.OrderAfterSaleBill</webApiBean>
+        <webApiBean>com.dderp.common.entity.order.OrderDeliveryInfo</webApiBean>
+    </api>
+
+    <api>
+        <methodName>创建配送单</methodName>
+        <sort>11</sort>
+        <businessMethod>Express_bbbbb</businessMethod>
+        <tokenFrom>/order/orderFormToken</tokenFrom>
+        <pageFrom></pageFrom>
+
+        <paramOnly>com.dderp.common.entity.order.BusinessOrder</paramOnly>
+
+        <webApiBean></webApiBean>
+    </api>
+</apis>

+ 69 - 0
conf/apiFiles/expressOut.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<apis>
+    <api>
+        <!-- 接口名称 -->
+        <methodName>测试api</methodName>
+        <!-- 排序 -->
+        <sort>10</sort>
+        <!-- 使用的脚本业务名称 不要BE_ -->
+        <businessMethod>Express_aaaa</businessMethod>
+        <!-- 是否需要token,留着空表示不需要,需要token的写上获取token的方法,不能不要该节点 -->
+        <tokenFrom></tokenFrom>
+        <!-- 是否需要分页,留着空表示不需要,随便写的啥表示需要分页参数,不能不要该节点 -->
+        <pageFrom>1</pageFrom>
+
+        <!--
+            参数信息,paramOnly和params是互斥的,有的时候我们需要的参数是单个类的json就完事,但是又懒得一个一个写param,可以只用paramOnly标识,里面输入带包类名,
+            然后在类里面用@ApiPlce("脚本业务名称")标记上需要的字段即可,不能不要该节点,使用params时,paramOnly写空即可。
+        -->
+        <paramOnly></paramOnly>
+        <!--
+            参数信息,
+        -->
+        <params>
+            <param>
+                <!-- 参数说明 -->
+                <paramComment>参数1</paramComment>
+                <!-- 参数名称 -->
+                <paramKey>keyWord1</paramKey>
+                <!-- 参数类型 "Integer", "Long", "Double", "Float", "Boolean", "String" 为基础类型 -->
+                <paramKind>Integer</paramKind>
+            </param>
+
+            <param>
+                <paramComment>参数2</paramComment>
+                <paramKey>keyWord2</paramKey>
+                <paramKind>String</paramKind>
+            </param>
+
+            <param>
+                <paramComment>参数3</paramComment>
+                <paramKey>keyWord3</paramKey>
+                <!-- 参数类型 为类时,文档会读取注解@ApiPlce("脚本业务名称")的字段生成参数表 -->
+                <paramKind>com.dderp.common.entity.order.BusinessOrder</paramKind>
+            </param>
+
+            <param>
+                <paramComment>参数4</paramComment>
+                <paramKey>keyWord4</paramKey>
+                <paramKind>com.dderp.common.entity.order.OrderAfterSaleBill</paramKind>
+            </param>
+        </params>
+
+        <!-- 类说明,文档会生成类的说明表格 -->
+        <webApiBean>com.dderp.common.entity.order.OrderAfterSaleBill</webApiBean>
+        <webApiBean>com.dderp.common.entity.order.OrderDeliveryInfo</webApiBean>
+    </api>
+
+    <api>
+        <methodName>创建配送单</methodName>
+        <sort>11</sort>
+        <businessMethod>Express_bbbbb</businessMethod>
+        <tokenFrom>/order/orderFormToken</tokenFrom>
+        <pageFrom></pageFrom>
+
+        <paramOnly>com.dderp.common.entity.order.BusinessOrder</paramOnly>
+
+        <webApiBean></webApiBean>
+    </api>
+</apis>

+ 20 - 67
conf/script/1000/expressApi/BE_Express_CallBack_VerifySign_SFTC.groovy

@@ -1,14 +1,10 @@
 import com.dderp.common.api.BusinessExecutor
-import com.dderp.common.api.ERPLockDataService
-import com.dderp.common.api.NoSqlKeysService
 import com.dderp.common.datas.ERPModule
-import com.dderp.common.datas.RedisKeys
 import com.dderp.common.entity.base.InvokeCallParams
 import com.dderp.common.entity.base.InvokeCallResult
 import com.dySweetFishPlugin.sql.dao.OperatorWait
 import com.sweetfish.convert.json.JsonConvert
 import com.sweetfish.service.RetResult
-import groovy.json.JsonSlurper
 import org.apache.commons.lang3.StringUtils
 import org.apache.logging.log4j.LogManager
 import org.apache.logging.log4j.Logger
@@ -19,14 +15,14 @@ class BE_Express_CallBack_VerifySign_SFTC implements BusinessExecutor<InvokeCall
 
     private final Logger logger = LogManager.getLogger(this.getClass().getSimpleName())
 
-    @Resource
-    JsonConvert jsonConvert
+    @Resource(name = "property.sftc.appId")
+    long sfAppId
 
-    @Resource
-    ERPLockDataService lockDataService
+    @Resource(name = "property.sftc.appKey")
+    String sfAppKey
 
     @Resource
-    NoSqlKeysService keysService
+    JsonConvert jsonConvert
 
     @Override
     String scriptName() {
@@ -40,72 +36,29 @@ class BE_Express_CallBack_VerifySign_SFTC implements BusinessExecutor<InvokeCall
 
     @Override
     OperatorWait getAWait(InvokeCallParams source) {
-        return OperatorWait.ASNYC
+        return OperatorWait.SYNC
     }
 
-    @Override
-    RetResult<InvokeCallParams> beforeExecute(InvokeCallParams source) {
-        //此回调post的数据看着没有子表,直接用groovy自带的json即可,如果有子表,还是应该建立一个类进行转换
-
-        //锁定下数据,防止重复提交
-        def jsonSlurper = new JsonSlurper()
-        def invokeOrder = jsonSlurper.parseText(source.params)
-
-        String urlIndex = invokeOrder["url_index"] as String
-
-        //这里除了店铺授权和店铺取消授权,其它都是订单,if-else先写着
-        if (StringUtils.equalsIgnoreCase("bindnotify", urlIndex)) {
-            //店铺授权回调
-            String shopId = invokeOrder["shop_id"] as String
-            if (lockDataService.hLockAdd(shopId, RedisKeys.KEY_ERP_WORKING_SF_STORE, source.supplierCode) > 1) {
-                return RetResult.<InvokeCallParams> errorT().retinfo(shopId + "提交工作中,请稍后刷新即可,无需重复操作")
-            }
-        } else if (StringUtils.equalsIgnoreCase("cancelbindnotify", urlIndex)) {
-            //店铺取消授权回调
-            String shopId = invokeOrder["shop_id"] as String
-            if (lockDataService.hLockAdd(shopId, RedisKeys.KEY_ERP_WORKING_SF_STORE, source.supplierCode) > 1) {
-                return RetResult.<InvokeCallParams> errorT().retinfo(shopId + "提交工作中,请稍后刷新即可,无需重复操作")
-            }
-        } else {
-            String invokeOrderId = invokeOrder["sf_order_id"] as String
-            if (lockDataService.hLockAdd(invokeOrderId, RedisKeys.KEY_ERP_WORKING_SF_ORDER, source.supplierCode) > 1) {
-                return RetResult.<InvokeCallParams> errorT().retinfo(invokeOrderId + "提交工作中,请稍后刷新即可,无需重复操作")
-            }
-        }
+    RetResult<InvokeCallResult> execute(InvokeCallParams source) {
+        String body = source.params
+        String currentSign = ExpressApiSign.sfGenerateOpenSign(body, sfAppId, sfAppKey)
 
-        return RetResult.<InvokeCallParams> successT().result(source)
-    }
+        String orginSign = source.attach.getValue("sign", "")
 
-    @Override
-    void afterExecute(boolean executeError, InvokeCallParams source, InvokeCallResult dest) {
-        def jsonSlurper = new JsonSlurper()
-        def invokeOrder = jsonSlurper.parseText(source.params)
+        def resultData = [
+                error_code: 0,
+                error_msg: "success"
+        ]
 
-        String urlIndex = invokeOrder["url_index"] as String
-        if (StringUtils.equalsIgnoreCase("bindnotify", urlIndex)) {
-            //店铺授权回调
-            String shopId = invokeOrder["shop_id"] as String
-            lockDataService.hLockDel(shopId, RedisKeys.KEY_ERP_WORKING_SF_STORE, source.supplierCode)
-        } else if (StringUtils.equalsIgnoreCase("cancelbindnotify", urlIndex)) {
-            //店铺取消授权回调
-            String shopId = invokeOrder["shop_id"] as String
-            lockDataService.hLockDel(shopId, RedisKeys.KEY_ERP_WORKING_SF_STORE, source.supplierCode)
-        } else {
-            String invokeOrderId = invokeOrder["sf_order_id"] as String
-            lockDataService.hLockDel(invokeOrderId,RedisKeys.KEY_ERP_WORKING_SF_ORDER, source.supplierCode)
+        if (!StringUtils.equals(currentSign, orginSign)) {
+            logger.error("签名错误")
+            return RetResult.<InvokeCallResult> errorT().retinfo("签名错误").result(
+                    InvokeCallResult.success().data(jsonConvert.convertTo(resultData))
+            )
         }
-    }
-
-    RetResult<InvokeCallResult> execute(InvokeCallParams source) {
-        def jsonSlurper = new JsonSlurper()
-        def invokeOrder = jsonSlurper.parseText(source.params)
-
-        logger.info(invokeOrder["url_index"])
-        logger.info(invokeOrder["sf_order_id"])
-        logger.info(invokeOrder["operator_name"])
 
         return RetResult.<InvokeCallResult> successT().result(
-                InvokeCallResult.success()
+                InvokeCallResult.success().data(jsonConvert.convertTo(resultData))
         )
     }
 }

+ 1 - 1
conf/script/1000/expressApi/BE_Express_PreCreateOrder_SFTC.groovy

@@ -43,7 +43,7 @@ class BE_Express_PreCreateOrder_SFTC implements BusinessExecutor<InvokeCallParam
 
     @Override
     String scriptName() {
-        return "顺丰同城预创建订单"
+        return "顺丰同城预创建订单--价格计算"
     }
 
     @Override

+ 2 - 2
conf/script/1000/orderApi/BE_Order_Sync_RiderLocation_DYLK.groovy

@@ -39,12 +39,12 @@ class BE_Order_Sync_RiderLocation_DYLK implements BusinessExecutor<InvokeCallPar
 
     @Override
     ERPModule module() {
-        return ERPModule.ORDER_API
+        return ERPModule.ERP_ORDER
     }
 
     @Override
     OperatorWait getAWait(InvokeCallParams source) {
-        return OperatorWait.AWAIT
+        return OperatorWait.SYNC
     }
 
 

+ 0 - 13
ddBusiness/src/main/java/com/dderp/business/service/flycat/DouyinServiceImpl.java

@@ -164,17 +164,4 @@ public class DouyinServiceImpl extends BaseService implements DouyinService {
         }
     }
 
-    @Override
-    public RetResult<BusinessOrder> importOrderFromDouyin(String requestContent, String dataSourceId, long supplierCode, ERPTokenUser currentUser) {
-        return handleScript("Order_CreateOrder_Douyin", ERPModule.ORDER_API,
-                dataSourceId, supplierCode,
-                () -> ProcessStringItem.newBuilder()
-                        .itemValue(requestContent)
-                        .dataSourceId(dataSourceId)
-                        .supplierCode(supplierCode)
-                        .currentUser(currentUser)
-                        .build()
-        );
-    }
-
 }

+ 0 - 1
ddCommon/src/main/java/com/dderp/common/api/flycat/DouyinService.java

@@ -7,7 +7,6 @@ import com.sweetfish.service.Service;
 
 public interface DouyinService extends Service {
     String postInvoker(String subUrl, String body);
-    RetResult<BusinessOrder> importOrderFromDouyin(String requestContent, String dataSourceId, long supplierCode, ERPTokenUser currentUser);
 
 
 

+ 13 - 0
ddCommon/src/main/java/com/dderp/common/entity/base/InvokeCallParams.java

@@ -5,6 +5,7 @@ import com.dySweetFishPlugin.tool.crypto.EncryptUtil;
 import com.sweetfish.convert.json.JsonConvert;
 import com.sweetfish.source.PageFlipper;
 import com.sweetfish.util.AnyValue;
+import com.sweetfish.util.Comment;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.HashMap;
@@ -12,8 +13,10 @@ import java.util.Map;
 
 public class InvokeCallParams extends BaseEntity {
 
+    @Comment("业务名称")
     private String businessMethod;
 
+    @Comment("时间戳")
     private String timeStamp;
 
     /**
@@ -21,21 +24,25 @@ public class InvokeCallParams extends BaseEntity {
      * 这里针对业务类的数据直接用json字符串,是当前各平台的通用做法,之前验签如果双发使用了不同的序列化工具,导致有的null的字段没有带上,序列化之后的结果会不同,验签就出现问题
      * 现在直接统一Params即可
      */
+    @Comment("参数")
     private String params;
 
     /**
      * 附加字符串,params不够用的情况下,增加附件参数
      */
+    @Comment("附加参数")
     private AnyValue attach;
 
     /**
      * 分页对象,这里还是把分页信息从params里面拆分出来,减少脚本中转换的次数
      */
+    @Comment("分页对象")
     private PageFlipper page;
 
     /**
      * 客户编号,由我们提供
      */
+    @Comment("客户编号")
     private String clientCode;
 
     /**
@@ -43,18 +50,24 @@ public class InvokeCallParams extends BaseEntity {
      * <ul>clientCode=xxx&params=xxx&timestamp=xxx</ul>
      * 组成,所有参数均使用此格式,避免扩展麻烦,至于具体的业务,则由params的json字符串定义即可
      */
+    @Comment("md5串")
     private String digest;
 
+    @Comment("token")
     private String token;
 
     //下面的字段由服务端赋值
 
+    @Comment("操作人")
     private ERPTokenUser currentUser;
 
+    @Comment("分库")
     private String dataSourceId;
 
+    @Comment("分表")
     private long supplierCode;
 
+    @Comment("json转换器")
     private final Map<String, JsonConvert> jsonConvertMap = new HashMap<>();
 
     public InvokeCallParams() {

+ 5 - 0
ddCommon/src/main/java/com/dderp/common/entity/order/BusinessOrder.java

@@ -1,6 +1,7 @@
 package com.dderp.common.entity.order;
 
 import com.dderp.common.entity.base.BaseEntity;
+import com.dderp.common.tdoc.ApiPlace;
 import com.sweetfish.util.Comment;
 import org.rex.db.RColumn;
 
@@ -19,14 +20,18 @@ public class BusinessOrder extends BaseEntity {
 
     @RColumn("ordername")
     @Comment("订单名称--一般是门店名称+订单渠道")
+    @ApiPlace("Express_aaaa")
+    @ApiPlace("Express_bbbbb")
     private String orderName;
 
     @RColumn("ordercode")
     @Comment("订单编号")
+    @ApiPlace("Express_aaaa")
     private String orderCode;
 
     @RColumn("orderstatus")
     @Comment("订单状态")
+    @ApiPlace("Express_bbbbb")
     private int orderStatus;
 
     @RColumn("ordersequence")

+ 4 - 1
ddCommon/src/main/java/com/dderp/common/servlet/apidoc/ApiDocServlet.java

@@ -81,6 +81,9 @@ public class ApiDocServlet extends ERPAdminHttpServlet {
     @Resource(name = "APPLICATION")
     Application application;
 
+    @Resource(name = "APP_HOME")
+    private String appHome;
+
     //网页根目录
     @Resource(name = RESNAME_SERVER_ROOT)
     File webRoot;
@@ -160,7 +163,7 @@ public class ApiDocServlet extends ERPAdminHttpServlet {
 
             TAppDoc doc = new TAppDoc();
             doc.setAppName(System.getProperty("current.app.name"));
-            doc.bind(application).generate();
+            doc.bind(application).appHome(appHome).generate();
 
             t.binding("appdoc", doc);
             String content = t.render();

+ 25 - 0
ddCommon/src/main/java/com/dderp/common/tdoc/ApiParam.java

@@ -0,0 +1,25 @@
+package com.dderp.common.tdoc;
+
+import java.lang.annotation.*;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Inherited
+@Documented
+@Target({FIELD})
+@Retention(RUNTIME)
+@Repeatable(ApiParam.ApiParams.class)
+public @interface ApiParam {
+
+    String value();
+
+    @Inherited
+    @Documented
+    @Target({FIELD})
+    @Retention(RUNTIME)
+    @interface ApiParams {
+
+        ApiParam[] value();
+    }
+}

+ 59 - 0
ddCommon/src/main/java/com/dderp/common/tdoc/TAppCallParam.java

@@ -0,0 +1,59 @@
+package com.dderp.common.tdoc;
+
+public class TAppCallParam {
+
+    private String paramName;
+
+    private String paramType;
+
+    private String paramSrc;
+
+    private String paramComment;
+
+    private Boolean paramRequired;
+
+    public String getParamName() {
+        return paramName;
+    }
+
+    public TAppCallParam setParamName(String paramName) {
+        this.paramName = paramName;
+        return this;
+    }
+
+    public String getParamType() {
+        return paramType;
+    }
+
+    public TAppCallParam setParamType(String paramType) {
+        this.paramType = paramType;
+        return this;
+    }
+
+    public String getParamSrc() {
+        return paramSrc;
+    }
+
+    public TAppCallParam setParamSrc(String paramSrc) {
+        this.paramSrc = paramSrc;
+        return this;
+    }
+
+    public String getParamComment() {
+        return paramComment;
+    }
+
+    public TAppCallParam setParamComment(String paramComment) {
+        this.paramComment = paramComment;
+        return this;
+    }
+
+    public Boolean getParamRequired() {
+        return paramRequired;
+    }
+
+    public TAppCallParam setParamRequired(Boolean paramRequired) {
+        this.paramRequired = paramRequired;
+        return this;
+    }
+}

+ 491 - 195
ddCommon/src/main/java/com/dderp/common/tdoc/TAppDoc.java

@@ -9,11 +9,16 @@ import com.sweetfish.source.PageFlipper;
 import com.sweetfish.util.ClassesUtils;
 import com.sweetfish.util.Comment;
 import org.apache.commons.lang3.StringUtils;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
 
+import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -23,13 +28,18 @@ import java.util.List;
 public class TAppDoc {
 
     //  private static String SPACE8 = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
-    private static String SPACE8 = "    ";
-    private static String SPACE16 = SPACE8 + SPACE8;
-    private static String SPACE24 = SPACE16 + SPACE8;
+    private static final String SPACE8 = "    ";
+    private static final String SPACE16 = SPACE8 + SPACE8;
+    private static final String SPACE24 = SPACE16 + SPACE8;
 
     private String appName;
+
+    private String appHome;
+
     private Application application;
-    private List<TAppModule> moduleList = new ArrayList<>();
+    private final List<TAppModule> moduleList = new ArrayList<>();
+
+    private static final String[] simpleParamType = {"Integer", "Long", "Double", "Float", "Boolean", "String"};
 
     public String getAppName() {
         return appName;
@@ -44,6 +54,11 @@ public class TAppDoc {
         return this;
     }
 
+    public TAppDoc appHome(String appHome) {
+        this.appHome = appHome;
+        return this;
+    }
+
     public void addModule(TAppModule module) {
         this.moduleList.add(module);
     }
@@ -57,7 +72,8 @@ public class TAppDoc {
             HttpServer server = node.getServer();
 
             for (HttpServlet servlet : server.getPrepareServlet().getServlets()) {
-                if (!(servlet instanceof HttpServlet)) continue;
+
+                if (servlet == null) continue;
                 if (servlet.getClass() == ApiDocServlet.class) continue;
                 WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
 
@@ -84,8 +100,8 @@ public class TAppDoc {
                         .setModuleId(moduleId);
                 addModule(module);
 
-                final Class selfClz = servlet.getClass();
-                Class clz = servlet.getClass();
+                final Class<? extends HttpServlet> selfClz = servlet.getClass();
+                Class<? extends HttpServlet> clz = servlet.getClass();
                 if (Modifier.isAbstract(clz.getModifiers())) continue;
 
                 for (Method method : clz.getMethods()) {
@@ -96,215 +112,425 @@ public class TAppDoc {
                     //忽略不被继承的方法
                     if (!action.inherited() && selfClz != clz) continue;
 
-                    String methodUrl = prefix + action.url();
-                    Boolean auth = action.auth();
-                    Integer actionId = action.actionid();
-                    String methodName = "";
-                    if (StringUtils.isNotEmpty(action.comment())) {
-                        methodName = action.comment();
-                    } else {
-                        methodName = method.getName();
-                    }
-                    String methodResult = action.result();
-                    methodResult = methodResult.replace("<", " ");
-                    methodResult = methodResult.replace(">", " ");
-
-                    TAppMethod appMethod = new TAppMethod()
-                            .setMethodName(methodName)
-                            .setUrl(methodUrl)
-                            .setMethodId(actionId)
-                            .setAuth(auth)
-                            .setResult(methodResult)
-                            .setSort(action.sort())
-                            .setSupportMethods(String.join("/", action.methods()));
-                    module.addMethod(appMethod);
-
-                    String jQueryMethod = "post";
-
-                    if (action.methods().length > 0) {
-                        String methodArrayStr = String.join("/", action.methods());
-                        methodArrayStr = methodArrayStr.toLowerCase();
-                        if (methodArrayStr.contains("post")) {
-                            jQueryMethod = "post";
-                        } else if (methodArrayStr.contains("get")) {
-                            jQueryMethod = "get";
-                        }
-                    }
+                    WebApiFile[] apiFiles = method.getAnnotationsByType(WebApiFile.class);
+                    if (apiFiles.length > 0) {
+                        //有接口说明文件时,以文件为主,
+                        //读取接口文件
+                        String methodUrl = prefix + action.url();
+
+                        String methodResult = action.result();
+                        methodResult = methodResult.replace("<", " ");
+                        methodResult = methodResult.replace(">", " ");
+                        String finalMethodResult = methodResult;
+
+                        for (WebApiFile apiFile : apiFiles) {
+                            // 创建saxReader对象
+                            SAXReader reader = new SAXReader();
+
+                            String apiXmlFileName = appHome + File.separator + "conf" + File.separator + "apiFiles" + File.separator + apiFile.fileName();
+                            File apiXmlFile = new File(apiXmlFileName);
+
+                            // 通过read方法读取一个文件 转换成Document对象
+                            Document document = reader.read(apiXmlFile);
+                            //获取根节点元素对象
+                            Element root = document.getRootElement();
+                            List<Element> apiNodes = root.elements("api");
+
+                            apiNodes.forEach((apiNode) -> {
+                                String methodName = apiNode.element("methodName").getTextTrim();
+
+                                TAppMethod appMethod = new TAppMethod()
+                                        .setMethodName(methodName)
+                                        .setCallFlag(1)
+                                        .setUrl(methodUrl)
+                                        .setMethodId(action.actionid())
+                                        .setAuth(action.auth())
+                                        .setResult(finalMethodResult)
+                                        .setSort(Integer.parseInt(apiNode.element("sort").getTextTrim()))
+                                        .setSupportMethods(String.join("/", action.methods()));
+                                module.addMethod(appMethod);
+
+                                //业务脚本
+                                TAppMethodParam businessMethodParam = new TAppMethodParam()
+                                        .setParamName("businessMethod")
+                                        .setParamComment("业务名称")
+                                        .setParamRequired(true)
+                                        .setParamSrc(apiNode.elementTextTrim("businessMethod"))
+                                        .setParamType("String");
+                                appMethod.addParam(businessMethodParam);
+
+                                //token参数
+                                String tokenFrom = apiNode.elementTextTrim("tokenFrom");
+                                if (StringUtils.isNotBlank(tokenFrom)) {
+                                    TAppMethodParam tokenParam = new TAppMethodParam()
+                                            .setParamName("token")
+                                            .setParamComment("操作token")
+                                            .setParamRequired(true)
+                                            .setParamSrc(tokenFrom)
+                                            .setParamType("String");
+                                    appMethod.addParam(tokenParam);
+                                }
 
+                                String pageFrom = apiNode.elementTextTrim("pageFrom");
+                                if (StringUtils.isNotBlank(pageFrom)) {
+                                    TAppMethodParam pageParam = new TAppMethodParam()
+                                            .setParamName("page")
+                                            .setParamComment("分页参数")
+                                            .setParamRequired(true)
+                                            .setParamSrc("JSON.stringify({\"pageIndex\": x, \"pageSize\": y })")
+                                            .setParamType("PageFlipper");
+                                    appMethod.addParam(pageParam);
+                                }
 
-                    List<String> jsDefineList = new ArrayList<>();
-                    List<String> jsReferenceList = new ArrayList<>();
-
-                    for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) {
-                        //region 服务说明和参数表格
-                        String paramSrc = "";
-                        if (param.src() == HttpParamSourceType.COOKIE) {
-                            paramSrc = "Cookie";
-                        } else if (param.src() == HttpParamSourceType.HEADER) {
-                            paramSrc = "Header";
-                        } else if (param.src() == HttpParamSourceType.BODY) {
-                            paramSrc = "Body";
-                        } else if (param.src() == HttpParamSourceType.PARAMETER) {
-                            paramSrc = "Parameter";
-                        }
-                        TAppMethodParam methodParam = new TAppMethodParam()
-                                .setParamName(param.name())
-                                .setParamComment(param.comment())
-                                .setParamRequired(param.required())
-                                .setParamSrc(paramSrc)
-                                .setParamType(param.type().getSimpleName());
-
-                        appMethod.addParam(methodParam);
-                        //endregion
-
-                        //参数类型
-                        final boolean isarray = param.type().isArray();
-                        final Class ptype = isarray ? param.type().getComponentType() : param.type();
-
-                        //region 示例代码
-                        if ("parameter".equalsIgnoreCase(paramSrc)) {
-                            if (!"&".equalsIgnoreCase(param.name())) {
-                                if (ClassesUtils.isPrimitive(ptype)) {
-                                    Class primitiveClass = ClassesUtils.getPrimitive(ptype);
-                                    if (primitiveClass != null) {
-                                        if (primitiveClass == int.class) {
-                                            jsDefineList.add("var " + param.name() + " = 测试整数;");
-                                            jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
-                                        } else if (primitiveClass == long.class) {
-                                            jsDefineList.add("var " + param.name() + " = '测试长整数';");
-                                            jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
-                                        } else if (primitiveClass == short.class) {
-                                            jsDefineList.add("var " + param.name() + " = '测试整数';");
-                                            jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
-                                        } else if (primitiveClass == boolean.class) {
-                                            jsDefineList.add("var " + param.name() + " = 测试bool参数;");
-                                            jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
-                                        } else if (primitiveClass == byte.class) {
-                                            jsDefineList.add("var " + param.name() + " = 测试字节数组;");
-                                            jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
-                                        } else if (primitiveClass == float.class) {
-                                            jsDefineList.add("var " + param.name() + " = 测试浮点float参数;");
-                                            jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
-                                        } else if (primitiveClass == double.class) {
-                                            jsDefineList.add("var " + param.name() + " = 测试浮点double参数;");
-                                            jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                TAppMethodParam invokeParam = new TAppMethodParam()
+                                        .setParamName("params")
+                                        .setParamComment("JSON序列化参数")
+                                        .setParamRequired(true)
+                                        .setParamSrc("参数见下表")
+                                        .setParamType("String");
+                                appMethod.addParam(invokeParam);
+
+                                String paramOnly = apiNode.elementTextTrim("paramOnly");
+                                if (StringUtils.isNotBlank(paramOnly)) {
+                                    try {
+                                        //在对象里面找注解的字段
+                                        Class ptype = Class.forName(paramOnly);
+                                        for (Field field : ptype.getDeclaredFields()) {
+                                            if (Modifier.isFinal(field.getModifiers())) {
+                                                continue;
+                                            }
+                                            if (Modifier.isStatic(field.getModifiers())) {
+                                                continue;
+                                            }
+
+                                            String fieldcomment = "";
+                                            Comment comment = field.getAnnotation(Comment.class);
+                                            if (comment != null) {
+                                                fieldcomment = comment.value();
+                                            }
+                                            ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
+                                            for (ApiPlace place : apiPlaces) {
+                                                if (apiNode.elementTextTrim("businessMethod").equalsIgnoreCase(place.value())) {
+                                                    TAppCallParam callFieldParam = new TAppCallParam()
+                                                            .setParamName(field.getName())
+                                                            .setParamComment(fieldcomment)
+                                                            .setParamRequired(true)
+                                                            .setParamSrc("")
+                                                            .setParamType(field.getType().getSimpleName());
+                                                    appMethod.addCallParam(callFieldParam);
+                                                }
+                                            }
                                         }
+
+                                    } catch (ClassNotFoundException e) {
+                                        throw new RuntimeException(e);
                                     }
-                                } else if (ptype == String.class) {
-                                    jsDefineList.add("var " + param.name() + " = '测试字符串';");
-                                    jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
-                                } else if (ptype == PageFlipper.class) {
-                                    jsDefineList.add("var " + param.name() + " = {");
-                                    jsDefineList.add(SPACE8 + "pageIndex: 页码(整数),");
-                                    jsDefineList.add(SPACE8 + "pageSize: 每页记录数(整数),");
-                                    jsDefineList.add("};");
-                                    jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": JSON.stringify(" + param.name() + "),");
                                 } else {
-                                    jsDefineList.add("var " + param.name() + " = {;");
-                                    for (Field field : ptype.getDeclaredFields()) {
-                                        if (Modifier.isFinal(field.getModifiers())) {
-                                            continue;
-                                        }
-                                        if (Modifier.isStatic(field.getModifiers())) {
-                                            continue;
-                                        }
+                                    Element paramsNode = apiNode.element("params");
+                                    List<Element> paramNodeList = paramsNode.elements("param");
+                                    paramNodeList.forEach((callParamNode) -> {
+                                        String callParamType = callParamNode.elementTextTrim("paramKind");
+                                        if (Arrays.stream(simpleParamType).noneMatch((x) -> x.equalsIgnoreCase(callParamType))) {
+                                            //对象
+                                            //基础类型
+                                            TAppCallParam callParam = null;
+                                            try {
+                                                callParam = new TAppCallParam()
+                                                        .setParamName(callParamNode.elementTextTrim("paramKey"))
+                                                        .setParamComment(callParamNode.elementTextTrim("paramComment"))
+                                                        .setParamRequired(true)
+                                                        .setParamSrc(Class.forName(callParamType).getSimpleName())
+                                                        .setParamType(callParamType);
+                                                appMethod.addCallParam(callParam);
+
+                                                //在对象里面找注解的字段
+                                                Class ptype = Class.forName(callParamType);
+                                                for (Field field : ptype.getDeclaredFields()) {
+                                                    if (Modifier.isFinal(field.getModifiers())) {
+                                                        continue;
+                                                    }
+                                                    if (Modifier.isStatic(field.getModifiers())) {
+                                                        continue;
+                                                    }
+
+                                                    String fieldcomment = "";
+                                                    Comment comment = field.getAnnotation(Comment.class);
+                                                    if (comment != null) {
+                                                        fieldcomment = comment.value();
+                                                    }
+                                                    ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
+                                                    for (ApiPlace place : apiPlaces) {
+                                                        if (apiNode.elementTextTrim("businessMethod").equalsIgnoreCase(place.value())) {
+                                                            TAppCallParam callFieldParam = new TAppCallParam()
+                                                                    .setParamName("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + "    --" + field.getName())
+                                                                    .setParamComment(fieldcomment)
+                                                                    .setParamRequired(true)
+                                                                    .setParamSrc(callParamNode.elementTextTrim("paramKey") + "子属性")
+                                                                    .setParamType(field.getType().getSimpleName());
+                                                            appMethod.addCallParam(callFieldParam);
+                                                        }
+                                                    }
+                                                }
+
+                                            } catch (ClassNotFoundException e) {
+                                                throw new RuntimeException(e);
+                                            }
+
+                                        } else {
+                                            //基础类型
+                                            TAppCallParam callParam = new TAppCallParam()
+                                                    .setParamName(callParamNode.elementTextTrim("paramKey"))
+                                                    .setParamComment(callParamNode.elementTextTrim("paramComment"))
+                                                    .setParamRequired(true)
+                                                    .setParamSrc("")
+                                                    .setParamType(callParamType);
 
-                                        String fieldcomment = "";
-                                        Comment comment = field.getAnnotation(Comment.class);
-                                        if (comment != null) {
-                                            fieldcomment = comment.value();
+                                            appMethod.addCallParam(callParam);
                                         }
-                                        ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
-                                        for (ApiPlace place : apiPlaces) {
-                                            if (methodUrl.equalsIgnoreCase(place.value())) {
-                                                jsDefineList.add(SPACE8 + field.getName() + ": " + fieldcomment + ",");
+                                    });
+                                }
+
+                                List<Element> apiBeanNodeList = apiNode.elements("webApiBean");
+                                apiBeanNodeList.forEach((apiBeanNode) -> {
+                                    String apiBeanType = apiBeanNode.getTextTrim();
+                                    if (StringUtils.isNotBlank(apiBeanType)) {
+                                        try {
+                                            Class ptype = Class.forName(apiBeanType);
+
+                                            //region 对象表格
+                                            List<TAppClassField> fieldList = new LinkedList<>();
+                                            for (Field field : ptype.getDeclaredFields()) {
+                                                if (Modifier.isFinal(field.getModifiers())) {
+                                                    continue;
+                                                }
+                                                if (Modifier.isStatic(field.getModifiers())) {
+                                                    continue;
+                                                }
+
+                                                TAppClassField classField = new TAppClassField();
+                                                classField.setFieldName(field.getName());
+                                                classField.setFieldComment("");
+                                                Comment comment = field.getAnnotation(Comment.class);
+                                                if (comment != null) {
+                                                    classField.setFieldComment(comment.value());
+                                                }
+                                                classField.setFieldCurrent(0);
+                                                ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
+                                                for (ApiPlace place : apiPlaces) {
+                                                    classField.getFieldPlace().add(place.value());
+                                                    if (apiNode.elementTextTrim("businessMethod").equalsIgnoreCase(place.value())) {
+                                                        classField.setFieldCurrent(1);
+                                                    }
+                                                }
+
+                                                fieldList.add(classField);
                                             }
+
+                                            TAppMethodClass methodClass = new TAppMethodClass()
+                                                    .setClassName(ptype.getSimpleName())
+                                                    .setClassField(fieldList)
+                                                    .setResult(false);
+
+                                            appMethod.addClass(methodClass);
+                                            //endregion
+
+                                        } catch (ClassNotFoundException e) {
+                                            throw new RuntimeException(e);
                                         }
                                     }
-                                    jsDefineList.add("};");
 
-                                    jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": JSON.stringify(" + param.name() + "),");
-                                }
 
+                                });
+                                WebApiBean[] apiBeans = method.getAnnotationsByType(WebApiBean.class);
+                                for (WebApiBean apiBean : apiBeans) {
+                                    final boolean isarray = apiBean.type().isArray();
+                                    final Class ptype = isarray ? apiBean.type().getComponentType() : apiBean.type();
+
+                                    if (ptype.isPrimitive() || ptype == String.class || ptype == PageFlipper.class) {
+                                        continue;
+                                    }
+
+                                    if (!appMethod.containClass(ptype.getSimpleName(), apiBean.result())) {
+                                        //region 对象表格
+                                        List<TAppClassField> fieldList = new LinkedList<>();
+                                        for (Field field : ptype.getDeclaredFields()) {
+                                            if (Modifier.isFinal(field.getModifiers())) {
+                                                continue;
+                                            }
+                                            if (Modifier.isStatic(field.getModifiers())) {
+                                                continue;
+                                            }
+
+                                            TAppClassField classField = new TAppClassField();
+                                            classField.setFieldName(field.getName());
+                                            classField.setFieldComment("");
+                                            Comment comment = field.getAnnotation(Comment.class);
+                                            if (comment != null) {
+                                                classField.setFieldComment(comment.value());
+                                            }
+                                            classField.setFieldCurrent(0);
+                                            ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
+                                            for (ApiPlace place : apiPlaces) {
+                                                classField.getFieldPlace().add(place.value());
+                                                if (methodUrl.equalsIgnoreCase(place.value())) {
+                                                    classField.setFieldCurrent(1);
+                                                }
+                                            }
+
+                                            fieldList.add(classField);
+                                        }
+
+                                        TAppMethodClass methodClass = new TAppMethodClass()
+                                                .setClassName(ptype.getSimpleName())
+                                                .setClassField(fieldList)
+                                                .setResult(apiBean.result());
+
+                                        appMethod.addClass(methodClass);
+                                        //endregion
+                                    }
+                                }
+                            });
+                        }
+                    } else {
+                        String methodUrl = prefix + action.url();
+                        Boolean auth = action.auth();
+                        Integer actionId = action.actionid();
+                        String methodName = "";
+                        if (StringUtils.isNotEmpty(action.comment())) {
+                            methodName = action.comment();
+                        } else {
+                            methodName = method.getName();
+                        }
+                        String methodResult = action.result();
+                        methodResult = methodResult.replace("<", " ");
+                        methodResult = methodResult.replace(">", " ");
+
+                        TAppMethod appMethod = new TAppMethod()
+                                .setMethodName(methodName)
+                                .setCallFlag(0)
+                                .setUrl(methodUrl)
+                                .setMethodId(actionId)
+                                .setAuth(auth)
+                                .setResult(methodResult)
+                                .setSort(action.sort())
+                                .setSupportMethods(String.join("/", action.methods()));
+                        module.addMethod(appMethod);
+
+                        String jQueryMethod = "post";
+
+                        if (action.methods().length > 0) {
+                            String methodArrayStr = String.join("/", action.methods());
+                            methodArrayStr = methodArrayStr.toLowerCase();
+                            if (methodArrayStr.contains("post")) {
+                                jQueryMethod = "post";
+                            } else if (methodArrayStr.contains("get")) {
+                                jQueryMethod = "get";
                             }
                         }
-                        //endregion
 
-                        if (ptype.isPrimitive() || ptype == String.class || ptype == PageFlipper.class) {
-                            continue;
-                        }
 
-                        //region 对象表格
-                        List<TAppClassField> fieldList = new LinkedList<>();
-                        for (Field field : ptype.getDeclaredFields()) {
-                            if (Modifier.isFinal(field.getModifiers())) {
-                                continue;
-                            }
-                            if (Modifier.isStatic(field.getModifiers())) {
-                                continue;
+                        List<String> jsDefineList = new ArrayList<>();
+                        List<String> jsReferenceList = new ArrayList<>();
+
+                        for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) {
+                            //region 服务说明和参数表格
+                            String paramSrc = "";
+                            if (param.src() == HttpParamSourceType.COOKIE) {
+                                paramSrc = "Cookie";
+                            } else if (param.src() == HttpParamSourceType.HEADER) {
+                                paramSrc = "Header";
+                            } else if (param.src() == HttpParamSourceType.BODY) {
+                                paramSrc = "Body";
+                            } else if (param.src() == HttpParamSourceType.PARAMETER) {
+                                paramSrc = "Parameter";
                             }
+                            TAppMethodParam methodParam = new TAppMethodParam()
+                                    .setParamName(param.name())
+                                    .setParamComment(param.comment())
+                                    .setParamRequired(param.required())
+                                    .setParamSrc(paramSrc)
+                                    .setParamType(param.type().getSimpleName());
+
+                            appMethod.addParam(methodParam);
+                            //endregion
 
-                            TAppClassField classField = new TAppClassField();
-                            classField.setFieldName(field.getName());
-                            classField.setFieldComment("");
-                            Comment comment = field.getAnnotation(Comment.class);
-                            if (comment != null) {
-                                classField.setFieldComment(comment.value());
-                            }
-                            classField.setFieldCurrent(0);
-                            ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
-                            for (ApiPlace place : apiPlaces) {
-                                classField.getFieldPlace().add(place.value());
-                                if (methodUrl.equalsIgnoreCase(place.value())) {
-                                    classField.setFieldCurrent(1);
-                                }
-                            }
+                            //参数类型
+                            final boolean isarray = param.type().isArray();
+                            final Class ptype = isarray ? param.type().getComponentType() : param.type();
+
+                            //region 示例代码
+                            if ("parameter".equalsIgnoreCase(paramSrc)) {
+                                if (!"&".equalsIgnoreCase(param.name())) {
+                                    if (ClassesUtils.isPrimitive(ptype)) {
+                                        Class primitiveClass = ClassesUtils.getPrimitive(ptype);
+                                        if (primitiveClass != null) {
+                                            if (primitiveClass == int.class) {
+                                                jsDefineList.add("var " + param.name() + " = 测试整数;");
+                                                jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                            } else if (primitiveClass == long.class) {
+                                                jsDefineList.add("var " + param.name() + " = '测试长整数';");
+                                                jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                            } else if (primitiveClass == short.class) {
+                                                jsDefineList.add("var " + param.name() + " = '测试整数';");
+                                                jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                            } else if (primitiveClass == boolean.class) {
+                                                jsDefineList.add("var " + param.name() + " = 测试bool参数;");
+                                                jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                            } else if (primitiveClass == byte.class) {
+                                                jsDefineList.add("var " + param.name() + " = 测试字节数组;");
+                                                jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                            } else if (primitiveClass == float.class) {
+                                                jsDefineList.add("var " + param.name() + " = 测试浮点float参数;");
+                                                jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                            } else if (primitiveClass == double.class) {
+                                                jsDefineList.add("var " + param.name() + " = 测试浮点double参数;");
+                                                jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                            }
+                                        }
+                                    } else if (ptype == String.class) {
+                                        jsDefineList.add("var " + param.name() + " = '测试字符串';");
+                                        jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": " + param.name() + ",");
+                                    } else if (ptype == PageFlipper.class) {
+                                        jsDefineList.add("var " + param.name() + " = {");
+                                        jsDefineList.add(SPACE8 + "pageIndex: 页码(整数),");
+                                        jsDefineList.add(SPACE8 + "pageSize: 每页记录数(整数),");
+                                        jsDefineList.add("};");
+                                        jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": JSON.stringify(" + param.name() + "),");
+                                    } else {
+                                        jsDefineList.add("var " + param.name() + " = {;");
+                                        for (Field field : ptype.getDeclaredFields()) {
+                                            if (Modifier.isFinal(field.getModifiers())) {
+                                                continue;
+                                            }
+                                            if (Modifier.isStatic(field.getModifiers())) {
+                                                continue;
+                                            }
 
-                            fieldList.add(classField);
-                        }
+                                            String fieldcomment = "";
+                                            Comment comment = field.getAnnotation(Comment.class);
+                                            if (comment != null) {
+                                                fieldcomment = comment.value();
+                                            }
+                                            ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
+                                            for (ApiPlace place : apiPlaces) {
+                                                if (methodUrl.equalsIgnoreCase(place.value())) {
+                                                    jsDefineList.add(SPACE8 + field.getName() + ": " + fieldcomment + ",");
+                                                }
+                                            }
+                                        }
+                                        jsDefineList.add("};");
 
-                        TAppMethodClass methodClass = new TAppMethodClass()
-                                .setClassName(param.type().getSimpleName())
-                                .setClassField(fieldList)
-                                .setResult(false);
+                                        jsReferenceList.add(SPACE16 + "\"" + param.name() + "\": JSON.stringify(" + param.name() + "),");
+                                    }
 
-                        appMethod.addClass(methodClass);
-                        //endregion
-                    }
+                                }
+                            }
+                            //endregion
 
-                    appMethod.getDemoJquery().addAll(jsDefineList);
-                    appMethod.getDemoJquery().add("");
-                    appMethod.getDemoJquery().add("$.ajax({");
-                    appMethod.getDemoJquery().add(SPACE8 + "url: '/apis" + methodUrl + "',");
-                    appMethod.getDemoJquery().add(SPACE8 + "type: '" + jQueryMethod + "',");
-                    appMethod.getDemoJquery().add(SPACE8 + "data: {");
-                    appMethod.getDemoJquery().addAll(jsReferenceList);
-                    //appMethod.getDemoJquery().add(SPACE16 + "\"user\": JSON.stringify(data)");
-                    appMethod.getDemoJquery().add(SPACE8 + "},");
-                    appMethod.getDemoJquery().add(SPACE8 + "dataType: 'json',");
-                    appMethod.getDemoJquery().add(SPACE8 + "contentType: 'application/json',");
-                    appMethod.getDemoJquery().add(SPACE8 + "success: function (res) {");
-                    appMethod.getDemoJquery().add(SPACE16 + "if(res.httpCode == 200){");
-                    appMethod.getDemoJquery().add(SPACE24 + "console.log(res);");
-                    appMethod.getDemoJquery().add(SPACE16 + "} else {");
-                    appMethod.getDemoJquery().add(SPACE24 + "alert(res.msg);");
-                    appMethod.getDemoJquery().add(SPACE16 + "}");
-                    appMethod.getDemoJquery().add(SPACE8 + "},");
-                    appMethod.getDemoJquery().add(SPACE8 + "error: function (res) {");
-                    appMethod.getDemoJquery().add(SPACE16 + "alert(res);");
-                    appMethod.getDemoJquery().add(SPACE8 + "}");
-                    appMethod.getDemoJquery().add("});");
-
-                    WebApiBean[] apiBeans = method.getAnnotationsByType(WebApiBean.class);
-                    for (WebApiBean apiBean : apiBeans) {
-                        final boolean isarray = apiBean.type().isArray();
-                        final Class ptype = isarray ? apiBean.type().getComponentType() : apiBean.type();
-
-                        if (ptype.isPrimitive() || ptype == String.class || ptype == PageFlipper.class) {
-                            continue;
-                        }
+                            if (ptype.isPrimitive() || ptype == String.class || ptype == PageFlipper.class) {
+                                continue;
+                            }
 
-                        if (!appMethod.containClass(ptype.getSimpleName(), apiBean.result())) {
                             //region 对象表格
                             List<TAppClassField> fieldList = new LinkedList<>();
                             for (Field field : ptype.getDeclaredFields()) {
@@ -335,15 +561,85 @@ public class TAppDoc {
                             }
 
                             TAppMethodClass methodClass = new TAppMethodClass()
-                                    .setClassName(ptype.getSimpleName())
+                                    .setClassName(param.type().getSimpleName())
                                     .setClassField(fieldList)
-                                    .setResult(apiBean.result());
+                                    .setResult(false);
 
                             appMethod.addClass(methodClass);
                             //endregion
                         }
 
+                        appMethod.getDemoJquery().addAll(jsDefineList);
+                        appMethod.getDemoJquery().add("");
+                        appMethod.getDemoJquery().add("$.ajax({");
+                        appMethod.getDemoJquery().add(SPACE8 + "url: '/apis" + methodUrl + "',");
+                        appMethod.getDemoJquery().add(SPACE8 + "type: '" + jQueryMethod + "',");
+                        appMethod.getDemoJquery().add(SPACE8 + "data: {");
+                        appMethod.getDemoJquery().addAll(jsReferenceList);
+                        //appMethod.getDemoJquery().add(SPACE16 + "\"user\": JSON.stringify(data)");
+                        appMethod.getDemoJquery().add(SPACE8 + "},");
+                        appMethod.getDemoJquery().add(SPACE8 + "dataType: 'json',");
+                        appMethod.getDemoJquery().add(SPACE8 + "contentType: 'application/json',");
+                        appMethod.getDemoJquery().add(SPACE8 + "success: function (res) {");
+                        appMethod.getDemoJquery().add(SPACE16 + "if(res.httpCode == 200){");
+                        appMethod.getDemoJquery().add(SPACE24 + "console.log(res);");
+                        appMethod.getDemoJquery().add(SPACE16 + "} else {");
+                        appMethod.getDemoJquery().add(SPACE24 + "alert(res.msg);");
+                        appMethod.getDemoJquery().add(SPACE16 + "}");
+                        appMethod.getDemoJquery().add(SPACE8 + "},");
+                        appMethod.getDemoJquery().add(SPACE8 + "error: function (res) {");
+                        appMethod.getDemoJquery().add(SPACE16 + "alert(res);");
+                        appMethod.getDemoJquery().add(SPACE8 + "}");
+                        appMethod.getDemoJquery().add("});");
+
+                        WebApiBean[] apiBeans = method.getAnnotationsByType(WebApiBean.class);
+                        for (WebApiBean apiBean : apiBeans) {
+                            final boolean isarray = apiBean.type().isArray();
+                            final Class ptype = isarray ? apiBean.type().getComponentType() : apiBean.type();
+
+                            if (ptype.isPrimitive() || ptype == String.class || ptype == PageFlipper.class) {
+                                continue;
+                            }
 
+                            if (!appMethod.containClass(ptype.getSimpleName(), apiBean.result())) {
+                                //region 对象表格
+                                List<TAppClassField> fieldList = new LinkedList<>();
+                                for (Field field : ptype.getDeclaredFields()) {
+                                    if (Modifier.isFinal(field.getModifiers())) {
+                                        continue;
+                                    }
+                                    if (Modifier.isStatic(field.getModifiers())) {
+                                        continue;
+                                    }
+
+                                    TAppClassField classField = new TAppClassField();
+                                    classField.setFieldName(field.getName());
+                                    classField.setFieldComment("");
+                                    Comment comment = field.getAnnotation(Comment.class);
+                                    if (comment != null) {
+                                        classField.setFieldComment(comment.value());
+                                    }
+                                    classField.setFieldCurrent(0);
+                                    ApiPlace[] apiPlaces = field.getAnnotationsByType(ApiPlace.class);
+                                    for (ApiPlace place : apiPlaces) {
+                                        classField.getFieldPlace().add(place.value());
+                                        if (methodUrl.equalsIgnoreCase(place.value())) {
+                                            classField.setFieldCurrent(1);
+                                        }
+                                    }
+
+                                    fieldList.add(classField);
+                                }
+
+                                TAppMethodClass methodClass = new TAppMethodClass()
+                                        .setClassName(ptype.getSimpleName())
+                                        .setClassField(fieldList)
+                                        .setResult(apiBean.result());
+
+                                appMethod.addClass(methodClass);
+                                //endregion
+                            }
+                        }
                     }
 
                 }

+ 25 - 0
ddCommon/src/main/java/com/dderp/common/tdoc/TAppMethod.java

@@ -15,10 +15,14 @@ public class TAppMethod {
     private String result;
     private String supportMethods;
 
+    private int callFlag;
+
     private int sort;
 
     private List<String> demoJquery = new ArrayList<>();
 
+    private List<TAppCallParam> callParamList = new ArrayList<>();
+
     private List<TAppMethodParam> paramList = new ArrayList<>();
     private List<TAppMethodClass> classList = new ArrayList<>();
 
@@ -76,6 +80,18 @@ public class TAppMethod {
         return this;
     }
 
+    public void addCallParam(TAppCallParam param) {
+        callParamList.add(param);
+    }
+
+    public List<TAppCallParam> getCallParamList() {
+        return callParamList;
+    }
+
+    public void setCallParamList(List<TAppCallParam> callParamList) {
+        this.callParamList = callParamList;
+    }
+
     public void addParam(TAppMethodParam param) {
         paramList.add(param);
     }
@@ -120,4 +136,13 @@ public class TAppMethod {
         this.sort = sort;
         return this;
     }
+
+    public int getCallFlag() {
+        return callFlag;
+    }
+
+    public TAppMethod setCallFlag(int callFlag) {
+        this.callFlag = callFlag;
+        return this;
+    }
 }

+ 1 - 0
ddWebCore/src/main/java/com/dderp/webcore/rest/PlatformRest.java

@@ -29,6 +29,7 @@ public class PlatformRest extends BaseService {
     private PlatformService platformService;
 
     //region 平台档案
+
     @RestMapping(name = "queryPlatformList", auth = true, sort = 1, comment = "获取平台档案列表", methods = {"GET", "POST"})
     @WebApiBean(result = true, type = PlatformInfo.class)
     public CompletableFuture<RMap> queryPlatformList(

+ 1 - 0
ddWebCore/src/main/java/com/dderp/webcore/rest/flycat/ExpressOutRest.java

@@ -41,6 +41,7 @@ public class ExpressOutRest extends BaseService {
     }
 
     @RestMapping(name = "callExpress", auth = true, sort = 1, logging = 4, comment = "配送快递接口", methods = {"POST"})
+    @WebApiFile(fileName = "expressOut.xml")
     public CompletableFuture<InvokeCallResult> callExpress(
             @RestBody(comment = "提交数据") InvokeCallParams callParams,
             @RestParam(name = "&", comment = "登录用户,无需传入") ERPTokenUser currentUser,

+ 5 - 10
ddWebCore/src/main/java/com/dderp/webcore/servlet/ExpressCallServlet.java

@@ -124,18 +124,13 @@ public class ExpressCallServlet extends HttpServlet {
                     .dataSourceId(dataSourceId)
                     .supplierCode(supplierCode)
                     .build();
-            //此脚本异步调用算了,统一返回配送平台需要的返回值
-            RetResult<InvokeCallResult> callInvokeResult = expressOutService.callExpress(callInvokeParams, ERPUtils.getSysTokenUser(), dataSourceId, supplierCode);
-            //每个平台需要返回的玩意也不同,
-            if (callInvokeResult.isSuccess()) {
-                response.finish(200, callSignResult.getResult().getData());
-            } else {
-                response.finish(200, callSignResult.getRetinfo());
-            }
+            //此脚本异步调用算了
+            expressOutService.callExpress(callInvokeParams, ERPUtils.getSysTokenUser(), dataSourceId, supplierCode);
+            //懒得再写脚本生成各平台需要的返回值了,直接在验证签名里面的内容返回,让验证签名脚本返回各平台返回内容
+            response.finish(200, callSignResult.getResult().getData());
         } else {
-            response.finish(200, callSignResult.getRetinfo());
+            response.finish(200, callSignResult.getResult().getData());
         }
-
     }
 
     //当前查看顺丰的回调,没有字段体现出顺丰的接口,大概每个快递平台需要不同的地址提供回调,顺丰的回调可以通过url_index来判断回调的业务

+ 1 - 1
pom.xml

@@ -21,7 +21,7 @@
         <java.version>17</java.version>
         <maven.compiler.source>${java.version}</maven.compiler.source>
         <maven.compiler.target>${java.version}</maven.compiler.target>
-        <frame.version>10.2.77</frame.version>
+        <frame.version>10.2.80</frame.version>
     </properties>
 
     <repositories>

+ 361 - 0
root/api/api-template.html

@@ -0,0 +1,361 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <title>${appdoc.appName}接口说明文档</title>
+    <link href="/api/bootstrap.min.css" rel="stylesheet">
+    <!--<link href="https://cdn.bootcss.com/highlight.js/9.12.0/styles/default.min.css" rel="stylesheet">-->
+    <link href="https://cdn.staticfile.org/prettify/r298/prettify.min.css" rel="stylesheet">
+    <script src="/api/jquery.min.js"></script>
+    <script src="/api/bootstrap.min.js"></script>
+    <!--<script src="https://cdn.bootcss.com/highlight.js/9.12.0/highlight.min.js"></script>-->
+    <script src="https://cdn.staticfile.org/prettify/r298/prettify.min.js"></script>
+    <style type="text/css">
+        body {
+            padding-top: 50px;
+        }
+
+        .sub-header {
+            padding-bottom: 10px;
+            border-bottom: 1px solid #eee;
+        }
+
+        /*
+         * Top navigation
+         * Hide default border to remove 1px line.
+         */
+        .navbar-fixed-top {
+            border: 0;
+        }
+
+        /*
+         * Sidebar
+         */
+
+        /* Hide for mobile, show later */
+        .sidebar {
+            display: none;
+        }
+
+        @media (min-width: 768px) {
+            .sidebar {
+                position: fixed;
+                top: 51px;
+                bottom: 0;
+                left: 0;
+                z-index: 1000;
+                display: block;
+                padding: 20px;
+                overflow-x: hidden;
+                overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
+                background-color: #f5f5f5;
+                border-right: 1px solid #eee;
+            }
+        }
+
+        /* Sidebar navigation */
+        .nav-sidebar {
+            margin-right: 5px; /* 20px padding + 1px border */
+            margin-bottom: 20px;
+            margin-left: 5px;
+            margin-top: 5px;
+        }
+
+        .nav-sidebar > li > a {
+            padding-right: 20px;
+            padding-left: 20px;
+        }
+
+        .nav-sidebar > .active > a,
+        .nav-sidebar > .active > a:hover,
+        .nav-sidebar > .active > a:focus {
+            color: #fff;
+            background-color: #428bca;
+        }
+
+        /*
+         * Main content
+         */
+
+        .main {
+            padding: 20px;
+        }
+
+        @media (min-width: 768px) {
+            .main {
+                padding-right: 40px;
+                padding-left: 40px;
+            }
+        }
+
+        .main .page-header {
+            margin-top: 0;
+        }
+
+        /*
+         * Placeholder dashboard ideas
+         */
+
+        .placeholders {
+            margin-bottom: 30px;
+            text-align: center;
+        }
+
+        .placeholders h4 {
+            margin-bottom: 0;
+        }
+
+        .placeholder {
+            margin-bottom: 20px;
+        }
+
+        .placeholder img {
+            display: inline-block;
+            border-radius: 50%;
+        }
+
+        .linenums li {
+            list-style-type: decimal !important;
+        }
+    </style>
+
+    <!--<script>hljs.initHighlightingOnLoad();</script>-->
+</head>
+<body onload="prettyPrint()">
+<nav class="navbar navbar-inverse navbar-fixed-top">
+    <div class="container-fluid">
+        <div class="navbar-header">
+            <a class="navbar-brand" href="#">${appdoc.appName}接口说明文档</a>
+        </div>
+    </div>
+</nav>
+<div class="container-fluid">
+    <div class="row">
+        <div class="col-sm-3 col-md-2 sidebar">
+            <%
+            var panelModuleHeadId;
+            var panelModuleHeadIndex = 0;
+            var panelModuleCollapseId;
+            var panelModuleCollapseIndex = 0;
+            var moduleList = appdoc.moduleList;
+            var moduleLeftIndex = 0;
+            for (appmodule in moduleList) {
+            panelModuleHeadId = "panelModuleHead" + panelModuleHeadIndex;
+            panelModuleCollapseId = "panelModuleCollapse" + panelModuleHeadIndex;
+            %>
+            <div class="panel-group" role="tablist">
+                <div class="panel panel-default">
+                    <div class="panel-heading" role="tab" id="${panelModuleHeadId}">
+                        <h4 class="panel-title">
+                            <a class="" role="button" data-toggle="collapse" href="#${panelModuleCollapseId}"
+                               aria-expanded="false" aria-controls="${panelModuleCollapseId}">
+                                ${appmodule.moduleName}
+                                <span class="badge">${appmodule.methodList.~size}</span>
+                            </a>
+                        </h4>
+                    </div>
+                    <div id="${panelModuleCollapseId}" class="panel-collapse collapse in" role="tabpanel"
+                         aria-labelledby="${panelModuleHeadId}" aria-expanded="false" style="">
+                        <ul class="nav nav-sidebar">
+                            <%
+                            var methodList = appmodule.methodList;
+                            var methodLeftIndex = 0;
+                            for (appmethod in methodList) {
+                            %>
+                            <li><a href="#" moduleindex="${moduleLeftIndex}" methodindex="${methodLeftIndex}"
+                                   onclick="viewMethodDetail(this)">${appmethod.methodName}</a></li>
+                            <%
+                            methodLeftIndex++;
+                            }
+                            %>
+                        </ul>
+                    </div>
+                </div>
+            </div>
+            <%
+            moduleLeftIndex++;
+            panelModuleHeadIndex++;
+            panelModuleHeadIndex++;
+            }
+            %>
+
+
+        </div>
+        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
+            <h1 class="page-header">函数说明</h1>
+            <%
+            var moduleIndex = 0;
+            //var moduleList = appdoc.moduleList;
+            for (appmodule in moduleList) {
+            var methodList = appmodule.methodList;
+            var methodIndex = 0;
+            for (appmethod in methodList) {
+            var moduleMethodPanelGroupId = "viewModuleMethodPanel-" + moduleIndex + "-" + methodIndex;
+            %>
+            <div class="panel panel-default" id="${moduleMethodPanelGroupId}" style="display: none;">
+                <div class="panel-heading">
+                    <h4 class="panel-title">
+                        ${appmethod.methodName} <% if (appmethod.callFlag == 1) { %> (Call InvokeCallParams方法) <% } %>
+                    </h4>
+                </div>
+                <div class="panel-body">
+                    <p><h4><span class="label label-primary">接口地址:${appmethod.url}</span></h4></p>
+                    <p><h4><span class="label label-success">调用方法:${appmethod.supportMethods}</span></h4></p>
+                    <p><h4><span class="label label-info">接口返回:${appmethod.result}</span></h4></p>
+                    <p><h4><span class="label label-warning">是否鉴权:${appmethod.auth}</span></h4></p>
+                    <p><h4><span class="label label-default">参数<span
+                        class="badge">${appmethod.paramList.~size}</span></span></h4></p>
+                    <div class="table-responsive">
+                        <table class="table table-striped">
+                            <thead>
+                            <tr>
+                                <th>参数名称</th>
+                                <th>参数描述</th>
+                                <th>参数类型</th>
+                                <th>来源</th>
+                                <th>是否必须</th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <%
+                            for (methodParam in appmethod.paramList) {
+                            %>
+                            <tr>
+                                <td>${methodParam.paramName}</td>
+                                <td>${methodParam.paramComment}</td>
+                                <td>${methodParam.paramType}</td>
+                                <td>${methodParam.paramSrc}</td>
+                                <td>${methodParam.paramRequired}</td>
+                            </tr>
+                            <%
+                            }
+                            %>
+
+
+                            </tbody>
+                        </table>
+                    </div>
+
+                    <p><h4><span class="label label-success">参数params的json说明</span></h4></p>
+                    <div class="table-responsive">
+                        <table class="table table-striped">
+                            <thead>
+                            <tr>
+                                <th>参数名称</th>
+                                <th>参数描述</th>
+                                <th>参数类型</th>
+                                <th>来源</th>
+                                <th>是否必须</th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <%
+                            for (callParam in appmethod.callParamList) {
+                            %>
+                            <tr>
+                                <td>${callParam.paramName}</td>
+                                <td>${callParam.paramComment}</td>
+                                <td>${callParam.paramType}</td>
+                                <td>${callParam.paramSrc}</td>
+                                <td>${callParam.paramRequired}</td>
+                            </tr>
+                            <%
+                            }
+                            %>
+
+
+                            </tbody>
+                        </table>
+                    </div>
+
+
+                    <p><h4><span class="label label-default">示例代码【apis表示nginx的反向代理路径,按配置替换;如果函数返回的直接字符串,请修改datatype为String、Script、XML等】</span></h4></p>
+                    <!--<div class="table-responsive">-->
+                                <pre class="prettyprint linenums">
+<%
+    for (demo in appmethod.demoJquery) {
+    %>
+${demo}
+    <%
+    }
+    %>
+                                   </pre>
+                    <p><h4><span class="label label-default">类型<span
+                        class="badge">${appmethod.classList.~size}</span></span></h4></p>
+                    <%
+                    for (methodClass in appmethod.classList) {
+                    %>
+                    <p><h4><span class="label label-danger">${methodClass.className}<% if (methodClass.result) { %><b>[返回对象]</b><% } %><b>[蓝色表示此接口需要的属性]</b></span></h4></p>
+                    <div class="table-responsive">
+                        <table class="table table-striped">
+                            <thead>
+                            <tr>
+                                <th>属性名称</th>
+                                <th>属性描述</th>
+                                <th>服务接口</th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <%
+                            for (field in methodClass.classField) {
+                            %>
+                            <tr>
+                                <%
+                                if (field.fieldCurrent == 1) {
+                                %>
+                                <td class="text-primary">
+                                    <b>${field.fieldName}</b>
+                                    <% } else { %>
+                                <td>
+                                    ${field.fieldName}
+                                    <% } %>
+                                </td>
+                                <td>
+                                    ${field.fieldComment}
+                                </td>
+                                <td>
+                                    <%
+                                    for (place in field.fieldPlace) {
+                                    %>
+                                    ${place}
+                                    <br>
+                                    <%
+                                    }
+                                    %>
+                                </td>
+                            </tr>
+                            <%
+                            }
+                            %>
+
+
+                            </tbody>
+                        </table>
+                    </div>
+                    <%
+                    }
+                    %>
+                </div>
+            </div>
+            <%
+            methodIndex++;
+            }
+            moduleIndex++;
+            }
+            %>
+        </div>
+    </div>
+</div>
+
+<script>
+    function viewMethodDetail(e) {
+        var moduleIndex = $(e).attr("moduleindex");
+        var methodIndex = $(e).attr("methodindex");
+        var methodDoc = "viewModuleMethodPanel-" + moduleIndex + "-" + methodIndex;
+        var $methodPanel = $("#" + methodDoc);
+        $methodPanel.siblings(".panel").hide();
+        $methodPanel.show();
+    }
+</script>
+</body>
+</html>

Datei-Diff unterdrückt, da er zu groß ist
+ 6 - 0
root/api/bootstrap.min.css


Datei-Diff unterdrückt, da er zu groß ist
+ 7 - 0
root/api/bootstrap.min.js


Datei-Diff unterdrückt, da er zu groß ist
+ 4 - 0
root/api/jquery.min.js


+ 121 - 0
root/apilogin.html

@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>接口服务登录</title>
+    <link href="//cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
+    <script src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
+    <script src="//cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
+
+    <style type="text/css">
+        .login-box {
+            width: 360px;
+            padding: 30px 20px;
+            background: #fff;
+            margin: auto;
+            margin-top: 170px;
+        }
+
+        .login-box-body h3 {
+            display: block;
+            font-weight: 500;
+            line-height: 1.1;
+            color: inherit;
+            font-size: 24px;
+            text-align: center;
+            padding-top: 20px;
+            padding-bottom: 20px;
+        }
+    </style>
+</head>
+<body style="background: #d2d6de;">
+<div class="login-box">
+    <!-- /.login-logo -->
+    <div class="login-box-body">
+        <!--<div class="logo"><img src="images/logo.png"></div>-->
+        <h3>接口服务登录</h3>
+
+        <div>
+            <div class="form-group has-feedback">
+                <input id="userName" type="text" class="form-control" name="account" placeholder="用户名">
+                <span class="glyphicon glyphicon-user form-control-feedback"></span>
+            </div>
+            <div class="form-group has-feedback">
+                <input id="password" type="password" class="form-control" name="pwd" placeholder="密码">
+                <span class="glyphicon glyphicon-lock form-control-feedback"></span>
+            </div>
+
+            <div style="margin-left: -18px;text-align: center">
+                <button id="loginBtn" type="submit" class="btn btn-primary" style="width: 310px;">登录</button>
+            </div>
+        </div>
+        <!-- /.login-box-body -->
+    </div>
+</div>
+<script type="text/javascript">
+    $(function () {
+        $("#loginBtn").click(function () {
+            login();
+        });
+
+        $(document).keydown(function (event) {
+            if (event.keyCode == 13) {
+                $("#loginBtn").click();
+            }
+        });
+
+    });
+    //登录方法
+    function login() {
+        $("#loginBtn").html("登陆中...");
+        var srcurl = window.location.href;
+        var url="/user/login";
+        if (srcurl.indexOf("apis") != -1) {
+            url="/apis/user/login";
+        }
+        var account=$("#userName").val();
+        var password=$("#password").val();
+        if(account ==""){
+            alert("用户名不能为空");
+            return;
+        }
+        if(password==""){
+            alert("请输入密码");
+            return;
+        }
+        var data={
+            "account":account,
+            "password":password
+        };
+
+        $.ajax({
+            url:url,
+            type:'post',
+            data: {
+                "user": JSON.stringify(data)
+            },
+            dataType:'json',
+            contentType : 'application/json',
+            success:function (res) {
+                if(res.httpCode == 200){
+                    $("#loginBtn").html("登陆成功");
+                    var destUrl="/xdoc/index";
+                    if (srcurl.indexOf("apis") != -1) {
+                        destUrl="/apis/xdoc/index";
+                    }
+                    window.location.href=destUrl;
+                }
+                else{
+                    $("#loginBtn").html("登陆");
+                    alert(res.msg);
+                }
+            },
+            error:function (res) {
+                alert(res.msg);
+            }
+        })
+
+    }
+</script>
+</body>
+</html>