项目初始化

parents
.idea
/out
/node_modules
/test
/public
/logs
/video
/files
*test*
*.logs
*.zip
/dist
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"program": "${workspaceFolder}/src/main.ts",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}
\ No newline at end of file
{
"compile-hero.disable-compile-files-on-did-save-code": true
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "screen",
"version": "1.0.0",
"description": "",
"main": "main.ts",
"dependencies": {
"@alicloud/sms-sdk": "^1.1.6",
"@types/node": "^10.12.18",
"bson": "^6.1.0",
"compression": "^1.7.4",
"connect-history-api-fallback": "^2.0.0",
"express": "^4.21.2",
"express-async-handler": "^1.1.4",
"express-history-api-fallback": "^2.2.1",
"formidable": "^1.2.1",
"iconv-lite": "^0.7.0",
"log4js": "^6.6.1",
"lru-cache": "^4.1.5",
"md5": "^2.2.1",
"moment": "^2.24.0",
"mongoose": "^7.6.0",
"mysql": "^2.18.1",
"mysql2": "^3.6.0",
"node-xlsx": "^0.16.1",
"nodemailer": "^6.1.1",
"qs": "^6.11.0",
"request": "^2.88.0",
"sequelize": "^6.32.1",
"ssh2": "^1.17.0",
"svg-captcha": "^1.3.12",
"tencentcloud-sdk-nodejs": "^4.0.562",
"winston": "^3.17.0",
"ws": "^5.2.2",
"xml2js": "^0.4.23",
"xmlrpc": "^1.3.2"
},
"devDependencies": {
"@types/express": "^5.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "cjj",
"license": "ISC",
"bin": "./out/main.js",
"pkg": {
"scripts": "out/**/*.js",
"assets": [
"public/**/*",
"res/**/*",
"images/**/*",
"video/**/*"
],
"outputPath": "dist"
}
}
<config>
<port>13268</port>
<sign></sign>
<img>http://192.168.0.71:13268</img>
<mysqldb>
<!-- 本地mysql配置 -->
<mysqlHost>127.0.0.1</mysqlHost>
<mysqlPort>3306</mysqlPort>
<mysqlUser>root</mysqlUser>
<mysqlPwd>123456</mysqlPwd>
<dataBase>beermuseumplatform</dataBase>
<!-- 服务器mysql配置 -->
<!-- <mysqlHost>127.0.0.1</mysqlHost>
<mysqlPort>3306</mysqlPort>
<mysqlUser>root</mysqlUser>
<mysqlPwd>qaz123456</mysqlPwd>
<dataBase>unicarbon_platform</dataBase> -->
</mysqldb>
</config>
import { selectOneDataByParam, selectDataListByParam, selectPaginatedDataWithOrder } from "../data/findData";
import { TABLENAME } from "../config/dbEnum";
import { getMySqlMs } from "../tools/systemTools";
import { BizError } from "../util/bizError";
import { ERRORENUM } from "../config/errorEnum";
import { addData } from "../data/addData";
import { updateManyData } from "../data/updateData";
/**
* 注册或更新设备信息
* @param deviceId 设备唯一标识(必填)
* @param regionKey 区域编号(必填),对应 region 表的 id
* @param deviceType 设备类型(必填),如“灯光”“空调”
* @param deviceName 设备名称(可选),如“主照明灯”
* @param controlParams 设备支持的参数定义(可选),JSON对象
* @returns {Promise<{success: boolean}>}
* @throws 如果 regionKey 对应的区域不存在,则抛出错误
*/
export async function registerOrUpdateDevice( deviceId: string, regionKey: number, deviceType: string, deviceName?: string | null, controlParams?: any ) {
// 校验区域是否存在
let region = await selectOneDataByParam(TABLENAME.区域表, { id: regionKey });
if (!region.data) {
throw new BizError(ERRORENUM.参数错误, `区域编号 ${regionKey} 不存在`);
}
let exist = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
let now = new Date().toISOString().slice(0, 19).replace('T', ' ');
if (Object.keys(exist.data).length) {
await updateManyData(TABLENAME.设备表, { device_id: deviceId }, {
region_key: regionKey,
device_type: deviceType,
device_name: deviceName,
control_params: controlParams || null,
updated_at: now,
});
} else {
await addData(TABLENAME.设备表, {
device_id: deviceId,
region_key: regionKey,
device_type: deviceType,
device_name: deviceName,
control_params: controlParams || null,
created_at: now,
updated_at: now,
});
}
return { success: true };
}
/**
* 处理设备商推送的数据
* @param deviceId 设备唯一标识(必填)
* @param data 设备上报的JSON数据(必填),例如:{ "power": "on", "brightness": 80 }
* @param deviceTime 设备本地时间(可选),格式:YYYY-MM-DD HH:MM:SS
* @returns {Promise<{recordId: number}>} 返回新插入记录的ID
* @throws 如果设备未在 device 表中注册,则抛出错误(需先调用注册接口)
*/
export async function handleDevicePush(deviceId: string, data: any, deviceTime?: string) {
if (!deviceId || !data) {
throw new BizError(ERRORENUM.参数错误, "deviceId和data不能为空");
}
// 检查设备是否存在,不存在则抛出错误(要求先注册)
let device = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
if (!device.data) {
throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 未注册,请先调用注册接口`);
}
let nowMs = getMySqlMs();
let receivedTime = new Date(nowMs).toISOString().slice(0, 19).replace('T', ' ');
await addData(TABLENAME.设备数据表, {
device_id: deviceId,
data: data,
received_time: receivedTime,
device_time: deviceTime ? new Date(deviceTime).toISOString().slice(0, 19).replace('T', ' ') : null,
created_at: receivedTime,
});
return { success: true };
}
/**
* 处理设备商推送的故障数据
* @param deviceId 设备唯一标识(必填)
* @param faultType 故障类型(必填),如“离线”“数据异常”“硬件故障”
* @param faultDescription 故障详细描述(可选)
* @param occurredTime 故障发生时间(可选),格式:YYYY-MM-DD HH:MM:SS,不传则使用服务器时间
* @returns {Promise<{faultId: number}>} 返回新插入故障记录的ID
* @throws 如果设备未注册,则抛出错误
*/
export async function handleDeviceFaultPush( deviceId: string, faultType: string, faultDescription?: string, occurredTime?: string ) {
if (!deviceId || !faultType) {
throw new BizError(ERRORENUM.参数错误, "deviceId 和 faultType 不能为空");
}
// 检查设备是否存在
let device = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
if (!device.data) {
throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 未注册,请先调用注册接口`);
}
let nowMs = getMySqlMs();
let nowStr = new Date(nowMs).toISOString().slice(0, 19).replace('T', ' ');
let faultTime = occurredTime
? new Date(occurredTime).toISOString().slice(0, 19).replace('T', ' ')
: nowStr;
let insertResult: any = await addData(TABLENAME.设备故障表, {
device_id: deviceId,
fault_type: faultType,
fault_description: faultDescription || null,
occurred_time: faultTime,
status: 0, // 0=未处理
resolved_time: null,
created_at: nowStr,
updated_at: nowStr,
});
return { faultId: insertResult.insertId, success: true };
}
/**
* 智能管控-运行分析
* @param regionName 区域名称(可选),如“A馆1F”,如果提供则返回该区域的分析数据
* @param regionType 区域类型(可选),如“A馆”,如果提供则返回该类型下所有区域的分析数据
* @returns 大屏所需的所有指标数据
*/
export async function getRunAnalysis(regionName: String, regionType: String) {
// 根据区域类型和名称筛选设备ID列表(如果提供了区域信息)
let regionKey = [];
if (regionType) {
let regionInfo: any = await selectDataListByParam(TABLENAME.区域表, {
type: regionType
}, ["id"]);
regionKey = regionInfo.data.map((r: any) => r.id);
}
if (regionName) {
let regionInfo: any = await selectDataListByParam(TABLENAME.区域表, {
name: regionName
}, ["id"]);
regionKey = regionInfo.data.map((r: any) => r.id);
}
console.log("筛选区域Key:", regionKey);
}
/**
* 获取所有区域列表(供前端下拉选择)
* @returns 区域列表,按 sort_order 排序
*/
export async function getRegionList() {
let regionList = await selectDataListByParam(
TABLENAME.区域表, { }
);
let regionMap: any = {};
regionList.data.forEach((r: any) => {
let regionArr = [];
if (regionMap[r.type]) {
regionArr = regionMap[r.type];
}
regionArr.push(r.name);
regionMap[r.type] = regionArr;
});
console.log("区域列表:", regionMap);
return {
list: regionMap
};
}
\ No newline at end of file
import { selectOneDataByParam } from "../data/findData";
import { TABLENAME } from "../config/dbEnum";
import { getMySqlMs } from "../tools/systemTools";
import { BizError } from "../util/bizError";
import { ERRORENUM } from "../config/errorEnum";
import { updateManyData } from "../data/updateData";
import { addData } from "../data/addData";
import { getToken, getPwdMd5 } from "../tools/system";
/**
* 注册
* @param loginId
* @param pwd
*/
export async function registerUser(loginId:string, pwd:string, name:string) {
let filesList = ["loginId", "id", "pwd", "name"];
let adminUserInfo : any = await selectOneDataByParam(TABLENAME.用户信息表, {loginId}, filesList);
if (!adminUserInfo) {
throw new BizError(ERRORENUM.用户已存在);
}
await addData(TABLENAME.用户信息表, {loginId, pwd: getPwdMd5(loginId, pwd), name: name});
return { success: true };
}
/**
* 登录
* @param loginId
* @param pwd
* @returns
*/
export async function adminLogin(loginId:string, pwd:string) {
let filesList = ["name", "id", "pwd", "pwd"];
let adminUserInfo : any = await selectOneDataByParam(TABLENAME.用户信息表, {loginId}, filesList);
console.log(adminUserInfo.pwd);
if (!adminUserInfo || !adminUserInfo.id) {
throw new BizError(ERRORENUM.账号或密码错误);
}
const encryptedPwd = getPwdMd5(adminUserInfo.id, pwd);
if (adminUserInfo.pwd != encryptedPwd) {
throw new BizError(ERRORENUM.账号或密码错误);
}
let updateUserInfo = {
token : getToken(adminUserInfo.loginId),
tokenMs : getMySqlMs()
};
await updateManyData(TABLENAME.用户信息表, updateUserInfo, {id:adminUserInfo.id});
let userInfo = {
userId:adminUserInfo.id,
userName:adminUserInfo.name,
token:updateUserInfo.token,
};
return {dataInfo:userInfo};
}
\ No newline at end of file
/**
* 表名
*/
export enum TABLENAME {
设备表 = 'device',
设备数据表 = 'device_data',
区域表 = 'region',
设备故障表 = 'device_fault',
用户信息表 = 'user_info'
};
/**
* 表ID前缀枚举(用于randomId生成)
*/
export enum TABLEID {
设备表 = 'device',
设备数据表 = 'device_data',
区域表 = 'region',
设备故障表 = 'device_fault',
用户信息表 = 'user_info'
};
/**
* 校验表单参数配置 【平台】
* 使用场景: 验证客户端请求参数
* 限制: 1.使用端不同不能共用一份配置
* 2.需要搭配 util/verificationParam -> eccFormParam() 方法使用
* 说明: notMustHave = true 时说明该字段是非必填的;不配该字段说明是必填的
*/
/**
* 使用端: 管理后台
* 场景: 新增/修改导师配置
* 备注:
*/
export const TeacherConfigAddConfig = {
teacher_id:{type:"String"}, //导师ID
start_time:{type:"Number", notMustHave:true}, //可选学生开始时间
end_time:{type:"Number", notMustHave:true}, //可选学生结束时间
max_students:{type:"Number"}, //可选学生数量上限
gender_limit:{type:"Number"}, //性别限制(1-无,2-只限男生,3-只限女生)
restrict_to_class:{type:"Boolean"} //是否限制只能带本班级学生
};
export enum DIRNAMEENUM {
系统上传 = 1
}
export enum OPERATIONTYPEENUM {
= 1,
,
,
}
/**上传文件类型 */
export enum FILETYPE {
word = 1,
pdf,
图片,
视频,
多类型
}
export enum TYPEENUM {
string = 1,
number,
object,
array,
boolean,
}
export enum ERRORENUM {
不存在表 = 1,
身份验证失败,
缺少必要参数_表名,
数据表不存在,
参数错误,
添加时数据对象为空,
修改时数据对象为空,
该方法仅可进行数据操作,
数据操作失败,
该方法仅可进行查询操作,
分页请设置当前页数,
数据查询失败,
该方法仅可进行联合查询操作,
数据联合查询失败,
INVALID_REQUEST,
INTERNAL_SERVER_ERROR,
文件不存在,
该身份证号码重复,
用户已存在,
账号或密码错误,
请求参数错误,
您的登录已失效,
答题记录不存在,
系统繁忙请稍后重试,
网络连接失败,
服务不可用,
请求超时,
获取用户信息失败,
非法登录,
重复答题,
数据不存在,
未找到数据,
数据已存在,
权限不足,
重复操作,
操作失败,
数据错误,
密码错误,
请完善信息,
地址数据不完整,
操作不允许,
数据被引用,
空文件失败,
文件上传失败,
只能上传docdocxexcelpngjpg图片,
该时间段已有预约
}
/**
* 只用做code码定义
*/
export enum ERRORCODEENUM {
身份验证失败 = 5001,
缺少必要参数_表名 = 5002,
数据表不存在,
}
let bizErrorMsgMap = {};
for (let key in ERRORENUM) {
bizErrorMsgMap[ERRORENUM[key]] = key;
}
export function getBizMsg(param) {
return bizErrorMsgMap[param];
}
\ No newline at end of file
/**
* 表统一管理
*/
export const TablesConfig = {}
export let EccTableConfig = {};
function initEccTableConfig() {
for(let tableChName in TablesConfig) {
let {tableName, schema} = TablesConfig[tableChName];
EccTableConfig[tableName] ={};
for (let filesName in schema) {
let valueType = typeof schema[filesName];
let value = schema[filesName];
EccTableConfig[tableName][filesName] = {type:'', notMustHave:false};
if (valueType == "function") {
EccTableConfig[tableName][filesName].type = value.name;
} else if (valueType == "object") {
if (!value.type) {
EccTableConfig[tableName][filesName].type = `[${value[0].name}]`;
} else {
if (typeof value.type == "function") {
EccTableConfig[tableName][filesName].type = value.type.name
} else EccTableConfig[tableName][filesName].type = `[${value.type[0].name}]`;
if (value.index) EccTableConfig[tableName][filesName].notMustHave = true;
}
}
}
}
console.log('table eccConfig init success');
}
initEccTableConfig();
\ No newline at end of file
const { Sequelize, DataTypes } = require('sequelize');
export const TablesConfig = [
// 区域配置表
{
tableNameCn: '区域配置表',
tableName: 'region',
schema: {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
comment: '区域编号(regionKey)'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '楼层'
},
type: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '所属楼栋'
},
sort_order: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '排序序号'
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '创建时间'
},
updated_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '更新时间'
}
},
association: [
{ type: "hasMany", target: "device", foreignKey: "region_key" }
]
},
// 设备主表(关联区域表)
{
tableNameCn: '设备表',
tableName: 'device',
schema: {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
comment: '自增主键'
},
device_id: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '设备唯一标识(由设备商提供)'
},
region_key: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '区域编号,关联region.id'
},
device_type: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '设备类型,如“空调”“出风口”'
},
device_name: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '设备名称(可选)'
},
control_params: {
type: DataTypes.JSON,
allowNull: true,
comment: '该设备支持的参数定义,JSON格式{"power":["on","off"]}'
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '创建时间'
},
updated_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '更新时间'
}
},
association: [
{ type: "belongsTo", target: "region", foreignKey: "region_key" },
{ type: "hasMany", target: "device_data", foreignKey: "device_id" }
]
},
// 设备数据记录表(存储推送的实时数据)
{
tableNameCn: '设备数据表',
tableName: 'device_data',
schema: {
id: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
comment: '自增主键'
},
device_id: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '设备ID,关联device.device_id'
},
data: {
type: DataTypes.JSON,
allowNull: false,
comment: '推送的数据,如{"power":"on","temperature":24,"mode":"cool","fan_speed":"medium","auto_control":"on"}'
},
received_time: {
type: DataTypes.DATE,
allowNull: false,
comment: '数据接收时间(服务器时间)'
},
device_time: {
type: DataTypes.DATE,
allowNull: true,
comment: '设备上报的时间戳(若有)'
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '记录创建时间'
}
},
association: [
{ type: "belongsTo", target: "device", foreignKey: "device_id" }
]
},
// 设备故障表
{
tableNameCn: '设备故障表',
tableName: 'device_fault',
schema: {
id: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
comment: '自增主键'
},
device_id: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '设备ID,关联device.device_id'
},
fault_type: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '故障类型,如“离线”“数据异常”“硬件故障”'
},
fault_description: {
type: DataTypes.TEXT,
allowNull: true,
comment: '故障详细描述'
},
occurred_time: {
type: DataTypes.DATE,
allowNull: false,
comment: '故障发生时间'
},
status: {
type: DataTypes.TINYINT,
allowNull: false,
defaultValue: 0,
comment: '处理状态:0=未处理,1=处理中,2=已解决'
},
resolved_time: {
type: DataTypes.DATE,
allowNull: true,
comment: '故障解决时间'
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '记录创建时间'
},
updated_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '记录更新时间'
}
},
association: [
{ type: "belongsTo", target: "device", foreignKey: "device_id" }
]
},
// 人员信息表
{
tableNameCn: '用户信息表',
tableName: 'user_info',
schema: {
id: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
comment: '自增主键'
},
loginId: {
type: Sequelize.STRING(255),
allowNull: false,
Comment: '账户,唯一'
},
pwd: {
type: Sequelize.STRING(255),
Comment: '密码'
},
token: {
type: Sequelize.STRING(255),
Comment: 'token'
},
tokenMs: {
type: Sequelize.DATE,
Comment: 'token有效期'
},
name: {
type: Sequelize.STRING(255),
Comment: '用户名称'
}
}
}
];
const path = require('path');
import * as fs from "fs";
import { BizError } from "../util/bizError";
import { analysisXml } from "../util/myXML";
import { ServerConfig } from "../config/systemClass";
export let systemConfig = new ServerConfig();
const ConfigName = "serverConfig.xml";
export async function initConfig() {
try {
let buff = fs.readFileSync(path.join(__dirname.substring(0, __dirname.indexOf("out")), ConfigName));
let configStr = buff.toString();
let configInfo: any = await analysisXml(configStr);
if (!configInfo || !configInfo.config) throw new BizError('xml中无配置');
let { port, sign, img, mysqldb } = configInfo.config;
// 基本配置
systemConfig.port = parseInt(port[0]);
systemConfig.sign = sign[0];
systemConfig.img = img[0];
// MySQL配置
if (mysqldb) {
let dbConfigInfo = mysqldb[0];
systemConfig.mysqldb = { host: '', port: 0, user: '', pwd: '', dataBase: '' };
if (dbConfigInfo.mysqlHost && dbConfigInfo.mysqlPort && dbConfigInfo.mysqlUser && dbConfigInfo.dataBase) {
systemConfig.mysqldb.host = dbConfigInfo.mysqlHost[0];
systemConfig.mysqldb.port = parseInt(dbConfigInfo.mysqlPort[0]);
systemConfig.mysqldb.user = dbConfigInfo.mysqlUser[0];
systemConfig.mysqldb.pwd = dbConfigInfo.mysqlPwd[0] || "";
systemConfig.mysqldb.dataBase = dbConfigInfo.dataBase[0];
}
}
} catch(err) {
console.log('ERROR => 服务器配置解析错误 请检查根目录下 serverConfig.xml 文件是否正确');
console.log(err);
throw new BizError("服务器配置解析错误 请检查根目录下 serverConfig.xml 文件是否正确");
}
}
/**
* 系统配置类
*
*/
export class ServerConfig {
/**系统配置 */
port:number;
sign:string;
img:string;
mysqldb:{
host:string,
port:number,
user:string,
pwd:string,
dataBase:string,
}
}
\ No newline at end of file
import { mysqlModelMap } from "../model/sqlModelBind";
/**
* 添加数据
* @param tableModel
* @param data
* @returns
*/
export async function addData(tableName:string, data:any) {
let tableModel = mysqlModelMap[tableName];
let dataArray = [];
if (!Array.isArray(data)) {
dataArray.push(data);
} else dataArray = data;
await tableModel.bulkCreate(dataArray);
return { isSuccess:true };
}
\ No newline at end of file
import { mysqlModelMap } from "../model/sqlModelBind";
export async function delData(tableName, param) {
let tableModel = mysqlModelMap[tableName];
await tableModel.destroy({where:param});
return {isSuccess:true};
}
\ No newline at end of file
import { Op, Sequelize } from "sequelize";
import { mysqlModelMap } from "../model/sqlModelBind";
/**
* where条件查询参数
* @param param
* %like%:模糊查询 {列名: {"%like%": }}
* %gt%:大于 {列名: {"%gt%": }}
* %gte%:大于等于 {列名: {"%gte%": }}
* %lt%:小于 {列名: {"%lt%": }}
* %lte%:小于等于 {列名: {"%lte%": }}
* %between%:查询范围内数据 {列名: {"%between%": ["开始参数", "结束参数"]}} ---BETWEEN 开始参数 AND 结束参数
* %notBetween%:查询不在范围内数据 {列名: {"%notBetween%": ["开始参数", "结束参数"]}} ---NOT BETWEEN 开始参数 AND 结束参数
* %orderDesc%: order by DESC {"%orderDesc%": "列名"}
* %limit%: {"%limit%": 数量}
* @returns
*/
function analysisParamToWhere(param) {
let where = {};
let order = [];
let limit = 0;
for (let key in param) {
if (typeof param[key] == "object") {
where[key] = {};
for (let whereKey in param[key]){
switch(whereKey) {
case "%like%":
where[key][Op.like] = `%${param[key]["%like%"]}%`;
break;
case "%gt%":
where[key][Op.gt] = param[key]["%gt%"];
break;
case "%gte%":
where[key][Op.gte] = param[key]["%gte%"];
break;
case "%lt%":
where[key][Op.lt] = param[key]["%lt%"];
break;
case "%lte%":
where[key][Op.lte] = param[key]["%lte%"];
break;
case "%between%":
where[key][Op.between] = param[key]["%between%"];
break;
case "%notBetween%":
where[key][Op.notBetween] = param[key]["%notBetween%"];
break;
case "%in%":
where[key][Op.in] = param[key]["%in%"];
break;
case "%notIn%":
where[key][Op.notIn] = param[key]["%notIn%"];
break;
}
}
}else {
switch (key) {
case "%orderDesc%":
order = [[Sequelize.col(param[key]), "DESC"]];
break;
case "%orderAsc%":
order = [[Sequelize.col(param[key]), "ASC"]];
break;
case "%limit%":
limit = param[key];
break;
default: where[key] = param[key];
}
}
}
let selectParam:any = {where};
if (order && order.length) selectParam.order = order;
if (limit) selectParam.limit = limit;
return selectParam;
}
export async function updateManyData(tableName, param:object, data:object) {
let tableModel = mysqlModelMap[tableName];
let where = analysisParamToWhere(param);
await tableModel.update(data, where);
return {isSuccess:true};
}
\ No newline at end of file
import { systemConfig } from "../config/serverConfig";
//导入sequelize模块
const Sequelize = require('sequelize');
var mysqlDB;
export async function initMysqlDB() {
mysqlDB = new Sequelize(systemConfig.mysqldb.dataBase,systemConfig.mysqldb.user,systemConfig.mysqldb.pwd,{
host:systemConfig.mysqldb.host,
port:systemConfig.mysqldb.port,
dialect:'mysql', //数据库类型
pool:{ //数据库连接池
max:20, //最大连接对象的个数
min:5, //最小连接对象的个数
idle:1000 //最长等待时间,单位为毫秒
},
timezone: '+08:00', //东八时区
dialectOptions: {
dateStrings: true,
typeCast: true
},
});
}
export { mysqlDB };
\ No newline at end of file
import { initConfig, systemConfig} from "./config/serverConfig";
import * as mysqlDB from "./db/mysqlInit";
import { initMysqlModel } from "./model/sqlModelBind";
import { httpServer } from "./net/http_server";
async function lanuch() {
/**初始化配置解析 */
await initConfig();
/**初始化sql */
await mysqlDB.initMysqlDB();
await initMysqlModel();
/**创建http服务 */
httpServer.createServer(systemConfig.port);
console.log('This indicates that the server is started successfully.');
}
lanuch();
import { DIRNAMEENUM } from "../config/enum";
import { ERRORENUM } from "../config/errorEnum";
import { BizError } from "../util/bizError";
var formidable = require("formidable");
const path = require('path');
export async function parseFormParam(req, res, next) {
let upType = DIRNAMEENUM.系统上传;
var form = new formidable.IncomingForm({
uploadDir: path.join(__dirname.substring(0,__dirname.indexOf("out")),'files', `${upType}`),
maxFildsSize: 10*1024*1024,
keepExtensions: true
});
form.parse(req, (err, fields, files)=>{
if (err) {
return next(err);
} else if (!files || !Object.keys(files).length) {
return next(new BizError(ERRORENUM.空文件失败));
}
else {
req.fields = fields;
req.files = {};
if (!files.formData && !files.file ) return next(new BizError(ERRORENUM.文件上传失败) );
if (files.formData) {
req.files.formData = files.formData;
req.files.formData.upType = upType;
}
else {
req.files.formData = files.file;
req.files.formData.upType = upType;
}
if (!req.files.formData || !req.files.formData.name) {
return next(new BizError(ERRORENUM.文件上传失败) );
}
console.log(req.files.formData.type);
//获取文件扩展名
const fileExtension = req.files.formData.name.split('.').pop().toLowerCase();
if ( req.files.formData.type == 'image/png') {
req.fileType = '.png';
return next();
} else if (req.files.formData.type == 'image/jpg' || req.files.formData.type == 'image/jpeg') {
req.fileType = '.jpg';
return next();
} else if (req.files.formData.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || fileExtension == 'docx') {
req.fileType = '.docx';
return next();
} else if (req.files.formData.type == "application/msword") {
req.fileType = '.doc';
return next();
} else if (req.files.formData.type == "application/pdf") {
req.fileType = '.pdf';
return next();
} else if (req.files.formData.type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"){
req.fileType = '.xlsx';
return next();
} else if (req.files.formData.type == "application/vnd.ms-excel"){
req.fileType = '.xls';
return next();
} else {
return next(new BizError(ERRORENUM.只能上传docdocxexcelpngjpg图片) )
}
}
})
}
import { bizlive } from "tencentcloud-sdk-nodejs";
import { ERRORCODEENUM } from "../config/errorEnum";
/**
* 中间件 错误返回
* @param err
* @param req
* @param res
* @param next
*/
export function httpErrorHandler(err, req, res, next) {
console.log("in httpErrorHandler");
console.log(err);
//todo 自定义错误编码
if (err) {
if ( ERRORCODEENUM[err.message] ) {
res.success({success:false, msg:err.message, code:ERRORCODEENUM[err.message]});
next();
}
else {
res.success({success:false, msg:err.message, code:500});
next();
}
}
}
\ No newline at end of file
import { ERRORENUM } from "../config/errorEnum";
import { systemConfig } from "../config/serverConfig";
import { EccTableConfig } from "../config/mongoTableConfig";
import { BizError } from "../util/bizError";
import { mysqlModelMap } from "../model/sqlModelBind";
import { selectOneDataByParam } from "../data/findData";
import { TABLENAME } from "../config/dbEnum";
/**
* 中间件 校验连接对象token
* @param req
* @param res
* @param next
* @returns
*/
export async function checkMongoSign(req, res, next) {
if (!req.headers) req.headers = {};
let sign = req.headers.sign;
let table = req.headers.table;
if (sign != systemConfig.sign) return next( new BizError(ERRORENUM.身份验证失败, `传入的sign值为:${sign}`) );
if (!table) return next( new BizError(ERRORENUM.缺少必要参数_表名, `传入的table值为:${table}`) );
if (!EccTableConfig[table]) return next( new BizError(ERRORENUM.不存在表, `传入的table值为:${table}`) );
next();
}
/**
* 中间件 校验连接对象token
* @param req
* @param res
* @param next
* @returns
*/
export async function checkMySqlSign(req, res, next) {
if (!req.headers) req.headers = {};
let sign = req.headers.sign;
let table = req.headers.table;
if (sign != systemConfig.sign) return next( new BizError(ERRORENUM.身份验证失败, `传入的sign值为:${sign}`) );
if (!table) return next( new BizError(ERRORENUM.缺少必要参数_表名, `传入的table值为:${table}`) );
if (!mysqlModelMap[table]) return next( new BizError(ERRORENUM.不存在表, `传入的table值为:${table}`) );
req.tableModel = mysqlModelMap[table];
next();
}
/**
* 中间件 校验用户登录token
* @param req
* @param res
* @param next
* @returns
*/
export async function checkUser(req, res, next) {
if (!req.headers) req.headers = {};
const userId = req.headers.userid || "";
const reqToken = req.headers.token || "";
const roleToken = req.headers.roletoken || "";
if (!userId) return next(new BizError(ERRORENUM.身份验证失败, `userId:${userId} token:${reqToken}`));
let userProfileData: any = await selectOneDataByParam(TABLENAME.用户信息表, {
user_id: userId,
token: reqToken
}, ["user_id", "roles", "current_role", "role_token", "permission", "token", "token_ms"]);
if (!userProfileData || !userProfileData.data || !userProfileData.data.user_id) {
return next(new BizError(ERRORENUM.非法登录, `userId:${userId} token:${reqToken}`));
}
if (userProfileData.data.token != reqToken) {
return next(new BizError(ERRORENUM.身份验证失败, `userId:${userId}`));
}
// 如果有roleToken,验证角色token
if (roleToken && userProfileData.data.role_token != roleToken) {
return next(new BizError(ERRORENUM.身份验证失败, `角色token无效`));
}
req.userInfo = {
userId: userId,
userName: req.headers.username || "",
token: reqToken,
roleToken: roleToken,
roles: userProfileData.data.roles || "",
currentRole: userProfileData.data.current_role || null,
permission: userProfileData.data.permission || 0
};
next();
}
/**
* 中间件 校验管理员权限(简化版)
*/
export async function checkAdmin(req, res, next) {
try {
const userInfo = req.userInfo;
if (!userInfo || !userInfo.userId) {
return next(new BizError(ERRORENUM.身份验证失败, '用户未登录'));
}
// 检查permission字段
if (userInfo.permission !== 1) {
return next(new BizError(ERRORENUM.权限不足, '需要管理员权限'));
}
next();
} catch (error) {
next(new BizError(ERRORENUM.系统繁忙请稍后重试, error.message));
}
}
export function watch(req, res, next) {
res.success = success.bind({res:res, req:req});
return next();
}
/**
* 中间件正确返回方法
* @param data
*/
function success(data) {
let resultPack;
if (data ) {
if ( data.success === undefined || data.success === true ) {
resultPack = {data, success:true, code:200};
}
else {
resultPack = data;
}
}else {
resultPack = {code:500, success:false, msg:'result is null'};
}
this.res.send(resultPack);
}
/**
* mysql 数据层
*/
import { TablesConfig } from "../config/mysqlTableConfig";
import { mysqlDB } from "../db/mysqlInit";
let mysqlModelMap = {};
export async function initMysqlModel() {
console.log('🚀 开始初始化 MySQL 模型...');
// 检查 TablesConfig
if (!TablesConfig || !Array.isArray(TablesConfig)) {
console.error('❌ TablesConfig 未定义或不是数组');
return;
}
if (TablesConfig.length === 0) {
console.warn('⚠️ TablesConfig 是空数组');
return;
}
console.log(`📊 共发现 ${TablesConfig.length} 个表配置`);
/**第一步:初始化所有表 */
for (let i = 0; i < TablesConfig.length; i++) {
let { tableName, schema } = TablesConfig[i];
if (!tableName) {
console.warn(`⚠️ 第 ${i} 个表配置缺少 tableName,跳过`);
continue;
}
if (!schema) {
console.warn(`⚠️ 表 ${tableName} 缺少 schema 配置,跳过`);
continue;
}
console.log(`🔄 正在初始化表: ${tableName}`);
let schemaConf = {
freezeTableName: true,
timestamps: false
};
try {
let model = mysqlDB.define(tableName, schema, schemaConf);
mysqlModelMap[tableName] = await model.sync({});
console.log(`✅ 表 ${tableName} 初始化成功`);
} catch (error) {
console.error(`❌ 表 ${tableName} 初始化失败:`, error);
}
}
console.log(`📊 表初始化完成,成功初始化 ${Object.keys(mysqlModelMap).length} 个表`);
/**第二步:初始化表关联 */
for (let i = 0; i < TablesConfig.length; i++) {
let { tableName, association } = TablesConfig[i];
// 检查表是否初始化成功
if (!mysqlModelMap[tableName]) {
console.warn(`⚠️ 表 ${tableName} 未成功初始化,跳过关联配置`);
continue;
}
// 检查关联配置
if (!association || !Array.isArray(association) || association.length === 0) {
console.log(`ℹ️ 表 ${tableName} 没有关联配置,跳过`);
continue;
}
console.log(`🔄 正在初始化表 ${tableName} 的关联关系,共 ${association.length} 个关联`);
association.forEach((item: any, index: number) => {
if (!item) {
console.warn(`⚠️ 表 ${tableName} 的第 ${index} 个关联配置为空,跳过`);
return;
}
let { type, check, foreignKey } = item;
if (!type || !check) {
console.warn(`⚠️ 表 ${tableName} 的关联配置缺少 type 或 check 字段`, item);
return;
}
// 检查关联表是否初始化成功
if (!mysqlModelMap[check]) {
console.warn(`⚠️ 关联表 ${check} 未初始化,跳过关联`);
return;
}
try {
console.log(` 处理关联: ${tableName} -> ${check}, 类型: ${type}`);
if (type == "hasOne") {
mysqlModelMap[check].hasOne(mysqlModelMap[tableName]);
} else if (type == "hasMany") {
mysqlModelMap[tableName].hasMany(mysqlModelMap[check], { foreignKey });
}
mysqlModelMap[check].belongsTo(mysqlModelMap[tableName], { foreignKey });
console.log(` ✅ 关联成功: ${tableName} <-> ${check}`);
} catch (error) {
console.error(` ❌ 关联失败: ${tableName} -> ${check}`, error);
}
});
}
console.log('🎉 MySQL 模型初始化完成');
console.log('📋 已初始化的表:', Object.keys(mysqlModelMap).join(', '));
}
export { mysqlModelMap };
\ No newline at end of file
import express = require('express');
import bodyParser = require('body-parser');
import routers = require('../routers/router');
import compression = require('compression');
import { watch } from '../middleware/watch';
import { httpErrorHandler } from '../middleware/httpErrorHandler';
import * as path from "path";
import fallback from 'express-history-api-fallback';
import historyFallback from 'express-history-api-fallback'; // 正确导入默认导出的函数
export class httpServer {
static createServer(port: number) {
var httpServer = express();
httpServer.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type,request-origin,userid,token,username');
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header('Access-Control-Allow-Credentials', 'true');
res.header("X-Powered-By", ' 3.2.1');
next();
});
httpServer.use(express.static('public'));
httpServer.use(express.static('img'));
httpServer.use(compression());
httpServer.use(watch);
httpServer.use(bodyParser.json({ limit: "500kb" }));
routers.setRouter(httpServer);
httpServer.use(httpErrorHandler);
const root = path.join(__dirname, "../../public");
httpServer.use(express.static(root))
// 使用 fallback 函数
httpServer.use(fallback('index.html', { root }));
httpServer.use(historyFallback('index.html', { root }));
console.log('web listen on port:' + port);
httpServer.listen(port);
console.log('server listen on port:' + port);
return httpServer;
}
}
\ No newline at end of file
/**
* 零碳展厅设备商数据推送接口(无需登录)
*/
import asyncHandler from 'express-async-handler';
import * as deviceBiz from '../biz/device';
import * as regionBiz from '../biz/region';
import { eccReqParamater } from '../util/verificationParam';
import { checkUser } from '../middleware/user';
export function setRouter(httpServer) {
/** 注册/更新设备信息 */
httpServer.post('/api/zc/device/register', asyncHandler(registerDevice));
httpServer.post('/api/zc/region/regionList', asyncHandler(getRegionList));
/** 设备商推送实时数据 */
httpServer.post('/api/zc/device/push', asyncHandler(deviceDataPush));
/** 设备商推送故障数据 */
httpServer.post('/api/zc/device/fault/push', asyncHandler(deviceFaultPush));
/** 运行分析 */
httpServer.post('/api/zc/run/analysis', asyncHandler(getRunAnalysis));
/**运行分析-弹窗 */
httpServer.post('/api/zc/run/analysis/pop', asyncHandler(getRunAnalysisPop));
/** 智能监控弹窗 */
httpServer.post('/api/zc/smart/monitor/pop', asyncHandler(getSmartMonitorPop));
}
/**
* 注册或更新设备信息
*/
async function registerDevice(req, res) {
let reqConf = {deviceId:'String', regionKey:'Number', deviceType:'String', deviceName:'String', controlParams:'Object'};
const NotMustHaveKeys = [];
let { deviceId, regionKey, deviceType, deviceName, controlParams } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.registerOrUpdateDevice(deviceId, regionKey, deviceType, deviceName, controlParams);
res.success(result);
}
async function getRegionList(req, res) {
const result = await regionBiz.getRegionList();
res.success(result);
}
/**
* 设备商推送数据
*/
async function deviceDataPush(req, res) {
let reqConf = {deviceId:'String', data:'Object', deviceTime:'String'};
const NotMustHaveKeys = [];
let { deviceId, data, deviceTime } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.handleDevicePush(deviceId, data, deviceTime);
res.success(result);
}
/**
* 设备商推送故障数据
*/
async function deviceFaultPush(req, res) {
let reqConf = {deviceId:'String', faultType:'String', faultDescription:'String', occurredTime:'String'};
const NotMustHaveKeys = ["faultDescription"];
let { deviceId, faultType, faultDescription, occurredTime } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.handleDeviceFaultPush(deviceId, faultType, faultDescription, occurredTime);
res.success(result);
}
/**
* 运行分析
*/
async function getRunAnalysis(req, res) {
const { } = req.body;
const result = await deviceBiz.getRunAnalysis();
res.success(result);
}
/**
* 运行分析-楼栋
*/
async function getRunAnalysisByBuilding(req, res) {
let reqConf = {regionType:'String'};
const NotMustHaveKeys = [];
let { type } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.getRunAnalysis("", type);
res.success(result);
}
/**
* 运行分析-楼层
*/
async function getRunAnalysisByFloor(req, res) {
let reqConf = {regionName:'String'};
const NotMustHaveKeys = [];
let { name } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.getRunAnalysis(name, "");
res.success(result);
}
/**
* 运行分析-弹窗
*/
async function getRunAnalysisPop(req, res) {
let reqConf = {regionKey:'Number'};
const NotMustHaveKeys = [];
let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.getRunAnalysisPop(regionKey);
res.success(result);
}
/**
* 智能监控弹窗
*/
async function getSmartMonitorPop(req, res) {
let reqConf = {regionKey:'Number'};
const NotMustHaveKeys = [];
let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.getSmartMonitorPop(regionKey);
res.success(result);
}
/**
* 总路由入口
*/
import * as deviceRouter from './device';
export function setRouter(httpServer) {
deviceRouter.setRouter(httpServer);
}
/**
* 青岛博物馆用户注册登录接口
*/
import asyncHandler from 'express-async-handler';
import * as usersBiz from '../biz/users';
import { eccReqParamater } from '../util/verificationParam';
export function setRouter(httpServer) {
/** 用户注册 */
httpServer.post('/api/zc/user/register', asyncHandler(registerUser));
/** 用户登录 */
httpServer.post('/api/zc/user/login', asyncHandler(login));
/**
* 注册信息
*/
async function registerUser(req, res) {
let reqConf = {username:'String', password:'String', name:'String'};
const NotMustHaveKeys = [];
let { username, password, name } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await usersBiz.registerUser(username, password, name);
res.success(result);
}
/**
* 登录信息
*/
async function login(req, res) {
let reqConf = {username:'String', password:'String'};
const NotMustHaveKeys = [];
let { username, password } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await usersBiz.adminLogin(username, password);
res.success(result);
}
}
\ No newline at end of file
import { ERRORENUM } from "../config/errorEnum";
import { BizError } from "../util/bizError";
/**
* 校验value是否符合传入的枚举
* @param name 被掉用名称 用于输出异常日志
* @param key 目标字段 用于输出异常日志
* @param enumConf 目标枚举
* @param value 目标值
* 无返回 有异常直接报错
*/
export function eccEnumValue(name:string, key:string, enumConf, value:any) {
let eccSuccess = true;
if ( typeof value == 'number' ) {
if (!enumConf[value] ) eccSuccess = false;
} else if (Array.isArray(value)) {
value.forEach(item => {
if ( !enumConf[item] ) eccSuccess = false;
});
}
if (!eccSuccess) throw new BizError(ERRORENUM.文件不存在, `${name} 下的 ${key} 字段值为 ${value} 不满足枚举范围`);
}
/**
* 将枚举值转换成对应的枚举名(key)
* @param enumConf 目标枚举
* @param value 目标值
* @returns string 返回字符串 如果传入多个枚举值,就拼接字符串
*/
export function changeEnumValue(enumConf, value:any) {
if (!value) return '';
if ( typeof value == 'number' ) {
let str = enumConf[value];
/**特化处理 */
if(/_dou/.test(str)) str = str.replace(/_dou/gm, ",");
if(/_zyh/.test(str)) str = str.replace(/_zyh/gm, "“");
if(/_yyh/.test(str)) str = str.replace(/_yyh/gm, "”");
if(/_dun/.test(str)) str = str.replace(/_dun/gm, "、");
if(/_ju/.test(str)) str = str.replace(/_ju/gm, "。");
return str
} else if (typeof value == 'string') {
try {//兼容数据库 '[1,2,3]'
value = JSON.parse(value);
}catch(err) {
return enumConf[parseInt(value)];
}
}
let str = "";
value.forEach((item, index) => {
let subStr = enumConf[item];
/**特化处理 */
if(/_dou/.test(subStr)) subStr = subStr.replace(/_dou/gm, ",");
if(/_zyh/.test(subStr)) subStr = subStr.replace(/_zyh/gm, "“");
if(/_yyh/.test(subStr)) subStr = subStr.replace(/_yyh/gm, "”");
if(/_dun/.test(subStr)) subStr = subStr.replace(/_dun/gm, "、");
if(/_ju/.test(subStr)) subStr = subStr.replace(/_ju/gm, "。");
str += subStr;
if (index == value.length-1) str+="";
else str += ","
});
return str;
}
\ No newline at end of file
import { ERRORENUM } from "../config/errorEnum";
import { BizError } from "../util/bizError";
import { TYPEENUM } from "../config/enum";
/**
* 根据conf配置校验请求参数
* @param conf 配置
* @param param 表单
* @param skipKeys []不必填的字段
*/
export function eccReqParamater(conf:object, param, skipKeys?) {
skipKeys = skipKeys || [];
let skipMap = {};
skipKeys.forEach(keyName => {
skipMap[keyName] = 1;
});
/**校验多余字段 */
for (let key in param) {
if (!conf[key]) throw new BizError(ERRORENUM.参数错误, `多余${key}字段`);
}
/**校验必填和缺失字段 */
for (let key in conf) {
let confType = conf[key];
let value = param[key];
let valueType = typeof value;
if ( value == null || value == undefined ) {
if (!skipMap[key]) throw new BizError(ERRORENUM.参数错误, `缺失${key}字段`);
} else {
let isError = false;
let errorStr = "";
switch(confType) {
case 'Number':
if ( confType.toLowerCase() != valueType ) isError = true;
else {
if ((""+param[key]).indexOf('.') > -1) {
param[key] = parseInt(`${param[key] *100}`)/100;
}
}
break;
case 'String':
case 'Boolean':
case 'Object':
if ( confType.toLowerCase() != valueType ) isError = true;
break;
case '[Number]':
if ( !Array.isArray(param[key]) ) isError = true;
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'number' ) {
isError = true;
errorStr = `${key}应是number型数组其中下标${i}${typeof item}`;
}
}
break;
case '[Object]':
if ( !Array.isArray(param[key]) ) isError = true;
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'object' ) {
isError = true;
errorStr = `${key}应是object型数组其中下标${i}${typeof item}`;
}
}
break;
case '[String]':
if ( !Array.isArray(param[key]) ) isError = true;
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'string' ) {
isError = true;
errorStr = `${key}应是String型数组其中下标${i}${typeof item}`;
}
}
break;
// case 'Address':
// /**地址类型 基本数据类型为数组字符串但是要判断层级关系 */
// if ( !Array.isArray(param[key]) ) {
// isError = true;
// errorStr = `${key}应是数组形`;
// }
// if ( param[key].length != 4) {
// isError = true;
// errorStr = `${key}超过特定长度4 目前长度 ${param[key].length}`;
// }
// for (let i =0; i < param[key].length; i++) {
// let item = param[key][i];
// if ( typeof item != 'string' ) {
// isError = true;
// errorStr = `${key}应是string型数组其中下标${i}是${typeof item}`;
// }
// }
// /** 不符合规则的 */
// let nullIndex = -1;
// for (let i = 0; i < param[key].length; i++) {
// if (nullIndex != -1) {//出现过空 第一次出现后的位置 都不能有值
// if (param[key]) {
// //做一个特化
// throw new BizError(ERRORENUM.地址数据不完整, `${key} 下标 ${nullIndex} 为空 `);
// }
// }
// if (nullIndex == -1 && !param[key][i]) {
// /**按顺序第一次赋值 */
// nullIndex = i;
// }
// }
// break;
}
errorStr = isError && errorStr == "" ? `${key}应该是${confType}型 而不是${valueType}`: errorStr;
if (isError) throw new BizError(ERRORENUM.参数错误, errorStr);
}
}
return param;
}
//对象判空
export function objectKeyIsNull(obj, ...keyNames) {
let isNull = false;
for (let i = 0; i < keyNames.length; i++) {
let keyStr = keyNames[i];
let moreKeyList = keyStr.split(".");
let lastObj;
for (let j = 0; j < moreKeyList.length; j++) {
lastObj = obj[moreKeyList[j]];
if (!lastObj) {
isNull = true;
break;
}
}
if (isNull) break;
}
return isNull;
}
/**
* 校验类型
* @param target 目标值
* @param type TYPEENUM枚举值
* @returns 通过 = true 不通过 = false
*/
export function checkType(target, type) {
if (target == undefined || target == null) return false;
switch (type) {
case TYPEENUM.string:
if (typeof target == 'string') {
return true;
}
break;
case TYPEENUM.number:
if (typeof target == 'number') {
return true;
}
break;
case TYPEENUM.object:
if (typeof target == 'object' && !Array.isArray(target) ) {
return true;
}
break;
case TYPEENUM.array:
if (typeof target == 'object' && Array.isArray(target) ) {
return true;
}
break;
case TYPEENUM.boolean:
if (typeof target == 'boolean') {
return true;
}
break;
};
return false;
}
export function checkStrLeng(str, length?) {
length = length ? length : 40;
let result = true;
if (str.length < length) result = false;
return result;
}
import moment = require("moment");
import { ERRORENUM } from "../config/errorEnum";
import { BizError } from "../util/bizError";
import { FILETYPE } from "../config/enum";
const md5 = require("md5");
export function randomId(tableName:string) {
let randomStr = `${new Date().valueOf()}_${Math.ceil(Math.random()*100000)}`;
return `${tableName}_${md5(randomStr)}`;
}
export function getUserToken(loginId:string) {
return md5(`${loginId}_${Math.ceil(Math.random()*1000)}${new Date().valueOf()}`);
}
/**
* 密码加密
* @param uscc 信用代码
* @param pwd 密码
* @returns md5后的密码
*/
export function getPwdMd5(uscc:string, pwd:string) {
return md5(uscc+pwd);
}
export function getMySqlMs(time?) {
time = time || new Date().valueOf();
// time += (8*3600*1000);
return moment(time).format("YYYY-MM-DD HH:mm:ss");
}
export function getClientMs(time) {
if (!time) return new Date().valueOf();
return new Date(time).valueOf();
}
export function getPartyMemberId(param) {
return md5(`${param}-${new Date().valueOf()}-${Math.ceil(Math.random() * 10000)}`);
}
export function getDefPwd(phone:string) {
return md5(`${phone.slice(5, 11)}`);
}
export function getFileType(fileName) {
let fileType = 0;
fileName.forEach(info => {
let repList = info.split(".");
let type = repList[repList.length-1];
if (!type) throw new BizError(ERRORENUM.文件不存在, `文件名 ${info}`);
let typeNum = 0;
switch(type) {
case 'pdf': typeNum = FILETYPE.pdf; break;
case 'doc':
case 'docx': typeNum = FILETYPE.word; break;
case 'jpg':
case 'png': typeNum = FILETYPE.图片; break;
};
if (typeNum) {
if (!fileType) fileType = typeNum;
else if (fileType != typeNum) fileType = FILETYPE.多类型;
}
});
return fileType;
}
const xlsx = require('node-xlsx');
const path = require('path');
/**
* onceSheetBecomeOfblockData 将excel文件的指定sheet解析成数据块数据
* @param fileName 文件名称
* @param sheetName 表名称
* @returns [ {blockData:数据块(二维数组), blockTitle:"数据标题"}]
*/
export function onceSheetBecomeOfblockData(fileName, sheetName) {
let {sheetMap} = getExcel( path.join(__dirname.substring(0,__dirname.indexOf("out")), "res", fileName ));
// return sheetMap;
let thisBlockData = getBlockData(sheetMap[sheetName]);
return thisBlockData;
}
/**
* excelBecomeOfBlockData 将excel所有的sheet解析成数据块
* @param fileName 文件名称
* @returns {"sheetName1":[ {blockData:数据块(二维数组), blockTitle:"数据标题"}], ...}
*/
export function excelBecomeOfBlockData(fileName) {
let {sheetMap} = getExcel( path.join(__dirname.substring(0,__dirname.indexOf("out")), "res", fileName ));
let result = {};
for (let sheetName in sheetMap) {
result[sheetName] = getBlockData(sheetMap[sheetName]);
}
return result;
}
/**
* planaryArrayBecomeOfBlockData 将符合excel规则的sheet二维数组转为 数据块
* @param dataList excel解出来的数据
* @returns thisBlockData 返回数据块集合 格式:blockList = [ {blockData:数据块(二维数组), blockTitle:"数据标题"}]
*/
export function planaryArrayBecomeOfBlockData(planaryArray) {
return getBlockData(planaryArray);;
}
//===
/**
* getBlockData 数据分块
* @param dataList 解析出来的excel二维数组
* @returns 返回数据块集合 格式:blockList = [ {blockData:数据块(二维数组), blockTitle:"数据标题"}]
*/
function getBlockData(dataList) {
let blockList = [];
for (let i = 0; i < 999; i++) {
let {blockData, blockTitle, notItem, delDataList} = checkBlock(dataList);
if (notItem) break;
dataList = delDataList;
if (blockTitle) blockList.push({blockData, blockTitle});
}
return blockList;
}
function getListFristNotNullItemIndex(list) { //获取起始坐标
if (!list.length) return null;
for (let i = 0; i < list.length; i++) {
if (list[i]) return i;
}
}
function getListFirstNullItemIndex(startX, list) { //获取第一个为空的坐标
if (!list.length) return null;
let checkItem = false;
let firstItemIndex = 0;
for (let i = startX; i <= list.length; i++) {
let item = list[i];
if (!checkItem && item) checkItem = true;
if (checkItem && !item) {
firstItemIndex = i;
break;
}
}
return firstItemIndex;
}
function listRegionIsNull(list, startX, endX) { //指定区间内数据是否未空
let isNull = true;
if ( !list.length ) return isNull;
for (let i = startX; i < endX; i++) {
let item = list[i];
if (item) {
isNull = false;
break;
}
}
return isNull;
}
function thisListNotItem(list) {
for (let i = 0; i < list.length; i++) {
if (list[i]) return false;
}
return true
}
function checkBlock(dataList) {
//纵向有效起始点
let startY = 0;
let startX = 0;
let isNotBlockTitle = false; //没有块标题
let isLook = false;
let endX = 0;//x轴最长结束下标 【包括下标】
let blockTitle = ''; //标题块名称
let notItem = true;
for (let i = 0; i < dataList.length; i++) {
let childList = dataList[i] || [];
if (!thisListNotItem(childList)) {
if ( !isLook ) {
let thisRoowStartX = getListFristNotNullItemIndex(childList);
let thisRoowLastItem = childList[thisRoowStartX + 1];
let LastList = dataList[i+1] || [];
// let lastRoowStartX = getListFristNotNullItemIndex(LastList);
let lastRoowHaveItem = LastList[thisRoowStartX];
if ( thisRoowLastItem || (LastList.length && lastRoowHaveItem) ) {
if (lastRoowHaveItem && thisRoowLastItem ) {
isNotBlockTitle = true; //不存在标题块
blockTitle = `${thisRoowStartX}_${i}`;
startY = i;
startX = thisRoowStartX;
}
else {
blockTitle = dataList[i][thisRoowStartX];
dataList[i][thisRoowStartX] = null;
if ( thisRoowLastItem ) { // 同行存在元素 标题在y轴上
startY = i;
startX = thisRoowStartX + 1;
} else { // 同行存在元素 标题在x轴上
startY = i + 1;
startX = thisRoowStartX;
}
}
isLook = true;
} else { //只有标题 无内容
console.log(dataList[i][thisRoowStartX]);
dataList[i][thisRoowStartX] = null;
}
} else {
//测量最大连续长度
let firstNullX = getListFirstNullItemIndex(startX, childList);
if (firstNullX) endX = Math.max(endX, firstNullX-1);
break;
}
notItem = false;
}
}
let endY = 0;//y轴连续下标 【包括下标】
let yInfoStart = false;
let yInfoEnd = false;
for (let y = startY; y < dataList.length; y++) {
//纵向找连续性
let thisRoow = dataList[y];
let regionIsNull = listRegionIsNull(thisRoow, startX, endX);
if (!regionIsNull) {
endY = y;
if (!yInfoStart) yInfoStart = true;
}
if (yInfoStart && regionIsNull) yInfoEnd = true;
if (yInfoEnd) break;
}
let blockData = [];
for (let y = startY; y <= endY; y++) {
let onceList = [];
for (let x = startX; x <= endX; x++) {
onceList.push(dataList[y][x]);
dataList[y][x] = null;
}
blockData.push(onceList);
}
return {blockData, blockTitle, delDataList:dataList,notItem};
}
//获取单个excel文件的数据
function getExcel(filePath) {
const workSheetsFromFile = xlsx.parse(filePath);
let sheetMap = {};
let sheetList = [];
for (let i = 0; i < workSheetsFromFile.length; i++) {
let sheetInfo = workSheetsFromFile[i];
sheetMap[sheetInfo.name] = sheetInfo.data;
sheetList.push(sheetInfo);
}
return {sheetMap, sheetList}
}
\ No newline at end of file
/**
* 异常类
* 需要和log4js共同使用
*/
import { getBizMsg } from "../config/errorEnum";
import { logError } from "./log";
export class BizError extends Error {
statusCode(statusCode: any) {
throw new Error('Method not implemented.');
}
constructor(...msgs) {
let reqErrorMsg = '';
let logErrorMsg = '';
for (let i = 0; i <msgs.length; i++) {
if (!i) {
let msg = getBizMsg(msgs[i]);
reqErrorMsg = msg;
logErrorMsg = msg;
} else {
logErrorMsg += ` | ${msgs[i]} `;
}
}
logError(logErrorMsg);
super(reqErrorMsg);
}
}
/**
* 日志类
* 包括错误日志 普通日志
* 日志存放在根目录的logs内
*/
let log4js = require('log4js');
let path = require('path');
//log路径
export const systemLogPath = {
errorLogFile:"error",
errorLogDir:"error",
handleLogFile:"handle",
handleLogDir:"handle"
}
//日志根目录
// let baseLogPath = path.resolve(__dirname.substring(0, __dirname.indexOf("out")), 'logs');
let baseLogPath = path.resolve('./', 'logs');
let errFile = path.resolve(baseLogPath, systemLogPath.errorLogDir, systemLogPath.errorLogFile);
let handFile =path.resolve(baseLogPath, systemLogPath.handleLogDir, systemLogPath.handleLogFile);
let config = {
appenders:
{
"rule-console": {"type": "console"},
"errorLogger": {
"type": "dateFile", // 日志类型
"filename": errFile, // 输出文件名
"pattern": "yyyy-MM-dd.log", // 后缀
"alwaysIncludePattern": true, // 上面两个参数是否合并
"encoding": "utf-8", // 编码格式
"maxLogSize": 1000, // 最大存储内容
"numBackups": 3, // 当文件内容超过文件存储空间时,备份文件的数量
"path": `/${systemLogPath.errorLogDir}`
},
"handleLogger": {
"type": "dateFile",
"filename": handFile,
"pattern": "yyyy-MM-dd.log",
"alwaysIncludePattern": true,
"encoding": "utf-8",
"maxLogSize": 1000,
"numBackups": 3,
"path": `/${systemLogPath.handleLogDir}`
}
},
categories: {
"default": {"appenders": ["rule-console"], "level": "all"}, //这个配置一定要有
"errorLogger": {"appenders": ["errorLogger"], "level": "error"},
"handleLogger": {"appenders": ["handleLogger"], "level": "all"}
},
"baseLogPath": path.resolve(baseLogPath, systemLogPath.handleLogDir, systemLogPath.handleLogFile)
};
log4js.configure(config); //加载配置文件
//调用预先定义的日志名称
let errorLogger = log4js.getLogger("errorLogger");
let handleLogger = log4js.getLogger("handleLogger");
let consoleLogger = log4js.getLogger("rule-console");
//错误日志
export function logError(...errStrs) {
let str = "";
errStrs.forEach(item => {
str += item + " | ";
});
errorLogger.error(`errorInfo => ${str}`);
}
//普通日志
export function logHandle(msgStr:string) {
handleLogger.info(`logInfo => ${msgStr}`);
}
//输出日志
export function logConsole(logStr:string) {
consoleLogger.info(`logInfo => ${logStr}`);
}
/**
* 解析xml
*/
var xml2js = require("xml2js");
/**
*
* @param str 需要解析的xml文本
* @returns 解析好的对象
*/
export function analysisXml(str) {
return new Promise( (resolve, reject) => {
xml2js.parseString(str, (err, result) => {
if (err) return reject(err);
return resolve(result);
});
});
}
\ No newline at end of file
/**
* 零碎的通用工具
*/
import moment = require("moment");
/**
* 匹配新旧对象变化
* 将newObj 与 oldObj 比对,将newObj中发生变化的key返回
* 使用前需要校验对象中的内容
* @param newObj 新对象
* @param oldObj 旧对象
* @returns [key] 发生变化的key
*/
export function checkChange(newObj, oldObj) {
let changeKeyList = [];
for (let newKey in newObj) {
if (`${newObj[newKey]}` != `${oldObj[newKey]}`) changeKeyList.push(newKey);
}
return changeKeyList;
}
/**
* 根据conf截取data中的数据
* @param conf
* @param data
* @returns
*/
export function extractData(conf, data, isAdmin) {
let result = {};
for (let key in conf) {
let confInfo = conf[key];
if (confInfo.changeDate) {
if (isAdmin) result[key] = data[key] ? moment(data[key]).format("YYYY-MM-DD") : '-';
else result[key] = data[key] || 0;
} else if (confInfo.isAdd && isAdmin) {
let addStr = "";
data[key].forEach(str => {
addStr += str;
});
result[key] = addStr;
}
else {
result[key] = data[key];
if (typeof result[key] == 'string' && !result[key]) result[key] = '';
}
}
return result;
}
/**
* 校验数据对象是否有空
* @param data
* @param sensitive 敏感校验 true时 0 和 ""会校验失败 false时 校验成功
* @returns true/false true = 有空值 false=无空值
*/
export function checkDataHaveNull(data:object, sensitive:boolean) {
if (Array.isArray(data)) return data.length == 0;
if (Object.keys(data).length == 0) return true;
let success = false;
for (let key in data) {
if (data[key] == null || data[key] == undefined) success = true;
if (sensitive) {
if (data[key] === 0 || data[key] === "" ) success = true;
}
}
return success;
}
\ No newline at end of file
/**
* 请求工具
*/
import request from 'request';
import { BizError } from './bizError';
/**
* 请求接口(get)
* @param url 路由
* @param query 请求参数
* @param headers 请求头
* @returns
*/
export function get(url:string, query?, headers?) {
if (!url || (url.search(/http:/) && url.search(/https:/)) ) throw new BizError(!url ? "请求地址为空" : "请求地址错误");
return new Promise((resolve, reject)=>{
let paramater:any = { url, json:true };
if (query) paramater.qs = query;
if (headers) paramater.headers = headers;
request.get(paramater, function (err, r, body) {
if (err) return reject(err);
if (r && r.statusCode != 200) return reject(new Error('httpError:'+r.statusCode));
resolve(body);
});
})
}
export function post(url, body, headers) {
if (!url || (url.search(/http:/) && url.search(/https:/)) ) throw new BizError(!url ? "请求地址为空" : "请求地址错误");
let header = {"content-type": "application/json"};
return new Promise((resolve, reject)=>{
request({
url: url,
method: "POST",
json: true,
headers: Object.assign(header, headers),
body: body
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
resolve(body);
}
else {
// reject(error)
}
});
})
}
export function postForm(url, body, headers) {
if (!url || (url.search(/http:/) && url.search(/https:/)) ) throw new BizError(!url ? "请求地址为空" : "请求地址错误");
return new Promise((resolve, reject)=>{
request({
url: url,
method: "POST",
json: true,
form:body
}, function(error, response, res) {
if (!error) {
resolve(res);
}
else {
reject(error)
}
});
})
}
/**
* 校验枚举工具
*
*/
import { ERRORENUM } from "../config/errorEnum";
import { BizError } from "./bizError";
/**
* 校验value是否符合传入的枚举
* @param name 被掉用名称 用于输出异常日志
* @param key 目标字段 用于输出异常日志
* @param enumConf 目标枚举
* @param value 目标值
* 无返回 有异常直接报错
*/
export function eccEnumValue(name:string, key:string, enumConf, value:any) {
let eccSuccess = true;
if ( typeof value == 'number' ) {
if (!enumConf[value] ) eccSuccess = false;
} else if (Array.isArray(value)) {
value.forEach(item => {
if ( !enumConf[item] ) eccSuccess = false;
});
}
if (!eccSuccess) throw new BizError(ERRORENUM.请完善信息, `${name} 下的 ${key} 字段值为 ${value} 不满足枚举范围`);
}
/**
* 将枚举值转换成对应的枚举名(key)
* @param enumConf 目标枚举
* @param value 目标值
* @returns string 返回字符串 如果传入多个枚举值,就拼接字符串
*/
export function changeEnumValue(enumConf, value:any) {
if (!value) return '';
if ( typeof value == 'number' ) {
let str = enumConf[value];
/** 特化处理 中文引号在枚举中不适用*/
if (str == "_投资__孵化_类型") {
str = str.replace("__","+");
str = str.replace("_","“");
str = str.replace("_","”");
}
if (str == "经营成本过高_场地成本或人员成本_" || str == "办公空间拓展_无合适办公空间_") {
str = str.replace("_","(");
str = str.replace("_",")");
}
if (str == "迁出孵化器_仍在张江" || str == "迁出张江_仍在浦东" || str == "迁出浦东_仍在上海") {
str = str.replace("_",",");
}
if (str == "科技金融_风险投资_" || str == "科技金融_其他_" || str == "技术专家_法律专家_") {
str = str.replace("_","(");
str = str.replace("_",")");
}
return str
}
let str = "";
value.forEach((item, index) => {
let subStr = enumConf[item];
/** 特化处理 中文引号在枚举中不适用*/
if (subStr == "_投资__孵化_类型") {
subStr = subStr.replace("__","+");
subStr = subStr.replace("_","“");
subStr = subStr.replace("_","”");
}
if (subStr == "经营成本过高_场地成本或人员成本_" || subStr == "办公空间拓展_无合适办公空间_") {
subStr = subStr.replace("_","(");
subStr = subStr.replace("_",")");
}
if (subStr == "迁出孵化器_仍在张江" || subStr == "迁出张江_仍在浦东" || subStr == "迁出浦东_仍在上海") {
subStr = subStr.replace("_",",");
}
if (subStr == "科技金融_风险投资_" || subStr == "科技金融_其他_" || subStr == "技术专家_法律专家_") {
subStr = subStr.replace("_","(");
subStr = subStr.replace("_",")");
}
str += subStr;
if (index == value.length-1) str+="";
else str += ","
});
return str;
}
export function changeEnumManyValue(enumConf, value:any) {
let list = [];
value.forEach((item, index) => {
let subStr = enumConf[item];
/** 特化处理 中文引号在枚举中不适用*/
if (subStr == "_投资__孵化_类型") {
subStr = subStr.replace("__","+");
subStr = subStr.replace("_","“");
subStr = subStr.replace("_","”");
}
if (subStr == "经营成本过高_场地成本或人员成本_" || subStr == "办公空间拓展_无合适办公空间_") {
subStr = subStr.replace("_","(");
subStr = subStr.replace("_",")");
}
if (subStr == "迁出孵化器_仍在张江" || subStr == "迁出张江_仍在浦东" || subStr == "迁出浦东_仍在上海") {
subStr = subStr.replace("_",",");
}
if (subStr == "科技金融_风险投资_" || subStr == "科技金融_其他_" || subStr == "技术专家_法律专家_") {
subStr = subStr.replace("_","(");
subStr = subStr.replace("_",")");
}
list.push(subStr);
});
return list;
}
/**
* 根据枚举值反向获取名称
* @param enumConf
* @param code
* @returns
*/
export function changeEnumNameByCode(enumConf, code: string) {
for (const [name, value] of Object.entries(enumConf)) {
if (value === code) {
return name;
}
}
return '未知行业';
}
/**
* 校验参数工具
*
*/
import { ERRORENUM } from "../config/errorEnum";
import { BizError } from "./bizError";
/**
* 通过config校验表单参数param
* 包括类型 String, Number, Boolean, [Number], [Object]
* 参数是必填
* 方法会校验表单中存在的多余字段
* @param name 被调用的方法名
* @param config 校验配置
* @param param 需要校验的参数
* @returns true 无需关注返回
*/
export function eccFormParam(name:string, keyTypeConf:object, param:object) {
/**校验多余字段 */
for (let key in param) {
if (!keyTypeConf[key]) throw new BizError(ERRORENUM.参数错误, name, `多余${key}字段`);
}
/**校验已填参数 */
for (let key in keyTypeConf ) {
let {type, notMustHave} = keyTypeConf[key];
let isError = false; //校验是否异常
let errorStr = "";//异常说明
let value = param[key];
let valueType = typeof value;
if ( value == null || value == undefined ) {
if (!notMustHave) throw new BizError(ERRORENUM.参数错误, name, `缺失${key}字段`);
} else {
switch(type) {
case 'Number':
if ( type.toLowerCase() != valueType ) {
isError = true;
} else {
if ((""+param[key]).indexOf('.') > -1) {
param[key] = parseInt(`${param[key] *1000}`)/1000;
}
}
break;
case 'String':
case 'Boolean':
if ( type.toLowerCase() != valueType ) isError = true;
break;
case '[Number]':
if ( !Array.isArray(param[key]) ) isError = true;
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'number' ) {
isError = true;
errorStr = `${key}应是number型数组其中下标${i}${typeof item}`;
}
}
break;
case '[Object]':
if ( !Array.isArray(param[key]) ) isError = true;
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'object' ) {
isError = true;
errorStr = `${key}应是object型数组其中下标${i}${typeof item}`;
}
}
break;
case 'Address':
/**地址类型 基本数据类型为数组字符串但是要判断层级关系 */
if ( !Array.isArray(param[key]) ) {
isError = true;
errorStr = `${key}应是数组形`;
}
if ( param[key].length != 4) {
isError = true;
errorStr = `${key}超过特定长度4 目前长度 ${param[key].length}`;
}
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'string' ) {
isError = true;
errorStr = `${key}应是string型数组其中下标${i}${typeof item}`;
}
}
/** 不符合规则的 */
let nullIndex = -1;
for (let i = 0; i < param[key].length; i++) {
if (nullIndex != -1) {//出现过空 第一次出现后的位置 都不能有值
if (param[key]) {
//做一个特化
throw new BizError(ERRORENUM.地址数据不完整, name, `${key} 下标 ${nullIndex} 为空 `);
}
}
if (nullIndex == -1 && !param[key][i]) {
/**按顺序第一次赋值 */
nullIndex = i;
}
}
break;
}
errorStr = isError && errorStr == "" ? `${key}应该是${type}型 而不是${valueType}`: errorStr;
if ( isError ) throw new BizError(ERRORENUM.请完善信息, name, errorStr);
}
}
return true;
}
/**
* 根据conf配置校验请求参数
* @param conf 配置
* @param param 表单
* @param skipKeys []不必填的字段
*/
export function eccReqParamater(conf:object, param, skipKeys?) {
skipKeys = skipKeys || [];
let skipMap = {};
skipKeys.forEach(keyName => {
skipMap[keyName] = 1;
});
/**校验多余字段 */
for (let key in param) {
if (!conf[key]) throw new BizError(ERRORENUM.参数错误, `多余${key}字段`);
}
/**校验必填和缺失字段 */
for (let key in conf) {
let confType = conf[key];
let value = param[key];
let valueType = typeof value;
if ( value == null || value == undefined ) {
if (!skipMap[key]) throw new BizError(ERRORENUM.参数错误, `缺失${key}字段`);
} else {
let isError = false;
let errorStr = "";
switch(confType) {
case 'Number':
if ( confType.toLowerCase() != valueType ) isError = true;
else {
if ((""+param[key]).indexOf('.') > -1) {
param[key] = parseInt(`${param[key] *100}`)/100;
}
}
break;
case 'String':
case 'Boolean':
if ( confType.toLowerCase() != valueType ) isError = true;
break;
case '[Number]':
if ( !Array.isArray(param[key]) ) isError = true;
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'number' ) {
isError = true;
errorStr = `${key}应是number型数组其中下标${i}${typeof item}`;
}
}
break;
case '[Object]':
if ( !Array.isArray(param[key]) ) isError = true;
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'object' ) {
isError = true;
errorStr = `${key}应是object型数组其中下标${i}${typeof item}`;
}
}
break;
case 'Address':
/**地址类型 基本数据类型为数组字符串但是要判断层级关系 */
if ( !Array.isArray(param[key]) ) {
isError = true;
errorStr = `${key}应是数组形`;
}
if ( param[key].length != 4) {
isError = true;
errorStr = `${key}超过特定长度4 目前长度 ${param[key].length}`;
}
for (let i =0; i < param[key].length; i++) {
let item = param[key][i];
if ( typeof item != 'string' ) {
isError = true;
errorStr = `${key}应是string型数组其中下标${i}${typeof item}`;
}
}
/** 不符合规则的 */
let nullIndex = -1;
for (let i = 0; i < param[key].length; i++) {
if (nullIndex != -1) {//出现过空 第一次出现后的位置 都不能有值
if (param[key]) {
//做一个特化
throw new BizError(ERRORENUM.地址数据不完整, `${key} 下标 ${nullIndex} 为空 `);
}
}
if (nullIndex == -1 && !param[key][i]) {
/**按顺序第一次赋值 */
nullIndex = i;
}
}
break;
}
errorStr = isError && errorStr == "" ? `${key}应该是${confType}型 而不是${valueType}`: errorStr;
if (isError) throw new BizError(ERRORENUM.参数错误, errorStr);
}
}
return param;
}
\ No newline at end of file
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2017",
"sourceMap": true,
"rootDir":"./src",
"outDir":"./out",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": false,
"noImplicitAny": false,
"strictNullChecks": false,
"types": ["node"]
},
"exclude": [
"node_modules",
"public"
]
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment