diff --git a/README.md b/README.md index c73c3889aa1d3c1d503f3601995ef77aa9f5bea5..73ca2c47fbe5f4bed91ebcdced0cfc7a0588ea0d 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,149 @@ ## 同乘码 + 同乘码作为一个开源的微信小程序项目,希望帮助大家及时发现是否与确诊/疑似新冠病例同行,可查询,可订阅,订阅之后基于事件及时而精准地推送病例信息,基于小程序·云开发技术进行开发,简洁易用友好。 + 详细说明: [https://segmentfault.com/a/1190000021762607](https://segmentfault.com/a/1190000021762607) -## 主要功能: +## 1 主要功能: ![Image text](https://ask.qcloudimg.com/draft/1333088/8qft99ff6t.png?imageView2/2/w/1620) -## 实现方案 +## 2 实现方案 客户端使用原生小程序开发, 服务端全部使用云开发 ![Image text](https://ask.qcloudimg.com/draft/1333088/kn2p6njw6c.png?imageView2/2/w/1620) + + + +## 3 特性 + +1. 基于WeUI开发的UI,界面简洁友好 +2. 基于缓存+存储的查询,高性能 +3. 基于小程序云开发,无服务化,部署更简单 +4. 基于微信疫情同行订阅消息,接收更及时 +5. 基于用户上去上报+外部数据源实时推送,来源更全更灵活 + + + +## 4 部署方式 + +### 4.1下载 + +下载源码 + +```shell +git clone https://gitee.com/tencent_cloud_development/tcb-hackthon-ncov2019confirm.git +``` + +### 4.2 配置 + +修改appid + +project.config.json里修改appid + +```json +{ + // ... + "appid": "自己的appid" + // ... +} +``` + +微信开发者工具导入项目,开通“云开发”,新建云开发环境,得到evn_id,并配置`config`/`config.js` + +```json +{ + "env": "xxx", // 新建的云开发环境的id,注意是id不是name + "traceUser": true // 是否追踪用户 true / false +} +``` + + + +云开发控制台 - 数据库 - 新建集合 + +- admin +- member +- report +- roomInfo +- traffic + +集合结构如下: + +**admin**集合 + +| field | type | +| ------ | ------ | +| openId | string | + +**member**集合 + +| field | type | +| -------- | ------ | +| opendId | string | +| roomId | string | +| src | string | +| status | number | +| userInfo | object | + +**report**集合 + +| field | type | +| ------ | ------ | +| date | string | +| name | string | +| phone | string | +| review | number | +| roomId | string | +| rtype | string | + +**roomInfo**集合 + +| field | type | +| --------- | ------ | +| date | string | +| key | string | +| typeName | string | +| typeValue | string | +| wxacode | string | + +**traffic**集合 + +| field | type | +| ----------- | ------ | +| created_at | string | +| source | string | +| t_date | string | +| t_end | string | +| t_memo | string | +| t_no | string | +| t_no_sub | string | +| t_pos_end | string | +| t_pos_start | string | +| t_start | string | +| t_type | string | +| updated_at | string | +| verified | number | +| who | string | + +新建后,修改权限为“**所有用户可读,仅创建者可读写**”!!! + +### 4.3 部署云函数 + +login`云函数目录,右键 - 上传并部署 + +`openapi`云函数目录,右键 - 上传并部署 + +`sendmsg`云函数目录,右键 - 上传并部署 + +### 4.4 订阅消息模板配置 + +- 订阅消息模板的申请方法:在https://mp.weixin.qq.com/上申请自定义的推送模板,“功能” - “订阅消息” - “添加”, 注意参数的格式要一致; + +- 订阅消息模板的设置方法:cloudfunctions - sendmsg - index.js - templateId 参数配置为自定义的订阅消息模板ID即可; + +### 4.5 完成部署 + +Ctrl + S,即可看到项目Running起来啦! + diff --git a/miniprogram/app.js b/miniprogram/app.js index dcabdeb6b3d480ca34dc653074b9ab938a517c5c..e9c64ba2306e5dff5232ae469085a59a8c38fb3b 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -1,7 +1,10 @@ //app.js +import { + config +} from './config/config.js' App({ - onLaunch: function () { - + onLaunch: function() { + if (!wx.cloud) { console.error('请使用 2.2.3 或以上的基础库以使用云能力') } else { @@ -11,8 +14,8 @@ App({ // 此处请填入环境 ID, 环境 ID 可打开云控制台查看 // 如不填则使用默认环境(第一个创建的环境) // env: 'release-2cwn8', - env: 'test-juk88', - traceUser: true, + env: config.env, + traceUser: config.traceUser, }) } @@ -36,6 +39,6 @@ App({ }) } }) - + } -}) +}) \ No newline at end of file diff --git a/miniprogram/config/config.js b/miniprogram/config/config.js new file mode 100644 index 0000000000000000000000000000000000000000..004d789ec567b0b93407acd120d58a36702030e5 --- /dev/null +++ b/miniprogram/config/config.js @@ -0,0 +1,8 @@ +const config = { + env: 'test-juk88', + traceUser: true +} + +module.exports = { + config +} \ No newline at end of file diff --git a/miniprogram/pages/focus/focus.js b/miniprogram/pages/focus/focus.js index 50e52d5743e1da43e4af511dbb880cca5ff9a108..b725edfcce1339be3077254fd0f861627f50d0e1 100644 --- a/miniprogram/pages/focus/focus.js +++ b/miniprogram/pages/focus/focus.js @@ -27,6 +27,7 @@ Page({ isAdmin: false }, + // 获取我的订阅 showMine: function() { var self = this console.log("show openid", app.globalData.openid) @@ -73,9 +74,7 @@ Page({ * 生命周期函数--监听页面加载 */ onLoad: function(options) { - this.setData({ - isAdmin: ['ogacd5D7Jmu1tiLuUdGfbGVOPNic', 'ogacd5OZFhnml16yxrG8h1iE26iQ'].indexOf(app.globalData.openid) > -1 - }) + this.checkAdmin() }, /** @@ -125,5 +124,20 @@ Page({ */ onShareAppMessage: function() { + }, + + // 判断是否为管理员 + checkAdmin() { + db.collection('admin').where({ + openId: app.globalData.openid + }).get().then(res => { + if (res.errMsg === 'collection.get:ok' && res.data.length > 0) { + this.setData({ + isAdmin: true + }) + } + }).catch(err => { + console.log(err) + }) } }) \ No newline at end of file diff --git a/miniprogram/pages/index/index.js b/miniprogram/pages/index/index.js index 0b5fcd230ab436c7c0a33f060689cf1af4002d6e..49ef9b03e6e74c4f00d6aa6e8076b1799f996afc 100644 --- a/miniprogram/pages/index/index.js +++ b/miniprogram/pages/index/index.js @@ -1,13 +1,9 @@ //index.js +const util = require('../../util/tools.js') const app = getApp() const db = wx.cloud.database() const _ = db.command -function getDateStr() { - var dd = new Date() - return dd.getFullYear().toString() + '-' + (dd.getMonth() + 1).toString() + '-' + dd.getDate().toString() -} - Page({ data: { formData: {}, @@ -53,7 +49,7 @@ Page({ // }) db.collection("report").where({}).get() - var ddstr = getDateStr() + var ddstr = util.getDateStr() this.setData({ date: ddstr }) @@ -150,64 +146,59 @@ Page({ showCancel: false }) } else { - // 从公开数据源查询 - let key = '' - switch (this.data.formData.typeName) { - case "airplane": - key = '飞机' - break - case "train": - key = '火车' - break - case "car": - key = _.in(['出租车', '公交车']) - break - default: - break - } - let date = this.data.formData.date.split('-') - let items = [] - items.push(date[0]) - date[1][0] === '0' ? items.push(date[1].slice(1)) : items.push(date[1]) - date[2][0] === '0' ? items.push(date[2].slice(1)) : items.push(date[2]) - date = items.join('/') - console.log({ - t_type: key, - t_no: this.data.formData.typeValue, - t_date: date - }) - db.collection('traffic').where({ - t_type: key, - t_no: this.data.formData.typeValue, - t_date: date - }).get({ - success: res => { - console.log(res) - if (res.errMsg === 'collection.get:ok' && res.data.length > 0) { - wx.showModal({ - title: '查询结果', - content: self.data.typeName + self.data.formData.typeValue + '存在新冠病例,请注意安全,及时隔离!', - showCancel: false - }) - } else { - // 安全 - if (!danger) { - wx.showModal({ - title: '查询结果', - content: '恭喜你!' + self.data.typeName + self.data.formData.typeValue + '安全,请关注后续信息', - showCancel: false - }) - } - } - } - }) + this.checkFromOpenSource() } }, - error: console.log + fail: console.error + }) + } else { + this.checkFromOpenSource() + } + }, + fail: console.error + }) + }, + + checkFromOpenSource() { + const self = this + // 从公开数据源查询 + let key = '' + switch (this.data.formData.typeName) { + case "airplane": + key = '飞机' + break + case "train": + key = '火车' + break + case "car": + key = _.in(['出租车', '公交车']) + break + default: + break + } + let date = util.translateDate(this.data.formData.date) + db.collection('traffic').where({ + t_type: key, + t_no: this.data.formData.typeValue, + t_date: date + }).get({ + success: res => { + if (res.errMsg === 'collection.get:ok' && res.data.length > 0) { + wx.showModal({ + title: '查询结果', + content: self.data.typeName + self.data.formData.typeValue + '存在新冠病例,请注意安全,及时隔离!', + showCancel: false + }) + } else { + // 安全 + wx.showModal({ + title: '查询结果', + content: '恭喜你!' + self.data.typeName + self.data.formData.typeValue + '安全,请关注后续信息', + showCancel: false }) } }, - error: console.log + fail: console.error }) }, diff --git a/miniprogram/pages/report/report.js b/miniprogram/pages/report/report.js index d87892741f0ec7c923e0ec03c75609bd587c67f7..cd005a38a0cdfd107c2101faa727caab99d69d6d 100644 --- a/miniprogram/pages/report/report.js +++ b/miniprogram/pages/report/report.js @@ -1,12 +1,7 @@ // miniprogram/pages/report/report.js - +const util = require('../../util/tools.js') const db = wx.cloud.database() -function getDateStr() { - var dd = new Date() - return dd.getFullYear().toString() + '-' + (dd.getMonth() + 1).toString() + '-' + dd.getDate().toString() -} - Page({ /** @@ -33,6 +28,7 @@ Page({ this.data.roomId = options.roomId }, + // 提交表单上报疫情 submitForm: function(e) { let vals = e.detail.value if (!vals.rtype) { @@ -68,7 +64,7 @@ Page({ icon: 'none', }) vals.roomId = this.data.roomId - vals.date = getDateStr() + vals.date = util.getDateStr() vals.review = 0 db.collection("report").add({ @@ -84,6 +80,7 @@ Page({ }) }, + // 病例类型变化处理 onTypeChange: function(e) { var radioItems = this.data.radioItems; for (var i = 0, len = radioItems.length; i < len; ++i) { diff --git a/miniprogram/pages/report/report.wxml b/miniprogram/pages/report/report.wxml index b98a3b15d304526e7c50f68fc949322464530fd9..54e69ad18fec10fc0a72e732a51f5cdb946c12de 100644 --- a/miniprogram/pages/report/report.wxml +++ b/miniprogram/pages/report/report.wxml @@ -53,8 +53,8 @@ - - 提交信息后, 平台先会审核信息, 有可能会主动联系您, 感谢您的配合与支持! + + 备注:提交信息后, 平台先会审核信息, 有可能会主动联系您, 感谢您的配合与支持! \ No newline at end of file diff --git a/miniprogram/pages/room/room.js b/miniprogram/pages/room/room.js index 4cb04b2d6efa2ee2d9cb044ede06938b93e39721..91be5920e34f3162a1ad20928a3b98d01adee2a8 100644 --- a/miniprogram/pages/room/room.js +++ b/miniprogram/pages/room/room.js @@ -1,4 +1,3 @@ - //index.js const app = getApp() const db = wx.cloud.database() @@ -29,9 +28,9 @@ Page({ /** * 生命周期函数--监听页面加载 */ - onLoad: function (options) { + onLoad: function(options) { var roomId = options.roomId - if(!roomId){ + if (!roomId) { console.error("missing room id", options) return } @@ -43,8 +42,8 @@ Page({ // wait openid ready var self = this var openId = app.globalData.openid - if(!openId){ - setTimeout(function(){ + if (!openId) { + setTimeout(function() { self.onLoad(options) }, 500) return @@ -52,8 +51,8 @@ Page({ // 初始化页面 self.initRoom(roomId) - - + + // 检查是否有权限 // 获取用户信息 @@ -72,14 +71,14 @@ Page({ } }) } - if(!res.authSetting['scope.subscribeMessage']){ + if (!res.authSetting['scope.subscribeMessage']) { //开启订阅 wx.showModal({ content: '请同意订阅消息', - complete: ()=>{ + complete: () => { wx.requestSubscribeMessage({ tmplIds: [tmpId], - success: res =>{ + success: res => { console.log('scope.subscribeMessage done') }, fail: console.error @@ -91,8 +90,9 @@ Page({ }) }, - onJoinRoom: function(e){ - if(!e.detail.userInfo){ + // 加入同行者时处理 + onJoinRoom: function(e) { + if (!e.detail.userInfo) { wx.showToast({ title: '请同意获取信息', icon: 'none', @@ -107,10 +107,10 @@ Page({ //开启订阅 wx.showModal({ content: '请同意订阅消息', - complete: ()=>{ + complete: () => { wx.requestSubscribeMessage({ tmplIds: [tmpId], - success: res =>{ + success: res => { console.log('scope.subscribeMessage done') }, fail: console.error @@ -120,14 +120,15 @@ Page({ wx.requestSubscribeMessage({ tmplIds: [tmpId], - success: res =>{ + success: res => { console.log('scope.subscribeMessage done') }, fail: console.error }) }, - joinRoom: function(){ + // 加入同行者 + joinRoom: function() { var roomId = this.data.roomId var openId = app.globalData.openid var self = this @@ -140,7 +141,7 @@ Page({ src: self.data.userInfo.avatarUrl, userInfo: self.data.userInfo, }, - success: function(e){ + success: function(e) { console.log("join succ", e) self.onShow() // 刷新一次页面数据 }, @@ -151,11 +152,13 @@ Page({ /** * 生命周期函数--监听页面隐藏 */ - initRoom: function (roomId) { + initRoom: function(roomId) { console.log('init room', roomId) var self = this - db.collection('roomInfo').where({_id: roomId}).get({ - success: function(e){ + db.collection('roomInfo').where({ + _id: roomId + }).get({ + success: function(e) { console.log("room info", e.data[0]) self.setData({ room: { @@ -167,7 +170,7 @@ Page({ } }) }, - fail: function(e){ + fail: function(e) { wx.showToast({ title: '信息不存在', }) @@ -178,28 +181,32 @@ Page({ /** * 生命周期函数--监听页面初次渲染完成 */ - onReady: function () { - }, + onReady: function() {}, /** * 生命周期函数--监听页面显示 */ - onShow: function () { + onShow: function() { var roomId = this.data.roomId var self = this - db.collection("member").where({roomId: roomId, status: 0}).count({ + db.collection("member").where({ + roomId: roomId, + status: 0 + }).count({ success: res => { self.setData({ memberNum: res.total, }) - }, + }, fail: console.error, }) - db.collection("report").where({roomId: roomId}).count({ + db.collection("report").where({ + roomId: roomId + }).count({ success: res => { - if(res.total > 0){ + if (res.total > 0) { self.setData({ reportText: "共有" + res.total + "例报告", }) @@ -210,7 +217,8 @@ Page({ }, - showWXCode: function(){ + // 显示二维码 + showWXCode: function() { wx.previewImage({ current: this.data.room.wxacode, // 当前显示图片的http链接 urls: [this.data.room.wxacode] // 需要预览的图片http链接列表 @@ -220,36 +228,39 @@ Page({ /** * 页面上拉触底事件的处理函数 */ - onReachBottom: function () { + onReachBottom: function() { }, /** * 用户点击右上角分享 */ - onShareAppMessage: function () { + onShareAppMessage: function() { }, - exitRoom: function(){ + // 退出同行者 + exitRoom: function() { var self = this wx.showModal({ content: '退出后将无法收到后续的订阅消息', success: res => { - if(res.confirm){ + if (res.confirm) { console.log("start exit", self.data.room.id, app.globalData.openid) db.collection("member").where({ roomId: self.data.room.id, - openId: app.globalData.openid, + openId: app.globalData.openid, }).update({ - data: {status: 1}, - success: res =>{ + data: { + status: 1 + }, + success: res => { wx.showToast({ title: '退出成功', duration: 2000, }) - + setTimeout(function() { wx.switchTab({ url: '/pages/focus/focus', diff --git a/miniprogram/util/tools.js b/miniprogram/util/tools.js new file mode 100644 index 0000000000000000000000000000000000000000..c703d32f15c6ca93b372a9e5941c69d6b3e11e53 --- /dev/null +++ b/miniprogram/util/tools.js @@ -0,0 +1,18 @@ +let getDateStr = function() { + var dd = new Date() + return dd.getFullYear().toString() + '-' + (dd.getMonth() + 1).toString() + '-' + dd.getDate().toString() +} + +let translateDate = function(ds) { + const date = ds.split('-') + let items = [] + items.push(date[0]) + date[1][0] === '0' ? items.push(date[1].slice(1)) : items.push(date[1]) + date[2][0] === '0' ? items.push(date[2].slice(1)) : items.push(date[2]) + return items.join('/') +} + +module.exports = { + getDateStr, + translateDate +}