From 9708e1f673e7c18a3cc055de78ded962bf264cd7 Mon Sep 17 00:00:00 2001
From: xuecong <>
Date: Tue, 16 Nov 2021 20:42:59 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=97=A8=E7=A6=81=E3=80=81?=
=?UTF-8?q?=E8=AE=BF=E5=AE=A2=E7=A0=81=E3=80=81=E4=B8=9A=E4=B8=BB=E4=BA=8C?=
=?UTF-8?q?=E7=BB=B4=E7=A0=81=E5=8A=A0=E5=AF=86=E6=96=B9=E5=BC=8F&?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=99=BA=E8=83=BD=E9=97=A8=E7=A6=81=E7=89=A9?=
=?UTF-8?q?=E8=81=94=E7=BD=91=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
iot/README.MD | 17 ++
ower-mp/project.config.json | 2 +-
ower-mp/src/pages/vistor/create.js | 2 +-
ower-mp/src/pages/vistor/create.wxml | 2 +-
resource/db.sql | 8 -
server/src/app.ts | 16 +-
server/src/constant/code.ts | 16 ++
server/src/iot/entrance/access.ts | 124 +++++++++++
server/src/iot/entrance/index.ts | 197 ++++++++++++++++++
server/src/iot/entrance/status.ts | 51 +++++
server/src/iot/index.ts | 18 ++
.../src/module/mp/controller/access/list.ts | 23 +-
.../controller/community/binding_by_family.ts | 2 +-
.../community/binding_by_property.ts | 2 +-
.../mp/controller/community/family_code.ts | 2 +-
server/src/module/mp/controller/user/card.ts | 10 +-
.../src/module/mp/controller/vistor/create.ts | 8 -
.../src/module/mp/controller/vistor/detail.ts | 7 +-
server/src/module/mp/controller/vistor/use.ts | 8 +-
.../module/pc/controller/building/import.ts | 13 --
.../src/module/pc/controller/option/card.ts | 14 +-
.../src/module/pc/controller/ower/approve.ts | 2 +-
.../src/module/pc/controller/vistor/create.ts | 9 +-
.../src/module/pc/controller/vistor/detail.ts | 5 +
.../src/module/pc/controller/vistor/scan.ts | 2 +-
server/src/types/model.d.ts | 9 -
.../src/utils/{open_access.ts => access.ts} | 36 ++--
server/src/utils/community.ts | 22 +-
server/src/utils/crypto.ts | 24 +++
server/src/utils/index.ts | 8 +-
server/src/utils/redis.ts | 58 ++++++
web/src/views/basic/vistor/detail.vue | 5 +-
32 files changed, 599 insertions(+), 123 deletions(-)
create mode 100644 server/src/iot/entrance/access.ts
create mode 100644 server/src/iot/entrance/index.ts
create mode 100644 server/src/iot/entrance/status.ts
create mode 100644 server/src/iot/index.ts
rename server/src/utils/{open_access.ts => access.ts} (65%)
create mode 100644 server/src/utils/redis.ts
diff --git a/iot/README.MD b/iot/README.MD
index 9fa8bac..21636b8 100644
--- a/iot/README.MD
+++ b/iot/README.MD
@@ -1,3 +1,20 @@
## 「e家宜业」物联网服务端
> 代码整理中,敬请期待
+
+### 智能门禁
+
+> 目前仅支持卡帕斯品牌
+
+#### 接口地址
+
+- 心跳检测 `域名/iot/entrance?id={{接口形参第一位}}&community_id={{接口形参第二位}}&method=status`
+- 开门刷卡 `域名/iot/entrance?id={{接口形参第一位}}&community_id={{接口形参第二位}}&method=access`
+
+#### 门禁配置
+
+* 选择`启用AES128加密`,秘钥配置中台对应的秘钥
+* 心跳间隔设置为`60`
+* `本机信息-标识`配置为中台对应的标识名称
+* `通讯协议`选择`启用Http通讯`
+* `验证模式`选择`脱机验证优先`(主要针对老旧小区用的RFID卡)
diff --git a/ower-mp/project.config.json b/ower-mp/project.config.json
index a8c05d6..af8eca2 100644
--- a/ower-mp/project.config.json
+++ b/ower-mp/project.config.json
@@ -72,4 +72,4 @@
"list": []
}
}
-}
+}
\ No newline at end of file
diff --git a/ower-mp/src/pages/vistor/create.js b/ower-mp/src/pages/vistor/create.js
index 1a43916..96bb828 100644
--- a/ower-mp/src/pages/vistor/create.js
+++ b/ower-mp/src/pages/vistor/create.js
@@ -97,7 +97,7 @@ CwPage({
},
chooseContact() {
wx.chooseContact({
- success: (res) => {
+ success: res => {
this.setData({
vistor_name: res.displayName,
vistor_phone: res.phoneNumber
diff --git a/ower-mp/src/pages/vistor/create.wxml b/ower-mp/src/pages/vistor/create.wxml
index 9d241ad..76bf7b8 100644
--- a/ower-mp/src/pages/vistor/create.wxml
+++ b/ower-mp/src/pages/vistor/create.wxml
@@ -10,7 +10,7 @@
is-detail
url="/pages/community/index"
/>
-
+
{
diff --git a/server/src/constant/code.ts b/server/src/constant/code.ts
index bf65578..781b959 100644
--- a/server/src/constant/code.ts
+++ b/server/src/constant/code.ts
@@ -148,3 +148,19 @@ export const PARK_NAME_EXIST = -171;
export const PARK_BLACKLIST_EXIST = -172;
// 预警中控名称存在
export const WARNING_NAME_EXIST = -173;
+// 门禁非法
+export const IOT_ENTRANCE_ILLEGAL = -174;
+// 梯控非法
+export const IOT_ELEVATOR_ILLEGAL = -175;
+// 灯控非法
+export const IOT_LAMP_ILLEGAL = -176;
+// 中继器非法
+export const IOT_REPEATER_ILLEGAL = -177;
+// 仪表非法
+export const IOT_METER_ILLEGAL = -178;
+// 停车差非法
+export const IOT_PARK_ILLEGAL = -179;
+// 预警中控非法
+export const IOT_WARNING_ILLEGAL = -180;
+// 物联网设备秘钥错误
+export const IOT_SECRET_ERROR = -181;
diff --git a/server/src/iot/entrance/access.ts b/server/src/iot/entrance/access.ts
new file mode 100644
index 0000000..7bd4c8b
--- /dev/null
+++ b/server/src/iot/entrance/access.ts
@@ -0,0 +1,124 @@
+/**
+ * +----------------------------------------------------------------------
+ * | 「e家宜业」 —— 助力物业服务升级,用心服务万千业主
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 2020~2021 https://www.chowa.com All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「e家宜业」和「卓瓦科技」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: jixuecong@chowa.cn
+ * +----------------------------------------------------------------------
+ */
+
+import moment from 'moment';
+import Knex from 'Knex';
+import { EjyyIotEntrance } from '~/types/model';
+import { SELF_ACCESS_QRCODE, VISTOR_ACCESS_QRCODE } from '~/constant/open_access';
+import utils from '~/utils';
+import { IOT_METHOD_QRCODE, IOT_METHOD_NFC, IOT_METHOD_ICCARD } from '~/constant/iot';
+
+const TYPE_CARD = '0';
+const TYPE_SERIAL = '1';
+const TYPE_PASSWORD = '2';
+const TYPE_BUTTON = '3';
+const TYPE_ID_CARD = '6';
+const TYPE_QRCODE = '9';
+const TYPE_FINGERPRINT = '10';
+const TYPE_PULSE = '11';
+const TYPE_RFID = '12';
+const TYPE_FACE = '13';
+
+const OPEN = '1';
+const CLOSE = '3';
+
+const ENTER = '0';
+const LEAVE = '1';
+
+interface AccessParams {
+ type:
+ | typeof TYPE_CARD
+ | typeof TYPE_SERIAL
+ | typeof TYPE_PASSWORD
+ | typeof TYPE_BUTTON
+ | typeof TYPE_ID_CARD
+ | typeof TYPE_QRCODE
+ | typeof TYPE_FINGERPRINT
+ | typeof TYPE_PULSE
+ | typeof TYPE_RFID
+ | typeof TYPE_FACE;
+ Reader: string;
+ DataLen: string;
+ Index: string;
+ Serial: string;
+ Status: string;
+ Input: string;
+ Ver: string;
+ ID: string;
+ IP: string;
+ MAC: string;
+ Card: string;
+}
+
+interface AccessResponse {
+ AcsRes: typeof OPEN | typeof CLOSE;
+ ActIndex?: typeof ENTER | typeof LEAVE;
+ Time?: string;
+}
+
+async function accessMethod(params: AccessParams, entranceInfo: EjyyIotEntrance, model: Knex): Promise {
+ const mom = moment();
+ await utils.redis.set(`entrance${entranceInfo.id}`, mom.valueOf().toString());
+
+ // 卡 二维码
+ if (
+ params.type === TYPE_CARD ||
+ params.type === TYPE_RFID ||
+ params.type === TYPE_SERIAL ||
+ params.type === TYPE_QRCODE
+ ) {
+ const reuslt =
+ params.type === TYPE_SERIAL || params.type === TYPE_QRCODE
+ ? utils.access.decrypt(Buffer.from(params.Card, 'base64').toString())
+ : utils.access.decrypt(params.Card);
+
+ if (!reuslt.success) {
+ return { AcsRes: CLOSE };
+ }
+
+ const existBuilding = await model
+ .from('ejyy_building_info')
+ .where('id', reuslt.building_id)
+ .andWhere('community_id', entranceInfo.community_id)
+ .first();
+
+ if (!existBuilding) {
+ return { AcsRes: CLOSE };
+ }
+
+ let method: typeof IOT_METHOD_NFC | typeof IOT_METHOD_ICCARD | typeof IOT_METHOD_QRCODE = IOT_METHOD_QRCODE;
+
+ if (params.type === TYPE_CARD) {
+ method = IOT_METHOD_NFC;
+ } else if (params.type === TYPE_RFID) {
+ method = IOT_METHOD_ICCARD;
+ }
+
+ await model.from('ejyy_iot_entrance_log').insert({
+ wechat_mp_user_id: reuslt.type === VISTOR_ACCESS_QRCODE ? null : reuslt.id,
+ vistor_id: reuslt.type === SELF_ACCESS_QRCODE ? null : reuslt.id,
+ entrance_id: entranceInfo.id,
+ method,
+ created_at: mom.valueOf()
+ });
+
+ return { AcsRes: OPEN, ActIndex: ENTER, Time: '5' };
+ }
+ // 按钮
+ else if (params.type === TYPE_BUTTON) {
+ return { AcsRes: OPEN, ActIndex: LEAVE };
+ } else {
+ return { AcsRes: CLOSE };
+ }
+}
+
+export default accessMethod;
diff --git a/server/src/iot/entrance/index.ts b/server/src/iot/entrance/index.ts
new file mode 100644
index 0000000..33ee5f8
--- /dev/null
+++ b/server/src/iot/entrance/index.ts
@@ -0,0 +1,197 @@
+/**
+ * +----------------------------------------------------------------------
+ * | 「e家宜业」 —— 助力物业服务升级,用心服务万千业主
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 2020~2021 https://www.chowa.com All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「e家宜业」和「卓瓦科技」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: jixuecong@chowa.cn
+ * +----------------------------------------------------------------------
+ */
+
+import path from 'path';
+import crypto from 'crypto';
+import RawBody from 'raw-body';
+import KoaRouter from 'koa-router';
+import { Context } from 'koa';
+import { Action } from '~/types/action';
+import { IOT_ENTRANCE_ILLEGAL, PARAMS_ERROR, IOT_SECRET_ERROR } from '~/constant/code';
+import validatorService from '~/service/validator';
+import statusMethod from './status';
+import accessMethod from './access';
+import config from '~/config';
+import cwlog from 'chowa-log';
+
+function zeroPad(str: string, length = 8): string {
+ str = Buffer.from(str, 'utf8').toString('hex');
+ const bitLength = str.length * length;
+ if (bitLength < 256) {
+ for (let i = bitLength; i < 256; i += length) {
+ str += 0;
+ }
+ } else if (bitLength > 256) {
+ while ((str.length * length) % 256 != 0) {
+ str += 0;
+ }
+ }
+ return Buffer.from(str, 'hex').toString('utf8');
+}
+
+function encrypt(str: string, secret: string): string {
+ const cipher = crypto.createCipheriv('aes-128-ecb', secret, '');
+
+ cipher.setAutoPadding(false);
+
+ let crypted = cipher.update(zeroPad(str), 'utf8', 'hex');
+
+ crypted += cipher.final('hex');
+
+ return crypted;
+}
+
+function decrypt(str: string, secret: string): string {
+ const cipher = crypto.createDecipheriv('aes-128-ecb', secret, '');
+ const isBase64 = Buffer.from(str, 'base64').toString('base64') === str;
+
+ cipher.setAutoPadding(false);
+
+ let decrypted = cipher.update(zeroPad(str), isBase64 ? 'base64' : 'hex', 'utf8');
+ decrypted = decrypted + cipher.final('utf8');
+
+ return decrypted.replace(/[\u0000-\u0019]+/g, '');
+}
+
+const IotEntranceAction = {
+ router: {
+ path: '/entrance',
+ method: 'post',
+ authRequired: false
+ },
+ validator: {
+ query: [
+ {
+ name: 'community_id',
+ required: true,
+ regex: /^\d+$/
+ },
+ {
+ name: 'id',
+ required: true,
+ regex: /^\d+$/
+ },
+ {
+ name: 'method',
+ required: true,
+ regex: /^status|access$/
+ }
+ ]
+ },
+ response: async ctx => {
+ const { community_id, id, method } = ctx.request.query;
+
+ const entranceInfo = await ctx.model
+ .from('ejyy_iot_entrance')
+ .where('id', id)
+ .andWhere('community_id', community_id)
+ .first();
+
+ if (!entranceInfo) {
+ return (ctx.body = {
+ code: IOT_ENTRANCE_ILLEGAL,
+ message: '非法的物联网门禁'
+ });
+ }
+
+ const raw = await RawBody(ctx.req, {
+ length: ctx.request.length,
+ limit: '1mb',
+ encoding: ctx.request.charset || 'utf-8'
+ });
+
+ const datas = raw.replace('DATAS=', '');
+
+ if (!datas) {
+ return (ctx.body = {
+ code: PARAMS_ERROR,
+ message: '物联网门禁受控参数错误'
+ });
+ }
+
+ let decrypted = null;
+
+ try {
+ decrypted = decrypt(datas, entranceInfo.secret);
+ } catch (e) {
+ return (ctx.bod = {
+ code: IOT_SECRET_ERROR,
+ message: '物联网门禁秘钥错误'
+ });
+ }
+
+ let params = null;
+
+ try {
+ params = JSON.parse(decrypted);
+ } catch (e) {
+ return (ctx.body = {
+ code: IOT_SECRET_ERROR,
+ message: '物联网门禁秘钥错误'
+ });
+ }
+
+ if (!('Serial' in params) || !('ID' in params)) {
+ return (ctx.body = {
+ code: PARAMS_ERROR,
+ message: '物联网门禁控制参数错误'
+ });
+ }
+
+ if (params.ID !== entranceInfo.sign) {
+ return (ctx.body = {
+ code: PARAMS_ERROR,
+ message: '物联网门禁自定义标识符配置错误'
+ });
+ }
+
+ let res = {};
+ console.log(params);
+ switch (method) {
+ // 心跳
+ case 'status':
+ res = await statusMethod(params, entranceInfo);
+ break;
+
+ // 开门
+ case 'access':
+ res = await accessMethod(params, entranceInfo, ctx.model);
+ break;
+
+ default:
+ }
+
+ ctx.res.setHeader('Content-Type', 'text/html');
+ ctx.body = `DATAS={${encrypt(JSON.stringify(res), entranceInfo.secret)}}`;
+ }
+};
+
+export default (appRouter: KoaRouter) => {
+ const { router, validator, response } = IotEntranceAction;
+
+ appRouter[router.method](path.posix.join('/iot', router.path), async (ctx: Context, next) => {
+ const vs = validatorService(ctx, validator);
+
+ if (!vs.success) {
+ return (ctx.body = {
+ code: PARAMS_ERROR,
+ message: vs.message
+ });
+ }
+
+ await response.apply(this, [ctx, next]);
+ });
+
+ if (config.debug) {
+ cwlog.info(`IotEntrance mounted and request from ${path.posix.join('/iot', router.path)} by ${router.method}`);
+ }
+};
diff --git a/server/src/iot/entrance/status.ts b/server/src/iot/entrance/status.ts
new file mode 100644
index 0000000..167f769
--- /dev/null
+++ b/server/src/iot/entrance/status.ts
@@ -0,0 +1,51 @@
+/**
+ * +----------------------------------------------------------------------
+ * | 「e家宜业」 —— 助力物业服务升级,用心服务万千业主
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 2020~2021 https://www.chowa.com All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「e家宜业」和「卓瓦科技」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: jixuecong@chowa.cn
+ * +----------------------------------------------------------------------
+ */
+
+import moment from 'moment';
+import { EjyyIotEntrance } from '~/types/model';
+import utils from '~/utils';
+
+interface StatusParams {
+ Serial: string;
+ Status: string;
+ Input: string;
+ Ver: string;
+ ID: string;
+ IP: string;
+ MAC: string;
+ Now: string;
+ Crc: string;
+ T1: string;
+ H1: string;
+ T2: string;
+ H2: string;
+ NextNum: string;
+ Key: string;
+}
+
+interface StatusResponse {
+ Key: string;
+ Now: string;
+}
+
+async function statusMethod(params: StatusParams, entranceInfo: EjyyIotEntrance): Promise {
+ const mom = moment();
+
+ await utils.redis.set(`entrance${entranceInfo.id}`, mom.valueOf().toString());
+
+ return {
+ Key: params.Key,
+ Now: `${mom.format('YYYYMMDDHHmmss')}${mom.day()}`
+ };
+}
+
+export default statusMethod;
diff --git a/server/src/iot/index.ts b/server/src/iot/index.ts
new file mode 100644
index 0000000..f853f16
--- /dev/null
+++ b/server/src/iot/index.ts
@@ -0,0 +1,18 @@
+/**
+ * +----------------------------------------------------------------------
+ * | 「e家宜业」 —— 助力物业服务升级,用心服务万千业主
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 2020~2021 https://www.chowa.com All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「e家宜业」和「卓瓦科技」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: jixuecong@chowa.cn
+ * +----------------------------------------------------------------------
+ */
+
+import KoaRouter from 'koa-router';
+import IotEntrance from './entrance';
+
+export function init(appRouter: KoaRouter) {
+ IotEntrance(appRouter);
+}
diff --git a/server/src/module/mp/controller/access/list.ts b/server/src/module/mp/controller/access/list.ts
index a26a20c..7267283 100644
--- a/server/src/module/mp/controller/access/list.ts
+++ b/server/src/module/mp/controller/access/list.ts
@@ -12,6 +12,8 @@
import { Action } from '~/types/action';
import { SUCCESS } from '~/constant/code';
+import utils from '~/utils';
+import { SELF_ACCESS_QRCODE } from '~/constant/open_access';
interface RequestBody {
building_ids: number[];
@@ -43,11 +45,22 @@ const MpAccessListAction = {
response: async ctx => {
const { building_ids, community_id } = ctx.request.body;
- const cardList = await ctx.model
- .from('ejyy_building_access')
- .whereIn('building_id', building_ids)
- .select('building_id', 'uid')
- .orderBy('id', 'desc');
+ const buildings = await ctx.model
+ .from('ejyy_user_building')
+ .leftJoin('ejyy_building_info', 'ejyy_building_info.id', 'ejyy_user_building.building_id')
+ .where('ejyy_building_info.community_id', community_id)
+ .whereIn('ejyy_user_building.building_id', building_ids)
+ .select('ejyy_user_building.building_id')
+ .orderBy('ejyy_user_building.id', 'desc');
+
+ const cardList = [];
+
+ buildings.forEach(({ building_id }) => {
+ cardList.push({
+ building_id,
+ uid: utils.access.encrypt(ctx.mpUserInfo.id, building_id, SELF_ACCESS_QRCODE)
+ });
+ });
const entranceList = await ctx.model
.from('ejyy_iot_entrance')
diff --git a/server/src/module/mp/controller/community/binding_by_family.ts b/server/src/module/mp/controller/community/binding_by_family.ts
index e9f0642..51efba7 100644
--- a/server/src/module/mp/controller/community/binding_by_family.ts
+++ b/server/src/module/mp/controller/community/binding_by_family.ts
@@ -41,7 +41,7 @@ const MpCommunityBindingByFamliyAction = {
response: async ctx => {
const { qrcontent } = ctx.request.body;
- const qrInfo = utils.community.decode(qrcontent);
+ const qrInfo = utils.community.decrypt(qrcontent);
if (
!qrInfo.success ||
diff --git a/server/src/module/mp/controller/community/binding_by_property.ts b/server/src/module/mp/controller/community/binding_by_property.ts
index 2a24fd7..7a352fb 100644
--- a/server/src/module/mp/controller/community/binding_by_property.ts
+++ b/server/src/module/mp/controller/community/binding_by_property.ts
@@ -39,7 +39,7 @@ const MpCommunityBindingByPropertyAction = {
},
response: async ctx => {
const { qrcontent } = ctx.request.body;
- const qrInfo = utils.community.decode(qrcontent);
+ const qrInfo = utils.community.decrypt(qrcontent);
if (!qrInfo.success || qrInfo.authenticated_type !== AUTHENTICTED_BY_PROPERTY_COMPANY) {
return (ctx.body = {
diff --git a/server/src/module/mp/controller/community/family_code.ts b/server/src/module/mp/controller/community/family_code.ts
index 7a4e3d2..20a776e 100644
--- a/server/src/module/mp/controller/community/family_code.ts
+++ b/server/src/module/mp/controller/community/family_code.ts
@@ -41,7 +41,7 @@ const MpCommunityFamliyCodeAction = {
response: async ctx => {
const { building_ids } = ctx.request.body;
- const content = utils.community.encode(building_ids, AUTHENTICTED_BY_FAMILY, ctx.mpUserInfo.id);
+ const content = utils.community.encrypt(building_ids, AUTHENTICTED_BY_FAMILY, ctx.mpUserInfo.id);
ctx.body = {
code: SUCCESS,
diff --git a/server/src/module/mp/controller/user/card.ts b/server/src/module/mp/controller/user/card.ts
index 7477691..53f559f 100644
--- a/server/src/module/mp/controller/user/card.ts
+++ b/server/src/module/mp/controller/user/card.ts
@@ -12,8 +12,7 @@
import { Action } from '~/types/action';
import { SUCCESS } from '~/constant/code';
-import config from '~/config';
-import crypto from 'crypto';
+import utils from '~/utils';
const MpUserCardAction = {
router: {
@@ -24,15 +23,10 @@ const MpUserCardAction = {
},
response: async ctx => {
- const cipher = crypto.createCipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
- let crypted = cipher.update(`${ctx.mpUserInfo.id}-${Date.now()}`, 'utf8', 'hex');
-
- crypted += cipher.final('hex');
-
ctx.body = {
code: SUCCESS,
data: {
- uid: crypted
+ uid: utils.crypto.encrypt(`${ctx.mpUserInfo.id}-${Date.now()}`)
}
};
}
diff --git a/server/src/module/mp/controller/vistor/create.ts b/server/src/module/mp/controller/vistor/create.ts
index 5530ada..7c0cda8 100644
--- a/server/src/module/mp/controller/vistor/create.ts
+++ b/server/src/module/mp/controller/vistor/create.ts
@@ -13,7 +13,6 @@
import { Action } from '~/types/action';
import { SUCCESS, STATUS_ERROR } from '~/constant/code';
import { BINDING_BUILDING, TRUE, FALSE } from '~/constant/status';
-import { VISTOR_ACCESS_QRCODE } from '~/constant/open_access';
import utils from '~/utils';
import * as vistorService from '~/service/vistor';
import moment from 'moment';
@@ -117,13 +116,6 @@ const MpVistorCreateAction = {
created_at: Date.now()
});
- const uid = utils.openAccess.encode(id, building_id, VISTOR_ACCESS_QRCODE);
-
- await ctx.model
- .from('ejyy_vistor')
- .update({ uid })
- .where('id', id);
-
if (vistorInfo) {
vistorService.pushAccessToVistor(ctx.model, vistorInfo, id, expire);
}
diff --git a/server/src/module/mp/controller/vistor/detail.ts b/server/src/module/mp/controller/vistor/detail.ts
index 03b4e1a..2feec64 100644
--- a/server/src/module/mp/controller/vistor/detail.ts
+++ b/server/src/module/mp/controller/vistor/detail.ts
@@ -12,6 +12,7 @@
import { Action } from '~/types/action';
import { SUCCESS } from '~/constant/code';
+import { VISTOR_ACCESS_QRCODE } from '~/constant/open_access';
import utils from '~/utils';
interface RequestParams {
@@ -51,6 +52,7 @@ const MpVistorDetailAction = {
'ejyy_vistor.expire',
'ejyy_vistor.used_at',
'ejyy_vistor.created_at',
+ 'ejyy_vistor.building_id',
'ejyy_building_info.type',
'ejyy_building_info.area',
'ejyy_building_info.building',
@@ -65,10 +67,13 @@ const MpVistorDetailAction = {
detail.vistor_phone = utils.phone.hide(detail.vistor_phone);
+ const uid = utils.access.encrypt(detail.id, detail.building_id, VISTOR_ACCESS_QRCODE);
+
ctx.body = {
code: SUCCESS,
data: {
- ...detail
+ ...detail,
+ uid
}
};
}
diff --git a/server/src/module/mp/controller/vistor/use.ts b/server/src/module/mp/controller/vistor/use.ts
index bcae657..b759489 100644
--- a/server/src/module/mp/controller/vistor/use.ts
+++ b/server/src/module/mp/controller/vistor/use.ts
@@ -12,6 +12,8 @@
import { Action } from '~/types/action';
import { SUCCESS } from '~/constant/code';
+import { VISTOR_ACCESS_QRCODE } from '~/constant/open_access';
+import utils from '~/utils';
interface RequestParams {
id: number;
@@ -47,6 +49,7 @@ const MpVistorUseAction = {
'ejyy_vistor.expire',
'ejyy_vistor.used_at',
'ejyy_vistor.created_at',
+ 'ejyy_vistor.building_id',
'ejyy_building_info.type',
'ejyy_building_info.area',
'ejyy_building_info.building',
@@ -59,10 +62,13 @@ const MpVistorUseAction = {
.where('ejyy_vistor.id', id)
.first();
+ const uid = utils.access.encrypt(detail.id, detail.building_id, VISTOR_ACCESS_QRCODE);
+
ctx.body = {
code: SUCCESS,
data: {
- ...detail
+ ...detail,
+ uid
}
};
}
diff --git a/server/src/module/pc/controller/building/import.ts b/server/src/module/pc/controller/building/import.ts
index ccb2b53..798da23 100644
--- a/server/src/module/pc/controller/building/import.ts
+++ b/server/src/module/pc/controller/building/import.ts
@@ -15,7 +15,6 @@ import { SUCCESS } from '~/constant/code';
import * as ROLE from '~/constant/role_access';
import utils from '~/utils';
import { HOUSE, CARPORT, WAREHOUSE, MERCHANT, GARAGE } from '~/constant/building';
-import { SELF_ACCESS_QRCODE } from '~/constant/open_access';
interface Building {
type: typeof HOUSE | typeof CARPORT | typeof WAREHOUSE;
@@ -127,18 +126,6 @@ const PcBuildingImportAction = {
});
}
- if (type === HOUSE) {
- const [accessId] = await ctx.model.from('ejyy_building_access').insert({
- building_id: insertId,
- created_at
- });
- const uid = utils.openAccess.encode(accessId, insertId, SELF_ACCESS_QRCODE);
- await ctx.model
- .from('ejyy_building_access')
- .update({ uid })
- .where('id', accessId);
- }
-
ids.push(insertId);
}
diff --git a/server/src/module/pc/controller/option/card.ts b/server/src/module/pc/controller/option/card.ts
index 4501482..dcd4b3a 100644
--- a/server/src/module/pc/controller/option/card.ts
+++ b/server/src/module/pc/controller/option/card.ts
@@ -17,8 +17,6 @@ import { NORMAL_STATUS, TRUE, BINDING_BUILDING } from '~/constant/status';
import { HOUSE, CARPORT, WAREHOUSE, MERCHANT, GARAGE } from '~/constant/building';
import * as ROLE from '~/constant/role_access';
import utils from '~/utils';
-import config from '~/config';
-import crypto from 'crypto';
interface RequestBody {
uid: string;
@@ -57,21 +55,17 @@ const PcOptionCardAction = {
},
response: async ctx => {
const { uid, community_id } = ctx.request.body;
- let userId = null;
- let stamp = null;
+ const origin = utils.crypto.decrypt(uid);
- try {
- const cipher = crypto.createDecipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
- const decrypted = cipher.update(uid, 'hex', 'utf8');
- const origin = decrypted + cipher.final('utf8');
- [userId, stamp] = origin.split('-');
- } catch (e) {
+ if (!origin) {
return (ctx.body = {
code: QUERY_ILLEFAL,
message: '非法业主名片'
});
}
+ const [userId, stamp] = origin.split('-');
+
if (!/^\d+$/.test(userId) || !/^\d{13}$/.test(stamp)) {
return (ctx.body = {
code: QUERY_ILLEFAL,
diff --git a/server/src/module/pc/controller/ower/approve.ts b/server/src/module/pc/controller/ower/approve.ts
index 8f83605..89eee42 100644
--- a/server/src/module/pc/controller/ower/approve.ts
+++ b/server/src/module/pc/controller/ower/approve.ts
@@ -61,7 +61,7 @@ const PcOwerApproveAction = {
});
}
- const res = utils.community.encode(building_ids, AUTHENTICTED_BY_PROPERTY_COMPANY, ctx.pcUserInfo.id);
+ const res = utils.community.encrypt(building_ids, AUTHENTICTED_BY_PROPERTY_COMPANY, ctx.pcUserInfo.id);
ctx.body = {
code: SUCCESS,
diff --git a/server/src/module/pc/controller/vistor/create.ts b/server/src/module/pc/controller/vistor/create.ts
index 7902271..6ccc473 100644
--- a/server/src/module/pc/controller/vistor/create.ts
+++ b/server/src/module/pc/controller/vistor/create.ts
@@ -110,17 +110,12 @@ const PcVistorCreateAction = {
created_at: Date.now()
});
- const uid = utils.openAccess.encode(id, building_id, VISTOR_ACCESS_QRCODE);
-
- await ctx.model
- .from('ejyy_vistor')
- .update({ uid })
- .where('id', id);
-
if (vistorInfo) {
vistorService.pushAccessToVistor(ctx.model, vistorInfo, id, expire);
}
+ const uid = utils.access.encrypt(id, building_id, VISTOR_ACCESS_QRCODE);
+
ctx.body = {
code: SUCCESS,
data: {
diff --git a/server/src/module/pc/controller/vistor/detail.ts b/server/src/module/pc/controller/vistor/detail.ts
index e185b30..f4077b5 100644
--- a/server/src/module/pc/controller/vistor/detail.ts
+++ b/server/src/module/pc/controller/vistor/detail.ts
@@ -13,6 +13,8 @@
import { Action } from '~/types/action';
import { SUCCESS, QUERY_ILLEFAL } from '~/constant/code';
import * as ROLE from '~/constant/role_access';
+import { VISTOR_ACCESS_QRCODE } from '~/constant/open_access';
+import utils from '~/utils';
interface RequestBody {
id: number;
@@ -94,10 +96,13 @@ const PcVistorDetailAction = {
delete info.property_company_user_id;
+ const uid = utils.access.encrypt(info.id, info.building_id, VISTOR_ACCESS_QRCODE);
+
ctx.body = {
code: SUCCESS,
data: {
info,
+ uid,
registrant
}
};
diff --git a/server/src/module/pc/controller/vistor/scan.ts b/server/src/module/pc/controller/vistor/scan.ts
index 78fe959..2e0c5ff 100644
--- a/server/src/module/pc/controller/vistor/scan.ts
+++ b/server/src/module/pc/controller/vistor/scan.ts
@@ -45,7 +45,7 @@ const PcVistorScanAction = {
},
response: async ctx => {
const { uid, community_id } = ctx.request.body;
- const { id, building_id, type, success } = utils.openAccess.decode(uid);
+ const { id, building_id, type, success } = utils.access.decrypt(uid);
if (!success || type !== VISTOR_ACCESS_QRCODE) {
return (ctx.body = {
diff --git a/server/src/types/model.d.ts b/server/src/types/model.d.ts
index 9cb4ab3..35809b5 100644
--- a/server/src/types/model.d.ts
+++ b/server/src/types/model.d.ts
@@ -415,13 +415,6 @@ declare namespace EjyyModel {
replyed_at?: number;
}
- interface EjyyBuildingAccess {
- id?: number;
- building_id: number;
- uid?: string;
- created_at: number;
- }
-
interface EjyyRepair {
id?: number;
wechat_mp_user_id?: number;
@@ -656,7 +649,6 @@ declare namespace EjyyModel {
vistor_phone: string;
car_number?: string;
have_vistor_info: typeof TRUE | typeof FALSE;
- uid?: string;
expire: number;
used_at?: number;
scan_by?: number;
@@ -1355,7 +1347,6 @@ declare module 'knex/types/tables' {
ejyy_notice_to_user_readed: EjyyModel.EjyyNoticeToUserReaded;
ejyy_notice_tpl: EjyyModel.EjyyNoticeTpl;
ejyy_feedback: EjyyModel.EjyyFeedback;
- ejyy_building_access: EjyyModel.EjyyBuildingAccess;
ejyy_repair: EjyyModel.EjyyRepair;
ejyy_repair_urge: EjyyModel.EjyyRepairUrge;
ejyy_complain: EjyyModel.EjyyComplain;
diff --git a/server/src/utils/open_access.ts b/server/src/utils/access.ts
similarity index 65%
rename from server/src/utils/open_access.ts
rename to server/src/utils/access.ts
index 9c4a037..5489456 100644
--- a/server/src/utils/open_access.ts
+++ b/server/src/utils/access.ts
@@ -10,39 +10,32 @@
* +----------------------------------------------------------------------
*/
-import crypto from 'crypto';
-import config from '~/config';
+import * as crypto from './crypto';
+
import { SELF_ACCESS_QRCODE, VISTOR_ACCESS_QRCODE } from '~/constant/open_access';
-interface DecodeResult {
+interface DecryptResult {
id?: number;
building_id?: number;
type?: typeof SELF_ACCESS_QRCODE | typeof VISTOR_ACCESS_QRCODE;
+ stamp?: number;
success: boolean;
}
// 区分访客二维码和自己的二维码
-export function encode(
+// 访客二维码 id 为 vistor_id 自己的二维码为user_id
+export function encrypt(
id: number,
building_id: number,
type: typeof SELF_ACCESS_QRCODE | typeof VISTOR_ACCESS_QRCODE
): string {
- const cipher = crypto.createCipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
- let crypted = cipher.update(`${id}#${building_id}#${type}`, 'utf8', 'hex');
-
- crypted += cipher.final('hex');
-
- return crypted;
+ return crypto.encrypt(`${id}#${building_id}#${type}${Date.now()}`);
}
-export function decode(uid: string): DecodeResult {
- let origin = null;
+export function decrypt(uid: string): DecryptResult {
+ const origin = crypto.decrypt(uid);
- try {
- const cipher = crypto.createDecipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
- const decrypted = cipher.update(uid, 'hex', 'utf8');
- origin = decrypted + cipher.final('utf8');
- } catch (e) {
+ if (!origin) {
return { success: false };
}
@@ -50,9 +43,15 @@ export function decode(uid: string): DecodeResult {
const id = parseInt(arr[0], 10);
const building_id = parseInt(arr[1], 10);
const type = parseInt(arr[2], 10);
+ const stamp = parseInt(arr[3], 10);
let success = true;
- if ((type !== SELF_ACCESS_QRCODE && type !== VISTOR_ACCESS_QRCODE) || building_id === NaN || id === NaN) {
+ if (
+ (type !== SELF_ACCESS_QRCODE && type !== VISTOR_ACCESS_QRCODE) ||
+ building_id === NaN ||
+ id === NaN ||
+ /^\d{13}$/.test(stamp.toString())
+ ) {
success = false;
}
@@ -60,6 +59,7 @@ export function decode(uid: string): DecodeResult {
id,
building_id,
type,
+ stamp,
success
};
}
diff --git a/server/src/utils/community.ts b/server/src/utils/community.ts
index d668ae1..8143fa0 100644
--- a/server/src/utils/community.ts
+++ b/server/src/utils/community.ts
@@ -10,8 +10,7 @@
* +----------------------------------------------------------------------
*/
-import crypto from 'crypto';
-import config from '~/config';
+import * as crypto from './crypto';
import { AUTHENTICTED_BY_PROPERTY_COMPANY, AUTHENTICTED_BY_FAMILY } from '~/constant/authenticated_type';
interface DecodeResult {
@@ -106,7 +105,7 @@ function fake(len: number): string {
return ret.join('');
}
-export function encode(
+export function encrypt(
building_ids: number[],
authenticated_type: typeof AUTHENTICTED_BY_PROPERTY_COMPANY | typeof AUTHENTICTED_BY_FAMILY,
user_id: number
@@ -120,25 +119,16 @@ export function encode(
ret.push(fake(8));
- const cipher = crypto.createCipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
- let crypted = cipher.update(ret.join(''), 'utf8', 'base64');
-
- crypted += cipher.final('base64');
-
return {
- text: crypted,
+ text: crypto.encrypt(ret.join('')),
stamp
};
}
-export function decode(text: string): DecodeResult {
- let str = null;
+export function decrypt(text: string): DecodeResult {
+ let str = crypto.decrypt(text);
- try {
- const cipher = crypto.createDecipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
- let decrypted = cipher.update(text, 'base64', 'utf8');
- str = decrypted + cipher.final('utf8');
- } catch (e) {
+ if (!str) {
return { success: false };
}
diff --git a/server/src/utils/crypto.ts b/server/src/utils/crypto.ts
index eef80f7..b062c1e 100644
--- a/server/src/utils/crypto.ts
+++ b/server/src/utils/crypto.ts
@@ -11,6 +11,7 @@
*/
import crypto from 'crypto';
+import config from '~/config';
export function md5(str: string, upperCase = false): string {
const hash = crypto.createHash('md5');
@@ -21,3 +22,26 @@ export function md5(str: string, upperCase = false): string {
return upperCase ? ret.toUpperCase() : ret;
}
+
+export function encrypt(str: string): string {
+ const cipher = crypto.createCipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
+ let crypted = cipher.update(str, 'utf8', 'hex');
+
+ crypted += cipher.final('hex');
+
+ return crypted;
+}
+
+export function decrypt(str: string): string {
+ let result = null;
+
+ try {
+ const cipher = crypto.createDecipheriv('aes-256-cbc', config.crypto.key, config.crypto.iv);
+ const decrypted = cipher.update(str, 'hex', 'utf8');
+ result = decrypted + cipher.final('utf8');
+ } catch (e) {
+ return result;
+ }
+
+ return result;
+}
diff --git a/server/src/utils/index.ts b/server/src/utils/index.ts
index f4b7d9e..271cb65 100644
--- a/server/src/utils/index.ts
+++ b/server/src/utils/index.ts
@@ -11,7 +11,7 @@
*/
import * as crypto from './crypto';
-import * as openAccess from './open_access';
+import * as access from './access';
import * as phone from './phone';
import * as community from './community';
import * as building from './building';
@@ -20,10 +20,11 @@ import * as order from './order';
import * as text from './text';
import * as idcard from './idcard';
import * as mail from './mail';
+import * as redis from './redis';
const utils = {
crypto,
- openAccess,
+ access,
phone,
community,
building,
@@ -31,7 +32,8 @@ const utils = {
order,
text,
idcard,
- mail
+ mail,
+ redis
};
export default utils;
diff --git a/server/src/utils/redis.ts b/server/src/utils/redis.ts
new file mode 100644
index 0000000..2a87e61
--- /dev/null
+++ b/server/src/utils/redis.ts
@@ -0,0 +1,58 @@
+/**
+ * +----------------------------------------------------------------------
+ * | 「e家宜业」 —— 助力物业服务升级,用心服务万千业主
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 2020~2021 https://www.chowa.com All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「e家宜业」和「卓瓦科技」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: jixuecong@chowa.cn
+ * +----------------------------------------------------------------------
+ */
+
+import redis from 'redis';
+import config from '~/config';
+
+const client = config.debug ? null : redis.createClient(config.redis);
+
+const ERROR = 'REDIS_ERROR';
+const SUCCESS = 'OK';
+
+export type ERROR_TYPE = typeof ERROR;
+export type SUCCESS_TYPE = typeof SUCCESS;
+
+const storage = {};
+
+export async function set(key: string, value: string): Promise {
+ if (config.debug) {
+ storage[key] = value;
+
+ return SUCCESS;
+ }
+
+ return new Promise(resolve => {
+ client.set(key, value, (err, reply) => {
+ if (err) {
+ resolve(ERROR);
+ } else {
+ resolve(reply);
+ }
+ });
+ });
+}
+
+export async function get(key: string): Promise {
+ if (config.debug) {
+ return storage[key];
+ }
+
+ return new Promise(resolve => {
+ client.get(key, (err, reply) => {
+ if (err) {
+ resolve(ERROR);
+ } else {
+ resolve(reply);
+ }
+ });
+ });
+}
diff --git a/web/src/views/basic/vistor/detail.vue b/web/src/views/basic/vistor/detail.vue
index 1e859d4..8879fd4 100644
--- a/web/src/views/basic/vistor/detail.vue
+++ b/web/src/views/basic/vistor/detail.vue
@@ -151,7 +151,8 @@ export default {
fetching: true,
detail: {
info: {},
- registrant: {}
+ registrant: {},
+ uid: ''
},
now: Date.now()
};
@@ -175,7 +176,7 @@ export default {
this.detail = res.data;
this.$nextTick(() => {
- qrcode.toCanvas(this.$refs.canvas, res.data.info.uid, {
+ qrcode.toCanvas(this.$refs.canvas, res.data.uid, {
width: 220,
height: 220,
margin: 2