lrf před 2 roky
revize
b303c06174
76 změnil soubory, kde provedl 1852 přidání a 0 odebrání
  1. 29 0
      .autod.conf.js
  2. 1 0
      .eslintignore
  3. 4 0
      .eslintrc
  4. 46 0
      .github/workflows/nodejs.yml
  5. 14 0
      .gitignore
  6. 33 0
      README.md
  7. 40 0
      app/controller/dev/config/.dictData.js
  8. 39 0
      app/controller/dev/config/.dictIndex.js
  9. 13 0
      app/controller/dev/dictData.js
  10. 13 0
      app/controller/dev/dictIndex.js
  11. 12 0
      app/controller/home.js
  12. 39 0
      app/controller/shop/config/.goods.js
  13. 42 0
      app/controller/shop/config/.shop.js
  14. 13 0
      app/controller/shop/goods.js
  15. 13 0
      app/controller/shop/shop.js
  16. 13 0
      app/controller/system/banner.js
  17. 40 0
      app/controller/system/config/.banner.js
  18. 38 0
      app/controller/system/config/.indexModule.js
  19. 37 0
      app/controller/system/config/.serviceContace.js
  20. 13 0
      app/controller/system/indexModule.js
  21. 13 0
      app/controller/system/serviceContace.js
  22. 13 0
      app/controller/trade/afterSale.js
  23. 40 0
      app/controller/trade/config/.afterSale.js
  24. 42 0
      app/controller/trade/config/.order.js
  25. 44 0
      app/controller/trade/config/.orderDetail.js
  26. 13 0
      app/controller/trade/order.js
  27. 13 0
      app/controller/trade/orderDetail.js
  28. 49 0
      app/controller/user/config/.user.js
  29. 13 0
      app/controller/user/user.js
  30. 24 0
      app/model/dev/dictData.js
  31. 22 0
      app/model/dev/dictIndex.js
  32. 31 0
      app/model/shop/goods.js
  33. 29 0
      app/model/shop/shop.js
  34. 25 0
      app/model/system/banner.js
  35. 22 0
      app/model/system/indexModule.js
  36. 21 0
      app/model/system/serviceContace.js
  37. 32 0
      app/model/trade/afterSale.js
  38. 35 0
      app/model/trade/order.js
  39. 36 0
      app/model/trade/orderDetail.js
  40. 26 0
      app/model/user/user.js
  41. 27 0
      app/public/routerRegister.js
  42. 14 0
      app/router.js
  43. 15 0
      app/service/dev/dictData.js
  44. 15 0
      app/service/dev/dictIndex.js
  45. 15 0
      app/service/shop/goods.js
  46. 15 0
      app/service/shop/shop.js
  47. 15 0
      app/service/system/banner.js
  48. 15 0
      app/service/system/indexModule.js
  49. 15 0
      app/service/system/serviceContace.js
  50. 15 0
      app/service/trade/afterSale.js
  51. 15 0
      app/service/trade/order.js
  52. 15 0
      app/service/trade/orderDetail.js
  53. 60 0
      app/service/user/user.js
  54. 71 0
      app/service/util/install.js
  55. 18 0
      app/service/util/jwt.js
  56. 86 0
      app/service/util/token.js
  57. 19 0
      app/z_router/dev/dictData.js
  58. 19 0
      app/z_router/dev/dictIndex.js
  59. 7 0
      app/z_router/dev/index.js
  60. 6 0
      app/z_router/shop/index.js
  61. 19 0
      app/z_router/shop/shop.js
  62. 19 0
      app/z_router/system/banner.js
  63. 8 0
      app/z_router/system/index.js
  64. 19 0
      app/z_router/system/indexModule.js
  65. 19 0
      app/z_router/system/serviceContace.js
  66. 19 0
      app/z_router/trade/afterSale.js
  67. 8 0
      app/z_router/trade/index.js
  68. 19 0
      app/z_router/trade/order.js
  69. 19 0
      app/z_router/trade/orderDetail.js
  70. 6 0
      app/z_router/user/index.js
  71. 23 0
      app/z_router/user/user.js
  72. 74 0
      config/config.default.js
  73. 7 0
      config/config.secret.js
  74. 9 0
      config/plugin.js
  75. 52 0
      package.json
  76. 20 0
      test/app/controller/home.test.js

+ 29 - 0
.autod.conf.js

@@ -0,0 +1,29 @@
+'use strict';
+
+module.exports = {
+  write: true,
+  prefix: '^',
+  plugin: 'autod-egg',
+  test: [
+    'test',
+    'benchmark',
+  ],
+  dep: [
+    'egg',
+    'egg-scripts',
+  ],
+  devdep: [
+    'egg-ci',
+    'egg-bin',
+    'egg-mock',
+    'autod',
+    'autod-egg',
+    'eslint',
+    'eslint-config-egg',
+  ],
+  exclude: [
+    './test/fixtures',
+    './dist',
+  ],
+};
+

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+coverage

+ 4 - 0
.eslintrc

@@ -0,0 +1,4 @@
+{
+  "extends": "eslint-config-egg",
+  "root": true
+}

+ 46 - 0
.github/workflows/nodejs.yml

@@ -0,0 +1,46 @@
+# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
+
+name: Node.js CI
+
+on:
+  push:
+    branches:
+      - main
+      - master
+  pull_request:
+    branches:
+      - main
+      - master
+  schedule:
+    - cron: '0 2 * * *'
+
+jobs:
+  build:
+    runs-on: ${{ matrix.os }}
+
+    strategy:
+      fail-fast: false
+      matrix:
+        node-version: [16]
+        os: [ubuntu-latest, windows-latest, macos-latest]
+
+    steps:
+    - name: Checkout Git Source
+      uses: actions/checkout@v2
+
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+
+    - name: Install Dependencies
+      run: npm i
+
+    - name: Continuous Integration
+      run: npm run ci
+
+    - name: Code Coverage
+      uses: codecov/codecov-action@v1
+      with:
+        token: ${{ secrets.CODECOV_TOKEN }}

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+logs/
+npm-debug.log
+yarn-error.log
+node_modules/
+package-lock.json
+yarn.lock
+coverage/
+.idea/
+run/
+.DS_Store
+*.sw*
+*.un~
+typings/
+.nyc_output/

+ 33 - 0
README.md

@@ -0,0 +1,33 @@
+# server
+
+
+
+## QuickStart
+
+<!-- add docs here for user -->
+
+see [egg docs][egg] for more detail.
+
+### Development
+
+```bash
+$ npm i
+$ npm run dev
+$ open http://localhost:7001/
+```
+
+### Deploy
+
+```bash
+$ npm start
+$ npm stop
+```
+
+### npm scripts
+
+- Use `npm run lint` to check code style.
+- Use `npm test` to run unit test.
+- Use `npm run autod` to auto detect dependencies upgrade, see [autod](https://www.npmjs.com/package/autod) for more detail.
+
+
+[egg]: https://eggjs.org

+ 40 - 0
app/controller/dev/config/.dictData.js

@@ -0,0 +1,40 @@
+module.exports = {
+  create: {
+    requestBody: ['label', 'value', 'sort', 'status'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['label', 'value', 'sort', 'status'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        label: 'label',
+        value: 'value',
+        status: 'status',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 39 - 0
app/controller/dev/config/.dictIndex.js

@@ -0,0 +1,39 @@
+module.exports = {
+  create: {
+    requestBody: ['name', 'code', 'status'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['name', 'code', 'status'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        name: 'name',
+        code: 'code',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 13 - 0
app/controller/dev/dictData.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.dictData.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class DictDataController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.dev.dictData;
+  }
+}
+module.exports = CrudController(DictDataController, meta);

+ 13 - 0
app/controller/dev/dictIndex.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.dictIndex.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class DictIndexController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.dev.dictIndex;
+  }
+}
+module.exports = CrudController(DictIndexController, meta);

+ 12 - 0
app/controller/home.js

@@ -0,0 +1,12 @@
+'use strict';
+
+const Controller = require('egg').Controller;
+
+class HomeController extends Controller {
+  async index() {
+    const { ctx } = this;
+    ctx.body = 'hi, egg';
+  }
+}
+
+module.exports = HomeController;

+ 39 - 0
app/controller/shop/config/.goods.js

@@ -0,0 +1,39 @@
+module.exports = {
+  create: {
+    requestBody: ['sell_money', 'flow_money', 'freight', 'shop', 'name', 'shot_brief', 'send_time', 'specs', 'brief', 'file'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['sell_money', 'flow_money', 'freight', 'shop', 'name', 'shot_brief', 'send_time', 'specs', 'brief', 'file'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        shop: 'shop',
+        name: 'name',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 42 - 0
app/controller/shop/config/.shop.js

@@ -0,0 +1,42 @@
+module.exports = {
+  create: {
+    requestBody: ['name', 'code', 'person', 'phone', 'address', 'file', 'status'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['name', 'code', 'person', 'phone', 'address', 'file', 'status'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        name: 'name',
+        code: 'code',
+        person: 'person',
+        phone: 'phone',
+        status: 'status',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 13 - 0
app/controller/shop/goods.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.goods.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class GoodsController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.shop.goods;
+  }
+}
+module.exports = CrudController(GoodsController, meta);

+ 13 - 0
app/controller/shop/shop.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.shop.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class ShopController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.shop.shop;
+  }
+}
+module.exports = CrudController(ShopController, meta);

+ 13 - 0
app/controller/system/banner.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.banner.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class BannerController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.system.banner;
+  }
+}
+module.exports = CrudController(BannerController, meta);

+ 40 - 0
app/controller/system/config/.banner.js

@@ -0,0 +1,40 @@
+module.exports = {
+  create: {
+    requestBody: ['name', 'url', 'type', 'to', 'is_use'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['name', 'url', 'type', 'to', 'is_use'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        name: 'name',
+        type: 'type',
+        is_use: 'is_use',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 38 - 0
app/controller/system/config/.indexModule.js

@@ -0,0 +1,38 @@
+module.exports = {
+  create: {
+    requestBody: ['name', 'to_type', 'to', 'is_use'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['name', 'to_type', 'to', 'is_use'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        name: 'name',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 37 - 0
app/controller/system/config/.serviceContace.js

@@ -0,0 +1,37 @@
+module.exports = {
+  create: {
+    requestBody: ['phone', 'wx', 'qq', 'email'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['phone', 'wx', 'qq', 'email'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 13 - 0
app/controller/system/indexModule.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.indexModule.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class IndexModuleController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.system.indexModule;
+  }
+}
+module.exports = CrudController(IndexModuleController, meta);

+ 13 - 0
app/controller/system/serviceContace.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.serviceContace.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class ServiceContaceController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.system.serviceContace;
+  }
+}
+module.exports = CrudController(ServiceContaceController, meta);

+ 13 - 0
app/controller/trade/afterSale.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.afterSale.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class AfterSaleController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.trade.afterSale;
+  }
+}
+module.exports = CrudController(AfterSaleController, meta);

+ 40 - 0
app/controller/trade/config/.afterSale.js

@@ -0,0 +1,40 @@
+module.exports = {
+  create: {
+    requestBody: ['total', 'order', 'order_detail', 'customer', 'shop', 'goods', 'type', 'apply_time', 'end_time', 'status'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['total', 'order', 'order_detail', 'customer', 'shop', 'goods', 'type', 'apply_time', 'end_time', 'status'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        type: 'type',
+        apply_time: 'apply_time',
+        end_time: 'end_time',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 42 - 0
app/controller/trade/config/.order.js

@@ -0,0 +1,42 @@
+module.exports = {
+  create: {
+    requestBody: ['total', 'freight', 'customer', 'address', 'shop', 'discount', 'buy_time', 'pay_time', 'pay_id', 'pay_no', 'status'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['total', 'freight', 'customer', 'address', 'shop', 'discount', 'buy_time', 'pay_time', 'pay_id', 'pay_no', 'status'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        customer: 'customer',
+        shop: 'shop',
+        buy_time: 'buy_time',
+        pay_id: 'pay_id',
+        pay_no: 'pay_no',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 44 - 0
app/controller/trade/config/.orderDetail.js

@@ -0,0 +1,44 @@
+module.exports = {
+  create: {
+    requestBody: ['total', 'order', 'shop', 'customer', 'address', 'goods', 'freight', 'buy_time', 'pay_time', 'status'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['total', 'order', 'shop', 'customer', 'address', 'goods', 'freight', 'buy_time', 'pay_time', 'status'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        order: 'order',
+        shop: 'shop',
+        customer: 'customer',
+        address: 'address',
+        buy_time: 'buy_time',
+        pay_time: 'pay_time',
+        status: 'status',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 13 - 0
app/controller/trade/order.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.order.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class OrderController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.trade.order;
+  }
+}
+module.exports = CrudController(OrderController, meta);

+ 13 - 0
app/controller/trade/orderDetail.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.orderDetail.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class OrderDetailController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.trade.orderDetail;
+  }
+}
+module.exports = CrudController(OrderDetailController, meta);

+ 49 - 0
app/controller/user/config/.user.js

@@ -0,0 +1,49 @@
+module.exports = {
+  create: {
+    requestBody: ['name', 'phone', 'password', 'icon', 'birth'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['name', 'phone', 'password', 'icon', 'birth'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        name: 'name',
+        phone: 'phone',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+  resetPwd: {
+    params: ['!id'],
+    requestBody: ['!password'],
+  },
+  login: {
+    requestBody: ['!phone', '!password'],
+  },
+  wxLogin: {
+    requestBody: ['!openid'],
+  },
+};

+ 13 - 0
app/controller/user/user.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.user.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class UserController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.user.user;
+  }
+}
+module.exports = CrudController(UserController, meta);

+ 24 - 0
app/model/dev/dictData.js

@@ -0,0 +1,24 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+// 字典内容
+const dictData = {
+  label: { type: String, required: false, zh: '显示内容' }, //
+  value: { type: String, required: false, zh: '值' }, //
+  sort: { type: Number, required: false, default: '1', zh: '排序' }, //
+  status: { type: String, required: false, default: '0', zh: '状态' }, // 0:正常;1停用
+};
+const schema = new Schema(dictData, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ label: 1 });
+schema.index({ value: 1 });
+schema.index({ status: 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('DictData', schema, 'dictData');
+};

+ 22 - 0
app/model/dev/dictIndex.js

@@ -0,0 +1,22 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+// 字典目录
+const dictIndex = {
+  name: { type: String, required: false, zh: '目录名称' }, //
+  code: { type: String, required: false, zh: '目录编码' }, //
+  status: { type: String, required: false, default: '0', zh: '状态' }, // 0:正常;1:停用
+};
+const schema = new Schema(dictIndex, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ name: 1 });
+schema.index({ code: 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('DictIndex', schema, 'dictIndex');
+};

+ 31 - 0
app/model/shop/goods.js

@@ -0,0 +1,31 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+const MoneyPlugin = require('naf-framework-mongoose-free/lib/model/type-money-plugin');
+
+// 商品信息
+const goods = {
+  shop: { type: String, required: false, zh: '店铺', ref: 'Shop.Shop' }, //
+  name: { type: String, required: false, zh: '商品名称' }, //
+  shot_brief: { type: String, required: false, zh: '简短简介' }, // 长度在50以内
+  send_time: { type: String, required: false, zh: '发货时间' }, //
+  specs: { type: Object, required: false, zh: '规格' }, // {规格名:[候选规格]}
+  brief: { type: String, required: false, zh: '商品介绍' }, //
+  file: { type: Array, required: false, zh: '商品图片' }, //
+};
+const schema = new Schema(goods, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ shop: 1 });
+schema.index({ name: 1 });
+
+schema.plugin(metaPlugin);
+schema.plugin(MoneyPlugin({ zh: '实际销售价格', required: false, key: 'sell_money' }));
+schema.plugin(MoneyPlugin({ zh: '划掉销售价格', required: false, key: 'flow_money' }));
+schema.plugin(MoneyPlugin({ zh: '运费', required: false, key: 'freight' }));
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Goods', schema, 'goods');
+};

+ 29 - 0
app/model/shop/shop.js

@@ -0,0 +1,29 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+// 商店信息
+const shop = {
+  name: { type: String, required: false, zh: '商店名称' }, //
+  code: { type: String, required: false, zh: '店铺编号' }, // 自增,中间件处理
+  person: { type: String, required: false, zh: '店主' }, //
+  phone: { type: String, required: false, zh: '联系电话' }, //
+  address: { type: String, required: false, zh: '地址' }, //
+  file: { type: Array, required: false, zh: '证件照片' }, //
+  status: { type: String, required: false, zh: '店铺状态' }, // 字典:shop_status
+};
+const schema = new Schema(shop, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ name: 1 });
+schema.index({ code: 1 });
+schema.index({ person: 1 });
+schema.index({ phone: 1 });
+schema.index({ status: 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Shop', schema, 'shop');
+};

+ 25 - 0
app/model/system/banner.js

@@ -0,0 +1,25 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+// 首屏滚动广告
+const banner = {
+  name: { type: String, required: false, zh: '名称' }, //
+  url: { type: Array, required: false, zh: '路径' }, //
+  type: { type: String, required: false, zh: '类型' }, // 字典表:banner_type
+  to: { type: String, required: false, zh: '跳转至' }, // 填写路径
+  is_use: { type: String, required: false, default: '0', zh: '是否正在使用' }, // 字典:use
+};
+const schema = new Schema(banner, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ name: 1 });
+schema.index({ type: 1 });
+schema.index({ is_use: 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Banner', schema, 'banner');
+};

+ 22 - 0
app/model/system/indexModule.js

@@ -0,0 +1,22 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+// 首页模块管理
+const indexModule = {
+  name: { type: String, required: false, zh: '模块名称' }, //
+  to_type: { type: String, required: false, zh: '跳转类型' }, // 字典:to_type
+  to: { type: String, required: false, zh: '跳转至' }, //
+  is_use: { type: String, required: false, zh: '是否使用' }, // 字典:use
+};
+const schema = new Schema(indexModule, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ name: 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('IndexModule', schema, 'indexModule');
+};

+ 21 - 0
app/model/system/serviceContace.js

@@ -0,0 +1,21 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+// 联系客服
+const serviceContact = {
+  phone: { type: String, required: false, zh: '联系电话' }, //
+  wx: { type: String, required: false, zh: '微信' }, //
+  qq: { type: String, required: false, zh: 'QQ' }, //
+  email: { type: String, required: false, zh: '邮箱' }, //
+};
+const schema = new Schema(serviceContact, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('ServiceContact ', schema, 'serviceContact ');
+};

+ 32 - 0
app/model/trade/afterSale.js

@@ -0,0 +1,32 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+const MoneyPlugin = require('naf-framework-mongoose-free/lib/model/type-money-plugin');
+
+// 售后进度
+const afterSale = {
+  order: { type: String, required: false, zh: '总订单', ref: 'Trade.Order' }, //
+  order_detail: { type: String, required: false, zh: '订单详情', ref: 'Trade.OrderDetail' }, //
+  customer: { type: String, required: false, zh: '顾客', ref: 'User.Customer' }, //
+  shop: { type: String, required: false, zh: '店铺', ref: 'Shop.Shop' }, //
+  goods: { type: Array, required: false, zh: '需要售后的商品快照清单' }, //
+  type: { type: String, required: false, zh: '售后类型' }, // 字典:afterSale_type
+  apply_time: { type: String, required: false, zh: '售后申请时间' }, //
+  end_time: { type: String, required: false, zh: '售后结束时间' }, //
+  status: { type: String, required: false, zh: '售后状态' }, // 字典:afterSale_status
+};
+const schema = new Schema(afterSale, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ type: 1 });
+schema.index({ apply_time: 1 });
+schema.index({ end_time: 1 });
+
+schema.plugin(metaPlugin);
+schema.plugin(MoneyPlugin({ zh: '总金额', required: false, key: 'total' }));
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('AfterSale', schema, 'afterSale');
+};

+ 35 - 0
app/model/trade/order.js

@@ -0,0 +1,35 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+const MoneyPlugin = require('naf-framework-mongoose-free/lib/model/type-money-plugin');
+
+// 订单
+const order = {
+  customer: { type: String, required: false, zh: '顾客', ref: 'User.Customer' }, //
+  address: { type: String, required: false, zh: '邮寄地址', ref: 'User.address' }, //
+  shop: { type: String, required: false, zh: '店铺', ref: 'Shop.Shop' }, //
+  discount: { type: Array, required: false, zh: '优惠' }, // 优惠情况汇总:e.g.:多个可重叠的优惠券
+  buy_time: { type: String, required: false, zh: '下单时间' }, //
+  pay_time: { type: String, required: false, zh: '支付时间' }, //
+  pay_id: { type: String, required: false, zh: '支付id' }, //
+  pay_no: { type: String, required: false, zh: '支付订单号' }, //
+  status: { type: String, required: false, zh: '订单状态' }, // 字典:order_process
+};
+const schema = new Schema(order, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ customer: 1 });
+schema.index({ shop: 1 });
+schema.index({ buy_time: 1 });
+schema.index({ pay_id: 1 });
+schema.index({ pay_no: 1 });
+
+schema.plugin(metaPlugin);
+schema.plugin(MoneyPlugin({ zh: '总金额', required: false, key: 'total' }));
+schema.plugin(MoneyPlugin({ zh: '运费', required: false, key: 'freight' }));
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Order', schema, 'order');
+};

+ 36 - 0
app/model/trade/orderDetail.js

@@ -0,0 +1,36 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+const MoneyPlugin = require('naf-framework-mongoose-free/lib/model/type-money-plugin');
+
+// 订单详情
+const orderDetail = {
+  order: { type: String, required: false, zh: '总订单', ref: 'Trade.order' }, //
+  shop: { type: String, required: false, zh: '店铺', ref: 'Shop.Shop' }, //
+  customer: { type: String, required: false, zh: '顾客', ref: 'User.Customer' }, //
+  address: { type: String, required: false, zh: '邮寄地址', ref: 'User.address' }, //
+  goods: { type: Array, required: false, zh: '商品快照清单' }, // 下单时,商品的属性设置
+  freight: { type: String, required: false, zh: '运费' }, //
+  buy_time: { type: String, required: false, zh: '下单时间' }, //
+  pay_time: { type: String, required: false, zh: '支付时间' }, //
+  status: { type: String, required: false, zh: '订单状态' }, // 字典:order_process
+};
+const schema = new Schema(orderDetail, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ order: 1 });
+schema.index({ shop: 1 });
+schema.index({ customer: 1 });
+schema.index({ address: 1 });
+schema.index({ buy_time: 1 });
+schema.index({ pay_time: 1 });
+schema.index({ status: 1 });
+
+schema.plugin(metaPlugin);
+schema.plugin(MoneyPlugin({ zh: '总金额', required: false, key: 'total' }));
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('OrderDetail', schema, 'orderDetail');
+};

+ 26 - 0
app/model/user/user.js

@@ -0,0 +1,26 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+const { Secret } = require('naf-framework-mongoose-free/lib/model/schema');
+
+// 顾客
+const customer = {
+  name: { type: String, required: false, zh: '用户名' }, //
+  phone: { type: String, required: false, zh: '手机号' }, //
+  password: { type: Secret, required: false, select: false, zh: '密码' }, //
+  icon: { type: Array, required: false, zh: '头像' }, //
+  birth: { type: String, required: false, zh: '生日' }, //
+};
+const schema = new Schema(customer, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ name: 1 });
+schema.index({ phone: 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Customer', schema, 'customer');
+};

+ 27 - 0
app/public/routerRegister.js

@@ -0,0 +1,27 @@
+'use strict';
+/**
+ * 注册路由
+ * @param {App} app 全局变量
+ * @param {Array} routes 路由数组
+ * @param {String} keyZh 路由主题
+ * @param {String} rkey 路由主位置的标识
+ * @param {String} ckey 路由对应的controller的标识
+ */
+const _ = require('lodash');
+module.exports = (app, routes, keyZh, rkey, ckey) => {
+  const { router, config } = app;
+  const mwares = app.middleware;
+  if (process.env.NODE_ENV === 'development') console.log(`${keyZh}:  ${rkey}`);
+  for (const route of routes) {
+    const { method, path, controller: ctl, zh } = route;
+    let { middleware = [] } = route;
+    if (!method || !path || !ctl) continue;
+    // 拼全路径
+    const allPath = `${config.routePrefix}/${path}`;
+    if (process.env.NODE_ENV === 'development') console.log(`     ${zh}: ${allPath}`);
+    // 处理中间件
+    if (middleware.length > 0) middleware = middleware.map(i => _.get(mwares, i)({ enable: true, model: ckey || rkey }));
+    // 注册路由
+    router[method](zh, allPath, ...middleware, ctl);
+  }
+};

+ 14 - 0
app/router.js

@@ -0,0 +1,14 @@
+'use strict';
+
+/**
+ * @param {Egg.Application} app - egg application
+ */
+module.exports = app => {
+  const { router, controller } = app;
+  router.get('/', controller.home.index);
+  require('./z_router/dev/index')(app); // 开发者使用部分
+  require('./z_router/user/index')(app); // 用户部分
+  require('./z_router/system/index')(app); // 系统部分
+  require('./z_router/shop/index')(app); // 店铺部分
+  require('./z_router/trade/index')(app); // 交易部分
+};

+ 15 - 0
app/service/dev/dictData.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class DictDataService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'dictdata');
+    this.model = this.ctx.model.Dev.DictData;
+  }
+}
+
+module.exports = DictDataService;

+ 15 - 0
app/service/dev/dictIndex.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class DictIndexService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'dictindex');
+    this.model = this.ctx.model.Dev.DictIndex;
+  }
+}
+
+module.exports = DictIndexService;

+ 15 - 0
app/service/shop/goods.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class GoodsService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'goods');
+    this.model = this.ctx.model.Shop.Goods;
+  }
+}
+
+module.exports = GoodsService;

+ 15 - 0
app/service/shop/shop.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class ShopService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'shop');
+    this.model = this.ctx.model.Shop.Shop;
+  }
+}
+
+module.exports = ShopService;

+ 15 - 0
app/service/system/banner.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class BannerService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'banner');
+    this.model = this.ctx.model.System.Banner;
+  }
+}
+
+module.exports = BannerService;

+ 15 - 0
app/service/system/indexModule.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class IndexModuleService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'indexmodule');
+    this.model = this.ctx.model.System.IndexModule;
+  }
+}
+
+module.exports = IndexModuleService;

+ 15 - 0
app/service/system/serviceContace.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class ServiceContaceService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'servicecontace');
+    this.model = this.ctx.model.System.ServiceContace;
+  }
+}
+
+module.exports = ServiceContaceService;

+ 15 - 0
app/service/trade/afterSale.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class AfterSaleService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'aftersale');
+    this.model = this.ctx.model.Trade.AfterSale;
+  }
+}
+
+module.exports = AfterSaleService;

+ 15 - 0
app/service/trade/order.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class OrderService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'order');
+    this.model = this.ctx.model.Trade.Order;
+  }
+}
+
+module.exports = OrderService;

+ 15 - 0
app/service/trade/orderDetail.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 
+class OrderDetailService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'orderdetail');
+    this.model = this.ctx.model.Trade.OrderDetail;
+  }
+}
+
+module.exports = OrderDetailService;

+ 60 - 0
app/service/user/user.js

@@ -0,0 +1,60 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+//
+class UserService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'user');
+    this.model = this.ctx.model.User.User;
+  }
+  async resetPwd({ id }, { password }) {
+    const data = await this.model.findById(id);
+    if (!data) throw new BusinessError(ErrorCode.USER_NOT_EXIST);
+    data.password = { secret: password };
+    await data.save();
+  }
+
+  /**
+   * 登陆
+   * @param {Object} body 登陆参数
+   * @param body.phone 账户
+   * @param body.password 密码
+   */
+  async login({ phone, password }) {
+    const { populate } = this.getRefMods();
+    const user = await this.model.findOne({ phone }, '+password').populate(populate);
+    if (!user) throw new BusinessError(ErrorCode.USER_NOT_EXIST);
+    const { password: upwd, status } = user;
+    if (status !== '0') throw new BusinessError(ErrorCode.USER_NOT_BIND, '该账号处于禁止使用状态');
+    if (password !== upwd.secret) throw new BusinessError(ErrorCode.BAD_PASSWORD);
+    // // 使用redis存储,后续的任何操作进行token的校验
+    // await this.setUserInRedis(user);
+    delete user.password;
+    delete user.meta;
+    delete user.__v;
+    const token = this.ctx.service.util.jwt.encrypt(user);
+    return token;
+  }
+
+  /**
+   * 微信登录
+   * @param {Object} body 登陆参数
+   * @param body.openid 微信小程序的openid
+   */
+  async wxLogin({ openid }) {
+    const { populate } = this.getRefMods();
+    const user = await this.model.findOne({ openid }).populate(populate);
+    if (!user) throw new BusinessError(ErrorCode.USER_NOT_EXIST);
+    const { status } = user;
+    if (status !== '0') throw new BusinessError(ErrorCode.USER_NOT_BIND, '该账号处于禁止使用状态');
+    delete user.meta;
+    delete user.__v;
+    const token = this.ctx.service.util.jwt.encrypt(user);
+    return token;
+  }
+}
+
+module.exports = UserService;

+ 71 - 0
app/service/util/install.js

@@ -0,0 +1,71 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+const path = require('path');
+
+//
+class InstallService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'install');
+    this.dataIndex = path.resolve('app', 'public', 'defaultData');
+  }
+  async init() {
+    await this.initRole();
+    await this.initAdmin();
+    await this.initTestBasic();
+  }
+  /**
+   * 初始化角色数据
+   */
+  async initRole() {
+    const model = this.ctx.model.System.Role;
+    const num = await model.count();
+    if (num > 0) return;
+    const dataPath = path.resolve(this.dataIndex, 'role.js');
+    const list = require(dataPath);
+    await model.insertMany(list);
+  }
+  /**
+   * 初始化管理员
+   */
+  async initAdmin() {
+    const model = this.ctx.model.System.User;
+    const roleModel = this.ctx.model.System.Role;
+    const num = await model.count();
+    if (num > 0) return;
+    // 需要将role中menu的mode为all的数据取出来,这是超级管理员的角色信息
+    const roleData = await roleModel.findOne({ code: 'sadmin' });
+    if (!roleData) return;
+    const { _id: role } = roleData;
+    const dataPath = path.resolve(this.dataIndex, 'user.js');
+    let list = require(dataPath);
+    list = list.map(i => ({ ...i, role }));
+    await model.insertMany(list);
+  }
+  /**
+   * 初始化测试用户
+   */
+  async initTestBasic() {
+    const model = this.ctx.model.Basic;
+    const roleModel = this.ctx.model.System.Role;
+    const userModel = this.ctx.model.System.User;
+    const num = await model.count();
+    if (num > 0) return;
+    const roleData = await roleModel.findOne({ code: 'user' });
+    if (!roleData) return;
+    const { _id: role } = roleData;
+    const dataPath = path.resolve(this.dataIndex, 'basic.js');
+    const list = require(dataPath);
+    const basicList = await model.insertMany(list);
+    const users = basicList.map(i => {
+      const { _id: basic, name, phone } = i;
+      const obj = { role, basic, phone, name, account: name, password: { secret: '111111' } };
+      return obj;
+    });
+    await userModel.insertMany(users);
+  }
+}
+
+module.exports = InstallService;

+ 18 - 0
app/service/util/jwt.js

@@ -0,0 +1,18 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const jwt = require('jsonwebtoken');
+
+// jsonWebToken处理
+class JwtService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'jwt');
+  }
+
+  encrypt(data) {
+    const { secret } = this.config.jwt;
+    const token = jwt.sign(data, secret);
+    return token;
+  }
+}
+
+module.exports = JwtService;

+ 86 - 0
app/service/util/token.js

@@ -0,0 +1,86 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+const { ObjectId } = require('mongoose').Types;
+
+//
+class TokenService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'token');
+    this.redis = this.app.redis;
+    this.tokenTimes = 2; // token的使用次数
+    this.tokenTimeOut = 5 * 60; // x * 60s;token超时时间
+  }
+
+  /**
+   * 生成token
+   * token用于检测是否允许请求接口
+   * 每个用户的每个token有使用次数(上面设置的),
+   * @param {String} id 用户信息id
+   * @property {String} oid 随机ObjectId作为key,在redis中也是使用这个作为key
+   */
+  async initToken(id) {
+    // 生成混合id
+    const oid = ObjectId().toString();
+    const arr = [];
+    // 将用户id翻转
+    const idArr = _.reverse(oid.split(''));
+    const oidArr = id.split('');
+    // 将用户id和混合id进行穿插组合
+    for (let i = 0; i < idArr.length; i++) {
+      const v = idArr[i];
+      const s = oidArr[i];
+      arr.push(v, s);
+    }
+    // 拼接成要返回的token的键
+    const tokenKey = arr.join('');
+    // 设置该用户的token次数
+    await this.redis.set(`token:${oid}`, this.tokenTimes, 'ex', this.tokenTimeOut);
+    return tokenKey;
+  }
+  /**
+   * 从token中取出用户id
+   * @param {String} str token
+   * @return {String} uid 用户id/oid redis的key
+   */
+  getIdFromToken(str) {
+    const idArr = str.split('');
+    const arr = [];
+    const uArr = [];
+    for (let i = 0; i < idArr.length; i += 2) {
+      arr.push(idArr[i]);
+      if (idArr[i + 1]) uArr.push(idArr[i + 1]);
+    }
+    const oid = _.reverse(arr).join('');
+    const uid = uArr.join('');
+    return { oid, uid };
+  }
+
+  /**
+   * 检查并使用token
+   * @param {String} token token
+   */
+  async useToken(token) {
+    const { oid, uid } = this.getIdFromToken(token);
+    let tvalue = await this.redis.get(`token:${oid}`);
+    if (!(tvalue && parseInt(tvalue))) return { token, check: false };
+    tvalue = parseInt(tvalue);
+    if (tvalue - 1 <= 0) {
+      // 把上个token删了
+      await this.redis.del(`token:${oid}`);
+      // 该token用完了,换下一个
+      const newToken = await this.initToken(uid);
+      console.log(newToken);
+      return { token: newToken, check: true, fresh: true };
+    }
+    // token使用次数-1
+    tvalue--;
+    await this.redis.set(`token:${oid}`, tvalue);
+    return { token, check: true };
+  }
+
+}
+
+module.exports = TokenService;

+ 19 - 0
app/z_router/dev/dictData.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'dictData';
+const ckey = 'dev.dictData';
+const keyZh = '字典内容';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 19 - 0
app/z_router/dev/dictIndex.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'dictIndex';
+const ckey = 'dev.dictIndex';
+const keyZh = '字典目录';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 7 - 0
app/z_router/dev/index.js

@@ -0,0 +1,7 @@
+/**
+ * @param {Egg.Application} app - egg application
+ */
+module.exports = app => {
+  require('./dictIndex')(app); // 字典目录
+  require('./dictData')(app); // 字典内容
+};

+ 6 - 0
app/z_router/shop/index.js

@@ -0,0 +1,6 @@
+/**
+ * @param {Egg.Application} app - egg application
+ */
+module.exports = app => {
+  require('./shop')(app); // 店铺
+};

+ 19 - 0
app/z_router/shop/shop.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'shop';
+const ckey = 'shop.shop';
+const keyZh = '店铺信息';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 19 - 0
app/z_router/system/banner.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'banner';
+const ckey = 'system.banner';
+const keyZh = '广告图';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 8 - 0
app/z_router/system/index.js

@@ -0,0 +1,8 @@
+/**
+ * @param {Egg.Application} app - egg application
+ */
+module.exports = app => {
+  require('./banner')(app); // 广告图
+  require('./indexModule')(app); // 图标菜单
+  require('./serviceContace')(app); // 客服信息
+};

+ 19 - 0
app/z_router/system/indexModule.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'indexModule';
+const ckey = 'system.indexModule';
+const keyZh = '首页图标菜单';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 19 - 0
app/z_router/system/serviceContace.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'serviceContace';
+const ckey = 'system.serviceContace';
+const keyZh = '客服信息';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 19 - 0
app/z_router/trade/afterSale.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'afterSale';
+const ckey = 'trade.afterSale';
+const keyZh = '售后';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 8 - 0
app/z_router/trade/index.js

@@ -0,0 +1,8 @@
+/**
+ * @param {Egg.Application} app - egg application
+ */
+module.exports = app => {
+  require('./order')(app); // 总订单
+  require('./orderDetail')(app); // 订单详情
+  require('./afterSale')(app); // 售后
+};

+ 19 - 0
app/z_router/trade/order.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'order';
+const ckey = 'trade.order';
+const keyZh = '总订单';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 19 - 0
app/z_router/trade/orderDetail.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'orderDetail';
+const ckey = 'trade.orderDetail';
+const keyZh = '订单详情';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: [ 'password' ], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 6 - 0
app/z_router/user/index.js

@@ -0,0 +1,6 @@
+/**
+ * @param {Egg.Application} app - egg application
+ */
+module.exports = app => {
+  require('./user')(app); // 用户
+};

+ 23 - 0
app/z_router/user/user.js

@@ -0,0 +1,23 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'user';
+const ckey = 'user.user';
+const keyZh = '用户';
+const routes = [
+  // { method: 'get', path: `${rkey}/getUserInfo`, controller: `${ckey}.getUserInfo`, name: `${ckey}GetUserInfo`, zh: `${keyZh}换取用户信息` },
+  { method: 'post', path: `${rkey}/wxLogin`, controller: `${ckey}.wxLogin`, name: `${ckey}wxLogin`, zh: `${keyZh}微信登陆` },
+  { method: 'post', path: `${rkey}/login`, controller: `${ckey}.login`, name: `${ckey}Login`, zh: `${keyZh}登陆` },
+  { method: 'post', path: `${rkey}/resetPwd/:id`, controller: `${ckey}.resetPwd`, name: `${ckey}ResetPwd`, zh: `重置密码${keyZh}` },
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: ['password'], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, middleware: ['password'], name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 74 - 0
config/config.default.js

@@ -0,0 +1,74 @@
+/* eslint valid-jsdoc: "off" */
+
+'use strict';
+
+/**
+ * @param {Egg.EggAppInfo} appInfo app info
+ */
+const { jwt } = require('./config.secret');
+module.exports = appInfo => {
+  /**
+   * built-in config
+   * @type {Egg.EggAppConfig}
+   **/
+  const config = (exports = {});
+
+  // use for cookie sign key, should change to your own and keep security
+  config.keys = appInfo.name + '_1664237342649_2194';
+
+  // add your middleware config here
+  config.middleware = [];
+
+  // add your user config here
+  const userConfig = {
+    // myAppName: 'egg',
+  };
+  config.checkToken = {
+    enable: false,
+  };
+  // 进程设置
+  config.cluster = {
+    listen: {
+      port: 14001,
+    },
+  };
+  // 数据库设置
+  config.dbName = 'point_shopping';
+  config.mongoose = {
+    url: `mongodb://127.0.0.1:27017/${config.dbName}`, // 120.48.146.1 127.0.0.1
+    options: {
+      user: 'admin',
+      pass: 'admin',
+      authSource: 'admin',
+      useNewUrlParser: true,
+      useCreateIndex: true,
+    },
+  };
+  // jwt设置
+  config.jwt = {
+    ...jwt,
+    expiresIn: '1d',
+    issuer: 'shopping',
+  };
+
+  // redis设置
+  config.redis = {
+    client: {
+      port: 6379, // Redis port
+      host: '127.0.0.1', // Redis host
+      password: '123456',
+      db: 1,
+    },
+  };
+  // 路由设置
+  config.routePrefix = '/point/v1/api';
+  // 中间件
+  config.requestLog = {
+    toMongoDB: true,
+  };
+
+  return {
+    ...config,
+    ...userConfig,
+  };
+};

+ 7 - 0
config/config.secret.js

@@ -0,0 +1,7 @@
+'use strict';
+
+module.exports = {
+  jwt: {
+    secret: 'Ziyouyanfa!@#',
+  },
+};

+ 9 - 0
config/plugin.js

@@ -0,0 +1,9 @@
+'use strict';
+
+/** @type Egg.EggPlugin */
+module.exports = {
+  // had enabled by egg
+  // static: {
+  //   enable: true,
+  // }
+};

+ 52 - 0
package.json

@@ -0,0 +1,52 @@
+{
+  "name": "server",
+  "version": "1.0.0",
+  "description": "",
+  "private": true,
+  "egg": {
+    "framework": "naf-framework-mongoose-free"
+  },
+  "dependencies": {
+    "egg": "^2",
+    "egg-redis": "^2.4.0",
+    "egg-scripts": "^2",
+    "jsonwebtoken": "^8.5.1",
+    "lodash": "^4.17.21",
+    "moment": "^2.29.4",
+    "naf-framework-mongoose-free": "^0.0.36"
+  },
+  "devDependencies": {
+    "autod": "^3",
+    "autod-egg": "^1",
+    "egg-bin": "^4",
+    "egg-ci": "^2",
+    "egg-mock": "^4",
+    "eslint": "^8",
+    "eslint-config-egg": "^12"
+  },
+  "engines": {
+    "node": ">=16.0.0"
+  },
+  "scripts": {
+    "start": "egg-scripts start --daemon --title=egg-server-server",
+    "stop": "egg-scripts stop --title=egg-server-server",
+    "dev": "egg-bin dev",
+    "debug": "egg-bin debug",
+    "test": "npm run lint -- --fix && npm run test-local",
+    "test-local": "egg-bin test",
+    "cov": "egg-bin cov",
+    "lint": "eslint .",
+    "ci": "npm run lint && npm run cov",
+    "autod": "autod"
+  },
+  "ci": {
+    "version": "16",
+    "type": "github"
+  },
+  "repository": {
+    "type": "git",
+    "url": ""
+  },
+  "author": "",
+  "license": "MIT"
+}

+ 20 - 0
test/app/controller/home.test.js

@@ -0,0 +1,20 @@
+'use strict';
+
+const { app, assert } = require('egg-mock/bootstrap');
+
+describe('test/app/controller/home.test.js', () => {
+  it('should assert', async () => {
+    const pkg = require('../../../package.json');
+    assert(app.config.keys.startsWith(pkg.name));
+
+    // const ctx = app.mockContext({});
+    // yield ctx.service.xx();
+  });
+
+  it('should GET /', async () => {
+    return app.httpRequest()
+      .get('/')
+      .expect('hi, egg')
+      .expect(200);
+  });
+});