فهرست منبع

Merge branch 'kuaiyingyong' of iqiyoo:zhuishuyun_wap into kuaiyingyong

songdb 5 سال پیش
والد
کامیت
11e0ebaae0
100فایلهای تغییر یافته به همراه14766 افزوده شده و 1998 حذف شده
  1. 229 19
      .env.example
  2. 68 0
      app/Http/Controllers/QuickApp/BaseController.php
  3. 70 0
      app/Http/Controllers/QuickApp/Book/BookCategoryController.php
  4. 658 0
      app/Http/Controllers/QuickApp/Book/BookController.php
  5. 668 0
      app/Http/Controllers/QuickApp/Book/ChapterController.php
  6. 45 0
      app/Http/Controllers/QuickApp/Book/Transformers/BookTransformer.php
  7. 22 0
      app/Http/Controllers/QuickApp/Book/Transformers/ChapterListTransformer.php
  8. 24 0
      app/Http/Controllers/QuickApp/Book/Transformers/ChapterTransformer.php
  9. 124 0
      app/Http/Controllers/QuickApp/Oauth/UsersController.php
  10. 719 0
      app/Http/Controllers/QuickApp/Order/OrdersController.php
  11. 17 0
      app/Http/Controllers/QuickApp/Order/Transformers/BookOrderTransformer.php
  12. 19 0
      app/Http/Controllers/QuickApp/Order/Transformers/ChapterOrderTransformer.php
  13. 16 0
      app/Http/Controllers/QuickApp/Order/Transformers/ChargeListTransformer.php
  14. 19 0
      app/Http/Controllers/QuickApp/Order/Transformers/OrderTransformer.php
  15. 189 0
      app/Http/Controllers/QuickApp/User/ReadRecordController.php
  16. 20 0
      app/Http/Controllers/QuickApp/User/Transformers/ReadRecordTransformer.php
  17. 24 0
      app/Http/Controllers/QuickApp/User/Transformers/UserShelfBooksTransformer.php
  18. 96 0
      app/Http/Controllers/QuickApp/User/UserController.php
  19. 225 0
      app/Http/Controllers/QuickApp/User/UserShelfBooksController.php
  20. 15 0
      app/Http/Controllers/QuickApp/apidoc.json
  21. 4 0
      app/Http/Controllers/QuickApp/header.md
  22. 2 16
      app/Http/Kernel.php
  23. 0 65
      app/Http/Middleware/CrmUser.php
  24. 0 30
      app/Http/Middleware/EnableCrossRequest.php
  25. 1 25
      app/Http/Middleware/EncryptCookies.php
  26. 9 12
      app/Http/Middleware/XchengxuGetUserFromToken.php
  27. 2 6
      app/Http/Middleware/CheckParam.php
  28. 7 6
      app/Http/Middleware/XchengxuRefreshToken.php
  29. 0 794
      app/Http/Middleware/ReadOauth.php
  30. 0 17
      app/Http/Middleware/VerifyCsrfToken.php
  31. 74 0
      app/Http/Routes/QuickApp/QuickAppRoutes.php
  32. 0 13
      app/Http/Routes/Wap/OfficialAccountRoutes.php
  33. 0 345
      app/Http/Routes/Wap/WapRoutes.php
  34. 0 32
      app/Http/Routes/WapBrower/WebRoutes.php
  35. 0 245
      app/Http/Routes/Wechat/OfficialAccountRoutes.php
  36. 0 87
      app/Http/Routes/Xchengxu/XchengxuRoutes.php
  37. 18 0
      app/Modules/BaseService.php
  38. 93 73
      app/Modules/Book/Services/BookService.php
  39. 11 0
      app/Modules/User/Models/QappUser.php
  40. 101 0
      app/Modules/User/Services/QappUserService.php
  41. 4 49
      app/Providers/RouteServiceProvider.php
  42. 186 164
      config/error.php
  43. 3773 0
      public/kyydoc/api_data.js
  44. 3773 0
      public/kyydoc/api_data.json
  45. 1 0
      public/kyydoc/api_project.js
  46. 1 0
      public/kyydoc/api_project.json
  47. 532 0
      public/kyydoc/css/style.css
  48. BIN
      public/kyydoc/img/favicon.ico
  49. BIN
      public/kyydoc/img/glyphicons-halflings-white.png
  50. BIN
      public/kyydoc/img/glyphicons-halflings.png
  51. 659 0
      public/kyydoc/index.html
  52. 25 0
      public/kyydoc/locales/ca.js
  53. 25 0
      public/kyydoc/locales/de.js
  54. 25 0
      public/kyydoc/locales/es.js
  55. 25 0
      public/kyydoc/locales/fr.js
  56. 25 0
      public/kyydoc/locales/it.js
  57. 47 0
      public/kyydoc/locales/locale.js
  58. 25 0
      public/kyydoc/locales/nl.js
  59. 25 0
      public/kyydoc/locales/pl.js
  60. 25 0
      public/kyydoc/locales/pt_br.js
  61. 25 0
      public/kyydoc/locales/ru.js
  62. 25 0
      public/kyydoc/locales/zh.js
  63. 25 0
      public/kyydoc/locales/zh_cn.js
  64. 746 0
      public/kyydoc/main.js
  65. 340 0
      public/kyydoc/utils/handlebars_helper.js
  66. 181 0
      public/kyydoc/utils/send_sample_request.js
  67. 9 0
      public/kyydoc/vendor/bootstrap-responsive.min.css
  68. 9 0
      public/kyydoc/vendor/bootstrap.min.css
  69. 6 0
      public/kyydoc/vendor/bootstrap.min.js
  70. 49 0
      public/kyydoc/vendor/diff_match_patch.min.js
  71. 28 0
      public/kyydoc/vendor/handlebars.min.js
  72. 4 0
      public/kyydoc/vendor/jquery.min.js
  73. 61 0
      public/kyydoc/vendor/lodash.min.js
  74. 21 0
      public/kyydoc/vendor/path-to-regexp/LICENSE
  75. 204 0
      public/kyydoc/vendor/path-to-regexp/index.js
  76. 100 0
      public/kyydoc/vendor/polyfill.js
  77. 101 0
      public/kyydoc/vendor/prettify.css
  78. 2 0
      public/kyydoc/vendor/prettify/lang-apollo.js
  79. 3 0
      public/kyydoc/vendor/prettify/lang-basic.js
  80. 18 0
      public/kyydoc/vendor/prettify/lang-clj.js
  81. 2 0
      public/kyydoc/vendor/prettify/lang-css.js
  82. 3 0
      public/kyydoc/vendor/prettify/lang-dart.js
  83. 2 0
      public/kyydoc/vendor/prettify/lang-erlang.js
  84. 1 0
      public/kyydoc/vendor/prettify/lang-go.js
  85. 2 0
      public/kyydoc/vendor/prettify/lang-hs.js
  86. 3 0
      public/kyydoc/vendor/prettify/lang-lisp.js
  87. 1 0
      public/kyydoc/vendor/prettify/lang-llvm.js
  88. 2 0
      public/kyydoc/vendor/prettify/lang-lua.js
  89. 6 0
      public/kyydoc/vendor/prettify/lang-matlab.js
  90. 2 0
      public/kyydoc/vendor/prettify/lang-ml.js
  91. 2 0
      public/kyydoc/vendor/prettify/lang-mumps.js
  92. 4 0
      public/kyydoc/vendor/prettify/lang-n.js
  93. 3 0
      public/kyydoc/vendor/prettify/lang-pascal.js
  94. 1 0
      public/kyydoc/vendor/prettify/lang-proto.js
  95. 2 0
      public/kyydoc/vendor/prettify/lang-r.js
  96. 1 0
      public/kyydoc/vendor/prettify/lang-rd.js
  97. 2 0
      public/kyydoc/vendor/prettify/lang-scala.js
  98. 2 0
      public/kyydoc/vendor/prettify/lang-sql.js
  99. 3 0
      public/kyydoc/vendor/prettify/lang-tcl.js
  100. 0 0
      public/kyydoc/vendor/prettify/lang-tex.js

+ 229 - 19
.env.example

@@ -1,21 +1,41 @@
 APP_ENV=local
 APP_DEBUG=true
 APP_KEY=base64:IUGnJ8O7ITqPwyOTQn85mFcioQaEqwu4Vn2UXk9VN5c=
-
+APP_LOG=daily
+LOG_MAX_FILES=3
+APP_ALLOW='*'
 DB_CONNECTION=mysql
-DB_HOST=rm-bp1z1dto3n2rdb02f.mysql.rds.aliyuncs.com
+#DB_HOST=rm-bp1z1dto3n2rdb02f.mysql.rds.aliyuncs.com
+DB_READ_HOST=rm-bp1z1dto3n2rdb02f.mysql.rds.aliyuncs.com
+DB_WRITE_HOST=rm-bp1z1dto3n2rdb02f.mysql.rds.aliyuncs.com
 DB_PORT=3306
-DB_DATABASE=yueduyun
+DB_DATABASE=otest_yueduyun
 DB_USERNAME=yueduyun
 DB_PASSWORD=yueduyun2017#Ydy
 
+CHAPTER_DB_HOST=rm-bp1z1dto3n2rdb02f.mysql.rds.aliyuncs.com
+CHAPTER_DB_PORT=3306
+CHAPTER_DB_DATABASE=otest_zsy_sub
+CHAPTER_DB_USERNAME=yueduyun
+CHAPTER_DB_PASSWORD=yueduyun2017#Ydy
+
+NEW_YUNQI_DB_HOST=rm-bp1hr7s22jozojrap.mysql.rds.aliyuncs.com
+NEW_YUNQI_DB_PORT=3306
+NEW_YUNQI_DB_DATABASE=xinyunxi
+NEW_YUNQI_DB_USERNAME=xinyunxi
+NEW_YUNQI_DB_PASSWORD=NewYunxi!zwkj2066
+
 CACHE_DRIVER=file
 SESSION_DRIVER=file
 QUEUE_DRIVER=sync
 
 REDIS_HOST=127.0.0.1
-REDIS_PASSWORD=null
+REDIS_PASSWORD=bd_zsy_online_test
 REDIS_PORT=6379
+REDIS_HOST_CHAPTER=127.0.0.1
+REDIS_PORT_CHAPTER=6379
+REDIS_PASSWORD_CHAPTER=bd_zsy_online_test
+REDIS_PASSWORD_ORDER=bd_zsy_online_test
 
 MAIL_DRIVER=smtp
 MAIL_HOST=mailtrap.io
@@ -24,39 +44,229 @@ MAIL_USERNAME=null
 MAIL_PASSWORD=null
 MAIL_ENCRYPTION=null
 
-
 QUEUE_DRIVER=rabbitmq
 
-RABBITMQ_HOST=172.16.157.148
+#RABBITMQ_HOST=116.62.153.249
+RABBITMQ_HOST=172.16.114.111
 RABBITMQ_PORT=5672
 RABBITMQ_VHOST=/
-RABBITMQ_LOGIN=test
-RABBITMQ_PASSWORD=6acbQWE13
+RABBITMQ_LOGIN=online_test
+RABBITMQ_PASSWORD='online_test_123#123'
 RABBITMQ_QUEUE=queue_name
 
+YCSD_CALL_PAY_MERCHANT_ID=44
 
-
-CHANNEL_MAIN_DOMAIN=fx.readcloud.com
+CHANNEL_MAIN_DOMAIN=pre.aizhuishu.com
 CHANNEL_SITE_PREFIX=site
-PROTOCOL=http
 IAMGE_SERVER_DOMAIN=//images.wangluogudu.com
 OFFICIAL_ACCOUNT_HOST=http://oauth.yueloo.com.cn
 OFFICIAL_ACCOUNT_AUTH_URL=http://oauth.yueloo.com.cn/oauth/oauth_platform
 OFFICIAL_ACCOUNT_AUTH_REDIRECT_URL=/service/new
 
-LOWEST_WITHDRAW_MONEY=100
+LOWEST_WITHDRAW_MONEY=1
+
+PROTOCOL=http
+H5_SCHEME=http
+CUSTOM_HOST=pre.aizhuishu
+WECHAT_CUSTOM_HOST=zhuishuyun
+GROUP_NICK=zhuishuyun
+MEDIA_API_BASE_URI=http://preauth.aizhuishu.com/api/
 
 SECRET_KEY=zhuishuyun#_2017
 
-CHANNEL_DOMAIN=
-WAP_DOMAIN=
-MANAGE_DOMAIN=
-WECHAT_DOMAIN=
+CHANNEL_DOMAIN=channel.pre.aizhuishu.com
+WAP_DOMAIN=site{i}.pre.{j}.com
+WAP_BROWER_DOMAIN=bsite{i}.pre.{j}.com
+PAY_WAP_DOMAIN=prepay.aizhuishu.com
+MANAGE_DOMAIN=manage.pre.aizhuishu.com
+WECHAT_DOMAIN=172.16.114.111
+CP_DOMAIN=cp.pre.aizhuishu.com
+FRIEND_LINK_DOMAIN=site{i}.pre.{j}.com
+
+OFFICIAL_AUTH_FOMAIN=preauth.aizhuishu.com
+
+HELP_DOMAIN=help
+OFFICIAL_FINANCE='116.62.153.249'
+
+
+OSS_ACCESS_ID=VM2vq1ZtF0LT4hUx
+OSS_ACCESS_KEY=eqllJuthP21t5A6tln7dSE3g9N00Ad
+OSS_END_POINT__=oss-cn-hangzhou.internal.aliyuncs.com
+OSS_END_POINT=oss-cn-hangzhou.aliyuncs.com
+OSS_BUCKET=yueduyun
+
+SMS_AccessKeyId=LTAIGE5kkq4PGUjv
+SMS_AccessKeySecret=q2HsOLhOqQtvkc2PHlR6uROdZ1zQFg
+SMS_SIGN=追书云
+
+OAUTH_KEY=zhuishuyun#_2017
+U_COOKIE_EXPIRE=2592000
+COOKIE_AUTH_WEB_WECHAT=web_user_auth
+BOOK_MALE_LOOP=1,2,3,4,5,6
+BOOK_MALE_HOT=76,757,4,73,642,780
+BOOK_MALE_ZHIBO=635,623,88
+BOOK_MALE_RECOM=72,85,639
+BOOK_MALE_NEW_RECOM=673,67,668
+
+BOOK_FEMALE_LOOP=6,535,521,135,47,46
+BOOK_FEMALE_HOT=7,5,464,126,60,52
+BOOK_FEMALE_ZHIBO=159,354,151,31,12,517
+BOOK_FEMALE_RECOM=46,51,45,242,124,165
+BOOK_FEMALE_NEW_RECOM=442,355,115,56,479,20
+
+
+OFFICIAL_PAY_CALL_BACK_URL='https://prepay.aizhuishu.com/pay/wcback_official'
+ALLINPAY_PAY_NOFITY_URL='https://prepay.aizhuishu.com/pay/wcback_allinpay'
+PALMPAY_NOFITY_URL='https://prepay.aizhuishu.com/pay/wcback_palmpay'
+ORIGINBANK_NOFITY_URL='https://prepay.aizhuishu.com/pay/wcback_originbank'
+LIANLIAN_PAY_CALL_BACK_URL='https://prepay.aizhuishu.com/pay/wcback_lianlianpay'
+UNIONPAY_NOFITY_URL='https://prepay.aizhuishu.com/pay/wcback_union'
+AUTH_CALLBACK_URL=https://zsynewauth.aizhuishu.com/wxAuthCallBack/
+AUTH_CALLBACK_URL_V2=https://preauth.aizhuishu.com/wxAuthCallBackv2/
+AUTH_CALLBACK_URL_V2_2=https://preauth.aizhuishu.com/wxAuthCallBack2v2/
+#WECHAT_AUTH_APPID=wxdbc486f1b4f6a8c3
+WECHAT_AUTH_APPID=wxebcb86ec4b80eaca
+WECHAT_SUB_APPID=wxdbc486f1b4f6a8c3
+
+XIYUE_HOST='https://xiyue.aizhuishu.com'
 
+AUTH_URL_='https://preauth.aizhuishu.com/user_oauth_v2'
+AUTH_URL__='https://zsyauth.aizhuishu.com/user_oauth'
+AUTH_URL='https://zsynewauth.aizhuishu.com/user_oauth'
+CREATE_PAY_URL='https://prepay.aizhuishu.com/goToPay'
+
+
+MONTH_ORDER_APPID=Aekgrv87yo5785l715
+MONTH_ORDER_APP_SECRET=57gwsnd158ib2sn7wluq57167858fzm
+MONTH_ORDER_KEY=sn7wluq5716brp8fzm
+MONTH_ORDER_PLAN_ID=127955
+MONTH_WEEK_ORDER_PLAN_ID=128349
+MONTH_QUARTER_ORDER_PLAN_ID=129440
+MONTH_PAY_SIGN_NOTIFY_URLV2='https://prepay.aizhuishu.com/monthpay/signcallbackv2'
+MONTH_PAY_CALLBACK_URLV2='https://prepay.aizhuishu.com/monthpay/ordercallbackv2'
+
+
+TEST_UID=10008,20213,167642,7530358,7530367,7530394,7530399,7530436,7530362,7530397,109861757,109861817,109861821,109861826,109861820,109861819,109861830,109861819
 
-#自动打款开关
 PAYMENT_AUTO_PAY_ON=false
 
 
-#结算过滤
-not_in_pay_merchant_id=13
+WEB_READER_BACKGROUND=3
+WEB_READER_FONT_SIZE=19
+WEB_READER_PAGE_STYLE=vertical
+
+SEND_ORDER_EXPORT_SPECIAL_CHANNEL=123,14,691,691,695,2
+
+SIGN_MALE_RECOMF=758,642
+SIGN_FEMALE_RECOMF=857,917
+
+ACTIVITY_ID=16
+ACTIVITY_TITLE='[五一书币欢乐送,领取6000书币]'
+OFF_SHELF_DOMAIN='iycdm.com'
+CHANNEL_INNER_ROLE=5,14,123,2
+QRCODE_AUTH_URL='https://zsyauth.aizhuishu.com/user_oauth'
+LONG_ACTIVITY_PRODUCT_ID=916,917
+
+
+SIGN_CALL_BACK_ACTIVITY_ID=16
+SIGN_CALL_BACK_ACTIVITY_TITLE=¥¥五一书币欢乐送·充28送32
+
+
+YEAR_ACTIVITY_CHANNEL=5,8,123,266,14,2
+YEAR_MALE_BID=2346,2434,1108,2408,1599,642,2115
+YEAR_FEMALE_BID=2346,2434,1108,2408,1599,642,2115
+YEAR_MALE_SITE=5
+YEAR_ACTIVITY_ID=10816
+YEAR_OLD_ACTIVITY_ID=17
+internal_shelf_text_mode_book_white_list=1
+
+READR_BOTTOM_ACTIVITY_IMG='https://yueduyun.oss-cn-hangzhou.aliyuncs.com/h5/2018042605.jpg'
+READR_BOTTOM_ACTIVITY_IMG_YEAR='https://yueduyun.oss-cn-hangzhou.aliyuncs.com/h5/2018042702.jpg'
+
+not_in_pay_merchant_id=13,14,15,16,17,18,19,20,21,22,23,24,25,26,27
+
+CHANNEL_RECOMMEND_IMPORTANT_BIDS=1460,1005,936,48,1347,1576,917,1021,532,10,1154,1257,1304,1475,1389,490,857,324
+CHANNEL_RECOMMEND_BIDS=1460,1005,936,48,1347,917,1021,532,10,1257,1304,1475,1389,490,857,324
+
+HIDDEN_READER_MENUS=69434,123
+no_participate_activity=168
+
+FILL_SEND_ORDER_ID=67
+
+NOT_ALLOW_ACCESS_SITE=1
+
+
+HIDE_BOOKS=1850,1347
+
+SHOW_NEW_BOOK_PAGE=
+
+SHOW_CHAPTER_PRICE_CHANNEL=all
+
+OPEN_WELFARE_CHANNEL=12,14
+
+IS_OPEN_DB_RECORD=false
+
+IS_OPEN_PAGE_RECORD=true
+
+SHOW_CUSTOM_ACTIVITY_CHANNEL=2
+
+HIDDEN_READ_BOOTSTRAP=22
+
+OSS_INTERNAL_DOMAIN=http://yueduyun.oss-cn-hangzhou-internal.aliyuncs.com
+
+NATIONAL_DAY_ACTIVITY_IDS=2322,2323,2324
+
+NOT_ALLOW_PAID_USER_CHANNEL_ID=2,1
+
+FOREVER_ACTIVITY_ID=930
+
+FOREVER_ACTIVITY_BID=1,2,3
+
+HELP_PAY_PAGE_CHANNEL_ID=2
+
+JUMP_AGENT_URL=t.leyuee.com
+
+SHOPPING_DAY_ACTIVITY_IDS=2990,2991,2992
+
+CAN_ACCESS_SITE=2,1
+
+FISSION_SEND_ORDER_ID=1
+
+RECORD_FIRST_VISIT_PAY_PAGE_TIME=1542088800
+
+NEW_SUB_PAGE=2
+
+DATA_ANALYSIS_SELECT_USERS_SUBSCRIBE_TIME=1542088800
+
+SPECIAL_USER_LOG=7530391
+
+JS_AppId=wx1037462ad78bf1f2
+JS_AppSecret=d6bda51bfdb4cf6eb504c622fd72c210
+AGENT_DOMAIN=agent.pre.aizhuishu.com
+
+SHARE_DOMAIN=pre.aizhuishu.com
+
+WECHAT_OP_APPID=wxceb2aacdce248393
+
+SHORT_PUSH_SHORT_START_TIME=1545705063
+
+TEMPLATE_COMPARE_START_TIME=147804996
+
+NEW_AUTH_CHANNEL_ID=2
+
+OTHER_ACTIVITY_ID=2631
+OTHER_ACTIVITY_CHANNEL=14
+
+ADS_OPEN_SITE=2
+
+OUTSTANDING_YEAR_ORDER_SITE=3,4,5,6
+
+SHOW_CUSTOM_NEW_USER_ACTIVITY_CHANNEL=21
+
+share_domain_url=http://sitejump.pre.aizhuishu.com/share/show
+
+share_domain=sitejump.pre.aizhuishu.com
+SHARE_OPEN_SITE=2
+
+TEST_CPC_UID=167655,109861757,7530367,109861732,7530398
+CREATE_PAY_URL=https://prepay.aizhuishu.com/goToPay

+ 68 - 0
app/Http/Controllers/QuickApp/BaseController.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp;
+use Illuminate\Routing\Controller;
+use App\Modules\User\Models\Xcxuser;
+use JWTAuth;
+class BaseController extends Controller
+{
+
+    /**
+     * 公众号接口签名密钥
+     * @var string
+     */
+    protected $secret_key = 'Uv%vkPI5K8Opqoww';
+
+
+    protected function checkUid(){
+        if(!$this->getAuthenticatedUser()) return false;
+        return true;
+    }
+
+    public function __get($name)
+    {
+        static $user =null;
+        if(is_null($user)) $user =  $this->getAuthenticatedUser();
+
+        if($name == '_user_info'){
+            return $user;
+        }
+        if($name == 'uid'){
+            return $user->id;
+        }
+        if($name == 'distribution_channel_id'){
+            return $user->distribution_channel_id;
+        }
+        if($name == 'openid'){
+            $QAPP_user = Xcxuser::where('uid',$user->id)->select('openid')->first();
+            return $QAPP_user->openid;
+        }
+        return null;
+    }
+
+    public function getAuthenticatedUser(){
+        try {
+            if (! $user = JWTAuth::parseToken()->authenticate()) {
+                return false;
+            }
+        } catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
+            return false;
+            //return response()->json(['token_expired'], $e->getStatusCode());
+        } catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
+            return false;
+            //return response()->json(['token_invalid'], $e->getStatusCode());
+        } catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
+            return false;
+            //return response()->json(['token_absent'], $e->getStatusCode());
+        }
+
+        return $user;
+    }
+
+    public function checkSign($param){
+        if(!isset($param['sign'])) return false;
+        if(empty($param['sign'])) return false;
+
+        return $param['sigin'] == _sign($param,$this->secret_key);
+    }
+}

+ 70 - 0
app/Http/Controllers/QuickApp/Book/BookCategoryController.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Book;
+
+use Illuminate\Http\Request;
+use App\Http\Controllers\Controller;
+use App\Modules\Book\Services\BookCategoryService;
+class BookCategoryController extends Controller
+{
+    /**
+     * @apiDefine Book 图书
+     */
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 获取分类
+     * @api {get} books/getCategory  获取分类
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Book
+     * @apiName getCategory
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:  [
+     *                   {
+     *                   id: 1,
+     *                   name: "男频",
+     *                   children: [
+     *                           {
+     *                               id: 7,
+     *                               name: "灵异鬼怪"
+     *                           },
+     *                           {
+     *                               id: 8,
+     *                               name: "历史穿越"
+     *                           },
+     *                           {
+     *                               id: 30,
+     *                               name: "青春爱情"
+     *                           }
+     *                       ]
+     *                   },
+     *                   {
+     *                   id: 2,
+     *                   name: "女频",
+     *                   children: [
+     *                           {
+     *                               id: 26,
+     *                               name: "豪门总裁"
+     *                           },
+     *                           {
+     *                               id: 35,
+     *                               name: "民国爱情"
+     *                           }
+     *                       ]
+     *                   }
+     *               ]
+     *       }
+     */
+    public function getCategory(Request $request){
+        $res = BookCategoryService::getCategory(true);
+        return response()->success($res);
+    }
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 658 - 0
app/Http/Controllers/QuickApp/Book/BookController.php


+ 668 - 0
app/Http/Controllers/QuickApp/Book/ChapterController.php

@@ -0,0 +1,668 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Book;
+
+use Illuminate\Http\Request;
+use App\Http\Controllers\QuickApp\BaseController;
+use Redis;
+use App\Modules\Book\Services\ChapterService;
+use App\Modules\User\Services\ReadRecordService;
+use App\Http\Controllers\QuickApp\Book\Transformers\ChapterTransformer;
+use App\Modules\Book\Services\BookConfigService;
+use App\Http\Controllers\QuickApp\Book\Transformers\ChapterListTransformer;
+use App\Modules\Subscribe\Services\BookOrderService;
+use App\Modules\Subscribe\Services\ChapterOrderService;
+use App\Modules\Subscribe\Services\YearOrderService;
+use App\Modules\OfficialAccount\Services\ForceSubscribeService;
+use App\Modules\Subscribe\Services\ChapterReminderService;
+use App\Modules\OfficialAccount\Services\OfficialAccountService;
+use Hashids;
+use GuzzleHttp\Client;
+use Log;
+use DB;
+use App\Modules\User\Services\UserSignService;
+use App\Modules\User\Services\UserDeepReadTagService;
+class ChapterController extends BaseController
+{
+
+    /**
+     * @apiDefine Chapter 章节
+     */
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 章节列表不分页
+     * @api {get} books/{bid}/allcatalog 章节列表不分页
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Chapter
+     * @apiName getCatalog
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Array}       data.list 分页结果集
+     * @apiSuccess {Int}         data.list.bid   bid
+     * @apiSuccess {Int}         data.list.chapter_id   章节id
+     * @apiSuccess {String}      data.list.chapter_name   章节名称
+     * @apiSuccess {Int}         data.list.chapter_sequence   序号
+     * @apiSuccess {Int}         data.list.chapter_is_vip   是否vip
+     * @apiSuccess {Int}         data.list.chapter_size   章节大小
+     * @apiSuccess {Int}         data.list.prev_cid   上一章节id
+     * @apiSuccess {Int}         data.list.next_cid   下一章节
+     * @apiSuccess {String}      data.list.recent_update_at   更新时间
+     * @apiSuccess {String}      data.list.is_need_subscirbe   是否强制关注
+     * @apiSuccess {object}      data.meta   分页信息
+     * @apiSuccess {Int}         data.meta.total  总条数
+     * @apiSuccess {Int}         data.meta.per_page  每页条数
+     * @apiSuccess {Int}         data.meta.current_page 当前页
+     * @apiSuccess {Int}         data.meta.last_page  最后页
+     * @apiSuccess {String}      data.meta.next_page_url  下一页
+     * @apiSuccess {String}      data.meta.prev_page_url  上一页
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:
+     *            [
+     *             {
+     *               bid: 5,
+     *               chapter_id: 5,
+     *               chapter_name: "第1240章 不是我",
+     *               chapter_sequence: 1239,
+     *               chapter_is_vip: 1,
+     *               chapter_size: 2422,
+     *               prev_cid: 0,
+     *               next_cid: 0,
+     *               recent_update_at: 2017-11-20 15:01:56,
+     *               is_need_subscirbe: 1,
+     *            },
+     *             {
+     *               bid: 5,
+     *               chapter_id: 5,
+     *               chapter_name: "第1240章 不是我",
+     *               chapter_sequence: 1239,
+     *               chapter_is_vip: 1,
+     *               chapter_size: 2422,
+     *               prev_cid: 0,
+     *               next_cid: 0,
+     *               recent_update_at: 2017-11-20 15:01:56,
+     *               is_need_subscirbe: 1,
+     *            },
+     *          ]
+     *       }
+     */
+    public function getCatalog(Request $request,$bid){
+        $bid = Hashids::decode($bid)[0];
+        $lists = ChapterService::getChapterLists($bid);
+        return response()->collection(new ChapterListTransformer,$lists);
+    }
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 章节列表分页
+     * @api {get} books/{bid}/catalog 章节列表分页
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Chapter
+     * @apiName getCatalogPerPage
+     * @apiParam   {Int}         page_size  分页大小(默认15)
+     * @apiParam   {Int}         page  页码(默认1)
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Array}       data.list 分页结果集
+     * @apiSuccess {Int}         data.list.bid   bid
+     * @apiSuccess {Int}         data.list.chapter_id   章节id
+     * @apiSuccess {String}      data.list.chapter_name   章节名称
+     * @apiSuccess {Int}         data.list.chapter_sequence   序号
+     * @apiSuccess {Int}         data.list.chapter_is_vip   是否vip
+     * @apiSuccess {Int}         data.list.chapter_size   章节大小
+     * @apiSuccess {Int}         data.list.prev_cid   上一章节id
+     * @apiSuccess {Int}         data.list.next_cid   下一章节
+     * @apiSuccess {String}      data.list.recent_update_at   更新时间
+     * @apiSuccess {String}      data.list.is_need_subscirbe   是否强制关注
+     * @apiSuccess {object}      data.meta   分页信息
+     * @apiSuccess {Int}         data.meta.total  总条数
+     * @apiSuccess {Int}         data.meta.per_page  每页条数
+     * @apiSuccess {Int}         data.meta.current_page 当前页
+     * @apiSuccess {Int}         data.meta.last_page  最后页
+     * @apiSuccess {String}      data.meta.next_page_url  下一页
+     * @apiSuccess {String}      data.meta.prev_page_url  上一页
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:
+     *            list:[
+     *             {
+     *               bid: 5,
+     *               chapter_id: 5,
+     *               chapter_name: "第1240章 不是我",
+     *               chapter_sequence: 1239,
+     *               chapter_is_vip: 1,
+     *               chapter_size: 2422,
+     *               prev_cid: 0,
+     *               next_cid: 0,
+     *               recent_update_at: 2017-11-20 15:01:56,
+     *               is_need_subscirbe: 1,
+     *            },
+     *             {
+     *               bid: 5,
+     *               chapter_id: 5,
+     *               chapter_name: "第1240章 不是我",
+     *               chapter_sequence: 1239,
+     *               chapter_is_vip: 1,
+     *               chapter_size: 2422,
+     *               prev_cid: 0,
+     *               next_cid: 0,
+     *               recent_update_at: 2017-11-20 15:01:56,
+     *               is_need_subscirbe: 1,
+     *            },
+     *          ]
+     *          meta:{
+     *              total: 1253,
+     *              per_page: 15,
+     *              current_page: 1,
+     *              last_page: 84,
+     *              next_page_url: "http://myapi.cn/api/books/1/chapter?page=2",
+     *              prev_page_url: ""
+     *         }
+     *       }
+     */
+    public function getCatalogPerPage(Request $request,$bid){
+        $bid = Hashids::decode($bid)[0];
+        $page_size = $request->input('page_size',15);
+        $res = ChapterService::getChapterListsPage($bid,$page_size);
+        return response()->pagination(new ChapterListTransformer,$res);
+    }
+
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 章节内容
+     * @api {get} books/{bid}/chapters/{chapter_id} 章节内容
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Chapter
+     * @apiName index
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Int}         data.chapter_id   章节id
+     * @apiSuccess {String}      data.chapter_name   章节名称
+     * @apiSuccess {Int}         data.chapter_sequence   序号
+     * @apiSuccess {Int}         data.chapter_is_vip   是否vip
+     * @apiSuccess {Int}         data.chapter_size   章节大小
+     * @apiSuccess {Int}         data.prev_cid   上一章节id
+     * @apiSuccess {Int}         data.next_cid   下一章节
+     * @apiSuccess {String}      data.recent_update_at   更新时间
+     * @apiSuccess {String}      data.chapter_content  章节内容
+     * @apiSuccess {Int}         data.is_need_subscirbe  是否强制关注(删除)
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {
+     *               chapter_id: 5,
+     *               chapter_name: "第1240章 不是我",
+     *               chapter_sequence: 1239,
+     *               chapter_is_vip: 1,
+     *               chapter_size: 2422,
+     *               prev_cid: 0,
+     *               next_cid: 0,
+     *               recent_update_at: 2017-11-20 15:01:56,
+     *               chapter_content: "叶妩被司行霈的阴阳怪气一吓,思路偏得太远了。 她张口结舌,忘记了自己要说什么。",
+     *            }
+     *       }
+     */
+    public function index(Request $request,$bid,$cid){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $oldbid = $bid;
+        $bid = Hashids::decode($bid)[0];
+        //获取图书信息
+        $book_info  = BookConfigService::getBookById($bid);
+
+
+        if(empty($book_info)) return response()->error('QAPP_SYS_ERROR');
+
+        if($book_info->is_on_shelf == 0 || $book_info->is_on_shelf == 3){
+            //return response()->error('QAPP_OFF_SHELF');
+        }
+        //获取章节信息
+        $chapter = ChapterService::getChapterNameById($cid,$bid);
+        if(!$chapter){
+            return response()->error('QAPP_SYS_ERROR');
+        }
+
+        if($chapter->is_vip == 0){
+            ReadRecordService::addReadRecord(['uid'=>$this->uid,'bid'=>$bid,'book_name'=>$book_info->book_name,
+                'cid'=>$cid,'chapter_name'=>$chapter->name ]);
+            //用户标签
+            if($chapter->sequence >= 20){
+                $this->addTag($book_info);
+            }
+            return response()->item(new ChapterTransformer,$this->getChapter($bid,$cid,$chapter));
+        }
+
+        //已经付费
+        if($this->getOrderRecord($bid,$cid)){
+            ReadRecordService::addReadRecord(['uid'=>$this->uid,'bid'=>$bid,'book_name'=>$book_info->book_name,
+                'cid'=>$cid,'chapter_name'=>$chapter->name ]);
+            //用户标签
+            if($chapter->sequence >= 20){
+                $this->addTag($book_info);
+            }
+            return response()->item(new ChapterTransformer,$this->getChapter($bid,$cid,$chapter));
+        }
+
+        //未付费 要提醒
+        $user_info = $this->_user_info;
+        //未付费 余额不足
+        $fee = $this->getPrice($book_info,$chapter->size);
+        $data = [
+            'book_id'=>$oldbid,
+            'book_name'=>$book_info->book_name,
+            'chapter_name'=>$chapter->name,
+            'chapter_id'=>$cid,
+            'pay_type'=>$book_info->charge_type,
+            'fee'=>$fee,
+            'user_balance'=>$user_info->balance,
+            'product_id'=>$book_info->product_id,
+            'uid'=>$this->uid,
+            'distribution_channel_id'=>$this->distribution_channel_id,
+            'is_discount'=>0,
+            'discount_fee' => '',
+            'discount'=>''
+
+        ];
+
+        if($user_info['balance'] < $fee){
+            //需要提箱
+            if($this->isOrderRemind($bid)){
+
+                if($book_info->charge_type == 'BOOK'){
+                    return response()->error('QAPP_BOOK_BALANCE_PAY',$data);
+                }else
+                    if($book_info->charge_type == 'CHAPTER'){
+                        return response()->error('QAPP_CHAPTER_INSUFFICIENT_BALANCE',$data);
+                    }else{
+                        return response()->error('QAPP_SYS_ERROR');
+                    }
+            }else{
+                //不需要提醒
+                if($book_info->charge_type == 'BOOK'){
+                    return response()->error('QAPP_BOOK_SECOND_BALANCE_PAY',$data);
+                }elseif($book_info->charge_type == 'CHAPTER'){
+                    return response()->error('QAPP_CHAPTER_SECOND_BALANCE_PAY',$data);
+                }else{
+                    return response()->error('QAPP_SYS_ERROR');
+                }
+            }
+        }
+
+        if($this->isOrderRemind($bid)){
+            //return response()->error('INSUFFICIENT_BALANCE',$data);
+            if($book_info->charge_type  == 'BOOK'){
+                return response()->error('QAPP_BOOK_BUY',$data);
+            }else
+                if($book_info->charge_type == 'CHAPTER'){
+                    return response()->error('QAPP_CHAPTER_BUY',$data);
+                }else{
+                    return response()->error('QAPP_SYS_ERROR');
+                }
+        }
+        //付费 不提醒
+        if($this->balancePay($book_info,$cid,$chapter->size,$chapter->name,0)){
+
+            ReadRecordService::addReadRecord(['uid'=>$this->uid,'bid'=>$bid,'book_name'=>$book_info->book_name,
+                'cid'=>$cid,'chapter_name'=>$chapter->name ]);
+            //用户标签
+            if($chapter->sequence >= 20){
+                $this->addTag($book_info);
+            }
+            return response()->item(new ChapterTransformer,$this->getChapter($bid,$cid,$chapter));
+        }else{
+
+            //不需要提醒
+            if($book_info->charge_type == 'BOOK'){
+                return response()->error('QAPP_BOOK_SECOND_BALANCE_PAY',$data);
+            }elseif($book_info->charge_type == 'CHAPTER'){
+                return response()->error('QAPP_CHAPTER_SECOND_BALANCE_PAY',$data);
+            }else{
+                return response()->error('QAPP_SYS_ERROR');
+            }
+        }
+    }
+
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 余额支付
+     * @api {get} books/{bid}/balance/chapterOrders/{cid} 余额支付
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Chapter
+     * @apiName pay
+     * @apiParam   (Int)         remind 提醒
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Int}         data.chapter_id   章节id
+     * @apiSuccess {String}      data.chapter_name   章节名称
+     * @apiSuccess {Int}         data.chapter_sequence   序号
+     * @apiSuccess {Int}         data.chapter_is_vip   是否vip
+     * @apiSuccess {Int}         data.chapter_size   章节大小
+     * @apiSuccess {Int}         data.prev_cid   上一章节id
+     * @apiSuccess {Int}         data.next_cid   下一章节
+     * @apiSuccess {String}      data.recent_update_at   更新时间
+     * @apiSuccess {String}      data.chapter_content  章节内容
+     * @apiSuccess {Int}         data.is_need_subscirbe  是否强制关注(删除)
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {
+     *               chapter_id: 5,
+     *               chapter_name: "第1240章 不是我",
+     *               chapter_sequence: 1239,
+     *               chapter_is_vip: 1,
+     *               chapter_size: 2422,
+     *               prev_cid: 0,
+     *               next_cid: 0,
+     *               recent_update_at: 2017-11-20 15:01:56,
+     *               chapter_content: "叶妩被司行霈的阴阳怪气一吓,思路偏得太远了。 她张口结舌,忘记了自己要说什么。",
+     *            }
+     *       }
+     */
+    public function pay(Request $request,$bid,$cid){
+
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $remind = (int)$request->input('remind');
+        $oldbid = $bid;
+        $bid = Hashids::decode($bid)[0];
+        $book_info = BookConfigService::getBookById($bid);;
+        if(empty($book_info)) response()->error('QAPP_SYS_ERROR');
+
+        if($book_info->is_on_shelf == 0 || $book_info->is_on_shelf == 3){
+            if(!$this->isBookOrdered($bid)){
+                response()->error('QAPP_OFF_SHELF');
+            } 
+        }
+        //获取章节
+        $chapter = ChapterService::getChapterNameById($cid,$bid);
+        if(!$chapter){
+            return response()->error('QAPP_SYS_ERROR');
+        }
+
+
+        if($this->balancePay($book_info,$cid,$chapter->size,$chapter->name,$remind)){
+            ReadRecordService::addReadRecord(['uid'=>$this->uid,'bid'=>$bid,'book_name'=>$book_info->book_name,
+                'cid'=>$cid,'chapter_name'=>$chapter->name ]);
+            //用户标签
+            if($chapter->sequence >= 20){
+                $this->addTag($book_info);
+            }
+            return response()->item(new ChapterTransformer,$this->getChapter($bid,$cid,$chapter));
+        }else{
+            $fee = $this->getPrice($book_info,$chapter->size);
+
+            $data = [
+                'book_id'=>$oldbid,
+                'book_name'=>$book_info->book_name,
+                'chapter_name'=>$chapter->name,
+                'chapter_id'=>$cid,
+                'pay_type'=>$book_info->charge_type,
+                'fee'=>$fee,
+                'user_balance'=>$this->_user_info['balance'],
+                'product_id'=>$book_info->product_id,
+                'uid'=>$this->uid,
+                'distribution_channel_id'=>$this->distribution_channel_id,
+                'is_discount'=>0,
+                'discount_fee' => '',
+                'discount'=>''
+            ];
+            //不需要提醒
+            if($book_info->charge_type == 'BOOK'){
+                return response()->error('QAPP_BOOK_SECOND_BALANCE_PAY',$data);
+            }elseif($book_info->charge_type == 'CHAPTER'){
+                return response()->error('QAPP_CHAPTER_SECOND_BALANCE_PAY',$data);
+            }else{
+                return response()->error('QAPP_SYS_ERROR');
+            }
+        }
+    }
+
+
+
+    /**
+     * 余额支付
+     * @param $book_info
+     * @param $chapter_id
+     * @param $chapter_size
+     * @return bool
+     */
+    protected function balancePay($book_info,$chapter_id,$chapter_size,$chapter_name,$is_remind){
+        $fee = $this->getPrice($book_info,$chapter_size);
+        if((int)$this->_user_info['balance'] >= $fee){
+            if($this->bookOrderOrChapterOrder($book_info,$chapter_id,$fee,$chapter_name,$is_remind)){
+                return true;
+            }
+            return false;
+        }else{
+            return false;
+        }
+
+    }
+
+
+    /**
+     * 获取章节内容
+     * @param $bid
+     * @param $cid
+     * @return bool|mixed
+     */
+    protected function getChapter($bid,$cid,$chapter){
+        $chapter_content = ChapterService::getChapter($bid,$cid);
+        if(!$chapter_content) return false;
+        //$chapter->content = $chapter_content->content;
+        $chapter->content = trim(str_replace($chapter_content->name, '', $chapter_content->content));
+        //统计点击率
+        $key = 'book_click_num_bid_'.$bid;
+        $field = date('Y-m-d');
+        $old = Redis::hget($key,$field);
+        if(!$old)  $old = 0;
+        Redis::hset($key,$field,$old+1);
+        //签到奖励
+        /*
+        $sign_key = 'sign-'.date('Y-m-d');
+        $sign_status = 0;
+        if(!Redis::hexists($sign_key,$this->uid)){
+            Redis::hset($sign_key,$this->uid,time());
+            $sign_status = 1;
+        }*/
+        $sign_status = $this->userSign($this->uid);
+        $chapter->sign_status = $sign_status;
+        return $chapter;
+    }
+
+    /**
+     * 签到
+     * @param $uid
+     * @return int
+     */
+    protected function userSign($uid){
+
+        if($this->distribution_channel_id != 5){
+            return 0;
+        }
+
+        $day = date('Y-m-d');
+        if(UserSignService::isSign($uid,$day)){
+            return 0;
+        }
+        if(UserSignService::sign($uid,$day)){
+            return 1;
+        }
+        return 0;
+
+    }
+    /**
+     * 添加订购记录
+     * @param $book_info
+     * @param $chapter_id
+     * @param $fee
+     * @return bool
+     */
+    protected function bookOrderOrChapterOrder($book_info,$chapter_id,$fee,$chapter_name,$is_remind){
+        $send_order_id = 0;
+        if($book_info['charge_type'] == 'BOOK'){
+            $data = [
+                'uid'=>$this->uid,
+                'fee'=>$fee,
+                'u'=>$send_order_id,
+                'distribution_channel_id'=>$this->distribution_channel_id,
+                'bid'=>$book_info->bid,
+                'book_name'=>$book_info->book_name,
+                'send_order_id'=>$send_order_id,
+            ];
+            return BookOrderService::addOrderRecodeAndDecrUserBalance($data,$this->uid);
+        }
+        else{
+            $data = [
+                'uid'=>$this->uid,
+                'fee'=>$fee,
+                'cid'=>$chapter_id,
+                'bid'=>$book_info->bid,
+                'distribution_channel_id'=>$this->distribution_channel_id,
+                'book_name'=>$book_info->book_name,
+                'chapter_name'=>$chapter_name,
+                'send_order_id'=>$send_order_id,
+                'is_remind'=>$is_remind
+            ];
+            //print_r($data);
+            if($is_remind){
+                $this->addOrderRemind($book_info->bid);
+            }
+            return ChapterOrderService::addOrderAndDecrUserBalance($data,$this->uid);
+        }
+    }
+
+
+    protected  function  addOrderRemind($bid){
+        if(ChapterReminderService::checkIsNoReminder($this->uid,$bid)){
+            return true;
+        }else{
+            ChapterReminderService::add($this->uid,$bid);
+            return true;
+        }
+    }
+    /**
+     * 是否订购提醒
+     * @param $chapter_id
+     * @return bool
+     */
+    protected function isOrderRemind($bid){
+        $is_no_reminder = ChapterReminderService::checkIsNoReminder($this->uid,$bid) ? 1 : 0;
+        return $is_no_reminder == 0;
+    }
+
+    /**
+     * 用户是否关注
+     * @param $uid
+     * @return bool
+     */
+    protected  function getSubscribe(){
+        $res = ForceSubscribeService::forceSubscribeUsersByUid(['uid'=>$this->uid]);
+        if($res) return true;
+        return false;
+    }
+
+
+    /**
+     * 获取订购记录
+     * @param $book_info
+     * @param $chapter_id
+     * @return bool
+     */
+    protected function getOrderRecord($bid,$chapter_id){
+        //包年记录
+        $uid = $this->uid;
+        $res = YearOrderService::getRecord($uid);
+        if($res) return true;
+        $res = null;
+
+        //单本订购记录
+        $res = BookOrderService::getRecordByuidBid($uid,$bid);
+        if($res) return true;
+        $res = null;
+
+        //章节订购记录
+        $chapterOrder = new ChapterOrderService();
+
+        if($chapterOrder->checkIsOrdered($uid,$bid,$chapter_id))  return true;
+
+        return false;
+    }
+
+
+    /**
+     * 计算价格
+     * @param $book_info
+     * @param $chapter_size
+     * @return float
+     */
+    protected function getPrice($book_info,$chapter_size){
+        if($book_info->charge_type == 'BOOK')
+            return $book_info->price*100;
+        return   ceil($chapter_size/100);
+    }
+
+
+
+
+    /**
+     * 用户添加标签
+     * @param $book_info
+     */
+    protected function addTag($book_info){
+        $send_order_id = 0;
+        if(!UserDeepReadTagService::isAddTag($this->uid,$book_info->bid)){
+            try{
+                UserDeepReadTagService::addTag([
+                    'uid'=>$this->uid,
+                    'bid'=>$book_info->bid,
+                    'book_name'=>$book_info->book_name,
+                    'category_id'=>$book_info->category_id,
+                    'category_name'=>$book_info->category_name,
+                    'sex_preference'=>$book_info->channel_name?$book_info->channel_name:'',
+                    'distribution_channel_id'=>$this->distribution_channel_id?$this->distribution_channel_id:'0',
+                    'send_order_id'=>$send_order_id,
+                ]);
+            }catch (\Exception  $e){
+
+            }
+
+        }    
+    }
+
+
+    protected function isBookOrdered($bid){
+
+        $chapter_order = ChapterOrderService::checkBookIsOrdered($this->uid,$bid);
+        if($chapter_order) return true;
+
+        $res = BookOrderService::getRecordByuidBid($this->uid,$bid);
+        if($res) return true;
+        return false;
+    }
+}

+ 45 - 0
app/Http/Controllers/QuickApp/Book/Transformers/BookTransformer.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * Date: 2017/3/31
+ * Time: 14:02
+ */
+
+namespace App\Http\Controllers\QuickApp\Book\Transformers;
+use Hashids;
+class BookTransformer
+{
+    public function transform($book){
+        return [
+            'book_id'=>Hashids::encode($book->bid),
+            //'book_id_no_hash'=>$book->bid,
+            'book_name'=>$book->book_name,
+            'book_summary'=>$book->intro,
+            'book_author'=>$book->author,
+            'cover_url'=>$book->cover,
+            'book_word_count'=>$book->size,
+            'book_chapter_total'=>$book->chapter_count,
+            'book_category_id'=>$book->category_id,
+            'book_category'=>$book->category_name,
+            'book_end_status'=>$book->status,
+            'book_published_time'=>is_null($book->updated_at)?'':$book->updated_at,
+            'copyright'=>is_null($book->copyright)? '':$book->copyright,
+            'charge_type'=>is_null($book->charge_type)?'':$book->charge_type,
+            'force_subscribe_chapter_id'=>$book->force_subscribe_chapter_seq,
+            'update_time'=>$book->updated_at,
+            'is_on_shelf'=>$book->is_on_shelf,
+            'book_price'=>is_null($book->price)?0:$book->price,
+            'keyword'=>$book->keyword,
+            'recommend_index'=> $book->recommend_index,
+            'is_show_index_content'=>$book->is_show_index_content,
+            'click_count'=>$book->click_count,
+            'product_id'=>$book->product_id,
+            'sex_preference'=>$book->channel_name,
+            'last_cid'=>$book->last_cid,
+            'last_chapter'=>$book->last_chapter,
+            'first_cid'=>$book->first_cid,
+            'is_on_user_shelf'=>$book->is_on_user_shelf,
+            'last_chapter_is_vip'=>$book->last_chapter_is_vip
+        ];
+    }
+}

+ 22 - 0
app/Http/Controllers/QuickApp/Book/Transformers/ChapterListTransformer.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Book\Transformers;
+use Hashids;
+class ChapterListTransformer
+{
+
+    public function transform($chapter){
+        return [
+            'bid'       =>  Hashids::encode($chapter->bid),
+            //'bid_no_hash'       =>  ($chapter->bid),
+            'chapter_id'       =>  $chapter->id,
+            'chapter_name'   =>  $chapter->name,
+            'chapter_sequence'   =>  $chapter->sequence,
+            'chapter_is_vip'   =>  $chapter->is_vip,
+            'chapter_size'   =>  $chapter->size,
+            'prev_cid'   =>  $chapter->prev_cid,
+            'next_cid'   =>  $chapter->next_cid,
+            'recent_update_at'   =>  $chapter->recent_update_at,
+        ];
+    }
+}

+ 24 - 0
app/Http/Controllers/QuickApp/Book/Transformers/ChapterTransformer.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Book\Transformers;
+use Hashids;
+class ChapterTransformer
+{
+    public function transform($chapter){
+        return [
+            'bid'       =>  Hashids::encode($chapter->bid),
+            //'bid_no_hash'       =>  $chapter->bid,
+            'chapter_id'       =>  $chapter->id,
+            'chapter_name'   =>  $chapter->name,
+            'chapter_sequence'   =>  $chapter->sequence,
+            'chapter_is_vip'   =>  $chapter->is_vip,
+            'chapter_size'   =>  $chapter->size,
+            'prev_cid'   =>  $chapter->prev_cid,
+            'next_cid'   =>  $chapter->next_cid,
+            'recent_update_at'   =>  $chapter->recent_update_at,
+            'chapter_content'   =>  $chapter->content,
+            'sign_status'   =>  $chapter->sign_status,
+            //'is_need_subscirbe'   =>  $chapter->is_need_subscirbe,
+        ];
+    }
+}

+ 124 - 0
app/Http/Controllers/QuickApp/Oauth/UsersController.php

@@ -0,0 +1,124 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Oauth;
+
+use App\Consts\SysConsts;
+use Illuminate\Http\Request;
+use App\Http\Controllers\Controller;
+use App\Modules\User\Services\QappUserService;
+use JWTAuth;
+
+class UsersController extends Controller
+{
+    /**
+     * @apiDefine Login 登录
+     */
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 登录
+     * @api {post} login 登录
+     * @apiParam  {String}  js_code js_code
+     * @apiParam  {Int}     distribution_channel_id distribution_channel_id
+     * @apiParam  {String}  sign    签名
+     * @apiGroup Login
+     * @apiName index
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {String}      data.uid 用户uid
+     * @apiSuccess {String}      data.token token
+     * @apiSuccess {Int}         data.time 过期时间
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:  {
+     *            token:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
+     *            time:123455,
+     *            uid:1
+     *           }
+     *       }
+     */
+    public function index(Request $request)
+    {
+        $send_order_id = $request->input('send_order_id', 0);
+        $device_no = $request->input('device_no', '');
+        $device_info = $request->input('device_info', '');
+        if ($device_no) {
+            $data = QappUserService::loginStatic(compact('send_order_id', 'device_no', 'device_info'));
+            return response()->success($data);
+        } else {
+            return response()->error('PARAM_ERROR');
+        }
+    }
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 刷新token
+     * @api {get} RefreshToken 刷新token
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Login
+     * @apiName RefreshToken
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {String}      data.token token
+     * @apiSuccess {Int}         data.time 过期时间
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:  {
+     *            token:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
+     *            time:123455
+     *           }
+     *       }
+     */
+    public function RefreshToken()
+    {
+        try {
+            $old_token = JWTAuth::getToken();
+            $token = JWTAuth::refresh($old_token);
+            $time = time() + SysConsts::ONE_HOUR_SECONDS * 2;
+            return response()->success(compact('token', 'time'));
+        } catch (Exception $e) { }
+        return response()->error('XCX_NOT_LOGIN');
+    }
+
+    /**
+     * 检验数据的真实性,并且获取解密后的明文.
+     * @param $encryptedData string 加密的用户数据
+     * @param $iv string 与用户数据一同返回的初始向量
+     * @param $data string 解密后的原文
+     *
+     * @return int 成功0,失败返回对应的错误码
+     */
+    private function decryptData($encryptedData, $iv, &$data)
+    {
+        if (strlen($iv) != 24) {
+            return ErrorCode::$IllegalIv;
+        }
+        $aesIV = base64_decode($iv);
+        $aesCipher = base64_decode($encryptedData);
+        $aesKey = "";
+        $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
+        $dataObj = json_decode($result);
+        if ($dataObj  == NULL) {
+            return ErrorCode::$IllegalBuffer;
+        }
+        $data = $result;
+        return ErrorCode::$OK;
+    }
+}
+class ErrorCode
+{
+    public static $OK = 0;
+    public static $IllegalAesKey = -41001;
+    public static $IllegalIv = -41002;
+    public static $IllegalBuffer = -41003;
+    public static $DecodeBase64Error = -41004;
+}

+ 719 - 0
app/Http/Controllers/QuickApp/Order/OrdersController.php

@@ -0,0 +1,719 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Order;
+
+use App\Http\Controllers\QuickApp\BaseController;
+use Illuminate\Http\Request;
+use App\Modules\Subscribe\Services\BookOrderService;
+use App\Modules\Subscribe\Services\ChapterOrderService;
+use App\Modules\Xcx\Services\XcxOrderService as OrderService;
+use App\Http\Controllers\QuickApp\Order\Transformers\BookOrderTransformer;
+use App\Http\Controllers\QuickApp\Order\Transformers\ChapterOrderTransformer;
+use App\Http\Controllers\QuickApp\Order\Transformers\ChargeListTransformer;
+use App\Modules\Subscribe\Services\YearOrderService;
+use App\Modules\User\Services\UserService;
+use DB;
+use Redis;
+use Hashids;
+use EasyWeChat\Foundation\Application;
+use EasyWeChat\Payment\Order as Wxorder;
+use App\Modules\Product\Services\ProductService;
+use App\Modules\Book\Services\BookConfigService;
+use Log;
+
+class OrdersController extends BaseController
+{
+    /**
+     * @apiDefine Order 订单
+     */
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 充值列表
+     * @api {get} order/chargeList 充值列表
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Order
+     * @apiName chargeList
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: [
+     *               {
+     *                   product_id: 1,
+     *                   price: "30.00元",
+     *                   vip: 0,
+     *                   intro: [
+     *                       {
+     *                           label: 3000,
+     *                           important: false
+     *                       },
+     *                       {
+     *                           label: "书币",
+     *                           important: true
+     *                       }
+     *                   ]
+     *               },
+     *               {
+     *                   product_id: 2,
+     *                   price: "50.00元",
+     *                   vip: 1,
+     *                   intro: [
+     *                       {
+     *                           label: 5000,
+     *                           important: false
+     *                       },
+     *                       {
+     *                           label: "1000+",
+     *                           important: true
+     *                       },
+     *                       {
+     *                           label: "书币",
+     *                           important: false
+     *                       }
+     *                       ]
+     *               },
+     *               {
+     *                   product_id: 5,
+     *                   price: "365.00元",
+     *                   vip: 0,
+     *                   intro: [
+     *                       {
+     *                       label: "年费VIP会员",
+     *                       important: true
+     *                       }
+     *                   ]
+     *               }
+     *               ]
+     *       }
+     */
+    public function chargeList(Request $request)
+    {
+        if (!$this->checkUid()) {
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        
+        $device = $request->has('device')?$request->get('device'):'';
+        
+        // 苹果先不展示充值
+        if($device == 'iPhone'){
+        	return response()->success();
+        }
+        
+        $res = ProductService::getChargeProduct();
+        if (!$res->isEmpty()) {
+            $data = [];
+            foreach ($res as $v) {
+                $intro = [];
+                if ($v->given > 0 && $v->type == 'TICKET_RECHARGE') {
+                    $intro = [
+                        [
+                            'label' => ($v->price * 100) . "+",
+                            'important' => false,
+                        ],
+                        [
+                            'label' => $v->given,
+                            'important' => true,
+                        ],
+                        [
+                            'label' => '书币',
+                            'important' => false,
+                        ]
+
+                    ];
+                    $intro2 = [
+                        ['label' => '多送', 'important' => false],
+                        ['label' => (int)($v->given / 100), 'important' => true],
+                        ['label' => '元', 'important' => false],
+                    ];
+                    $v->vip = 0;
+                }
+                if ($v->given == 0 && $v->type == 'TICKET_RECHARGE') {
+                    $intro = [
+                        [
+                            'label' => $v->price * 100,
+                            'important' => false,
+                        ],
+                        [
+                            'label' => '书币',
+                            'important' => false,
+                        ]
+                    ];
+                    $v->vip = 0;
+                    $intro2 = [];
+                }
+                if ($v->given == 0 && $v->type == 'YEAR_ORDER') {
+                    $intro = [
+                        [
+                            'label' => '年费VIP会员',
+                            'important' => true,
+                        ]
+                    ];
+                    $v->vip = 1;
+                    $intro2 = [
+                        ['label' => '每天1元,全年免费看', 'important' => false],
+                    ];
+                }
+                $data[] = [
+                    'product_id' => $v->id,
+                    'price' => (int)$v->price . '元',
+                    'vip' => $v->vip,
+                    'intro' => $intro,
+                    'intro2' => $intro2,
+                    'is_default' => $v->is_default,
+                ];
+            }
+            return response()->success($data);
+        } else {
+            return response()->error('QAPP_SYS_ERROR');
+        }
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 单本消费记录
+     * @api {get} order/bookOrderList 单本消费记录
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Order
+     * @apiName bookOrderList
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Int}         uid uid
+     * @apiSuccess {Int}         bid bid
+     * @apiSuccess {Int}         book_name 书名
+     * @apiSuccess {Int}         fee 钱
+     * @apiSuccess {String}      created_at 时间
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: list:[
+     *               {
+     *                   uid: 4,
+     *                   bid: 1,
+     *                   book_name: "dfsedfertrwet",
+     *                   fee: 100,
+     *                   created_at: "2017-12-02 16:24:54"
+     *                   }
+     *               ]
+     *            meta: {
+     *                       total: 1,
+     *                       per_page: 15,
+     *                       current_page: 1,
+     *                       last_page: 1,
+     *                       next_page_url: "",
+     *                       prev_page_url: ""
+     *               }
+     *       }
+     */
+    public function bookOrderList(Request $request)
+    {
+        if (!$this->checkUid()) {
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $page_size = $request->input('page_size', 15);
+        $book_order = BookOrderService::getRecord($this->uid, $page_size);
+        //$book_order = BookOrder::where('uid', $this->uid)->select('bid', 'uid', 'book_name', 'created_at', 'fee')->paginate($page_size);
+
+        return response()->pagination(new BookOrderTransformer(), $book_order);
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 章节消费记录
+     * @api {get} order/chapterOrderList 章节消费记录
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Order
+     * @apiName chapterOrderList
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Int}         uid uid
+     * @apiSuccess {Int}         bid bid
+     * @apiSuccess {Int}         cid cid
+     * @apiSuccess {Int}         chapter_name 章节名
+     * @apiSuccess {Int}         book_name 书名
+     * @apiSuccess {Int}         fee 钱
+     * @apiSuccess {String}      created_at 时间
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: list:[
+     *               {
+     *                   uid: 4,
+     *                   bid: 1,
+     *                   cid: 1,
+     *                   chapter_name: "sdfsd",
+     *                   book_name: "dfsedfertrwet",
+     *                   fee: 100,
+     *                   created_at: "2017-12-02 16:24:54"
+     *                   }
+     *               ]
+     *            meta: {
+     *                       total: 1,
+     *                       per_page: 15,
+     *                       current_page: 1,
+     *                       last_page: 1,
+     *                       next_page_url: "",
+     *                       prev_page_url: ""
+     *               }
+     *       }
+     */
+    public function chapterOrderList(Request $request)
+    {
+        if (!$this->checkUid()) {
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $chapter_model = new ChapterOrderService();
+        $page_size = $request->input('page_size', 15);
+        $chapter_order = $chapter_model->getByUid($this->uid, $page_size);
+        return response()->pagination(new ChapterOrderTransformer(), $chapter_order);
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 充值记录
+     * @api {get} order/chargeRecordLists 充值记录
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup Order
+     * @apiName chargeRecordLists
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {String}      data.price       价格
+     * @apiSuccess {String}      data.status      状态
+     * @apiSuccess {String}      data.trade_no    订单号
+     * @apiSuccess {String}      data.created_at  时间
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *    {
+     *       code: 0,
+     *       msg: "",
+     *           data: {
+     *               list: [
+     *               {
+     *                    id: 134,
+     *                    price: "1.00",
+     *                    status: "PAID",
+     *                    trade_no: "201712021915481585670623626232",
+     *                    created_at: "2017-12-02 19:15:56"
+     *           }
+     *       ],
+     *           meta: {
+     *               total: 1,
+     *               per_page: 15,
+     *               current_page: 1,
+     *               last_page: 1,
+     *               next_page_url: "",
+     *               prev_page_url: ""
+     *           }
+     *       }
+     *    }
+     */
+    public function chargeRecordLists(Request $request)
+    {
+        if (!$this->checkUid()) {
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $page_size = $request->input('page_size', 15);
+        $res = OrderService::getOrderList($this->uid, $page_size);
+        return response()->pagination(new ChargeListTransformer(), $res);
+    }
+
+    //订单是否成功
+    public function isSuccess(Request $request)
+    {
+        $order = $request->input('order');
+        $order_info = OrderService::getByTradeNo($order);
+        if ($order_info && $order_info->status == 'PAID') {
+            return response()->success();
+        }
+        return response()->error('QAPP_SYS_ERROR');
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 支付
+     * @api {get} goToPay 支付
+     * @apiGroup pay
+     * @apiName wxindex
+     * @apiParam {Int}  product_id   product_id
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiParam {String}  bid   bid
+     * @apiParam {String}  sign   签名
+     * @apiSuccess {int}   code 状态码
+     * @apiSuccess {String}  msg 信息
+     * @apiSuccess {Object}  data 信息
+     * @apiSuccess {Object}  data.appId 唤起支付的appId
+     * @apiSuccess {Object}  data.package 唤起支付的package
+     * @apiSuccess {Object}  data.nonceStr 唤起支付的nonceStr
+     * @apiSuccess {Object}  data.timeStamp 唤起支付的timeStamp
+     * @apiSuccess {Object}  data.signType 唤起支付的signType
+     * @apiSuccess {Object}  data.paySign 唤起支付sign
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {
+     *
+     *       }
+     */
+    function wxindex(Request $request)
+    {
+        Log::info($request->all());
+        $product_id = $request->has('product_id') ? $request->get('product_id') : '';
+        $sign = $request->has('sign') ? $request->get('sign') : '';
+        $send_order_id = $request->has('send_order_id') ? $request->get('send_order_id') : 0;
+
+        if (!$this->checkUid()) {
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+
+        if (empty($product_id) || empty($sign)) {
+            return response()->error('QAPP_PARAM_ERROR');
+        }
+
+        if (!$this->checkSign($request->all('_url'))) {
+            //return response()->error('QAPP_PARAM_ERROR');
+        }
+        $bid = $request->has('bid') ? $request->get('bid') : 0;
+        $openid = $this->openid;
+        //$openid = 'o0bsX0b3-kgWIb4JBdTAk7HVtnrk';
+        //根据分校id获取支付配置id
+
+        $trade_no = date("YmdHis") . hexdec(uniqid());
+        $product_info = ProductService::getProductSingle($product_id);
+        $uid = $this->uid;
+        $distribution_channel_id = $this->distribution_channel_id;
+        $price = $product_info->price * 100;
+        if ($uid < 32) {
+            //$price = 1;
+        }
+
+        if (in_array($uid, explode(',', env('TEST_UID')))) {
+            $price = 1;
+        }
+        if ($bid) {
+            try {
+                $bid = Hashids::decode($bid)[0];
+            } catch (\Exception $e) {
+                $bid = 0;
+            }
+
+        }
+        //$price = 1;
+        if ($product_info->type == 'YEAR_ORDER') {
+            $order_type = 'YEAR';
+        } elseif ($product_info->type == 'BOOK_ORDER') {
+            $order_type = 'BOOK';
+        } elseif ($product_info->type == 'TICKET_RECHARGE') {
+            $order_type = 'RECHARGE';
+        } else {
+            $order_type = '';
+        }
+
+        $res = $this->createUnPayOrder([
+            'distribution_channel_id' => $distribution_channel_id,
+            'uid' => $uid,
+            'product_id' => $product_id,
+            'price' => $price / 100,
+            'pay_type' => 1,
+            'trade_no' => $trade_no,
+            'pay_merchant_source' => 'XIAOCHENGXU',
+            'pay_merchant_id' => 0,
+            'create_ip' => $request->getClientIp(),
+            'send_order_id' => $send_order_id,
+            'order_type' => $order_type,
+            'from_bid' => $bid,
+            'from_type' => 'QuickApp',
+            'activity_id'=>0
+        ]);
+        Log::info('order is');
+        Log::info($res);
+        //微信下单
+        $options = [
+            'app_id' => 'wxa0c8331eba3b34d5',
+            'payment' => [
+                'merchant_id' => 1501944981,
+                'key' => 'ee245088b93ba88008279d95f6d30413',
+            ]
+        ];
+        $app = new Application($options);
+        $payment = $app->payment;
+        $attributes = [
+            'trade_type' => 'JSAPI',
+            'body' => 'novel read',
+            'detail' => 'novel read',
+            'out_trade_no' => $trade_no,
+            'total_fee' => $price,
+            'notify_url' => env('QAPP_PAY_CALL_BACK_URL'),
+            'openid' => $openid,
+            'spbill_create_ip' => $request->getClientIp(),
+        ];
+
+        $order = new Wxorder($attributes);
+        Log::info('wexin order--------');
+        Log::info($order);
+        $result = $payment->prepare($order);
+        Log::info('wexin order--xcx--result-----');
+        Log::info($result);
+        if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') {
+            Log::info('wechat success in');
+            $data = [
+                'appId' => $result->appid,
+                'package' => 'prepay_id=' . $result->prepay_id,
+                'nonceStr' => $result->nonce_str,
+                'timeStamp' => (string)time(),
+                'signType' => 'MD5',
+            ];
+            $data['paySign'] = $this->MakeSign($data);
+            unset($data['appId']);
+            $data['order'] = $trade_no;
+            $data = [
+                'code' => 0,
+                'msg' => '',
+                'data' => $data
+            ];
+            return response()->json(($data));
+        }
+        return null;
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 订单查询
+     * @api {get} checkOrder 订单查询
+     * @apiGroup pay
+     * @apiName checkOrder
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiParam {String}  order   order
+     * @apiSuccess {int}   code 状态码
+     * @apiSuccess {String}  msg 信息
+     * @apiSuccess {Object}  data 信息
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {
+     *
+     *       }
+     */
+    public function checkOrder(Request $request){
+        $order = $request->input('order','');
+        $i = 0;
+        $uid = $this->uid;
+        while ($i <= 10){
+            $order_info = OrderService::getByTradeNo($order);
+            if(!$order_info){
+                return response()->error('QAPP_SYS_ERROR');
+                break;
+            }
+            if (isset($order_info->status) && $order_info->status == 'PAID') {
+                $data['balance'] = 0;
+                $user = UserService::getById($uid);
+                if($user){
+                    $data['balance'] = $user->balance;
+                }
+                return response()->success($data);
+            }
+            sleep(1);
+            $i++;
+        }
+        return response()->error('QAPP_SYS_ERROR');
+
+    }
+    protected function MakeSign($value)
+    {
+        $data = $value;
+        //签名步骤一:按字典序排序参数
+        ksort($data);
+        $buff = "";
+        foreach ($data as $k => $v) {
+            if ($k != "sign" && $v != "" && !is_array($v)) {
+                $buff .= $k . "=" . $v . "&";
+            }
+        }
+        $buff = trim($buff, "&");
+        //签名步骤二:在string后加入KEY
+        $string = $buff . "&key=" . 'ee245088b93ba88008279d95f6d30413';
+        //签名步骤三:MD5加密
+        $string = md5($string);
+        //签名步骤四:所有字符转为大写
+        $result = strtoupper($string);
+        return $result;
+    }
+
+    /**
+     * 官方微信回调
+     * @param Request $request
+     * @return
+     */
+    function wxback_xcx(Request $request)
+    {
+        Log::info('-------------en----------------');
+        $options = [
+            'app_id' => 'wxa0c8331eba3b34d5',
+            'payment' => [
+                'merchant_id' => 1501944981,
+                'key' => 'ee245088b93ba88008279d95f6d30413',
+            ]
+        ];
+        $app = new Application($options);
+        $response = $app->payment->handleNotify(function ($notify, $successful) {
+            if (!$successful) return 'fail';
+            $trade_no = $notify->out_trade_no;
+            $order = OrderService::getByTradeNo($trade_no);
+            if (!$order) {
+                return 'fail';
+            }
+
+            if (isset($order->status) && $order->status == 'PAID') {
+                Log::info('has_pay:' . $trade_no);
+                return true;
+            }
+            DB::beginTransaction();
+            try {
+                $transaction_id = $notify->transaction_id;
+                $uid = $order->uid;
+                $distribution_channel_id = $order->distribution_channel_id;
+                $product_id = $order->product_id;
+                $product = ProductService::getProductSingle($product_id);
+                $send_order_id = 0;
+                $price = $product->price;
+
+                //$this->updateOrderTotal($trade_no,$transaction_id);
+                // 更新其他定制Order表
+                if ($product->type == 'YEAR_ORDER') {
+                    Log::info('YEAR_ORDERYEAR_ORDERYEAR_ORDERYEAR_ORDERYEAR_ORDERYEAR_ORDERYEAR_ORDERYEAR_ORDER');
+                    $order_type = 'YEAR';
+                    $this->yearOrder($uid, $distribution_channel_id, $price, $send_order_id);
+                    $order->order_type = $order_type;
+                    $order->status = 'PAID';
+                    $order->pay_end_at = date('Y-m-d H:i:s');
+                    $order->transaction_id = $transaction_id;
+                    $order->save();
+
+                } elseif ($product->type == 'BOOK_ORDER') {
+                    Log::info('BOOK_ORDERBOOK_ORDERBOOK_ORDERBOOK_ORDERBOOK_ORDERBOOK_ORDERBOOK_ORDERBOOK_ORDER');
+                    $order_type = 'BOOK';
+                    $this->bookOrder($product_id, $uid, $send_order_id, $price, $distribution_channel_id);
+                    $order->order_type = $order_type;
+                    $order->status = 'PAID';
+                    $order->pay_end_at = date('Y-m-d H:i:s');
+                    $order->transaction_id = $transaction_id;
+                    $order->save();
+
+                } elseif ($product->type == 'TICKET_RECHARGE') {
+                    Log::info('TICKET_RECHARGETICKET_RECHARGETICKET_RECHARGETICKET_RECHARGETICKET_RECHARGE');
+                    $order_type = 'RECHARGE';
+                    $this->userCharge($product, $uid);
+                    $order->order_type = $order_type;
+                    $order->status = 'PAID';
+                    $order->pay_end_at = date('Y-m-d H:i:s');
+                    $order->transaction_id = $transaction_id;
+                    $order->save();
+
+                } else {
+                    DB::rollback();
+                    return 'Order not exist.';
+                }
+                DB::commit();
+                return true;
+            } catch (\Exception $e) {
+                DB::rollback();
+                return 'fail';
+            }
+        });
+        return $response;
+    }
+
+    /**
+     * 单本充值会掉
+     * @param $product_id
+     * @param $uid
+     * @param $send_order_id
+     * @param $fee
+     */
+    private function bookOrder($product_id, $uid, $send_order_id, $fee, $distribution_channel_id)
+    {
+        $book_conf = BookConfigService::getBookByProduct($product_id);
+        $insert_data['bid'] = isset($book_conf->bid) ? $book_conf->bid : '';
+        $insert_data['book_name'] = isset($book_conf->book_name) ? $book_conf->book_name : '';
+        $insert_data['uid'] = $uid;
+        $insert_data['distribution_channel_id'] = $distribution_channel_id;
+        $insert_data['fee'] = $fee;
+        $insert_data['send_order_id'] = $send_order_id;
+        $insert_data['charge_balance'] = 0;
+        $insert_data['reward_balance'] = 0;
+        Log::info('start_save_book_order');
+        Log::info($insert_data);
+        return BookOrderService::save_book_order($insert_data);
+    }
+
+    /**
+     * 包年
+     * @param $uid
+     * @param $distribution_channel_id
+     * @param $fee
+     * @param $send_order_id
+     * @return mixed
+     */
+    private function yearOrder($uid, $distribution_channel_id, $fee, $send_order_id)
+    {
+        Log::info('start_save_year_order');
+        $insert_data['uid'] = $uid;
+        $insert_data['distribution_channel_id'] = $distribution_channel_id;
+        $insert_data['fee'] = $fee;
+        $insert_data['send_order_id'] = $send_order_id;
+        Log::info($insert_data);
+        return YearOrderService::save_year_order($insert_data);
+    }
+
+    /**
+     * 用户充值
+     * @param $product
+     * @param $uid\
+     */
+    private function userCharge($product, $uid)
+    {
+        $total = $product->price * 100 + $product->given;
+        UserService::addBalance($uid, $total, $product->price * 100, $product->given);
+        Log::info('update_user_balance_end:' . $uid . ' balance_add:' . $total);
+    }
+
+    /**
+     * 添加位置付订单
+     * @param $data
+     * @return mixed
+     */
+    private function createUnPayOrder($data)
+    {
+        $data['status'] = 'UNPAID';
+        $data['transaction_id'] = '';
+        $data['pay_end_at'] = '0000-00-00 00:00:00';
+        return OrderService::save_order($data);
+    }
+
+}

+ 17 - 0
app/Http/Controllers/QuickApp/Order/Transformers/BookOrderTransformer.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Order\Transformers;
+use Hashids;
+class BookOrderTransformer
+{
+    public function transform($res){
+        return [
+            //'bid_no_hash'          =>  $res->uid,
+            'bid'          =>  Hashids::encode($res->bid),
+            'book_name'    =>  $res->book_name,
+            'uid'            =>  $res->uid,
+            'created_at'      => date("Y-m-d H:i:s",strtotime($res->created_at)),
+            'fee'            =>  $res->fee,
+        ];
+    }
+}

+ 19 - 0
app/Http/Controllers/QuickApp/Order/Transformers/ChapterOrderTransformer.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Order\Transformers;
+use Hashids;
+class ChapterOrderTransformer
+{
+    public function transform($res){
+        return [
+            'uid'          		=>  $res->uid,
+            //'bid_no_hash'       =>  $res->bid,
+            'bid'            	=>  Hashids::encode($res->bid),
+            'cid'   		    =>  $res->cid,
+            'book_name'         =>  $res->book_name,
+            'chapter_name'      =>  $res->chapter_name,
+            'fee'               =>  $res->fee,
+            'created_at'        =>  date("Y-m-d H:i:s",strtotime($res->created_at)),
+        ];
+    }
+}

+ 16 - 0
app/Http/Controllers/QuickApp/Order/Transformers/ChargeListTransformer.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Order\Transformers;
+class ChargeListTransformer
+{
+
+    public function transform($res){
+        return [
+            'id'       =>  $res->id,
+            'price'       =>  $res->price,
+            'status'   =>  $res->status,
+            'trade_no'=>$res->trade_no,
+            'created_at'   =>  date("Y-m-d H:i:s",strtotime($res->created_at))
+        ];
+    }
+}

+ 19 - 0
app/Http/Controllers/QuickApp/Order/Transformers/OrderTransformer.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\Order\Transformers;
+
+class OrderTransformer
+{
+    public function transform($order){
+        return [
+            'merchant_order_id'       =>  $order->merchant_order_id,
+            'user_id'       =>  $order->user_id,
+            'user_nickname'       =>  $order->user_nickname,
+            'pay_money'       =>  (int)$order->pay_money/100,
+            'pay_flag'   =>  $order->pay_flag,
+            'pay_time'   =>  $order->pay_time ? date('Y-m-d H:i:s',$order->pay_time) : '',
+            'send_order_id' => isset($order->send_order_id) ? $order->send_order_id : 0,
+            'send_order_name' => isset($order->send_order_name) ? $order->send_order_name : ''
+        ];
+    }
+}

+ 189 - 0
app/Http/Controllers/QuickApp/User/ReadRecordController.php

@@ -0,0 +1,189 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\User;
+
+use Illuminate\Http\Request;
+use App\Http\Controllers\QuickApp\BaseController;
+use App\Modules\Book\Services\BookConfigService;
+use App\Modules\Book\Services\UserShelfBooksService;
+use App\Http\Controllers\QuickApp\User\Transformers\ReadRecordTransformer;
+use App\Modules\User\Services\ReadRecordService;
+use Hashids;
+use Redis;
+use Log;
+class ReadRecordController extends BaseController
+{
+
+    /**
+     * @apiDefine ReadRecord 阅读记录
+     */
+
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 获取阅读记录
+     * @api {get} readrecord 获取阅读记录
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup ReadRecord
+     * @apiName index
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Int}         data.book_name    书名
+     * @apiSuccess {String}      data.chapter_name  章节名
+     * @apiSuccess {Int}         data.bid   bid
+     * @apiSuccess {Int}         data.time   时间
+     * @apiSuccess {Int}         data.cid    章节id
+     * @apiSuccess {Int}         data.chapter_name    章节名
+     * @apiSuccess {Int}         data.cover    封面
+     * @apiSuccess {Int}         data.last_chapter    最后一张
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:[
+     *           {
+     *               book_name: "我来好好爱你",
+     *               bid: 2,
+     *               cid: 10402,
+     *               time: 1511783120,
+     *               chapter_name: "你言重了"
+     *           },
+     *           {
+     *               book_name: "京华烟云",
+     *               bid: 1,
+     *               cid: 4,
+     *               time: 1511783068,
+     *               chapter_name: "背水一战"
+     *           }
+     *           ]
+     */
+    public function index(Request $request){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+
+        $res = ReadRecordService::getReadRecord($this->uid);
+        if($res){
+            $id_arr = [];
+            foreach ($res as $key => $value) {
+                $id_arr[] = $value['bid'];
+            }
+
+            $book = BookConfigService::getBooksByIds($id_arr);
+            foreach ($res as $key => &$value) {
+                foreach ($book as  $val) {
+                    if($value['bid'] == $val->bid){
+                        $value['cover'] = $val->cover;
+                        $value['last_chapter'] = $val->last_chapter;
+                        break;
+                    }
+                }
+            }
+
+            $shelf = UserShelfBooksService::getUserShelfBooksListByUid($this->uid);
+            foreach ($res as &$v){
+                $v['is_on_user_shelf'] = 0;
+                foreach ($shelf as $val){
+                    if($v['bid'] == $val->bid){
+                        $v['is_on_user_shelf'] = 1;
+                        break;
+                    }
+                }
+            }
+        }
+        usort($res,function($a,$b){
+            if($a['time'] >= $b['time']) return -1;
+            return 1;
+
+        });
+        $res = json_encode($res);
+        $res = json_decode($res);
+        return response()->collection(new ReadRecordTransformer(),$res);
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 添加阅读记录
+     * @api {post} readrecord 添加阅读记录
+     * @apiGroup ReadRecord
+     * @apiName addReadRecord
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiParam   {Int}         book_name    书名
+     * @apiParam   {String}      chapter_name  章节名
+     * @apiParam   {Int}         bid     bid
+     * @apiParam   {Int}         cid    章节id
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:{}
+     *
+     */
+    public function addReadRecord(Request $request){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $param = $request->except('_url');
+        if(checkParam($param,['bid','cid','chapter_name'])){
+            return response()->error('LACK_PARAM');
+        }
+        //Redis::hset('book_read:' . $uid, $bid, "{$cid}_{$book_name}_{$chapter_name}_" . time());
+
+        $param['uid'] = $this->uid;
+        $param['bid'] = Hashids::decode($param['bid'])[0];
+        $record_info = Redis::hget('book_read:'.$this->uid,$param['bid']);
+        $param['book_name'] = 'unknown';
+        if($record_info){
+            $param['book_name'] = explode('_',$record_info)[1];
+        }
+
+        ReadRecordService::addReadRecord($param);
+        return response()->success();
+
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 删除阅读记录
+     * @api {get} readrecord/delete 删除阅读记录
+     * @apiGroup ReadRecord
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiName delReadRecord
+     * @apiParam   {Int}         bid     bid
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data:{}
+     *
+     */
+    public function delReadRecord(Request $request){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $param = $request->except('_url');
+        if(checkParam($param,['bid'])){
+            return response()->error('LACK_PARAM');
+        }
+        $param['bid'] = Hashids::decode($param['bid'])[0];
+        ReadRecordService::delReadRecord($this->uid,$param['bid']);
+        return response()->success();
+    }
+
+}

+ 20 - 0
app/Http/Controllers/QuickApp/User/Transformers/ReadRecordTransformer.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\User\Transformers;
+use Hashids;
+class ReadRecordTransformer
+{
+    public function transform($res)
+    {
+        return [
+            'bid' => Hashids::encode($res->bid),
+            'cid' => $res->cid,
+            'book_name' => $res->book_name,
+            'chapter_name' => $res->chapter_name,
+            'time' => $res->time,
+            'cover' => $res->cover,
+            'last_chapter' => $res->last_chapter,
+            'is_on_user_shelf'=>$res->is_on_user_shelf
+        ];
+    }
+}

+ 24 - 0
app/Http/Controllers/QuickApp/User/Transformers/UserShelfBooksTransformer.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\User\Transformers;
+
+use Hashids;
+
+class UserShelfBooksTransformer
+{
+    public function transform($userShelfBook)
+    {
+        return [
+            'id' => $userShelfBook->id,
+            'uid' => $userShelfBook->uid,
+            'distribution_channel_id' => $userShelfBook->distribution_channel_id,
+            //'bid_no_hash' => $userShelfBook->bid,
+            'bid' => Hashids::encode($userShelfBook->bid),
+            'book_name' => $userShelfBook->book_name,
+            'cover' => $userShelfBook->cover,
+            'updated_at' => strtotime($userShelfBook->updated_at),
+            'first_cid' => $userShelfBook->first_cid,
+            'last_cid' => $userShelfBook->last_cid,
+        ];
+    }
+}

+ 96 - 0
app/Http/Controllers/QuickApp/User/UserController.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\User;
+
+
+use App\Http\Controllers\QuickApp\BaseController;
+use App\Modules\Subscribe\Services\YearOrderService;
+use App\Modules\User\Transformers\YearOrderTransformer;
+use App\Modules\User\Services\UserService;
+use Log;
+use Redis;
+use Cookie;
+class UserController extends BaseController
+{
+    /**
+     * @apiDefine User 用户
+     */
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 获取用户信息
+     * @api {GET} userinfo 获取用户信息
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup User
+     * @apiName index
+     * @apiSuccess {Number}  id 用户ID.
+     * @apiSuccess {String}  openid 微信openid.
+     * @apiSuccess {String}  unionid 微信unionid.
+     * @apiSuccess {Number}  distribution_channel_id 分销渠道ID.
+     * @apiSuccess {String}  province 省份.
+     * @apiSuccess {String}  city 城市.
+     * @apiSuccess {String}  country 国家.
+     * @apiSuccess {String}  headimgurl 头像地址.
+     * @apiSuccess {Number}  send_order_id 派单ID.
+     * @apiSuccess {Number=0,1}  sex 性别.
+     * @apiSuccess {String}  balance 书币余额.
+     * @apiSuccess {Int}     is_vip 是否vip
+     * @apiSuccess {String}  vip_days 364天.
+     * @apiSuccessExample {json} Success-Response:
+     *
+     *     {
+     *         "code": 0,
+     *         "msg": "",
+     *         "data": {
+     *             "id": 56,
+     *             "openid": "sdfs34ssdfdsf",
+     *             "unionid": "SDFSD3343S",
+     *             "distribution_channel_id": 1212,
+     *             "province": "浙江省",
+     *             "city": "杭州",
+     *             "country": "中国",
+     *             "headimgurl": "http://.."
+     *             "send_order_id": 323
+     *             "balance": 8956
+     *             "register_time": "2017-12-12 12:12:12"
+     *         }
+     *     }
+     */
+    public function index(){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+
+        $data = UserService::getById($this->uid);
+        if(!$data->head_img){
+            $data->head_img = 'https://yueduyun.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/img/defaulthead.png';
+        }
+        $data['is_vip'] = 0;
+        $data['vip_days'] = 0;
+        $year_record = YearOrderService::getRecord($this->uid);
+        if($year_record){
+            $data['is_vip'] = 1;
+            
+            $time = strtotime($year_record['end_time'])-time();
+            if($time>=86400){
+                  $data['vip_days'] = floor($time/86400).'天';  
+            }elseif ($time>3600) {
+                 $data['vip_days'] = floor($time/3600).'小时';  
+            }elseif ($time>60) {
+                 $data['vip_days'] = floor($time/60).'分钟';  
+            }else{
+                $data['vip_days'] = $time.'秒';  
+            }
+        }
+        return response()->success($data);
+    }
+
+    function logout()
+    {
+        setcookie(env('COOKIE_AUTH_WEB_WECHAT'), '', -1);
+        setcookie('u', '', -1);
+        return response('logout');
+    }
+}

+ 225 - 0
app/Http/Controllers/QuickApp/User/UserShelfBooksController.php

@@ -0,0 +1,225 @@
+<?php
+
+namespace App\Http\Controllers\QuickApp\User;
+
+use Illuminate\Http\Request;
+use App\Http\Controllers\QuickApp\BaseController;
+use Redis;
+use App\Modules\Book\Services\UserShelfBooksService;
+use App\Http\Controllers\QuickApp\User\Transformers\UserShelfBooksTransformer;
+use App\Modules\User\Services\ReadRecordService;
+use Hashids;
+use Log;
+class UserShelfBooksController extends BaseController
+{
+
+    /**
+     * @apiDefine UserShelfBooks 书架
+     */
+
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 获取书架
+     * @api {get} userShelfBooks 获取书架
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup UserShelfBooks
+     * @apiName getChapter
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Int}         data.id   编号
+     * @apiSuccess {String}      data.distribution_channel_id   分销
+     * @apiSuccess {Int}         data.uid   uid
+     * @apiSuccess {Int}         data.bid   bid
+     * @apiSuccess {Int}         data.book_name   书名
+     * @apiSuccess {Int}         data.cover   封面
+     * @apiSuccess {Int}         data.last_cid   章节id
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {
+     *          [
+     *               {
+     *                   id: 6,
+     *                   uid: 4,
+     *                   distribution_channel_id: 1,
+     *                   bid: 1,
+     *                   book_name: "京华烟云",
+     *                   cover: "https://leyue-bucket.oss-cn-hangzhou.aliyuncs.com/ycsd_cover/covermiddle/0/1.jpg",
+     *                   updated_at: 1511783068,
+     *                   last_cid: 4
+     *               },
+     *               {
+     *                   id: 7,
+     *                   uid: 4,
+     *                   distribution_channel_id: 1,
+     *                   bid: 1,
+     *                   book_name: "我来好好爱你",
+     *                   cover: "https://leyue-bucket.oss-cn-hangzhou.aliyuncs.com/ycsd_cover/covermiddle/0/11.jpg",
+     *                   updated_at: 1511783068,
+     *                   last_cid: 4
+     *               }
+     *           ]
+     *       }
+     */
+    public function index(Request $request){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+
+        $res = UserShelfBooksService::getUserShelfBooksListByUid($this->uid);
+
+        if($res->isEmpty()){
+            return response()->success();
+        }
+
+        $record = ReadRecordService::getReadRecord($this->uid);
+
+        //if(empty($record))   return response()->success($res);
+
+        foreach ($res as &$v){
+            $v['last_cid'] = $v['first_cid'];
+            foreach ($record as $val){
+                if($v['bid'] == $val['bid']){
+                    $v['updated_at'] = $val['time'];
+                    $v['last_cid'] = $val['cid'];
+                    break;
+                }
+            }
+        }
+
+        /*
+        usort($res,function($a,$b){
+            if($a['updated_at'] >= $b['updated_at']) return -1;
+            return 1;
+
+        });*/
+        return response()->collection(new UserShelfBooksTransformer(),$res);
+    }
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 添加书架
+     * @api {post} userShelfBooks 添加书架
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup UserShelfBooks
+     * @apiName addShelf
+     * @apiParam   {int}         bid bid
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {}
+     */
+    public function addShelf(Request $request){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $param = $request->except('_url');
+        if(checkParam($param,['bid'])){
+            return response()->error('LACK_PARAM');
+        }
+
+        $param['uid'] = $this->uid;
+        $param['bid'] = Hashids::decode($param['bid'])[0];
+        $param['distribution_channel_id'] = $this->distribution_channel_id;
+        $res = null;
+        try{
+            $res = UserShelfBooksService::create($param);
+        }catch (\Exception $e){
+            return response()->error('QAPP_PARAM_ERROR');
+        }
+
+        if($res){
+            return response()->success($res);
+        }
+        return response()->error('QAPP_SYS_ERROR');
+    }
+
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 删除书架
+     * @api {get} userShelfBooks/delete 删除书架
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup UserShelfBooks
+     * @apiName delShelf
+     * @apiParam   {int}         bid bid
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {}
+     */
+    public function delShelf(Request $request){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $bid = $request->input('bid');
+        if(empty($bid)) return response()->error('LACK_PARAM');
+        $param['uid'] = $this->uid;
+        $param['bid'] = Hashids::decode($bid)[0];
+        $res = UserShelfBooksService::del($this->uid,$param['bid']);
+        if($res){
+            return response()->success();
+        }
+        return response()->error('QAPP_SYS_ERROR');
+    }
+    
+
+    /**
+     * @apiVersion 1.0.0
+     * @apiDescription 是否在书架上
+     * @api {get} userShelfBooks/isonshelf 是否在书架上
+     * @apiParam {String}  [token]  token
+     * @apiHeader {String} [Authorization]  token 两个token任选其一
+     * @apiGroup UserShelfBooks
+     * @apiName isOnshelf
+     * @apiParam   {int}         bid bid
+     * @apiSuccess {int}         code 状态码
+     * @apiSuccess {String}      msg  信息
+     * @apiSuccess {object}      data 结果集
+     * @apiSuccess {Int}         data.is_on 是否在书架上(0|1)
+     * @apiSuccessExample {json} Success-Response:
+     *     HTTP/1.1 200 OK
+     *     {
+     *       code: 0,
+     *       msg: "",
+     *       data: {
+     *               is_on:0
+     *           }
+     */
+    public function  isOnshelf(Request $request){
+        if(!$this->checkUid()){
+            return response()->error('QAPP_NOT_LOGIN');
+        }
+        $bid = $request->input('bid');
+        if(!$bid) return response()->error('LACK_PARAM');
+        $bid = Hashids::decode($bid)[0];
+        //$param['uid'] = $this->uid;
+        //$res = $this->apiClient->get($this->joinUrl('Read/isonshelf'),$param);
+        $res = UserShelfBooksService::getUserShelfBooksListByUidAndBid($this->uid,$bid);
+        if($res){
+            $data['is_on'] = 1;
+        }else{
+            $data['is_on'] = 0;
+        }
+        return response()->success($data);
+    }
+
+}

+ 15 - 0
app/Http/Controllers/QuickApp/apidoc.json

@@ -0,0 +1,15 @@
+{
+"name": "追书云快应用平台接口",
+"version": "1.0.0",
+"description": "追书云快应用平台接口,除了登录的所有接口都需要传token或者在header头里面加上Authorization,如(Authorization:Bearer eyJ0eXAiOiJKV1QiLCJ****),所有返回的接口都在返回头中都有Authorization,BearerZz之后的内容就是token,即下一次请求时的token",
+"title": "追书云快应用平台接口",
+"url": "https://kyy.aizhuishu.com/api/",
+"template": {
+"withCompare": true,
+"withGenerator": true
+},
+"header": {
+    "title": "概述",
+    "filename": "header.md"
+  }
+}

+ 4 - 0
app/Http/Controllers/QuickApp/header.md

@@ -0,0 +1,4 @@
+状态码:<br>
+
+	0:     正常
+    

+ 2 - 16
app/Http/Kernel.php

@@ -28,20 +28,10 @@ class Kernel extends HttpKernel
             \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
             \Illuminate\Session\Middleware\StartSession::class,
             \Illuminate\View\Middleware\ShareErrorsFromSession::class,
-            //\App\Http\Middleware\VerifyCsrfToken::class,
-            //\App\Http\Middleware\EnableCrossRequest::class,
-        ],
-        'wap' => [
-            \App\Http\Middleware\EncryptCookies::class,
-            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
-            //\Illuminate\Session\Middleware\StartSession::class,
-            //\Illuminate\View\Middleware\ShareErrorsFromSession::class,
-            //\App\Http\Middleware\VerifyCsrfToken::class,
-            //\App\Http\Middleware\EnableCrossRequest::class,
         ],
         'jwttoken' => [
-            \App\Http\Middleware\XchengxuRefreshToken::class,
-            \App\Http\Middleware\XchengxuGetUserFromToken::class,
+            \App\Http\Middleware\QuickAppRefreshToken::class,
+            \App\Http\Middleware\QuickAppGetUserFromToken::class,
         ],
         'api' => [
             'throttle:60,1',
@@ -56,10 +46,6 @@ class Kernel extends HttpKernel
      * @var array
      */
     protected $routeMiddleware = [
-        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
         'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
-        'ReadOauth' => \App\Http\Middleware\ReadOauth::class,
-        'EnableCross' => \App\Http\Middleware\EnableCrossRequest::class,
-        'CrmUser' =>  \App\Http\Middleware\CrmUser::class,
     ];
 }

+ 0 - 65
app/Http/Middleware/CrmUser.php

@@ -1,65 +0,0 @@
-<?php
-
-namespace App\Http\Middleware;
-
-use App\Modules\User\Services\SiteUser;
-use Closure;
-use EasyWeChat\Foundation\Application;
-use Cookie;
-
-class CrmUser
-{
-    /**
-     * Handle an incoming request.
-     *
-     * @param  \Illuminate\Http\Request  $request
-     * @param  \Closure  $next
-     * @return mixed
-     */
-    public function handle($request, Closure $next)
-    {
-        $uid = Cookie::get('crm_user_id');
-        if ($uid) {
-            $channel_id = Cookie::get('crm_channel_id');
-            $this->setUserGlobal($uid, $channel_id);
-        } else {
-            $openid = $request->get('openid');
-            // $openid = 'oHhd20YbxxOZ34ahoE7ZrX8M8O2c';
-            $params = $request->except('_url');
-            if (empty($openid)) {
-                $url = url()->current() . '?' . http_build_query($params);
-                $params['redirect_url'] = urlencode($url);
-                $app = new Application($this->auth($params));
-                return $app->oauth->redirect();
-            } else {
-                $site_user = new SiteUser($openid);
-                $user = $site_user->getChannelUser();
-                Cookie::queue('crm_user_id', $user[0], env('U_COOKIE_EXPIRE'));
-                Cookie::queue('crm_channel_id', $user[1], env('U_COOKIE_EXPIRE'));
-                $this->setUserGlobal($user[0], $user[1]);
-            }
-        }
-        return $next($request);
-    }
-
-    private function setUserGlobal($uid, $channel_id)
-    {
-        $user = app()->make('user');
-        $user->id = $uid;
-        $user->channel_id = $channel_id;
-    }
-
-    private function auth($param)
-    {
-        $param['appid'] = 'wx9d389a73b88bbeae';
-        $options = [
-            'app_id' => 'wx9d389a73b88bbeae',
-            'secret' => '2f6594bb595dfa256b5512e43a32a3d3',
-            'oauth' => [
-                'scopes' => ['snsapi_base'],
-                'callback' => env('AUTH_CALLBACK_URL') . '?' . http_build_query($param),
-            ],
-        ];
-        return $options;
-    }
-}

+ 0 - 30
app/Http/Middleware/EnableCrossRequest.php

@@ -1,30 +0,0 @@
-<?php 
-
-namespace App\Http\Middleware;
-
-use Closure;
-use Response;
-use Log;
-class EnableCrossRequest {
-
-  /**
-   * Handle an incoming request.
-   *
-   * @param  \Illuminate\Http\Request  $request
-   * @param  \Closure  $next
-   * @return mixed
-   */
-  public function handle($request, Closure $next)
-  {     
-        $response = $next($request);
-
-        /*
-        header("Access-Control-Allow-Credentials: true");
-        header("Access-Control-Allow-Methods: *");
-        header("Access-Control-Allow-Headers: Content-Type,Access-Token");
-        header("Access-Control-Expose-Headers: *");*/
-        
-        return $response;
-  }
-
-}

+ 1 - 25
app/Http/Middleware/EncryptCookies.php

@@ -11,29 +11,5 @@ class EncryptCookies extends BaseEncrypter
      *
      * @var array
      */
-    protected $except = [
-        'u',
-        'send_order_id',
-        'from',
-        'send_order_continue',
-        'send_order_flag',
-        'force_subscribe_name',
-        'sub_random_num',
-        'reader_share_bid',
-        'reader_share_cid',
-        'force_show_qrcode',
-        'cpc_ad_status',
-        'crm',
-        'crm_flag',
-        'is_paid',
-        'crm_person_img',
-        'crm_person_name',
-        'sign_recomand_bids',
-        'month_pay_redirect',
-        'is_force_out_guide',
-        'push_transfer_custom_flag',
-        'push_transfer_template_flag',
-        'crm_user_id',
-        'crm_channel_id'
-    ];
+    protected $except = [];
 }

+ 9 - 12
app/Http/Middleware/XchengxuGetUserFromToken.php

@@ -3,11 +3,12 @@
 namespace App\Http\Middleware;
 
 use Closure;
+use Illuminate\Http\Request;
 use Tymon\JWTAuth\Exceptions\JWTException;
 use Tymon\JWTAuth\Exceptions\TokenExpiredException;
 use Tymon\JWTAuth\Middleware\BaseMiddleware;
 
-class XchengxuGetUserFromToken extends BaseMiddleware
+class QuickAppGetUserFromToken extends BaseMiddleware
 {
     /**
      * Handle an incoming request.
@@ -16,26 +17,22 @@ class XchengxuGetUserFromToken extends BaseMiddleware
      * @param  \Closure  $next
      * @return mixed
      */
-    public function handle($request, \Closure $next)
+    public function handle(Request $request, Closure $next)
     {
-        if (! $token = $this->auth->setRequest($request)->getToken()) {
-            return response()->error('XCX_TOKEN_ERROR');
+        if (!$token = $this->auth->setRequest($request)->getToken()) {
+            return response()->error('QAPP_TOKEN_ERROR');
         }
-
         try {
             $user = $this->auth->authenticate($token);
         } catch (TokenExpiredException $e) {
-            return response()->error('XCX_TOKEN_ERROR');
+            return response()->error('QAPP_TOKEN_ERROR');
         } catch (JWTException $e) {
-            return response()->error('XCX_TOKEN_ERROR');
+            return response()->error('QAPP_TOKEN_ERROR');
         }
-
-        if (! $user) {
-             return response()->error('XCX_TOKEN_ERROR');
+        if (!$user) {
+            return response()->error('QAPP_TOKEN_ERROR');
         }
-
         $this->events->fire('tymon.jwt.valid', $user);
-
         return $next($request);
     }
 }

+ 2 - 6
app/Http/Middleware/CheckParam.php

@@ -3,8 +3,8 @@
 namespace App\Http\Middleware;
 
 use Closure;
-use App\Api\Common\Common;
-class CheckParam
+
+class QuickAppOAuth
 {
     /**
      * Handle an incoming request.
@@ -15,10 +15,6 @@ class CheckParam
      */
     public function handle($request, Closure $next)
     {
-
-        if(empty($request->all())){
-            return response()->json(Common::error('1001'));
-        }
         return $next($request);
     }
 }

+ 7 - 6
app/Http/Middleware/XchengxuRefreshToken.php

@@ -3,10 +3,12 @@
 namespace App\Http\Middleware;
 
 use Closure;
+use Illuminate\Http\Request;
 use Tymon\JWTAuth\Exceptions\JWTException;
 use Tymon\JWTAuth\Exceptions\TokenExpiredException;
 use Tymon\JWTAuth\Middleware\BaseMiddleware;
-class XchengxuRefreshToken extends  BaseMiddleware
+
+class QuickAppRefreshToken extends  BaseMiddleware
 {
     /**
      * Handle an incoming request.
@@ -15,17 +17,16 @@ class XchengxuRefreshToken extends  BaseMiddleware
      * @param  \Closure  $next
      * @return mixed
      */
-    public function handle($request, \Closure $next)
+    public function handle(Request $request, Closure $next)
     {
         $response = $next($request);
-
         try {
             $newToken = $this->auth->setRequest($request)->parseToken()->getToken();
-            $response->headers->set('Authorization', 'Bearer '.$newToken);
+            $response->headers->set('Authorization', 'Bearer ' . $newToken);
         } catch (TokenExpiredException $e) {
-            //return response()->error('XCX_TOKEN_ERROR');
+            return response()->error('QAPP_TOKEN_ERROR');
         } catch (JWTException $e) {
-            //return response()->error('XCX_TOKEN_ERROR');
+            return response()->error('QAPP_TOKEN_ERROR');
         }
         return $response;
     }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 794
app/Http/Middleware/ReadOauth.php


+ 0 - 17
app/Http/Middleware/VerifyCsrfToken.php

@@ -1,17 +0,0 @@
-<?php
-
-namespace App\Http\Middleware;
-
-use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
-
-class VerifyCsrfToken extends BaseVerifier
-{
-    /**
-     * The URIs that should be excluded from CSRF verification.
-     *
-     * @var array
-     */
-    protected $except = [
-        'api/*'
-    ];
-}

+ 74 - 0
app/Http/Routes/QuickApp/QuickAppRoutes.php

@@ -0,0 +1,74 @@
+<?php
+
+Route::group(['domain' => env('QUICKAPP_DOMAIN'), 'namespace' => 'App\Http\Controllers\QuickApp'], function () {
+    //登录
+    Route::post('login', 'Oauth\UsersController@index');
+    //支付
+    Route::any('pay/wcback_xchengxu', 'Order\OrdersController@wxindex');
+    Route::get('RefreshToken', 'Oauth\UsersController@RefreshToken');
+
+    //支付回调
+    Route::any('pay/wcback', 'Order\OrdersController@wxback_xcx');
+
+    Route::group(['prefix' => 'api', 'middleware' => 'jwttoken'], function () {
+        //首页
+        Route::get('books/{sex}/index', 'Book\BookController@getBookLists');
+        //书库
+        Route::get('books/library', 'Book\BookController@library');
+        //相似推荐
+        Route::get('books/similar', 'Book\BookController@similarRecom');
+        //阅读完后的推荐
+        Route::get('books/readOverRecommend', 'Book\BookController@readOverRecommend');
+        //排行榜
+        Route::get('books/rank', 'Book\BookController@rank');
+        //分类列表
+        Route::get('books/getCategory', 'Book\BookCategoryController@getCategory');
+
+        //图书详情
+        Route::get('book/{bid}', 'Book\BookController@index');
+
+        //目录不分页
+        Route::get('books/{bid}/allcatalog', 'Book\ChapterController@getCatalog');
+
+        //目录分页
+        Route::get('books/{bid}/catalog', 'Book\ChapterController@getCatalogPerPage');
+        //章节详情
+        Route::get('books/{bid}/chapters/{cid}', 'Book\ChapterController@index')->where('cid', '\d+');
+        //余额订购
+        Route::get('books/{bid}/balance/chapterOrders/{cid}', 'Book\ChapterController@pay');
+
+        //充值列表
+        Route::get('order/chargeList', 'Order\OrdersController@chargeList');
+        //章节订购列表
+        Route::get('order/chapterOrderList', 'Order\OrdersController@chapterOrderList');
+        //单本订购列表
+        Route::get('order/bookOrderList', 'Order\OrdersController@bookOrderList');
+        //充值记录
+        Route::get('order/chargeRecordLists', 'Order\OrdersController@chargeRecordLists');
+
+        Route::get('order/issuccess', 'Order\OrdersController@isSuccess');
+        Route::get('userinfo', 'User\UserController@index');
+
+
+        //书架
+        Route::get('userShelfBooks', 'User\UserShelfBooksController@index');
+        //添加书架
+        Route::post('userShelfBooks', 'User\UserShelfBooksController@addShelf');
+        //删除书架
+        Route::get('userShelfBooks/delete', 'User\UserShelfBooksController@delShelf');
+
+        Route::get('userShelfBooks/isonshelf', 'User\UserShelfBooksController@isOnshelf');
+
+        //阅读记录
+        Route::get('readrecord', 'User\ReadRecordController@index');
+        //添加阅读记录
+        Route::post('readrecord', 'User\ReadRecordController@addReadRecord');
+        //删除阅读记录
+        Route::get('readrecord/delete', 'User\ReadRecordController@delReadRecord');
+
+        //支付
+        Route::get('goToPay', 'Order\OrdersController@wxindex');
+
+        Route::get('checkOrder', 'Order\OrdersController@checkOrder');
+    });
+});

+ 0 - 13
app/Http/Routes/Wap/OfficialAccountRoutes.php

@@ -1,13 +0,0 @@
-<?php
-
-Route::group(['domain'=>env('WAP_DOMAIN'),'namespace'=>'App\Http\Controllers\Wap\OfficialAccount','prefix'=>'api'],function(){
-
-    /**
-    * 公众号
-    */
-
-	//获取公众号信息
-    Route::any('OfficialAccount/customerImgUrlByChannelId', 'CustomMsgController@customerImgUrlByChannelId');
-
-
-});

+ 0 - 345
app/Http/Routes/Wap/WapRoutes.php

@@ -1,345 +0,0 @@
-<?php
-/**
- * 图书路由文件
- * by zhanyang
- *
- * 2017-12-05
- */
-
-
-Route::group(['domain'=>env('PAY_WAP_DOMAIN'),'namespace'=>'App\Http\Controllers\Wap'],function(){
-    //支付
-    Route::get('/goToPay','Pay\OrdersController@wxindex');
-    //支付回调
-    Route::any('pay/wcback_allinpay','Pay\OrdersController@wcback_allinpay');
-    Route::any('pay/wcback_official','Pay\OrdersController@wcback_official');
-    Route::any('pay/wcback_lianlianpay','Pay\OrdersController@wcback_lianlianpay');
-    Route::any('pay/wcback_palmpay','Pay\OrdersController@wcback_palmpay');
-    Route::any('pay/wcback_originbank','Pay\OrdersController@wcback_originbank');
-    Route::any('pay/wcback_smkpay','Pay\OrdersController@wcback_smkpay');
-    Route::any('pay/wcback_union','Pay\OrdersController@wcback_union');
-    Route::post('pay/reportError','Pay\OrdersController@reportError');
-    //包月
-
-    Route::any('monthpay/signcallback','Pay\MonthOrderController@signCallBack');
-    Route::post('monthpay/ordercallback','Pay\MonthOrderController@orderCallBack');
-    Route::post('xiyue/paynotify','Other\XiyueController@xiyuePayNotity');
-    Route::post('xiyue/getuserbalance','Other\XiyueController@getUserBalance');
-    Route::post('xiyue/consume','Other\XiyueController@xiyueConsume');
-
-
-    Route::post('monthpay/signcallbackv2','Pay\MonthOrderController@signCallBackV2');
-    Route::post('monthpay/ordercallbackv2','Pay\MonthOrderController@orderCallBackV2');
-
-});
-Route::group(['domain'=>env('OFFICIAL_AUTH_FOMAIN'),'namespace'=>'App\Http\Controllers\Wap'],function(){
-    //避免人工上传授权目录验证文件
-    Route::get('MP_verify_{id}.txt',function ($id){
-        return response($id);
-    });
-
-    Route::any('/user_oauth','Oauth\UsersController@user_oauth');
-    Route::get('/wxAuthCallBack','Oauth\UsersController@wcCallbackParse');
-
-    Route::any('/user_oauth_v2','Oauth\UsersV2Controller@user_oauth');
-    Route::get('/wxAuthCallBackv2','Oauth\UsersV2Controller@wcCallbackParse');
-    Route::get('/wxAuthCallBack2v2','Oauth\UsersV2Controller@wcCallbackParse2');
-});
-
-Route::group(['domain'=>env('HELP_DOMAIN'),'namespace'=>'App\Http\Controllers\Wap\Help'],function(){
-    Route::get('/','HelpController@index');
-    Route::get('/{id}','HelpController@helpDetail');
-    Route::get('/help/getLinkByAppid','HelpController@getLinkByAppid');
-});
-//分享中间页
-Route::group(['domain'=>env('WAP_DOMAIN'),'prefix'=>'api','namespace'=>'App\Http\Controllers\Wap\Book'],function(){//,'middleware'=>'ReadOauth'
-    Route::any('red_book/getLoop','RedBookController@getLoop');
-    Route::any('red_book/getBookDetail','RedBookController@getBookDetail');
-    Route::any('red_book/getChapterList','RedBookController@getChapterList');
-
-});
-
-//分享中间页
-Route::group(['domain'=>env('share_domain'),'namespace'=>'App\Http\Controllers\Wap'],function(){//,'middleware'=>'ReadOauth'
-    Route::get('share/show','Advertises\ShareController@share');
-    Route::any('share/outjump','Advertises\ShareController@ShareJump');
-});
-
-
-
-Route::group(['domain'=>env('FRIEND_LINK_DOMAIN'),'namespace'=>'App\Http\Controllers\Wap'],function(){
-    Route::get('friendlink','User\CoflController@index');
-    Route::get('fl','User\CoflController@index');
-    Route::get('r/{number}','User\CoflController@r')->where('number','\d+');;
-    Route::get('recent','User\CoflController@recent');
-    Route::get('person','User\CoflController@person');
-    Route::get('index','User\CoflController@indexRoot');
-    Route::get('sign','User\CoflController@sign');
-    Route::get('wsign','User\CoflController@sign');
-    Route::get('guidepersonal','User\CoflController@guidePersonal');
-    Route::get('friendactivitylink','User\CoflController@activity');
-    Route::get('freecurrency','User\CoflController@freeCurrencyView');
-    Route::get('fcandt','User\CoflController@freeCurrencyViewForTransfer');
-    Route::get('wfreecurrency','User\CoflController@freeCurrencyView');
-    Route::get('freethrhcurrency','User\CoflController@freeCurrencyView');
-    Route::get('frocefreethrhcurrency','User\CoflController@freeCurrencyView');
-
-    Route::get('customfcl','User\CoflController@freeCurrencyViewOther');
-
-    Route::post('freecurrency','User\CoflController@freeCurrencyPost');
-    Route::get('activity','User\CoflController@longActivity');
-    Route::get('activity/year','User\CoflController@yearActivity');
-    
-    Route::group(['middleware'=>'CrmUser'], function () {
-        Route::get('individualWechat/national_day','IndividualWechat\ActivityController@national');
-        Route::get('crm/entrance','User\CrmBooklistController@index');
-        Route::get('crm/booklist','User\CrmBooklistController@booklist');
-        Route::get('crm/bookshow','User\CrmBooklistController@bookshow');
-    });
-});
-
-Route::group(['domain'=>env('WAP_DOMAIN'),'namespace'=>'App\Http\Controllers\Wap','middleware'=>'EnableCross'],function(){
-
-    Route::any('help','Web\RedirectController@help');
-
-    Route::any('testLogin','Web\RedirectController@testLogin');
-    Route::get('mmdggce','User\UserController@setOrderDelCookie');
-
-    Route::get('pay/wait','Pay\OrdersController@waitPage');
-    Route::get('pay/waitv2','Pay\OrdersController@waitPageV2');
-    Route::get('pay/wait_union','Pay\OrdersController@waitPageUnion');
-
-    Route::get('monthpay/wait','Pay\MonthOrderController@wait');
-    Route::get('api/monthorder/issuccess','Pay\MonthOrderController@issuccess');
-
-    //链接强关跳转地址
-    Route::get('subscribeauth','Subscribe\LinkSubscribeController@index');
-    //枪关链接回调地址
-    Route::get('subauthback','Subscribe\LinkSubscribeController@wxAuthCallBack');
-    //用户行为
-    Route::any('api/userBehavior','User\UserbehaviorController@collectionBehavior');
-    Route::any('subscribe/qrcode','Subscribe\LinkSubscribeController@subscribeView');
-
-    Route::get('advertise/show','Advertises\AdvertiseController@jump');
-    Route::get('advertise/jump','Advertises\AdvertiseController@jumpToSource');
-    Route::any('advertise/recieveUserBehavior','Advertises\AdvertiseController@recieveUserBehavior');
-
-    Route::get('advertise/pdd','Advertises\AdvertiseController@pddAd');
-
-    //crm导粉
-    Route::get('guidestrem','User\CoflController@guidePersonalAccount');
-
-    Route::get('xiyue/sharelink','Other\WeixinController@shareLink');
-
-//web 接口
-    Route::group(['prefix'=>'api'],function () {
-
-        //图书详情
-        Route::get('book/{bid}','Book\BookController@index');
-        Route::get('bookFromWhere/{bid}','Book\BookController@index');
-        //首页
-        Route::get('books/{sex}/index','Book\BookController@getBookLists');
-        //书库
-        Route::get('books/library','Book\BookController@library');
-        //相似推荐
-        Route::get('books/similar','Book\BookController@similarRecom');
-        //阅读完后的推荐
-        Route::get('books/readOverRecommend','Book\BookController@readOverRecommend');
-        //h5书籍智能推荐
-        Route::get('books/H5SmartRecommendBooks','Book\BookController@H5SmartRecommendBooks');
-        //排行榜
-        Route::get('books/rank','Book\BookController@rank');
-        //分类列表
-        Route::get('books/getCategory','Book\BookCategoryController@getCategory');
-        //目录不分页
-        Route::get('books/{bid}/allcatalog','Book\ChapterController@getCatalog');
-
-        //Route::get('books/{bid}/allcatalog/{aa}',function ($tt,$bid,$aa){return $bid.'---'.$aa;});
-        //目录分页
-        Route::get('books/{bid}/catalog','Book\ChapterController@getCatalogPerPage');
-        //章节详情
-        Route::get('books/{bid}/chapters/{cid}','Book\ChapterController@index')->where('cid','\d+');
-        //余额订购
-        Route::get('books/{bid}/balance/chapterOrders/{cid}','Book\ChapterController@pay');
-
-        //Route::get('books/search','Book\BookController@bookSearch');
-
-        //充值列表
-        Route::get('order/chargeList','Order\OrdersController@chargeList');
-        //章节订购列表
-        Route::get('order/chapterOrderList','Order\OrdersController@chapterOrderList');
-        //单本订购列表
-        Route::get('order/bookOrderList','Order\OrdersController@bookOrderList');
-        //充值记录
-        Route::get('order/chargeRecordLists','Order\OrdersController@chargeRecordLists');
-
-        Route::get('order/issuccess','Order\OrdersController@isSuccess');
-
-        //代付列表
-        Route::get('order/substitutePayChargeList','Order\OrdersController@substitutePayChargeList');
-        //代付按钮
-        Route::get('order/substitutePay','Order\OrdersController@substitutePay');
-
-        //用户信息
-        Route::get('userinfo','User\UserController@index');
-        //签到记录
-        Route::get('user/sign_record','User\UserController@signRecord');
-        Route::get('user/sign','User\UserController@sign');
-        //催更
-        Route::get('user/urgeUpdate','User\UserController@urgeUpdate');
-
-        Route::post('bindphone/sendcode','User\UserController@sendCode');
-        Route::post('bindphone/bind','User\UserController@bindPhone');
-
-
-        // 测试登录
-//         Route::any('user/test_add_user_login_cookie','User\UserController@test_add_user_login_cookie');
-        
-//         // 用户免费分享
-//         Route::any('user/FreeShare','User\UserFreeShareController@FreeShare');
-//         // 用户免费分享统计
-//         Route::any('user/add_free_share_visitstat','User\UserFreeShareController@add_free_share_visitstat');
-
-        
-
-        //书架
-        Route::get('userShelfBooks','User\UserShelfBooksController@index');
-        //添加书架
-        Route::post('userShelfBooks','User\UserShelfBooksController@addShelf');
-        //删除书架
-        Route::get('userShelfBooks/delete','User\UserShelfBooksController@delShelf');
-
-        Route::get('userShelfBooks/isonshelf','User\UserShelfBooksController@isOnshelf');
-        //阅读器用户分享统计
-        Route::get('user/recordShare','User\UserController@recordShare');
-        //广告统计
-        Route::post('user/advisitstat','User\UserController@adVisitStat');
-
-        //阅读记录
-        Route::get('readrecord','User\ReadRecordController@index');
-        //添加阅读记录
-        Route::post('readrecord','User\ReadRecordController@addReadRecord');
-        //删除阅读记录
-        Route::get('readrecord/delete','User\ReadRecordController@delReadRecord');
-        //获取强关二维码
-        Route::get('subscribe/qrcode','Subscribe\SubscribeController@getSubscribeQrcode');
-
-        Route::post('error/undefinedCollect','Other\ErrorCollectionController@undefinedCollect');
-        Route::post('activity/getCoupons','User\UserController@getCoupons');
-
-        //章节评论
-        Route::post('chapter/comment','Book\ChapterController@commentChapter');
-        Route::get('chapter/getComment','Book\ChapterController@getChapterComment');
-
-        //书籍送礼
-        Route::get('gift/getGiftsList','Book\BooksGiftController@getAllGiftsV2');
-        Route::post('gift/sendGifts','Book\BooksGiftController@sendGiftsV2');
-        Route::get('gift/getUserSendGiftsRecord','Book\BooksGiftController@getUserSendGiftsRecordV2');
-        Route::get('gift/getUserGiftsConsumeRecord','Book\BooksGiftController@getSendGiftsForConsumeRecord');
-        //分享签名
-        Route::get('chapter/getWechatJsConfig','Book\ChapterController@getWechatJsConfig');
-
-        //投诉
-        Route::get('complaints/getComplaintTags','Other\ComplaintsController@getComplaintTags');
-        Route::post('complaints/add','Other\ComplaintsController@Complaints');
-
-        Route::post('activity/huaweiLottery','Web\ActivityController@huaweiLottery');
-
-        Route::post('weixin/jsSdkConfig','Other\WeixinController@jsSdkConfig');
-
-    });
-
-    //授权回掉
-    Route::get('/oauthCallBack','Oauth\UsersController@wcback');
-
-    Route::get('/oauthCallBackV2','Oauth\UsersController@wcbackV2');
-
-    Route::get('/oauthCallBacktest','Oauth\UsersController@wcbackTest');
-
-    Route::get('/testauth','Web\RedirectController@testauth');
-
-    //退出 测试
-    Route::get('logout','User\UserController@logout');
-
-    Route::get('clearcookie','Web\RedirectController@clearCookie');
-
-    Route::get('custom_msgs_page','Web\RedirectController@customPushtransferPage');
-
-    //需要微信授权
-    Route::group(['middleware'=>'ReadOauth'],function (){
-
-        //推广链接
-        Route::get('yun/{param}','Web\RedirectController@index')->where('param','\d+');
-        //活动推送
-        Route::get('push/activity','Web\WelcomeController@activity');
-        Route::get('sale/activity','Web\WelcomeController@channelActivity');
-        Route::get('sale/springactivity','Web\WelcomeController@springActivity');
-        Route::get('sale/womenactivity','Web\WelcomeController@womenActivity');
-        Route::get('sale/newUserActivity','Web\WelcomeController@newUserActivity');
-
-        Route::get('sale/channelsalenine','Web\WelcomeController@channelActivityNine');
-        Route::get('sale/channelsalesixty','Web\WelcomeController@channelActivitySixtyEight');
-        Route::get('sale/channelsaleforty','Web\WelcomeController@channelActivityFortyEight');
-        Route::get('sale/channelnewuser','Web\WelcomeController@channelNewUserActivity');
-        Route::get('sale/qingMingActivity','Web\WelcomeController@qingMingActivity');
-        Route::get('sale/readDayActivity','Web\WelcomeController@readDayActivity');
-        Route::get('sale/laborDayActivity','Web\WelcomeController@readDayActivity');
-        Route::get('sale/yearActivity','Web\WelcomeController@yearActivity');
-        Route::get('sale','Web\WelcomeController@commonActivity');
-        Route::get('sale/summer','Web\WelcomeController@summerActivity');
-        Route::get('sale/newUserSale','Web\WelcomeController@newUserSale');
-        Route::get('sale/nationday','Web\ActivityController@nationDayActivity');
-        Route::get('sale/chongyang','Web\WelcomeController@cYactivity');
-        Route::get('sale/newUserForeverActivity','Web\WelcomeController@newUserForeverActivity');
-        Route::get('sale/tempC','Web\WelcomeController@tempC');
-
-        Route::get('sale/seYearActivity','Web\WelcomeController@otherYearActivity');
-        Route::get('sale/shoppingDayActivity','Web\WelcomeController@shoppingDayActivity');
-        Route::get('sale/thanksGivingDayActivity','Web\WelcomeController@thanksGivingDayActivity');
-
-        Route::get('sale/greatcold','Web\WelcomeController@greatColdActivity');
-        Route::get('sale/vernalequinox','Web\WelcomeController@vernalEquinoxActivity');
-        Route::get('sale/customvip','Web\WelcomeController@channelYearActivity');
-
-        Route::get('activity/channel','Web\ActivityController@channelCustomActivity');
-        Route::get('activity/common','Web\ActivityController@commonActivity');
-        Route::get('activity/crm','Web\ActivityController@crmActivity');
-
-        Route::get('activity/huaweiLottery','Web\ActivityController@huaweiLotteryView');
-        Route::get('activity/huaweiActivity','Web\ActivityController@huaweiActivity');
-        Route::get('activity/summerholiday','Web\ActivityController@summerHoliday');
-
-        Route::get('activity/ragonBoatActivity','Web\ActivityController@ragonBoatActivity');
-        Route::get('activity/crmy','Web\ActivityController@crmYearActivity');
-
-        Route::get('welfare/giving','Web\WelfareController@giving');
-
-        //继续阅读
-        Route::get('continue','User\ReadRecordController@latestRead');
-        //分享站内跳转
-        Route::any('share/jump','Advertises\ShareController@jump');
-        //用户强关信息页
-        Route::any('subscribe/getFromUser','Subscribe\SubscribeController@getFromUser');
-
-        Route::get('sign','User\UserController@signV3');
-        Route::get('signV3','User\UserController@signV3');
-
-        Route::get('force/guide','User\UserController@guidePersonalAccount');
-
-        //绑定手机号码
-        //Route::get('bindphone/bind','User\UserController@bindPhoneView');
-
-        //随机每日领5-15书币
-        Route::get('randSign','User\UserController@day_rand_sign');
-
-        Route::get('rmonthpay','Order\OrdersController@monthPay');
-        Route::get('crmpush','User\UserController@crmPushView');
-        //crm导粉
-        Route::get('guidestremv2','Crm\CrmGuideFansController@activeGuidePersonalAccountV2');
-
-        Route::any('{slug}','Web\WelcomeController@index')->where('slug', '(.*)?');
-
-    });
-
-});
-

+ 0 - 32
app/Http/Routes/WapBrower/WebRoutes.php

@@ -1,32 +0,0 @@
-<?php
-/**
- * Created by PhpStorm.
- * User: z-yang
- * Date: 2018/5/21
- * Time: 10:43
- */
-
-Route::group(['domain'=>env('WAP_BROWER_DOMAIN'),'namespace'=>'App\Http\Controllers\WapBrowser'],function(){
-    Route::get('test','Chapter\ChapterController@test');
-    //reader
-    Route::get('reader','Chapter\ChapterController@index');
-    //强管
-    Route::get('subscribe','Subscribe\SubscribeController@subscribeView');
-
-    //书库
-    Route::get('stock','BookController@stock');
-    Route::get('search','BookController@search');
-    //书城
-    Route::get('/api/books/library','BookController@library');
-
-    //详情
-    Route::get('detail','Chapter\ChapterController@bookDetail');
-    //目录
-    Route::get('catalog','Chapter\ChapterController@catalog');
-    Route::get('apiCatalog','Chapter\ChapterController@apiCatalog');
-    //首页
-    Route::get('/','Index\IndexController@index');
-    //派单
-    Route::get('yun/{param}','Index\IndexController@yun')->where('param','\d+');
-
-});

+ 0 - 245
app/Http/Routes/Wechat/OfficialAccountRoutes.php

@@ -1,245 +0,0 @@
-<?php
-
-Route::group(['domain'=>env('WECHAT_DOMAIN'),'namespace'=>'App\Http\Controllers\Wechat\OfficialAccount','prefix'=>'api'],function(){
-
-    /**
-    * 公众号
-    */
-
-	//获取公众号信息
-    Route::any('OfficialAccount/officialAccountByAppid', 'OfficialAccountsController@officialAccountByAppid');
-
-    //获取一个可分发的公众号
-    Route::any('OfficialAccount/canUseOfficialAccountByChannelId', 'OfficialAccountsController@canUseOfficialAccountByChannelId');
-
-    //授权公众号信息
-    Route::any('OfficialAccount/authOfficialAccount','OfficialAccountsController@authOfficialAccount');
-
-    //分销渠道ID获取服务号列表
-    Route::any('OfficialAccount/allOfficialAccountBydistributionChannelId','OfficialAccountsController@allOfficialAccountBydistributionChannelId');
-
-    //根据分销渠道ID获取授权公众号
-    Route::any('OfficialAccount/officialAuthAccountBydistributionChannelId', 'OfficialAccountsController@officialAuthAccountBydistributionChannelId');
-
-    //取消公众号授权
-    Route::any('OfficialAccount/cancelAuthOfficialAccount','OfficialAccountsController@cancelAuthOfficialAccount');
-
-    //服务号授权返回URL
-    Route::any('OfficialAccount/officialAccountAuthUrl','OfficialAccountsController@officialAccountAuthUrl');
-
-    //更新公众号信息
-    Route::any('OfficialAccount/updateOfficialAccount','OfficialAccountsController@updateOfficialAccount');
-
-    //测试发送客服消息
-    Route::any('OfficialAccount/sendNewsTask','WechatCustomerMsgController@sendNewsTask');
-    
-    /**
-     * 监控
-     */
-    // 检查公众号是否被封
-    Route::any('OfficialAccount/check_gzh_ban', 'OfficialAccountService@check_gzh_ban');
-
-    /**
-    * 模板消息
-    */
-
-    //获取模板
-    Route::any('OfficialAccount/wechatTemplateByAppidAndCommonTemplateId', 'WechatTemplatesController@wechatTemplateByAppidAndCommonTemplateId');
-
-    //通过ID获取模板
-    Route::any('OfficialAccount/wechatTemplateMsgById', 'WechatTemplatesController@wechatTemplateMsgById');
-
-    //更新模板消息任务状态
-    Route::any('OfficialAccount/updateWechatTemplateStatus', 'WechatTemplatesController@updateWechatTemplateStatus');
-
-    //更新模板消息
-    Route::any('OfficialAccount/updateWechatTemplateMsgById', 'WechatTemplatesController@updateWechatTemplateMsgById');
-
-    //得到要开通的模板消息列表
-    Route::any('OfficialAccount/wechatPublicTemplateList', 'WechatPublicTemplatesController@wechatPublicTemplateList');
-
-    //保存模板
-    Route::any('OfficialAccount/addWechatTemplate', 'WechatTemplatesController@addWechatTemplate');
-
-    //更新模板
-    Route::any('OfficialAccount/updateWechatTemplateId', 'WechatTemplatesController@updateWechatTemplateId');
-    
-    //发送模板消息
-    Route::any('OfficialAccount/addWechatTemplateMsg', 'WechatTemplatesController@addWechatTemplateMsg');
-
-    //测试发送模板消息
-    Route::any('OfficialAccount/testAddWechatTemplateMsg', 'WechatTemplatesController@testAddWechatTemplateMsg');
-
-    //测试RebitMQ
-    Route::any('OfficialAccount/testRebitMQ', 'WechatTemplatesController@testRebitMQ');
-
-    //个人测试发送模板消息
-    Route::any('OfficialAccount/test_add_template_task', 'WechatTemplatesController@test_add_template_task');
-
-    //获取推送模板消息
-    Route::any('OfficialAccount/wechatTemplateMsgBydistributionChannelId', 'WechatTemplatesController@wechatTemplateMsgBydistributionChannelId');
-
-
-
-    /**
-    * 强制关注用户
-    */
-
-    //获取强关用户
-    Route::any('OfficialAccount/forceSubscribeUsersByAppidAndOpenId', 'ForceSubscribeUsersController@forceSubscribeUsersByAppidAndOpenId');   
-
-    //appid获取强关用户
-    Route::any('OfficialAccount/forceSubscribeUsersByUid', 'ForceSubscribeUsersController@forceSubscribeUsersByUid');
-
-    //添加强关用户
-    Route::any('OfficialAccount/addForceSubscribeUsers', 'ForceSubscribeUsersController@addForceSubscribeUsers');
-
-    //强制关注用户
-    Route::any('OfficialAccount/subscribeForceSubscribeUsers', 'ForceSubscribeUsersController@subscribeForceSubscribeUsers');
-
-    //取消强制关注
-    Route::any('OfficialAccount/cancelForceSubscribeUsers', 'ForceSubscribeUsersController@cancelForceSubscribeUsers');
-
-    //强关用户与公众号最后交互时间更新
-    Route::any('OfficialAccount/forceSubscribeUsersLastTimeUpdate', 'ForceSubscribeUsersController@forceSubscribeUsersLastTimeUpdate');
-    
-    //强关用户uid强制更新
-    Route::any('OfficialAccount/updateForceSubscribeUsers', 'ForceSubscribeUsersController@updateForceSubscribeUsers');
-    
-    //根据渠道号,appid,openid获取用户
-    Route::any('OfficialAccount/forceSubscribeUsersByOpenidAndAppidAndChannelId', 'ForceSubscribeUsersController@forceSubscribeUsersByOpenidAndAppidAndChannelId');
-    
-    //根据渠道号,openid获取素材强关映射
-    Route::any('OfficialAccount/getMaterialForceSubscribeMapping', 'ForceSubscribeUsersController@getMaterialForceSubscribeMapping');
-    
-    
-    
-    /**
-    * ----------------------方法测试--START
-    */
-    //测试queue延迟队列
-    Route::any('OfficialAccount/testGetSimpleChannelBook', 'WechatTestController@getSimpleChannelBook');
-
-    //测试queue延迟队列
-    Route::any('OfficialAccount/testSendMailDelayQueue', 'WechatTestController@testSendMailDelayQueue');
-
-    //从redis中取officialaccount或者从数据库中取并更新redis
-    Route::any('OfficialAccount/officialAccountByAppidTestRedis', 'WechatTestController@officialAccountByAppidTestRedis');
-
-    //同步official_account表至Redis
-    Route::any('OfficialAccount/updateOfficialToRedisTest', 'WechatTestController@updateOfficialToRedisTest');
-
-    //从Redis中取一个公众号
-    Route::any('OfficialAccount/getOfficialFromRedis', 'WechatTestController@getOfficialFromRedis');
-
-    //从Redis中取一个客服消息
-    Route::any('OfficialAccount/getWechatMsgRedis', 'WechatTestController@getWechatMsgRedis');
-
-    //sign参数
-    Route::any('OfficialAccount/getSignTest', 'WechatTestController@getSignTest');
-    
-    //获取渠道下所有的服务号信息
-    Route::any('OfficialAccount/allOfficialAccountBydistributionChannelId', 'WechatTestController@allOfficialAccountBydistributionChannelId');
-
-    //获取渠道的服务号数量
-    Route::any('OfficialAccount/officialAccountCountByChannelId', 'WechatTestController@officialAccountCountByChannelId');
-    
-    //通过渠道号和时间获取用户数
-    Route::any('OfficialAccount/forceSubscribeUserCountByChannelIdAndDate', 'ForceSubscribeUsersController@forceSubscribeUserCountByChannelIdAndDate');
-
-    //通过渠道号和时间获取用户数
-    Route::any('OfficialAccount/customerImgUrlByChannelId', 'ForceSubscribeUsersController@customerImgUrlByChannelId');
-
-    //测试上传客服图片
-    Route::any('OfficialAccount/uploadCustomerImgUrl', 'ForceSubscribeUsersController@uploadCustomerImgUrl');
-
-    //根据模板消息参数获取用户
-    Route::any('OfficialAccount/forceUserCountByTemplatePrams', 'WechatTemplatesController@forceUserCountByTemplatePrams');
-
-    //测试文字格式的模板消息发送
-    Route::any('OfficialAccount/testSendTextOfficialMsg', 'ForceSubscribeUsersController@testSendTextOfficialMsg');
-
-    //客服消息开关设置
-    Route::any('OfficialAccount/customMsgSwitchSetting', 'WechatCustomerMsgController@customMsgSwitchSetting');
-    //客服消息开关列表获取
-    Route::any('OfficialAccount/customMsgSwitchsByChannelId', 'WechatCustomerMsgController@customMsgSwitchsByChannelId');
-    //获取指定客服消息开关
-    Route::any('OfficialAccount/customMsgSwitchsByChannelCate', 'WechatCustomerMsgController@customMsgSwitchsByChannelCate');
-
-    //发送客服消息列表获取
-    Route::any('OfficialAccount/customSendMsgsByChannelId', 'WechatCustomerMsgController@customSendMsgsByChannelId');
-    //通过ID获取指定客服消息
-    Route::any('OfficialAccount/customSendMsgsById', 'WechatCustomerMsgController@customSendMsgsById');
-    //停止发送客服消息
-    Route::any('OfficialAccount/stopCustomSendMsgsById', 'WechatCustomerMsgController@stopCustomSendMsgsById');
-    //发送客服消息
-    Route::any('OfficialAccount/addCustomSendMsgs', 'WechatCustomerMsgController@addCustomSendMsgs');
-    //测试发送客服消息
-    Route::any('OfficialAccount/testAddCustomSendMsgs', 'WechatCustomerMsgController@testAddCustomSendMsgs');
-
-
-    //测试3天回本12小时推送
-    Route::any('OfficialAccount/add_news_recovery_push_hot', 'WechatCustomerMsgController@add_news_recovery_push_hot');
-    //测试3天回本24小时推送
-    Route::any('OfficialAccount/add_news_recovery_push_activity', 'WechatCustomerMsgController@add_news_recovery_push_activity');
-    //测试发送定制书籍推送
-    Route::any('OfficialAccount/add_news_point_book', 'WechatCustomerMsgController@add_news_point_book');
-    //测试发送热门书籍推送
-    Route::any('OfficialAccount/add_news_top_book', 'WechatCustomerMsgController@add_news_top_book');
-    //测试发送未支付订单提醒
-    Route::any('OfficialAccount/sendUnpaid', 'WechatCustomerMsgController@sendUnpaid');
-    
-    //支付用户每日推送提醒
-    Route::any('OfficialAccount/add_template_pay_push_daily', 'WechatCustomerMsgController@add_template_pay_push_daily');
-
-    //获取每日智能推送分析数据
-    Route::any('OfficialAccount/customSendDayStatsByChannelAndFrom', 'CustomSendStatsController@customSendDayStatsByChannelAndFrom');
-    //获取渠道下智能推送分析总数据
-    Route::any('OfficialAccount/customSendStatsByChannelAndFrom', 'CustomSendStatsController@customSendStatsByChannelAndFrom');
-
-
-    //获取所有图库素材
-    Route::any('OfficialAccount/getAllDocumentCovers', 'WechatCustomerMsgController@getAllDocumentCovers');
-    //获取所有标签文案
-    Route::any('OfficialAccount/getAllHeadlinesNoType', 'WechatCustomerMsgController@getAllHeadlinesNoType');
-
-    //获取图文链接
-    Route::any('OfficialAccount/createOfficialImgTxtUrl', 'WechatCustomerMsgController@createOfficialImgTxtUrl');
-    //获取图文链接列表
-    Route::any('OfficialAccount/getOfficialImgTxtUrl', 'WechatCustomerMsgController@getOfficialImgTxtUrl');
-    
-    /**
-    * ----------------------方法测试--END
-    */
-
-    /**
-    * 公众号事件交互
-    */
-
-    //公众号事件交互
-    Route::any('OfficialAccount/officialInteractiveEventFeedback', 'OfficialInteractiveEventController@officialInteractiveEventFeedback');
-
-
-
-    /**
-    * 公众号菜单
-    */
-
-    //获取菜单列表
-    Route::any('OfficialAccount/officialMenusList', 'OfficialMenusController@officialMenusList');
-
-
-
-    /**
-    * 分销用户
-    */
-
-    //保存公众号用户
-    Route::any('OfficialAccount/saveOfficialWechatUser', 'OfficialWechatUserController@saveOfficialWechatUser');
-
-    //获取公众号用户
-    Route::any('OfficialAccount/getOfficialWechatUser', 'OfficialWechatUserController@getOfficialWechatUser');
-
-
-
-});

+ 0 - 87
app/Http/Routes/Xchengxu/XchengxuRoutes.php

@@ -1,87 +0,0 @@
-<?php
-/**
- * 图书路由文件
- * by zhanyang
- *
- * 2017-12-05
- */
-
-
-Route::group(['domain'=>env('XCHENGXU_DOMAIN'),'namespace'=>'App\Http\Controllers\Xchengxu'],function(){
-    //支付会掉
-    Route::any('pay/wcback_xchengxu','Order\OrdersController@wcback_official');
-    Route::any('help','Web\RedirectController@help');
-    Route::post('api/login','Oauth\UsersController@index');
-    Route::get('api/RefreshToken','Oauth\UsersController@RefreshToken');
-    //支付回调
-    Route::any('pay/wcback','Order\OrdersController@wxback_xcx');
-    Route::group(['prefix'=>'api'],function (){
-        //首页
-        Route::get('books/{sex}/index','Book\BookController@getBookLists');
-        //书库
-        Route::get('books/library','Book\BookController@library');
-        //相似推荐
-        Route::get('books/similar','Book\BookController@similarRecom');
-        //阅读完后的推荐
-        Route::get('books/readOverRecommend','Book\BookController@readOverRecommend');
-        //排行榜
-        Route::get('books/rank','Book\BookController@rank');
-        //分类列表
-        Route::get('books/getCategory','Book\BookCategoryController@getCategory');
-    });
-    //web 接口  jwttoken
-    Route::group(['prefix'=>'api','middleware'=>'jwttoken'],function () {
-
-        //图书详情
-        Route::get('book/{bid}','Book\BookController@index');
-
-        //目录不分页
-        Route::get('books/{bid}/allcatalog','Book\ChapterController@getCatalog');
-
-        //Route::get('books/{bid}/allcatalog/{aa}',function ($tt,$bid,$aa){return $bid.'---'.$aa;});
-        //目录分页
-        Route::get('books/{bid}/catalog','Book\ChapterController@getCatalogPerPage');
-        //章节详情
-        Route::get('books/{bid}/chapters/{cid}','Book\ChapterController@index')->where('cid','\d+');
-        //余额订购
-        Route::get('books/{bid}/balance/chapterOrders/{cid}','Book\ChapterController@pay');
-
-        //Route::get('books/search','Book\BookController@bookSearch');
-
-        //充值列表
-        Route::get('order/chargeList','Order\OrdersController@chargeList');
-        //章节订购列表
-        Route::get('order/chapterOrderList','Order\OrdersController@chapterOrderList');
-        //单本订购列表
-        Route::get('order/bookOrderList','Order\OrdersController@bookOrderList');
-        //充值记录
-        Route::get('order/chargeRecordLists','Order\OrdersController@chargeRecordLists');
-
-        Route::get('order/issuccess','Order\OrdersController@isSuccess');
-        Route::get('userinfo','User\UserController@index');
-
-
-        //书架
-        Route::get('userShelfBooks','User\UserShelfBooksController@index');
-        //添加书架
-        Route::post('userShelfBooks','User\UserShelfBooksController@addShelf');
-        //删除书架
-        Route::get('userShelfBooks/delete','User\UserShelfBooksController@delShelf');
-
-        Route::get('userShelfBooks/isonshelf','User\UserShelfBooksController@isOnshelf');
-
-        //阅读记录
-        Route::get('readrecord','User\ReadRecordController@index');
-        //添加阅读记录
-        Route::post('readrecord','User\ReadRecordController@addReadRecord');
-        //删除阅读记录
-        Route::get('readrecord/delete','User\ReadRecordController@delReadRecord');
-
-        //支付
-        Route::get('goToPay','Order\OrdersController@wxindex');
-
-        Route::get('checkOrder','Order\OrdersController@checkOrder');
-    });
-
-});
-

+ 18 - 0
app/Modules/BaseService.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Modules;
+
+
+trait BaseService
+{
+    private static $_instance;
+
+    public function __callStatic($name, $arguments)
+    {
+        $name = preg_replace('/Static$/', '', $name);
+        if (self::$_instance == null) {
+            self::$_instance = new self;
+        }
+        return self::$_instance->$name(...$arguments);
+    }
+}

+ 93 - 73
app/Modules/Book/Services/BookService.php

@@ -4,18 +4,24 @@ namespace App\Modules\Book\Services;
 
 use App\Modules\Book\Models\Book;
 use App\Modules\Book\Models\Chapter;
-use GuzzleHttp\Client;
+use Hashids;
 use Redis;
 use App\Modules\Statistic\Services\WapVisitStatService;
 use DB;
 
 class BookService
 {
+    public function decodeBid(string $bid)
+    {
+        return Hashids::decode($bid)[0];
+    }
+
     /**
      * 获取book中存在的分类信息id
      * @return array
      */
-    public static  function getCategoryId(){
+    public static  function getCategoryId()
+    {
         return Book::getCategoryId();
     }
 
@@ -26,8 +32,9 @@ class BookService
      * @return mixed
      *
      */
-    public  static function  updateIntro($bid,$intro){
-        return Book::where('id',$bid)->update(['intro'=>$intro]);
+    public  static function  updateIntro($bid, $intro)
+    {
+        return Book::where('id', $bid)->update(['intro' => $intro]);
     }
 
     /**
@@ -35,12 +42,11 @@ class BookService
      * @param int $channel_id
      * @param int $price
      */
-    public static function setChapterPrice(int $channel_id,float $price):void{
-        try{
-            Redis::hset('book_chapter_price',$channel_id,$price);
-        }catch (\Exception $e){
-
-        }
+    public static function setChapterPrice(int $channel_id, float $price): void
+    {
+        try {
+            Redis::hset('book_chapter_price', $channel_id, $price);
+        } catch (\Exception $e) { }
     }
 
     /**
@@ -48,98 +54,112 @@ class BookService
      * @param int $channel_id
      * @return int
      */
-    public static function getChapterPrice(int $channel_id){
-        try{
-            $fee =  Redis::hget('book_chapter_price',$channel_id);
-            if($fee)
-                return (float)$fee;
+    public static function getChapterPrice(int $channel_id)
+    {
+        try {
+            $fee =  Redis::hget('book_chapter_price', $channel_id);
+            if ($fee)
+                return (float) $fee;
             else
                 return 0;
-        }catch (\Exception $e){
-
-        }
+        } catch (\Exception $e) { }
         return 0;
     }
-    
-    public static function getBookStatistics($smart_push_books){
-    	// 获取书籍统计数据
-    	if(!empty($smart_push_books)){
-    		foreach($smart_push_books as $key=>$smart_push_book){
-    			$book_statistics = WapVisitStatService::smartPushTestBookStats($smart_push_book->bid);
-    			$smart_push_books[$key]->uv = $book_statistics['uv'];
-    			$smart_push_books[$key]->pv = $book_statistics['pv'];
-    			$smart_push_books[$key]->charge_amount = $book_statistics['charge_amount'];
-    			$smart_push_books[$key]->charge_user_num = $book_statistics['charge_user_num'];
-    			$smart_push_books[$key]->book_amount = $book_statistics['book_amount'];
-    			$smart_push_books[$key]->book_user_num = $book_statistics['book_user_num'];
-    			$smart_push_books[$key]->real_push_user_num = $book_statistics['real_push_user_num'];
-    			$smart_push_books[$key]->second_chapter_uv = $book_statistics['second_chapter_uv'];
-    		}
-    	}
-    	return $smart_push_books;
+
+    public static function getBookStatistics($smart_push_books)
+    {
+        // 获取书籍统计数据
+        if (!empty($smart_push_books)) {
+            foreach ($smart_push_books as $key => $smart_push_book) {
+                $book_statistics = WapVisitStatService::smartPushTestBookStats($smart_push_book->bid);
+                $smart_push_books[$key]->uv = $book_statistics['uv'];
+                $smart_push_books[$key]->pv = $book_statistics['pv'];
+                $smart_push_books[$key]->charge_amount = $book_statistics['charge_amount'];
+                $smart_push_books[$key]->charge_user_num = $book_statistics['charge_user_num'];
+                $smart_push_books[$key]->book_amount = $book_statistics['book_amount'];
+                $smart_push_books[$key]->book_user_num = $book_statistics['book_user_num'];
+                $smart_push_books[$key]->real_push_user_num = $book_statistics['real_push_user_num'];
+                $smart_push_books[$key]->second_chapter_uv = $book_statistics['second_chapter_uv'];
+            }
+        }
+        return $smart_push_books;
     }
 
-    public static function getBookById($bid){
+    public static function getBookById($bid)
+    {
         return Book::find($bid);
     }
 
-    public static function newYunQiBook($bid){
-        $old = DB::table('book_yunqi')->where('yq_bid',$bid)->where('type','NEW_YUNQI')->first();
-        if($old){
+    public static function newYunQiBook($bid)
+    {
+        $old = DB::table('book_yunqi')->where('yq_bid', $bid)->where('type', 'NEW_YUNQI')->first();
+        if ($old) {
             return -1;
         }
         $new_yunqi_book = DB::connection('new_yunqi')
             ->table('books')
-            ->join('book_configs','books.id','=','book_configs.bid')
+            ->join('book_configs', 'books.id', '=', 'book_configs.bid')
             ->select(
-                'books.id','books.author','books.author','books.intro','books.category_name','books.keyword',
-                'books.status','books.chapter_count','books.size','books.last_chapter','book_configs.book_name as name',
-                'book_configs.cover','book_configs.force_subscribe_chapter_seq','book_configs.charge_type','book_configs.roles'
+                'books.id',
+                'books.author',
+                'books.author',
+                'books.intro',
+                'books.category_name',
+                'books.keyword',
+                'books.status',
+                'books.chapter_count',
+                'books.size',
+                'books.last_chapter',
+                'book_configs.book_name as name',
+                'book_configs.cover',
+                'book_configs.force_subscribe_chapter_seq',
+                'book_configs.charge_type',
+                'book_configs.roles'
             )
-            ->where('books.id',$bid)
+            ->where('books.id', $bid)
             ->first();
-        if(!$new_yunqi_book){
+        if (!$new_yunqi_book) {
             return -2;
         }
 
         $book = Book::create(
             [
-                'ly_bid'=>0,'name'=>$new_yunqi_book->name,'author'=>$new_yunqi_book->author,'intro'=>$new_yunqi_book->intro,'cover'=>$new_yunqi_book->cover,
-                'category_name'=>$new_yunqi_book->category_name,'keyword'=>$new_yunqi_book->keyword,'category_id'=>0,'status'=>$new_yunqi_book->status,
-                'chapter_count'=>$new_yunqi_book->chapter_count,'first_cid'=>0,'last_cid'=>0,'size'=>$new_yunqi_book->size,'last_chapter'=>$new_yunqi_book->last_chapter,
-                'sequence'=>0,'yq_bid'=>$bid
+                'ly_bid' => 0, 'name' => $new_yunqi_book->name, 'author' => $new_yunqi_book->author, 'intro' => $new_yunqi_book->intro, 'cover' => $new_yunqi_book->cover,
+                'category_name' => $new_yunqi_book->category_name, 'keyword' => $new_yunqi_book->keyword, 'category_id' => 0, 'status' => $new_yunqi_book->status,
+                'chapter_count' => $new_yunqi_book->chapter_count, 'first_cid' => 0, 'last_cid' => 0, 'size' => $new_yunqi_book->size, 'last_chapter' => $new_yunqi_book->last_chapter,
+                'sequence' => 0, 'yq_bid' => $bid
             ]
         );
         DB::table('book_yunqi')->insert([
-            'bid'=>$book->id,
-            'yq_bid'=>$bid,
-            'type'=>'NEW_YUNQI',
-            'created_at'=>date('Y-m-d H:i:s'),
-            'updated_at'=>date('Y-m-d H:i:s')
+            'bid' => $book->id,
+            'yq_bid' => $bid,
+            'type' => 'NEW_YUNQI',
+            'created_at' => date('Y-m-d H:i:s'),
+            'updated_at' => date('Y-m-d H:i:s')
         ]);
-        for ($i = 1;$i<=$new_yunqi_book->chapter_count;$i++){
-            $temp = DB::connection('new_yunqi')->table('chapters')->where('bid',$bid)->where('sequence',$i)->select('name','content','is_vip','size')->first();
-            if($temp){
+        for ($i = 1; $i <= $new_yunqi_book->chapter_count; $i++) {
+            $temp = DB::connection('new_yunqi')->table('chapters')->where('bid', $bid)->where('sequence', $i)->select('name', 'content', 'is_vip', 'size')->first();
+            if ($temp) {
                 Chapter::create([
-                    'bid'=>$book->id,
-                    'name'=>$temp->name,
-                    'sequence'=>$i,
-                    'is_vip'=>$temp->is_vip,
-                    'size'=>$temp->size,
-                    'prev_cid'=>0,
-                    'next_cid'=>0,
-                    'recent_update_at'=>date('Y-m-d H:i:s'),
-                    'content'=>$temp->content,
-                    'ly_chapter_id'=>0
+                    'bid' => $book->id,
+                    'name' => $temp->name,
+                    'sequence' => $i,
+                    'is_vip' => $temp->is_vip,
+                    'size' => $temp->size,
+                    'prev_cid' => 0,
+                    'next_cid' => 0,
+                    'recent_update_at' => date('Y-m-d H:i:s'),
+                    'content' => $temp->content,
+                    'ly_chapter_id' => 0
                 ]);
             }
         }
-        \Artisan::call('book:afs',['bid'=>[$book->id]]);
-        DB::table('book_configs')->where('bid',$book->id)->update([
-            'force_subscribe_chapter_seq'=>$new_yunqi_book->force_subscribe_chapter_seq,
-            'roles'=>$new_yunqi_book->roles,
-            'charge_type'=>$new_yunqi_book->charge_type,
-            'cp_source'=>'new_yunqi'
+        \Artisan::call('book:afs', ['bid' => [$book->id]]);
+        DB::table('book_configs')->where('bid', $book->id)->update([
+            'force_subscribe_chapter_seq' => $new_yunqi_book->force_subscribe_chapter_seq,
+            'roles' => $new_yunqi_book->roles,
+            'charge_type' => $new_yunqi_book->charge_type,
+            'cp_source' => 'new_yunqi'
         ]);
         return 0;
     }

+ 11 - 0
app/Modules/User/Models/QappUser.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace App\Modules\User\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class QappUser extends Model
+{
+    protected $table = 'qapp_users';
+    protected $fillable = ['uid', 'device_no', 'device_info', 'phone'];
+}

+ 101 - 0
app/Modules/User/Services/QappUserService.php

@@ -0,0 +1,101 @@
+<?php
+
+
+namespace App\Modules\User\Services;
+
+use App\Consts\SysConsts;
+use App\Modules\BaseService;
+use App\Modules\User\Models\QappUser;
+use App\Modules\User\Models\User;
+use DB;
+use Exception;
+use Tymon\JWTAuth\Facades\JWTAuth;
+
+/**
+ * 
+ * @method static \App\Modules\User\Models\QappUser loginStatic(array $data) 快应用用户登录
+ */
+class QappUserService
+{
+    use BaseService;
+
+    /**
+     * 获取快应用用户
+     */
+    public function login(array $data)
+    {
+        $device_no = $data['device_no'];
+        $qapp_user = $this->getQAppUserByDeviceNo($device_no);
+        if (!$qapp_user) {
+            $qapp_user = $this->createQuickAppUser($data);
+        }
+        $user = $qapp_user->user;
+        $uid = $user->id;
+        $time = time() + SysConsts::ONE_HOUR_SECONDS * 2;
+        $token = JWTAuth::fromUser($user);
+        return compact('token', 'time', 'uid');
+    }
+
+    /**
+     * 根据设备号获取快应用用户信息
+     */
+    public function getQAppUserByDeviceNo(string $device_no)
+    {
+        $qapp_user = QappUser::where('device_no', $device_no)->first();
+        if ($qapp_user) {
+            $user = User::find($qapp_user->uid);
+            $qapp_user->user = $user;
+        }
+        return $qapp_user;
+    }
+
+    /**
+     * 根据uid获取快应用用户信息
+     */
+    public function getQAppUserByUid(int $uid)
+    {
+        $qapp_user = QappUser::where('uid', $uid)->first();
+        if ($qapp_user) {
+            $user = User::find($uid);
+            $qapp_user->user = $user;
+        }
+        return $qapp_user;
+    }
+
+
+    /**
+     * 创建快应用用户信息
+     */
+    public function createQuickAppUser(array $data)
+    {
+        try {
+            DB::beginTransaction();
+            $user = $this->createUser($data);
+            $uid = $user->id;
+            $device_no = $data['device_no'];
+            $data = compact('device_info');
+            $unique_key = compact('device_no', 'uid');
+            $qapp_user = QappUser::firstOrCreate($unique_key, $data);
+            $qapp_user->user = $user;
+            DB::commit();
+            return $qapp_user;
+        } catch (Exception $e) {
+            myLog('create_user')->error($e->getMessage());
+        }
+    }
+
+    /**
+     * 创建用户
+     */
+    private function createUser(array $data)
+    {
+        $openid = $data['device_no'];
+        $unionid = $data['device_no'];
+        $register_ip = _getIp();
+        $distribution_channel_id = 0;
+        $send_order_id = $data['send_order_id'];
+        $unique_key = compact('unionid', 'distribution_channel_id');
+        $data = compact('openid', 'register_ip', 'send_order_id');
+        return User::firstOrCreate($unique_key, $data);
+    }
+}

+ 4 - 49
app/Providers/RouteServiceProvider.php

@@ -35,63 +35,18 @@ class RouteServiceProvider extends ServiceProvider
      */
     public function map(Router $router)
     {
-        $this->mapOfficialAccountRoutes($router);
-        $this->mapWapRoutes($router);
-        $this->mapWebRoutes($router);
-        $this->mapXchengyuRoutes($router);
+        $this->mapQuickAppRoutes($router);
     }
 
-
-
-    protected function mapOfficialAccountRoutes(Router $router)
-    {
-        $router->group([
-            'middleware' => 'web',
-        ], function ($router) {
-            require app_path('Http/Routes/Wap/OfficialAccountRoutes.php');
-        });
-        $router->group([
-            'middleware' => 'wap',
-        ], function ($router) {
-            require app_path('Http/Routes/Wechat/OfficialAccountRoutes.php');
-        });
-    }
-
-
     /**
-     * h5路由
-     * @param Router $router
+     * 快应用
      */
-    protected function mapWapRoutes(Router $router)
+    protected function mapQuickAppRoutes(Router $router)
     {
         $router->group([
-            'middleware' => 'wap',
-        ], function ($router) {
-            require app_path('Http/Routes/Wap/WapRoutes.php');
-        });
-    }
-
-    //UI界面
-    protected function mapWebRoutes(Router $router)
-
-    {
-        $router->group([
-            'middleware' => 'web',
-        ], function ($router) {
-            require app_path('Http/Routes/WapBrower/WebRoutes.php');
-
-        });
-    }
-
-    /**
-     * 小程序路由
-     * @param Router $router
-     */
-    protected function mapXchengyuRoutes(Router $router){
-        $router->group([
             'middleware' => 'web',
         ], function ($router) {
-            require app_path('Http/Routes/Xchengxu/XchengxuRoutes.php');
+            require app_path('Http/Routes/QuickApp/QuickAppRoutes.php');
         });
     }
 

+ 186 - 164
config/error.php

@@ -1,194 +1,216 @@
 <?php
 
 return [
-
-
-        'NOT_EXIST'=>['code'=>1001,'msg'=>'记录不存在'],
-        'PARAM_EMPTY'=>['code'=>1002,'msg'=>'参数不能为空'],
-        'PARAM_ERROR'=>['code'=>1003,'msg'=>'参数错误'],
-        'TASK_IN_PROGRESS'=>['code'=>1004,'msg'=>'任务执行中'],
-        'BALANCE_NOT_ENOUGH'=>['code'=>2001,'msg'=>'账户余额不足'],
-        'NOT_CHECKED'=>['code'=>6001,'msg'=>'还没审核通过'],
-        'ALREADY_CHECKED'=>['code'=>6002,'msg'=>'已审核通过'],
-        'ALREADY_PUSHED'=>['code'=>6003,'msg'=>'已推送'],
+        'NOT_EXIST' => ['code' => 1001, 'msg' => '记录不存在'],
+        'PARAM_EMPTY' => ['code' => 1002, 'msg' => '参数不能为空'],
+        'PARAM_ERROR' => ['code' => 1003, 'msg' => '参数错误'],
+        'TASK_IN_PROGRESS' => ['code' => 1004, 'msg' => '任务执行中'],
+        'BALANCE_NOT_ENOUGH' => ['code' => 2001, 'msg' => '账户余额不足'],
+        'NOT_CHECKED' => ['code' => 6001, 'msg' => '还没审核通过'],
+        'ALREADY_CHECKED' => ['code' => 6002, 'msg' => '已审核通过'],
+        'ALREADY_PUSHED' => ['code' => 6003, 'msg' => '已推送'],
 
 
         //渠道
-        'ACCOUNT_PASSWORD_NOT_EMPTY'=>['code'=>10003,'msg'=>'账号密码不能为空'],
-        'LOGIN_VERIFY_ERROR'=>['code'=>10003,'msg'=>'账号密码错误'],
-        'PHONE_NOT_REGISTERED'=>['code'=>10003,'msg'=>'号码尚未注册'],
-        'VERIFY_CODE_ERROR'=>['code'=>10004,'msg'=>'验证码错误'],
-        'PHONE_CODE_ERROR'=>['code'=>10004,'msg'=>'验证码未找到'],
-        'SMS_CODE_ERROR'=>['code'=>10007,'msg'=>'短信验证码错误'],
-        'PASSWORD_NOT_SAME'=>['code'=>10008,'msg'=>'密码不一致'],
-        'SMS_SEND_ERROR'=>['code'=>10011,'msg'=>'验证码发送失败'],
-        'SMS_GET_TOO_OFTEN'=>['code'=>10012,'msg'=>'验证码获取过于频繁'],
-        'PASSWORD_WRONG'=>['code'=>10012,'msg'=>'密码错误'],
-        'CHANNEL_NOT_ENABLED'=>['code'=>10012,'msg'=>'账号正在审核,暂不能登录'],
-        'USER_NOT_FOUND'=>['code'=>10013,'msg'=>'用户不存在'],
-        'REGISTER_NUMBER_ERROR'=>['code'=>10013,'msg'=>'注册编号错误'],
-        'COMPANY_EXIST' => ['code'=>10014,'msg'=>'公司名称已经存在'],
-        'UPLOAD_FAILED' => ['code'=>10015,'msg'=>'上传失败'],
-        'CONTENT_MAINTAIN' => ['code'=>10015,'msg'=>'该书正在内容精修中,10月15日恢复正常,敬请期待!'],
+        'ACCOUNT_PASSWORD_NOT_EMPTY' => ['code' => 10003, 'msg' => '账号密码不能为空'],
+        'LOGIN_VERIFY_ERROR' => ['code' => 10003, 'msg' => '账号密码错误'],
+        'PHONE_NOT_REGISTERED' => ['code' => 10003, 'msg' => '号码尚未注册'],
+        'VERIFY_CODE_ERROR' => ['code' => 10004, 'msg' => '验证码错误'],
+        'PHONE_CODE_ERROR' => ['code' => 10004, 'msg' => '验证码未找到'],
+        'SMS_CODE_ERROR' => ['code' => 10007, 'msg' => '短信验证码错误'],
+        'PASSWORD_NOT_SAME' => ['code' => 10008, 'msg' => '密码不一致'],
+        'SMS_SEND_ERROR' => ['code' => 10011, 'msg' => '验证码发送失败'],
+        'SMS_GET_TOO_OFTEN' => ['code' => 10012, 'msg' => '验证码获取过于频繁'],
+        'PASSWORD_WRONG' => ['code' => 10012, 'msg' => '密码错误'],
+        'CHANNEL_NOT_ENABLED' => ['code' => 10012, 'msg' => '账号正在审核,暂不能登录'],
+        'USER_NOT_FOUND' => ['code' => 10013, 'msg' => '用户不存在'],
+        'REGISTER_NUMBER_ERROR' => ['code' => 10013, 'msg' => '注册编号错误'],
+        'COMPANY_EXIST' => ['code' => 10014, 'msg' => '公司名称已经存在'],
+        'UPLOAD_FAILED' => ['code' => 10015, 'msg' => '上传失败'],
+        'CONTENT_MAINTAIN' => ['code' => 10015, 'msg' => '该书正在内容精修中,10月15日恢复正常,敬请期待!'],
 
 
         // 管理
-        'MANAGE_NOT_ENABLED'=>['code'=>10101,'msg'=>'账号暂不可用'],
-        'TEST_BOOK_LIMIT_NUM'=>['code'=>10102,'msg'=>'已有测书数量达到上限'],
-        'BOOK_NOT_EXIST'=>['code'=>10103,'msg'=>'上架书库里没找到该bid'],
-        
-        'MANAGE_USER_EXIST'=>['code'=>10103,'msg'=>'用户已经存在'],
-        'MANAGE_PRIV_ERROR'=>['code'=>10103,'msg'=>'当前账号没有权限'],
-        'ACCOUNT_NOT_EXIST'=>['code'=>10103,'msg'=>'账户不存在'],
-        
-
-        'CASH_ACCOUNT_NOT_SET'=>['code'=>10006,'msg'=>'提现账户未设置'],
-        'CASH_ACCOUNT_EXIST'=>['code'=>10006,'msg'=>'提现账户已存在'],
-        'INSUFFICIENT_BALANCE'=>['code'=>10006,'msg'=>'余额不足'],
-        'LESS_THAN_LOWEST_WITHDRAW_MONEY'=>['code'=>10006,'msg'=>'提现不能低于100'],
-        
-        'PHONE_REGISTERED'=>['code'=>10009,'msg'=>'手机号码已注册'],
-        'PROMOTION_NAME_NOT_EMPTY'=>['code'=>10005,'msg'=>'推广名称不能为空'],
-        'NOT_LOGIN'=>['code'=>10010,'msg'=>'尚未登陆'],
-        
-        'NO_AUTHORIZED_OFFICIAL_ACCOUNT'=>['code'=>10012,'msg'=>'无授权公众号'],
-        'READ_USER_NOT_EXIST'=>['code'=>10012,'msg'=>'阅读用户不存在'],
-        'USER_NOT_SUBSCRIBE'=>['code'=>10012,'msg'=>'用户未关注'],
-        'ACCOUNT_FRONZEN'=>['code'=>10012,'msg'=>'账户被冻结,请咨询官方客服'],
-        'DRAW_ONLY_ONCE_A_DAY'=>['code'=>10012,'msg'=>'每天仅允许提现一次'],
-
-        'HANDLE_FAILED'=>['code'=>10013,'msg'=>'操作失败'],
-        'SAME_ACCOUNT_ERROR'=>['code'=>20028,'msg'=>'两个账号不能相同'],
+        'MANAGE_NOT_ENABLED' => ['code' => 10101, 'msg' => '账号暂不可用'],
+        'TEST_BOOK_LIMIT_NUM' => ['code' => 10102, 'msg' => '已有测书数量达到上限'],
+        'BOOK_NOT_EXIST' => ['code' => 10103, 'msg' => '上架书库里没找到该bid'],
+
+        'MANAGE_USER_EXIST' => ['code' => 10103, 'msg' => '用户已经存在'],
+        'MANAGE_PRIV_ERROR' => ['code' => 10103, 'msg' => '当前账号没有权限'],
+        'ACCOUNT_NOT_EXIST' => ['code' => 10103, 'msg' => '账户不存在'],
+
+
+        'CASH_ACCOUNT_NOT_SET' => ['code' => 10006, 'msg' => '提现账户未设置'],
+        'CASH_ACCOUNT_EXIST' => ['code' => 10006, 'msg' => '提现账户已存在'],
+        'INSUFFICIENT_BALANCE' => ['code' => 10006, 'msg' => '余额不足'],
+        'LESS_THAN_LOWEST_WITHDRAW_MONEY' => ['code' => 10006, 'msg' => '提现不能低于100'],
+
+        'PHONE_REGISTERED' => ['code' => 10009, 'msg' => '手机号码已注册'],
+        'PROMOTION_NAME_NOT_EMPTY' => ['code' => 10005, 'msg' => '推广名称不能为空'],
+        'NOT_LOGIN' => ['code' => 10010, 'msg' => '尚未登陆'],
+
+        'NO_AUTHORIZED_OFFICIAL_ACCOUNT' => ['code' => 10012, 'msg' => '无授权公众号'],
+        'READ_USER_NOT_EXIST' => ['code' => 10012, 'msg' => '阅读用户不存在'],
+        'USER_NOT_SUBSCRIBE' => ['code' => 10012, 'msg' => '用户未关注'],
+        'ACCOUNT_FRONZEN' => ['code' => 10012, 'msg' => '账户被冻结,请咨询官方客服'],
+        'DRAW_ONLY_ONCE_A_DAY' => ['code' => 10012, 'msg' => '每天仅允许提现一次'],
+
+        'HANDLE_FAILED' => ['code' => 10013, 'msg' => '操作失败'],
+        'SAME_ACCOUNT_ERROR' => ['code' => 20028, 'msg' => '两个账号不能相同'],
         //验证码登陆
-        'CAPTCHA_VERIFY_ERROR'=>['code'=>10021,'msg'=>'验证码错误'],
+        'CAPTCHA_VERIFY_ERROR' => ['code' => 10021, 'msg' => '验证码错误'],
 
         //各个模块自己写错误码
         //
         //图书  100001
-        'UNKNOWN_ERROR'=>['code'=>'10013','msg'=>'未知错误'],
-        'LACK_PARAM'=>['code'=>'10014','msg'=>'缺少参数'],
+        'UNKNOWN_ERROR' => ['code' => '10013', 'msg' => '未知错误'],
+        'LACK_PARAM' => ['code' => '10014', 'msg' => '缺少参数'],
         //
         //2000
 
 
         //Notice
 
-        'NAME_EXIST'=>['code'=>50003,'msg'=>'名称已经存在'],
+        'NAME_EXIST' => ['code' => 50003, 'msg' => '名称已经存在'],
         //Finance
-        'FORBID_CHANGE'=>['code'=>60001,'msg'=>'禁止修改'],
-
-        'WITHDRAW_CASH_AMOUNT'=>['code'=>60002,'msg'=>'提现金额必须 >= 100'],
-        'WITHDRAW_CASH_AMOUNT_MORE'=>['code'=>60002,'msg'=>'提现金额不能大于20万'],
-        'WITHDRAW_CASH_AMOUNT_INSUFFICIEN'=>['code'=>60003,'msg'=>'可提现金额不足'],
-        'WITHDRAW_CASH_AMOUNT_FROZEN'=>['code'=>60004,'msg'=>'账号已冻结'],
-        'WITHDRAW_CASH_AMOUNT_NO_CHANGE'=>['code'=>60005,'msg'=>'不能修改状态'],
-        'WITHDRAW_CASH_AMOUNT_ACCOUNT'=>['code'=>60006,'msg'=>'账号未设置'],
-        'PAYMENT_CHANNEL_AMOUNT_WITHOUT'=>['code'=>60007,'msg'=>'渠道可打款金额不足,请走人工打款'],
-        'LIQUIDATED_STAT_AMOUNT_WITHOUT'=>['code'=>60008,'msg'=>'渠道可清算金额不足'],
-        'COMMISSION_RATE_WITHOUT'=>['code'=>60009,'msg'=>'结算比例在 0.1 到 0.99 之间'],
-        'WITHDRAW_CASH_TODAY_USE'=>['code'=>60010,'msg'=>'您今天已经提现过了!'],
-        'PAYMENT_AUTO_NOT_OPEN'=>['code'=>60007,'msg'=>'自动打款暂未开通,请人工打款!'],
+        'FORBID_CHANGE' => ['code' => 60001, 'msg' => '禁止修改'],
+
+        'WITHDRAW_CASH_AMOUNT' => ['code' => 60002, 'msg' => '提现金额必须 >= 100'],
+        'WITHDRAW_CASH_AMOUNT_MORE' => ['code' => 60002, 'msg' => '提现金额不能大于20万'],
+        'WITHDRAW_CASH_AMOUNT_INSUFFICIEN' => ['code' => 60003, 'msg' => '可提现金额不足'],
+        'WITHDRAW_CASH_AMOUNT_FROZEN' => ['code' => 60004, 'msg' => '账号已冻结'],
+        'WITHDRAW_CASH_AMOUNT_NO_CHANGE' => ['code' => 60005, 'msg' => '不能修改状态'],
+        'WITHDRAW_CASH_AMOUNT_ACCOUNT' => ['code' => 60006, 'msg' => '账号未设置'],
+        'PAYMENT_CHANNEL_AMOUNT_WITHOUT' => ['code' => 60007, 'msg' => '渠道可打款金额不足,请走人工打款'],
+        'LIQUIDATED_STAT_AMOUNT_WITHOUT' => ['code' => 60008, 'msg' => '渠道可清算金额不足'],
+        'COMMISSION_RATE_WITHOUT' => ['code' => 60009, 'msg' => '结算比例在 0.1 到 0.99 之间'],
+        'WITHDRAW_CASH_TODAY_USE' => ['code' => 60010, 'msg' => '您今天已经提现过了!'],
+        'PAYMENT_AUTO_NOT_OPEN' => ['code' => 60007, 'msg' => '自动打款暂未开通,请人工打款!'],
         //'PAYMENT_AUTO_NOT_OPEN'=>['code'=>60011,'msg'=>'自动打款暂未开通,请人工打款!'],
-        'PAYMENT_WITHDRAW_MONEY_TOO'=>['code'=>60012,'msg'=>'已成功打款!'],
-        'PAYMENT_WITHDRAW_MONEY'=>['code'=>60013,'msg'=>'打款金额错误,提现金额 - 手续费 = 打款金额'],
+        'PAYMENT_WITHDRAW_MONEY_TOO' => ['code' => 60012, 'msg' => '已成功打款!'],
+        'PAYMENT_WITHDRAW_MONEY' => ['code' => 60013, 'msg' => '打款金额错误,提现金额 - 手续费 = 打款金额'],
 
         //officialAccount
-        'FORCESUBSCRIBEUSER_IS_EXIST'=>['code'=>80003,'msg'=>'该用户已经强关'],
-        'ADDFORCESUBSCRIBEUSER_UNSUCCESSFUL'=>['code'=>80004,'msg'=>'添加强关用户失败'],
-        'UPDATEFORCESUBSCRIBEUSER_UNSUCCESSFUL'=>['code'=>80004,'msg'=>'更新强关用户失败'],
-        'CANCELFORCESUBSCRIBEUSER_UNSUCCESSFUL'=>['code'=>80004,'msg'=>'强关用户取关失败'],
-        'FORCESUBSCRIBEUSER_NOT_FOUND'=>['code'=>80003,'msg'=>'无法获取用户'],
-        'TEMEPATES_NOT_FOUND'=>['code'=>80003,'msg'=>'无法找到模板'],
-        'TEMEPATES_UNSUCCESSFUL'=>['code'=>80003,'msg'=>'模板消息操作失败'],
-        'TEMEPATES_IS_EXIST'=>['code'=>80003,'msg'=>'模板已经存在'],
-        'WECHAT_USER_REQUEST_ERROR'=>['code'=>80004,'msg'=>'新增用户接口调用失败'],
-        'OFFICIAL_ACCOUNT_IS_EXIST'=>['code'=>80003,'msg'=>'公众号已经存在'],
-        'OFFICIAL_ACCOUNT_FAILED'=>['code'=>80004,'msg'=>'公众号授权失败'],
-        'UPDATE_OFFICIAL_ACCOUNT_FAILED'=>['code'=>80004,'msg'=>'更新公众号失败'],
-        'CANCEL_OFFICIAL_ACCOUNT_FAILED'=>['code'=>80004,'msg'=>'取消公众号授权失败'],
-        'OFFICIAL_ACCOUNT_NOT_FOUND'=>['code'=>80003,'msg'=>'公众号不存在'],
-        'EVENT_NOT_FOUND'=>['code'=>80003,'msg'=>'事件未配置'],
-        'HAS_NO_CUSTOMS'=>['code'=>80003,'msg'=>'无法获取客服消息'],
-        'CUSTOMS_SETTING_FAILED'=>['code'=>80004,'msg'=>'客服消息操作失败'],
-        'CUSTOMS_SEND_FAILED'=>['code'=>80004,'msg'=>'客服消息发送失败'],
-        'OFFICIAL_IMGTXT_FAILED'=>['code'=>80004,'msg'=>'素材创建失败'],
-        'OFFICIAL_IMGTXT_DELFAILED'=>['code'=>80004,'msg'=>'素材删除失败'],
-        'OFFICIAL_IMGTXT_FOUNDFAILED'=>['code'=>80004,'msg'=>'未找到素材'],
-        'CUSTOMS_DOUBLE_FAILED'=>['code'=>80008,'msg'=>'你已经在1小时内创建过该客服消息,不能重复创建'],
-        'TEMPLATE_DOUBLE_FAILED'=>['code'=>80008,'msg'=>'你已经在1小时内创建过该模板消息,不能重复创建'],
-        'TEMPLATEMSG_IS_SEDDING'=>['code'=>80008,'msg'=>'该模板消息正在发送中,当前不允许操作'],
-        'CUSTOMMSG_IS_SEDDING'=>['code'=>80008,'msg'=>'该客服消息正在发送中,当前不允许操作'],
-        'KEYWORD_NOT_FOUND'=>['code'=>80009,'msg'=>'无法找到关键字'],
-        'SMART_PUSHS_UNSUCCESSFUL'=>['code'=>80008,'msg'=>'自定义操作失败'],
-        'SMART_PUSHS_NOT_ALL_EDIT'=>['code'=>80008,'msg'=>'修改自定义前必须先编辑好男女的内容'],
-
-        'NOT_ENABLE_FULL_SEND'=>['code'=>80008,'msg'=>'全量发送暂不支持,容易封号'],
-        
-        'KEYWORDS_UNSUCCESSFUL'=>['code'=>80009,'msg'=>'关键字操作失败'],
-        'OLD_APP_NOT_ALLOW_DELETE'=>['code'=>80010,'msg'=>'公众号已有一定数量注册用户不允许删除'],
-        'NOT_FOUND_OFFICIAL_ACCOUNT'=>['code'=>80011,'msg'=>'公众号不存在'],
-        'SHORT_URL_ERROR'=>['code'=>80012,'msg'=>'短连接转化失败,请稍后再试'],
-        'SHORT_URL_NOT_ALLOW_ERROR'=>['code'=>80013,'msg'=>'内容不允许使用短链接,请重新编辑'],
-        
-        'CHECK_TEMPLATE_LIMIT_NUM'=>['code'=>80014,'msg'=>'请只勾选1个公众号测试'],
-        'UNABLE_DEL_EXIST_GZH'=>['code'=>80014,'msg'=>'站点还有绑定公众号,请解绑后再删除站点'],
-        'UNABLE_DEL_EXIST_WITHDRAWL_AMOUNT'=>['code'=>80014,'msg'=>'站点还有余额,请提现后再删除站点'],
-        'NOT_ENABLE_TEMPLATE_SEND'=>['code'=>80014,'msg'=>'最近封号严重,模板消息暂时关停'],
+        'FORCESUBSCRIBEUSER_IS_EXIST' => ['code' => 80003, 'msg' => '该用户已经强关'],
+        'ADDFORCESUBSCRIBEUSER_UNSUCCESSFUL' => ['code' => 80004, 'msg' => '添加强关用户失败'],
+        'UPDATEFORCESUBSCRIBEUSER_UNSUCCESSFUL' => ['code' => 80004, 'msg' => '更新强关用户失败'],
+        'CANCELFORCESUBSCRIBEUSER_UNSUCCESSFUL' => ['code' => 80004, 'msg' => '强关用户取关失败'],
+        'FORCESUBSCRIBEUSER_NOT_FOUND' => ['code' => 80003, 'msg' => '无法获取用户'],
+        'TEMEPATES_NOT_FOUND' => ['code' => 80003, 'msg' => '无法找到模板'],
+        'TEMEPATES_UNSUCCESSFUL' => ['code' => 80003, 'msg' => '模板消息操作失败'],
+        'TEMEPATES_IS_EXIST' => ['code' => 80003, 'msg' => '模板已经存在'],
+        'WECHAT_USER_REQUEST_ERROR' => ['code' => 80004, 'msg' => '新增用户接口调用失败'],
+        'OFFICIAL_ACCOUNT_IS_EXIST' => ['code' => 80003, 'msg' => '公众号已经存在'],
+        'OFFICIAL_ACCOUNT_FAILED' => ['code' => 80004, 'msg' => '公众号授权失败'],
+        'UPDATE_OFFICIAL_ACCOUNT_FAILED' => ['code' => 80004, 'msg' => '更新公众号失败'],
+        'CANCEL_OFFICIAL_ACCOUNT_FAILED' => ['code' => 80004, 'msg' => '取消公众号授权失败'],
+        'OFFICIAL_ACCOUNT_NOT_FOUND' => ['code' => 80003, 'msg' => '公众号不存在'],
+        'EVENT_NOT_FOUND' => ['code' => 80003, 'msg' => '事件未配置'],
+        'HAS_NO_CUSTOMS' => ['code' => 80003, 'msg' => '无法获取客服消息'],
+        'CUSTOMS_SETTING_FAILED' => ['code' => 80004, 'msg' => '客服消息操作失败'],
+        'CUSTOMS_SEND_FAILED' => ['code' => 80004, 'msg' => '客服消息发送失败'],
+        'OFFICIAL_IMGTXT_FAILED' => ['code' => 80004, 'msg' => '素材创建失败'],
+        'OFFICIAL_IMGTXT_DELFAILED' => ['code' => 80004, 'msg' => '素材删除失败'],
+        'OFFICIAL_IMGTXT_FOUNDFAILED' => ['code' => 80004, 'msg' => '未找到素材'],
+        'CUSTOMS_DOUBLE_FAILED' => ['code' => 80008, 'msg' => '你已经在1小时内创建过该客服消息,不能重复创建'],
+        'TEMPLATE_DOUBLE_FAILED' => ['code' => 80008, 'msg' => '你已经在1小时内创建过该模板消息,不能重复创建'],
+        'TEMPLATEMSG_IS_SEDDING' => ['code' => 80008, 'msg' => '该模板消息正在发送中,当前不允许操作'],
+        'CUSTOMMSG_IS_SEDDING' => ['code' => 80008, 'msg' => '该客服消息正在发送中,当前不允许操作'],
+        'KEYWORD_NOT_FOUND' => ['code' => 80009, 'msg' => '无法找到关键字'],
+        'SMART_PUSHS_UNSUCCESSFUL' => ['code' => 80008, 'msg' => '自定义操作失败'],
+        'SMART_PUSHS_NOT_ALL_EDIT' => ['code' => 80008, 'msg' => '修改自定义前必须先编辑好男女的内容'],
+
+        'NOT_ENABLE_FULL_SEND' => ['code' => 80008, 'msg' => '全量发送暂不支持,容易封号'],
+
+        'KEYWORDS_UNSUCCESSFUL' => ['code' => 80009, 'msg' => '关键字操作失败'],
+        'OLD_APP_NOT_ALLOW_DELETE' => ['code' => 80010, 'msg' => '公众号已有一定数量注册用户不允许删除'],
+        'NOT_FOUND_OFFICIAL_ACCOUNT' => ['code' => 80011, 'msg' => '公众号不存在'],
+        'SHORT_URL_ERROR' => ['code' => 80012, 'msg' => '短连接转化失败,请稍后再试'],
+        'SHORT_URL_NOT_ALLOW_ERROR' => ['code' => 80013, 'msg' => '内容不允许使用短链接,请重新编辑'],
+
+        'CHECK_TEMPLATE_LIMIT_NUM' => ['code' => 80014, 'msg' => '请只勾选1个公众号测试'],
+        'UNABLE_DEL_EXIST_GZH' => ['code' => 80014, 'msg' => '站点还有绑定公众号,请解绑后再删除站点'],
+        'UNABLE_DEL_EXIST_WITHDRAWL_AMOUNT' => ['code' => 80014, 'msg' => '站点还有余额,请提现后再删除站点'],
+        'NOT_ENABLE_TEMPLATE_SEND' => ['code' => 80014, 'msg' => '最近封号严重,模板消息暂时关停'],
 
         //订阅
-        'NOT_ORDERED'=>['code'=>10012,'msg'=>'未订购'],
+        'NOT_ORDERED' => ['code' => 10012, 'msg' => '未订购'],
 
         //wap
-        'WAP_NOT_LOGIN'=>['code'=>10001,'msg'=>'未登录'],
-        'WAP_NOT_ORDERED'=>['code'=>10002,'msg'=>'未订购'],
-        'WAP_PARAM_ERROR'=>['code'=>10003,'msg'=>'参数错误'],
-        'WAP_SYS_ERROR'=>['code'=>10004,'msg'=>'系统错误'],
-        'WAP_SIGN_ERROR'=>['code'=>10005,'msg'=>'签名有误'],
-        'WAP_NOT_EXIST'=>['code'=>10007,'msg'=>'资源不存在'],
-        'WAP_ADDED_SHELF'=>['code'=>10008,'msg'=>'已添加到书架'],
-        'WAP_INSUFFICIENT_BALANCE'=>['code'=>10009,'msg'=>'余额不足'],
-        'WAP_ORDER_FAILED'=>['code'=>10010,'msg'=>'订购失败'],
-        'WAP_PRODUCT_NOT_FOUND'=>['code'=>10011,'msg'=>'产品找不到'],
-        'WAP_NOT_SUBSCRIBE'=>['code'=>10012,'msg'=>'未关注'],
-        'WAP_LACK_PARAM'=>['code'=>10013,'msg'=>'缺少参数'],
-        'WAP_BOOK_INSUFFICIENT_BALANCE'=>['code'=>10014,'msg'=>'单本余额不足'],
-        'WAP_CHAPTER_INSUFFICIENT_BALANCE'=>['code'=>10015,'msg'=>'章订余额不足'],
-        'WAP_CHAPTER_BUY'=>['code'=>10016,'msg'=>'购买章节'],
-        'WAP_BOOK_BUY'=>['code'=>10017,'msg'=>'购买图书'],
-        'WAP_OFF_SHELF'=>['code'=>10018,'msg'=>'图书下架'],
-        'WAP_BOOK_BALANCE_PAY'=>['code'=>10019,'msg'=>'购买图书'],
-        'WAP_BOOK_SECOND_BALANCE_PAY'=>['code'=>10020,'msg'=>'第二次图书订购余额不足'],
-        'WAP_CHAPTER_SECOND_BALANCE_PAY'=>['code'=>10021,'msg'=>'第二次章节订购余额不足'],
-        'WAP_DOMAIN_NOT_MATCH'=>['code'=>10022,'msg'=>'域名不匹配'],
-        'WAP_LINK_SUBSCRIBE'=>['code'=>10023,'msg'=>'链接强关'],
-        'WAP_HAD_ON_SHELF'=>['code'=>10024,'msg'=>'已经在书架上了'],
+        'WAP_NOT_LOGIN' => ['code' => 10001, 'msg' => '未登录'],
+        'WAP_NOT_ORDERED' => ['code' => 10002, 'msg' => '未订购'],
+        'WAP_PARAM_ERROR' => ['code' => 10003, 'msg' => '参数错误'],
+        'WAP_SYS_ERROR' => ['code' => 10004, 'msg' => '系统错误'],
+        'WAP_SIGN_ERROR' => ['code' => 10005, 'msg' => '签名有误'],
+        'WAP_NOT_EXIST' => ['code' => 10007, 'msg' => '资源不存在'],
+        'WAP_ADDED_SHELF' => ['code' => 10008, 'msg' => '已添加到书架'],
+        'WAP_INSUFFICIENT_BALANCE' => ['code' => 10009, 'msg' => '余额不足'],
+        'WAP_ORDER_FAILED' => ['code' => 10010, 'msg' => '订购失败'],
+        'WAP_PRODUCT_NOT_FOUND' => ['code' => 10011, 'msg' => '产品找不到'],
+        'WAP_NOT_SUBSCRIBE' => ['code' => 10012, 'msg' => '未关注'],
+        'WAP_LACK_PARAM' => ['code' => 10013, 'msg' => '缺少参数'],
+        'WAP_BOOK_INSUFFICIENT_BALANCE' => ['code' => 10014, 'msg' => '单本余额不足'],
+        'WAP_CHAPTER_INSUFFICIENT_BALANCE' => ['code' => 10015, 'msg' => '章订余额不足'],
+        'WAP_CHAPTER_BUY' => ['code' => 10016, 'msg' => '购买章节'],
+        'WAP_BOOK_BUY' => ['code' => 10017, 'msg' => '购买图书'],
+        'WAP_OFF_SHELF' => ['code' => 10018, 'msg' => '图书下架'],
+        'WAP_BOOK_BALANCE_PAY' => ['code' => 10019, 'msg' => '购买图书'],
+        'WAP_BOOK_SECOND_BALANCE_PAY' => ['code' => 10020, 'msg' => '第二次图书订购余额不足'],
+        'WAP_CHAPTER_SECOND_BALANCE_PAY' => ['code' => 10021, 'msg' => '第二次章节订购余额不足'],
+        'WAP_DOMAIN_NOT_MATCH' => ['code' => 10022, 'msg' => '域名不匹配'],
+        'WAP_LINK_SUBSCRIBE' => ['code' => 10023, 'msg' => '链接强关'],
+        'WAP_HAD_ON_SHELF' => ['code' => 10024, 'msg' => '已经在书架上了'],
         //送礼
-        'WAP_SEND_GIFT_FAILED'=>['code'=>10028,'msg'=>'送礼失败!请稍后再试!'],
-        'WAP_BIND_PHONE_EXIST'=>['code'=>10029,'msg'=>'手机号码已经被绑定了'],
-        'WAP_SEND_OPENID_EXIST'=>['code'=>10030,'msg'=>'您已经绑定过'],
-        'WAP_SEND_CODE_ERROR'=>['code'=>10031,'msg'=>'验证码错误'],
+        'WAP_SEND_GIFT_FAILED' => ['code' => 10028, 'msg' => '送礼失败!请稍后再试!'],
+        'WAP_BIND_PHONE_EXIST' => ['code' => 10029, 'msg' => '手机号码已经被绑定了'],
+        'WAP_SEND_OPENID_EXIST' => ['code' => 10030, 'msg' => '您已经绑定过'],
+        'WAP_SEND_CODE_ERROR' => ['code' => 10031, 'msg' => '验证码错误'],
         //XCX小程序
-        'XCX_NOT_LOGIN'=>['code'=>10023,'msg'=>'未登录'],
-        'XCX_TOKEN_ERROR'=>['code'=>10023,'msg'=>'token失效'],
-        'XCX_NOT_ORDERED'=>['code'=>10002,'msg'=>'未订购'],
-        'XCX_PARAM_ERROR'=>['code'=>10003,'msg'=>'参数错误'],
-        'XCX_SYS_ERROR'=>['code'=>10004,'msg'=>'系统错误'],
-        'XCX_SIGN_ERROR'=>['code'=>10005,'msg'=>'签名有误'],
-        'XCX_NOT_EXIST'=>['code'=>10007,'msg'=>'资源不存在'],
-        'XCX_ADDED_SHELF'=>['code'=>10008,'msg'=>'已添加到书架'],
-        'XCX_INSUFFICIENT_BALANCE'=>['code'=>10009,'msg'=>'余额不足'],
-        'XCX_ORDER_FAILED'=>['code'=>10010,'msg'=>'订购失败'],
-        'XCX_PRODUCT_NOT_FOUND'=>['code'=>10011,'msg'=>'产品找不到'],
-        'XCX_NOT_SUBSCRIBE'=>['code'=>10012,'msg'=>'未关注'],
-        'XCX_LACK_PARAM'=>['code'=>10013,'msg'=>'缺少参数'],
-        'XCX_BOOK_INSUFFICIENT_BALANCE'=>['code'=>10014,'msg'=>'单本余额不足'],
-        'XCX_CHAPTER_INSUFFICIENT_BALANCE'=>['code'=>10015,'msg'=>'章订余额不足'],
-        'XCX_CHAPTER_BUY'=>['code'=>10016,'msg'=>'购买章节'],
-        'XCX_BOOK_BUY'=>['code'=>10017,'msg'=>'购买图书'],
-        'XCX_OFF_SHELF'=>['code'=>10018,'msg'=>'图书下架'],
-        'XCX_BOOK_BALANCE_PAY'=>['code'=>10019,'msg'=>'购买图书'],
-        'XCX_BOOK_SECOND_BALANCE_PAY'=>['code'=>10020,'msg'=>'第二次图书订购余额不足'],
-        'XCX_CHAPTER_SECOND_BALANCE_PAY'=>['code'=>10021,'msg'=>'第二次章节订购余额不足'],
-        'XCX_DOMAIN_NOT_MATCH'=>['code'=>10022,'msg'=>'域名不匹配'],
-
-];
+        'XCX_NOT_LOGIN' => ['code' => 10023, 'msg' => '未登录'],
+        'XCX_TOKEN_ERROR' => ['code' => 10023, 'msg' => 'token失效'],
+        'XCX_NOT_ORDERED' => ['code' => 10002, 'msg' => '未订购'],
+        'XCX_PARAM_ERROR' => ['code' => 10003, 'msg' => '参数错误'],
+        'XCX_SYS_ERROR' => ['code' => 10004, 'msg' => '系统错误'],
+        'XCX_SIGN_ERROR' => ['code' => 10005, 'msg' => '签名有误'],
+        'XCX_NOT_EXIST' => ['code' => 10007, 'msg' => '资源不存在'],
+        'XCX_ADDED_SHELF' => ['code' => 10008, 'msg' => '已添加到书架'],
+        'XCX_INSUFFICIENT_BALANCE' => ['code' => 10009, 'msg' => '余额不足'],
+        'XCX_ORDER_FAILED' => ['code' => 10010, 'msg' => '订购失败'],
+        'XCX_PRODUCT_NOT_FOUND' => ['code' => 10011, 'msg' => '产品找不到'],
+        'XCX_NOT_SUBSCRIBE' => ['code' => 10012, 'msg' => '未关注'],
+        'XCX_LACK_PARAM' => ['code' => 10013, 'msg' => '缺少参数'],
+        'XCX_BOOK_INSUFFICIENT_BALANCE' => ['code' => 10014, 'msg' => '单本余额不足'],
+        'XCX_CHAPTER_INSUFFICIENT_BALANCE' => ['code' => 10015, 'msg' => '章订余额不足'],
+        'XCX_CHAPTER_BUY' => ['code' => 10016, 'msg' => '购买章节'],
+        'XCX_BOOK_BUY' => ['code' => 10017, 'msg' => '购买图书'],
+        'XCX_OFF_SHELF' => ['code' => 10018, 'msg' => '图书下架'],
+        'XCX_BOOK_BALANCE_PAY' => ['code' => 10019, 'msg' => '购买图书'],
+        'XCX_BOOK_SECOND_BALANCE_PAY' => ['code' => 10020, 'msg' => '第二次图书订购余额不足'],
+        'XCX_CHAPTER_SECOND_BALANCE_PAY' => ['code' => 10021, 'msg' => '第二次章节订购余额不足'],
+        'XCX_DOMAIN_NOT_MATCH' => ['code' => 10022, 'msg' => '域名不匹配'],
+
+        //Quick App 快应用
+        'QAPP_NOT_LOGIN' => ['code' => 10023, 'msg' => '未登录'],
+        'QAPP_TOKEN_ERROR' => ['code' => 10023, 'msg' => 'token失效'],
+        'QAPP_NOT_ORDERED' => ['code' => 10002, 'msg' => '未订购'],
+        'QAPP_PARAM_ERROR' => ['code' => 10003, 'msg' => '参数错误'],
+        'QAPP_SYS_ERROR' => ['code' => 10004, 'msg' => '系统错误'],
+        'QAPP_SIGN_ERROR' => ['code' => 10005, 'msg' => '签名有误'],
+        'QAPP_NOT_EXIST' => ['code' => 10007, 'msg' => '资源不存在'],
+        'QAPP_ADDED_SHELF' => ['code' => 10008, 'msg' => '已添加到书架'],
+        'QAPP_INSUFFICIENT_BALANCE' => ['code' => 10009, 'msg' => '余额不足'],
+        'QAPP_ORDER_FAILED' => ['code' => 10010, 'msg' => '订购失败'],
+        'QAPP_PRODUCT_NOT_FOUND' => ['code' => 10011, 'msg' => '产品找不到'],
+        'QAPP_NOT_SUBSCRIBE' => ['code' => 10012, 'msg' => '未关注'],
+        'QAPP_LACK_PARAM' => ['code' => 10013, 'msg' => '缺少参数'],
+        'QAPP_BOOK_INSUFFICIENT_BALANCE' => ['code' => 10014, 'msg' => '单本余额不足'],
+        'QAPP_CHAPTER_INSUFFICIENT_BALANCE' => ['code' => 10015, 'msg' => '章订余额不足'],
+        'QAPP_CHAPTER_BUY' => ['code' => 10016, 'msg' => '购买章节'],
+        'QAPP_BOOK_BUY' => ['code' => 10017, 'msg' => '购买图书'],
+        'QAPP_OFF_SHELF' => ['code' => 10018, 'msg' => '图书下架'],
+        'QAPP_BOOK_BALANCE_PAY' => ['code' => 10019, 'msg' => '购买图书'],
+        'QAPP_BOOK_SECOND_BALANCE_PAY' => ['code' => 10020, 'msg' => '第二次图书订购余额不足'],
+        'QAPP_CHAPTER_SECOND_BALANCE_PAY' => ['code' => 10021, 'msg' => '第二次章节订购余额不足'],
+        'QAPP_DOMAIN_NOT_MATCH' => ['code' => 10022, 'msg' => '域名不匹配'],
+
+];

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3773 - 0
public/kyydoc/api_data.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3773 - 0
public/kyydoc/api_data.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/kyydoc/api_project.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
public/kyydoc/api_project.json


+ 532 - 0
public/kyydoc/css/style.css

@@ -0,0 +1,532 @@
+/* ------------------------------------------------------------------------------------------
+ * Content
+ * ------------------------------------------------------------------------------------------ */
+body {
+  min-width: 980px;
+  max-width: 1280px;
+}
+
+body, p, a, div, th, td {
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 400;
+  font-size: 16px;
+}
+
+td.code {
+  font-size: 14px;
+  font-family: "Source Code Pro", monospace;
+  font-style: normal;
+  font-weight: 400;
+}
+
+#content {
+  padding-top: 16px;
+  z-Index: -1;
+  margin-left: 270px;
+}
+
+p {
+  color: #808080;
+}
+
+h1 {
+  font-family: "Source Sans Pro Semibold", sans-serif;
+  font-weight: normal;
+  font-size: 44px;
+  line-height: 50px;
+  margin: 0 0 10px 0;
+  padding: 0;
+}
+
+h2 {
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: normal;
+  font-size: 24px;
+  line-height: 40px;
+  margin: 0 0 20px 0;
+  padding: 0;
+}
+
+section {
+  border-top: 1px solid #ebebeb;
+  padding: 30px 0;
+}
+
+section h1 {
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 700;
+  font-size: 32px;
+  line-height: 40px;
+  padding-bottom: 14px;
+  margin: 0 0 20px 0;
+  padding: 0;
+}
+
+article {
+  padding: 14px 0 30px 0;
+}
+
+article h1 {
+  font-family: "Source Sans Pro Bold", sans-serif;
+  font-weight: 600;
+  font-size: 24px;
+  line-height: 26px;
+}
+
+article h2 {
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 600;
+  font-size: 18px;
+  line-height: 24px;
+  margin: 0 0 10px 0;
+}
+
+article h3 {
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 600;
+  font-size: 16px;
+  line-height: 18px;
+  margin: 0 0 10px 0;
+}
+
+article h4 {
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 600;
+  font-size: 14px;
+  line-height: 16px;
+  margin: 0 0 8px 0;
+}
+
+table {
+  border-collapse: collapse;
+  width: 100%;
+  margin: 0 0 20px 0;
+}
+
+th {
+  background-color: #f5f5f5;
+  text-align: left;
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 700;
+  padding: 4px 8px;
+  border: #e0e0e0 1px solid;
+}
+
+td {
+  vertical-align: top;
+  padding: 2px 8px;
+  border: #e0e0e0 1px solid;
+}
+
+#generator .content {
+  color: #b0b0b0;
+  border-top: 1px solid #ebebeb;
+  padding: 10px 0;
+}
+
+.label-optional {
+  float: right;
+}
+
+.open-left {
+  right: 0;
+  left: auto;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * apidoc - intro
+ * ------------------------------------------------------------------------------------------ */
+
+#apidoc .apidoc {
+  border-top: 1px solid #ebebeb;
+  padding: 30px 0;
+}
+
+#apidoc h1 {
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 700;
+  font-size: 32px;
+  line-height: 40px;
+  padding-bottom: 14px;
+  margin: 0 0 20px 0;
+  padding: 0;
+}
+
+#apidoc h2 {
+  font-family: "Source Sans Pro Bold", sans-serif;
+  font-weight: 600;
+  font-size: 22px;
+  line-height: 26px;
+  padding-top: 14px;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * pre / code
+ * ------------------------------------------------------------------------------------------ */
+pre {
+  background-color: #292b36;
+  color: #ffffff;
+  padding: 10px;
+  border-radius: 6px;
+  position: relative;
+  margin: 10px 0 20px 0;
+}
+
+code.language-text {
+  word-wrap: break-word;
+}
+
+pre.language-json {
+  overflow: auto;
+}
+
+pre.language-html {
+  margin: 40px 0 20px 0;
+}
+
+pre.language-html:before {
+  content: attr(data-type);
+  position: absolute;
+  top: -30px;
+  left: 0;
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 600;
+  font-size: 15px;
+  display: inline-block;
+  padding: 2px 5px;
+  border-radius: 6px;
+  text-transform: uppercase;
+  background-color: #3387CC;
+  color: #ffffff;
+}
+
+pre.language-html[data-type="get"]:before {
+  background-color: green;
+}
+
+pre.language-html[data-type="put"]:before {
+  background-color: #e5c500;
+}
+
+pre.language-html[data-type="post"]:before {
+  background-color: #4070ec;
+}
+
+pre.language-html[data-type="delete"]:before {
+  background-color: #ed0039;
+}
+
+pre.language-api .str {
+  color: #ffffff;
+}
+
+pre.language-api .pln,
+pre.language-api .pun {
+  color: #65B042;
+}
+
+pre code {
+  display: block;
+  font-size: 14px;
+  font-family: "Source Code Pro", monospace;
+  font-style: normal;
+  font-weight: 400;
+  word-wrap: normal;
+  white-space: pre;
+}
+
+pre code.sample-request-response-json {
+  white-space: pre-wrap;
+  max-height: 500px;
+  overflow: auto;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * Sidenav
+ * ------------------------------------------------------------------------------------------ */
+.sidenav {
+  width: 228px;
+  margin: 0;
+  padding: 20px;
+  position: fixed;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  overflow-x: hidden;
+  overflow-y: auto;
+  background-color: #f5f5f5;
+  z-index: 10;
+}
+
+.sidenav > li > a {
+  display: block;
+  width: 192px;
+  margin: 0;
+  padding: 2px 11px;
+  border: 0;
+  border-left: transparent 4px solid;
+  border-right: transparent 4px solid;
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 400;
+  font-size: 14px;
+}
+
+.sidenav > li.nav-header > a {
+  padding: 5px 15px;
+  border: 1px solid #e5e5e5;
+  width: 190px;
+  font-family: "Source Sans Pro", sans-serif;
+  font-weight: 700;
+  font-size: 16px;
+  background-color: #ffffff;
+}
+
+.sidenav > li.nav-header.active > a {
+  background-color: #0088cc;
+}
+
+.sidenav > .active > a {
+  position: relative;
+  z-index: 2;
+}
+
+.sidenav > li > a:hover {
+  background-color: #ffffff;
+}
+
+.sidenav > li.has-modifications a {
+  border-right: #60d060 4px solid;
+}
+
+.sidenav > li.is-new a {
+  border-left: #e5e5e5 4px solid;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * Compare
+ * ------------------------------------------------------------------------------------------ */
+
+ins {
+  background: #60d060;
+  text-decoration: none;
+  color: #000000;
+}
+
+del {
+  background: #f05050;
+  color: #000000;
+}
+
+.label-ins {
+  background-color: #60d060;
+}
+
+.label-del {
+  background-color: #f05050;
+  text-decoration: line-through;
+}
+
+pre.ins {
+  background-color: #60d060;
+}
+
+pre.del {
+  background-color: #f05050;
+  text-decoration: line-through;
+}
+
+table.ins th,
+table.ins td {
+  background-color: #60d060;
+}
+
+table.del th,
+table.del td {
+  background-color: #f05050;
+  text-decoration: line-through;
+}
+
+tr.ins td {
+  background-color: #60d060;
+}
+
+tr.del td {
+  background-color: #f05050;
+  text-decoration: line-through;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * Spinner
+ * ------------------------------------------------------------------------------------------ */
+
+#loader {
+  position: absolute;
+  width: 100%;
+}
+
+#loader p {
+  padding-top: 80px;
+  margin-left: -4px;
+}
+
+.spinner {
+  margin: 200px auto;
+  width: 60px;
+  height: 60px;
+  position: relative;
+}
+
+.container1 > div, .container2 > div, .container3 > div {
+  width: 14px;
+  height: 14px;
+  background-color: #0088cc;
+
+  border-radius: 100%;
+  position: absolute;
+  -webkit-animation: bouncedelay 1.2s infinite ease-in-out;
+  animation: bouncedelay 1.2s infinite ease-in-out;
+  /* Prevent first frame from flickering when animation starts */
+  -webkit-animation-fill-mode: both;
+  animation-fill-mode: both;
+}
+
+.spinner .spinner-container {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+}
+
+.container2 {
+  -webkit-transform: rotateZ(45deg);
+  transform: rotateZ(45deg);
+}
+
+.container3 {
+  -webkit-transform: rotateZ(90deg);
+  transform: rotateZ(90deg);
+}
+
+.circle1 { top: 0; left: 0; }
+.circle2 { top: 0; right: 0; }
+.circle3 { right: 0; bottom: 0; }
+.circle4 { left: 0; bottom: 0; }
+
+.container2 .circle1 {
+  -webkit-animation-delay: -1.1s;
+  animation-delay: -1.1s;
+}
+
+.container3 .circle1 {
+  -webkit-animation-delay: -1.0s;
+  animation-delay: -1.0s;
+}
+
+.container1 .circle2 {
+  -webkit-animation-delay: -0.9s;
+  animation-delay: -0.9s;
+}
+
+.container2 .circle2 {
+  -webkit-animation-delay: -0.8s;
+  animation-delay: -0.8s;
+}
+
+.container3 .circle2 {
+  -webkit-animation-delay: -0.7s;
+  animation-delay: -0.7s;
+}
+
+.container1 .circle3 {
+  -webkit-animation-delay: -0.6s;
+  animation-delay: -0.6s;
+}
+
+.container2 .circle3 {
+  -webkit-animation-delay: -0.5s;
+  animation-delay: -0.5s;
+}
+
+.container3 .circle3 {
+  -webkit-animation-delay: -0.4s;
+  animation-delay: -0.4s;
+}
+
+.container1 .circle4 {
+  -webkit-animation-delay: -0.3s;
+  animation-delay: -0.3s;
+}
+
+.container2 .circle4 {
+  -webkit-animation-delay: -0.2s;
+  animation-delay: -0.2s;
+}
+
+.container3 .circle4 {
+  -webkit-animation-delay: -0.1s;
+  animation-delay: -0.1s;
+}
+
+@-webkit-keyframes bouncedelay {
+  0%, 80%, 100% { -webkit-transform: scale(0.0) }
+  40% { -webkit-transform: scale(1.0) }
+}
+
+@keyframes bouncedelay {
+  0%, 80%, 100% {
+    transform: scale(0.0);
+    -webkit-transform: scale(0.0);
+  } 40% {
+    transform: scale(1.0);
+    -webkit-transform: scale(1.0);
+  }
+}
+
+/* ------------------------------------------------------------------------------------------
+ * Tabs
+ * ------------------------------------------------------------------------------------------ */
+ul.nav-tabs {
+  margin: 0;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * Print
+ * ------------------------------------------------------------------------------------------ */
+
+@media print {
+
+  #sidenav,
+  #version,
+  #versions,
+  section .version,
+  section .versions {
+    display: none;
+  }
+
+  #content {
+    margin-left: 0;
+  }
+
+  a {
+    text-decoration: none;
+    color: inherit;
+  }
+
+  a:after {
+    content: " [" attr(href) "] ";
+  }
+
+  p {
+    color: #000000
+  }
+
+  pre {
+    background-color: #ffffff;
+    color: #000000;
+    padding: 10px;
+    border: #808080 1px solid;
+    border-radius: 6px;
+    position: relative;
+    margin: 10px 0 20px 0;
+  }
+
+} /* /@media print */

BIN
public/kyydoc/img/favicon.ico


BIN
public/kyydoc/img/glyphicons-halflings-white.png


BIN
public/kyydoc/img/glyphicons-halflings.png


+ 659 - 0
public/kyydoc/index.html

@@ -0,0 +1,659 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+  <title>Loading...</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+  <link href="vendor/bootstrap.min.css" rel="stylesheet" media="screen">
+  <link href="vendor/prettify.css" rel="stylesheet" media="screen">
+  <link href="css/style.css" rel="stylesheet" media="screen, print">
+  <link href="img/favicon.ico" rel="icon" type="image/x-icon">
+  <script src="vendor/polyfill.js"></script>
+</head>
+<body>
+
+<script id="template-sidenav" type="text/x-handlebars-template">
+<nav id="scrollingNav">
+  <ul class="sidenav nav nav-list">
+  {{#each nav}}
+    {{#if title}}
+      {{#if isHeader}}
+        {{#if isFixed}}
+          <li class="nav-fixed nav-header" data-group="{{group}}"><a href="#api-{{group}}">{{underscoreToSpace title}}</a></li>
+        {{else}}
+          <li class="nav-header" data-group="{{group}}"><a href="#api-{{group}}">{{underscoreToSpace title}}</a></li>
+        {{/if}}
+      {{else}}
+        <li {{#if hidden}}class="hide" {{/if}}data-group="{{group}}" data-name="{{name}}" data-version="{{version}}">
+          <a href="#api-{{group}}-{{name}}">{{title}}</a>
+        </li>
+      {{/if}}
+    {{/if}}
+  {{/each}}
+  </ul>
+</nav>
+</script>
+
+<script id="template-project" type="text/x-handlebars-template">
+  <div class="pull-left">
+    <h1>{{name}}</h1>
+    {{#if description}}<h2>{{{nl2br description}}}</h2>{{/if}}
+  </div>
+  {{#if template.withCompare}}
+  <div class="pull-right">
+    <div class="btn-group">
+      <button id="version" class="btn btn-large dropdown-toggle" data-toggle="dropdown">
+        <strong>{{version}}</strong> <span class="caret"></span>
+      </button>
+      <ul id="versions" class="dropdown-menu open-left">
+          <li><a id="compareAllWithPredecessor" href="#">{{__ "Compare all with predecessor"}}</a></li>
+          <li class="divider"></li>
+          <li class="disabled"><a href="#">{{__ "show up to version:"}}</a></li>
+  {{#each versions}}
+        <li class="version"><a href="#">{{this}}</a></li>
+  {{/each}}
+      </ul>
+    </div>
+  </div>
+  {{/if}}
+  <div class="clearfix"></div>
+</script>
+
+<script id="template-header" type="text/x-handlebars-template">
+  {{#if content}}
+    <div id="api-_">{{{content}}}</div>
+  {{/if}}
+</script>
+
+<script id="template-footer" type="text/x-handlebars-template">
+  {{#if content}}
+    <div id="api-_footer">{{{content}}}</div>
+  {{/if}}
+</script>
+
+<script id="template-generator" type="text/x-handlebars-template">
+  {{#if template.withGenerator}}
+    {{#if generator}}
+      <div class="content">
+        {{__ "Generated with"}} <a href="{{{generator.url}}}">{{{generator.name}}}</a> {{{generator.version}}} - {{{generator.time}}}
+      </div>
+    {{/if}}
+  {{/if}}
+</script>
+
+<script id="template-sections" type="text/x-handlebars-template">
+  <section id="api-{{group}}">
+    <h1>{{underscoreToSpace title}}</h1>
+    {{#if description}}
+      <p>{{{nl2br description}}}</p>
+    {{/if}}
+    {{#each articles}}
+      <div id="api-{{group}}-{{name}}">
+        {{{article}}}
+      </div>
+    {{/each}}
+  </section>
+</script>
+
+<script id="template-article" type="text/x-handlebars-template">
+  <article id="api-{{article.group}}-{{article.name}}-{{article.version}}" {{#if hidden}}class="hide"{{/if}} data-group="{{article.group}}" data-name="{{article.name}}" data-version="{{article.version}}">
+    <div class="pull-left">
+      <h1>{{article.groupTitle}}{{#if article.title}} - {{article.title}}{{/if}}</h1>
+    </div>
+    {{#if template.withCompare}}
+    <div class="pull-right">
+      <div class="btn-group">
+        <button class="version btn dropdown-toggle" data-toggle="dropdown">
+          <strong>{{article.version}}</strong> <span class="caret"></span>
+        </button>
+        <ul class="versions dropdown-menu open-left">
+          <li class="disabled"><a href="#">{{__ "compare changes to:"}}</a></li>
+  {{#each versions}}
+          <li class="version"><a href="#">{{this}}</a></li>
+  {{/each}}
+        </ul>
+      </div>
+    </div>
+    {{/if}}
+    <div class="clearfix"></div>
+
+    {{#if article.description}}
+      <p>{{{nl2br article.description}}}</p>
+    {{/if}}
+
+    <pre class="prettyprint language-html" data-type="{{toLowerCase article.type}}"><code>{{article.url}}</code></pre>
+
+    {{#if article.permission}}
+      <p>
+        {{__ "Permission:"}}
+        {{#each article.permission}}
+          {{name}}
+          {{#if title}}
+            &nbsp;<a href="#" data-toggle="popover" data-placement="right" data-html="true" data-content="{{nl2br description}}" title="" data-original-title="{{title}}"><span class="label label-info"><i class="icon icon-info-sign icon-white"></i></span></a>
+            {{#unless @last}}, {{/unless}}
+          {{/if}}
+        {{/each}}
+      </p>
+    {{/if}}
+
+    {{#if_gt article.examples.length compare=0}}
+      <ul class="nav nav-tabs nav-tabs-examples">
+        {{#each article.examples}}
+          <li{{#if_eq @index compare=0}} class="active"{{/if_eq}}>
+            <a href="#examples-{{../id}}-{{@index}}">{{title}}</a>
+          </li>
+        {{/each}}
+      </ul>
+
+      <div class="tab-content">
+      {{#each article.examples}}
+        <div class="tab-pane{{#if_eq @index compare=0}} active{{/if_eq}}" id="examples-{{../id}}-{{@index}}">
+          <pre class="prettyprint language-{{type}}" data-type="{{type}}"><code>{{content}}</code></pre>
+        </div>
+      {{/each}}
+      </div>
+    {{/if_gt}}
+
+    {{subTemplate "article-param-block" params=article.header _hasType=_hasTypeInHeaderFields section="header"}}
+    {{subTemplate "article-param-block" params=article.parameter _hasType=_hasTypeInParameterFields section="parameter"}}
+    {{subTemplate "article-param-block" params=article.success _hasType=_hasTypeInSuccessFields section="success"}}
+    {{subTemplate "article-param-block" params=article.error _col1="Name" _hasType=_hasTypeInErrorFields section="error"}}
+
+    {{subTemplate "article-sample-request" article=article id=id}}
+
+  </article>
+</script>
+
+<script id="template-article-param-block" type="text/x-handlebars-template">
+  {{#if params}}
+    {{#each params.fields}}
+      <h2>{{__ @key}}</h2>
+      <table>
+        <thead>
+          <tr>
+          <th style="width: 30%">{{#if ../../_col1}}{{__ ../../_col1}}{{else}}{{__ "Field"}}{{/if}}</th>
+            {{#if ../../_hasType}}<th style="width: 10%">{{__ "Type"}}</th>{{/if}}
+            <th style="width: {{#if ../../_hasType}}60%{{else}}70%{{/if}}">{{__ "Description"}}</th>
+          </tr>
+        </thead>
+        <tbody>
+      {{#each this}}
+          <tr>
+            <td class="code">{{{splitFill field "." "&nbsp;&nbsp;"}}}{{#if optional}} <span class="label label-optional">{{__ "optional"}}</span>{{/if}}</td>
+            {{#if ../../_hasType}}
+              <td>
+                {{{type}}}
+              </td>
+            {{/if}}
+            <td>
+            {{{nl2br description}}}
+            {{#if defaultValue}}<p class="default-value">{{__ "Default value:"}} <code>{{{defaultValue}}}</code></p>{{/if}}
+            {{#if size}}<p class="type-size">{{__ "Size range:"}} <code>{{{size}}}</code></p>{{/if}}
+            {{#if allowedValues}}<p class="type-size">{{__ "Allowed values:"}}
+              {{#each allowedValues}}
+                <code>{{{this}}}</code>{{#unless @last}}, {{/unless}}
+              {{/each}}
+              </p>
+            {{/if}}
+            </td>
+          </tr>
+      {{/each}}
+        </tbody>
+      </table>
+    {{/each}}
+
+    {{#if_gt params.examples.length compare=0}}
+      <ul class="nav nav-tabs nav-tabs-examples">
+        {{#each params.examples}}
+          <li{{#if_eq @index compare=0}} class="active"{{/if_eq}}>
+            <a href="#{{../section}}-examples-{{../id}}-{{@index}}">{{title}}</a>
+          </li>
+        {{/each}}
+      </ul>
+
+      <div class="tab-content">
+      {{#each params.examples}}
+        <div class="tab-pane{{#if_eq @index compare=0}} active{{/if_eq}}" id="{{../section}}-examples-{{../id}}-{{@index}}">
+        <pre class="prettyprint language-{{type}}" data-type="{{type}}"><code>{{{reformat content type}}}</code></pre>
+        </div>
+      {{/each}}
+      </div>
+    {{/if_gt}}
+
+  {{/if}}
+</script>
+
+<script id="template-article-sample-request" type="text/x-handlebars-template">
+    {{#if article.sampleRequest}}
+      <h2>{{__ "Send a Sample Request"}}</h2>
+      <form class="form-horizontal">
+        <fieldset>
+          <div class="control-group">
+            <div class="controls">
+              <div class="input-prepend">>
+                <span class="add-on">{{__ "url"}}</span>
+                <input type="text" class="input-xxlarge sample-request-url" value="{{article.sampleRequest.0.url}}" />
+              </div>
+            </div>
+          </div>
+
+      {{#if article.header}}
+        {{#if article.header.fields}}
+          <h3>{{__ "Headers"}}</h3>
+          {{#each article.header.fields}}
+            <h4><input type="radio" data-sample-request-header-group-id="sample-request-header-{{@index}}" name="{{../id}}-sample-request-header" value="{{@index}}" class="sample-request-header sample-request-switch"{{#if_eq @index compare=0}} checked{{/if_eq}}> {{@key}}</h4>
+            <div class="{{../id}}-sample-request-header-fields{{#if_gt @index compare=0}} hide{{/if_gt}}">
+              {{#each this}}
+              <div class="control-group">
+                <label class="control-label" for="sample-request-header-field-{{field}}">{{field}}</label>
+                <div class="controls">
+                  <div class="input-append">>
+                    <input type="text" placeholder="{{field}}" class="input-xxlarge sample-request-header" data-sample-request-header-name="{{field}}" data-sample-request-header-group="sample-request-header-{{@../index}}">
+                    <span class="add-on">{{{type}}}</span>
+                  </div>
+                </div>
+              </div>
+              {{/each}}
+            </div>
+          {{/each}}
+        {{/if}}
+      {{/if}}
+
+      {{#if article.parameter}}
+        {{#if article.parameter.fields}}
+          <h3>{{__ "Parameters"}}</h3>
+          {{#each article.parameter.fields}}
+            <h4><input type="radio" data-sample-request-param-group-id="sample-request-param-{{@index}}"  name="{{../id}}-sample-request-param" value="{{@index}}" class="sample-request-param sample-request-switch"{{#if_eq @index compare=0}} checked{{/if_eq}}> {{@key}}</h4>
+            <div class="{{../id}}-sample-request-param-fields{{#if_gt @index compare=0}} hide{{/if_gt}}">
+              {{#each this}}
+              <div class="control-group">
+                <label class="control-label" for="sample-request-param-field-{{field}}">{{field}}</label>
+                <div class="controls">
+                  <div class="input-append">>
+                    <input type="text" placeholder="{{field}}" class="input-xxlarge sample-request-param" data-sample-request-param-name="{{field}}" data-sample-request-param-group="sample-request-param-{{@../index}}">
+                    <span class="add-on">{{{type}}}</span>
+                  </div>
+                </div>
+              </div>
+              {{/each}}
+            </div>
+          {{/each}}
+        {{/if}}
+      {{/if}}
+
+          <div class="control-group">
+            <div class="controls">
+              <button class="btn btn-default sample-request-send" data-sample-request-type="{{article.type}}">{{__ "Send"}}</button>
+            </div>
+          </div>
+
+          <div class="sample-request-response" style="display: none;">
+            <h3>
+              {{__ "Response"}}
+              <button class="btn btn-small btn-default pull-right sample-request-clear">X</button>
+            </h3>
+            <pre class="prettyprint language-json" data-type="json"><code class="sample-request-response-json"></code></pre>
+          </div>
+
+        </fieldset>
+      </form>
+    {{/if}}
+</script>
+
+<script id="template-compare-article" type="text/x-handlebars-template">
+  <article id="api-{{article.group}}-{{article.name}}-{{article.version}}" {{#if hidden}}class="hide"{{/if}} data-group="{{article.group}}" data-name="{{article.name}}" data-version="{{article.version}}" data-compare-version="{{compare.version}}">
+    <div class="pull-left">
+      <h1>{{underscoreToSpace article.group}} - {{{showDiff article.title compare.title}}}</h1>
+    </div>
+
+    <div class="pull-right">
+      <div class="btn-group">
+        <button class="btn btn-success" disabled>
+          <strong>{{article.version}}</strong> {{__ "compared to"}}
+        </button>
+        <button class="version btn btn-danger dropdown-toggle" data-toggle="dropdown">
+          <strong>{{compare.version}}</strong> <span class="caret"></span>
+        </button>
+        <ul class="versions dropdown-menu open-left">
+          <li class="disabled"><a href="#">{{__ "compare changes to:"}}</a></li>
+          <li class="divider"></li>
+  {{#each versions}}
+          <li class="version"><a href="#">{{this}}</a></li>
+  {{/each}}
+        </ul>
+      </div>
+    </div>
+    <div class="clearfix"></div>
+
+    {{#if article.description}}
+      <p>{{{showDiff article.description compare.description "nl2br"}}}</p>
+    {{else}}
+      {{#if compare.description}}
+      <p>{{{showDiff "" compare.description "nl2br"}}}</p>
+      {{/if}}
+    {{/if}}
+
+    <pre class="prettyprint language-html" data-type="{{toLowerCase article.type}}"><code>{{{showDiff article.url compare.url}}}</code></pre>
+
+    {{subTemplate "article-compare-permission" article=article compare=compare}}
+
+    <ul class="nav nav-tabs nav-tabs-examples">
+    {{#each_compare_title article.examples compare.examples}}
+
+      {{#if typeSame}}
+        <li{{#if_eq index compare=0}} class="active"{{/if_eq}}>
+          <a href="#compare-examples-{{../../article.id}}-{{index}}">{{{showDiff source.title compare.title}}}</a>
+        </li>
+      {{/if}}
+
+      {{#if typeIns}}
+        <li{{#if_eq index compare=0}} class="active"{{/if_eq}}>
+          <a href="#compare-examples-{{../../article.id}}-{{index}}"><ins>{{{source.title}}}</ins></a>
+        </li>
+      {{/if}}
+
+      {{#if typeDel}}
+        <li{{#if_eq index compare=0}} class="active"{{/if_eq}}>
+          <a href="#compare-examples-{{../../article.id}}-{{index}}"><del>{{{compare.title}}}</del></a>
+        </li>
+      {{/if}}
+
+    {{/each_compare_title}}
+    </ul>
+
+    <div class="tab-content">
+    {{#each_compare_title article.examples compare.examples}}
+
+      {{#if typeSame}}
+        <div class="tab-pane{{#if_eq index compare=0}} active{{/if_eq}}" id="compare-examples-{{../../article.id}}-{{index}}">
+          <pre class="prettyprint language-{{source.type}}" data-type="{{source.type}}"><code>{{{showDiff source.content compare.content}}}</code></pre>
+        </div>
+      {{/if}}
+
+      {{#if typeIns}}
+        <div class="tab-pane{{#if_eq index compare=0}} active{{/if_eq}}" id="compare-examples-{{../../article.id}}-{{index}}">
+          <pre class="prettyprint language-{{source.type}}" data-type="{{source.type}}"><code>{{{source.content}}}</code></pre>
+        </div>
+      {{/if}}
+
+      {{#if typeDel}}
+        <div class="tab-pane{{#if_eq index compare=0}} active{{/if_eq}}" id="compare-examples-{{../../article.id}}-{{index}}">
+          <pre class="prettyprint language-{{source.type}}" data-type="{{compare.type}}"><code>{{{compare.content}}}</code></pre>
+        </div>
+      {{/if}}
+
+    {{/each_compare_title}}
+    </div>
+
+    {{subTemplate "article-compare-param-block" source=article.parameter compare=compare.parameter _hasType=_hasTypeInParameterFields section="parameter"}}
+    {{subTemplate "article-compare-param-block" source=article.success compare=compare.success _hasType=_hasTypeInSuccessFields section="success"}}
+    {{subTemplate "article-compare-param-block" source=article.error compare=compare.error _col1="Name" _hasType=_hasTypeInErrorFields section="error"}}
+
+    {{subTemplate "article-sample-request" article=article id=id}}
+
+  </article>
+</script>
+
+<script id="template-article-compare-permission" type="text/x-handlebars-template">
+  {{#each_compare_list_field article.permission compare.permission field="name"}}
+    {{#if source}}
+      {{#if typeSame}}
+        {{source.name}}
+        {{#if source.title}}
+          &nbsp;<a href="#" data-toggle="popover" data-placement="right" data-html="true" data-content="{{nl2br source.description}}" title="" data-original-title="{{source.title}}"><span class="label label-info"><i class="icon icon-info-sign icon-white"></i></span></a>
+          {{#unless _last}}, {{/unless}}
+        {{/if}}
+      {{/if}}
+
+      {{#if typeIns}}
+        <ins>{{source.name}}</ins>
+        {{#if source.title}}
+          &nbsp;<a href="#" data-toggle="popover" data-placement="right" data-html="true" data-content="{{nl2br source.description}}" title="" data-original-title="{{source.title}}"><span class="label label-info"><i class="icon icon-info-sign icon-white"></i></span></a>
+          {{#unless _last}}, {{/unless}}
+        {{/if}}
+      {{/if}}
+
+      {{#if typeDel}}
+        <del>{{source.name}}</del>
+        {{#if source.title}}
+          &nbsp;<a href="#" data-toggle="popover" data-placement="right" data-html="true" data-content="{{nl2br source.description}}" title="" data-original-title="{{source.title}}"><span class="label label-info"><i class="icon icon-info-sign icon-white"></i></span></a>
+          {{#unless _last}}, {{/unless}}
+        {{/if}}
+      {{/if}}
+    {{else}}
+      {{#if typeSame}}
+        {{compare.name}}
+        {{#if compare.title}}
+          &nbsp;<a href="#" data-toggle="popover" data-placement="right" data-html="true" data-content="{{nl2br compare.description}}" title="" data-original-title="{{compare.title}}"><span class="label label-info"><i class="icon icon-info-sign icon-white"></i></span></a>
+          {{#unless _last}}, {{/unless}}
+        {{/if}}
+      {{/if}}
+
+      {{#if typeIns}}
+        <ins>{{compare.name}}</ins>
+        {{#if compare.title}}
+          &nbsp;<a href="#" data-toggle="popover" data-placement="right" data-html="true" data-content="{{nl2br compare.description}}" title="" data-original-title="{{compare.title}}"><span class="label label-info"><i class="icon icon-info-sign icon-white"></i></span></a>
+          {{#unless _last}}, {{/unless}}
+        {{/if}}
+      {{/if}}
+
+      {{#if typeDel}}
+        <del>{{compare.name}}</del>
+        {{#if compare.title}}
+          &nbsp;<a href="#" data-toggle="popover" data-placement="right" data-html="true" data-content="{{nl2br compare.description}}" title="" data-original-title="{{compare.title}}"><span class="label label-info"><i class="icon icon-info-sign icon-white"></i></span></a>
+          {{#unless _last}}, {{/unless}}
+        {{/if}}
+      {{/if}}
+    {{/if}}
+  {{/each_compare_list_field}}
+</script>
+
+<script id="template-article-compare-param-block" type="text/x-handlebars-template">
+  {{#if source}}
+    {{#each_compare_keys source.fields compare.fields}}
+      {{#if typeSame}}
+        <h2>{{__ source.key}}</h2>
+        <table>
+        <thead>
+          <tr>
+            <th style="width: 30%">{{#if ../../_col1}}{{__ ../../_col1}}{{else}}{{__ "Field"}}{{/if}}</th>
+            {{#if ../../_hasType}}<th style="width: 10%">{{__ "Type"}}</th>{{/if}}
+            <th style="width: {{#if ../../_hasType}}60%{{else}}70%{{/if}}">{{__ "Description"}}</th>
+          </tr>
+        </thead>
+        {{subTemplate "article-compare-param-block-body" source=source.value compare=compare.value _hasType=../../_hasType}}
+        </table>
+      {{/if}}
+
+      {{#if typeIns}}
+        <h2><ins>{{__ source.key}}</ins></h2>
+        <table class="ins">
+        <thead>
+          <tr>
+            <th style="width: 30%">{{#if ../../_col1}}{{__ ../../_col1}}{{else}}{{__ "Field"}}{{/if}}</th>
+            {{#if ../../_hasType}}<th style="width: 10%">{{__ "Type"}}</th>{{/if}}
+            <th style="width: {{#if ../../_hasType}}60%{{else}}70%{{/if}}">{{__ "Description"}}</th>
+          </tr>
+        </thead>
+        {{subTemplate "article-compare-param-block-body" source=source.value compare=source.value _hasType=../../_hasType}}
+        </table>
+      {{/if}}
+
+      {{#if typeDel}}
+        <h2><del>{{__ compare.key}}</del></h2>
+        <table class="del">
+        <thead>
+          <tr>
+            <th style="width: 30%">{{#if ../../_col1}}{{__ ../../_col1}}{{else}}{{__ "Field"}}{{/if}}</th>
+            {{#if ../../_hasType}}<th style="width: 10%">{{__ "Type"}}</th>{{/if}}
+            <th style="width: {{#if ../../_hasType}}60%{{else}}70%{{/if}}">{{__ "Description"}}</th>
+          </tr>
+        </thead>
+        {{subTemplate "article-compare-param-block-body" source=compare.value compare=compare.value _hasType=../../_hasType}}
+        </table>
+      {{/if}}
+    {{/each_compare_keys}}
+
+    <ul class="nav nav-tabs nav-tabs-examples">
+    {{#each_compare_title source.examples compare.examples}}
+
+      {{#if typeSame}}
+        <li{{#if_eq index compare=0}} class="active"{{/if_eq}}>
+          <a href="#{{../../section}}-compare-examples-{{../../article.id}}-{{index}}">{{{showDiff source.title compare.title}}}</a>
+        </li>
+      {{/if}}
+
+      {{#if typeIns}}
+        <li{{#if_eq index compare=0}} class="active"{{/if_eq}}>
+          <a href="#{{../../section}}-compare-examples-{{../../article.id}}-{{index}}"><ins>{{{source.title}}}</ins></a>
+        </li>
+      {{/if}}
+
+      {{#if typeDel}}
+        <li{{#if_eq index compare=0}} class="active"{{/if_eq}}>
+          <a href="#{{../../section}}-compare-examples-{{../../article.id}}-{{index}}"><del>{{{compare.title}}}</del></a>
+        </li>
+      {{/if}}
+
+    {{/each_compare_title}}
+    </ul>
+
+    <div class="tab-content">
+    {{#each_compare_title source.examples compare.examples}}
+
+      {{#if typeSame}}
+        <div class="tab-pane{{#if_eq index compare=0}} active{{/if_eq}}" id="{{../../section}}-compare-examples-{{../../article.id}}-{{index}}">
+          <pre class="prettyprint language-{{source.type}}" data-type="{{source.type}}"><code>{{{showDiff source.content compare.content}}}</code></pre>
+        </div>
+      {{/if}}
+
+      {{#if typeIns}}
+        <div class="tab-pane{{#if_eq index compare=0}} active{{/if_eq}}" id="{{../../section}}-compare-examples-{{../../article.id}}-{{index}}">
+          <pre class="prettyprint language-{{source.type}}" data-type="{{source.type}}"><code>{{{source.content}}}</code></pre>
+        </div>
+      {{/if}}
+
+      {{#if typeDel}}
+        <div class="tab-pane{{#if_eq index compare=0}} active{{/if_eq}}" id="{{../../section}}-compare-examples-{{../../article.id}}-{{index}}">
+          <pre class="prettyprint language-{{source.type}}" data-type="{{compare.type}}"><code>{{{compare.content}}}</code></pre>
+        </div>
+      {{/if}}
+
+    {{/each_compare_title}}
+    </div>
+
+  {{/if}}
+</script>
+
+<script id="template-article-compare-param-block-body" type="text/x-handlebars-template">
+  <tbody>
+    {{#each_compare_field source compare}}
+      {{#if typeSame}}
+        <tr>
+          <td class="code">
+            {{{splitFill source.field "." "&nbsp;&nbsp;"}}}
+            {{#if source.optional}}
+              {{#if compare.optional}} <span class="label label-optional">{{__ "optional"}}</span>
+              {{else}} <span class="label label-optional label-ins">{{__ "optional"}}</span>
+              {{/if}}
+            {{else}}
+              {{#if compare.optional}} <span class="label label-optional label-del">{{__ "optional"}}</span>{{/if}}
+            {{/if}}
+          </td>
+
+        {{#if source.type}}
+          {{#if compare.type}}
+          <td>{{{showDiff source.type compare.type}}}</td>
+          {{else}}
+          <td>{{{source.type}}}</td>
+          {{/if}}
+        {{else}}
+          {{#if compare.type}}
+          <td>{{{compare.type}}}</td>
+          {{else}}
+            {{#if ../../../../_hasType}}<td></td>{{/if}}
+          {{/if}}
+        {{/if}}
+          <td>
+            {{{showDiff source.description compare.description "nl2br"}}}
+            {{#if source.defaultValue}}<p class="default-value">{{__ "Default value:"}} <code>{{{showDiff source.defaultValue compare.defaultValue}}}</code><p>{{/if}}
+          </td>
+        </tr>
+      {{/if}}
+
+      {{#if typeIns}}
+        <tr class="ins">
+          <td class="code">
+            {{{splitFill source.field "." "&nbsp;&nbsp;"}}}
+            {{#if source.optional}} <span class="label label-optional label-ins">{{__ "optional"}}</span>{{/if}}
+          </td>
+
+        {{#if source.type}}
+          <td>{{{source.type}}}</td>
+        {{else}}
+          {{{typRowTd}}}
+        {{/if}}
+
+          <td>
+            {{{nl2br source.description}}}
+            {{#if source.defaultValue}}<p class="default-value">{{__ "Default value:"}} <code>{{{source.defaultValue}}}</code><p>{{/if}}
+          </td>
+        </tr>
+      {{/if}}
+
+      {{#if typeDel}}
+        <tr class="del">
+          <td class="code">
+            {{{splitFill compare.field "." "&nbsp;&nbsp;"}}}
+            {{#if compare.optional}} <span class="label label-optional label-del">{{__ "optional"}}</span>{{/if}}
+          </td>
+
+        {{#if compare.type}}
+          <td>{{{compare.type}}}</td>
+        {{else}}
+          {{{typRowTd}}}
+        {{/if}}
+
+          <td>
+            {{{nl2br compare.description}}}
+            {{#if compare.defaultValue}}<p class="default-value">{{__ "Default value:"}} <code>{{{compare.defaultValue}}}</code><p>{{/if}}
+          </td>
+        </tr>
+      {{/if}}
+
+    {{/each_compare_field}}
+  </tbody>
+</script>
+
+<div class="container-fluid">
+  <div class="row-fluid">
+    <div id="sidenav" class="span2"></div>
+    <div id="content">
+      <div id="project"></div>
+      <div id="header"></div>
+      <div id="sections"></div>
+      <div id="footer"></div>
+      <div id="generator"></div>
+    </div>
+  </div>
+</div>
+
+<div id="loader">
+  <div class="spinner">
+    <div class="spinner-container container1">
+      <div class="circle1"></div><div class="circle2"></div><div class="circle3"></div><div class="circle4"></div>
+    </div>
+    <div class="spinner-container container2">
+      <div class="circle1"></div><div class="circle2"></div><div class="circle3"></div><div class="circle4"></div>
+    </div>
+    <div class="spinner-container container3">
+      <div class="circle1"></div><div class="circle2"></div><div class="circle3"></div><div class="circle4"></div>
+    </div>
+    <p>Loading...</p>
+  </div>
+</div>
+
+<script data-main="main.js" src="vendor/require.min.js"></script>
+</body>
+</html>

+ 25 - 0
public/kyydoc/locales/ca.js

@@ -0,0 +1,25 @@
+define({
+    ca: {
+        'Allowed values:'             : 'Valors permesos:',
+        'Compare all with predecessor': 'Comparar tot amb versió anterior',
+        'compare changes to:'         : 'comparar canvis amb:',
+        'compared to'                 : 'comparat amb',
+        'Default value:'              : 'Valor per defecte:',
+        'Description'                 : 'Descripció',
+        'Field'                       : 'Camp',
+        'General'                     : 'General',
+        'Generated with'              : 'Generat amb',
+        'Name'                        : 'Nom',
+        'No response values.'         : 'Sense valors en la resposta.',
+        'optional'                    : 'opcional',
+        'Parameter'                   : 'Paràmetre',
+        'Permission:'                 : 'Permisos:',
+        'Response'                    : 'Resposta',
+        'Send'                        : 'Enviar',
+        'Send a Sample Request'       : 'Enviar una petició d\'exemple',
+        'show up to version:'         : 'mostrar versió:',
+        'Size range:'                 : 'Tamany de rang:',
+        'Type'                        : 'Tipus',
+        'url'                         : 'url'
+    }
+});

+ 25 - 0
public/kyydoc/locales/de.js

@@ -0,0 +1,25 @@
+define({
+    de: {
+        'Allowed values:'             : 'Erlaubte Werte:',
+        'Compare all with predecessor': 'Vergleiche alle mit ihren Vorgängern',
+        'compare changes to:'         : 'vergleiche Änderungen mit:',
+        'compared to'                 : 'verglichen mit',
+        'Default value:'              : 'Standardwert:',
+        'Description'                 : 'Beschreibung',
+        'Field'                       : 'Feld',
+        'General'                     : 'Allgemein',
+        'Generated with'              : 'Erstellt mit',
+        'Name'                        : 'Name',
+        'No response values.'         : 'Keine Rückgabewerte.',
+        'optional'                    : 'optional',
+        'Parameter'                   : 'Parameter',
+        'Permission:'                 : 'Berechtigung:',
+        'Response'                    : 'Antwort',
+        'Send'                        : 'Senden',
+        'Send a Sample Request'       : 'Eine Beispielanfrage senden',
+        'show up to version:'         : 'zeige bis zur Version:',
+        'Size range:'                 : 'Größenbereich:',
+        'Type'                        : 'Typ',
+        'url'                         : 'url'
+    }
+});

+ 25 - 0
public/kyydoc/locales/es.js

@@ -0,0 +1,25 @@
+define({
+    es: {
+        'Allowed values:'             : 'Valores permitidos:',
+        'Compare all with predecessor': 'Comparar todo con versión anterior',
+        'compare changes to:'         : 'comparar cambios con:',
+        'compared to'                 : 'comparado con',
+        'Default value:'              : 'Valor por defecto:',
+        'Description'                 : 'Descripción',
+        'Field'                       : 'Campo',
+        'General'                     : 'General',
+        'Generated with'              : 'Generado con',
+        'Name'                        : 'Nombre',
+        'No response values.'         : 'Sin valores en la respuesta.',
+        'optional'                    : 'opcional',
+        'Parameter'                   : 'Parámetro',
+        'Permission:'                 : 'Permisos:',
+        'Response'                    : 'Respuesta',
+        'Send'                        : 'Enviar',
+        'Send a Sample Request'       : 'Enviar una petición de ejemplo',
+        'show up to version:'         : 'mostrar a versión:',
+        'Size range:'                 : 'Tamaño de rango:',
+        'Type'                        : 'Tipo',
+        'url'                         : 'url'
+    }
+});

+ 25 - 0
public/kyydoc/locales/fr.js

@@ -0,0 +1,25 @@
+define({
+    fr: {
+        'Allowed values:'             : 'Valeurs autorisées :',
+        'Compare all with predecessor': 'Tout comparer avec ...',
+        'compare changes to:'         : 'comparer les changements à :',
+        'compared to'                 : 'comparer à',
+        'Default value:'              : 'Valeur par défaut :',
+        'Description'                 : 'Description',
+        'Field'                       : 'Champ',
+        'General'                     : 'Général',
+        'Generated with'              : 'Généré avec',
+        'Name'                        : 'Nom',
+        'No response values.'         : 'Aucune valeur de réponse.',
+        'optional'                    : 'optionnel',
+        'Parameter'                   : 'Paramètre',
+        'Permission:'                 : 'Permission :',
+        'Response'                    : 'Réponse',
+        'Send'                        : 'Envoyer',
+        'Send a Sample Request'       : 'Envoyer une requête représentative',
+        'show up to version:'         : 'Montrer à partir de la version :',
+        'Size range:'                 : 'Ordre de grandeur :',
+        'Type'                        : 'Type',
+        'url'                         : 'url'
+    }
+});

+ 25 - 0
public/kyydoc/locales/it.js

@@ -0,0 +1,25 @@
+define({
+    it: {
+        'Allowed values:'             : 'Valori permessi:',
+        'Compare all with predecessor': 'Confronta tutto con versioni precedenti',
+        'compare changes to:'         : 'confronta modifiche con:',
+        'compared to'                 : 'confrontato con',
+        'Default value:'              : 'Valore predefinito:',
+        'Description'                 : 'Descrizione',
+        'Field'                       : 'Campo',
+        'General'                     : 'Generale',
+        'Generated with'              : 'Creato con',
+        'Name'                        : 'Nome',
+        'No response values.'         : 'Nessnu valore di risposta.',
+        'optional'                    : 'opzionale',
+        'Parameter'                   : 'Parametro',
+        'Permission:'                 : 'Permessi:',
+        'Response'                    : 'Risposta',
+        'Send'                        : 'Invia',
+        'Send a Sample Request'       : 'Invia una richiesta di esempio',
+        'show up to version:'         : 'visualizza la versione:',
+        'Size range:'                 : 'Intervallo dimensione:',
+        'Type'                        : 'Tipo',
+        'url'                         : 'url'
+    }
+});

+ 47 - 0
public/kyydoc/locales/locale.js

@@ -0,0 +1,47 @@
+define([
+    './locales/ca.js',
+    './locales/de.js',
+    './locales/es.js',
+    './locales/fr.js',
+    './locales/it.js',
+    './locales/nl.js',
+    './locales/pl.js',
+    './locales/pt_br.js',
+    './locales/ru.js',
+    './locales/zh.js',
+    './locales/zh_cn.js'
+], function() {
+    var langId = (navigator.language || navigator.userLanguage).toLowerCase().replace('-', '_');
+    var language = langId.substr(0, 2);
+    var locales = {};
+
+    for (index in arguments) {
+        for (property in arguments[index])
+            locales[property] = arguments[index][property];
+    }
+    if ( ! locales['en'])
+        locales['en'] = {};
+
+    if ( ! locales[langId] && ! locales[language])
+        language = 'en';
+
+    var locale = (locales[langId] ? locales[langId] : locales[language]);
+
+    function __(text) {
+        var index = locale[text];
+        if (index === undefined)
+            return text;
+        return index;
+    };
+
+    function setLanguage(language) {
+        locale = locales[language];
+    }
+
+    return {
+        __         : __,
+        locales    : locales,
+        locale     : locale,
+        setLanguage: setLanguage
+    };
+});

+ 25 - 0
public/kyydoc/locales/nl.js

@@ -0,0 +1,25 @@
+define({
+    nl: {
+        'Allowed values:'             : 'Toegestane waarden:',
+        'Compare all with predecessor': 'Vergelijk alle met voorgaande versie',
+        'compare changes to:'         : 'vergelijk veranderingen met:',
+        'compared to'                 : 'vergelijk met',
+        'Default value:'              : 'Standaard waarde:',
+        'Description'                 : 'Omschrijving',
+        'Field'                       : 'Veld',
+        'General'                     : 'Algemeen',
+        'Generated with'              : 'Gegenereerd met',
+        'Name'                        : 'Naam',
+        'No response values.'         : 'Geen response waardes.',
+        'optional'                    : 'optioneel',
+        'Parameter'                   : 'Parameter',
+        'Permission:'                 : 'Permissie:',
+        'Response'                    : 'Antwoorden',
+        'Send'                        : 'Sturen',
+        'Send a Sample Request'       : 'Stuur een sample aanvragen',
+        'show up to version:'         : 'toon tot en met versie:',
+        'Size range:'                 : 'Maatbereik:',
+        'Type'                        : 'Type',
+        'url'                         : 'url'
+    }
+});

+ 25 - 0
public/kyydoc/locales/pl.js

@@ -0,0 +1,25 @@
+define({
+    pl: {
+        'Allowed values:'             : 'Dozwolone wartości:',
+        'Compare all with predecessor': 'Porównaj z poprzednimi wersjami',
+        'compare changes to:'         : 'porównaj zmiany do:',
+        'compared to'                 : 'porównaj do:',
+        'Default value:'              : 'Wartość domyślna:',
+        'Description'                 : 'Opis',
+        'Field'                       : 'Pole',
+        'General'                     : 'Generalnie',
+        'Generated with'              : 'Wygenerowano z',
+        'Name'                        : 'Nazwa',
+        'No response values.'         : 'Brak odpowiedzi.',
+        'optional'                    : 'opcjonalny',
+        'Parameter'                   : 'Parametr',
+        'Permission:'                 : 'Uprawnienia:',
+        'Response'                    : 'Odpowiedź',
+        'Send'                        : 'Wyślij',
+        'Send a Sample Request'       : 'Wyślij przykładowe żądanie',
+        'show up to version:'         : 'pokaż do wersji:',
+        'Size range:'                 : 'Zakres rozmiaru:',
+        'Type'                        : 'Typ',
+        'url'                         : 'url'
+    }
+});

+ 25 - 0
public/kyydoc/locales/pt_br.js

@@ -0,0 +1,25 @@
+define({
+    'pt_br': {
+        'Allowed values:'             : 'Valores permitidos:',
+        'Compare all with predecessor': 'Compare todos com antecessores',
+        'compare changes to:'         : 'comparar alterações com:',
+        'compared to'                 : 'comparado com',
+        'Default value:'              : 'Valor padrão:',
+        'Description'                 : 'Descrição',
+        'Field'                       : 'Campo',
+        'General'                     : 'Geral',
+        'Generated with'              : 'Gerado com',
+        'Name'                        : 'Nome',
+        'No response values.'         : 'Sem valores de resposta.',
+        'optional'                    : 'opcional',
+        'Parameter'                   : 'Parâmetro',
+        'Permission:'                 : 'Permissão:',
+        'Response'                    : 'Resposta',
+        'Send'                        : 'Enviar',
+        'Send a Sample Request'       : 'Enviar um Exemplo de Pedido',
+        'show up to version:'         : 'aparecer para a versão:',
+        'Size range:'                 : 'Faixa de tamanho:',
+        'Type'                        : 'Tipo',
+        'url'                         : 'url'
+    }
+});

+ 25 - 0
public/kyydoc/locales/ru.js

@@ -0,0 +1,25 @@
+define({
+    ru: {
+        'Allowed values:'             : 'Допустимые значения:',
+        'Compare all with predecessor': 'Сравнить с предыдущей версией',
+        'compare changes to:'         : 'сравнить с:',
+        'compared to'                 : 'в сравнении с',
+        'Default value:'              : 'По умолчанию:',
+        'Description'                 : 'Описание',
+        'Field'                       : 'Название',
+        'General'                     : 'Общая информация',
+        'Generated with'              : 'Сгенерировано с помощью',
+        'Name'                        : 'Название',
+        'No response values.'         : 'Нет значений для ответа.',
+        'optional'                    : 'необязательный',
+        'Parameter'                   : 'Параметр',
+        'Permission:'                 : 'Разрешено:',
+        'Response'                    : 'Ответ',
+        'Send'                        : 'Отправить',
+        'Send a Sample Request'       : 'Отправить тестовый запрос',
+        'show up to version:'         : 'показать версию:',
+        'Size range:'                 : 'Ограничения:',
+        'Type'                        : 'Тип',
+        'url'                         : 'URL'
+    }
+});

+ 25 - 0
public/kyydoc/locales/zh.js

@@ -0,0 +1,25 @@
+define({
+    zh: {
+        'Allowed values​​:'             : '允許值:',
+        'Compare all with predecessor': '預先比較所有',
+        'compare changes to:'         : '比較變更:',
+        'compared to'                 : '對比',
+        'Default value:'              : '默認值:',
+        'Description'                 : '描述',
+        'Field'                       : '字段',
+        'General'                     : '概括',
+        'Generated with'              : '生成工具',
+        'Name'                        : '名稱',
+        'No response values​​.'         : '無對應資料.',
+        'optional'                    : '選項',
+        'Parameter'                   : '參數',
+        'Permission:'                 : '允許:',
+        'Response'                    : '回應',
+        'Send'                        : '發送',
+        'Send a Sample Request'       : '發送試用需求',
+        'show up to version:'         : '顯示到版本:',
+        'Size range:'                 : '尺寸範圍:',
+        'Type'                        : '類型',
+        'url'                         : '網址'
+    }
+});

+ 25 - 0
public/kyydoc/locales/zh_cn.js

@@ -0,0 +1,25 @@
+define({
+    'zh_cn': {
+        'Allowed values:'             : '允许值:',
+        'Compare all with predecessor': '与所有较早的比较',
+        'compare changes to:'         : '将当前版本与指定版本比较:',
+        'compared to'                 : '相比于',
+        'Default value:'              : '默认值:',
+        'Description'                 : '描述',
+        'Field'                       : '字段',
+        'General'                     : '概要',
+        'Generated with'              : '基于',
+        'Name'                        : '名称',
+        'No response values.'         : '无返回值.',
+        'optional'                    : '可选',
+        'Parameter'                   : '参数',
+        'Permission:'                 : '权限:',
+        'Response'                    : '返回',
+        'Send'                        : '发送',
+        'Send a Sample Request'       : '发送示例请求',
+        'show up to version:'         : '显示到指定版本:',
+        'Size range:'                 : '取值范围:',
+        'Type'                        : '类型',
+        'url'                         : '网址'
+    }
+});

+ 746 - 0
public/kyydoc/main.js

@@ -0,0 +1,746 @@
+require.config({
+    paths: {
+        bootstrap: './vendor/bootstrap.min',
+        diffMatchPatch: './vendor/diff_match_patch.min',
+        handlebars: './vendor/handlebars.min',
+        handlebarsExtended: './utils/handlebars_helper',
+        jquery: './vendor/jquery.min',
+        locales: './locales/locale',
+        lodash: './vendor/lodash.min',
+        pathToRegexp: './vendor/path-to-regexp/index',
+        prettify: './vendor/prettify/prettify',
+        semver: './vendor/semver.min',
+        utilsSampleRequest: './utils/send_sample_request',
+        webfontloader: './vendor/webfontloader'
+    },
+    shim: {
+        bootstrap: {
+            deps: ['jquery']
+        },
+        diffMatchPatch: {
+            exports: 'diff_match_patch'
+        },
+        handlebars: {
+            exports: 'Handlebars'
+        },
+        handlebarsExtended: {
+            deps: ['jquery', 'handlebars'],
+            exports: 'Handlebars'
+        },
+        prettify: {
+            exports: 'prettyPrint'
+        }
+    },
+    urlArgs: 'v=' + (new Date()).getTime(),
+    waitSeconds: 15
+});
+
+require([
+    'jquery',
+    'lodash',
+    'locales',
+    'handlebarsExtended',
+    './api_project.js',
+    './api_data.js',
+    'prettify',
+    'utilsSampleRequest',
+    'semver',
+    'webfontloader',
+    'bootstrap',
+    'pathToRegexp'
+], function($, _, locale, Handlebars, apiProject, apiData, prettyPrint, sampleRequest, semver, WebFont) {
+
+    // load google web fonts
+    loadGoogleFontCss();
+
+    var api = apiData.api;
+
+    //
+    // Templates
+    //
+    var templateHeader         = Handlebars.compile( $('#template-header').html() );
+    var templateFooter         = Handlebars.compile( $('#template-footer').html() );
+    var templateArticle        = Handlebars.compile( $('#template-article').html() );
+    var templateCompareArticle = Handlebars.compile( $('#template-compare-article').html() );
+    var templateGenerator      = Handlebars.compile( $('#template-generator').html() );
+    var templateProject        = Handlebars.compile( $('#template-project').html() );
+    var templateSections       = Handlebars.compile( $('#template-sections').html() );
+    var templateSidenav        = Handlebars.compile( $('#template-sidenav').html() );
+
+    //
+    // apiProject defaults
+    //
+    if ( ! apiProject.template)
+        apiProject.template = {};
+
+    if (apiProject.template.withCompare == null)
+        apiProject.template.withCompare = true;
+
+    if (apiProject.template.withGenerator == null)
+        apiProject.template.withGenerator = true;
+
+    if (apiProject.template.forceLanguage)
+        locale.setLanguage(apiProject.template.forceLanguage);
+
+    // Setup jQuery Ajax
+    $.ajaxSetup(apiProject.template.jQueryAjaxSetup);
+
+    //
+    // Data transform
+    //
+    // grouped by group
+    var apiByGroup = _.groupBy(api, function(entry) {
+        return entry.group;
+    });
+
+    // grouped by group and name
+    var apiByGroupAndName = {};
+    $.each(apiByGroup, function(index, entries) {
+        apiByGroupAndName[index] = _.groupBy(entries, function(entry) {
+            return entry.name;
+        });
+    });
+
+    //
+    // sort api within a group by title ASC and custom order
+    //
+    var newList = [];
+    var umlauts = { 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // TODO: remove in version 1.0
+    $.each (apiByGroupAndName, function(index, groupEntries) {
+        // get titles from the first entry of group[].name[] (name has versioning)
+        var titles = [];
+        $.each (groupEntries, function(titleName, entries) {
+            var title = entries[0].title;
+            if(title !== undefined) {
+                title.toLowerCase().replace(/[äöüß]/g, function($0) { return umlauts[$0]; });
+                titles.push(title + '#~#' + titleName); // '#~#' keep reference to titleName after sorting
+            }
+        });
+        // sort by name ASC
+        titles.sort();
+
+        // custom order
+        if (apiProject.order)
+            titles = sortByOrder(titles, apiProject.order, '#~#');
+
+        // add single elements to the new list
+        titles.forEach(function(name) {
+            var values = name.split('#~#');
+            var key = values[1];
+            groupEntries[key].forEach(function(entry) {
+                newList.push(entry);
+            });
+        });
+    });
+    // api overwrite with ordered list
+    api = newList;
+
+    //
+    // Group- and Versionlists
+    //
+    var apiGroups = {};
+    var apiGroupTitles = {};
+    var apiVersions = {};
+    apiVersions[apiProject.version] = 1;
+
+    $.each(api, function(index, entry) {
+        apiGroups[entry.group] = 1;
+        apiGroupTitles[entry.group] = entry.groupTitle || entry.group;
+        apiVersions[entry.version] = 1;
+    });
+
+    // sort groups
+    apiGroups = Object.keys(apiGroups);
+    apiGroups.sort();
+
+    // custom order
+    if (apiProject.order)
+        apiGroups = sortByOrder(apiGroups, apiProject.order);
+
+    // sort versions DESC
+    apiVersions = Object.keys(apiVersions);
+    apiVersions.sort(semver.compare);
+    apiVersions.reverse();
+
+    //
+    // create Navigationlist
+    //
+    var nav = [];
+    apiGroups.forEach(function(group) {
+        // Mainmenu entry
+        nav.push({
+            group: group,
+            isHeader: true,
+            title: apiGroupTitles[group]
+        });
+
+        // Submenu
+        var oldName = '';
+        api.forEach(function(entry) {
+            if (entry.group === group) {
+                if (oldName !== entry.name) {
+                    nav.push({
+                        title: entry.title,
+                        group: group,
+                        name: entry.name,
+                        type: entry.type,
+                        version: entry.version
+                    });
+                } else {
+                    nav.push({
+                        title: entry.title,
+                        group: group,
+                        hidden: true,
+                        name: entry.name,
+                        type: entry.type,
+                        version: entry.version
+                    });
+                }
+                oldName = entry.name;
+            }
+        });
+    });
+
+    // Mainmenu Header entry
+    if (apiProject.header) {
+        nav.unshift({
+            group: '_',
+            isHeader: true,
+            title: (apiProject.header.title == null) ? locale.__('General') : apiProject.header.title,
+            isFixed: true
+        });
+    }
+
+    // Mainmenu Footer entry
+    if (apiProject.footer && apiProject.footer.title != null) {
+        nav.push({
+            group: '_footer',
+            isHeader: true,
+            title: apiProject.footer.title,
+            isFixed: true
+        });
+    }
+
+    // render pagetitle
+    var title = apiProject.title ? apiProject.title : 'apiDoc: ' + apiProject.name + ' - ' + apiProject.version;
+    $(document).attr('title', title);
+
+    // remove loader
+    $('#loader').remove();
+
+    // render sidenav
+    var fields = {
+        nav: nav
+    };
+    $('#sidenav').append( templateSidenav(fields) );
+
+    // render Generator
+    $('#generator').append( templateGenerator(apiProject) );
+
+    // render Project
+    _.extend(apiProject, { versions: apiVersions});
+    $('#project').append( templateProject(apiProject) );
+
+    // render apiDoc, header/footer documentation
+    if (apiProject.header)
+        $('#header').append( templateHeader(apiProject.header) );
+
+    if (apiProject.footer)
+        $('#footer').append( templateFooter(apiProject.footer) );
+
+    //
+    // Render Sections and Articles
+    //
+    var articleVersions = {};
+    var content = '';
+    apiGroups.forEach(function(groupEntry) {
+        var articles = [];
+        var oldName = '';
+        var fields = {};
+        var title = groupEntry;
+        var description = '';
+        articleVersions[groupEntry] = {};
+
+        // render all articles of a group
+        api.forEach(function(entry) {
+            if(groupEntry === entry.group) {
+                if (oldName !== entry.name) {
+                    // determine versions
+                    api.forEach(function(versionEntry) {
+                        if (groupEntry === versionEntry.group && entry.name === versionEntry.name) {
+                            if ( ! articleVersions[entry.group].hasOwnProperty(entry.name) ) {
+                                articleVersions[entry.group][entry.name] = [];
+                            }
+                            articleVersions[entry.group][entry.name].push(versionEntry.version);
+                        }
+                    });
+                    fields = {
+                        article: entry,
+                        versions: articleVersions[entry.group][entry.name]
+                    };
+                } else {
+                    fields = {
+                        article: entry,
+                        hidden: true,
+                        versions: articleVersions[entry.group][entry.name]
+                    };
+                }
+
+                // add prefix URL for endpoint
+                if (apiProject.url)
+                    fields.article.url = apiProject.url + fields.article.url;
+
+                addArticleSettings(fields, entry);
+
+                if (entry.groupTitle)
+                    title = entry.groupTitle;
+
+                // TODO: make groupDescription compareable with older versions (not important for the moment)
+                if (entry.groupDescription)
+                    description = entry.groupDescription;
+
+                articles.push({
+                    article: templateArticle(fields),
+                    group: entry.group,
+                    name: entry.name
+                });
+                oldName = entry.name;
+            }
+        });
+
+        // render Section with Articles
+        var fields = {
+            group: groupEntry,
+            title: title,
+            description: description,
+            articles: articles
+        };
+        content += templateSections(fields);
+    });
+    $('#sections').append( content );
+
+    // Bootstrap Scrollspy
+    $(this).scrollspy({ target: '#scrollingNav', offset: 18 });
+
+    // Content-Scroll on Navigation click.
+    $('.sidenav').find('a').on('click', function(e) {
+        e.preventDefault();
+        var id = $(this).attr('href');
+        if ($(id).length > 0)
+            $('html,body').animate({ scrollTop: parseInt($(id).offset().top) }, 400);
+        window.location.hash = $(this).attr('href');
+    });
+
+    // Quickjump on Pageload to hash position.
+    if(window.location.hash) {
+        var id = window.location.hash;
+        if ($(id).length > 0)
+            $('html,body').animate({ scrollTop: parseInt($(id).offset().top) }, 0);
+    }
+
+    /**
+     * Check if Parameter (sub) List has a type Field.
+     * Example: @apiSuccess          varname1 No type.
+     *          @apiSuccess {String} varname2 With type.
+     *
+     * @param {Object} fields
+     */
+    function _hasTypeInFields(fields) {
+        var result = false;
+        $.each(fields, function(name) {
+            if (_.any(fields[name], function(item) { return item.type; }) )
+                result = true;
+        });
+        return result;
+    }
+
+    /**
+     * On Template changes, recall plugins.
+     */
+    function initDynamic() {
+        // bootstrap popover
+        $('a[data-toggle=popover]')
+            .popover()
+            .click(function(e) {
+                e.preventDefault();
+            })
+        ;
+
+        var version = $('#version strong').html();
+        $('#sidenav li').removeClass('is-new');
+        if (apiProject.template.withCompare) {
+            $('#sidenav li[data-version=\'' + version + '\']').each(function(){
+                var group = $(this).data('group');
+                var name = $(this).data('name');
+                var length = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').length;
+                var index  = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').index($(this));
+                if (length === 1 || index === (length - 1))
+                    $(this).addClass('is-new');
+            });
+        }
+
+        // tabs
+        $('.nav-tabs-examples a').click(function (e) {
+            e.preventDefault();
+            $(this).tab('show');
+        });
+        $('.nav-tabs-examples').find('a:first').tab('show');
+
+        // sample request switch
+        $('.sample-request-switch').click(function (e) {
+            var name = '.' + $(this).attr('name') + '-fields';
+            $(name).addClass('hide');
+            $(this).parent().next(name).removeClass('hide');
+        });
+
+        // call scrollspy refresh method
+        $(window).scrollspy('refresh');
+
+        // init modules
+        sampleRequest.initDynamic();
+    }
+    initDynamic();
+
+    // Pre- / Code-Format
+    prettyPrint();
+
+    //
+    // HTML-Template specific jQuery-Functions
+    //
+    // Change Main Version
+    $('#versions li.version a').on('click', function(e) {
+        e.preventDefault();
+
+        var selectedVersion = $(this).html();
+        $('#version strong').html(selectedVersion);
+
+        // hide all
+        $('article').addClass('hide');
+        $('#sidenav li:not(.nav-fixed)').addClass('hide');
+
+        // show 1st equal or lower Version of each entry
+        $('article[data-version]').each(function(index) {
+            var group = $(this).data('group');
+            var name = $(this).data('name');
+            var version = $(this).data('version');
+
+            if (version <= selectedVersion) {
+                if ($('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible').length === 0) {
+                    // enable Article
+                    $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
+                    // enable Navigation
+                    $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
+                    $('#sidenav li.nav-header[data-group=\'' + group + '\']').removeClass('hide');
+                }
+            }
+        });
+
+
+        // show 1st equal or lower Version of each entry
+        $('article[data-version]').each(function(index) {
+            var group = $(this).data('group');
+            $('section#api-' + group).removeClass('hide');
+            if ($('section#api-' + group + ' article:visible').length === 0) {
+                $('section#api-' + group).addClass('hide');
+            } else {
+                $('section#api-' + group).removeClass('hide');
+            }
+        });
+
+        initDynamic();
+        return;
+    });
+
+    // compare all article with their predecessor
+    $('#compareAllWithPredecessor').on('click', changeAllVersionCompareTo);
+
+    // change version of an article
+    $('article .versions li.version a').on('click', changeVersionCompareTo);
+
+    // compare url-parameter
+    $.urlParam = function(name) {
+        var results = new RegExp('[\\?&amp;]' + name + '=([^&amp;#]*)').exec(window.location.href);
+        return (results && results[1]) ? results[1] : null;
+    };
+
+    if ($.urlParam('compare')) {
+        // URL Paramter ?compare=1 is set
+        $('#compareAllWithPredecessor').trigger('click');
+
+        if (window.location.hash) {
+            var id = window.location.hash;
+            $('html,body').animate({ scrollTop: parseInt($(id).offset().top) - 18 }, 0);
+        }
+    }
+
+    /**
+     * Change version of an article to compare it to an other version.
+     */
+    function changeVersionCompareTo(e) {
+        e.preventDefault();
+
+        var $root = $(this).parents('article');
+        var selectedVersion = $(this).html();
+        var $button = $root.find('.version');
+        var currentVersion = $button.find('strong').html();
+        $button.find('strong').html(selectedVersion);
+
+        var group = $root.data('group');
+        var name = $root.data('name');
+        var version = $root.data('version');
+
+        var compareVersion = $root.data('compare-version');
+
+        if (compareVersion === selectedVersion)
+            return;
+
+        if ( ! compareVersion && version == selectedVersion)
+            return;
+
+        if (compareVersion && articleVersions[group][name][0] === selectedVersion || version === selectedVersion) {
+            // the version of the entry is set to the highest version (reset)
+            resetArticle(group, name, version);
+        } else {
+            var $compareToArticle = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + selectedVersion + '\']');
+
+            var sourceEntry = {};
+            var compareEntry = {};
+            $.each(apiByGroupAndName[group][name], function(index, entry) {
+                if (entry.version === version)
+                    sourceEntry = entry;
+                if (entry.version === selectedVersion)
+                    compareEntry = entry;
+            });
+
+            var fields = {
+                article: sourceEntry,
+                compare: compareEntry,
+                versions: articleVersions[group][name]
+            };
+
+            // add unique id
+            // TODO: replace all group-name-version in template with id.
+            fields.article.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
+            fields.article.id = fields.article.id.replace(/\./g, '_');
+
+            fields.compare.id = fields.compare.group + '-' + fields.compare.name + '-' + fields.compare.version;
+            fields.compare.id = fields.compare.id.replace(/\./g, '_');
+
+            var entry = sourceEntry;
+            if (entry.parameter && entry.parameter.fields)
+                fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
+
+            if (entry.error && entry.error.fields)
+                fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
+
+            if (entry.success && entry.success.fields)
+                fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
+
+            if (entry.info && entry.info.fields)
+                fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
+
+            var entry = compareEntry;
+            if (fields._hasTypeInParameterFields !== true && entry.parameter && entry.parameter.fields)
+                fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
+
+            if (fields._hasTypeInErrorFields !== true && entry.error && entry.error.fields)
+                fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
+
+            if (fields._hasTypeInSuccessFields !== true && entry.success && entry.success.fields)
+                fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
+
+            if (fields._hasTypeInInfoFields !== true && entry.info && entry.info.fields)
+                fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
+
+            var content = templateCompareArticle(fields);
+            $root.after(content);
+            var $content = $root.next();
+
+            // Event on.click re-assign
+            $content.find('.versions li.version a').on('click', changeVersionCompareTo);
+
+            // select navigation
+            $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + currentVersion + '\']').addClass('has-modifications');
+
+            $root.remove();
+            // TODO: on change main version or select the highest version re-render
+        }
+
+        initDynamic();
+    }
+
+    /**
+     * Compare all currently selected Versions with their predecessor.
+     */
+    function changeAllVersionCompareTo(e) {
+        e.preventDefault();
+        $('article:visible .versions').each(function(){
+            var $root = $(this).parents('article');
+            var currentVersion = $root.data('version');
+            var $foundElement = null;
+            $(this).find('li.version a').each(function() {
+                var selectVersion = $(this).html();
+                if (selectVersion < currentVersion && ! $foundElement)
+                    $foundElement = $(this);
+            });
+
+            if($foundElement)
+                $foundElement.trigger('click');
+        });
+        initDynamic();
+    }
+
+    /**
+     * Sort the fields.
+     */
+    function sortFields(fields_object) {
+        $.each(fields_object, function (key, fields) {
+
+            var reversed = fields.slice().reverse()
+
+            var max_dot_count = Math.max.apply(null, reversed.map(function (item) {
+                return item.field.split(".").length - 1;
+            }))
+
+            for (var dot_count = 1; dot_count <= max_dot_count; dot_count++) {
+                reversed.forEach(function (item, index) {
+                    var parts = item.field.split(".");
+                    if (parts.length - 1 == dot_count) {
+                        var fields_names = fields.map(function (item) { return item.field; });
+                        if (parts.slice(1).length  >= 1) {
+                            var prefix = parts.slice(0, parts.length - 1).join(".");
+                            var prefix_index = fields_names.indexOf(prefix);
+                            if (prefix_index > -1) {
+                                fields.splice(fields_names.indexOf(item.field), 1);
+                                fields.splice(prefix_index + 1, 0, item);
+                            }
+                        }
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * Add article settings.
+     */
+    function addArticleSettings(fields, entry) {
+        // add unique id
+        // TODO: replace all group-name-version in template with id.
+        fields.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
+        fields.id = fields.id.replace(/\./g, '_');
+
+        if (entry.header && entry.header.fields) {
+            sortFields(entry.header.fields);
+            fields._hasTypeInHeaderFields = _hasTypeInFields(entry.header.fields);
+        }
+
+        if (entry.parameter && entry.parameter.fields) {
+            sortFields(entry.parameter.fields);
+            fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
+        }
+
+        if (entry.error && entry.error.fields) {
+            sortFields(entry.error.fields);
+            fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
+        }
+
+        if (entry.success && entry.success.fields) {
+            sortFields(entry.success.fields);
+            fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
+        }
+
+        if (entry.info && entry.info.fields) {
+            sortFields(entry.info.fields);
+            fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
+        }
+
+        // add template settings
+        fields.template = apiProject.template;
+    }
+
+    /**
+     * Render Article.
+     */
+    function renderArticle(group, name, version) {
+        var entry = {};
+        $.each(apiByGroupAndName[group][name], function(index, currentEntry) {
+            if (currentEntry.version === version)
+                entry = currentEntry;
+        });
+        var fields = {
+            article: entry,
+            versions: articleVersions[group][name]
+        };
+
+        addArticleSettings(fields, entry);
+
+        return templateArticle(fields);
+    }
+
+    /**
+     * Render original Article and remove the current visible Article.
+     */
+    function resetArticle(group, name, version) {
+        var $root = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible');
+        var content = renderArticle(group, name, version);
+
+        $root.after(content);
+        var $content = $root.next();
+
+        // Event on.click muss neu zugewiesen werden (sollte eigentlich mit on automatisch funktionieren... sollte)
+        $content.find('.versions li.version a').on('click', changeVersionCompareTo);
+
+        $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('has-modifications');
+
+        $root.remove();
+        return;
+    }
+
+    /**
+     * Load google fonts.
+     */
+    function loadGoogleFontCss() {
+        WebFont.load({
+            active: function() {
+                // Update scrollspy
+                $(window).scrollspy('refresh')
+            },
+            google: {
+                families: ['Source Code Pro', 'Source Sans Pro:n4,n6,n7']
+            }
+        });
+    }
+
+    /**
+     * Return ordered entries by custom order and append not defined entries to the end.
+     * @param  {String[]} elements
+     * @param  {String[]} order
+     * @param  {String}   splitBy
+     * @return {String[]} Custom ordered list.
+     */
+    function sortByOrder(elements, order, splitBy) {
+        var results = [];
+        order.forEach (function(name) {
+            if (splitBy)
+                elements.forEach (function(element) {
+                    var parts = element.split(splitBy);
+                    var key = parts[1]; // reference keep for sorting
+                    if (key == name)
+                        results.push(element);
+                });
+            else
+                elements.forEach (function(key) {
+                    if (key == name)
+                        results.push(name);
+                });
+        });
+        // Append all other entries that ar not defined in order
+        elements.forEach(function(element) {
+            if (results.indexOf(element) === -1)
+                results.push(element);
+        });
+        return results;
+    }
+
+});

+ 340 - 0
public/kyydoc/utils/handlebars_helper.js

@@ -0,0 +1,340 @@
+define([
+    'locales',
+    'handlebars',
+    'diffMatchPatch'
+], function(locale, Handlebars, DiffMatchPatch) {
+
+    /**
+     * start/stop timer for simple performance check.
+     */
+    var timer;
+    Handlebars.registerHelper('startTimer', function(text) {
+        timer = new Date();
+        return '';
+    });
+
+    Handlebars.registerHelper('stopTimer', function(text) {
+        console.log(new Date() - timer);
+        return '';
+    });
+
+    /**
+     * Return localized Text.
+     * @param string text
+     */
+    Handlebars.registerHelper('__', function(text) {
+        return locale.__(text);
+    });
+
+    /**
+     * Console log.
+     * @param mixed obj
+     */
+    Handlebars.registerHelper('cl', function(obj) {
+        console.log(obj);
+        return '';
+    });
+
+    /**
+     * Replace underscore with space.
+     * @param string text
+     */
+    Handlebars.registerHelper('underscoreToSpace', function(text) {
+        return text.replace(/(_+)/g, ' ');
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('assign', function(name) {
+        if(arguments.length > 0) {
+            var type = typeof(arguments[1]);
+            var arg = null;
+            if(type === 'string' || type === 'number' || type === 'boolean') arg = arguments[1];
+            Handlebars.registerHelper(name, function() { return arg; });
+        }
+        return '';
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('nl2br', function(text) {
+        return _handlebarsNewlineToBreak(text);
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('if_eq', function(context, options) {
+        var compare = context;
+        // Get length if context is an object
+        if (context instanceof Object && ! (options.hash.compare instanceof Object))
+             compare = Object.keys(context).length;
+
+        if (compare === options.hash.compare)
+            return options.fn(this);
+
+        return options.inverse(this);
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('if_gt', function(context, options) {
+        var compare = context;
+        // Get length if context is an object
+        if (context instanceof Object && ! (options.hash.compare instanceof Object))
+             compare = Object.keys(context).length;
+
+        if(compare > options.hash.compare)
+            return options.fn(this);
+
+        return options.inverse(this);
+    });
+
+    /**
+     *
+     */
+    var templateCache = {};
+    Handlebars.registerHelper('subTemplate', function(name, sourceContext) {
+        if ( ! templateCache[name])
+            templateCache[name] = Handlebars.compile($('#template-' + name).html());
+
+        var template = templateCache[name];
+        var templateContext = $.extend({}, this, sourceContext.hash);
+        return new Handlebars.SafeString( template(templateContext) );
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('toLowerCase', function(value) {
+        return (value && typeof value === 'string') ? value.toLowerCase() : '';
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('splitFill', function(value, splitChar, fillChar) {
+        var splits = value.split(splitChar);
+        return new Array(splits.length).join(fillChar) + splits[splits.length - 1];
+    });
+
+    /**
+     * Convert Newline to HTML-Break (nl2br).
+     *
+     * @param {String} text
+     * @returns {String}
+     */
+    function _handlebarsNewlineToBreak(text) {
+        return ('' + text).replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br>' + '$2');
+    }
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('each_compare_list_field', function(source, compare, options) {
+        var fieldName = options.hash.field;
+        var newSource = [];
+        if (source) {
+            source.forEach(function(entry) {
+                var values = entry;
+                values['key'] = entry[fieldName];
+                newSource.push(values);
+            });
+        }
+
+        var newCompare = [];
+        if (compare) {
+            compare.forEach(function(entry) {
+                var values = entry;
+                values['key'] = entry[fieldName];
+                newCompare.push(values);
+            });
+        }
+        return _handlebarsEachCompared('key', newSource, newCompare, options);
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('each_compare_keys', function(source, compare, options) {
+        var newSource = [];
+        if (source) {
+            var sourceFields = Object.keys(source);
+            sourceFields.forEach(function(name) {
+                var values = {};
+                values['value'] = source[name];
+                values['key'] = name;
+                newSource.push(values);
+            });
+        }
+
+        var newCompare = [];
+        if (compare) {
+            var compareFields = Object.keys(compare);
+            compareFields.forEach(function(name) {
+                var values = {};
+                values['value'] = compare[name];
+                values['key'] = name;
+                newCompare.push(values);
+            });
+        }
+        return _handlebarsEachCompared('key', newSource, newCompare, options);
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('each_compare_field', function(source, compare, options) {
+        return _handlebarsEachCompared('field', source, compare, options);
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('each_compare_title', function(source, compare, options) {
+        return _handlebarsEachCompared('title', source, compare, options);
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('reformat', function(source, type){
+        if (type == 'json')
+            try {
+               return JSON.stringify(JSON.parse(source.trim()),null, "    ");
+            } catch(e) {
+
+            }
+        return source
+    });
+
+    /**
+     *
+     */
+    Handlebars.registerHelper('showDiff', function(source, compare, options) {
+        var ds = '';
+        if(source === compare) {
+            ds = source;
+        } else {
+            if( ! source)
+                return compare;
+
+            if( ! compare)
+                return source;
+
+            var d = diffMatchPatch.diff_main(compare, source);
+            diffMatchPatch.diff_cleanupSemantic(d);
+            ds = diffMatchPatch.diff_prettyHtml(d);
+            ds = ds.replace(/&para;/gm, '');
+        }
+        if(options === 'nl2br')
+            ds = _handlebarsNewlineToBreak(ds);
+
+        return ds;
+    });
+
+    /**
+     *
+     */
+    function _handlebarsEachCompared(fieldname, source, compare, options)
+    {
+        var dataList = [];
+        var index = 0;
+        if(source) {
+            source.forEach(function(sourceEntry) {
+                var found = false;
+                if (compare) {
+                    compare.forEach(function(compareEntry) {
+                        if(sourceEntry[fieldname] === compareEntry[fieldname]) {
+                            var data = {
+                                typeSame: true,
+                                source: sourceEntry,
+                                compare: compareEntry,
+                                index: index
+                            };
+                            dataList.push(data);
+                            found = true;
+                            index++;
+                        }
+                    });
+                }
+                if ( ! found) {
+                    var data = {
+                        typeIns: true,
+                        source: sourceEntry,
+                        index: index
+                    };
+                    dataList.push(data);
+                    index++;
+                }
+            });
+        }
+
+        if (compare) {
+            compare.forEach(function(compareEntry) {
+                var found = false;
+                if (source) {
+                    source.forEach(function(sourceEntry) {
+                        if(sourceEntry[fieldname] === compareEntry[fieldname])
+                            found = true;
+                    });
+                }
+                if ( ! found) {
+                    var data = {
+                        typeDel: true,
+                        compare: compareEntry,
+                        index: index
+                    };
+                    dataList.push(data);
+                    index++;
+                }
+            });
+        }
+
+        var ret = '';
+        var length = dataList.length;
+        for (var index in dataList) {
+            if(index == (length - 1))
+                dataList[index]['_last'] = true;
+            ret = ret + options.fn(dataList[index]);
+        }
+        return ret;
+    }
+
+    var diffMatchPatch = new DiffMatchPatch();
+
+    /**
+     * Overwrite Colors
+     */
+    DiffMatchPatch.prototype.diff_prettyHtml = function(diffs) {
+      var html = [];
+      var pattern_amp = /&/g;
+      var pattern_lt = /</g;
+      var pattern_gt = />/g;
+      var pattern_para = /\n/g;
+      for (var x = 0; x < diffs.length; x++) {
+        var op = diffs[x][0];    // Operation (insert, delete, equal)
+        var data = diffs[x][1];  // Text of change.
+        var text = data.replace(pattern_amp, '&amp;').replace(pattern_lt, '&lt;')
+            .replace(pattern_gt, '&gt;').replace(pattern_para, '&para;<br>');
+        switch (op) {
+          case DIFF_INSERT:
+            html[x] = '<ins>' + text + '</ins>';
+            break;
+          case DIFF_DELETE:
+            html[x] = '<del>' + text + '</del>';
+            break;
+          case DIFF_EQUAL:
+            html[x] = '<span>' + text + '</span>';
+            break;
+        }
+      }
+      return html.join('');
+    };
+
+    // Exports
+    return Handlebars;
+});

+ 181 - 0
public/kyydoc/utils/send_sample_request.js

@@ -0,0 +1,181 @@
+define([
+  'jquery'
+], function($) {
+
+  var initDynamic = function() {
+      // Button send
+      $(".sample-request-send").off("click");
+      $(".sample-request-send").on("click", function(e) {
+          e.preventDefault();
+          var $root = $(this).parents("article");
+          var group = $root.data("group");
+          var name = $root.data("name");
+          var version = $root.data("version");
+          sendSampleRequest(group, name, version, $(this).data("sample-request-type"));
+      });
+
+      // Button clear
+      $(".sample-request-clear").off("click");
+      $(".sample-request-clear").on("click", function(e) {
+          e.preventDefault();
+          var $root = $(this).parents("article");
+          var group = $root.data("group");
+          var name = $root.data("name");
+          var version = $root.data("version");
+          clearSampleRequest(group, name, version);
+      });
+  }; // initDynamic
+
+  function sendSampleRequest(group, name, version, type)
+  {
+      var $root = $('article[data-group="' + group + '"][data-name="' + name + '"][data-version="' + version + '"]');
+
+      // Optional header
+      var header = {};
+      $root.find(".sample-request-header:checked").each(function(i, element) {
+          var group = $(element).data("sample-request-header-group-id");
+          $root.find("[data-sample-request-header-group=\"" + group + "\"]").each(function(i, element) {
+            var key = $(element).data("sample-request-header-name");
+            var value = element.value;
+            if ( ! element.optional && element.defaultValue !== '') {
+                value = element.defaultValue;
+            }
+            header[key] = value;
+          });
+      });
+
+      // create JSON dictionary of parameters
+      var param = {};
+      var paramType = {};
+      $root.find(".sample-request-param:checked").each(function(i, element) {
+          var group = $(element).data("sample-request-param-group-id");
+          $root.find("[data-sample-request-param-group=\"" + group + "\"]").each(function(i, element) {
+            var key = $(element).data("sample-request-param-name");
+            var value = element.value;
+            if ( ! element.optional && element.defaultValue !== '') {
+                value = element.defaultValue;
+            }
+            param[key] = value;
+            paramType[key] = $(element).next().text();
+          });
+      });
+
+      // grab user-inputted URL
+      var url = $root.find(".sample-request-url").val();
+
+      // Insert url parameter
+      var pattern = pathToRegexp(url, null);
+      var matches = pattern.exec(url);
+      for (var i = 1; i < matches.length; i++) {
+          var key = matches[i].substr(1);
+          if (param[key] !== undefined) {
+              url = url.replace(matches[i], encodeURIComponent(param[key]));
+
+              // remove URL parameters from list
+              delete param[key];
+          }
+      } // for
+
+      $root.find(".sample-request-response").fadeTo(250, 1);
+      $root.find(".sample-request-response-json").html("Loading...");
+      refreshScrollSpy();
+
+      _.each( param, function( val, key ) {
+          var t = paramType[ key ].toLowerCase();
+          if ( t === 'object' || t === 'array' ) {
+              try {
+                  param[ key ] = JSON.parse( val );
+              } catch (e) {
+              }
+          }
+      });
+
+      // send AJAX request, catch success or error callback
+      var ajaxRequest = {
+          url        : url,
+          headers    : header,
+          data       : param,
+          type       : type.toUpperCase(),
+          success    : displaySuccess,
+          error      : displayError
+      };
+
+      $.ajax(ajaxRequest);
+
+
+      function displaySuccess(data, status, jqXHR) {
+          var jsonResponse;
+          try {
+              jsonResponse = JSON.parse(jqXHR.responseText);
+              jsonResponse = JSON.stringify(jsonResponse, null, 4);
+          } catch (e) {
+              jsonResponse = data;
+          }
+          $root.find(".sample-request-response-json").html(jsonResponse);
+          refreshScrollSpy();
+      };
+
+      function displayError(jqXHR, textStatus, error) {
+          var message = "Error " + jqXHR.status + ": " + error;
+          var jsonResponse;
+          try {
+              jsonResponse = JSON.parse(jqXHR.responseText);
+              jsonResponse = JSON.stringify(jsonResponse, null, 4);
+          } catch (e) {
+              jsonResponse = escape(jqXHR.responseText);
+          }
+
+          if (jsonResponse)
+              message += "<br>" + jsonResponse;
+
+          // flicker on previous error to make clear that there is a new response
+          if($root.find(".sample-request-response").is(":visible"))
+              $root.find(".sample-request-response").fadeTo(1, 0.1);
+
+          $root.find(".sample-request-response").fadeTo(250, 1);
+          $root.find(".sample-request-response-json").html(message);
+          refreshScrollSpy();
+      };
+  }
+
+  function clearSampleRequest(group, name, version)
+  {
+      var $root = $('article[data-group="' + group + '"][data-name="' + name + '"][data-version="' + version + '"]');
+
+      // hide sample response
+      $root.find(".sample-request-response-json").html("");
+      $root.find(".sample-request-response").hide();
+
+      // reset value of parameters
+      $root.find(".sample-request-param").each(function(i, element) {
+          element.value = "";
+      });
+
+      // restore default URL
+      var $urlElement = $root.find(".sample-request-url");
+      $urlElement.val($urlElement.prop("defaultValue"));
+
+      refreshScrollSpy();
+  }
+
+  function refreshScrollSpy()
+  {
+      $('[data-spy="scroll"]').each(function () {
+          $(this).scrollspy("refresh");
+      });
+  }
+
+  function escapeHtml(str) {
+      var div = document.createElement("div");
+      div.appendChild(document.createTextNode(str));
+      return div.innerHTML;
+  }
+
+  /**
+   * Exports.
+   */
+  return {
+      initDynamic: initDynamic
+  };
+
+});

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 9 - 0
public/kyydoc/vendor/bootstrap-responsive.min.css


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 9 - 0
public/kyydoc/vendor/bootstrap.min.css


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 6 - 0
public/kyydoc/vendor/bootstrap.min.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 49 - 0
public/kyydoc/vendor/diff_match_patch.min.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 28 - 0
public/kyydoc/vendor/handlebars.min.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 4 - 0
public/kyydoc/vendor/jquery.min.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 61 - 0
public/kyydoc/vendor/lodash.min.js


+ 21 - 0
public/kyydoc/vendor/path-to-regexp/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com)
+
+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.

+ 204 - 0
public/kyydoc/vendor/path-to-regexp/index.js

@@ -0,0 +1,204 @@
+var isArray = Array.isArray || function (arr) {
+  return Object.prototype.toString.call(arr) == '[object Array]';
+};
+
+/**
+ * Expose `pathToRegexp`.
+ */
+// module.exports = pathToRegexp
+
+/**
+ * The main path matching regexp utility.
+ *
+ * @type {RegExp}
+ */
+var PATH_REGEXP = new RegExp([
+  // Match escaped characters that would otherwise appear in future matches.
+  // This allows the user to escape special characters that won't transform.
+  '(\\\\.)',
+  // Match Express-style parameters and un-named parameters with a prefix
+  // and optional suffixes. Matches appear as:
+  //
+  // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"]
+  // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined]
+  '([\\/.])?(?:\\:(\\w+)(?:\\(((?:\\\\.|[^)])*)\\))?|\\(((?:\\\\.|[^)])*)\\))([+*?])?',
+  // Match regexp special characters that are always escaped.
+  '([.+*?=^!:${}()[\\]|\\/])'
+].join('|'), 'g');
+
+/**
+ * Escape the capturing group by escaping special characters and meaning.
+ *
+ * @param  {String} group
+ * @return {String}
+ */
+function escapeGroup (group) {
+  return group.replace(/([=!:$\/()])/g, '\\$1');
+}
+
+/**
+ * Attach the keys as a property of the regexp.
+ *
+ * @param  {RegExp} re
+ * @param  {Array}  keys
+ * @return {RegExp}
+ */
+function attachKeys (re, keys) {
+  re.keys = keys;
+  return re;
+}
+
+/**
+ * Get the flags for a regexp from the options.
+ *
+ * @param  {Object} options
+ * @return {String}
+ */
+function flags (options) {
+  return options.sensitive ? '' : 'i';
+}
+
+/**
+ * Pull out keys from a regexp.
+ *
+ * @param  {RegExp} path
+ * @param  {Array}  keys
+ * @return {RegExp}
+ */
+function regexpToRegexp (path, keys) {
+  // Use a negative lookahead to match only capturing groups.
+  var groups = path.source.match(/\((?!\?)/g);
+
+  if (groups) {
+    for (var i = 0; i < groups.length; i++) {
+      keys.push({
+        name:      i,
+        delimiter: null,
+        optional:  false,
+        repeat:    false
+      });
+    }
+  }
+
+  return attachKeys(path, keys);
+}
+
+/**
+ * Transform an array into a regexp.
+ *
+ * @param  {Array}  path
+ * @param  {Array}  keys
+ * @param  {Object} options
+ * @return {RegExp}
+ */
+function arrayToRegexp (path, keys, options) {
+  var parts = [];
+
+  for (var i = 0; i < path.length; i++) {
+    parts.push(pathToRegexp(path[i], keys, options).source);
+  }
+
+  var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
+  return attachKeys(regexp, keys);
+}
+
+/**
+ * Replace the specific tags with regexp strings.
+ *
+ * @param  {String} path
+ * @param  {Array}  keys
+ * @return {String}
+ */
+function replacePath (path, keys) {
+  var index = 0;
+
+  function replace (_, escaped, prefix, key, capture, group, suffix, escape) {
+    if (escaped) {
+      return escaped;
+    }
+
+    if (escape) {
+      return '\\' + escape;
+    }
+
+    var repeat   = suffix === '+' || suffix === '*';
+    var optional = suffix === '?' || suffix === '*';
+
+    keys.push({
+      name:      key || index++,
+      delimiter: prefix || '/',
+      optional:  optional,
+      repeat:    repeat
+    });
+
+    prefix = prefix ? ('\\' + prefix) : '';
+    capture = escapeGroup(capture || group || '[^' + (prefix || '\\/') + ']+?');
+
+    if (repeat) {
+      capture = capture + '(?:' + prefix + capture + ')*';
+    }
+
+    if (optional) {
+      return '(?:' + prefix + '(' + capture + '))?';
+    }
+
+    // Basic parameter support.
+    return prefix + '(' + capture + ')';
+  }
+
+  return path.replace(PATH_REGEXP, replace);
+}
+
+/**
+ * Normalize the given path string, returning a regular expression.
+ *
+ * An empty array can be passed in for the keys, which will hold the
+ * placeholder key descriptions. For example, using `/user/:id`, `keys` will
+ * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
+ *
+ * @param  {(String|RegExp|Array)} path
+ * @param  {Array}                 [keys]
+ * @param  {Object}                [options]
+ * @return {RegExp}
+ */
+function pathToRegexp (path, keys, options) {
+  keys = keys || [];
+
+  if (!isArray(keys)) {
+    options = keys;
+    keys = [];
+  } else if (!options) {
+    options = {};
+  }
+
+  if (path instanceof RegExp) {
+    return regexpToRegexp(path, keys, options);
+  }
+
+  if (isArray(path)) {
+    return arrayToRegexp(path, keys, options);
+  }
+
+  var strict = options.strict;
+  var end = options.end !== false;
+  var route = replacePath(path, keys);
+  var endsWithSlash = path.charAt(path.length - 1) === '/';
+
+  // In non-strict mode we allow a slash at the end of match. If the path to
+  // match already ends with a slash, we remove it for consistency. The slash
+  // is valid at the end of a path match, not in the middle. This is important
+  // in non-ending mode, where "/test/" shouldn't match "/test//route".
+  if (!strict) {
+    route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?';
+  }
+
+  if (end) {
+    route += '$';
+  } else {
+    // In non-ending mode, we need the capturing groups to match as much as
+    // possible by using a positive lookahead to the end or next path segment.
+    route += strict && endsWithSlash ? '' : '(?=\\/|$)';
+  }
+
+  return attachKeys(new RegExp('^' + route, flags(options)), keys);
+}

+ 100 - 0
public/kyydoc/vendor/polyfill.js

@@ -0,0 +1,100 @@
+// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
+if (!Object.keys) {
+  Object.keys = (function () {
+    'use strict';
+    var hasOwnProperty = Object.prototype.hasOwnProperty,
+        hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
+        dontEnums = [
+          'toString',
+          'toLocaleString',
+          'valueOf',
+          'hasOwnProperty',
+          'isPrototypeOf',
+          'propertyIsEnumerable',
+          'constructor'
+        ],
+        dontEnumsLength = dontEnums.length;
+
+    return function (obj) {
+      if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
+        throw new TypeError('Object.keys called on non-object');
+      }
+
+      var result = [], prop, i;
+
+      for (prop in obj) {
+        if (hasOwnProperty.call(obj, prop)) {
+          result.push(prop);
+        }
+      }
+
+      if (hasDontEnumBug) {
+        for (i = 0; i < dontEnumsLength; i++) {
+          if (hasOwnProperty.call(obj, dontEnums[i])) {
+            result.push(dontEnums[i]);
+          }
+        }
+      }
+      return result;
+    };
+  }());
+}
+
+//Production steps of ECMA-262, Edition 5, 15.4.4.18
+//Reference: http://es5.github.com/#x15.4.4.18
+if (!Array.prototype.forEach) {
+
+Array.prototype.forEach = function (callback, thisArg) {
+
+ var T, k;
+
+ if (this == null) {
+   throw new TypeError(" this is null or not defined");
+ }
+
+ // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
+ var O = Object(this);
+
+ // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
+ // 3. Let len be ToUint32(lenValue).
+ var len = O.length >>> 0;
+
+ // 4. If IsCallable(callback) is false, throw a TypeError exception.
+ // See: http://es5.github.com/#x9.11
+ if (typeof callback !== "function") {
+   throw new TypeError(callback + " is not a function");
+ }
+
+ // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
+ if (arguments.length > 1) {
+   T = thisArg;
+ }
+
+ // 6. Let k be 0
+ k = 0;
+
+ // 7. Repeat, while k < len
+ while (k < len) {
+
+   var kValue;
+
+   // a. Let Pk be ToString(k).
+   //   This is implicit for LHS operands of the in operator
+   // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
+   //   This step can be combined with c
+   // c. If kPresent is true, then
+   if (k in O) {
+
+     // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
+     kValue = O[k];
+
+     // ii. Call the Call internal method of callback with T as the this value and
+     // argument list containing kValue, k, and O.
+     callback.call(T, kValue, k, O);
+   }
+   // d. Increase k by 1.
+   k++;
+ }
+ // 8. return undefined
+};
+}

+ 101 - 0
public/kyydoc/vendor/prettify.css

@@ -0,0 +1,101 @@
+/* Pretty printing styles. Used with prettify.js. */
+/* Vim sunburst theme by David Leibovic */
+pre .str {
+  color: #65B042;
+}
+/* string  - green */
+pre .kwd {
+  color: #E28964;
+}
+/* keyword - dark pink */
+pre .com {
+  color: #AEAEAE;
+  font-style: italic;
+}
+/* comment - gray */
+pre .typ {
+  color: #89bdff;
+}
+/* type - light blue */
+pre .lit {
+  color: #3387CC;
+}
+/* literal - blue */
+pre .pun {
+  color: #fff;
+}
+/* punctuation - white */
+pre .pln {
+  color: #fff;
+}
+/* plaintext - white */
+pre .tag {
+  color: #89bdff;
+}
+/* html/xml tag    - light blue */
+pre .atn {
+  color: #bdb76b;
+}
+/* html/xml attribute name  - khaki */
+pre .atv {
+  color: #65B042;
+}
+/* html/xml attribute value - green */
+pre .dec {
+  color: #3387CC;
+}
+/* decimal - blue */
+/* Specify class=linenums on a pre to get line numbering */
+ol.linenums {
+  margin-top: 0;
+  margin-bottom: 0;
+  color: #AEAEAE;
+}
+/* IE indents via margin-left */
+li.L0,
+li.L1,
+li.L2,
+li.L3,
+li.L5,
+li.L6,
+li.L7,
+li.L8 {
+  list-style-type: none;
+}
+/* Alternate shading for lines */
+@media print {
+  pre .str {
+    color: #060;
+  }
+  pre .kwd {
+    color: #006;
+    font-weight: bold;
+  }
+  pre .com {
+    color: #600;
+    font-style: italic;
+  }
+  pre .typ {
+    color: #404;
+    font-weight: bold;
+  }
+  pre .lit {
+    color: #044;
+  }
+  pre .pun {
+    color: #440;
+  }
+  pre .pln {
+    color: #000;
+  }
+  pre .tag {
+    color: #006;
+    font-weight: bold;
+  }
+  pre .atn {
+    color: #404;
+  }
+  pre .atv {
+    color: #060;
+  }
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/kyydoc/vendor/prettify/lang-apollo.js


+ 3 - 0
public/kyydoc/vendor/prettify/lang-basic.js

@@ -0,0 +1,3 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^"(?:[^\n\r"\\]|\\.)*(?:"|$)/,a,'"'],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["com",/^REM[^\n\r]*/,a],["kwd",/^\b(?:AND|CLOSE|CLR|CMD|CONT|DATA|DEF ?FN|DIM|END|FOR|GET|GOSUB|GOTO|IF|INPUT|LET|LIST|LOAD|NEW|NEXT|NOT|ON|OPEN|OR|POKE|PRINT|READ|RESTORE|RETURN|RUN|SAVE|STEP|STOP|SYS|THEN|TO|VERIFY|WAIT)\b/,a],["pln",/^[a-z][^\W_]?(?:\$|%)?/i,a],["lit",/^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i,a,"0123456789"],["pun",
+/^.[^\s\w"$%.]*/,a]]),["basic","cbm"]);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 18 - 0
public/kyydoc/vendor/prettify/lang-clj.js


+ 2 - 0
public/kyydoc/vendor/prettify/lang-css.js

@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n\u000c"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]+)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],
+["com",/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}\b/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3 - 0
public/kyydoc/vendor/prettify/lang-dart.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/kyydoc/vendor/prettify/lang-erlang.js


+ 1 - 0
public/kyydoc/vendor/prettify/lang-go.js

@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]);

+ 2 - 0
public/kyydoc/vendor/prettify/lang-hs.js

@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/,
+null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]);

+ 3 - 0
public/kyydoc/vendor/prettify/lang-lisp.js

@@ -0,0 +1,3 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a],
+["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","lsp","scm","ss","rkt"]);

+ 1 - 0
public/kyydoc/vendor/prettify/lang-llvm.js

@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^!?"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["com",/^;[^\n\r]*/,null,";"]],[["pln",/^[!%@](?:[$\-.A-Z_a-z][\w$\-.]*|\d+)/],["kwd",/^[^\W\d]\w*/,null],["lit",/^\d+\.\d+/],["lit",/^(?:\d+|0[Xx][\dA-Fa-f]+)/],["pun",/^[(-*,:<->[\]{}]|\.\.\.$/]]),["llvm","ll"]);

+ 2 - 0
public/kyydoc/vendor/prettify/lang-lua.js

@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],
+["pln",/^[_a-z]\w*/i],["pun",/^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/]]),["lua"]);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 6 - 0
public/kyydoc/vendor/prettify/lang-matlab.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/kyydoc/vendor/prettify/lang-ml.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/kyydoc/vendor/prettify/lang-mumps.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 4 - 0
public/kyydoc/vendor/prettify/lang-n.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3 - 0
public/kyydoc/vendor/prettify/lang-pascal.js


+ 1 - 0
public/kyydoc/vendor/prettify/lang-proto.js

@@ -0,0 +1 @@
+PR.registerLangHandler(PR.sourceDecorator({keywords:"bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true",types:/^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/,cStyleComments:!0}),["proto"]);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/kyydoc/vendor/prettify/lang-r.js


+ 1 - 0
public/kyydoc/vendor/prettify/lang-rd.js

@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\n\r]*/,null,"%"]],[["lit",/^\\(?:cr|l?dots|R|tab)\b/],["kwd",/^\\[@-Za-z]+/],["kwd",/^#(?:ifn?def|endif)/],["pln",/^\\[{}]/],["pun",/^[()[\]{}]+/]]),["Rd","rd"]);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/kyydoc/vendor/prettify/lang-scala.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 0
public/kyydoc/vendor/prettify/lang-sql.js


+ 3 - 0
public/kyydoc/vendor/prettify/lang-tcl.js

@@ -0,0 +1,3 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^{+/,a,"{"],["clo",/^}+/,a,"}"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:after|append|apply|array|break|case|catch|continue|error|eval|exec|exit|expr|for|foreach|if|incr|info|proc|return|set|switch|trace|uplevel|upvar|while)\b/,a],["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",
+/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["tcl"]);

+ 0 - 0
public/kyydoc/vendor/prettify/lang-tex.js


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است