├── client ├── utils │ ├── wechat.js │ └── util.js ├── pages │ ├── login │ │ ├── login.json │ │ ├── login.wxss │ │ ├── login.wxml │ │ └── login.js │ ├── test │ │ ├── test.json │ │ ├── test.wxss │ │ ├── test.wxml │ │ ├── test.js │ │ ├── index.wxml │ │ ├── index.wxss │ │ └── index.js │ ├── index │ │ ├── index.json │ │ ├── rule.wxss │ │ ├── rule.json │ │ ├── user-unlogin.png │ │ ├── index.wxss │ │ ├── rule.js │ │ ├── rule.wxml │ │ ├── index.wxml │ │ └── index.js │ ├── chat │ │ ├── chat.json │ │ ├── chat.wxml │ │ ├── chat.js │ │ └── chat.wxss │ ├── shouhuo │ │ ├── shouhuo.wxss │ │ ├── shouhuo.json │ │ ├── shouhuo.wxml │ │ └── shouhuo.js │ ├── fahuo │ │ ├── fahuo.json │ │ ├── fahuo.wxss │ │ ├── fahuo.wxml │ │ └── fahuo.js │ ├── yaoqiu │ │ ├── yaoqiu.json │ │ ├── yaoqiu.wxss │ │ ├── yaoqiu.wxml │ │ └── yaoqiu.js │ ├── addCgi │ │ ├── addCgi.json │ │ ├── code1.png │ │ ├── code2.png │ │ ├── addCgi.wxml │ │ ├── addCgi.js │ │ └── addCgi.wxss │ ├── userCenter │ │ ├── user.json │ │ ├── user.wxss │ │ ├── user.js │ │ └── user.wxml │ └── libs │ │ ├── qqmap-wx-jssdk.min.js │ │ └── qqmap-wx-jssdk.js ├── app.wxss ├── images │ ├── logo.png │ ├── refresh.png │ ├── icon │ │ ├── cake.png │ │ ├── eat.png │ │ ├── end.png │ │ ├── file.png │ │ ├── kefu.png │ │ ├── key.png │ │ ├── order.png │ │ ├── other.png │ │ ├── shuma.png │ │ ├── start.png │ │ ├── user.png │ │ ├── advice.png │ │ ├── clothes.png │ │ ├── flower.png │ │ ├── redbag.png │ │ ├── shezhi.png │ │ ├── tianjia.png │ │ ├── weight.png │ │ ├── yaopin.png │ │ ├── zhaomu.png │ │ ├── kuaidiyuan.png │ │ ├── newredbag.png │ │ └── songredbag.png │ └── location.png ├── sitemap.json ├── app.js ├── vendor │ └── wafer2-client-sdk │ │ ├── package.json │ │ ├── lib │ │ ├── utils.js │ │ ├── session.js │ │ ├── constants.js │ │ ├── wxTunnel.js │ │ ├── request.js │ │ ├── login.js │ │ └── tunnel.js │ │ ├── index.js │ │ ├── LICENSE │ │ └── README.md ├── package.json ├── config.js ├── app.json ├── LICENSE └── weui.wxss ├── README.md ├── server ├── controllers │ ├── upload.js │ ├── login.js │ ├── user.js │ ├── helloworld.js │ ├── index.js │ ├── message.js │ └── tunnel.js ├── process.prod.json ├── .eslintrc.json ├── nodemon.json ├── config.local.js.example ├── app.js ├── tools.md ├── middlewares │ ├── bodyparser.js │ └── response.js ├── qcloud.js ├── config.js ├── tools │ ├── initdb.js │ └── cAuth.sql ├── package.json ├── routes │ └── index.js └── README.md └── .gitignore /client/utils/wechat.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/pages/login/login.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /client/pages/test/test.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /client/app.wxss: -------------------------------------------------------------------------------- 1 | @import 'weui.wxss'; 2 | -------------------------------------------------------------------------------- /client/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /client/pages/test/test.wxss: -------------------------------------------------------------------------------- 1 | /* pages/test/test.wxss */ -------------------------------------------------------------------------------- /client/pages/index/rule.wxss: -------------------------------------------------------------------------------- 1 | /* pages/index/rule.wxss */ -------------------------------------------------------------------------------- /client/pages/chat/chat.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /client/pages/shouhuo/shouhuo.wxss: -------------------------------------------------------------------------------- 1 | /* pages/shouhuo/shouhuo.wxss */ -------------------------------------------------------------------------------- /client/pages/fahuo/fahuo.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "选择发货地址" 3 | } -------------------------------------------------------------------------------- /client/pages/index/rule.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "计价规则" 3 | } -------------------------------------------------------------------------------- /client/pages/yaoqiu/yaoqiu.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "物品信息" 3 | } -------------------------------------------------------------------------------- /client/pages/addCgi/addCgi.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "快速添加 CGI" 3 | } -------------------------------------------------------------------------------- /client/pages/shouhuo/shouhuo.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "选择收获地址" 3 | } -------------------------------------------------------------------------------- /client/pages/userCenter/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "个人中心" 3 | } -------------------------------------------------------------------------------- /client/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/logo.png -------------------------------------------------------------------------------- /client/pages/test/test.wxml: -------------------------------------------------------------------------------- 1 | 2 | pages/test/test.wxml 3 | -------------------------------------------------------------------------------- /client/images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/refresh.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 项目暂未上线,提供演示视频 2 | ## 演示视频链接 3 | (链接:https://pan.baidu.com/s/18BD5VwNrvcoFyadGfovJww 提取码:d2mj) 4 | -------------------------------------------------------------------------------- /client/images/icon/cake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/cake.png -------------------------------------------------------------------------------- /client/images/icon/eat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/eat.png -------------------------------------------------------------------------------- /client/images/icon/end.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/end.png -------------------------------------------------------------------------------- /client/images/icon/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/file.png -------------------------------------------------------------------------------- /client/images/icon/kefu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/kefu.png -------------------------------------------------------------------------------- /client/images/icon/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/key.png -------------------------------------------------------------------------------- /client/images/icon/order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/order.png -------------------------------------------------------------------------------- /client/images/icon/other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/other.png -------------------------------------------------------------------------------- /client/images/icon/shuma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/shuma.png -------------------------------------------------------------------------------- /client/images/icon/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/start.png -------------------------------------------------------------------------------- /client/images/icon/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/user.png -------------------------------------------------------------------------------- /client/images/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/location.png -------------------------------------------------------------------------------- /client/images/icon/advice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/advice.png -------------------------------------------------------------------------------- /client/images/icon/clothes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/clothes.png -------------------------------------------------------------------------------- /client/images/icon/flower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/flower.png -------------------------------------------------------------------------------- /client/images/icon/redbag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/redbag.png -------------------------------------------------------------------------------- /client/images/icon/shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/shezhi.png -------------------------------------------------------------------------------- /client/images/icon/tianjia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/tianjia.png -------------------------------------------------------------------------------- /client/images/icon/weight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/weight.png -------------------------------------------------------------------------------- /client/images/icon/yaopin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/yaopin.png -------------------------------------------------------------------------------- /client/images/icon/zhaomu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/zhaomu.png -------------------------------------------------------------------------------- /client/pages/addCgi/code1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/pages/addCgi/code1.png -------------------------------------------------------------------------------- /client/pages/addCgi/code2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/pages/addCgi/code2.png -------------------------------------------------------------------------------- /client/images/icon/kuaidiyuan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/kuaidiyuan.png -------------------------------------------------------------------------------- /client/images/icon/newredbag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/newredbag.png -------------------------------------------------------------------------------- /client/images/icon/songredbag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/images/icon/songredbag.png -------------------------------------------------------------------------------- /client/pages/shouhuo/shouhuo.wxml: -------------------------------------------------------------------------------- 1 | 2 | pages/shouhuo/shouhuo.wxml 3 | -------------------------------------------------------------------------------- /client/pages/index/user-unlogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmc1214/fmeituan/HEAD/client/pages/index/user-unlogin.png -------------------------------------------------------------------------------- /client/pages/login/login.wxss: -------------------------------------------------------------------------------- 1 | /* pages/login/login.wxss */ 2 | #queding{ 3 | background: #FDA81C; 4 | } 5 | #shouquan{ 6 | margin-top: 100rpx; 7 | } -------------------------------------------------------------------------------- /client/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /server/controllers/upload.js: -------------------------------------------------------------------------------- 1 | const { uploader } = require('../qcloud') 2 | 3 | module.exports = async ctx => { 4 | // 获取上传之后的结果 5 | // 具体可以查看: 6 | const data = await uploader(ctx.req) 7 | 8 | ctx.state.data = data 9 | } 10 | -------------------------------------------------------------------------------- /server/controllers/login.js: -------------------------------------------------------------------------------- 1 | // 登录授权接口 2 | module.exports = async (ctx, next) => { 3 | // 通过 Koa 中间件进行登录之后 4 | // 登录信息会被存储到 ctx.state.$wxInfo 5 | // 具体查看: 6 | if (ctx.state.$wxInfo.loginState) { 7 | ctx.state.data = ctx.state.$wxInfo.userinfo 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/pages/fahuo/fahuo.wxss: -------------------------------------------------------------------------------- 1 | /* pages/fahuo/fahuo.wxss */ 2 | #addAddress{ 3 | width:95%; 4 | position:fixed; 5 | top:540px; 6 | border-bottom: 1px solid #ccc; 7 | text-align: center; 8 | } 9 | .weui-cell__bd span{ 10 | position: relative; 11 | bottom: 15px; 12 | left: -10px; 13 | } -------------------------------------------------------------------------------- /server/process.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "session", 3 | "script": "app.js", 4 | "cwd": "./", 5 | "exec_mode": "fork", 6 | "watch": true, 7 | "ignore_watch": ["tmp"], 8 | "env": { 9 | "NODE_ENV": "production" 10 | }, 11 | "engines": { 12 | "node": ">=7.6" 13 | } 14 | } -------------------------------------------------------------------------------- /server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "parserOptions": { 5 | "sourceType": "module" 6 | }, 7 | "extends": "standard", 8 | "rules": { 9 | "indent": [2, 4, { "SwitchCase": 1 }], 10 | "arrow-parens": 0, 11 | "generator-star-spacing": 0 12 | }, 13 | "env": { 14 | "mocha": true 15 | } 16 | } -------------------------------------------------------------------------------- /client/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /* pages/index/index.wxss */ 2 | 3 | .title{ 4 | font-size: 28rpx; 5 | font-family: "微软雅黑"; 6 | margin-left: 170rpx; 7 | } 8 | .mainmap{ 9 | width: 100%; 10 | height:600rpx; 11 | } 12 | 13 | .weui-footer{ 14 | margin-top: 15px; 15 | } 16 | 17 | .refresh{ 18 | position: relative; 19 | top: 85%; 20 | left: 90%; 21 | } -------------------------------------------------------------------------------- /server/controllers/user.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx, next) => { 2 | // 通过 Koa 中间件进行登录态校验之后 3 | // 登录信息会被存储到 ctx.state.$wxInfo 4 | // 具体查看: 5 | if (ctx.state.$wxInfo.loginState === 1) { 6 | // loginState 为 1,登录态校验成功 7 | ctx.state.data = ctx.state.$wxInfo.userinfo 8 | } else { 9 | ctx.state.code = -1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "restartable": "rs", 3 | "ignore": [ 4 | ".git", 5 | "node_modules/**/node_modules" 6 | ], 7 | "verbose": true, 8 | "execMap": { 9 | "js": "node --harmony" 10 | }, 11 | "env": { 12 | "NODE_ENV": "local", 13 | "DEBUG": "*,-nodemon:*,-nodemon,-knex:pool" 14 | }, 15 | "ext": "js json" 16 | } 17 | -------------------------------------------------------------------------------- /client/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 微信小程序的入口文件 3 | */ 4 | 5 | var qcloud = require('./vendor/wafer2-client-sdk/index'); 6 | var config = require('./config'); 7 | 8 | App({ 9 | /** 10 | * 小程序初始化时执行,我们初始化客户端的登录地址,以支持所有的会话操作 11 | */ 12 | globalData: { 13 | 14 | }, 15 | onLaunch() { 16 | qcloud.setLoginUrl(config.service.loginUrl); 17 | } 18 | 19 | 20 | }); -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wafer2-client-sdk", 3 | "version": "2.0.0", 4 | "description": "Wafer client SDK", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/tencentyun/wafer2-client-sdk.git" 12 | }, 13 | "author": "CFETeam", 14 | "license": "MIT" 15 | } 16 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qcloud-weapp-client-demo", 3 | "version": "2.0.0", 4 | "description": "腾讯云微信小程序客户端 DEMO", 5 | "main": "app.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/tencentyun/weapp-client-demo.git" 9 | }, 10 | "keywords": [], 11 | "author": "CFETeam", 12 | "license": "MIT", 13 | "dependencies": { 14 | "wafer2-client-sdk": "^2.1.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/controllers/helloworld.js: -------------------------------------------------------------------------------- 1 | const { mysql } = require('../qcloud') 2 | 3 | 4 | module.exports = async ctx => { 5 | var userId = 1 6 | // 增 7 | var user = { 8 | 9 | username:"xmq", 10 | password:'qwe' 11 | } 12 | await mysql("userInfo").insert(user) 13 | await mysql("userInfo").update({username:'谢名丞'}).where({userId}) 14 | 15 | var res = await mysql("userInfo").where({ userId }).first() 16 | 17 | ctx.state.data = res 18 | } -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/lib/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 拓展对象 4 | */ 5 | exports.extend = function extend(target) { 6 | var sources = Array.prototype.slice.call(arguments, 1); 7 | 8 | for (var i = 0; i < sources.length; i += 1) { 9 | var source = sources[i]; 10 | for (var key in source) { 11 | if (source.hasOwnProperty(key)) { 12 | target[key] = source[key]; 13 | } 14 | } 15 | } 16 | 17 | return target; 18 | }; -------------------------------------------------------------------------------- /server/config.local.js.example: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mysql: { 3 | host: 'localhost', 4 | port: 3306, 5 | user: '', 6 | pass: '', 7 | db: '', 8 | char: 'utf8mb4' 9 | }, 10 | serverHost: 'localhost', 11 | tunnelServerUrl: '', 12 | tunnelSignatureKey: '', 13 | // 腾讯云相关配置可以查看云 API 秘钥控制台:https://console.qcloud.com/capi 14 | qcloudAppId: '', 15 | qcloudSecretId: '', 16 | qcloudSecretKey: '', 17 | wxMessageToken: '' 18 | } 19 | -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/lib/session.js: -------------------------------------------------------------------------------- 1 | var constants = require('./constants'); 2 | var SESSION_KEY = 'weapp_session_' + constants.WX_SESSION_MAGIC_ID; 3 | 4 | var Session = { 5 | get: function () { 6 | return wx.getStorageSync(SESSION_KEY) || null; 7 | }, 8 | 9 | set: function (session) { 10 | wx.setStorageSync(SESSION_KEY, session); 11 | }, 12 | 13 | clear: function () { 14 | wx.removeStorageSync(SESSION_KEY); 15 | }, 16 | }; 17 | 18 | module.exports = Session; -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const app = new Koa() 3 | const debug = require('debug')('koa-weapp-demo') 4 | const response = require('./middlewares/response') 5 | const bodyParser = require('./middlewares/bodyparser') 6 | const config = require('./config') 7 | 8 | // 使用响应处理中间件 9 | app.use(response) 10 | 11 | // 解析请求体 12 | app.use(bodyParser()) 13 | 14 | // 引入路由分发 15 | const router = require('./routes') 16 | app.use(router.routes()) 17 | 18 | // 启动程序,监听端口 19 | app.listen(config.port, () => debug(`listening on port ${config.port}`)) 20 | -------------------------------------------------------------------------------- /client/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 小程序配置文件 3 | */ 4 | 5 | // 此处主机域名修改成腾讯云解决方案分配的域名 6 | var host = 'https://s8sagp6p.qcloud.la'; 7 | 8 | var config = { 9 | 10 | // 下面的地址配合云端 Demo 工作 11 | service: { 12 | host, 13 | 14 | // 登录地址,用于建立会话 15 | loginUrl: `${host}/weapp/login`, 16 | 17 | // 测试的请求地址,用于测试会话 18 | requestUrl: `${host}/weapp/user`, 19 | 20 | // 测试的信道服务地址 21 | tunnelUrl: `${host}/weapp/tunnel`, 22 | 23 | // 上传图片接口 24 | uploadUrl: `${host}/weapp/upload` 25 | } 26 | }; 27 | 28 | module.exports = config; -------------------------------------------------------------------------------- /server/tools.md: -------------------------------------------------------------------------------- 1 | # 腾讯云小程序解决方案 Demo 工具使用文档 2 | 3 | 本文件夹下的脚本为腾讯云小程序解决方案 Demo 配套的工具,旨在让用户方便快捷的使用并创建小程序的开发环境。 4 | 5 | 工具包括: 6 | 7 | - [数据库初始化工具](#数据库初始化工具) 8 | 9 | ## 数据库初始化工具 10 | 11 | 本工具是为了让用户快速的按照腾讯云制定的数据库 schema 创建符合 SDK 标准的数据库结构。 12 | 13 | _**注意**:本工具支持的 MySQL 版本为 **5.7**,并且需提前在数据库中创建名为 `cAuth` 的数据库。`charset` 设置为 `utf8mb4`。_ 14 | 15 | 快速使用: 16 | 17 | ```bash 18 | npm run initdb 19 | ``` 20 | 21 | 或直接执行 `tools` 目录下的 `initdb.js` 文件: 22 | 23 | ```bash 24 | # 请保证已经执行了 npm install 安装了所需要的依赖 25 | node tools/initdb.js 26 | ``` 27 | 28 | 我们提供了初始化的 SQL 文件,你也可以用其他数据库工具(如 Navicat)直接导入 SQL 文件。 29 | -------------------------------------------------------------------------------- /server/middlewares/bodyparser.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('koa-bodyparser') 2 | 3 | /** 4 | * 这里做一个兼容 5 | * 由于微信方面客服接口 post 过来的数据是 JSON 6 | * 但是 header 里的 Content-Type 却错误标记为 text/xml 7 | * 这里对所有 Content-Type 为 text/xml 的用 JSON 解析 8 | * 如果你在消息推送后台配置的是 xml,请直接将 detectJSON 设置为空 9 | */ 10 | module.exports = (opts = {}) => { 11 | const options = Object.assign({}, { 12 | detectJSON (ctx) { 13 | if (ctx.request.type === 'text/xml') { 14 | return true 15 | } else { 16 | return false 17 | } 18 | } 19 | }, opts) 20 | 21 | return bodyParser(options) 22 | } 23 | -------------------------------------------------------------------------------- /client/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/test/test", 5 | "pages/chat/chat", 6 | "pages/shouhuo/shouhuo", 7 | "pages/fahuo/fahuo", 8 | "pages/yaoqiu/yaoqiu", 9 | "pages/index/rule", 10 | "pages/userCenter/user", 11 | "pages/login/login" 12 | ], 13 | "window": { 14 | "backgroundTextStyle": "dark", 15 | "navigationBarBackgroundColor": "white", 16 | "navigationBarTitleText": "跑腿帮", 17 | "navigationBarTextStyle": "black" 18 | }, 19 | "sitemapLocation": "sitemap.json", 20 | "permission": { 21 | "scope.userLocation": { 22 | "desc": "你的位置信息将用于实时显示跑腿员的位置" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /client/pages/login/login.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/index.js: -------------------------------------------------------------------------------- 1 | var constants = require('./lib/constants'); 2 | var login = require('./lib/login'); 3 | var Session = require('./lib/session'); 4 | var request = require('./lib/request'); 5 | var Tunnel = require('./lib/tunnel'); 6 | 7 | var exports = module.exports = { 8 | login: login.login, 9 | loginWithCode: login.loginWithCode, 10 | setLoginUrl: login.setLoginUrl, 11 | 12 | Session, 13 | clearSession: Session.clear, 14 | 15 | request: request.request, 16 | RequestError: request.RequestError, 17 | 18 | Tunnel: Tunnel, 19 | }; 20 | 21 | // 导出错误类型码 22 | Object.keys(constants).forEach(function (key) { 23 | if (key.indexOf('ERR_') === 0) { 24 | exports[key] = constants[key]; 25 | } 26 | }); -------------------------------------------------------------------------------- /client/pages/userCenter/user.wxss: -------------------------------------------------------------------------------- 1 | /* pages/userCenter/user.wxss */ 2 | page{ 3 | background-color: #F8F8F8; 4 | font-size: 16px; 5 | font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; 6 | } 7 | .page__hd { 8 | padding: 40px; 9 | } 10 | .page__bd { 11 | padding-bottom: 40px; 12 | } 13 | .page__bd_spacing { 14 | padding-left: 15px; 15 | padding-right: 15px; 16 | } 17 | 18 | .page__ft{ 19 | padding-bottom: 10px; 20 | text-align: center; 21 | } 22 | 23 | .page__title { 24 | text-align: left; 25 | font-size: 20px; 26 | font-weight: 400; 27 | } 28 | 29 | .page__desc { 30 | margin-top: 5px; 31 | color: #888888; 32 | text-align: left; 33 | font-size: 14px; 34 | } 35 | 36 | #list{ 37 | font-size: 14px; 38 | } -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/lib/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | WX_HEADER_CODE: 'X-WX-Code', 3 | WX_HEADER_ENCRYPTED_DATA: 'X-WX-Encrypted-Data', 4 | WX_HEADER_IV: 'X-WX-IV', 5 | WX_HEADER_ID: 'X-WX-Id', 6 | WX_HEADER_SKEY: 'X-WX-Skey', 7 | 8 | WX_SESSION_MAGIC_ID: 'F2C224D4-2BCE-4C64-AF9F-A6D872000D1A', 9 | 10 | ERR_INVALID_PARAMS: 'ERR_INVALID_PARAMS', 11 | 12 | ERR_WX_LOGIN_FAILED: 'ERR_WX_LOGIN_FAILED', 13 | ERR_WX_GET_USER_INFO: 'ERR_WX_GET_USER_INFO', 14 | ERR_LOGIN_TIMEOUT: 'ERR_LOGIN_TIMEOUT', 15 | ERR_LOGIN_FAILED: 'ERR_LOGIN_FAILED', 16 | ERR_LOGIN_SESSION_NOT_RECEIVED: 'ERR_LOGIN_MISSING_SESSION', 17 | 18 | ERR_SESSION_INVALID: 'ERR_SESSION_INVALID', 19 | ERR_CHECK_LOGIN_FAILED: 'ERR_CHECK_LOGIN_FAILED', 20 | }; -------------------------------------------------------------------------------- /server/controllers/index.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const fs = require('fs') 3 | const path = require('path') 4 | 5 | /** 6 | * 映射 d 文件夹下的文件为模块 7 | */ 8 | const mapDir = d => { 9 | const tree = {} 10 | 11 | // 获得当前文件夹下的所有的文件夹和文件 12 | const [dirs, files] = _(fs.readdirSync(d)).partition(p => fs.statSync(path.join(d, p)).isDirectory()) 13 | 14 | // 映射文件夹 15 | dirs.forEach(dir => { 16 | tree[dir] = mapDir(path.join(d, dir)) 17 | }) 18 | 19 | // 映射文件 20 | files.forEach(file => { 21 | if (path.extname(file) === '.js') { 22 | tree[path.basename(file, '.js')] = require(path.join(d, file)) 23 | } 24 | }) 25 | 26 | return tree 27 | } 28 | 29 | // 默认导出当前文件夹下的映射 30 | module.exports = mapDir(path.join(__dirname)) 31 | -------------------------------------------------------------------------------- /client/pages/yaoqiu/yaoqiu.wxss: -------------------------------------------------------------------------------- 1 | /* pages/yaoqiu/yaoqiu.wxss */ 2 | slider{ 3 | margin-bottom: 30px; 4 | } 5 | page{ 6 | 7 | font-size: 16px; 8 | font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; 9 | } 10 | 11 | .page__bd { 12 | padding-bottom: 40px; 13 | } 14 | .page__bd_spacing { 15 | padding-left: 15px; 16 | padding-right: 15px; 17 | } 18 | 19 | .page__ft{ 20 | padding-bottom: 10px; 21 | text-align: center; 22 | } 23 | 24 | .page__title { 25 | text-align: left; 26 | font-size: 20px; 27 | font-weight: 400; 28 | text-align: center; 29 | } 30 | 31 | .page__desc { 32 | margin-top: 5px; 33 | color: #888888; 34 | text-align: center; 35 | font-size: 14px; 36 | } 37 | 38 | #queding{ 39 | background: #FDA81C; 40 | } 41 | 42 | .weui-grid:hover{ 43 | background-color: #DFDAD8; 44 | 45 | } -------------------------------------------------------------------------------- /server/controllers/message.js: -------------------------------------------------------------------------------- 1 | const { message: { checkSignature } } = require('../qcloud') 2 | 3 | /** 4 | * 响应 GET 请求(响应微信配置时的签名检查请求) 5 | */ 6 | async function get (ctx, next) { 7 | const { signature, timestamp, nonce, echostr } = ctx.query 8 | if (checkSignature(signature, timestamp, nonce)) ctx.body = echostr 9 | else ctx.body = 'ERR_WHEN_CHECK_SIGNATURE' 10 | } 11 | 12 | async function post (ctx, next) { 13 | // 检查签名,确认是微信发出的请求 14 | const { signature, timestamp, nonce } = ctx.query 15 | if (!checkSignature(signature, timestamp, nonce)) ctx.body = 'ERR_WHEN_CHECK_SIGNATURE' 16 | 17 | /** 18 | * 解析微信发送过来的请求体 19 | * 可查看微信文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/receive.html#接收消息和事件 20 | */ 21 | const body = ctx.request.body 22 | 23 | ctx.body = 'success' 24 | } 25 | 26 | module.exports = { 27 | post, 28 | get 29 | } 30 | -------------------------------------------------------------------------------- /server/middlewares/response.js: -------------------------------------------------------------------------------- 1 | const debug = require('debug')('koa-weapp-demo') 2 | 3 | /** 4 | * 响应处理模块 5 | */ 6 | module.exports = async function (ctx, next) { 7 | try { 8 | // 调用下一个 middleware 9 | await next() 10 | 11 | // 处理响应结果 12 | // 如果直接写入在 body 中,则不作处理 13 | // 如果写在 ctx.body 为空,则使用 state 作为响应 14 | ctx.body = ctx.body ? ctx.body : { 15 | code: ctx.state.code !== undefined ? ctx.state.code : 0, 16 | data: ctx.state.data !== undefined ? ctx.state.data : {} 17 | } 18 | } catch (e) { 19 | // catch 住全局的错误信息 20 | debug('Catch Error: %o', e) 21 | 22 | // 设置状态码为 500 - 服务端错误 23 | ctx.status = 200 24 | 25 | // 输出详细的错误信息 26 | ctx.body = { 27 | code: -1, 28 | error: e && e.message ? e.message : e.toString() 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/lib/wxTunnel.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | const noop = () => void(0); 3 | 4 | let onOpen, onClose, onMessage, onError; 5 | 6 | /* istanbul ignore next */ 7 | function listen(listener) { 8 | if (listener) { 9 | onOpen = listener.onOpen; 10 | onClose = listener.onClose; 11 | onMessage = listener.onMessage; 12 | onError = listener.onError; 13 | } else { 14 | onOpen = noop; 15 | onClose = noop; 16 | onMessage = noop; 17 | onError = noop; 18 | } 19 | } 20 | 21 | /* istanbul ignore next */ 22 | function bind() { 23 | wx.onSocketOpen(result => onOpen(result)); 24 | wx.onSocketClose(result => onClose(result)); 25 | wx.onSocketMessage(result => onMessage(result)); 26 | wx.onSocketError(error => onError(error)); 27 | } 28 | 29 | listen(null); 30 | bind(); 31 | 32 | module.exports = { listen }; -------------------------------------------------------------------------------- /client/pages/test/test.js: -------------------------------------------------------------------------------- 1 | // pages/test/test.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | }, 10 | 11 | /** 12 | * 生命周期函数--监听页面加载 13 | */ 14 | onLoad: function (options) { 15 | 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面初次渲染完成 20 | */ 21 | onReady: function () { 22 | 23 | }, 24 | 25 | /** 26 | * 生命周期函数--监听页面显示 27 | */ 28 | onShow: function () { 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面隐藏 34 | */ 35 | onHide: function () { 36 | 37 | }, 38 | 39 | /** 40 | * 生命周期函数--监听页面卸载 41 | */ 42 | onUnload: function () { 43 | 44 | }, 45 | 46 | /** 47 | * 页面相关事件处理函数--监听用户下拉动作 48 | */ 49 | onPullDownRefresh: function () { 50 | 51 | }, 52 | 53 | /** 54 | * 页面上拉触底事件的处理函数 55 | */ 56 | onReachBottom: function () { 57 | 58 | }, 59 | 60 | /** 61 | * 用户点击右上角分享 62 | */ 63 | onShareAppMessage: function () { 64 | 65 | } 66 | }) -------------------------------------------------------------------------------- /client/pages/index/rule.js: -------------------------------------------------------------------------------- 1 | // pages/index/rule.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | }, 10 | 11 | /** 12 | * 生命周期函数--监听页面加载 13 | */ 14 | onLoad: function (options) { 15 | 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面初次渲染完成 20 | */ 21 | onReady: function () { 22 | 23 | }, 24 | 25 | /** 26 | * 生命周期函数--监听页面显示 27 | */ 28 | onShow: function () { 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面隐藏 34 | */ 35 | onHide: function () { 36 | 37 | }, 38 | 39 | /** 40 | * 生命周期函数--监听页面卸载 41 | */ 42 | onUnload: function () { 43 | 44 | }, 45 | 46 | /** 47 | * 页面相关事件处理函数--监听用户下拉动作 48 | */ 49 | onPullDownRefresh: function () { 50 | 51 | }, 52 | 53 | /** 54 | * 页面上拉触底事件的处理函数 55 | */ 56 | onReachBottom: function () { 57 | 58 | }, 59 | 60 | /** 61 | * 用户点击右上角分享 62 | */ 63 | onShareAppMessage: function () { 64 | 65 | } 66 | }) -------------------------------------------------------------------------------- /client/pages/shouhuo/shouhuo.js: -------------------------------------------------------------------------------- 1 | // pages/shouhuo/shouhuo.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | }, 10 | 11 | /** 12 | * 生命周期函数--监听页面加载 13 | */ 14 | onLoad: function (options) { 15 | 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面初次渲染完成 20 | */ 21 | onReady: function () { 22 | 23 | }, 24 | 25 | /** 26 | * 生命周期函数--监听页面显示 27 | */ 28 | onShow: function () { 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面隐藏 34 | */ 35 | onHide: function () { 36 | 37 | }, 38 | 39 | /** 40 | * 生命周期函数--监听页面卸载 41 | */ 42 | onUnload: function () { 43 | 44 | }, 45 | 46 | /** 47 | * 页面相关事件处理函数--监听用户下拉动作 48 | */ 49 | onPullDownRefresh: function () { 50 | 51 | }, 52 | 53 | /** 54 | * 页面上拉触底事件的处理函数 55 | */ 56 | onReachBottom: function () { 57 | 58 | }, 59 | 60 | /** 61 | * 用户点击右上角分享 62 | */ 63 | onShareAppMessage: function () { 64 | 65 | } 66 | }) -------------------------------------------------------------------------------- /client/pages/yaoqiu/yaoqiu.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{item.name}} 9 | 10 | 11 | 12 | 13 | 14 | 重量 15 | {{weight}}公斤 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /server/qcloud.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const qcloud = require('wafer-node-sdk') 3 | 4 | // 获取基础配置 5 | const configs = require('./config') 6 | 7 | // 获取 sdk.config 8 | const sdkConfig = (() => { 9 | const sdkConfigPath = '/data/release/sdk.config.json' 10 | 11 | // 检查文件是否存在 12 | try { 13 | const stats = fs.statSync(sdkConfigPath) 14 | 15 | if (!stats.isFile()) { 16 | console.log('sdk.config.json 不存在,将使用 config.js 中的配置') 17 | return {} 18 | } 19 | } catch (e) { 20 | return {} 21 | } 22 | 23 | // 返回配置信息 24 | try { 25 | const content = fs.readFileSync(sdkConfigPath, 'utf8') 26 | return JSON.parse(content) 27 | } catch (e) { 28 | // 如果配置读取错误或者 JSON 解析错误,则输出空配置项 29 | console.log('sdk.config.json 解析错误,不是 JSON 字符串') 30 | return {} 31 | } 32 | })() 33 | 34 | // 初始化 SDK 35 | // 将基础配置和 sdk.config 合并传入 SDK 并导出初始化完成的 SDK 36 | module.exports = qcloud(Object.assign({}, sdkConfig, configs)) 37 | -------------------------------------------------------------------------------- /client/utils/util.js: -------------------------------------------------------------------------------- 1 | const formatTime = date => { 2 | const year = date.getFullYear() 3 | const month = date.getMonth() + 1 4 | const day = date.getDate() 5 | const hour = date.getHours() 6 | const minute = date.getMinutes() 7 | const second = date.getSeconds() 8 | 9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 10 | } 11 | 12 | const formatNumber = n => { 13 | n = n.toString() 14 | return n[1] ? n : '0' + n 15 | } 16 | 17 | 18 | // 显示繁忙提示 19 | var showBusy = text => wx.showToast({ 20 | title: text, 21 | icon: 'loading', 22 | duration: 10000 23 | }) 24 | 25 | // 显示成功提示 26 | var showSuccess = text => wx.showToast({ 27 | title: text, 28 | icon: 'success' 29 | }) 30 | 31 | // 显示失败提示 32 | var showModel = (title, content) => { 33 | wx.hideToast(); 34 | 35 | wx.showModal({ 36 | title, 37 | content: JSON.stringify(content), 38 | showCancel: false 39 | }) 40 | } 41 | 42 | module.exports = { formatTime, showBusy, showSuccess, showModel } 43 | -------------------------------------------------------------------------------- /server/config.js: -------------------------------------------------------------------------------- 1 | const CONF = { 2 | port: '5757', 3 | rootPathname: '', 4 | 5 | // 微信小程序 App ID 6 | appId: 'wxf20f36c2e7dba430', 7 | 8 | // 微信小程序 App Secret 9 | appSecret: '', 10 | 11 | // 是否使用腾讯云代理登录小程序 12 | useQcloudLogin: true, 13 | 14 | /** 15 | * MySQL 配置,用来存储 session 和用户信息 16 | * 若使用了腾讯云微信小程序解决方案 17 | * 开发环境下,MySQL 的初始密码为您的微信小程序 appid 18 | */ 19 | mysql: { 20 | host: 'localhost', 21 | port: 3306, 22 | user: 'root', 23 | db: 'cAuth', 24 | pass: '1214xmc0709fl', 25 | char: 'utf8mb4' 26 | }, 27 | 28 | cos: { 29 | /** 30 | * 区域 31 | * @查看 https://cloud.tencent.com/document/product/436/6224 32 | */ 33 | region: 'ap-guangzhou', 34 | // Bucket 名称 35 | fileBucket: 'wximg', 36 | // 文件夹 37 | uploadFolder: '' 38 | }, 39 | 40 | // 微信登录态有效期 41 | wxLoginExpires: 7200 42 | } 43 | 44 | module.exports = process.env.NODE_ENV === 'local' ? Object.assign({}, CONF, require('./config.local')) : CONF; 45 | -------------------------------------------------------------------------------- /server/tools/initdb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 腾讯云微信小程序解决方案 3 | * Demo 数据库初始化脚本 4 | * @author Jason 5 | */ 6 | const fs = require('fs') 7 | const path = require('path') 8 | const { mysql: config } = require('../config') 9 | 10 | console.log('\n======================================') 11 | console.log('开始初始化数据库...') 12 | 13 | // 初始化 SQL 文件路径 14 | const INIT_DB_FILE = path.join(__dirname, './cAuth.sql') 15 | 16 | const DB = require('knex')({ 17 | client: 'mysql', 18 | connection: { 19 | host: config.host, 20 | port: config.port, 21 | user: config.user, 22 | password: config.pass, 23 | database: config.db, 24 | charset: config.char, 25 | multipleStatements: true 26 | } 27 | }) 28 | 29 | console.log(`准备读取 SQL 文件:${INIT_DB_FILE}`) 30 | 31 | // 读取 .sql 文件内容 32 | const content = fs.readFileSync(INIT_DB_FILE, 'utf8') 33 | 34 | console.log('开始执行 SQL 文件...') 35 | 36 | // 执行 .sql 文件内容 37 | DB.raw(content).then(res => { 38 | console.log('数据库初始化成功!') 39 | process.exit(0) 40 | }, err => { 41 | throw new Error(err) 42 | }) 43 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-weapp-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "pm2 start process.prod.json --no-daemon", 8 | "local-dev": "nodemon --config nodemon.json app.js", 9 | "initdb": "npm install && node tools/initdb.js" 10 | }, 11 | "author": "Jason", 12 | "license": "MIT", 13 | "dependencies": { 14 | "axios": "^0.15.3", 15 | "knex": "^0.20.1", 16 | "koa": "^2.0.0", 17 | "koa-bodyparser": "^3.2.0", 18 | "koa-log4": "^2.1.0", 19 | "koa-router": "^7.0.1", 20 | "lodash": "^4.17.4", 21 | "mkdir-p": "0.0.7", 22 | "mysql": "^2.14.1", 23 | "pify": "^2.3.0", 24 | "wafer-node-sdk": "^1.4.1" 25 | }, 26 | "devDependencies": { 27 | "babel-eslint": "^7.1.0", 28 | "debug": "^2.6.8", 29 | "eslint": "^3.9.1", 30 | "eslint-config-standard": "^6.2.1", 31 | "eslint-plugin-promise": "^3.3.1", 32 | "eslint-plugin-standard": "^2.0.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | *.pid.lock 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | # Coverage directory used by tools like istanbul 13 | coverage 14 | # nyc test coverage 15 | .nyc_output 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | # Bower dependency directory (https://bower.io/) 19 | bower_components 20 | # node-waf configuration 21 | .lock-wscript 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | # Dependency directories 25 | node_modules/ 26 | jspm_packages/ 27 | # Typescript v1 declaration files 28 | typings/ 29 | # Optional npm cache directory 30 | .npm 31 | # Optional eslint cache 32 | .eslintcache 33 | # Optional REPL history 34 | .node_repl_history 35 | # Output of 'npm pack' 36 | *.tgz 37 | # Yarn Integrity file 38 | .yarn-integrity 39 | # dotenv environment variables file 40 | .env 41 | .vscode 42 | # ignore sh 43 | sh/ 44 | # ignore test sdk.config.json 45 | sdk.config.json 46 | # ignore local config 47 | server/config.local.js 48 | -------------------------------------------------------------------------------- /client/pages/fahuo/fahuo.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 常用地址 6 | 7 | 8 | {{item.provinceName}}{{item.cityName}}{{item.countyName}}{{item.detailInfo}} 9 | {{item.userName}} {{item.telNumber}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 添加发件地址 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /client/LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE - "MIT License" 2 | 3 | Copyright (c) 2016 by Tencent Cloud 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE - "MIT License" 2 | 3 | Copyright (c) 2016 by Tencent Cloud 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /client/pages/chat/chat.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{message.user.nickName}} 8 | {{message.content}} 9 | 10 | 11 | 12 | {{message.content}} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ajax 服务路由集合 3 | */ 4 | const router = require('koa-router')({ 5 | prefix: '/weapp' // 定义所有路由的前缀都已 /weapp 开头 6 | }) 7 | const controllers = require('../controllers') 8 | 9 | // 从 sdk 中取出中间件 10 | // 这里展示如何使用 Koa 中间件完成登录态的颁发与验证 11 | const { auth: { authorizationMiddleware, validationMiddleware } } = require('../qcloud') 12 | 13 | // --- 登录与授权 Demo --- // 14 | // 登录接口 /weapp/login 15 | router.get('/login', authorizationMiddleware, controllers.login) 16 | // 用户信息接口(可以用来验证登录态) /weapp/user 17 | router.get('/user', validationMiddleware, controllers.user) 18 | 19 | 20 | // --- 图片上传 Demo --- // 21 | // 图片上传接口,小程序端可以直接将 url 填入 wx.uploadFile 中 /weapp/upload 22 | router.post('/upload', controllers.upload) 23 | 24 | // --- 信道服务接口 Demo --- // 25 | // GET 用来响应请求信道地址的 /weapp/tunnel 26 | router.get('/tunnel', controllers.tunnel.get) 27 | // POST 用来处理信道传递过来的消息 28 | router.post('/tunnel', controllers.tunnel.post) 29 | 30 | // --- 客服消息接口 Demo --- // 31 | // GET 用来响应小程序后台配置时发送的验证请求 /weapp/message 32 | router.get('/message', controllers.message.get) 33 | // POST 用来处理微信转发过来的客服消息 34 | router.post('/message', controllers.message.post) 35 | 36 | 37 | router.get('/helloworld', controllers.helloworld) 38 | module.exports = router 39 | -------------------------------------------------------------------------------- /client/pages/userCenter/user.js: -------------------------------------------------------------------------------- 1 | // pages/userCenter/user.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | 10 | User:{} 11 | }, 12 | 13 | /** 14 | * 生命周期函数--监听页面加载 15 | */ 16 | onLoad: function(options) { 17 | var that = this; 18 | var temp; 19 | var Temp; 20 | wx.getStorage({ 21 | key: 'weapp_session_F2C224D4-2BCE-4C64-AF9F-A6D872000D1A', 22 | success: function(res) { 23 | temp = res.data; 24 | Temp = Object.assign({},temp.userinfo) 25 | that.setData({ 26 | User: Temp 27 | }) 28 | }, 29 | }) 30 | 31 | }, 32 | 33 | /** 34 | * 生命周期函数--监听页面初次渲染完成 35 | */ 36 | onReady: function() { 37 | 38 | }, 39 | 40 | /** 41 | * 生命周期函数--监听页面显示 42 | */ 43 | onShow: function() { 44 | 45 | }, 46 | 47 | /** 48 | * 生命周期函数--监听页面隐藏 49 | */ 50 | onHide: function() { 51 | 52 | }, 53 | 54 | /** 55 | * 生命周期函数--监听页面卸载 56 | */ 57 | onUnload: function() { 58 | 59 | }, 60 | 61 | /** 62 | * 页面相关事件处理函数--监听用户下拉动作 63 | */ 64 | onPullDownRefresh: function() { 65 | 66 | }, 67 | 68 | /** 69 | * 页面上拉触底事件的处理函数 70 | */ 71 | onReachBottom: function() { 72 | 73 | }, 74 | 75 | /** 76 | * 用户点击右上角分享 77 | */ 78 | onShareAppMessage: function() { 79 | 80 | } 81 | }) -------------------------------------------------------------------------------- /server/tools/cAuth.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : Localhost 5 | Source Server Type : MySQL 6 | Source Server Version : 50717 7 | Source Host : localhost 8 | Source Database : cAuth 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50717 12 | File Encoding : utf-8 13 | 14 | Date: 08/10/2017 22:22:52 PM 15 | */ 16 | 17 | SET NAMES utf8; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for `cSessionInfo` 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `cSessionInfo`; 24 | CREATE TABLE `cSessionInfo` ( 25 | `open_id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, 26 | `uuid` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, 27 | `skey` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, 28 | `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 29 | `last_visit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 30 | `session_key` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, 31 | `user_info` varchar(2048) COLLATE utf8mb4_unicode_ci NOT NULL, 32 | PRIMARY KEY (`open_id`), 33 | KEY `openid` (`open_id`) USING BTREE, 34 | KEY `skey` (`skey`) USING BTREE 35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会话管理用户信息'; 36 | 37 | SET FOREIGN_KEY_CHECKS = 1; 38 | -------------------------------------------------------------------------------- /client/pages/addCgi/addCgi.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 测试 CGI 7 | 8 | 9 | 期望输出:{"code":0,"data":{"msg":"Hello World"}} 10 | 11 | 12 | {{'请求结果:' + requestResult}} 13 | 14 | 15 | 16 | 17 | 18 | 快速添加CGI指引 19 | 1. 打开 server/routes/index.js 文件,添加如下语句: 20 | 21 | 22 | 2. 在 server/controllers 下新建一个 demo.js 文件,写入如下代码: 23 | 24 | 25 | 3. 点击开发者工具右上角“腾讯云” - “上传测试代码”,勾选“智能上传” 26 | 4. 点击测试 CGI 按钮,即可看到结果 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /client/pages/addCgi/addCgi.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | var qcloud = require('../../vendor/wafer2-client-sdk/index') 3 | var config = require('../../config') 4 | var util = require('../../utils/util.js') 5 | 6 | Page({ 7 | data: { 8 | requestResult: '', 9 | canIUseClipboard: wx.canIUse('setClipboardData') 10 | }, 11 | 12 | testCgi: function () { 13 | util.showBusy('请求中...') 14 | var that = this 15 | qcloud.request({ 16 | url: `${config.service.host}/weapp/demo`, 17 | login: false, 18 | success (result) { 19 | util.showSuccess('请求成功完成') 20 | that.setData({ 21 | requestResult: JSON.stringify(result.data) 22 | }) 23 | }, 24 | fail (error) { 25 | util.showModel('请求失败', error); 26 | console.log('request fail', error); 27 | } 28 | }) 29 | }, 30 | 31 | copyCode: function (e) { 32 | var codeId = e.target.dataset.codeId 33 | wx.setClipboardData({ 34 | data: code[codeId - 1], 35 | success: function () { 36 | util.showSuccess('复制成功') 37 | } 38 | }) 39 | } 40 | }) 41 | 42 | var code = [ 43 | `router.get('/demo', controllers.demo)`, 44 | `module.exports = ctx => { 45 | ctx.state.data = { 46 | msg: 'Hello World' 47 | } 48 | }` 49 | ] 50 | -------------------------------------------------------------------------------- /client/pages/fahuo/fahuo.js: -------------------------------------------------------------------------------- 1 | // pages/fahuo/fahuo.js 2 | var Address = new Array(); 3 | Page({ 4 | 5 | /** 6 | * 页面的初始数据 7 | */ 8 | data: { 9 | address:[] 10 | }, 11 | 12 | /** 13 | * 生命周期函数--监听页面加载 14 | */ 15 | onLoad: function (options) { 16 | var that = this 17 | wx.getStorage({ 18 | key: 'address', 19 | success: function(res) { 20 | that.setData({ 21 | address:res.data 22 | }) 23 | }, 24 | }) 25 | }, 26 | 27 | /** 28 | * 生命周期函数--监听页面初次渲染完成 29 | */ 30 | onReady: function () { 31 | 32 | }, 33 | 34 | /** 35 | * 生命周期函数--监听页面显示 36 | */ 37 | onShow: function () { 38 | 39 | }, 40 | 41 | /** 42 | * 生命周期函数--监听页面隐藏 43 | */ 44 | onHide: function () { 45 | 46 | }, 47 | 48 | /** 49 | * 生命周期函数--监听页面卸载 50 | */ 51 | onUnload: function () { 52 | 53 | }, 54 | 55 | /** 56 | * 页面相关事件处理函数--监听用户下拉动作 57 | */ 58 | onPullDownRefresh: function () { 59 | 60 | }, 61 | 62 | /** 63 | * 页面上拉触底事件的处理函数 64 | */ 65 | onReachBottom: function () { 66 | 67 | }, 68 | 69 | /** 70 | * 用户点击右上角分享 71 | */ 72 | onShareAppMessage: function () { 73 | 74 | }, 75 | 76 | //用户选择收货地址 77 | chooseAddress: function () { 78 | var that = this; 79 | if (wx.chooseAddress) { 80 | wx.chooseAddress({ 81 | success: function (res) { 82 | console.log(JSON.stringify(res)); 83 | console.log(res); 84 | Address.push(res); 85 | console.log(Address); 86 | wx.setStorage({ 87 | key: 'address', 88 | data:Address, 89 | }); 90 | that.setData({ 91 | address:Address 92 | }) 93 | }, 94 | fail: function (err) { 95 | console.log(JSON.stringify(err)); 96 | console.info("收货地址授权失败"); 97 | wx.showModal({ 98 | title: '授权失败', 99 | content: '您将无法进行下单支付;重新授权请删除小程序后再次进入', 100 | duration: 2000 101 | }) 102 | } 103 | }) 104 | } else { 105 | console.log('当前微信版本不支持chooseAddress'); 106 | } 107 | } 108 | }) -------------------------------------------------------------------------------- /client/pages/addCgi/addCgi.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | page { 3 | background: #F6F6F6; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: flex-start; 7 | } 8 | 9 | .list { 10 | margin-top: 40rpx; 11 | height: auto; 12 | width: 100%; 13 | background: #FFF; 14 | padding: 0 0 0 40rpx; 15 | border: 1px solid rgba(0, 0, 0, .1); 16 | border-left: none; 17 | border-right: none; 18 | transition: all 300ms ease; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: flex-start; 22 | box-sizing: border-box; 23 | } 24 | 25 | .list-item { 26 | width: 100%; 27 | padding: 0; 28 | line-height: 104rpx; 29 | font-size: 34rpx; 30 | color: #007AFF; 31 | border-top: 1px solid rgba(0, 0, 0, .1); 32 | display: flex; 33 | flex-direction: row; 34 | align-content: center; 35 | justify-content: space-between; 36 | box-sizing: border-box; 37 | } 38 | 39 | .list-item:first-child { 40 | border-top: none; 41 | } 42 | 43 | .request-text { 44 | color: #222; 45 | padding: 20rpx 0; 46 | font-size: 24rpx; 47 | line-height: 36rpx; 48 | word-break: break-all; 49 | } 50 | 51 | .guide { 52 | width: 100%; 53 | padding: 40rpx; 54 | box-sizing: border-box; 55 | display: flex; 56 | flex-direction: column; 57 | } 58 | 59 | .guide .headline { 60 | font-size: 34rpx; 61 | font-weight: bold; 62 | color: #555; 63 | line-height: 40rpx; 64 | } 65 | 66 | .guide .p { 67 | margin-top: 20rpx; 68 | font-size: 28rpx; 69 | line-height: 36rpx; 70 | color: #666; 71 | } 72 | 73 | .guide .code { 74 | margin-top: 20rpx; 75 | background: rgba(0, 0, 0, .8); 76 | padding: 20rpx; 77 | font-size: 28rpx; 78 | line-height: 36rpx; 79 | border-radius: 6rpx; 80 | color: #FFF; 81 | } 82 | 83 | .guide .image1 { 84 | margin-top: 20rpx; 85 | max-width: 100%; 86 | width: 356px; 87 | height: 47px; 88 | } 89 | 90 | .guide .image2 { 91 | margin-top: 20rpx; 92 | width: 264px; 93 | height: 100px; 94 | } 95 | 96 | .guide .copyBtn { 97 | width: 180rpx; 98 | font-size: 20rpx; 99 | margin-top: 16rpx; 100 | margin-left: 0; 101 | } 102 | -------------------------------------------------------------------------------- /client/pages/index/rule.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 里程费 9 | 10 | 11 | 3公里内:12元 12 | 13 | 14 | 15 | 16 | 3-10公里:2.0元/公里 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 超过10公里:2.5元/公里 25 | 26 | 27 | 28 | OFFICE订单费 29 | 30 | 31 | 5公里内:19元 32 | 33 | 34 | 35 | 36 | 5-10公里:2.0元/公里 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 超过10公里:2.5元/公里 45 | 46 | 47 | 48 | 49 | 排队费用 50 | 51 | 52 | 起步价(含60分钟)20.0元 53 | 54 | 55 | 56 | 57 | 1-10小时:15元/半小时 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 延长排队(线下支付)15元/半小时 66 | 67 | 68 | 69 | 夜间服务费 70 | 71 | 72 | 00:00-07:00每单加8元 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /client/pages/login/login.js: -------------------------------------------------------------------------------- 1 | // pages/login/login.js 2 | 3 | // pages/message/message.js 4 | var qcloud = require('../../vendor/wafer2-client-sdk/index'); 5 | 6 | // 引入配置 7 | var config = require('../../config'); 8 | // 显示成功提示 9 | var showSuccess = text => wx.showToast({ 10 | title: text, 11 | icon: 'success' 12 | }); 13 | 14 | // 显示失败提示 15 | var showModel = (title, content) => { 16 | wx.hideToast(); 17 | 18 | wx.showModal({ 19 | title, 20 | content: JSON.stringify(content), 21 | showCancel: false 22 | }); 23 | }; 24 | Page({ 25 | 26 | /** 27 | * 页面的初始数据 28 | */ 29 | data: { 30 | 31 | }, 32 | 33 | /** 34 | * 生命周期函数--监听页面加载 35 | */ 36 | onLoad: function (options) { 37 | 38 | }, 39 | 40 | /** 41 | * 生命周期函数--监听页面初次渲染完成 42 | */ 43 | onReady: function () { 44 | 45 | }, 46 | 47 | /** 48 | * 生命周期函数--监听页面显示 49 | */ 50 | onShow: function () { 51 | 52 | }, 53 | 54 | /** 55 | * 生命周期函数--监听页面隐藏 56 | */ 57 | onHide: function () { 58 | 59 | }, 60 | 61 | /** 62 | * 生命周期函数--监听页面卸载 63 | */ 64 | onUnload: function () { 65 | 66 | }, 67 | 68 | /** 69 | * 页面相关事件处理函数--监听用户下拉动作 70 | */ 71 | onPullDownRefresh: function () { 72 | 73 | }, 74 | 75 | /** 76 | * 页面上拉触底事件的处理函数 77 | */ 78 | onReachBottom: function () { 79 | 80 | }, 81 | 82 | /** 83 | * 用户点击右上角分享 84 | */ 85 | onShareAppMessage: function () { 86 | 87 | }, 88 | bindGetUserInfo: function (e) { 89 | const session = qcloud.Session.get() 90 | 91 | if (session) { 92 | // 第二次登录 93 | // 或者本地已经有登录态 94 | // 可使用本函数更新登录态 95 | qcloud.loginWithCode({ 96 | success: res => { 97 | this.setData({ userInfo: res, logged: true }) 98 | showSuccess('授权成功') 99 | }, 100 | fail: err => { 101 | console.error(err) 102 | showModel('授权错误', err.message) 103 | } 104 | }) 105 | } else { 106 | // 首次登录 107 | qcloud.login({ 108 | success: res => { 109 | this.setData({ userInfo: res, logged: true }) 110 | showSuccess('授权成功') 111 | setTimeout(function(){ 112 | wx.navigateBack({ 113 | 114 | }) 115 | },500) 116 | }, 117 | fail: err => { 118 | console.error(err) 119 | showModel('授权错误', err.message) 120 | } 121 | }) 122 | } 123 | }, 124 | }) -------------------------------------------------------------------------------- /client/pages/test/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 登录接口测试 5 | 6 | 7 | 8 | 9 | 10 | 清除登录会话 11 | 12 | 13 | 14 | 带会话请求测试 15 | 16 | 17 | 请求 18 | 19 | 20 | 21 | 上传图片测试 22 | 23 | 24 | 上传图片 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 客服消息测试 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | WebSocket 信道服务测试 42 | 43 | 44 | 45 | 信道 46 | 47 | {{tunnelStatusText[tunnelStatus]}} 48 | 49 | 50 | 51 | 52 | 发送消息 53 | 测试重连 54 | 55 | 56 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # 腾讯云小程序解决方案 Demo - Node.js 2 | 3 | Node.js 版本 Wafer SDK 的服务端 Demo 4 | 5 | ## 下载源码 6 | 7 | 你可以直接通过 git 将代码 clone 到本地,也可以点击[这里](https://github.com/tencentyun/wafer-node-server-demo/releases)下载。 8 | 9 | ```bash 10 | git clone https://github.com/tencentyun/wafer-node-server-demo.git 11 | ``` 12 | 13 | ## 开始使用 14 | 15 | #### 安装依赖 16 | 17 | ```bash 18 | # 安装全局依赖 19 | npm i pm2 nodemon -g 20 | 21 | # 安装项目依赖 22 | npm i 23 | ``` 24 | 25 | #### 启动项目 26 | 27 | ```bash 28 | # 开发环境,监听文件变化自动重启,并会输出 debug 信息 29 | tnpm run dev 30 | 31 | # 线上部署环境 32 | tnpm start 33 | ``` 34 | 35 | 按照[小程序创建资源配置指引](https://github.com/tencentyun/weapp-doc)进行操作,可以得到运行本示例所需的资源和服务,其中包括已部署好的示例代码及自动下发的 SDK 配置文件 `/etc/qcloud/sdk.config`。 36 | 37 | - 示例代码部署目录:`/data/release/node-weapp-demo` 38 | - 运行示例的 Node 版本:`v8.1.0` 39 | - Node 进程管理工具:`pm2` 40 | 41 | ## 项目结构 42 | 43 | ``` 44 | koa-weapp-demo 45 | ├── README.md 46 | ├── app.js 47 | ├── controllers 48 | │ ├── index.js 49 | │ ├── login.js 50 | │ ├── message.js 51 | │ ├── tunnel.js 52 | │ ├── upload.js 53 | │ └── user.js 54 | ├── middlewares 55 | │ └── response.js 56 | ├── config.js 57 | ├── package.json 58 | ├── process.json 59 | ├── nodemon.json 60 | ├── qcloud.js 61 | └── routes 62 | └── index.js 63 | ``` 64 | `app.js` 是 Demo 的主入口文件,Demo 使用 Koa 框架,在 `app.js` 创建一个 Koa 实例并响应请求。 65 | 66 | `routes/index.js` 是 Demo 的路由定义文件 67 | 68 | `controllers` 存放 Demo 所有业务逻辑的目录,`index.js` 不需要修改,他会动态的将 `controllers` 文件夹下的目录结构映射成 modules 的 Object,例如 Demo 中的目录将会被映射成如下的结构: 69 | 70 | ```javascript 71 | // index.js 输出 72 | { 73 | login: require('login'), 74 | message: require('message'), 75 | tunnel: require('tunnel'), 76 | upload: require('upload'), 77 | user: require('user') 78 | } 79 | ``` 80 | 81 | `qcloud.js` 导出了一个 SDK 的单例,包含了所有的 SDK 接口,之后使用的时候只需要 `require` 这个文件就行,无需重复初始化 SDK。 82 | 83 | `config.js` 主要的配置如下: 84 | 85 | ```javascript 86 | { 87 | port: '5757', // 项目启动的端口 88 | 89 | appId: 'wx00dd00dd00dd00dd', // 微信小程序 App ID 90 | appSecret: 'abcdefg', // 微信小程序 App Secret 91 | wxLoginExpires: 7200, // 微信登录态有效期 92 | useQcloudLogin: false, // 是否使用腾讯云代理登录 93 | 94 | /** 95 | * MySQL 配置,用来存储用户登录态和用户信息 96 | * 如果不提供 MySQL 配置,模式会使用自动配置好的本地镜像中的 MySQL 储存信息 97 | * 具体查看文档-登录态储存和校验 98 | **/ 99 | mysql: { 100 | host: 'localhost', 101 | port: 3306, 102 | user: 'root', 103 | db: 'cAuth', 104 | pass: '', 105 | char: 'utf8' 106 | }, 107 | 108 | // COS 配置,用于上传模块使用 109 | cos: { 110 | /** 111 | * 区域 112 | * 华北:cn-north 113 | * 华东:cn-east 114 | * 华南:cn-south 115 | * 西南:cn-southwest 116 | */ 117 | region: 'cn-south', 118 | fileBucket: 'test', // Bucket 名称 119 | uploadFolder: '' // 文件夹 120 | } 121 | } 122 | ``` 123 | 124 | 除了 `config.js` ,腾讯云还会在你初始化小程序解决方案的时候,向你的机器下发 `sdk.config`,里面包含了你的腾讯云 AppId、SecretId、SecretKey 和服务器等信息,无需修改,`qcloud.js` 会自动引入。如果你想要在自己的机器上部署 SDK 的 Demo,请查看[自行部署 Demo 说明]()。 125 | 126 | 除此以外,关于 SDK 的详细配置信息,还可以查看 [SDK 的 API 文档]()。 -------------------------------------------------------------------------------- /client/pages/test/index.wxss: -------------------------------------------------------------------------------- 1 | .root { 2 | background: #efeff4; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | right: 0; 7 | bottom:0; 8 | } 9 | .line { 10 | padding-top: 30rpx; 11 | } 12 | .line text { 13 | color: #6D6D72; 14 | font-size: 28rpx; 15 | margin-left: 40rpx; 16 | } 17 | .line-control { 18 | border-bottom: #d9d9d9 1px solid; 19 | border-top: #d9d9d9 1px solid; 20 | background-color: #fff; 21 | margin-top: 10rpx; 22 | } 23 | .line-control .item { 24 | box-sizing: border-box; 25 | text-align: left; 26 | color: #007AFF; 27 | font-size: 32rpx; 28 | padding-left: 40rpx; 29 | position: relative; 30 | } 31 | .line-control .item:active { 32 | background: #d9d9d9; 33 | } 34 | .line-control .item.flag { 35 | color: #333; 36 | } 37 | .line-control .item.flag:active { 38 | background: #fff; 39 | } 40 | .line-control .item-inner { 41 | border-bottom: #d9d9d9 1px solid; 42 | height: 90rpx; 43 | line-height: 90rpx; 44 | } 45 | .line-control .item-inner::after { 46 | border: none; 47 | } 48 | 49 | .line-control .login-button { 50 | background-color: white; 51 | border-radius: 0; 52 | color: #007AFF; 53 | font-size: 38rpx; 54 | text-align: left; 55 | padding-left: 0; 56 | } 57 | 58 | .line-control .item-image { 59 | text-align: center; 60 | } 61 | .line-control .item.button { 62 | padding: 10px 20px; 63 | } 64 | .line-control .item.button:active { 65 | background-color: #fff; 66 | } 67 | .line-control .item-image .image { 68 | width: 380rpx; 69 | height: 380rpx; 70 | text-align: center; 71 | margin: 10rpx; 72 | } 73 | .line-control .item:last-child .item-inner { 74 | border:none; 75 | } 76 | .line-control .switch { 77 | vertical-align: middle; 78 | line-height: normal; 79 | } 80 | .line-control text { 81 | display: inline-block; 82 | vertical-align: middle; 83 | margin-right: 10rpx; 84 | color: #8E8E93; 85 | margin-left: 0; 86 | } 87 | .line-control .switch-status { 88 | color: #999; 89 | position: absolute; 90 | right: 40rpx; 91 | top: 0; 92 | } 93 | .line-control .item.disabled { 94 | color: #999 95 | } 96 | .line-control .item.disabled:active { 97 | background: none; 98 | } 99 | .line .demo { 100 | height: 90rpx; 101 | line-height: 90rpx; 102 | box-sizing: border-box; 103 | text-align: left; 104 | color: #333; 105 | background-color: #fff; 106 | position: relative; 107 | font-size: 32rpx; 108 | padding-left: 40rpx; 109 | } 110 | .line .demo .link { 111 | position: absolute; 112 | right: 40rpx; 113 | top: 0rpx; 114 | color: #8E8E93; 115 | } 116 | .hide { 117 | display: none 118 | } 119 | 120 | @media (max-width : 360px) { 121 | .line text { 122 | font-size: 36rpx; 123 | } 124 | .line-control .item-inner { 125 | border-bottom: #d9d9d9 1px solid; 126 | height: 110rpx; 127 | line-height: 110rpx; 128 | } 129 | .line-control .item { 130 | height: 110rpx; 131 | line-height: 110rpx; 132 | box-sizing: border-box; 133 | text-align: left; 134 | color: #007AFF; 135 | font-size: 38rpx; 136 | padding-left: 40rpx; 137 | position: relative 138 | } 139 | .line .demo { 140 | height: 110rpx; 141 | line-height: 110rpx; 142 | font-size: 38rpx; 143 | padding-left: 40rpx; 144 | } 145 | } -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/lib/request.js: -------------------------------------------------------------------------------- 1 | var constants = require('./constants'); 2 | var utils = require('./utils'); 3 | var Session = require('./session'); 4 | var loginLib = require('./login'); 5 | 6 | var noop = function noop() {}; 7 | 8 | var buildAuthHeader = function buildAuthHeader(session) { 9 | var header = {}; 10 | 11 | if (session) { 12 | header[constants.WX_HEADER_SKEY] = session; 13 | } 14 | 15 | return header; 16 | }; 17 | 18 | /*** 19 | * @class 20 | * 表示请求过程中发生的异常 21 | */ 22 | var RequestError = (function () { 23 | function RequestError(type, message) { 24 | Error.call(this, message); 25 | this.type = type; 26 | this.message = message; 27 | } 28 | 29 | RequestError.prototype = new Error(); 30 | RequestError.prototype.constructor = RequestError; 31 | 32 | return RequestError; 33 | })(); 34 | 35 | function request(options) { 36 | if (typeof options !== 'object') { 37 | var message = '请求传参应为 object 类型,但实际传了 ' + (typeof options) + ' 类型'; 38 | throw new RequestError(constants.ERR_INVALID_PARAMS, message); 39 | } 40 | 41 | var requireLogin = options.login; 42 | var success = options.success || noop; 43 | var fail = options.fail || noop; 44 | var complete = options.complete || noop; 45 | var originHeader = options.header || {}; 46 | 47 | // 成功回调 48 | var callSuccess = function () { 49 | success.apply(null, arguments); 50 | complete.apply(null, arguments); 51 | }; 52 | 53 | // 失败回调 54 | var callFail = function (error) { 55 | fail.call(null, error); 56 | complete.call(null, error); 57 | }; 58 | 59 | // 是否已经进行过重试 60 | var hasRetried = false; 61 | 62 | if (requireLogin) { 63 | doRequestWithLogin(); 64 | } else { 65 | doRequest(); 66 | } 67 | 68 | // 登录后再请求 69 | function doRequestWithLogin() { 70 | loginLib.loginWithCode({ success: doRequest, fail: callFail }); 71 | } 72 | 73 | // 实际进行请求的方法 74 | function doRequest() { 75 | var authHeader = {} 76 | 77 | var session = Session.get(); 78 | 79 | if (session) { 80 | authHeader = buildAuthHeader(session.skey); 81 | } 82 | 83 | wx.request(utils.extend({}, options, { 84 | header: utils.extend({}, originHeader, authHeader), 85 | 86 | success: function (response) { 87 | var data = response.data; 88 | 89 | var error, message; 90 | if ((data && data.code === -1) || response.statusCode === 401) { 91 | Session.clear(); 92 | // 如果是登录态无效,并且还没重试过,会尝试登录后刷新凭据重新请求 93 | if (!hasRetried) { 94 | hasRetried = true; 95 | doRequestWithLogin(); 96 | return; 97 | } 98 | 99 | message = '登录态已过期'; 100 | error = new RequestError(data.error, message); 101 | 102 | callFail(error); 103 | return; 104 | } else { 105 | callSuccess.apply(null, arguments); 106 | } 107 | }, 108 | 109 | fail: callFail, 110 | complete: noop, 111 | })); 112 | }; 113 | 114 | }; 115 | 116 | module.exports = { 117 | RequestError: RequestError, 118 | request: request, 119 | }; -------------------------------------------------------------------------------- /client/pages/yaoqiu/yaoqiu.js: -------------------------------------------------------------------------------- 1 | // pages/yaoqiu/yaoqiu.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | grids: [{ 10 | id:0, 11 | flag:0, 12 | image: "/images/icon/eat.png", 13 | name: "餐饮" 14 | }, 15 | { 16 | id: 1, 17 | flag: 0, 18 | image:'/images/icon/file.png', 19 | name: "文件" 20 | }, 21 | 22 | { 23 | id: 2, 24 | flag: 0, 25 | image:'/images/icon/yaopin.png', 26 | name: "药品" 27 | }, 28 | 29 | { 30 | id: 3, 31 | flag: 0, 32 | image:'/images/icon/cake.png', 33 | name: "蛋糕" 34 | }, 35 | { 36 | id: 4, 37 | flag: 0, 38 | image: '/images/icon/flower.png', 39 | name: "鲜花" 40 | }, 41 | { 42 | id: 5, 43 | flag: 0, 44 | image:'/images/icon/key.png', 45 | name: "钥匙" 46 | }, 47 | { 48 | id:6, 49 | flag: 0, 50 | image:'/images/icon/shuma.png', 51 | name: "数码" 52 | }, 53 | { 54 | id: 7, 55 | flag: 0, 56 | image:'/images/icon/clothes.png', 57 | name: "服饰" 58 | }, 59 | { 60 | id: 8, 61 | flag: 0, 62 | image:'/images/icon/other.png', 63 | name: "其他" 64 | } 65 | ], 66 | weight: '小于5' 67 | }, 68 | 69 | /** 70 | * 生命周期函数--监听页面加载 71 | */ 72 | onLoad: function(options) { 73 | 74 | }, 75 | 76 | /** 77 | * 生命周期函数--监听页面初次渲染完成 78 | */ 79 | onReady: function() { 80 | 81 | }, 82 | 83 | /** 84 | * 生命周期函数--监听页面显示 85 | */ 86 | onShow: function() { 87 | 88 | }, 89 | 90 | /** 91 | * 生命周期函数--监听页面隐藏 92 | */ 93 | onHide: function() { 94 | 95 | }, 96 | 97 | /** 98 | * 生命周期函数--监听页面卸载 99 | */ 100 | onUnload: function() { 101 | 102 | }, 103 | 104 | /** 105 | * 页面相关事件处理函数--监听用户下拉动作 106 | */ 107 | onPullDownRefresh: function() { 108 | 109 | }, 110 | 111 | /** 112 | * 页面上拉触底事件的处理函数 113 | */ 114 | onReachBottom: function() { 115 | 116 | }, 117 | 118 | /** 119 | * 用户点击右上角分享 120 | */ 121 | onShareAppMessage: function() { 122 | 123 | }, 124 | slider: function(e) { 125 | var that = this; 126 | if (e.detail.value == 4) { 127 | that.setData({ 128 | weight: "小于5" 129 | }) 130 | } else { 131 | that.setData({ 132 | weight: e.detail.value 133 | }) 134 | } 135 | }, 136 | slider2: function(e) { 137 | var that = this; 138 | if (e.detail.value == 4) { 139 | that.setData({ 140 | weight: "小于5" 141 | }) 142 | } else { 143 | that.setData({ 144 | weight: e.detail.value 145 | }) 146 | } 147 | }, 148 | 149 | select:function(event){ 150 | var item = event.currentTarget.dataset.item; 151 | item.flag = 1; 152 | var id = parseInt(item.id); 153 | let grids = this.data.grids; 154 | grids[id].flag = item.flag; 155 | this.setData({ 156 | grids:grids 157 | }) 158 | console.log(this.data.grids); 159 | 160 | }, 161 | submit:function(){ 162 | let grids = this.data.grids; 163 | var detail ; 164 | for( var i = 0; i < grids.length; i++) { 165 | if(grids[i].flag == 1) { 166 | detail = grids[i].name; 167 | } 168 | } 169 | console.log(detail); 170 | wx.reLaunch({ 171 | 172 | url: '/pages/index/index?thingType=' + detail + "&weight=" + this.data.weight, 173 | }) 174 | } 175 | }) -------------------------------------------------------------------------------- /server/controllers/tunnel.js: -------------------------------------------------------------------------------- 1 | const { tunnel } = require('../qcloud') 2 | const debug = require('debug')('koa-weapp-demo') 3 | 4 | /** 5 | * 这里实现一个简单的聊天室 6 | * userMap 为 tunnelId 和 用户信息的映射 7 | * 实际使用请使用数据库存储 8 | */ 9 | const userMap = {} 10 | 11 | // 保存 当前已连接的 WebSocket 信道ID列表 12 | const connectedTunnelIds = [] 13 | 14 | /** 15 | * 调用 tunnel.broadcast() 进行广播 16 | * @param {String} type 消息类型 17 | * @param {String} content 消息内容 18 | */ 19 | const $broadcast = (type, content) => { 20 | tunnel.broadcast(connectedTunnelIds, type, content) 21 | .then(result => { 22 | const invalidTunnelIds = result.data && result.data.invalidTunnelIds || [] 23 | 24 | if (invalidTunnelIds.length) { 25 | console.log('检测到无效的信道 IDs =>', invalidTunnelIds) 26 | 27 | // 从 userMap 和 connectedTunnelIds 中将无效的信道记录移除 28 | invalidTunnelIds.forEach(tunnelId => { 29 | delete userMap[tunnelId] 30 | 31 | const index = connectedTunnelIds.indexOf(tunnelId) 32 | if (~index) { 33 | connectedTunnelIds.splice(index, 1) 34 | } 35 | }) 36 | } 37 | }) 38 | } 39 | 40 | /** 41 | * 调用 TunnelService.closeTunnel() 关闭信道 42 | * @param {String} tunnelId 信道ID 43 | */ 44 | const $close = (tunnelId) => { 45 | tunnel.closeTunnel(tunnelId) 46 | } 47 | 48 | /** 49 | * 实现 onConnect 方法 50 | * 在客户端成功连接 WebSocket 信道服务之后会调用该方法, 51 | * 此时通知所有其它在线的用户当前总人数以及刚加入的用户是谁 52 | */ 53 | function onConnect (tunnelId) { 54 | console.log(`[onConnect] =>`, { tunnelId }) 55 | 56 | if (tunnelId in userMap) { 57 | connectedTunnelIds.push(tunnelId) 58 | 59 | $broadcast('people', { 60 | 'total': connectedTunnelIds.length, 61 | 'enter': userMap[tunnelId] 62 | }) 63 | } else { 64 | console.log(`Unknown tunnelId(${tunnelId}) was connectd, close it`) 65 | $close(tunnelId) 66 | } 67 | } 68 | 69 | /** 70 | * 实现 onMessage 方法 71 | * 客户端推送消息到 WebSocket 信道服务器上后,会调用该方法,此时可以处理信道的消息。 72 | * 在本示例,我们处理 `speak` 类型的消息,该消息表示有用户发言。 73 | * 我们把这个发言的信息广播到所有在线的 WebSocket 信道上 74 | */ 75 | function onMessage (tunnelId, type, content) { 76 | console.log(`[onMessage] =>`, { tunnelId, type, content }) 77 | 78 | switch (type) { 79 | case 'speak': 80 | if (tunnelId in userMap) { 81 | $broadcast('speak', { 82 | 'who': userMap[tunnelId], 83 | 'word': content.word 84 | }) 85 | } else { 86 | $close(tunnelId) 87 | } 88 | break 89 | 90 | default: 91 | break 92 | } 93 | } 94 | 95 | /** 96 | * 实现 onClose 方法 97 | * 客户端关闭 WebSocket 信道或者被信道服务器判断为已断开后, 98 | * 会调用该方法,此时可以进行清理及通知操作 99 | */ 100 | function onClose (tunnelId) { 101 | console.log(`[onClose] =>`, { tunnelId }) 102 | 103 | if (!(tunnelId in userMap)) { 104 | console.log(`[onClose][Invalid TunnelId]=>`, tunnelId) 105 | $close(tunnelId) 106 | return 107 | } 108 | 109 | const leaveUser = userMap[tunnelId] 110 | delete userMap[tunnelId] 111 | 112 | const index = connectedTunnelIds.indexOf(tunnelId) 113 | if (~index) { 114 | connectedTunnelIds.splice(index, 1) 115 | } 116 | 117 | // 聊天室没有人了(即无信道ID)不再需要广播消息 118 | if (connectedTunnelIds.length > 0) { 119 | $broadcast('people', { 120 | 'total': connectedTunnelIds.length, 121 | 'leave': leaveUser 122 | }) 123 | } 124 | } 125 | 126 | module.exports = { 127 | // 小程序请求 websocket 地址 128 | get: async ctx => { 129 | const data = await tunnel.getTunnelUrl(ctx.req) 130 | const tunnelInfo = data.tunnel 131 | 132 | userMap[tunnelInfo.tunnelId] = data.userinfo 133 | 134 | ctx.state.data = tunnelInfo 135 | }, 136 | 137 | // 信道将信息传输过来的时候 138 | post: async ctx => { 139 | const packet = await tunnel.onTunnelMessage(ctx.request.body) 140 | 141 | debug('Tunnel recive a package: %o', packet) 142 | 143 | switch (packet.type) { 144 | case 'connect': 145 | onConnect(packet.tunnelId) 146 | break 147 | case 'message': 148 | onMessage(packet.tunnelId, packet.content.messageType, packet.content.messageContent) 149 | break 150 | case 'close': 151 | onClose(packet.tunnelId) 152 | break 153 | } 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /client/pages/libs/qqmap-wx-jssdk.min.js: -------------------------------------------------------------------------------- 1 | var _createClass=function(){function a(e,c){for(var b=0;b 2 | 3 | 4 | 5 | 用户信息 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{User.nickName}} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 我的订单 29 | 30 | 31 | 32 | 33 | 34 | 35 | 我的红包 36 | 37 | 38 | 39 | 40 | 41 | 42 | 邀请好友送红包 43 | 44 | 45 | 46 | 47 | 48 | 49 | 意见反馈 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 设置 65 | 66 | 67 | 68 | 69 | 70 | 71 | 跑腿员招募 72 | 73 | 74 | 75 | 76 | 77 | 78 | 联系客服 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /client/vendor/wafer2-client-sdk/lib/login.js: -------------------------------------------------------------------------------- 1 | /** 2 | * README!!! 3 | * 为了兼容微信修改的登录逻辑 4 | * 这里对登录的 SDK 进行重构 5 | * 微信公告:https://developers.weixin.qq.com/blogdetail?action=get_post_info&lang=zh_CN&token=&docid=0000a26e1aca6012e896a517556c01 6 | */ 7 | var constants = require('./constants'); 8 | var Session = require('./session'); 9 | 10 | /** 11 | * 微信登录,获取 code 和 encryptData 12 | */ 13 | function getWxLoginResult (cb) { 14 | wx.login({ 15 | success (loginResult) { 16 | wx.getUserInfo({ 17 | success (userResult) { 18 | cb(null, { 19 | code: loginResult.code, 20 | encryptedData: userResult.encryptedData, 21 | iv: userResult.iv, 22 | userInfo: userResult.userInfo 23 | }) 24 | }, 25 | fail (userError) { 26 | cb(new Error('获取微信用户信息失败,请检查网络状态'), null) 27 | } 28 | }); 29 | }, 30 | fail (loginError) { 31 | cb(new Error('微信登录失败,请检查网络状态'), null) 32 | } 33 | }) 34 | } 35 | 36 | const noop = function noop() {} 37 | const defaultOptions = { 38 | method: 'GET', 39 | success: noop, 40 | fail: noop, 41 | loginUrl: null, 42 | } 43 | 44 | /** 45 | * @method 46 | * 进行服务器登录,以获得登录会话 47 | * 受限于微信的限制,本函数需要在 的回调函数中调用 48 | * 需要先使用