*
This commit is contained in:
116
README.md
Normal file
116
README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# 🌐 无人机巡查平台
|
||||
|
||||
> 基于xMagic实现的前后端分离架构的无人机巡查平台
|
||||
|
||||
本项目用于实现对边坡状态的实时监控与数据分析,保障地质安全。系统采用前后端分离设计,前端使用 Vue.js,后端基于 xScript 开发。
|
||||
|
||||
---
|
||||
|
||||
## 🗂 项目结构
|
||||
|
||||
```
|
||||
CJGIS_UAVPatrol/
|
||||
├── server/ # 后端服务目录
|
||||
| └── init.js # 后端配置文件
|
||||
| └── main.js # 后端服务入口
|
||||
| └── init.sql # 数据库初始化脚本
|
||||
├── ui/ # 前端项目目录
|
||||
├── README.md # 项目说明文档
|
||||
```
|
||||
|
||||
- **`server`**:后端服务逻辑、接口、数据库交互等。
|
||||
- **`ui`**:前端界面,基于 Vue 框架开发。
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
### 🖥 前端配置 (`ui/.env`)
|
||||
|
||||
配置后端 API 地址:
|
||||
|
||||
```env
|
||||
VUE_APP_API_URL=http://172.16.17.32:8000
|
||||
```
|
||||
|
||||
> 示例:若需切换环境,请修改此地址指向目标后端服务。
|
||||
|
||||
---
|
||||
|
||||
### 🔽 后端配置 (`server/init.js`)
|
||||
|
||||
配置数据库连接信息:
|
||||
|
||||
```env
|
||||
MYSQL_HOST=172.16.17.32
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_USER=root
|
||||
MYSQL_PASS=your_password_here
|
||||
MYSQL_DB=sucdri
|
||||
```
|
||||
|
||||
✅ 确保数据库服务可访问,并已创建对应数据库。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 指令说明
|
||||
|
||||
### 🎨 前端 (`ui/` 目录下执行)
|
||||
|
||||
| 命令 | 说明 |
|
||||
|--------------------|--------------------|
|
||||
| `npm install` | 安装依赖 |
|
||||
| `npm run dev` | 启动开发服务器(测试) |
|
||||
| `npm run serve` | 启动生产环境服务 |
|
||||
| `npm run build` | 打包编译项目 |
|
||||
|
||||
> 推荐开发时使用 `dev`,部署前使用 `build` 生成静态资源。
|
||||
|
||||
---
|
||||
|
||||
### ⚙️ 后端 (`server/` 目录下执行)
|
||||
|
||||
| 命令 | 说明 |
|
||||
|--------------------|--------------------------|
|
||||
| `./xe main.js` | 启动正式服务 |
|
||||
| `./xe test.js` | 启动测试服务 |
|
||||
|
||||
> 确保数据库配置正确并能正常连接。
|
||||
|
||||
---
|
||||
|
||||
## 🌍 部署访问
|
||||
|
||||
🔗 **当前部署地址**:
|
||||
👉 [https://shkc.p-q.co/app/panel/#/login](https://shkc.p-q.co/app/panel/#/login)
|
||||
|
||||
🔐 **默认登录账号**:
|
||||
- **账户**:`root`
|
||||
- **密码**:`LMxeon5x`
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
- 请确保前后端服务在同一网络环境下互通。
|
||||
- 生产部署建议使用 Nginx 反向代理前端,PM2 托管后端服务。
|
||||
- 定期备份数据库,防止数据丢失。
|
||||
|
||||
---
|
||||
|
||||
## 📂 技术栈(建议补充)
|
||||
|
||||
- **前端**:Vue.js, Vue Router, Axios, Element UI
|
||||
- **后端**:Node.js, Express/Koa, MySQL, Sequelize/Knex
|
||||
- **部署**:Nginx, PM2, Docker
|
||||
|
||||
---
|
||||
|
||||
> 💡 如有疑问或问题,请联系项目负责人或查阅内部文档。
|
||||
|
||||
---
|
||||
|
||||
📌 *Version: 1.0.0*
|
||||
📅 *Last Updated: 2025-04-05*
|
||||
|
||||
---
|
||||
5
server/.gitignore
vendored
Normal file
5
server/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/app
|
||||
/uploads
|
||||
/.ssl
|
||||
xe
|
||||
*.log
|
||||
20
server/Dockerfile
Normal file
20
server/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
# 使用 Alpine Linux 作为基础镜像
|
||||
FROM alpine:latest
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /workspace
|
||||
|
||||
# 下载 xe 文件到当前目录
|
||||
RUN wget -O /usr/bin/xe https://xmagic-1313201839.cos.ap-shanghai.myqcloud.com/xe
|
||||
|
||||
# 给 xe 文件赋予可执行权限
|
||||
RUN chmod +x /usr/bin/xe
|
||||
|
||||
# 将本地当前目录下的所有文件拷贝到容器的 /workspace
|
||||
COPY . .
|
||||
|
||||
# 开放 80 和 443 端口
|
||||
EXPOSE 80 443
|
||||
|
||||
# 执行 main.js
|
||||
CMD ["xe", "main.js"]
|
||||
21
server/api/api/all/function.js
Normal file
21
server/api/api/all/function.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// -------------- 获取所有api数据
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const res = SQL.query('system_sql', 'SELECT * FROM sys_apis WHERE delete_time IS NULL');
|
||||
if (res) {
|
||||
// 格式化数据
|
||||
const newResult = res.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
});
|
||||
return newObj;
|
||||
});
|
||||
if (newResult) {
|
||||
return okMsg(newResult);
|
||||
}
|
||||
}
|
||||
return errMsg(404, '查询失败');
|
||||
}
|
||||
15
server/api/api/getPolicyPathByRoleId/function.js
Normal file
15
server/api/api/getPolicyPathByRoleId/function.js
Normal file
@@ -0,0 +1,15 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const data = payload.get().query;
|
||||
const res = SQL.query(
|
||||
DB_NAME,
|
||||
'SELECT rule_id,role_id,role_key,method,patterns FROM casbin_rule WHERE delete_time IS NULL AND role_key = ?',
|
||||
data.roleKey
|
||||
);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(404, '查询失败');
|
||||
}
|
||||
42
server/api/api/list/function.js
Normal file
42
server/api/api/list/function.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// ==========================================================================
|
||||
// 数据表:sys_apis - 系统接口数据
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'sys_apis';
|
||||
COLUMN_KEY = 'id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'description') {
|
||||
placeholders.push(`description LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'method') {
|
||||
placeholders.push(`method = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'path') {
|
||||
placeholders.push(`path = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'apiGroup') {
|
||||
placeholders.push(`api_group = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
return base_curd();
|
||||
}
|
||||
22
server/api/case/old_curd_code/function.js
Normal file
22
server/api/case/old_curd_code/function.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:OaPartner
|
||||
// 生成日期:2024/10/19 16:00:00
|
||||
// 生成路径: /oa/partner/oa-partner/function.js
|
||||
// 生成人:xMagic
|
||||
// 数据表:oa_partner - OA伙伴表
|
||||
// 备注: 这个文件夹是保留最初未被代码所替代的高度抽象化的CURD接口代码,以供后续业务需要进行参考。
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'oa-partner-oa-partner-gen-hQkjChmMeUu')
|
||||
|
||||
// 取数据应用的编码
|
||||
var tableId = 'hQkjChmMeUu'
|
||||
// 具体操作的数据库名称(数据源名称)
|
||||
DB_NAME = 'system_sql'
|
||||
// 具体操作的表名称
|
||||
TABLE_NAME = 'oa_partner'
|
||||
// 主要用于检索匹配的字段名称,一般为主键的名称。
|
||||
COLUMN_KEY = 'partner_id'
|
||||
|
||||
// 后续逻辑引用sqlBaseCurd进行数据请求处理
|
||||
'use api/dm/sql/sqlBaseCurd.js'
|
||||
40
server/api/case/stand_curd_code/function.js
Normal file
40
server/api/case/stand_curd_code/function.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// ==========================================================================
|
||||
// 数据表:oa_posts - 系统接口数据
|
||||
// 备注:这个文件夹是保留最初未被代码所替代的高度抽象化的CURD接口代码,以供后续业务需要进行参考。
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'oa_posts';
|
||||
COLUMN_KEY = 'post_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'postName') {
|
||||
placeholders.push(`post_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'postCode') {
|
||||
placeholders.push(`post_id = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
return base_curd();
|
||||
}
|
||||
52
server/api/config/list/function.js
Normal file
52
server/api/config/list/function.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// ==========================================================================
|
||||
// 数据表:sys_configs - 系统接口数据
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'sys_configs';
|
||||
COLUMN_KEY = 'config_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'title') {
|
||||
placeholders.push(`title LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'name') {
|
||||
placeholders.push(`name = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'isSystem') {
|
||||
placeholders.push(`is_system = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'DELETE':
|
||||
SQL.cache.remove( // 清除缓存
|
||||
'system_sql',
|
||||
"SELECT * FROM `sys_configs` WHERE `delete_time` IS NULL;",
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return base_curd();
|
||||
}
|
||||
10
server/api/config/refresh/function.js
Normal file
10
server/api/config/refresh/function.js
Normal file
@@ -0,0 +1,10 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
SQL.cache.remove( // 清除缓存
|
||||
'system_sql',
|
||||
"SELECT * FROM `sys_configs` WHERE `delete_time` IS NULL;",
|
||||
);
|
||||
return okMsg(phone)
|
||||
}
|
||||
35
server/api/core/runtime/cache/function.js
vendored
Normal file
35
server/api/core/runtime/cache/function.js
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
response.headers.add('X-Script-Version', 'runtime-cache-1.0')
|
||||
|
||||
function del() {
|
||||
var script = payload.get().query['path[]']
|
||||
if (script[0] == '*') {
|
||||
runtime.cache.purge()
|
||||
return ``
|
||||
}
|
||||
for (var i = 0; i < script.length; i++) {
|
||||
runtime.cache.remove(script[i])
|
||||
}
|
||||
}
|
||||
|
||||
function get() {
|
||||
var script = payload.get().query['path']
|
||||
if (!script || script == '' || script == '*') {
|
||||
return okMsg(runtime.cache.list())
|
||||
}
|
||||
return runtime.cache.get(script)
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
// 'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return get()
|
||||
case 'DELETE':
|
||||
return del()
|
||||
default:
|
||||
response.status.notFound()
|
||||
return `404 page not found`
|
||||
}
|
||||
}
|
||||
100
server/api/core/runtime/database/redis/function.js
Normal file
100
server/api/core/runtime/database/redis/function.js
Normal file
@@ -0,0 +1,100 @@
|
||||
response.headers.add('X-Script-Version', 'runtime-redis-1.0')
|
||||
|
||||
function connect() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.uri) {
|
||||
var result = REDIS.new(data.name, data.uri)
|
||||
if (result == data.name) {
|
||||
return okMsg("连接成功")
|
||||
}
|
||||
return errMsg(507, "连接失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function get() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.key) {
|
||||
var result = REDIS.get(data.name, data.key)
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
return errMsg(507, "查询失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function set() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.key && data.value) {
|
||||
var result = REDIS.set(data.name, data.key, data.value, data.ttl ? data.ttl : -1)
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
return errMsg(507, "赋值失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function del() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.sql) {
|
||||
var result = REDIS.del(data.name, data.key)
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
return errMsg(507, "查询失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function keys() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.sql) {
|
||||
var result = REDIS.keys(data.name, data.pattern)
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
return errMsg(507, "查询失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function flush() {
|
||||
var script = payload.get().query['name[]']
|
||||
for (var i = 0; i < script.length; i++) {
|
||||
REDIS.flush(script[i])
|
||||
}
|
||||
return okMsg("操作成功")
|
||||
}
|
||||
|
||||
function close() {
|
||||
var script = payload.get().query['name[]']
|
||||
for (var i = 0; i < script.length; i++) {
|
||||
REDIS.close(script[i])
|
||||
}
|
||||
return okMsg("操作成功")
|
||||
}
|
||||
|
||||
function list() {
|
||||
return okMsg(REDIS.list())
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return list()
|
||||
case 'POST':
|
||||
return keys()
|
||||
case 'PUT':
|
||||
return connect()
|
||||
case 'DELETE':
|
||||
return close()
|
||||
default:
|
||||
response.status.notFound()
|
||||
return `404 page not found`
|
||||
}
|
||||
}
|
||||
68
server/api/core/runtime/database/sql/function.js
Normal file
68
server/api/core/runtime/database/sql/function.js
Normal file
@@ -0,0 +1,68 @@
|
||||
response.headers.add('X-Script-Version', 'runtime-sql-1.0')
|
||||
|
||||
function connect() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.type && data.uri) {
|
||||
var result = SQL.new(data.name, data.type, data.uri, data.cache_size ? data.cache_size : 0)
|
||||
if (result == data.name) {
|
||||
return okMsg("连接成功")
|
||||
}
|
||||
return errMsg(507, "连接失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function query() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.sql) {
|
||||
var result = SQL.query(data.name, data.sql)
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
return errMsg(507, "查询失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function exec() {
|
||||
var data = payload.get().body?.toObject()
|
||||
if (data.name && data.sql) {
|
||||
var result = SQL.exec(data.name, data.sql)
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
return errMsg(507, "执行失败")
|
||||
}
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
|
||||
function close() {
|
||||
var script = payload.get().query['name[]']
|
||||
for (var i = 0; i < script.length; i++) {
|
||||
SQL.close(script[i])
|
||||
}
|
||||
return okMsg("操作成功")
|
||||
}
|
||||
|
||||
function list() {
|
||||
return okMsg(SQL.list())
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return list()
|
||||
case 'POST':
|
||||
return query()
|
||||
case 'PUT':
|
||||
return connect()
|
||||
case 'DELETE':
|
||||
return close()
|
||||
default:
|
||||
response.status.notFound()
|
||||
return `404 page not found`
|
||||
}
|
||||
}
|
||||
26
server/api/core/runtime/database/sql/table/function.js
Normal file
26
server/api/core/runtime/database/sql/table/function.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const params = formatSingleParams()
|
||||
|
||||
'use api/core/runtime/database/sql/tables.js'
|
||||
|
||||
// DB_NAME = 'system_sql'
|
||||
// TABLE_NAME = ''
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
DB_NAME = payload.get().query.db_name
|
||||
TABLE_NAME = payload.get().query.table_name
|
||||
if (!TABLE_NAME) {
|
||||
TABLE_NAME = payload.get().path.split("/").pop();
|
||||
}
|
||||
let info = getTableCollums()
|
||||
let result = []
|
||||
info.forEach(t => {
|
||||
const newObj = Object.keys(t).reduce((acc, key) => {
|
||||
acc[key.toLowerCase()] = t[key];
|
||||
return acc;
|
||||
}, {});
|
||||
result.push(newObj)
|
||||
});
|
||||
return okMsg(result)
|
||||
}
|
||||
81
server/api/core/runtime/database/sql/tables.js
Normal file
81
server/api/core/runtime/database/sql/tables.js
Normal file
@@ -0,0 +1,81 @@
|
||||
// 获取数据库所有表信息
|
||||
'use api/dm/sql/formatCondition.js'
|
||||
function getTablesInfoStmt() {
|
||||
// const placeholdersObj = formatCondition();
|
||||
// const newPlaceholders = placeholdersObj.placeholders;
|
||||
// console.log(placeholdersObj)
|
||||
const tableName = payload.get().query?.tableName;
|
||||
if (tableName) {
|
||||
// const tempParams = placeholdersObj.tempParams;
|
||||
return {
|
||||
// query: `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND ${newPlaceholders} LIMIT ?,?;`,
|
||||
query: `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE '%${tableName}%' LIMIT ?,?;`,
|
||||
// params: [...tempParams, (params.pageNum - 1) * params.pageSize, params.pageSize],
|
||||
params: [(params.pageNum - 1) * params.pageSize, params.pageSize],
|
||||
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() LIMIT ?,?`,
|
||||
params: [(params.pageNum - 1) * params.pageSize, params.pageSize],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 列表查询总数据
|
||||
function genCountTotal() {
|
||||
// const placeholdersObj = formatCondition();
|
||||
// const newPlaceholders = placeholdersObj.placeholders;
|
||||
const tableName = payload.get().query?.tableName;
|
||||
if (tableName) {
|
||||
// const tempParams = placeholdersObj.tempParams;
|
||||
return {
|
||||
query: `SELECT COUNT(*) AS count FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE '%${tableName}%'`,
|
||||
params: [],
|
||||
// params: [...tempParams],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT COUNT(*) AS count FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE()`,
|
||||
params: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getTablesInfo() {
|
||||
let stmt = getTablesInfoStmt()
|
||||
let countQuery = genCountTotal()
|
||||
var dataRes = SQL.query(DB_NAME, stmt.query, ...stmt.params)
|
||||
var countRes = SQL.query(DB_NAME, countQuery.query, ...countQuery.params)
|
||||
if (dataRes) {
|
||||
return { result: dataRes, count: Number(countRes[0]?.count) }
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function isExistTable() {
|
||||
var result = SQL.query(DB_NAME, `SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`, TABLE_NAME)
|
||||
return result && result.length
|
||||
}
|
||||
|
||||
function getTableInfo() {
|
||||
let isExist = isExistTable()
|
||||
if (isExist) {
|
||||
var result = SQL.query(DB_NAME, `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`, TABLE_NAME)
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function getTableCollums() {
|
||||
let isExist = isExistTable()
|
||||
if (isExist) {
|
||||
var result = SQL.query(DB_NAME, `SHOW FULL COLUMNS FROM ${TABLE_NAME};`)
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
26
server/api/core/runtime/database/sql/tables/function.js
Normal file
26
server/api/core/runtime/database/sql/tables/function.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const params = formatSingleParams();
|
||||
|
||||
'use api/core/runtime/database/sql/tables.js';
|
||||
'use api/lib/camelAndUnderscore.js';
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let db_name = payload.get().query.db_name;
|
||||
if (db_name) {
|
||||
DB_NAME = db_name;
|
||||
}
|
||||
|
||||
let tables = getTablesInfo();
|
||||
let result = [];
|
||||
tables.result.forEach((t) => {
|
||||
const newObj = Object.keys(t).reduce((acc, key) => {
|
||||
acc[underscoreToCamel(key.toLowerCase())] = t[key];
|
||||
return acc;
|
||||
}, {});
|
||||
result.push(newObj);
|
||||
});
|
||||
|
||||
return okMsg({ data: result, count: tables.count });
|
||||
}
|
||||
31
server/api/core/runtime/thread/function.js
Normal file
31
server/api/core/runtime/thread/function.js
Normal file
@@ -0,0 +1,31 @@
|
||||
response.headers.add('X-Script-Version', 'runtime-thread-1.0')
|
||||
|
||||
function stop() {
|
||||
var script = payload.get().query['path[]']
|
||||
if (script[0] == '*') {
|
||||
runtime.thread.stopAll()
|
||||
return ``
|
||||
}
|
||||
for (var i = 0; i < script.length; i++) {
|
||||
runtime.thread.stop(script[i])
|
||||
}
|
||||
}
|
||||
|
||||
function list() {
|
||||
return JSON.stringify(runtime.thread.list())
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return list()
|
||||
case 'DELETE':
|
||||
return stop()
|
||||
default:
|
||||
response.status.notFound()
|
||||
return `404 page not found`
|
||||
}
|
||||
}
|
||||
37
server/api/dict/data/add/function.js
Normal file
37
server/api/dict/data/add/function.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// --------sys_dict_data 添加字典配置标签
|
||||
COLUMN_KEY = 'dict_code';
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
});
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = Object.values(newData);
|
||||
const placeholders = [];
|
||||
|
||||
columnsArr.push(COLUMN_KEY)
|
||||
columnsArr.push('proprietor_id')
|
||||
columnsArr.push('proprietor_name')
|
||||
|
||||
dataArr.push(ex.suid().base58())
|
||||
dataArr.push(USER_INFO.user_id)
|
||||
dataArr.push(USER_INFO.nickname)
|
||||
|
||||
dataArr.forEach(() => {
|
||||
placeholders.push('?');
|
||||
});
|
||||
placeholders.join(',');
|
||||
|
||||
const columnsStr = columnsArr.join(',');
|
||||
const query = `INSERT INTO sys_dict_data (${columnsStr}) VALUES (${placeholders})`;
|
||||
const res = SQL.exec('system_sql', query, ...dataArr);
|
||||
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '添加失败');
|
||||
}
|
||||
12
server/api/dict/data/del/function.js
Normal file
12
server/api/dict/data/del/function.js
Normal file
@@ -0,0 +1,12 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const query = `UPDATE sys_dict_data SET delete_time = NOW() WHERE dict_code = ?`;
|
||||
const res = SQL.exec('system_sql', query, filter);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '删除失败');
|
||||
}
|
||||
69
server/api/dict/data/type/function.js
Normal file
69
server/api/dict/data/type/function.js
Normal file
@@ -0,0 +1,69 @@
|
||||
// --------sys_dict_data 获取字典配置详情
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'dictLabel') {
|
||||
placeholders.push(`dict_label LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'dictType') {
|
||||
// placeholders.push(`dict_type LIKE ?`);
|
||||
// tempParams.push('%' + condition[item] + '%');
|
||||
placeholders.push(`dict_type = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function selectData(condition) {
|
||||
const newPlaceholders = getPlaceholders(condition).placeholders;
|
||||
if (newPlaceholders) {
|
||||
const tempParams = getPlaceholders(condition).tempParams;
|
||||
return {
|
||||
query: `SELECT * FROM sys_dict_data WHERE ${newPlaceholders} AND delete_time IS NULL `,
|
||||
params: [...tempParams],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT * FROM sys_dict_data WHERE delete_time IS NULL`,
|
||||
params: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const data = payload.get().query;
|
||||
// 格式化这个queryS参数
|
||||
let newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[item] = data[item][0];
|
||||
});
|
||||
// 分页数据
|
||||
const LIST_DATA = selectData(newData);
|
||||
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
|
||||
// 格式化数据
|
||||
const newResult = result.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
});
|
||||
return newObj;
|
||||
});
|
||||
|
||||
console.log("newResult-----",newResult)
|
||||
|
||||
return okMsg(newResult);
|
||||
}
|
||||
14
server/api/dict/data/update/function.js
Normal file
14
server/api/dict/data/update/function.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// ===========
|
||||
// sys_dict_data 更新字典配置详情
|
||||
// ===========
|
||||
TABLE_NAME = 'sys_dict_data';
|
||||
COLUMN_KEY = 'dict_code';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
return sql_update();
|
||||
}
|
||||
38
server/api/dict/type/list/function.js
Normal file
38
server/api/dict/type/list/function.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// ==========================================================================
|
||||
// 数据表:sys_dict_types - 系统字典数据
|
||||
// ==========================================================================
|
||||
TABLE_NAME = 'sys_dict_types';
|
||||
COLUMN_KEY = 'dict_id';
|
||||
TABLE_FIELDS = '*'
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'dictName') {
|
||||
placeholders.push(`dict_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'dictType') {
|
||||
placeholders.push(`dict_type LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
return base_curd();
|
||||
}
|
||||
89
server/api/dm/cols/function.js
Normal file
89
server/api/dm/cols/function.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:DevGenTableColumns
|
||||
// 生成日期:2024-02-20 20:43:57 +0800 CST
|
||||
// 生成路径: /dm/cols/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:dm_table_columns - 代码生成字段
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'core-gen-cols-gen-147')
|
||||
|
||||
// ===== 获取dm_table_columns表所有数据
|
||||
TABLE_NAME = 'dm_table_columns';
|
||||
COLUMN_KEY = 'table_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'tableId') {
|
||||
placeholders.push(`tableId = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'tableName') {
|
||||
placeholders.push(`table_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'columnComment') {
|
||||
placeholders.push(`column_comment LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function update() {
|
||||
const data = payload.get().body?.toObject();
|
||||
delete data.createTime;
|
||||
delete data.updateTime;
|
||||
const newData = {};
|
||||
let field
|
||||
Object.keys(data).forEach((name) => {
|
||||
field = Object(TABLE_STRUCT).find(data => data.field_name === data).column_name
|
||||
newData[field] = data[name];
|
||||
});
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = [];
|
||||
const placeholders = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (newData[item]) {
|
||||
placeholders.push(`${item} =?`);
|
||||
dataArr.push(newData[item]);
|
||||
} else {
|
||||
placeholders.push(`${item} = NULL`);
|
||||
}
|
||||
})
|
||||
|
||||
const newPlaceholders = placeholders.join(',');
|
||||
const query = `UPDATE ${TABLE_NAME} SET ${newPlaceholders} WHERE ${COLUMN_KEY} = ?`;
|
||||
|
||||
const res = SQL.exec(DB_NAME, query, ...[...dataArr, filter]);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return sql_select();
|
||||
case 'PUT':
|
||||
return update();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
89
server/api/dm/cols/sync/function.js
Normal file
89
server/api/dm/cols/sync/function.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// 创建数据表的信息
|
||||
//
|
||||
'use api/core/runtime/database/sql/tables.js';
|
||||
'use api/lib/camelAndUnderscore.js';
|
||||
|
||||
// 根据sql列类型获取html类型
|
||||
function getHtmlTypeFromColumnType(columnType) {
|
||||
// const ColumnTypeStr = ['char', 'varchar', 'narchar', 'varchar2', 'tinytext', 'text', 'mediumtext', 'longtext']; // to "input";
|
||||
const ColumnTypeTime = ['datetime', 'time', 'date', 'timestamp', 'timestamptz']; // to "datetime";
|
||||
// const ColumnTypeNumber = [
|
||||
// 'smallint',
|
||||
// 'mediumint',
|
||||
// 'int',
|
||||
// 'int2',
|
||||
// 'int4',
|
||||
// 'int8',
|
||||
// 'number',
|
||||
// 'integer',
|
||||
// 'numeric',
|
||||
// 'bigint',
|
||||
// 'float',
|
||||
// 'float4',
|
||||
// 'float8',
|
||||
// 'double',
|
||||
// 'decimal',
|
||||
// ]; // to "input";
|
||||
const ColumnTypeBool = ['tinyint', 'bool', 'boolean']; // to "switch";
|
||||
const dataType = columnType.toLowerCase();
|
||||
if (ColumnTypeTime.includes(dataType)) {
|
||||
return 'datetime';
|
||||
} else if (ColumnTypeBool.includes(dataType)) {
|
||||
return 'switch';
|
||||
} else {
|
||||
return 'input';
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let tableId = payload.get().path.split('/').pop();
|
||||
|
||||
TABLE_STRUCT = SQL.query('system_sql', 'SELECT * FROM dm_table_columns WHERE table_id = ?', tableId);
|
||||
if (TABLE_STRUCT.length == 0) {
|
||||
return errMsg(507, '表结构为空');
|
||||
}
|
||||
|
||||
DB_NAME = SQL.query('system_sql', 'SELECT db_name FROM dm_tables WHERE table_id = ?', tableId)[0].db_name;
|
||||
TABLE_NAME = TABLE_STRUCT[0].table_name;
|
||||
|
||||
let tableCollums = getTableCollums();
|
||||
|
||||
// let fieldName = underscoreToCamel(col.Field);
|
||||
const temp = [];
|
||||
for (let i = 0; i < tableCollums.length; i++) {
|
||||
let col = tableCollums[i];
|
||||
|
||||
// 如果TABLE_STRUCT有元素的column_name和col.Field相同则跳过
|
||||
if (TABLE_STRUCT.find((item) => item.column_name == col.Field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let stmt =
|
||||
'INSERT INTO dm_table_columns (column_id, table_id, sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_increment, html_type, proprietor_id, proprietor_name) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
|
||||
// tid, sort, tname , cname, ccomment, ctype, ckey, jfield, ispk, isrequired, isinc, htype oid, own
|
||||
let params = [
|
||||
ex.suid().base58(),
|
||||
tableId,
|
||||
i + 1,
|
||||
TABLE_NAME,
|
||||
col.Field,
|
||||
col.Comment,
|
||||
col.Type,
|
||||
col.Key,
|
||||
underscoreToCamel(col.Field),
|
||||
col.Key == 'PKI' ? 1 : 0,
|
||||
col.Null == 'NO' ? 1 : 0,
|
||||
col.Extra.includes('auto_increment') ? 1 : 0,
|
||||
getHtmlTypeFromColumnType(col.Type),
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.nickname,
|
||||
];
|
||||
SQL.push('system_sql', stmt, ...params);
|
||||
temp.push(params);
|
||||
}
|
||||
|
||||
return okMsg('创建表信息成功');
|
||||
}
|
||||
25
server/api/dm/copy/function.js
Normal file
25
server/api/dm/copy/function.js
Normal file
@@ -0,0 +1,25 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let tableId = payload.get().path.split('/').pop()
|
||||
let newTableId = ex.suid().base58()
|
||||
let query = `INSERT INTO dm_tables (table_id, table_name, table_comment, class_name, module_name, business_name, function_name, function_author, pk_column, pk_field_name, proprietor_id, proprietor_name) SELECT ?, table_name, table_comment, class_name, module_name, business_name, function_name, function_author, pk_column, pk_field_name, proprietor_id, proprietor_name FROM dm_tables WHERE table_id =?`
|
||||
let res = SQL.exec('system_sql', query, newTableId, tableId)
|
||||
if (!res) {
|
||||
return errMsg(507, '创建表信息失败')
|
||||
}
|
||||
query = `SELECT column_id FROM dm_table_columns WHERE table_id =?`;
|
||||
res = SQL.query('system_sql', query, tableId)
|
||||
let newColumnId
|
||||
|
||||
res.forEach((columnId) => {
|
||||
newColumnId = ex.suid().base58()
|
||||
query = `INSERT INTO dm_table_columns (column_id, table_id, sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_insert, is_show, is_edit, is_list, is_query, is_acl, is_group, calc_type, is_order, query_type, search_value, default_value, is_increment, html_type, html_width, dict_type, proprietor_id, proprietor_name) SELECT '${newColumnId}', '${newTableId}', sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_insert, is_show, is_edit, is_list, is_query, is_acl, is_group, calc_type, is_order, query_type, search_value, default_value, is_increment, html_type, html_width, dict_type, proprietor_id, proprietor_name FROM dm_table_columns WHERE column_id =?`
|
||||
// console.log(query, columnId.column_id)
|
||||
SQL.exec('system_sql', query, columnId.column_id)
|
||||
})
|
||||
|
||||
return okMsg(newTableId)
|
||||
|
||||
}
|
||||
88
server/api/dm/function.js
Normal file
88
server/api/dm/function.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:DevGenTables
|
||||
// 生成日期:2024-02-20 21:43:27 +0800 CST
|
||||
// 生成路径: apps/system/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:dm_tables - DevGenTables
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'dm_tables';
|
||||
COLUMN_KEY = 'table_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'tableName') {
|
||||
placeholders.push(`table_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'tableId') {
|
||||
placeholders.push(`table_id = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'tableComment') {
|
||||
placeholders.push(`table_comment LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function update() {
|
||||
const data = payload.get().body?.toObject();
|
||||
// 验证data.tableName 和 data.columnName 只能有字母数字下划线
|
||||
if (!/^[a-zA-Z0-9_]+$/.test(data.tableName) || !/^[a-zA-Z0-9_]+$/.test(data.columnName) || !/^[a-zA-Z0-9_]+$/.test(data.dbName)) {
|
||||
return errMsg(405, '不支持的数据');
|
||||
}
|
||||
|
||||
delete data.createTime;
|
||||
delete data.updateTime;
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
});
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = [];
|
||||
const placeholders = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (newData[item]) {
|
||||
placeholders.push(`${item} = ?`);
|
||||
dataArr.push(newData[item]);
|
||||
} else {
|
||||
placeholders.push(`${item} = NULL`);
|
||||
}
|
||||
});
|
||||
const newPlaceholders = placeholders.join(',');
|
||||
const query = `UPDATE ${TABLE_NAME} SET ${newPlaceholders} WHERE ${COLUMN_KEY} = ?`;
|
||||
|
||||
const res = SQL.exec('system_sql', query, ...[...dataArr, filter]);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return sql_select();
|
||||
case 'PUT':
|
||||
return update();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
134
server/api/dm/import/function.js
Normal file
134
server/api/dm/import/function.js
Normal file
@@ -0,0 +1,134 @@
|
||||
// 创建数据表的信息
|
||||
//
|
||||
'use api/core/runtime/database/sql/tables.js';
|
||||
'use api/lib/camelAndUnderscore.js';
|
||||
|
||||
// 根据sql列类型获取html类型
|
||||
function getHtmlTypeFromColumnType(columnType) {
|
||||
// const ColumnTypeStr = ['char', 'varchar', 'narchar', 'varchar2', 'tinytext', 'text', 'mediumtext', 'longtext']; // to "input";
|
||||
const ColumnTypeTime = ['datetime', 'time', 'date', 'timestamp', 'timestamptz']; // to "datetime";
|
||||
// const ColumnTypeNumber = [
|
||||
// 'smallint',
|
||||
// 'mediumint',
|
||||
// 'int',
|
||||
// 'int2',
|
||||
// 'int4',
|
||||
// 'int8',
|
||||
// 'number',
|
||||
// 'integer',
|
||||
// 'numeric',
|
||||
// 'bigint',
|
||||
// 'float',
|
||||
// 'float4',
|
||||
// 'float8',
|
||||
// 'double',
|
||||
// 'decimal',
|
||||
// ]; // to "input";
|
||||
const ColumnTypeBool = ['tinyint', 'bool', 'boolean']; // to "switch";
|
||||
const dataType = columnType.toLowerCase();
|
||||
if (ColumnTypeTime.includes(dataType)) {
|
||||
return 'datetime';
|
||||
} else if (ColumnTypeBool.includes(dataType)) {
|
||||
return 'switch';
|
||||
} else {
|
||||
return 'input';
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let data = payload.get().body?.toObject();
|
||||
if (data.db) {
|
||||
DB_NAME = data.db;
|
||||
}
|
||||
TABLE_NAME = data.tables;
|
||||
|
||||
let dbInfo = SQL.query('system_sql', 'SELECT * FROM sys_database WHERE name = ?;', DB_NAME);
|
||||
if (!dbInfo.length) {
|
||||
return errMsg(507, '数据库不存在');
|
||||
}
|
||||
dbInfo = dbInfo[0];
|
||||
|
||||
// TABLE_NAME 可能由多个组成,采用, 分割
|
||||
let tables = TABLE_NAME.split(',');
|
||||
tables.forEach((table) => {
|
||||
let pkColumn = '';
|
||||
|
||||
let tableInfo = getTableInfo();
|
||||
let tableCollums = getTableCollums();
|
||||
|
||||
tableCollums.forEach((pk) => {
|
||||
// pk_column 是取Key为PKI的Field值
|
||||
if (pk.Key == 'PRI') {
|
||||
pkColumn = pk.Field;
|
||||
return pkColumn;
|
||||
}
|
||||
});
|
||||
|
||||
let moduleName = table.replace('_', '-');
|
||||
let businessName = moduleName.replace(/^.*?-/, '');
|
||||
let packageName = capitalizeFirstLetter(underscoreToCamel(table));
|
||||
|
||||
let stmt =
|
||||
'INSERT INTO dm_tables (db_id, db_name, db_comment, table_id, table_name, table_comment, class_name, module_name, business_name, function_name, function_author, pk_column, pk_field_name, proprietor_id, proprietor_name) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
|
||||
|
||||
const tableId = ex.suid().base58()
|
||||
|
||||
let res = SQL.exec(
|
||||
'system_sql',
|
||||
stmt,
|
||||
dbInfo.db_id,
|
||||
dbInfo.name,
|
||||
dbInfo.comment,
|
||||
tableId,
|
||||
table,
|
||||
tableInfo[0]['TABLE_COMMENT'],
|
||||
packageName,
|
||||
moduleName,
|
||||
businessName,
|
||||
capitalizeFirstLetter(businessName),
|
||||
USER_INFO.username + '-' + USER_INFO.nickname,
|
||||
pkColumn,
|
||||
underscoreToCamel(pkColumn),
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.nickname,
|
||||
);
|
||||
|
||||
if (!res) {
|
||||
return errMsg(507, '创建表信息失败');
|
||||
}
|
||||
|
||||
// let fieldName = underscoreToCamel(col.Field);
|
||||
const temp = [];
|
||||
for (let i = 0; i < tableCollums.length; i++) {
|
||||
let col = tableCollums[i];
|
||||
let stmt =
|
||||
'INSERT INTO dm_table_columns (column_id, table_id, sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_increment, html_type, proprietor_id, proprietor_name) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
|
||||
// tid, sort, tname , cname, ccomment, ctype, ckey, jfield, ispk, isrequired, isinc, htype oid, own
|
||||
let params = [
|
||||
ex.suid().base58(),
|
||||
tableId,
|
||||
i + 1,
|
||||
TABLE_NAME,
|
||||
col.Field,
|
||||
col.Comment,
|
||||
col.Type,
|
||||
col.Key,
|
||||
underscoreToCamel(col.Field),
|
||||
col.Key == 'PKI' ? 1 : 0,
|
||||
col.Null == 'NO' ? 1 : 0,
|
||||
col.Extra.includes('auto_increment') ? 1 : 0,
|
||||
getHtmlTypeFromColumnType(col.Type),
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.nickname,
|
||||
];
|
||||
SQL.push('system_sql', stmt, ...params);
|
||||
temp.push(params);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return okMsg('创建表信息成功');
|
||||
}
|
||||
89
server/api/dm/sql/data.js
Normal file
89
server/api/dm/sql/data.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// 获取数据
|
||||
function sqlGenSelect() {
|
||||
let userAccess
|
||||
// 判断用户表权限
|
||||
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
|
||||
let udr = []
|
||||
udr.push(`'${USER_INFO.user_id}'`)
|
||||
udr.push(`'${USER_INFO.role_id}'`)
|
||||
udr.push(`'${USER_INFO.organization_id}'`)
|
||||
|
||||
let columnIds = []
|
||||
TABLE_STRUCT.forEach(item => {
|
||||
if (item.is_acl == 1) {
|
||||
columnIds.push(`'${item.column_id}'`)
|
||||
}
|
||||
|
||||
if (item.search_value) {
|
||||
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
|
||||
}
|
||||
})
|
||||
|
||||
userAccess = SQL.query(
|
||||
DB_NAME,
|
||||
`SELECT DISTINCT column_name FROM dm_table_columns_acl WHERE column_id IN (${columnIds.split(',')}) AND access_id IN (${udr.split(',')}) AND level > 0 AND delete_time IS NULL;`,
|
||||
TABLE_INFO.table_id
|
||||
)
|
||||
} else {
|
||||
TABLE_STRUCT.forEach(item => {
|
||||
if (item.search_value) {
|
||||
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let value = payload.get().query[COLUMN_KEY]
|
||||
// 兼容 /user/1 这种通过 path 传值的方式
|
||||
if (!value) {
|
||||
// 声明是否使用上一级的脚本
|
||||
var useParent = scriptPath != payload.get().path.substring(1)
|
||||
|
||||
if (useParent) {
|
||||
value = payload.get().path.split("/").pop();
|
||||
} else {
|
||||
return sqlGenList();
|
||||
}
|
||||
}
|
||||
|
||||
var cols = []
|
||||
for (var i = 0; i < TABLE_STRUCT.length; i++) {
|
||||
if (TABLE_STRUCT[i].is_show == 1) {
|
||||
if (TABLE_STRUCT[i].is_acl == 1 && !userAccess.includes(TABLE_STRUCT[i].column_name)) {
|
||||
continue
|
||||
}
|
||||
cols.push(TABLE_STRUCT[i].column_name + ' as ' + TABLE_STRUCT[i].field_name)
|
||||
}
|
||||
}
|
||||
|
||||
const chooseData = cols.join(',');
|
||||
// const chooseData = "*";
|
||||
|
||||
stmt = `SELECT ${chooseData} FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ? ${searchStr}`
|
||||
|
||||
if (TABLE_INFO.before_hook) {
|
||||
runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
|
||||
SQL_STMTS.push(stmt)
|
||||
result = SQL.query(
|
||||
DB_NAME,
|
||||
stmt, value
|
||||
)
|
||||
|
||||
if (TABLE_INFO.after_hook) {
|
||||
runtime.call(TABLE_INFO.after_hook)
|
||||
}
|
||||
|
||||
// let result = []
|
||||
// res.forEach(t => {
|
||||
// const newObj = Object.keys(t).reduce((acc, key) => {
|
||||
// acc[underscoreToCamel(key.toLowerCase())] = t[key];
|
||||
// return acc;
|
||||
// }, {});
|
||||
// result.push(newObj)
|
||||
// });
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
return errMsg(404, '查询失败')
|
||||
}
|
||||
31
server/api/dm/sql/delete.js
Normal file
31
server/api/dm/sql/delete.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// 删除数据
|
||||
function sqlGenDelete() {
|
||||
const findData = TABLE_STRUCT.find(item => item.column_name == COLUMN_KEY) || {}
|
||||
const parName = findData?.field_name || underscoreToCamel(COLUMN_KEY)
|
||||
const ids = payload.get().query[parName + '[]']
|
||||
const tempParams = []
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
tempParams.push('?')
|
||||
}
|
||||
stmt = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} IN (${tempParams.join(',')})`
|
||||
if (TABLE_INFO.before_hook) {
|
||||
result = runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
|
||||
// let sql = `DELETE FROM ${TABLE_NAME} WHERE ${COLUMN_KEY} IN (${tempParams.join(',')})`
|
||||
SQL_STMTS.push(stmt)
|
||||
result = SQL.exec(
|
||||
DB_NAME,
|
||||
stmt, ...ids
|
||||
)
|
||||
|
||||
if (TABLE_INFO.after_hook) {
|
||||
runtime.call(TABLE_INFO.after_hook)
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return okMsg(result)
|
||||
}
|
||||
|
||||
return errMsg(507, '参数错误')
|
||||
}
|
||||
79
server/api/dm/sql/formatCondition.js
Normal file
79
server/api/dm/sql/formatCondition.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// 格式化筛选条件
|
||||
function formatCondition() {
|
||||
if (Object.keys(params).length > 0) {
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
|
||||
var cols = []
|
||||
let mapCols = {}
|
||||
TABLE_STRUCT.forEach(item => {
|
||||
if (item.is_query == 1) {
|
||||
cols.push(item.column_name)
|
||||
if (item.field_name) {
|
||||
mapCols[`${item.field_name}`] = item.column_name
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 将存在别名的params转换
|
||||
let coverParams = {}
|
||||
Object.keys(params).forEach(item => {
|
||||
if (mapCols[item]) {
|
||||
coverParams[mapCols[item]] = params[item]
|
||||
} else {
|
||||
coverParams[item] = params[item]
|
||||
}
|
||||
})
|
||||
|
||||
Object.keys(coverParams).forEach(key => {
|
||||
let col_name = camelToUnderscore(key)
|
||||
if (cols.includes(col_name)) {
|
||||
var row = {}
|
||||
TABLE_STRUCT.find(item => {
|
||||
if (item.column_name == col_name) {
|
||||
row = item
|
||||
}
|
||||
})
|
||||
|
||||
if (coverParams[key]) {
|
||||
// if (row.query_type == "NaN" || row.query_type == "VAL") {
|
||||
// placeholders.push(`${col_name} ${sql_operator[row.query_type]}`);
|
||||
// }
|
||||
// if (row.query_type == "LIKE") {
|
||||
// tempParams.push('%' + coverParams[key] + '%');
|
||||
// } else {
|
||||
// tempParams.push(coverParams[key]);
|
||||
// }
|
||||
switch (row.query_type) {
|
||||
case "NAN":
|
||||
case "VAL":
|
||||
placeholders.push(`${col_name} ${sql_operator[row.query_type]}`);
|
||||
break;
|
||||
case "LIKE":
|
||||
placeholders.push(`${col_name} ${sql_operator[row.query_type]} ?`);
|
||||
tempParams.push('%' + coverParams[key] + '%');
|
||||
break;
|
||||
case "BT":
|
||||
if (coverParams[key].includes(',')) {
|
||||
placeholders.push(`${col_name} BETWEEN ? AND ?`);
|
||||
let rangValue = coverParams[key].split(',')
|
||||
tempParams.push(rangValue[0]);
|
||||
tempParams.push(rangValue[1]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
placeholders.push(`${col_name} ${sql_operator[row.query_type]} ?`);
|
||||
tempParams.push(coverParams[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
}
|
||||
}
|
||||
}
|
||||
153
server/api/dm/sql/insert.js
Normal file
153
server/api/dm/sql/insert.js
Normal file
@@ -0,0 +1,153 @@
|
||||
// 添加数据
|
||||
function sqlGenInsert() {
|
||||
var data = payload.get().body?.toObject()
|
||||
|
||||
if (!data || (data && !Object.keys(data).length)) {
|
||||
return errMsg(404, '未传入参数')
|
||||
}
|
||||
|
||||
let userAccess
|
||||
// 判断用户表权限
|
||||
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
|
||||
let udr = []
|
||||
udr.push(`'${USER_INFO.user_id}'`)
|
||||
udr.push(`'${USER_INFO.role_id}'`)
|
||||
udr.push(`'${USER_INFO.organization_id}'`)
|
||||
|
||||
let columnIds = []
|
||||
TABLE_STRUCT.forEach(item => {
|
||||
if (item.is_acl == 1) {
|
||||
columnIds.push(`'${item.column_id}'`)
|
||||
}
|
||||
})
|
||||
|
||||
userAccess = SQL.query(
|
||||
DB_NAME,
|
||||
`SELECT DISTINCT column_name FROM dm_table_columns_acl WHERE column_id IN (${columnIds.split(',')}) AND access_id IN (${udr.split(',')}) AND level = 2 AND delete_time IS NULL;`,
|
||||
TABLE_INFO.table_id
|
||||
)
|
||||
}
|
||||
|
||||
var insertCols = []
|
||||
for (var i = 0; i < TABLE_STRUCT.length; i++) {
|
||||
if (TABLE_STRUCT[i].is_insert == 1) {
|
||||
if (TABLE_STRUCT[i].is_acl == 1 && !userAccess.includes(TABLE_STRUCT[i].column_name)) {
|
||||
continue
|
||||
}
|
||||
insertCols.push(TABLE_STRUCT[i].column_name)
|
||||
}
|
||||
}
|
||||
|
||||
var dataArr = []
|
||||
var dataKeyArr = Object.keys(data)
|
||||
|
||||
var dataId = ex.suid().base58()
|
||||
|
||||
var cols = []
|
||||
var values = []
|
||||
// var cols = [COLUMN_KEY]
|
||||
// var cols = [COLUMN_KEY, 'proprietor_id', 'proprietor_name']
|
||||
// var values = [`'${dataId}'`]
|
||||
// var values = [`'${dataId}'`, `'${USER_INFO.user_id}'`, `'${USER_INFO.nickname}'`]
|
||||
let newItem
|
||||
|
||||
// 如果table_struct 有proprietor_id字段,则自动添加
|
||||
if (TABLE_STRUCT.find(item => item.column_name === 'proprietor_id')) {
|
||||
cols.push('proprietor_id')
|
||||
values.push(`'${USER_INFO.user_id}'`)
|
||||
cols.push('proprietor_name')
|
||||
values.push(`'${USER_INFO.nickname}'`)
|
||||
}
|
||||
|
||||
for (let i = 0; i < dataKeyArr.length; i++) {
|
||||
newItem = TABLE_STRUCT.find(itemT => itemT.field_name === dataKeyArr[i])
|
||||
if (!newItem) {
|
||||
console.error('未找到字段:', dataKeyArr[i])
|
||||
return errMsg(404, '未找到字段')
|
||||
}
|
||||
if (insertCols.includes(newItem.column_name)) {
|
||||
cols.push(newItem.column_name)
|
||||
values.push('?')
|
||||
|
||||
if (newItem.column_name == COLUMN_KEY) {
|
||||
if (data[dataKeyArr[i]]) {
|
||||
dataId = data[dataKeyArr[i]]
|
||||
} else {
|
||||
data[dataKeyArr[i]] = dataId
|
||||
}
|
||||
}
|
||||
|
||||
if (!data[dataKeyArr[i]] && newItem?.default_value) {
|
||||
data[dataKeyArr[i]] = newItem.default_value
|
||||
}
|
||||
|
||||
if (data[dataKeyArr[i]] == "suid") {
|
||||
dataArr.push(ex.suid().base58())
|
||||
}
|
||||
|
||||
dataArr.push(data[dataKeyArr[i]])
|
||||
}
|
||||
}
|
||||
|
||||
if (!cols.includes(COLUMN_KEY)) {
|
||||
cols.push(COLUMN_KEY)
|
||||
values.push('?')
|
||||
dataArr.push(dataId)
|
||||
}
|
||||
|
||||
for (var i = 0; i < TABLE_STRUCT.length; i++) {
|
||||
if (TABLE_STRUCT[i].is_required == 1 && !cols.includes(TABLE_STRUCT[i].column_name)) {
|
||||
return errMsg(507, `未填写必选项:${TABLE_STRUCT[i].column_name}`)
|
||||
}
|
||||
}
|
||||
|
||||
stmt = `INSERT INTO ${TABLE_NAME}(${cols.join(',')}) VALUES (${values.join(',')})`
|
||||
if (TABLE_INFO.before_hook) {
|
||||
result = runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
payload.add.header("sql_exec", stmt)
|
||||
result = SQL.exec(DB_NAME, stmt, ...dataArr)
|
||||
if (TABLE_INFO.after_hook) {
|
||||
runtime.call(TABLE_INFO.after_hook)
|
||||
}
|
||||
if (!result) {
|
||||
console.error('添加失败')
|
||||
return errMsg(507, '添加失败')
|
||||
}
|
||||
|
||||
stmt = `SELECT access_id,level FROM dm_tables_acl WHERE table_id =? AND delete_time IS NULL`
|
||||
if (TABLE_INFO.before_hook) {
|
||||
result = runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
SQL_STMTS.push(stmt)
|
||||
result = SQL.query('system_sql', stmt, TABLE_INFO.table_id)
|
||||
if (TABLE_INFO.after_hook) {
|
||||
runtime.call(TABLE_INFO.after_hook)
|
||||
}
|
||||
|
||||
if (result) {
|
||||
result.forEach(item => {
|
||||
switch (item.access_id) {
|
||||
case 'self':
|
||||
item.access_id = USER_INFO.user_id
|
||||
break
|
||||
case 'role':
|
||||
item.access_id = USER_INFO.role_id
|
||||
break
|
||||
case 'dept':
|
||||
item.access_id = USER_INFO.organization_id
|
||||
break
|
||||
}
|
||||
if (TABLE_INFO.before_hook) {
|
||||
runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
SQL.push('system_sql', `INSERT INTO sys_data_acl (data_id,access_id,level,proprietor_id,proprietor_name) VALUES (?,?,?,?,?)`, dataId, item.access_id, item.level, USER_INFO.user_id, USER_INFO.nickname)
|
||||
})
|
||||
} else {
|
||||
if (TABLE_INFO.before_hook) {
|
||||
runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
SQL.push('system_sql', `INSERT INTO sys_data_acl (data_id,access_id,level,proprietor_id,proprietor_name) VALUES (?,?,?,?,?)`, dataId, item.user_id, 3, USER_INFO.user_id, USER_INFO.nickname)
|
||||
}
|
||||
return okMsg(dataId)
|
||||
}
|
||||
210
server/api/dm/sql/list.js
Normal file
210
server/api/dm/sql/list.js
Normal file
@@ -0,0 +1,210 @@
|
||||
// 分页数据
|
||||
function genSelectData(chooseData, placeholdersObj) {
|
||||
const newPlaceholders = placeholdersObj.placeholders;
|
||||
if (newPlaceholders != '') {
|
||||
const tempParams = placeholdersObj.tempParams;
|
||||
return {
|
||||
query: `SELECT ${chooseData} FROM ${TABLE_NAME} WHERE ${newPlaceholders}`,
|
||||
params: [...tempParams],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT ${chooseData} FROM ${TABLE_NAME} WHERE 1`,
|
||||
params: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 列表查询总数居
|
||||
function genCountTotal(placeholdersObj) {
|
||||
const newPlaceholders = placeholdersObj.placeholders;
|
||||
if (newPlaceholders != '') {
|
||||
const tempParams = placeholdersObj.tempParams;
|
||||
return {
|
||||
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE ${newPlaceholders}`,
|
||||
params: [...tempParams],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE 1`,
|
||||
params: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 分页列表查询
|
||||
function genListPage(chooseData, placeholdersObj) {
|
||||
// 总数据
|
||||
const ROWS_COUNT = genCountTotal(placeholdersObj);
|
||||
ROWS_COUNT.query += searchStr
|
||||
stmt = ROWS_COUNT.query
|
||||
|
||||
if (TABLE_INFO.before_hook) {
|
||||
runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
|
||||
SQL_STMTS.push(stmt)
|
||||
result = SQL.query(DB_NAME, stmt, ...ROWS_COUNT.params);
|
||||
|
||||
if (TABLE_INFO.after_hook) {
|
||||
runtime.call(TABLE_INFO.after_hook)
|
||||
}
|
||||
|
||||
// 总数据
|
||||
const count = result
|
||||
// 分页数据
|
||||
const LIST_DATA = genSelectData(chooseData, placeholdersObj);
|
||||
if (parseInt(params.pageSize) > 1000) {
|
||||
response.status.code(413)
|
||||
return false
|
||||
}
|
||||
|
||||
if (params.pageNum && params.pageSize) {
|
||||
searchStr += ` LIMIT ${(params.pageNum - 1) * parseInt(params.pageSize)}, ${parseInt(params.pageSize)}`
|
||||
}
|
||||
LIST_DATA.query += searchStr
|
||||
|
||||
stmt = LIST_DATA.query
|
||||
|
||||
// console.log(LIST_DATA.query)
|
||||
|
||||
if (TABLE_INFO.before_hook) {
|
||||
runtime.call(TABLE_INFO.before_hook)
|
||||
}
|
||||
|
||||
SQL_STMTS.push(stmt)
|
||||
result = SQL.query(DB_NAME, stmt, ...LIST_DATA.params);
|
||||
|
||||
if (!result) return;
|
||||
|
||||
const coverRes = result.sort(compareSorts)
|
||||
result = { result: coverRes, count: count[0].count, pageNum: params.pageNum, pageSize: params.pageSize }
|
||||
|
||||
if (TABLE_INFO.after_hook) {
|
||||
runtime.call(TABLE_INFO.after_hook)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 获取sql查询列表
|
||||
function sqlGenList() {
|
||||
let userAccess // 判断用户表权限
|
||||
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
|
||||
let udr = []
|
||||
udr.push(`'${USER_INFO.user_id}'`)
|
||||
udr.push(`'${USER_INFO.role_id}'`)
|
||||
udr.push(`'${USER_INFO.organization_id}'`)
|
||||
|
||||
let columnIds = []
|
||||
TABLE_STRUCT.forEach(item => {
|
||||
if (item.is_acl == 1) {
|
||||
columnIds.push(`'${item.column_id}'`)
|
||||
}
|
||||
if (item.search_value) {
|
||||
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
|
||||
}
|
||||
if (item.query_type == "NAN" || item.query_type == "VAL") {
|
||||
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]}`
|
||||
}
|
||||
})
|
||||
|
||||
userAccess = SQL.query(
|
||||
DB_NAME,
|
||||
`SELECT DISTINCT column_name FROM dm_table_columns_acl WHERE column_id IN (${columnIds.split(',')}) AND access_id IN (${udr.split(',')}) AND level > 0 AND delete_time IS NULL;`,
|
||||
TABLE_INFO.table_id
|
||||
)
|
||||
} else {
|
||||
TABLE_STRUCT.forEach(item => {
|
||||
if (item.search_value) {
|
||||
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
|
||||
}
|
||||
if (item.query_type == "NAN" || item.query_type == "VAL") {
|
||||
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]}`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var cols = []
|
||||
var groups = []
|
||||
var orders = []
|
||||
var distincts = []
|
||||
for (var i = 0; i < TABLE_STRUCT.length; i++) {
|
||||
if (TABLE_STRUCT[i].is_list == 1) {
|
||||
if (TABLE_STRUCT[i].is_acl == 1 && !userAccess.includes(TABLE_STRUCT[i].column_name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 运算规则 0 SUM 1 COUNT 2 AVG 3 MAX 4 MIN 5 VARIANCE 6 STDDEV 7 GROUP_CONCAT
|
||||
switch (TABLE_STRUCT[i].calc_type) {
|
||||
case 0:
|
||||
cols.push(`SUM(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
case 1:
|
||||
cols.push(`COUNT(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
case 2:
|
||||
cols.push(`AVG(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
case 3:
|
||||
cols.push(`MAX(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
case 4:
|
||||
cols.push(`MIN(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
case 5:
|
||||
cols.push(`VARIANCE(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
case 6:
|
||||
cols.push(`STDDEV(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
case 7:
|
||||
cols.push(`GROUP_CONCAT(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
|
||||
break
|
||||
default:
|
||||
cols.push(TABLE_STRUCT[i].column_name + ' as ' + TABLE_STRUCT[i].field_name)
|
||||
break
|
||||
}
|
||||
|
||||
if (TABLE_STRUCT[i].is_distinct == 1) {
|
||||
distincts.push(`${TABLE_STRUCT[i].field_name}`)
|
||||
}
|
||||
|
||||
if (TABLE_STRUCT[i].is_group == 1) {
|
||||
groups.push(`${TABLE_STRUCT[i].field_name}`)
|
||||
}
|
||||
|
||||
if (TABLE_STRUCT[i].is_order == 1) {
|
||||
orders.push(`${TABLE_STRUCT[i].field_name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chooseData = cols.join(',');
|
||||
if (chooseData == '') {
|
||||
return errMsg(507, '未设置列表字段')
|
||||
}
|
||||
|
||||
if (TABLE_INFO.is_distinct == 1) {
|
||||
chooseData = `DISTINCT ${chooseData}`
|
||||
}
|
||||
|
||||
distincts = distincts.join(',')
|
||||
if (distincts != '') {
|
||||
chooseData = `DISTINCT ${distincts}`
|
||||
}
|
||||
|
||||
groups = groups.join(',')
|
||||
if (groups != '') {
|
||||
searchStr += ` GROUP BY ${groups}`
|
||||
}
|
||||
|
||||
orders = orders.join(',')
|
||||
if (orders != '') {
|
||||
searchStr += ` ORDER BY ${orders} ${TABLE_INFO.order_type == 1 ? 'DESC' : 'ASC'}`
|
||||
}
|
||||
|
||||
const placeholdersObj = formatCondition();
|
||||
const res = genListPage(chooseData, placeholdersObj);
|
||||
return okMsg(res);
|
||||
}
|
||||
94
server/api/dm/sql/sql.js
Normal file
94
server/api/dm/sql/sql.js
Normal file
@@ -0,0 +1,94 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:core - gen - sql
|
||||
// 生成日期:2024-02-19 00:24:00 +0800 CST
|
||||
// 生成路径: dm/sql/sql.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:定义全局通用的SQL的CURD功能封装,包含分页列表和模版查询、数据增删等等。(注册于global.js内)
|
||||
// ==========================================================================
|
||||
|
||||
// 系统的sql数据库连接名为 system_sql
|
||||
|
||||
// 连接测试数据库
|
||||
// var test_sql = SQL.new(
|
||||
// 'mydb',
|
||||
// 'mysql',
|
||||
// 'root:123456@tcp(127.0.0.1:3306)/test_db'
|
||||
// )
|
||||
|
||||
var stmt
|
||||
var result
|
||||
let searchStr = ''
|
||||
|
||||
'use api/dm/struct/table.js'
|
||||
|
||||
'use api/dm/sql/utils.js'
|
||||
|
||||
'use api/dm/sql/data.js'
|
||||
'use api/dm/sql/list.js'
|
||||
'use api/dm/sql/delete.js'
|
||||
'use api/dm/sql/insert.js'
|
||||
'use api/dm/sql/update.js'
|
||||
|
||||
'use api/dm/sql/formatCondition.js'
|
||||
'use api/lib/requestQueryFormatSingle.js'
|
||||
'use api/lib/camelAndUnderscore.js'
|
||||
|
||||
// 基本的CURD功能
|
||||
function base_curd() {
|
||||
payload.set.header("Databus-Time", new Date().getTime())
|
||||
|
||||
let userAccess
|
||||
// 判断用户表权限
|
||||
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
|
||||
let udr = []
|
||||
udr.push(`'${USER_INFO.user_id}'`)
|
||||
udr.push(`'${USER_INFO.organization_id}'`)
|
||||
|
||||
userAccess = SQL.query(
|
||||
'system_sql',
|
||||
`SELECT MAX(level) as level FROM dm_tables_acl WHERE table_id =? AND access_id IN (${udr.split(',')}) AND delete_time IS NULL;`,
|
||||
TABLE_INFO.table_id
|
||||
)
|
||||
|
||||
if (userAccess) {
|
||||
userAccess = userAccess[0].level
|
||||
} else {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
}
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
if (TABLE_INFO.is_acl && userAccess < 1) {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
if (TABLE_INFO.is_acl && !getAclData(payload.get().query[COLUMN_KEY], 1)) {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
return sqlGenSelect()
|
||||
case 'PUT':
|
||||
if (TABLE_INFO.is_acl && userAccess < 2) {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
if (TABLE_INFO.is_acl && !getAclData(tableId, 1)) {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
return sqlGenUpdate()
|
||||
case 'POST':
|
||||
if (TABLE_INFO.is_acl && userAccess < 2) {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
return sqlGenInsert()
|
||||
case 'DELETE':
|
||||
if (TABLE_INFO.is_acl && userAccess < 3) {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
if (TABLE_INFO.is_acl && !getAclData(tableId, 3)) {
|
||||
return errMsg(403, '无权限')
|
||||
}
|
||||
return sqlGenDelete()
|
||||
default:
|
||||
response.status.notFound()
|
||||
return `404 page not found`
|
||||
}
|
||||
}
|
||||
34
server/api/dm/sql/sqlBaseCurd.js
Normal file
34
server/api/dm/sql/sqlBaseCurd.js
Normal file
@@ -0,0 +1,34 @@
|
||||
var params
|
||||
|
||||
function main() {
|
||||
// APPID认证
|
||||
'use api/lib/userPassport.js'
|
||||
|
||||
if (!payload.get().query.t) {
|
||||
return errMsg(507, '数据源不存在')
|
||||
}
|
||||
var tableId = payload.get().query.t
|
||||
|
||||
let startTime = new Date().getTime()
|
||||
TABLE_INFO = getTableMeta(tableId)
|
||||
if (!TABLE_INFO) {
|
||||
return errMsg(507, '数据源不存在')
|
||||
}
|
||||
|
||||
DB_NAME = TABLE_INFO.db_name
|
||||
TABLE_NAME = TABLE_INFO.table_name
|
||||
COLUMN_KEY = TABLE_INFO.pk_column
|
||||
ALLOW_PUBLIC_ACCESS = TABLE_INFO.is_public == 1
|
||||
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
TABLE_STRUCT = getTableStruct(tableId)
|
||||
|
||||
// 格式化参数
|
||||
params = formatSingleParams()
|
||||
|
||||
response.headers.add("Server-Timing", `Deserialization;dur=${new Date().getTime() - startTime}`)
|
||||
|
||||
return base_curd();
|
||||
}
|
||||
107
server/api/dm/sql/update.js
Normal file
107
server/api/dm/sql/update.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// 修改数据
|
||||
function sqlGenUpdate() {
|
||||
const commitData = payload.get().body?.toObject();
|
||||
let filter = payload.get().query[COLUMN_KEY];
|
||||
if (filter == undefined) {
|
||||
filter = payload.get().path.split("/").pop();
|
||||
}
|
||||
if (!filter) {
|
||||
return errMsg(507, "缺少主键参数");
|
||||
}
|
||||
var oldData = SQL.query(
|
||||
DB_NAME,
|
||||
`SELECT * FROM ${TABLE_NAME} WHERE ${COLUMN_KEY} = ?`,
|
||||
filter
|
||||
);
|
||||
if (!oldData || !oldData[0]) {
|
||||
return errMsg(507, "未找到原始数据");
|
||||
}
|
||||
|
||||
oldData = oldData[0];
|
||||
|
||||
const syncList = {};
|
||||
const cols = [];
|
||||
var linkBindColumn = {};
|
||||
TABLE_STRUCT.forEach((item) => {
|
||||
if (item.is_edit == 1) {
|
||||
if (
|
||||
item?.link_sync_type > 1 &&
|
||||
item?.link_db_name &&
|
||||
item?.link_table_name &&
|
||||
item?.link_column_name
|
||||
) {
|
||||
linkBindColumn = TABLE_STRUCT.find(
|
||||
(linkColumn) => linkColumn.column_name == item.link_bind_field
|
||||
);
|
||||
syncList[item.field_name] = {
|
||||
db_name: item.link_db_name,
|
||||
table_name: item.link_table_name,
|
||||
self_field: item.link_bind_field,
|
||||
link_field: item.link_column_name,
|
||||
bind_field: linkBindColumn.link_column_name,
|
||||
};
|
||||
}
|
||||
cols.push(item.column_name);
|
||||
}
|
||||
});
|
||||
|
||||
const updateArr = [];
|
||||
const dataArr = [];
|
||||
Object.keys(commitData).forEach((key) => {
|
||||
const colName = camelToUnderscore(key);
|
||||
if (cols.includes(colName)) {
|
||||
if (commitData[key] === "null" || commitData[key] === null) {
|
||||
updateArr.push(`${colName} = null`);
|
||||
} else if (commitData[key] != oldData[key]) {
|
||||
updateArr.push(`${colName} = ?`);
|
||||
dataArr.push(commitData[key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 处理同步更新
|
||||
const syncData = handleSyncUpdate(syncList, commitData, oldData);
|
||||
Object.keys(syncData).forEach((dbName) => {
|
||||
payload.add.header("sql_exec", stmt);
|
||||
SQL.transaction(dbName, syncData[dbName].join(";"))
|
||||
// if (SQL.exec(dbName, syncData[dbName].join(";"))) {
|
||||
// return errMsg(507, "同步更新失败");
|
||||
// }
|
||||
});
|
||||
|
||||
if (updateArr.length > 0) {
|
||||
const stmt = `UPDATE ${TABLE_NAME} SET ${updateArr.join(",")} WHERE ${COLUMN_KEY} = ?`;
|
||||
if (TABLE_INFO.before_hook) {
|
||||
runtime.call(TABLE_INFO.before_hook);
|
||||
}
|
||||
payload.add.header("sql_exec", stmt);
|
||||
const result = SQL.exec(DB_NAME, stmt, ...dataArr, filter);
|
||||
if (TABLE_INFO.after_hook) {
|
||||
runtime.call(TABLE_INFO.after_hook);
|
||||
}
|
||||
if (result) {
|
||||
return okMsg(result);
|
||||
}
|
||||
}
|
||||
return errMsg(507, "参数错误或无可更新字段");
|
||||
}
|
||||
|
||||
// 同步关联修改数据
|
||||
function handleSyncUpdate(syncList, commitData, oldData) {
|
||||
const syncData = {};
|
||||
Object.keys(syncList).forEach((key) => {
|
||||
if (!syncData[syncList[key].db_name]) {
|
||||
syncData[syncList[key].db_name] = [];
|
||||
}
|
||||
if (commitData[key] == "null" || commitData[key] == null) {
|
||||
syncData[syncList[key].db_name].push(
|
||||
`UPDATE ${syncList[key].table_name} SET ${syncList[key].link_field} = null WHERE ${syncList[key].bind_field} = '${oldData[syncList[key].self_field]}'`
|
||||
);
|
||||
} else if (commitData[key] != oldData[key]) {
|
||||
syncData[syncList[key].db_name].push(
|
||||
`UPDATE ${syncList[key].table_name} SET ${syncList[key].link_field} = '${commitData[key]}' WHERE ${syncList[key].bind_field} = '${oldData[syncList[key].self_field]}'`
|
||||
);
|
||||
}
|
||||
});
|
||||
return syncData;
|
||||
}
|
||||
38
server/api/dm/sql/utils.js
Normal file
38
server/api/dm/sql/utils.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**比较两个对象的 sort 属性*/
|
||||
function compareSorts(a, b) {
|
||||
// 如果 sort 属性为 null,则将其视为无穷大
|
||||
if (!a.sort || !b.sort) {
|
||||
return 0; // 不参与排序
|
||||
}
|
||||
// 比较 sort 属性
|
||||
return a.sort - b.sort;
|
||||
}
|
||||
|
||||
const sql_operator = {
|
||||
EQ: '=',
|
||||
NE: '!=',
|
||||
GT: '>',
|
||||
LT: '<',
|
||||
GTE: '>=',
|
||||
LTE: '<=',
|
||||
IN: 'IN',
|
||||
ON: 'ON',
|
||||
NOT: 'NOT',
|
||||
LIKE: 'LIKE',
|
||||
NL: 'NOT LIKE',
|
||||
NI: 'NOT IN',
|
||||
NAN: 'IS NULL',
|
||||
VAL: 'IS NOT NULL',
|
||||
BT: 'BETWEEN',
|
||||
}
|
||||
|
||||
// 通过sys_data_acl获取数据的权限
|
||||
function getAclData(dataId, level) {
|
||||
let userAccess = SQL.query(
|
||||
'system_sql',
|
||||
`SELECT 1 FROM sys_data_acl WHERE data_id =? AND level > ? AND delete_time IS NULL;`,
|
||||
dataId, level - 1
|
||||
)
|
||||
|
||||
return userAccess.length != 0
|
||||
}
|
||||
107
server/api/dm/struct/function.js
Normal file
107
server/api/dm/struct/function.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:DevGenTableColumns
|
||||
// 生成日期:2024-02-20 16:41:27 +0800 CST
|
||||
// 生成路径: apps/system/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:dm_table_columns - DevGenTableColumns
|
||||
// ==========================================================================
|
||||
|
||||
function select() {
|
||||
let value = payload.get().path.split('/').pop();
|
||||
let columns = "*";
|
||||
if (payload.get().query?.showAll) {
|
||||
SQL.cache.remove('system_sql',
|
||||
`SELECT * FROM dm_table_columns WHERE table_id = '${value}';`)
|
||||
SQL.cache.remove('system_sql',
|
||||
`SELECT * FROM dm_tables WHERE table_id = '${value}';`
|
||||
)
|
||||
} else {
|
||||
columns = `column_comment AS columnTitle,dict_type AS dictType,field_name AS fieldName,html_width AS htmlWidth,html_type AS htmlType,is_list AS isList,is_query AS isQuery,sort`;
|
||||
}
|
||||
const result = SQL.query('system_sql', `SELECT ${columns} FROM dm_table_columns WHERE delete_time IS NULL AND table_id = ?`, value);
|
||||
if (result.length) {
|
||||
const newResult = result.map((itemR) => {
|
||||
const obj = {};
|
||||
Object.keys(itemR).forEach((item) => {
|
||||
obj[underscoreToCamel(item)] = itemR[item];
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
|
||||
if (newResult) {
|
||||
return okMsg(newResult);
|
||||
}
|
||||
}
|
||||
return errMsg(507, '获取失败');
|
||||
}
|
||||
|
||||
function update() {
|
||||
const data = payload.get().body?.toObject();
|
||||
delete data.createTime;
|
||||
delete data.updateTime;
|
||||
// 验证data.tableName 和 data.columnName 只能有字母数字下划线
|
||||
if (!/^[a-zA-Z0-9_]+$/.test(data.tableName) || !/^[a-zA-Z0-9_]+$/.test(data.columnName) || !/^[a-zA-Z0-9_]+$/.test(data.dbName)) {
|
||||
return errMsg(405, '不支持的数据');
|
||||
}
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
});
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = [];
|
||||
const placeholders = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (newData[item]) {
|
||||
placeholders.push(`${item} = ?`);
|
||||
dataArr.push(newData[item]);
|
||||
// 如果在 item是 is_index 字段,并且值为 1,那么将 对应的数据表里面的字段添加到索引里面
|
||||
if (item == 'is_index' && newData[item] == 1) {
|
||||
const indexSql = `ALTER TABLE ${data.TABLE_NAME} ADD INDEX idx_x_${data.columnName} (${data.columnName})`;
|
||||
SQL.push(data.dbName, indexSql);
|
||||
}
|
||||
} else {
|
||||
placeholders.push(`${item} = NULL`);
|
||||
// 如果在 item是 is_index 字段,并且值为 0,那么将 对应的数据表里面的字段删除索引
|
||||
if (item == 'is_index' && newData[item] == 0) {
|
||||
if (t.length > 15 || c.length > 15) {
|
||||
return errMsg(413, '数据过长');
|
||||
}
|
||||
|
||||
const indexSql = `ALTER TABLE ${data.TABLE_NAME} DROP INDEX idx_x_${data.columnName}`;
|
||||
SQL.push(data.dbName, indexSql);
|
||||
}
|
||||
}
|
||||
});
|
||||
const newPlaceholders = placeholders.join(',');
|
||||
const query = `UPDATE dm_table_columns SET ${newPlaceholders} WHERE column_id = ?`;
|
||||
|
||||
const res = SQL.exec('system_sql', query, ...[...dataArr, filter]);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return select();
|
||||
case 'PUT':
|
||||
return update();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
|
||||
// response.headers.add('X-Script-Version', 'system-gentablecolumns-gen-146')
|
||||
|
||||
// var tableId = '2'
|
||||
// TABLE_NAME = 'dm_table_columns'
|
||||
// COLUMN_KEY = 'table_id'
|
||||
|
||||
// 'use api/dm/sql/sqlBaseCurdByTableId.js'
|
||||
35
server/api/dm/struct/table.js
Normal file
35
server/api/dm/struct/table.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// 查询数据表结构
|
||||
function getTableStruct(tableId) {
|
||||
let validated = validateTableId(tableId)
|
||||
if (!validated) {
|
||||
return validated
|
||||
}
|
||||
|
||||
const res = SQL.cache.query('system_sql', 180,
|
||||
`SELECT * FROM dm_table_columns WHERE table_id = '${tableId}';`)
|
||||
return res
|
||||
}
|
||||
|
||||
// 查询数据表结构
|
||||
function getTableMeta(tableId) {
|
||||
let validated = validateTableId(tableId)
|
||||
if (!validated) {
|
||||
return validated
|
||||
}
|
||||
|
||||
const res = SQL.cache.query('system_sql', 180,
|
||||
`SELECT * FROM dm_tables WHERE table_id = '${tableId}';`
|
||||
)
|
||||
if (res.length == 0) {
|
||||
return false
|
||||
}
|
||||
return res[0]
|
||||
}
|
||||
|
||||
function validateTableId(tableId) {
|
||||
// 过滤字符 tableId 规则为 0-9a-zA-Z
|
||||
if (/^[0-9a-zA-Z]+$/.test(tableId)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
20
server/api/dm/tables/function.js
Normal file
20
server/api/dm/tables/function.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// ===== 获取dm_tables表所有数据
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const result = SQL.query('system_sql', 'SELECT table_id,table_name,class_name,package_name FROM dm_tables WHERE delete_time IS NULL');
|
||||
if (result.length) {
|
||||
const newResult = result.map((itemR) => {
|
||||
const obj = {};
|
||||
Object.keys(itemR).forEach((item) => {
|
||||
obj[underscoreToCamel(item)] = itemR[item];
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
|
||||
return okMsg(newResult);
|
||||
}
|
||||
return errMsg('获取失败');
|
||||
}
|
||||
59
server/api/dm/template/function.js
Normal file
59
server/api/dm/template/function.js
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
// ==========================================================================
|
||||
// 所属模块:DevGenCode
|
||||
// 生成日期:2024-02-20 21:43:27 +0800 CST
|
||||
// 生成路径: api/dm/template/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:dm_tables - DevGenTables
|
||||
// dm_tables_column - DevGenTablesColumn
|
||||
// ==========================================================================
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const tableId = payload.get().path.split('/').pop()
|
||||
|
||||
let tablesInfo = SQL.query('system_sql', 'SELECT * FROM dm_tables WHERE table_id =?', tableId)
|
||||
if (!tablesInfo) {
|
||||
return errMsg(404, '未找到数据')
|
||||
}
|
||||
|
||||
tablesInfo = tablesInfo[0]
|
||||
|
||||
let tableInfo = Object.keys(tablesInfo).reduce((acc, key) => {
|
||||
acc[underscoreToCamel(key.toLowerCase())] = tablesInfo[key];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
let columns = SQL.query('system_sql', 'SELECT * FROM dm_table_columns WHERE table_id =?', tableId)
|
||||
if (!columns) {
|
||||
return errMsg(404, '未找到数据')
|
||||
}
|
||||
|
||||
let result = []
|
||||
columns.forEach(t => {
|
||||
const newObj = Object.keys(t).reduce((acc, key) => {
|
||||
acc[underscoreToCamel(key.toLowerCase())] = t[key];
|
||||
return acc;
|
||||
}, {});
|
||||
result.push(newObj)
|
||||
});
|
||||
|
||||
tableInfo.columns = result
|
||||
|
||||
// return okMsg(tableInfo)
|
||||
|
||||
let et = fs.read('api/dm/template/vue/edit-vue.template').toString()
|
||||
let lt = fs.read('api/dm/template/vue/list-vue.template').toString()
|
||||
|
||||
let ef = ex.template(et, tableInfo).toString()
|
||||
let lf = ex.template(lt, tableInfo).toString()
|
||||
|
||||
let res = {
|
||||
edit: ef,
|
||||
list: lf
|
||||
}
|
||||
|
||||
return okMsg(res)
|
||||
}
|
||||
177
server/api/dm/template/vue/edit-vue.template
Normal file
177
server/api/dm/template/vue/edit-vue.template
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="system-{{.businessName}}-container">
|
||||
<el-dialog v-model="state.isShowDialog" width="769px" center>
|
||||
<template #title>
|
||||
<div style="font-size: large" v-drag="['.system-{{.businessName}}-container .el-dialog', '.system-{{.businessName}}-container .el-dialog__header']">{{"{{"}}title{{"}}"}}</div>
|
||||
</template>
|
||||
<el-form
|
||||
:model="state.ruleForm"
|
||||
:rules="state.ruleRules"
|
||||
ref="ruleFormRef"
|
||||
label-width="80px"
|
||||
>
|
||||
{{- range $index, $column := .columns -}}
|
||||
{{- if and (eq $column.isInsert 1) (ne $column.isPk 1) (ne $column.fieldName "createTime") (ne $column.fieldName "updateTime") (ne $column.fieldName "deleteTime") -}}
|
||||
{{- if eq $column.htmlType "input" }}
|
||||
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
|
||||
<el-input v-model="state.ruleForm.{{$column.fieldName}}" placeholder="请输入{{$column.columnComment}}" />
|
||||
</el-form-item>
|
||||
{{- else if eq $column.htmlType "switch" }}
|
||||
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
|
||||
<el-switch v-model="state.ruleForm.{{$column.htmlField}}" />
|
||||
</el-form-item>
|
||||
{{- else if eq $column.htmlType "select" }}
|
||||
{{if ne $column.dictType ""}}
|
||||
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
|
||||
<el-select v-model="state.ruleForm.{{$column.fieldName}}" placeholder="请选择{{$column.columnComment}}">
|
||||
<el-option
|
||||
v-for="dict in state.{{$column.fieldName}}Options"
|
||||
:key="dict.dictValue"
|
||||
:value="Number(dict.dictValue)? Number(dict.dictValue) : dict.dictValue"
|
||||
:label="dict.dictLabel"
|
||||
>{{"{{"}} dict.dictLabel {{"}}"}}</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{{ end }}
|
||||
{{- else if eq $column.htmlType "radio" }}
|
||||
{{if ne $column.dictType ""}}
|
||||
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
|
||||
<el-radio-group v-model="state.ruleForm.{{$column.fieldName}}">
|
||||
<el-radio
|
||||
v-for="dict in state.{{$column.fieldName}}Options"
|
||||
:key="dict.dictValue"
|
||||
:label="dict.dictLabel"
|
||||
>{{"{{"}} dict.dictLabel {{"}}"}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
{{- end }}
|
||||
{{- else if eq $column.htmlType "datetime" }}
|
||||
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
|
||||
<el-date-picker clearable style="width: 200px"
|
||||
v-model="state.ruleForm.{{$column.fieldName}}"
|
||||
type="date" value-format="YYYY-MM-DD"
|
||||
placeholder="选择{{$column.columnComment}}">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
{{- else if eq $column.htmlType "textarea" -}}
|
||||
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
|
||||
<el-input v-model="state.ruleForm.{{$column.htmlField}}" type="textarea" placeholder="请输入{{$column.columnComment}}" />
|
||||
</el-form-item>
|
||||
{{- else if eq $column.htmlType "checkbox" }}
|
||||
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
|
||||
<el-checkbox-group v-model="state.ruleForm.{{$column.fieldName}}">
|
||||
<el-checkbox
|
||||
v-for="dict in state.{{$column.fieldName}}Options"
|
||||
:key="dict.dictValue"
|
||||
:label="dict.dictLabel"
|
||||
>{{"{{"}} dict.dictLabel {{"}}"}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :loading="state.loading">编 辑</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="edit{{.functionName}}">
|
||||
import { reactive, ref, unref, getCurrentInstance } from "vue";
|
||||
import { get,add, update } from "@/api/public/basic";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: () => "",
|
||||
},
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const ruleFormRef = ref<HTMLElement | null>(null);
|
||||
const state = reactive({
|
||||
// 是否显示弹出层
|
||||
isShowDialog: false,
|
||||
loading: false,
|
||||
ruleForm: {
|
||||
{{- range $index, $column := .columns -}}
|
||||
{{$column.fieldName}}: undefined,
|
||||
{{- end}}
|
||||
},
|
||||
{{- range $index, $column := .columns -}}
|
||||
{{- if ne $column.dictType nil }}
|
||||
// {{$column.fieldName}}Options字典数据
|
||||
{{$column.fieldName}}Options: [],
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
// 表单校验
|
||||
ruleRules: {
|
||||
{{- range $index, $column := .columns -}}
|
||||
{{- if and (eq $column.isRequired 1) ($column.columnComment)}}
|
||||
{{$column.fieldName}}: [
|
||||
{ required: true, message: "{{$column.columnComment}}不能为空", trigger: "blur" }
|
||||
],
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
},
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = (row: any) => {
|
||||
get(row.{{.pkFieldName}}, '{{.tableId}}').then((response) => {
|
||||
state.ruleForm = response.data?.length ? response.data[0] : {};
|
||||
});
|
||||
|
||||
state.isShowDialog = true;
|
||||
state.loading = false;
|
||||
{{- range $index, $column := .columns -}}
|
||||
{{- if ne $column.dictType nil }}
|
||||
proxy.getDicts("{{$column.dictType}}").then((response: any) => {
|
||||
state.{{$column.fieldName}}Options = response.data;
|
||||
});
|
||||
{{- end -}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
const closeDialog = (row?: object) => {
|
||||
proxy.mittBus.emit("onEdit{{.functionName}}Module", row);
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
// 保存
|
||||
const onSubmit = () => {
|
||||
const formWrap = unref(ruleFormRef) as any;
|
||||
if (!formWrap) return;
|
||||
formWrap.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
state.loading = true;
|
||||
if (state.ruleForm?.flowId) {
|
||||
update(state.ruleForm.{{.pkFieldName}}, state.ruleForm, '{{.tableId}}').then((response) => {
|
||||
ElMessage.success('修改成功');
|
||||
state.loading = false;
|
||||
closeDialog(state.ruleForm); // 关闭弹窗
|
||||
});
|
||||
} else {
|
||||
add(state.ruleForm, '{{.tableId}}').then((response) => {
|
||||
ElMessage.success('新增成功');
|
||||
closeDialog(state.ruleForm); // 关闭弹窗
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
340
server/api/dm/template/vue/list-vue.template
Normal file
340
server/api/dm/template/vue/list-vue.template
Normal file
@@ -0,0 +1,340 @@
|
||||
<template>
|
||||
<el-card style="margin-bottom: 20px">
|
||||
<!-- 查询 -->
|
||||
<el-form :model="state.queryParams" ref="queryForm" :inline="true">
|
||||
<el-form-item v-for="(item, index) in filteredQueryData" :label="item.columnTitle" :prop="item.fieldName"
|
||||
:key="index">
|
||||
<el-select v-if="item.htmlType == 'select'" v-model="state.queryParams[item.fieldName]"
|
||||
placeholder="请选择" clearable style="width: 240px">
|
||||
<el-option v-for="dict in state[item.fieldName + 'Option']" :key="dict.dictValue"
|
||||
:label="dict.dictLabel" :value="dict.dictValue" />
|
||||
</el-select>
|
||||
|
||||
<el-date-picker v-else-if="item.htmlType == 'datetime'" v-model="state.queryParams[item.fieldName]"
|
||||
type="date" placeholder="请选择" clearable style="width: 240px" />
|
||||
<el-input v-else v-model="state.queryParams[item.fieldName]" :placeholder="'请输入'" clearable
|
||||
@keyup.enter.native="handleQuery()" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery()">
|
||||
<SvgIcon name="elementSearch" />搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<SvgIcon name="elementRefresh" />
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<div v-loading="state.loading" style="min-height: 600px">
|
||||
<el-card class="box-card" v-if="filteredListData.length">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-header-text">{{.tableComment}}</span>
|
||||
<div>
|
||||
<el-button type="primary" plain v-auth="`{{.className}}:add`" @click="onOpenAddModule">
|
||||
<SvgIcon name="elementPlus" />新增
|
||||
</el-button>
|
||||
<el-button type="success" plain @click="onExport">
|
||||
<SvgIcon name="elementDownload" />导出
|
||||
</el-button>
|
||||
<el-button type="danger" plain v-auth="`{{.className}}:delete`" :disabled="state.multiple"
|
||||
@click="onTableRowDel">
|
||||
<SvgIcon name="elementDelete" />删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!--数据表格-->
|
||||
|
||||
<el-table :data="state.tableData" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-for="(item, index) in filteredListData" :key="index" :label="item.columnTitle"
|
||||
:width="item.htmlWidth ? item.htmlWidth : ''" :prop="item.fieldName">
|
||||
<template #default="scope" v-if="$slots[item.fieldName]">
|
||||
<slot :name="item.fieldName" :row="scope.row"></slot>
|
||||
</template>
|
||||
<template #default="scope" v-if="item.dictType">
|
||||
<el-tag type="success" disable-transitions>{{ "{{" }} findDictLabel(scope.row[item.fieldName],
|
||||
item.fieldName) || '-- --' {{ "}}" }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{ { dateStrFormat(scope.row.createTime) } }</span>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="操作" align="center" v-auth="`{{.className}}:delete` && `{{.className}}:edit`"class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center; justify-content: center; gap: 10px">
|
||||
<div>
|
||||
<el-tooltip class="box-item" effect="dark" content="编辑" placement="top-start">
|
||||
<el-button type="primary" size="small" circlesize="small" circle
|
||||
v-auth="`{{.className}}:edit`" @click="onOpenEditModule(scope.row)">
|
||||
<SvgIcon :size="12" name="elementEdit" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<slot name="listBtn" :row="scope.row"></slot>
|
||||
</div>
|
||||
<div>
|
||||
<el-tooltip class="box-item" effect="dark" content="删除" placement="top-start">
|
||||
<el-button type="danger" size="small" circle v-auth="`{{.className}}:delete`"
|
||||
@click="onTableRowDel(scope.row)">
|
||||
<SvgIcon :size="12" name="elementDelete" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页设置-->
|
||||
<div v-show="state.total > 0">
|
||||
<el-divider></el-divider>
|
||||
<el-pagination background :total="state.total" :current-page="state.queryParams.pageNum"
|
||||
:page-size="state.queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<EditModule ref="editModuleRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="Partner">
|
||||
import { reactive, ref, onMounted, getCurrentInstance, onUnmounted, computed, watch } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import EditModule from './component/editModule.vue';
|
||||
import { del, get, getStruct } from '@/api/public/basic';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const editModuleRef = ref();
|
||||
const route = useRoute();
|
||||
|
||||
const state = reactive({
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 表格数据
|
||||
tableData: [],
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 表格列数据
|
||||
tableStructData: [],
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
loading: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
// 页码
|
||||
pageNum: 1,
|
||||
// 每页大小
|
||||
pageSize: 10,
|
||||
},
|
||||
});
|
||||
|
||||
// 可搜索的字段
|
||||
const filteredQueryData = computed(() => {
|
||||
return state.tableStructData.filter((item: any) => item.isQuery) || [];
|
||||
});
|
||||
|
||||
// 列表展示字段
|
||||
const filteredListData = computed(() => {
|
||||
return state.tableStructData.filter((item: any) => item.isList) || [];
|
||||
});
|
||||
|
||||
|
||||
/** 查询列表 */
|
||||
const handleQuery = () => {
|
||||
const params = state.queryParams;
|
||||
state.loading = true;
|
||||
// 如果state.queryParams中有空值,则删除
|
||||
Object.keys(params).forEach((key) => {
|
||||
if (!params[key]) {
|
||||
delete params[key];
|
||||
}
|
||||
});
|
||||
get(params, '{{.tableId}}').then((response: any) => {
|
||||
state.tableData = response.data.result;
|
||||
state.total = response.data.count;
|
||||
state.loading = false;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 获取data struct
|
||||
getStruct('{{.tableId}}').then((res: any) => {
|
||||
if (res.status == 0) {
|
||||
const arr = res.data.map((item: any) => {
|
||||
if (item.isQuery) {
|
||||
state.queryParams[item.fieldName] = undefined;
|
||||
}
|
||||
// 如果存在字典数据
|
||||
if ((item.isQuery || item.isList) && item.dictType) {
|
||||
proxy.getDicts(item.dictType).then((response: any) => {
|
||||
state[item.fieldName + 'Option'] = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
arr.sort(compareSorts);
|
||||
state.tableStructData = arr;
|
||||
handleQuery();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
/** 字典label */
|
||||
const findDictLabel = (dictValue, fieldName) => {
|
||||
if (!state[fieldName + 'Option']) return;
|
||||
const obj = state[fieldName + 'Option'].find((item) => item.dictValue == dictValue) || {};
|
||||
return obj.dictLabel;
|
||||
};
|
||||
|
||||
/**比较两个对象的 sort 属性*/
|
||||
const compareSorts = (a: any, b: any) => {
|
||||
// 如果 sort 属性为 null,则将其视为无穷大
|
||||
if (a.sort === null || b.sort === null) {
|
||||
return 0; // 不参与排序
|
||||
}
|
||||
// 比较 sort 属性
|
||||
return a.sort - b.sort;
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
state.tableStructData.forEach((item: any) => {
|
||||
if (item.isQuery) {
|
||||
state.queryParams[item.columnName] = undefined;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val: number) => {
|
||||
state.queryParams.pageNum = val;
|
||||
handleQuery();
|
||||
};
|
||||
const handleSizeChange = (val: number) => {
|
||||
state.queryParams.pageSize = val;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 打开新增配置参数弹窗
|
||||
const onOpenAddModule = () => {
|
||||
state.title = '创建公共文件';
|
||||
editModuleRef.value.openDialog({});
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 打开编辑配置参数弹窗
|
||||
const onOpenEditModule = (row: object) => {
|
||||
state.title = '修改文件名';
|
||||
editModuleRef.value.openDialog(row);
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const onTableRowDel = (row: any) => {
|
||||
const ids = row['{{.pkFieldName}}'] ? [row['{{.pkFieldName}}']] : state.ids;
|
||||
|
||||
ElMessageBox({
|
||||
message: '是否确认删除的数据项?',
|
||||
title: '警告',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(function () {
|
||||
return del({ ['{{.pkFieldName}}']: ids }, '{{.tableId}}').then(() => {
|
||||
handleQuery();
|
||||
ElMessage.success('删除成功');
|
||||
});
|
||||
});
|
||||
};
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: any) => {
|
||||
state.ids = selection.map((item: any) => {
|
||||
return item['{{.pkFieldName}}'];
|
||||
});
|
||||
|
||||
state.single = selection.length != 1;
|
||||
state.multiple = !selection.length;
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => { });
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
proxy.mittBus.off('onEditModule');
|
||||
});
|
||||
|
||||
// 导出功能
|
||||
const onExport = async () => {
|
||||
// 判断是否有勾选项
|
||||
let list = [];
|
||||
if (state.ids && state.ids.length > 0) {
|
||||
// 有勾选项,仅导出勾选数据
|
||||
list = state.tableData.filter((row) => state.ids.includes(row.flowId));
|
||||
if (!list.length) {
|
||||
ElMessage.warning('无可导出数据');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 无勾选项,导出全部数据
|
||||
const params = { ...state.queryParams };
|
||||
Object.keys(params).forEach((key) => {
|
||||
if (!params[key]) {
|
||||
delete params[key];
|
||||
}
|
||||
});
|
||||
// 先获取总数
|
||||
const countRes = await get(params, 'hS9i1PUcWXs');
|
||||
const total = countRes.data.count;
|
||||
if (!total) {
|
||||
ElMessage.warning('无可导出数据');
|
||||
return;
|
||||
}
|
||||
// 拉取所有数据
|
||||
const allParams = { ...params, pageNum: 1, pageSize: total };
|
||||
const allRes = await get(allParams, 'hS9i1PUcWXs');
|
||||
list = allRes.data.result || [];
|
||||
}
|
||||
// 处理表头和字段
|
||||
const columns = filteredListData.value;
|
||||
const header = columns.map((col) => col.columnTitle);
|
||||
const fields = columns.map((col) => col.fieldName);
|
||||
// 处理字典类型显示
|
||||
const rows = list.map((row) => {
|
||||
return fields.map((field, idx) => {
|
||||
const col = columns[idx];
|
||||
if (col.dictType) {
|
||||
return findDictLabel(row[field], field) || '';
|
||||
}
|
||||
return row[field] !== undefined ? row[field] : '';
|
||||
});
|
||||
});
|
||||
// 生成CSV内容
|
||||
let csvContent = '\uFEFF' + header.join(',') + '\n';
|
||||
rows.forEach((row) => {
|
||||
csvContent += row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',') + '\n';
|
||||
});
|
||||
// 下载CSV
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = "{{.tableComment}}" + " - " + new Date().toLocaleString().replace(/\//g, '-') + ' - 导出数据' + '.csv';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
</script>
|
||||
19
server/api/event/broadcast/function.js
Normal file
19
server/api/event/broadcast/function.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:event - broadcast
|
||||
// 生成日期:2024/10/16
|
||||
// 生成路径: /api/event/broadcast/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:system
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'broadcast-24.10.20')
|
||||
|
||||
function main() {
|
||||
let event = payload.get().query.event
|
||||
let data = payload.get().body?.toString()
|
||||
if (sse.broadcast(event, data)) {
|
||||
return okMsg(true)
|
||||
} else {
|
||||
return errMsg(408, false)
|
||||
}
|
||||
}
|
||||
39
server/api/event/heartbeat/function.js
Normal file
39
server/api/event/heartbeat/function.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:event - heartbeat
|
||||
// 生成日期:2024/10/16
|
||||
// 生成路径: /api/event/heartbeat/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:system
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'heartbeat-24.10.20')
|
||||
|
||||
function main() {
|
||||
// 取 Authorization - Bearer 后面的Token值
|
||||
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookie ? payload.get().cookie["token"] : null);
|
||||
if (!userToken) {
|
||||
return errMsg(413, "未登录");
|
||||
}
|
||||
// 判断userToken是否是数组
|
||||
if (Array.isArray(userToken)) {
|
||||
userToken = userToken[0]
|
||||
}
|
||||
|
||||
// 删除 Bearer 部分
|
||||
userToken = userToken.replace("Bearer ", "").replace(/\s/g, "");
|
||||
let tokenInfo = jwt_parse(userToken) // 这里需要密钥进行验证
|
||||
if (!tokenInfo) {
|
||||
return errMsg(413, "身份验证失败");
|
||||
}
|
||||
userToken = crypto.hash.md5(userToken).toHEX.encoding().toString()
|
||||
let sessions = cache.get('session', tokenInfo.uid)
|
||||
if (sessions) {
|
||||
if (!sessions.includes(userToken)) {
|
||||
sessions.push(userToken)
|
||||
}
|
||||
} else {
|
||||
sessions = [userToken]
|
||||
}
|
||||
cache.set('session', tokenInfo.uid, sessions, 30 * 60 * 1000)
|
||||
return okMsg(true);
|
||||
}
|
||||
117
server/api/event/jy/function.js
Normal file
117
server/api/event/jy/function.js
Normal file
@@ -0,0 +1,117 @@
|
||||
let test_data = {
|
||||
"info": {
|
||||
"id": "202509231006",
|
||||
"name": "铁力路站~友谊路站测试巡查任务",
|
||||
"time": "2025-09-23 10:06:57",
|
||||
"timestamp": 1758593217.160917,
|
||||
"type": 0
|
||||
},
|
||||
"line": {
|
||||
"code": "03",
|
||||
"interval": "铁力路~友谊路",
|
||||
"interval_code": "03001",
|
||||
"name": "地铁3号线",
|
||||
"status": 1
|
||||
},
|
||||
"risk": {
|
||||
"content": "发现1个吊车",
|
||||
"id": "20250923100600003",
|
||||
"level": 2,
|
||||
"mark": {
|
||||
"left": [
|
||||
1062,
|
||||
1102
|
||||
],
|
||||
"right": [
|
||||
815,
|
||||
926
|
||||
]
|
||||
},
|
||||
"photo": {
|
||||
"origin": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/202509231006/S202509231006-1.jpg",
|
||||
"result": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/202509231006/R202509231006-1.jpg"
|
||||
},
|
||||
"time": "2025-09-23 10:07:19",
|
||||
"timestamp": 1758593239.038757,
|
||||
"type": 1011,
|
||||
"video": {
|
||||
"origin": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/S202509231006.mp4",
|
||||
"result": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/R202509231006.mp4"
|
||||
},
|
||||
"wgs84": {
|
||||
"x": 31.40948546,
|
||||
"y": 121.459669216064
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 目标类别
|
||||
// {
|
||||
// "Crane": "吊车",
|
||||
// "Digger": "挖掘机",
|
||||
// "Mound": "堆土",
|
||||
// "Steel": "施工钢材",
|
||||
// "TowerCrane": "塔吊",
|
||||
// "Bulldozer": "推土车",
|
||||
// "PileFoundation": "打桩机",
|
||||
// "Tankcar": "罐车",
|
||||
// "Grouting": "灌浆车",
|
||||
// "Roller": "压路车",
|
||||
// "Construction": "疑似施工机具",
|
||||
// "Gantry": "龙门架",
|
||||
// "Ladder": "登高车"
|
||||
// }
|
||||
function main() {
|
||||
let data = payload.get()?.body?.toObject()
|
||||
// 判断data的数据结构是否和test_data匹配
|
||||
if (data && data.info && data.risk && data.line) {
|
||||
const { name, time: findTime,id:taskId } = data.info || {}
|
||||
const { interval_code:stationId } = data.line || {}
|
||||
const { wgs84, photo,level,type } = data.risk || {}
|
||||
const extra = JSON.stringify(data)
|
||||
if (name && findTime && wgs84 && wgs84.lng && wgs84.lat && taskId && stationId) {
|
||||
const id = ex.suid().base58()
|
||||
let fileId
|
||||
if (photo?.origin) {
|
||||
const img = photo.result
|
||||
// 存储图片
|
||||
fileId = ex.suid().base58()
|
||||
const fileRes = SQL.exec('system_sql', `INSERT INTO sys_files (file_id, file_path,file_name) VALUES (?, ?,?)`, fileId, img, '标注图片.png')
|
||||
if (!fileRes) {
|
||||
return errMsg(500, '图片存储失败')
|
||||
}
|
||||
}
|
||||
// 插入事件表
|
||||
const res = SQL.exec('system_sql', `INSERT INTO dr_event (name, find_time, longitude, latitude,extra,proprietor_id,
|
||||
proprietor_name,
|
||||
station_id,
|
||||
task_id,
|
||||
id
|
||||
${fileId ? ',file_img' : ''}
|
||||
${level||level==0 ? ',abnormal_level' : ''}
|
||||
${type ? ',aim_type' : ''}
|
||||
) VALUES (?, ?, ?, ?,?,?,?,?,?,? ${fileId ? ',?' : ''} ${level||level==0 ? ',?' : ''} ${type ? ',?' : ''})`,
|
||||
name, findTime, wgs84.lng, wgs84.lat, extra, '1', '系统推送',
|
||||
stationId, taskId,
|
||||
id,
|
||||
fileId ? fileId : null,
|
||||
level||level==0 ? level : null,
|
||||
type ? type : null
|
||||
)
|
||||
if (res) {
|
||||
return okMsg(true)
|
||||
}
|
||||
}
|
||||
// 记录缺失字段
|
||||
const miss = []
|
||||
if (!name) miss.push('name')
|
||||
if (!findTime) miss.push('time')
|
||||
if (!wgs84) miss.push('wgs84')
|
||||
if (!wgs84.lat) miss.push('wgs84.lat')
|
||||
if (!wgs84.lng) miss.push('wgs84.lng')
|
||||
if (!taskId) miss.push('taskId')
|
||||
if (!stationId) miss.push('stationId')
|
||||
return errMsg(400, `缺少字段: ${miss.join(', ')}`)
|
||||
}
|
||||
return errMsg(400, "数据结构错误")
|
||||
}
|
||||
20
server/api/event/push/function.js
Normal file
20
server/api/event/push/function.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:event - push
|
||||
// 生成日期:2024/10/16
|
||||
// 生成路径: /api/event/push/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:system
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'pusher-24.10.20')
|
||||
|
||||
function main() {
|
||||
let sid = payload.get().path.split('/').pop()
|
||||
let event = payload.get().query.event
|
||||
let data = payload.get().body?.toString()
|
||||
if (sse.send(sid, event, data)) {
|
||||
return okMsg(true)
|
||||
} else {
|
||||
return errMsg(408, false)
|
||||
}
|
||||
}
|
||||
26
server/api/event/user/function.js
Normal file
26
server/api/event/user/function.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:event - user
|
||||
// 生成日期:2024/10/16
|
||||
// 生成路径: /api/event/user/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:system
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'user-24.10.20')
|
||||
|
||||
function main() {
|
||||
let uid = payload.get().path.split('/').pop()
|
||||
let event = payload.get().query.event
|
||||
let data = payload.get().body?.toString()
|
||||
|
||||
let sessions = cache.get('session', uid)
|
||||
for (let i in sessions) {
|
||||
if (!sse.send(sessions[i], event, data)) {
|
||||
// sse.send 失败,可能是 session 过期,删除该 session
|
||||
delete sessions[i]
|
||||
cache.set('session', uid, sessions)
|
||||
}
|
||||
}
|
||||
|
||||
return okMsg(true)
|
||||
}
|
||||
36
server/api/f/function.js
Normal file
36
server/api/f/function.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:fn
|
||||
// 生成日期:2024/10/16
|
||||
// 生成路径: /api/x/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:system
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'fn-24.10.20')
|
||||
|
||||
function main() {
|
||||
// APPID认证
|
||||
'use api/lib/userPassport.js'
|
||||
|
||||
// runtime.exec('api/x/hook.js', ...args)
|
||||
|
||||
let fnId = payload.get().query.f
|
||||
|
||||
// let app = SQL.cache.query(DB_NAME, 1800, 'SELECT * FROM fn_apps WHERE fn_id =?', fnId)
|
||||
let app = SQL.query(DB_NAME, 'SELECT * FROM fn_apps WHERE delete_time IS NULL AND status =1 AND (fn_id =? OR alias =?);', fnId, fnId)
|
||||
if (!app) {
|
||||
return errMsg(500, "逻辑函数不存在")
|
||||
}
|
||||
app = app[0]
|
||||
TABLE_INFO.cache_life == app?.cache_life || 0
|
||||
if (app?.cache_life == 0) {
|
||||
// 声明缓存
|
||||
payload.set.header("Cache-Control", ['skip-cache'])
|
||||
response.headers.set(
|
||||
'Cache-Control',
|
||||
'no-transform,no-cache,immutable'
|
||||
)
|
||||
}
|
||||
|
||||
return runtime.call(app.code)
|
||||
}
|
||||
37
server/api/f/test/function.js
Normal file
37
server/api/f/test/function.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:fn
|
||||
// 生成日期:2024/10/16
|
||||
// 生成路径: /api/x/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:system
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'fn-24.10.20')
|
||||
|
||||
function main() {
|
||||
// APPID认证
|
||||
'use api/lib/userPassport.js'
|
||||
|
||||
// runtime.exec('api/x/hook.js', ...args)
|
||||
|
||||
let fnId = payload.get().query.f
|
||||
|
||||
// let app = SQL.cache.query(DB_NAME, 1800, 'SELECT * FROM fn_apps WHERE fn_id =?', fnId)
|
||||
let app = SQL.query(DB_NAME, 'SELECT * FROM fn_apps WHERE delete_time IS NULL AND (fn_id =? OR alias =?);', fnId, fnId)
|
||||
if (!app) {
|
||||
return errMsg(500, "逻辑函数不存在")
|
||||
}
|
||||
DB_NAME = 'develop_sql'
|
||||
app = app[0]
|
||||
|
||||
if (!app.cache_life || app.cache_life == 0) {
|
||||
// 声明缓存
|
||||
payload.set.header("Cache-Control", ['skip-cache'])
|
||||
response.headers.set(
|
||||
'Cache-Control',
|
||||
'no-transform,no-cache,immutable'
|
||||
)
|
||||
}
|
||||
|
||||
return runtime.call(`${app.code};main()`)
|
||||
}
|
||||
63
server/api/files/function.js
Normal file
63
server/api/files/function.js
Normal file
@@ -0,0 +1,63 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:files
|
||||
// 生成日期:2024-02-08 17:09:21 +0800 CST
|
||||
// 生成路径: files/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:sys_files - 文件管理
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'uploads-1.0');
|
||||
// DB_NAME = 'system_sql'
|
||||
|
||||
|
||||
function add() {
|
||||
const params = payload.get().body?.toObject();
|
||||
if (params.isCatalogue) {
|
||||
// 目录
|
||||
const sql = `INSERT INTO sys_files (file_id,file_name,privacy,is_folder,parent_id, proprietor_id, proprietor_name) VALUES (?, ?, ?, ?, ?, ?, ?);`;
|
||||
const res = SQL.exec('system_sql', sql, ex.suid().base58(), params.name, params.isPublic, params.isCatalogue, params.parentId, USER_INFO.user_id, USER_INFO.nickname);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
function list() {
|
||||
const params = payload.get().query;
|
||||
if (params && params.isPublic == 0) {
|
||||
// 非公共文件
|
||||
const res = SQL.query('system_sql', `SELECT * FROM sys_files WHERE delete_time IS NULL AND privacy = 0 AND proprietor_id = ?`, params.userId);
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
const res = SQL.query('system_sql', `SELECT * FROM sys_files WHERE delete_time IS NULL AND (privacy != 0 OR privacy IS NULL)`);
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
|
||||
function update() {
|
||||
const params = payload.get().body?.toObject();
|
||||
const res = SQL.exec('system_sql', `UPDATE sys_files SET file_name = ? WHERE file_id = ?`, params.name, params.id);
|
||||
return res;
|
||||
}
|
||||
|
||||
function del() {
|
||||
const fileId = payload.get().query.fileId;
|
||||
const res = SQL.exec('system_sql', `UPDATE sys_files SET delete_time = NOW() WHERE file_id = ?`, fileId);
|
||||
return res;
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return list();
|
||||
case 'POST':
|
||||
return add();
|
||||
case 'PUT':
|
||||
return update();
|
||||
case 'DELETE':
|
||||
return del();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
106
server/api/fn/function.js
Normal file
106
server/api/fn/function.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/** 页面管理基础接口 **/
|
||||
|
||||
TABLE_NAME = 'fn_apps';
|
||||
COLUMN_KEY = 'fn_id';
|
||||
TABLE_FIELDS = '*';
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js';
|
||||
switch (payload.get().method) {
|
||||
case "GET":
|
||||
return list();
|
||||
case "POST":
|
||||
return insert();
|
||||
case "PUT":
|
||||
return sql_update();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'name') {
|
||||
placeholders.push(`name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
function list() {
|
||||
const queryPa = payload.get().query;
|
||||
if (!Object.keys(queryPa).length) {
|
||||
// 根据id查询详情
|
||||
var useParent = scriptPath != payload.get().path.substring(1);
|
||||
if (useParent) {
|
||||
let value = payload.get().path.split('/').pop();
|
||||
const result = SQL.query(
|
||||
'system_sql',
|
||||
`SELECT * FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
|
||||
value
|
||||
);
|
||||
if (result.length) {
|
||||
return okMsg(result);
|
||||
}
|
||||
return errMsg('获取失败');
|
||||
}
|
||||
}
|
||||
// 格式化这个query参数
|
||||
let newData = {};
|
||||
Object.keys(queryPa).forEach((item) => {
|
||||
newData[item] = queryPa[item][0];
|
||||
});
|
||||
|
||||
// 总数据
|
||||
const COUNT_TOTAL = countTotal(newData);
|
||||
const total = SQL.query('system_sql', COUNT_TOTAL.query, ...COUNT_TOTAL.params);
|
||||
// 分页数据
|
||||
const LIST_DATA = selectData(newData);
|
||||
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
|
||||
|
||||
return okMsg({ result, count: total[0].count, pageNum: Number(newData.pageNum), pageSize: Number(newData.pageSize) });
|
||||
}
|
||||
|
||||
function insert() {
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = { ...data };
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = Object.values(newData);
|
||||
const placeholders = [];
|
||||
|
||||
columnsArr.push(COLUMN_KEY)
|
||||
columnsArr.push('proprietor_id')
|
||||
columnsArr.push('proprietor_name')
|
||||
|
||||
|
||||
const suid = ex.suid().base58()
|
||||
dataArr.push(suid)
|
||||
dataArr.push(USER_INFO.user_id)
|
||||
dataArr.push(USER_INFO.nickname)
|
||||
|
||||
dataArr.forEach(() => {
|
||||
placeholders.push('?');
|
||||
});
|
||||
placeholders.join(',');
|
||||
|
||||
const columnsStr = columnsArr.join(',');
|
||||
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
|
||||
const res = SQL.exec('system_sql', query, ...dataArr);
|
||||
if (res) {
|
||||
return okMsg(suid);
|
||||
}
|
||||
return errMsg(507, '添加失败');
|
||||
}
|
||||
21
server/api/lib/camelAndUnderscore.js
Normal file
21
server/api/lib/camelAndUnderscore.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// 将驼峰命名改为下划线
|
||||
function camelToUnderscore(str) {
|
||||
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
|
||||
}
|
||||
|
||||
// 将下划线改为驼峰命名
|
||||
function underscoreToCamel(str) {
|
||||
return str.replace(/_([a-z])/g, function (match, letter) {
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
// 将首字母转换为大写
|
||||
function capitalizeFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
// 将首字母转换为小写
|
||||
function lowercaseFirstLetter(str) {
|
||||
return str.charAt(0).toLowerCase() + str.slice(1);
|
||||
}
|
||||
11
server/api/lib/debugSetting.js
Normal file
11
server/api/lib/debugSetting.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:lib - debugSetting
|
||||
// 生成日期:2024-02-19 00:24:00 +0800 CST
|
||||
// 生成路径: lib/debugSetting.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:用于定义调试模式的一些配置策略。
|
||||
// ==========================================================================
|
||||
|
||||
// console.warn('@ CURRENT SERVICE RUNNING DEBUG MODE @')
|
||||
// runtime.cache.purge()
|
||||
runtime.cache.disable()
|
||||
262
server/api/lib/func_api_common/gorm.js
Normal file
262
server/api/lib/func_api_common/gorm.js
Normal file
@@ -0,0 +1,262 @@
|
||||
function formatDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const hours = String(date.getHours()).padStart(2, "0");
|
||||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
const seconds = String(date.getSeconds()).padStart(2, "0");
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
class GormCommonClass {
|
||||
constructor(props) {
|
||||
this.wheres = [];
|
||||
this.values = [];
|
||||
this.tabName = props.tabName;
|
||||
}
|
||||
|
||||
getWhereStr() {
|
||||
if (this.wheres.length) {
|
||||
return ` WHERE ${this.wheres.join(" AND ")}`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
where(w, ...vals) {
|
||||
this.wheres.push(w);
|
||||
this.values.push(...vals);
|
||||
return this;
|
||||
}
|
||||
|
||||
verifyLen(sql, vals) {
|
||||
const wLen = sql.split("?")?.length - 1;
|
||||
|
||||
if (wLen != vals.length) {
|
||||
throw "占位符与值的数量不匹配";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GormSelectClass extends GormCommonClass {
|
||||
#selectKeys = [];
|
||||
#tabName = "";
|
||||
|
||||
#offset;
|
||||
#limit = 1;
|
||||
|
||||
#groupBys = [];
|
||||
#orderBys = [];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.#selectKeys = props.selectKeys;
|
||||
this.#tabName = props.tabName;
|
||||
}
|
||||
|
||||
where(w, ...vals) {
|
||||
this.wheres.push(w);
|
||||
this.values.push(...vals);
|
||||
return this;
|
||||
}
|
||||
|
||||
offset(val) {
|
||||
this.#offset = Number(val) || 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
limit(val) {
|
||||
this.#limit = Number(val) || 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
orderBy(orderKey, sort) {
|
||||
this.#orderBys.push(`${orderKey} ${sort}`);
|
||||
return this;
|
||||
}
|
||||
groupBy(...agrs) {
|
||||
this.#groupBys.push(...agrs);
|
||||
return this;
|
||||
}
|
||||
|
||||
build() {
|
||||
let sqlStr = `SELECT ${this.#selectKeys.join(",")} FROM ${this.#tabName}`;
|
||||
|
||||
sqlStr += this.getWhereStr();
|
||||
|
||||
if (this.#groupBys.length) {
|
||||
sqlStr += " GROUP BY ";
|
||||
sqlStr += this.#groupBys.join(", ");
|
||||
}
|
||||
|
||||
if (this.#orderBys.length) {
|
||||
sqlStr += " ORDER BY ";
|
||||
sqlStr += this.#orderBys.join(", ");
|
||||
}
|
||||
|
||||
if (this.#limit) {
|
||||
sqlStr += ` LIMIT ${this.#limit}`;
|
||||
}
|
||||
if (this.#offset) {
|
||||
sqlStr += ` OFFSET ${this.#offset}`;
|
||||
}
|
||||
|
||||
this.verifyLen(sqlStr, this.values);
|
||||
|
||||
return {
|
||||
sql: sqlStr,
|
||||
values: this.values,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class GormUpdateClass extends GormCommonClass {
|
||||
#tabName = "";
|
||||
|
||||
#updateList = [];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.#tabName = props.tabName;
|
||||
this.#init(props.updateData || {});
|
||||
}
|
||||
|
||||
#init(updateData) {
|
||||
const updateKeys = Object.keys(updateData);
|
||||
|
||||
if (!updateKeys.length) {
|
||||
throw "没有需要修改的key";
|
||||
}
|
||||
|
||||
for (const key of updateKeys) {
|
||||
this.#updateList.push(`${key} = ?`);
|
||||
|
||||
let data = updateData[key];
|
||||
|
||||
if (typeof data == "object" && data != null && !(data instanceof Date)) {
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
|
||||
if (data instanceof Date) {
|
||||
data = formatDate(data);
|
||||
}
|
||||
|
||||
this.values.push(data || null);
|
||||
}
|
||||
}
|
||||
|
||||
where(w, ...vals) {
|
||||
this.wheres.push(w);
|
||||
this.values.push(...vals);
|
||||
return this;
|
||||
}
|
||||
|
||||
build() {
|
||||
let sqlStr = `UPDATE ${this.#tabName}`;
|
||||
|
||||
if (!this.#updateList) {
|
||||
throw "没有需要修改的key";
|
||||
}
|
||||
|
||||
if (!this.wheres.length) {
|
||||
throw "修改数据必须指定条件";
|
||||
}
|
||||
|
||||
sqlStr += " SET ";
|
||||
|
||||
sqlStr += this.#updateList.join(", ");
|
||||
|
||||
sqlStr += this.getWhereStr();
|
||||
|
||||
this.verifyLen(sqlStr, this.values);
|
||||
|
||||
return {
|
||||
sql: sqlStr,
|
||||
values: this.values,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class GormInsertClass {
|
||||
#insertKeys = [];
|
||||
#tabName = "";
|
||||
|
||||
#values = [];
|
||||
|
||||
constructor(props) {
|
||||
this.#tabName = props.tabName;
|
||||
|
||||
this.#init(props.insertData);
|
||||
}
|
||||
|
||||
#init(insertData) {
|
||||
const insertKeys = Object.keys(insertData);
|
||||
if (!insertKeys.length) {
|
||||
throw "没有需要插入的key";
|
||||
}
|
||||
|
||||
this.#insertKeys = insertKeys;
|
||||
|
||||
for (const key of insertKeys) {
|
||||
let data = insertData[key];
|
||||
|
||||
if (typeof data == "object" && data != null && !(data instanceof Date)) {
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
|
||||
if (data instanceof Date) {
|
||||
data = formatDate(data);
|
||||
}
|
||||
|
||||
this.#values.push(data || null);
|
||||
}
|
||||
}
|
||||
|
||||
verifyLen(sql, vals) {
|
||||
const wLen = sql.split("?")?.length - 1;
|
||||
|
||||
if (wLen != vals.length) {
|
||||
throw "占位符与值的数量不匹配";
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
let sqlStr = `INSERT INTO ${this.#tabName} (${this.#insertKeys.join(
|
||||
", "
|
||||
)})`;
|
||||
sqlStr += ` VALUES (${this.#insertKeys.map(() => "?").join(", ")})`;
|
||||
|
||||
this.verifyLen(sqlStr, this.#values);
|
||||
|
||||
return {
|
||||
sql: sqlStr,
|
||||
values: this.#values,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class GormClass {
|
||||
#props;
|
||||
constructor(props) {
|
||||
this.#props = props;
|
||||
}
|
||||
|
||||
select(...selectKeys) {
|
||||
return new GormSelectClass({ ...this.#props, selectKeys });
|
||||
}
|
||||
|
||||
update(updateData) {
|
||||
return new GormUpdateClass({ ...this.#props, updateData });
|
||||
}
|
||||
|
||||
delete() {
|
||||
return new GormUpdateClass({
|
||||
...this.#props,
|
||||
updateData: { delete_time: new Date() },
|
||||
});
|
||||
}
|
||||
|
||||
insert(insertData) {
|
||||
return new GormInsertClass({ ...this.#props, insertData });
|
||||
}
|
||||
}
|
||||
55
server/api/lib/func_api_common/shunt.js
Normal file
55
server/api/lib/func_api_common/shunt.js
Normal file
@@ -0,0 +1,55 @@
|
||||
function getReqData() {
|
||||
const req = payload.get();
|
||||
|
||||
const data = req.body?.toString() ? req.body?.toObject() : {};
|
||||
|
||||
return { data, query: req.query };
|
||||
}
|
||||
|
||||
function getUniqueId() {
|
||||
return ex.suid().base58();
|
||||
}
|
||||
|
||||
function loginVerify() {
|
||||
const req = payload.get();
|
||||
const token = req?.cookie?.token;
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
return jwt_parse(token);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const { query } = getReqData();
|
||||
let [action] = query.action || [""];
|
||||
|
||||
const userInfo = loginVerify() || {};
|
||||
|
||||
if (!userInfo && !actionWhiteList.includes(action)) {
|
||||
return errMsg(413, "Invalid or missing token");
|
||||
}
|
||||
|
||||
return runAction(userInfo);
|
||||
}
|
||||
|
||||
function runAction(userInfo) {
|
||||
const { query } = getReqData();
|
||||
|
||||
let [action] = query.action || [""];
|
||||
if (!action) {
|
||||
return errMsg(500, "没有action字段,请检查");
|
||||
}
|
||||
|
||||
const apiMethod = payload.get().method;
|
||||
|
||||
const fnData = actionControls.find((f) => {
|
||||
const { run, method } = f;
|
||||
return run.name == action && method == apiMethod;
|
||||
});
|
||||
|
||||
if (fnData) {
|
||||
return fnData.run(userInfo);
|
||||
}
|
||||
|
||||
return errMsg(500, "Invalid or missing action Or method");
|
||||
}
|
||||
61
server/api/lib/getFileMimeType.js
Normal file
61
server/api/lib/getFileMimeType.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 获取文件的 MIME 类型
|
||||
* @param {string} filePath - 文件路径
|
||||
* @returns {string} - 文件的 MIME 类型
|
||||
*/
|
||||
function getFileMimeType(filePath) {
|
||||
const extension = filePath.split('.').pop().toLowerCase();
|
||||
switch (extension) {
|
||||
case 'htm':
|
||||
case 'html':
|
||||
return 'text/html';
|
||||
case 'css':
|
||||
return 'text/css';
|
||||
case 'js':
|
||||
return 'text/javascript';
|
||||
case 'json':
|
||||
return 'application/json';
|
||||
case 'xml':
|
||||
return 'application/xml';
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
return 'image/jpeg';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
case 'svg':
|
||||
return 'image/svg+xml';
|
||||
case 'ico':
|
||||
return 'image/x-icon';
|
||||
case 'pdf':
|
||||
return 'application/pdf';
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
return 'application/msword';
|
||||
case 'xls':
|
||||
case 'xlsx':
|
||||
return 'application/vnd.ms-excel';
|
||||
case 'ppt':
|
||||
case 'pptx':
|
||||
return 'application/vnd.ms-powerpoint';
|
||||
case 'zip':
|
||||
return 'application/zip';
|
||||
case 'rar':
|
||||
return 'application/rar';
|
||||
case 'gz':
|
||||
return 'application/gzip';
|
||||
case 'mp3':
|
||||
return 'audio/mpeg';
|
||||
case 'mp4':
|
||||
return 'video/mp4';
|
||||
case 'avi':
|
||||
return 'video/x-msvideo';
|
||||
case 'mov':
|
||||
return 'video/quicktime';
|
||||
case 'txt':
|
||||
return 'text/plain';
|
||||
default:
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
16
server/api/lib/package.js
Normal file
16
server/api/lib/package.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:lib - package
|
||||
// 生成日期:2024-02-19 00:24:00 +0800 CST
|
||||
// 生成路径: lib/package.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:用于定义全局公共的引用。(注册于global.js内)
|
||||
// ==========================================================================
|
||||
|
||||
var fs = require('fs');
|
||||
var SQL = require('sql');
|
||||
var ex = require('util');
|
||||
var cache = require('cache');
|
||||
var crypto = require('crypto');
|
||||
const request = require('request');
|
||||
var encoding = require('encoding');
|
||||
// var jwt = require('api/user/oauth/jwt/jwt.js');
|
||||
61
server/api/lib/protect.js
Normal file
61
server/api/lib/protect.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:lib - protect
|
||||
// 生成日期:2024-02-19 00:24:00 +0800 CST
|
||||
// 生成路径: lib/protect.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:用于需要保护的脚本文件及数据内容的加解密。
|
||||
// ==========================================================================
|
||||
|
||||
// 内部数据加密
|
||||
function encryptData(inputData) {
|
||||
// crypto.rc4(inputData,'/crypto.encrypt/').toBase58.encoding().toString();
|
||||
return crypto.rc4('/crypto.encrypt/',inputData).toBase58.encoding().toString()
|
||||
}
|
||||
|
||||
// 内部数据解密
|
||||
function decryptData(inputData) {
|
||||
// crypto.rc4(inputData,'/crypto.encrypt/').toBase58.decoding().toString();
|
||||
let data = encoding.from(inputData).toBase58.decoding().toString()
|
||||
return crypto.rc4('/crypto.encrypt/',data).toString()
|
||||
}
|
||||
|
||||
// var scriptData = global.ps + '\n' + global.js + '\n' + decode(function.ps) + '\n' + function.js
|
||||
// var prodPath = scriptPath.replace('zcx.the.bb', 'prod.the.bb')
|
||||
|
||||
// 写出加密后的js到正式
|
||||
// const scriptContext = fs.read(prodPath.replace('prod.the.bb', 'zcx.the.bb'))
|
||||
// if (scriptContext.toString().substring(0, 11) != '@Protected-') {
|
||||
// const ps = crypto.script.get(scriptContext.toBytes, payload.get().headers.Signet)
|
||||
// fs.write(ps.toString(), prodPath)
|
||||
// }
|
||||
|
||||
// ps = fs.read('/www/wwwroot/prod.the.bb/function.js')
|
||||
// ps = crypto.script.get(ps)
|
||||
// console.log('cryptOut', ps)
|
||||
|
||||
// 写出加密后的bin到正式
|
||||
// crypto.script.encode(
|
||||
// `${scriptPath}\\index.html`, // 执行脚本
|
||||
// `${prodPath}\\data.bin` // 加密路径
|
||||
// )
|
||||
|
||||
// 读取加密后的bin文件并decode,得到html的string,然后通过replace填充字符串完成整个html内容,然后通过return返回给前端
|
||||
// var html = crypto.script.decode(
|
||||
// 'C:\\PROJECT\\xkh-h5\\api\\user\\add\\data.bin',
|
||||
// 'crypto_test' // 解密密码
|
||||
// )
|
||||
|
||||
// var html = '<h2>XAFWD1231</h2>'
|
||||
|
||||
// html.replace('XAFWD1231', 'TEST')
|
||||
|
||||
// return html
|
||||
// <h2>TEST</h2>
|
||||
|
||||
// crypto.script.decode(
|
||||
// 'C:\\PROJECT\\xkh-h5\\api\\user\\add\\function1.js',
|
||||
// 'C:\\PROJECT\\xkh-h5\\api\\user\\add\\function2.js', // 解密路径
|
||||
// 'crypto_test' // 解密密码
|
||||
// )
|
||||
|
||||
// console.log(scriptPath)
|
||||
27
server/api/lib/requestLog.js
Normal file
27
server/api/lib/requestLog.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:lib - requestLog
|
||||
// 生成日期:2024-02-19 00:24:00 +0800 CST
|
||||
// 生成路径: lib/requestLog.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:用于输出请求日志。
|
||||
// ==========================================================================
|
||||
|
||||
// 日志输出格式:
|
||||
// 请求地址 > 方式 > 目标地址:端口/路径?参数=值 > 请求体 > 脚本路径
|
||||
// 6.0.0.14 > GET > 6.0.0.74:7788/extension/staff?pageNum=1&pageSize=10&list=true | > /www/wwwroot/xp-back/extension/staff
|
||||
|
||||
if (scriptPath != rootPath + '/core.js' && !scriptPath.includes('uploads')) {
|
||||
// prodPath += "/function.js"
|
||||
// if (scriptPath.includes('open')){console.log(payload.toCURL())}
|
||||
console.log(
|
||||
// 北京时间 = UTC 时间 + 8 小时
|
||||
new Date(new Date().getTime() + 8 * 60 * 60 * 1000).toISOString().replace(/T/, ' ').replace(/\..+/, ''),
|
||||
' * ',
|
||||
payload.get().remote_addr.split(':')[0],
|
||||
'>',
|
||||
payload.get().method,
|
||||
'>',
|
||||
payload.get().host + payload.get().url,
|
||||
// payload.get().body.toString() ? '| ' + payload.get().body.toString() : '',
|
||||
);
|
||||
}
|
||||
8
server/api/lib/requestQueryFormatSingle.js
Normal file
8
server/api/lib/requestQueryFormatSingle.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// 格式化单数据参数
|
||||
function formatSingleParams() {
|
||||
var params = {}
|
||||
Object.keys(payload.get().query).forEach(key => {
|
||||
params[key] = payload.get().query[key][0]
|
||||
})
|
||||
return params
|
||||
}
|
||||
104
server/api/lib/responseWriter.js
Normal file
104
server/api/lib/responseWriter.js
Normal file
@@ -0,0 +1,104 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:lib - responseWriter
|
||||
// 生成日期:2024-02-19 00:24:00 +0800 CST
|
||||
// 生成路径: lib/responseWriter.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:用于全局的请求返回消息结构体的构建。(注册于global.js内)
|
||||
// ==========================================================================
|
||||
|
||||
const compress = require('compress')
|
||||
|
||||
// 构建成功返回
|
||||
function okMsg(data) {
|
||||
let res = {
|
||||
"status": 0,
|
||||
"msg": 'success',
|
||||
"data": data
|
||||
}
|
||||
|
||||
log(1, JSON.stringify(res))
|
||||
|
||||
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookies ? payload.get().cookie["token"] : null);
|
||||
var signature = `${payload.get().method}:${payload.get().host}:${payload.get().url}::${payload.get().url}:${userToken}:${payload.get().body ? payload.get().body.toString() : '>'}`
|
||||
signature = crypto.hash.md5(signature).toHEX.encoding().toString()
|
||||
// 检查缓存
|
||||
if (!TABLE_INFO.cache_life) {
|
||||
TABLE_INFO.cache_life = 5
|
||||
}
|
||||
|
||||
// console.log(JSON.stringify(APP_INFO));
|
||||
|
||||
if (APP_INFO) {
|
||||
switch (APP_INFO.status) {
|
||||
case 3:
|
||||
res = crypto.sm4.encrypt('CBC', APP_INFO.secret.split('').reverse().join(''), JSON.stringify(res), APP_INFO.secret).toBase64.url.encoding().toString()
|
||||
case 2:
|
||||
// console.log(APP_INFO.secret)
|
||||
// console.log(APP_INFO.secret.split('').reverse().join(''))
|
||||
res.ciphertext = crypto.sm4.encrypt('CBC', APP_INFO.secret.split('').reverse().join(''), JSON.stringify(res), APP_INFO.secret).toBase64.url.encoding().toString()
|
||||
}
|
||||
}
|
||||
|
||||
// 如果请求头中包含 Accept-Encoding 并且 Accept-Encoding 的值包含 br,则设置 Content-Encoding 为 br。
|
||||
if (payload.get().headers['Accept-Encoding'] && payload.get().headers['Accept-Encoding'][0].includes('br')) {
|
||||
response.headers.set("Content-Encoding", 'br')
|
||||
res = compress.brotli.compress(res).toString()
|
||||
|
||||
'use api/lib/setResponseCache.js'
|
||||
response.write(res)
|
||||
return res
|
||||
}
|
||||
|
||||
res = JSON.stringify(res)
|
||||
|
||||
'use api/lib/setResponseCache.js'
|
||||
response.write(res)
|
||||
return res
|
||||
}
|
||||
|
||||
// 构建错误返回
|
||||
function errMsg(code, msg) {
|
||||
response.status.code(code)
|
||||
let res = JSON.stringify({
|
||||
"status": code,
|
||||
"msg": msg,
|
||||
})
|
||||
|
||||
log(0, res)
|
||||
response.write(res)
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
// 记录数据流
|
||||
function log(status, output) {
|
||||
let time = new Date().getTime()
|
||||
let dbTime = payload.get().headers["Databus-Time"]
|
||||
if (dbTime) {
|
||||
response.headers.add("Server-Timing", `DataBus;dur=${time - dbTime}`)
|
||||
}
|
||||
|
||||
let startTime = payload.get().headers["Timestamp"]
|
||||
let timeDiff
|
||||
if (startTime) {
|
||||
timeDiff = time - startTime
|
||||
response.headers.add("Server-Timing", `RequestCost;dur=${timeDiff}`)
|
||||
}
|
||||
|
||||
// 记录请求体
|
||||
payload.get().body = payload.get().body?.toString()
|
||||
|
||||
// 记录登录日志
|
||||
SQL.push('system_sql', 'INSERT INTO log_dataflow (event_id, table_id, path, status, ipaddr, input, sql_stmts, output, user_info, blank) VALUES (?,?,?,?,?,?,?,?,?,?)',
|
||||
response.headers.get('X-Request-Id') || ex.suid().base58(),
|
||||
payload.get().query?.t,
|
||||
payload.get().path,
|
||||
status,
|
||||
payload.get().remote_addr.split(':')[0],
|
||||
JSON.stringify(payload.get()),
|
||||
SQL_STMTS.length && SQL_STMTS.length > 0 ? JSON.stringify(SQL_STMTS) : null,
|
||||
output,
|
||||
USER_INFO ? JSON.stringify(USER_INFO) : null,
|
||||
timeDiff
|
||||
);
|
||||
}
|
||||
29
server/api/lib/setGlobalVariable.js
Normal file
29
server/api/lib/setGlobalVariable.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// 允许接口被公共访问
|
||||
var ALLOW_PUBLIC_ACCESS
|
||||
|
||||
// 获取当前用户的APPID信息
|
||||
var APP_INFO
|
||||
|
||||
// 将ACL验证后的用户信息存储在全局变量中
|
||||
var USER_INFO
|
||||
|
||||
var OPERATION_TITLE
|
||||
var OPERATION_TYPE
|
||||
// 例如:OPERATION_TITLE = '新增用户'
|
||||
// 当该请求操作需要被记录到操作日志时,请给该变量赋值。
|
||||
|
||||
// 声明数据库查询变量
|
||||
var TABLE_STRUCT = []
|
||||
var TABLE_INFO = {}
|
||||
var TABLE_NAME
|
||||
var DB_NAME = 'system_sql' // 默认数据库,默认为 system_sql
|
||||
var COLUMN_KEY
|
||||
var TABLE_FIELDS
|
||||
var SQL_STMTS = []
|
||||
|
||||
// 从系统配置表中读取配置信息,并存入缓存池中
|
||||
var SYSTEM_CONFIG = SQL.cache.query(
|
||||
'system_sql',
|
||||
86400, // 缓存时间,单位秒,0表示不缓存,-1表示永久缓存,默认缓存时间为1天
|
||||
"SELECT * FROM `sys_configs` WHERE `delete_time` IS NULL;",
|
||||
);
|
||||
12
server/api/lib/setResponseCache.js
Normal file
12
server/api/lib/setResponseCache.js
Normal file
@@ -0,0 +1,12 @@
|
||||
if (!payload.get().path.includes('auth')) {
|
||||
if (!payload.get().headers['Cache-Control'] || !JSON.stringify(payload.get().headers['Cache-Control']).includes('skip-cache')) {
|
||||
cache.set('results', signature, res, TABLE_INFO.cache_life * 1000)
|
||||
// var currentTime = new Date();
|
||||
// currentTime.setSeconds(currentTime.getSeconds() + TABLE_INFO.cache_life);
|
||||
// var timeAfter = currentTime.toUTCString();
|
||||
// response.headers.set("Expires", timeAfter)
|
||||
response.headers.set("Cache-Status", "set")
|
||||
response.headers.set("Cache-Key", signature)
|
||||
response.headers.set("Cache-Control", `no-transform,no-siteapp,max-age=${TABLE_INFO.cache_life}`)
|
||||
}
|
||||
}
|
||||
69
server/api/lib/setResponseHeaders.js
Normal file
69
server/api/lib/setResponseHeaders.js
Normal file
@@ -0,0 +1,69 @@
|
||||
// 【跨域配置】
|
||||
response.headers.set("Origin", "*");
|
||||
response.headers.set("Timing-Allow-Origin", "*");
|
||||
response.headers.set("Access-Control-Allow-Origin", "*");
|
||||
response.headers.set("Vary", "Etag, Save-Data, Accept-Encoding") // Vary 头用于告知客户端在进行请求时需要考虑的头部字段。
|
||||
response.headers.set("Access-Control-Allow-Headers", '*');
|
||||
response.headers.set("Access-Control-Allow-Methods", '*');
|
||||
response.headers.set("Access-Control-Allow-Credentials", true); // 设置允许跨域携带凭证
|
||||
response.headers.set("Access-Control-Expose-Headers", '*');
|
||||
response.headers.set("Access-Control-Request-Method", '*');
|
||||
response.headers.set("Access-Control-Request-Headers", '*');
|
||||
// response.headers.set("Cross-Origin-Embedder-Policy", "require-corp") // 设置跨域嵌入策略
|
||||
response.headers.set("Cross-Origin-Opener-Policy", "same-origin") // 设置跨域 opener 策略
|
||||
response.headers.set("Cross-Origin-Resource-Policy", "cross-origin") // 设置跨域资源策略
|
||||
|
||||
// 【安全配置】
|
||||
// response.headers.set("Content-Security-Policy", "default-src 'none'; script-src'self'; style-src'self'; img-src'self'; font-src'self'; connect-src'self'; frame-src'self'; media-src'self'; object-src'self'; manifest-src'self'; worker-src'self'; base-uri'self'; form-action'self'; frame-ancestors'self';")
|
||||
//X-Permitted-Cross-Domain-Policies
|
||||
response.headers.set("X-Permitted-Cross-Domain-Policies", "none") // 设置跨域策略
|
||||
response.headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload") // 启用严格传输安全
|
||||
response.headers.set("X-Frame-Options", "sameorigin") // 防止点击劫持
|
||||
response.headers.set("X-XSS-Protection", "1; mode=block") // 防止 XSS 攻击
|
||||
response.headers.set("Referrer-Policy", "origin-when-cross-origin") // 防止 referrer 泄漏
|
||||
response.headers.set("X-Content-Type-Options", "nosniff") // 防止 MIME 类型嗅探
|
||||
response.headers.set("X-DNS-Prefetch-Control", "auto") // 防止 DNS 预取取
|
||||
response.headers.set("X-Download-Options", "noopen") // 防止下载时自动打开
|
||||
response.headers.set("X-Frame-Options", "sameorigin") // 防止点击劫持 - 不允许从其他域加载 - 不允许被其它域iframe
|
||||
|
||||
|
||||
// 【性能配置】
|
||||
// response.headers.set("Accept-Ranges", "bytes") // 设置接受范围 [暂不支持]
|
||||
// response.status.earlyHints() // 设置预加载
|
||||
// response.headers.set("Link",'</font.ttf>; rel=preload; as=font') // 设置预加载字体
|
||||
// response.headers.set("Link",'</image.png>; rel=preload; as=image') // 设置预加载图片
|
||||
// response.headers.set("Link",'</audio.mp3>; rel=preload; as=audio') // 设置预加载音频
|
||||
// response.headers.set("Link",'</video.mp4>; rel=preload; as=video') // 设置预加载视频
|
||||
// response.headers.set("Link",'</style.css>; rel=preload; as=style') // 设置预加载样式
|
||||
// response.headers.set("Link",'</script.js>; rel=preload; as=script') // 设置预加载脚本
|
||||
// response.headers.set("Link",'</document.pdf>; rel=preload; as=document') // 设置预加载文档
|
||||
// response.write('')
|
||||
|
||||
response.headers.set("Access-Control-Max-Age", "31536000") // 设置最大缓存预检时间为一年
|
||||
// response.headers.set("Connection", "keep-alive") // 设置连接保持 *⚠️️ HTTP2 已标记该特性废弃
|
||||
// response.headers.set("Keep-Alive", "timeout=60, max=1000") // 设置连接保持时间为60秒,最大连接数为1000 *⚠️️ HTTP2 已标记该特性废
|
||||
|
||||
// 设置为 time 后的一年过期
|
||||
// time = new Date(time.getFullYear() + 1, time.getMonth(), time.getDate(), time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds());
|
||||
// timeStr = time.toUTCString()
|
||||
// response.headers.set("Expires", timeStr)
|
||||
|
||||
response.headers.set("Cache-Control", "no-transform,no-siteapp,max-age=31536000") // 设置缓存时间为一年并屏蔽搜索引擎的预取
|
||||
|
||||
if (payload.get().headers['If-Match']) {
|
||||
response.headers.set("If-Match", payload.get().headers['If-Match'][0])
|
||||
}
|
||||
|
||||
if (payload.get().headers['If-None-Match']) {
|
||||
response.headers.set("If-None-Match", payload.get().headers['If-None-Match'][0])
|
||||
}
|
||||
|
||||
// 【其它配置】
|
||||
response.headers.set("Alt-Svc", 'h2=":443"; ma=2592000;') // 设置服务端推送
|
||||
response.headers.set("Alt-Used", 'h2=":443";') // 设置服务端推送
|
||||
response.headers.set("X-Quic", 'h3')
|
||||
|
||||
response.headers.set("Accept-Ch", "Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, DPR, Viewport-Width, Width, ETag, If-Match, If-None-Match") // 设置请求头
|
||||
|
||||
// 声明请求ID,同时声明SUID的节点编号 范围0-1023,当分布式服务时,避免SUID冲突。(SUID首次生成后既确定NODEID,后续的请求将使用该NODEID,重复声明的NODEID将被忽略)
|
||||
response.headers.set('X-Request-Id', ex.suid(0).base58())
|
||||
24
server/api/lib/sourceAnti.js
Normal file
24
server/api/lib/sourceAnti.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// 如果存在Origin头,则优先使用Origin头中的域名,否则使用Referer头中的域名。
|
||||
let domain = payload.get().headers["Origin"]
|
||||
if (!domain) {
|
||||
domain = payload.get().headers["Referer"]
|
||||
}
|
||||
|
||||
if (domain) {
|
||||
let ref = new URL(domain)
|
||||
// console.log("Source:", ref.hostname)
|
||||
|
||||
// 如果不匹配,则返回403错误。
|
||||
// if (!ref.hostname.endsWith('qq.com') &&
|
||||
// !ref.hostname.endsWith('l-l.cn') &&
|
||||
// !ref.hostname.endsWith('dx5.cn') &&
|
||||
// !ref.hostname.endsWith('localhost') &&
|
||||
// !ref.hostname.endsWith('127.0.0.1')) {
|
||||
// console.error(domain)
|
||||
// return errMsg(403, "未授权的请求")
|
||||
// }
|
||||
} else {
|
||||
domain = "*"
|
||||
// 是否允许Origin 或 Referer 为空 - 如果不允许,将返回403错误。(如果不允许则打开注释)
|
||||
// return errMsg(403, "未授权的请求")
|
||||
}
|
||||
196
server/api/lib/sql.js
Normal file
196
server/api/lib/sql.js
Normal file
@@ -0,0 +1,196 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:lib - sql
|
||||
// 生成日期:2024-02-19 00:24:00 +0800 CST
|
||||
// 生成路径: lib/sql.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:定义全局通用的SQL的CURD功能封装,包含分页列表和模版查询、数据增删等等。(注册于global.js内)
|
||||
// ==========================================================================
|
||||
// 系统的sql数据库连接名为 DB_NAME
|
||||
|
||||
// 连接测试数据库
|
||||
// var test_sql = SQL.new(
|
||||
// 'mydb',
|
||||
// 'mysql',
|
||||
// 'root:123456@tcp(127.0.0.1:3306)/test_db'
|
||||
// )
|
||||
|
||||
function selectData(condition) {
|
||||
const newPlaceholders = getPlaceholders(condition).placeholders;
|
||||
if (newPlaceholders) {
|
||||
const tempParams = getPlaceholders(condition).tempParams;
|
||||
return {
|
||||
query: `SELECT ${TABLE_FIELDS ? TABLE_FIELDS : '*'} FROM ${TABLE_NAME} WHERE ${newPlaceholders} AND delete_time IS NULL LIMIT ?,?`,
|
||||
params: [...tempParams, (condition.pageNum - 1) * condition.pageSize, condition.pageSize],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT ${TABLE_FIELDS ? TABLE_FIELDS : '*'} FROM ${TABLE_NAME} WHERE delete_time IS NULL LIMIT ?,?`,
|
||||
params: [(condition.pageNum - 1) * condition.pageSize, condition.pageSize],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function countTotal(condition) {
|
||||
const newPlaceholders = getPlaceholders(condition).placeholders;
|
||||
const tempParams = getPlaceholders(condition).tempParams;
|
||||
if (newPlaceholders) {
|
||||
return {
|
||||
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE ${newPlaceholders} AND delete_time IS NULL `,
|
||||
params: [...tempParams],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE delete_time IS NULL`,
|
||||
params: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function sql_select() {
|
||||
const data = payload.get().query;
|
||||
if (!data.pageSize || !data.pageNum) {
|
||||
// 根据id查询详情
|
||||
var useParent = scriptPath != payload.get().path.substring(1);
|
||||
if (useParent) {
|
||||
let value = payload.get().path.split('/').pop();
|
||||
const result = SQL.query(
|
||||
DB_NAME,
|
||||
`SELECT * FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
|
||||
value
|
||||
);
|
||||
|
||||
if (result.length) {
|
||||
const newResult = {};
|
||||
Object.keys(result[0]).forEach((item) => {
|
||||
newResult[underscoreToCamel(item)] = result[0][item];
|
||||
});
|
||||
return okMsg(newResult);
|
||||
}
|
||||
return errMsg(507, '获取失败');
|
||||
}
|
||||
}
|
||||
// 格式化这个query参数
|
||||
let newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[item] = data[item][0];
|
||||
});
|
||||
|
||||
// 总数据
|
||||
const ROWS_COUNT = countTotal(newData);
|
||||
const count = SQL.query(DB_NAME, ROWS_COUNT.query, ...ROWS_COUNT.params);
|
||||
// 分页数据
|
||||
const LIST_DATA = selectData(newData);
|
||||
const result = SQL.query(DB_NAME, LIST_DATA.query, ...LIST_DATA.params);
|
||||
// 格式化数据
|
||||
const results = result.map((row) => {
|
||||
const result = {};
|
||||
Object.keys(row).forEach((col) => {
|
||||
result[underscoreToCamel(col)] = row[col];
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
return okMsg({
|
||||
data: results,
|
||||
count: Number(count[0]?.count),
|
||||
pageNum: Number(newData.pageNum),
|
||||
pageSize: Number(newData.pageSize),
|
||||
});
|
||||
}
|
||||
|
||||
function sql_update() {
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
});
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = [];
|
||||
const placeholders = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (newData[item]) {
|
||||
placeholders.push(`${item} = ?`);
|
||||
dataArr.push(newData[item]);
|
||||
} else {
|
||||
placeholders.push(`${item} = NULL`);
|
||||
}
|
||||
});
|
||||
const newPlaceholders = placeholders.join(',');
|
||||
const query = `UPDATE ${TABLE_NAME} SET ${newPlaceholders} WHERE ${COLUMN_KEY} = ?`;
|
||||
|
||||
const res = SQL.exec(DB_NAME, query, ...[...dataArr, filter]);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
|
||||
function sql_insert() {
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
});
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = Object.values(newData);
|
||||
const placeholders = [];
|
||||
|
||||
if (!data[COLUMN_KEY] || data[COLUMN_KEY] !== "auto") {
|
||||
columnsArr.push(COLUMN_KEY);
|
||||
dataArr.push(ex.suid().base58());
|
||||
}
|
||||
|
||||
// columnsArr.push(COLUMN_KEY)
|
||||
// columnsArr.push('proprietor_id')
|
||||
// columnsArr.push('proprietor_name')
|
||||
|
||||
// dataArr.push(ex.suid().base58())
|
||||
// dataArr.push(USER_INFO.user_id)
|
||||
// dataArr.push(USER_INFO.nickname)
|
||||
|
||||
dataArr.forEach(() => {
|
||||
placeholders.push('?');
|
||||
});
|
||||
placeholders.join(',');
|
||||
|
||||
const columnsStr = columnsArr.join(',');
|
||||
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
|
||||
const res = SQL.exec(DB_NAME, query, ...dataArr);
|
||||
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '添加失败');
|
||||
}
|
||||
|
||||
function sql_delete() {
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
let query;
|
||||
let params = filter.split(',');
|
||||
// 兼容批量删除
|
||||
const tables = params.map((item) => '?');
|
||||
query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} IN (${tables.join(',')});`;
|
||||
const res = SQL.exec(DB_NAME, query, ...params);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '删除失败');
|
||||
}
|
||||
|
||||
// 基本的CURD功能
|
||||
function base_curd() {
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return sql_select();
|
||||
case 'PUT':
|
||||
return sql_update();
|
||||
case 'POST':
|
||||
return sql_insert();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
15
server/api/lib/userPassport.js
Normal file
15
server/api/lib/userPassport.js
Normal file
@@ -0,0 +1,15 @@
|
||||
if (payload.get().method != "OPTIONS") {
|
||||
let appId = payload.get().headers["AppId"]
|
||||
if (!appId) {
|
||||
appId = "hVTfgGLRhv7" // 默认AppId
|
||||
}
|
||||
APP_INFO = SQL.cache.query(
|
||||
'system_sql',
|
||||
86400, // 缓存时间,单位秒,0表示不缓存,-1表示永久缓存,默认缓存时间为1天
|
||||
"SELECT * FROM `sys_passport` WHERE `app_id` = ? AND `status` > 0;", appId
|
||||
);
|
||||
if (!APP_INFO.length) {
|
||||
return errMsg(403, "未授权的访问")
|
||||
}
|
||||
APP_INFO = APP_INFO[0]
|
||||
}
|
||||
63
server/api/log/login/function.js
Normal file
63
server/api/log/login/function.js
Normal file
@@ -0,0 +1,63 @@
|
||||
// ==========================================================================
|
||||
// 数据表:log_logins - 系统菜单
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'log_logins';
|
||||
COLUMN_KEY = 'info_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'loginLocation') {
|
||||
placeholders.push(`login_location LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'username') {
|
||||
placeholders.push(`username LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
}
|
||||
});
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function del() {
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
let res;
|
||||
if (filter != 'list') {
|
||||
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} = ?`;
|
||||
res = SQL.exec(DB_NAME, query, filter);
|
||||
} else {
|
||||
// 清空
|
||||
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE delete_time IS NULL`;
|
||||
res = SQL.exec(DB_NAME, query);
|
||||
}
|
||||
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '删除失败');
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return sql_select();
|
||||
case 'DELETE':
|
||||
return del();
|
||||
case 'POST':
|
||||
return sql_insert();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
66
server/api/log/operation/list/function.js
Normal file
66
server/api/log/operation/list/function.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// ==========================================================================
|
||||
// 数据表:log_opers - 系统菜单
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'log_opers';
|
||||
COLUMN_KEY = 'oper_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'title') {
|
||||
placeholders.push(`title LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'operName') {
|
||||
placeholders.push(`oper_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'businessType') {
|
||||
placeholders.push(`business_type = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function del() {
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
let res;
|
||||
if (filter != 'list') {
|
||||
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} = ?`;
|
||||
res = SQL.exec(DB_NAME, query, filter);
|
||||
} else {
|
||||
// 清空
|
||||
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE delete_time IS NULL`;
|
||||
res = SQL.exec(DB_NAME, query);
|
||||
}
|
||||
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '删除失败');
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return sql_select();
|
||||
case 'DELETE':
|
||||
return del();
|
||||
case 'POST':
|
||||
return sql_insert();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
98
server/api/menu/list/function.js
Normal file
98
server/api/menu/list/function.js
Normal file
@@ -0,0 +1,98 @@
|
||||
// ==========================================================================
|
||||
// 数据表:sys_menus - 系统菜单
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'sys_menus';
|
||||
COLUMN_KEY = 'menu_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'menuName') {
|
||||
placeholders.push(`menu_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function selectData(condition) {
|
||||
const newPlaceholders = getPlaceholders(condition).placeholders;
|
||||
if (newPlaceholders) {
|
||||
const tempParams = getPlaceholders(condition).tempParams;
|
||||
return {
|
||||
query: `SELECT * FROM sys_menus WHERE ${newPlaceholders} AND delete_time IS NULL ORDER BY sort ASC`,
|
||||
params: [...tempParams],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT * FROM sys_menus WHERE delete_time IS NULL ORDER BY sort ASC`,
|
||||
params: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function list() {
|
||||
const data = payload.get().query;
|
||||
// 格式化这个query参数
|
||||
let newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[item] = data[item][0];
|
||||
});
|
||||
|
||||
const LIST_DATA = selectData(newData);
|
||||
const result = SQL.query(DB_NAME, LIST_DATA.query, ...LIST_DATA.params);
|
||||
// 格式化数据
|
||||
const newResult = result.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
});
|
||||
return {
|
||||
...newObj,
|
||||
parentId: newObj.parentId == 0 ? null : newObj.parentId
|
||||
};
|
||||
});
|
||||
const formatRes = formatMenuData(newResult);
|
||||
return okMsg(formatRes);
|
||||
}
|
||||
|
||||
function formatMenuData(data, parentId = null) {
|
||||
return data
|
||||
.filter((item) => item.parentId == parentId)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
children: formatMenuData(data, item.menuId),
|
||||
}));
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return list();
|
||||
case 'PUT':
|
||||
return sql_update();
|
||||
case 'POST':
|
||||
return sql_insert();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
28
server/api/menu/menuTreeSelect/function.js
Normal file
28
server/api/menu/menuTreeSelect/function.js
Normal file
@@ -0,0 +1,28 @@
|
||||
function formatMenuData(data, parentId = null) {
|
||||
return data
|
||||
.filter((item) => item.parentId == parentId)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
children: formatMenuData(data, item.menuId),
|
||||
}));
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const result = SQL.query(DB_NAME, `SELECT menu_id,menu_name,parent_id FROM sys_menus WHERE delete_time IS NULL ORDER BY sort ASC`);
|
||||
// 格式化数据
|
||||
const newResult = result.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
});
|
||||
return {
|
||||
...newObj,
|
||||
parentId: newObj.parentId == 0 ? null : newObj.parentId
|
||||
};
|
||||
});
|
||||
const formatRes = formatMenuData(newResult);
|
||||
return okMsg(formatRes);
|
||||
}
|
||||
34
server/api/menu/roleMenuTreeSelect/function.js
Normal file
34
server/api/menu/roleMenuTreeSelect/function.js
Normal file
@@ -0,0 +1,34 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const res1 = SQL.query('system_sql', 'SELECT menu_id FROM sys_role_menus_rel WHERE delete_time IS NULL AND role_id = ?', filter);
|
||||
const res = SQL.query('system_sql', 'SELECT menu_id, menu_name,parent_id FROM sys_menus WHERE delete_time IS NULL');
|
||||
if (res && res1) {
|
||||
const checkedKeys = res1.map((item) => item.menu_id);
|
||||
// 格式化数据
|
||||
const newResult = res.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
});
|
||||
return {
|
||||
...newObj,
|
||||
parentId: newObj.parentId == 0 ? null : newObj.parentId
|
||||
};
|
||||
});
|
||||
const formatRes = formatOrgData(newResult);
|
||||
return okMsg({ checkedKeys, menus: formatRes });
|
||||
}
|
||||
return errMsg(404, '查询失败');
|
||||
}
|
||||
|
||||
function formatOrgData(data, parentId = null) {
|
||||
return data
|
||||
.filter((item) => item.parentId == parentId)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
children: formatOrgData(data, item.menuId),
|
||||
}));
|
||||
}
|
||||
35
server/api/notice/list/function.js
Normal file
35
server/api/notice/list/function.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// ==========================================================================
|
||||
// 数据表:sys_notices - 公告通知
|
||||
// ==========================================================================
|
||||
TABLE_NAME = 'sys_notices';
|
||||
COLUMN_KEY = 'notice_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'title') {
|
||||
placeholders.push(`title LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'noticeType') {
|
||||
placeholders.push(`notice_type = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
return base_curd()
|
||||
}
|
||||
33
server/api/open/dingtalk/accessToken.js
Normal file
33
server/api/open/dingtalk/accessToken.js
Normal file
@@ -0,0 +1,33 @@
|
||||
function main() {
|
||||
const corpid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_corpid').value;
|
||||
const appkey = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appkey').value;
|
||||
const appid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_clientId').value;
|
||||
const appsecret = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appsecret').value;
|
||||
|
||||
const req = {
|
||||
url: `https://oapi.dingtalk.com/gettoken?appkey=${appkey}&appsecret=${appsecret}`
|
||||
}
|
||||
|
||||
const res = request.parse(req).body.toObject()
|
||||
// {
|
||||
// "errcode": 0,
|
||||
// "access_token": "9c499abd27fe373b8c8f1fbed327ba4e",
|
||||
// "errmsg": "ok",
|
||||
// "expires_in": 7200
|
||||
// }
|
||||
|
||||
|
||||
if (res.access_token) {
|
||||
return okMsg(res.access_token)
|
||||
}
|
||||
|
||||
cache.set('oauth', 'dingtalk', res.access_token, 7000 * 1000)
|
||||
os.sleep(7000)
|
||||
main()
|
||||
}
|
||||
|
||||
|
||||
// const access_token = cache.get('oauth', document.phone)
|
||||
// if (access_token == null) {
|
||||
// return false
|
||||
// }
|
||||
6
server/api/open/function.js
Normal file
6
server/api/open/function.js
Normal file
@@ -0,0 +1,6 @@
|
||||
response.headers.add('X-Script-Version', 'open-demo-1.0')
|
||||
|
||||
function main() {
|
||||
return okMsg(payload.get())
|
||||
// return JSON.stringify(SQL.query(DB_NAME, "select uuid()"))
|
||||
}
|
||||
7
server/api/open/upyun/function.js
Normal file
7
server/api/open/upyun/function.js
Normal file
@@ -0,0 +1,7 @@
|
||||
response.headers.add('X-Script-Version', 'open-upyun-1.0')
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
return okMsg('ok')
|
||||
}
|
||||
101
server/api/organization/list/function.js
Normal file
101
server/api/organization/list/function.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// ==========================================================================
|
||||
// 数据表:sys_organizations - 系统接口数据
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'sys_organizations';
|
||||
COLUMN_KEY = 'organization_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'organizationName') {
|
||||
placeholders.push(`organization_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function selectData(condition) {
|
||||
const newPlaceholders = getPlaceholders(condition).placeholders;
|
||||
if (newPlaceholders) {
|
||||
const tempParams = getPlaceholders(condition).tempParams;
|
||||
return {
|
||||
query: `SELECT * FROM sys_organizations WHERE ${newPlaceholders} AND delete_time IS NULL ORDER BY sort ASC`,
|
||||
params: [...tempParams],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
query: `SELECT * FROM sys_organizations WHERE delete_time IS NULL ORDER BY sort ASC`,
|
||||
params: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function list() {
|
||||
const data = payload.get().query;
|
||||
// 格式化这个query参数
|
||||
let newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[item] = data[item][0];
|
||||
});
|
||||
// 分页数据
|
||||
const LIST_DATA = selectData(newData);
|
||||
const result = SQL.query(DB_NAME, LIST_DATA.query, ...LIST_DATA.params);
|
||||
// 格式化数据
|
||||
const newResult = result.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
|
||||
});
|
||||
return {
|
||||
...newObj,
|
||||
parentId: newObj.parentId == 0 ? null : newObj.parentId
|
||||
};
|
||||
});
|
||||
const formatRes = formatOrgData(newResult);
|
||||
return okMsg(formatRes);
|
||||
}
|
||||
|
||||
|
||||
function formatOrgData(data, parentId = null) {
|
||||
return data
|
||||
.filter((item) => item.parentId == parentId)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
children: formatOrgData(data, item.organizationId),
|
||||
}));
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return list();
|
||||
case 'PUT':
|
||||
return sql_update();
|
||||
case 'POST':
|
||||
return sql_insert();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const res1 = SQL.query('system_sql', 'SELECT organization_id FROM sys_role_organizations_rel WHERE delete_time IS NULL AND role_id = ?', filter);
|
||||
const res = SQL.query('system_sql', 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL');
|
||||
if (res && res1) {
|
||||
const checkedKeys = res1.map((item) => item.organization_id);
|
||||
// 格式化数据
|
||||
const newResult = res.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
|
||||
});
|
||||
return {
|
||||
...newObj,
|
||||
parentId: newObj.parentId == 0 ? null : newObj.parentId
|
||||
};
|
||||
});
|
||||
const formatRes = formatOrgData(newResult);
|
||||
return okMsg({ checkedKeys, organizations: formatRes });
|
||||
}
|
||||
return errMsg(404, '查询失败');
|
||||
}
|
||||
|
||||
function formatOrgData(data, parentId = null) {
|
||||
return data
|
||||
.filter((item) => item.parentId == parentId)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
children: formatOrgData(data, item.organizationId),
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
const filter = payload.get().query;
|
||||
let orgSql
|
||||
let res
|
||||
if (!fileter) {
|
||||
// 查根元素
|
||||
orgSql = 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL AND parent_id = 0 OR parent_id IS NULL'
|
||||
} else {
|
||||
if (fileter.parentId) {
|
||||
// 查子元素
|
||||
orgSql = 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL AND parent_id = ?'
|
||||
} else {
|
||||
// 根据orgId查用户信息
|
||||
res = 'SELECT user_id, username FROM sys_users WHERE delete_time IS NULL AND organization_id = ?'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// let filter = payload.get().path.split('/').pop();
|
||||
const res1 = SQL.query('system_sql', 'SELECT organization_id FROM sys_role_organizations_rel WHERE delete_time IS NULL AND role_id = ?', filter);
|
||||
res = SQL.query('system_sql', 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL');
|
||||
if (res && res1) {
|
||||
const checkedKeys = res1.map((item) => item.organization_id);
|
||||
// 格式化数据
|
||||
const newResult = res.map((itemO) => {
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
});
|
||||
return {
|
||||
...newObj,
|
||||
parentId: newObj.parentId == 0 ? null : newObj.parentId
|
||||
};
|
||||
});
|
||||
const formatRes = formatOrgData(newResult);
|
||||
return okMsg({ checkedKeys, organizations: formatRes });
|
||||
}
|
||||
return errMsg(404, '查询失败');
|
||||
}
|
||||
|
||||
function formatOrgData(data, parentId = null) {
|
||||
return data
|
||||
.filter((item) => item.parentId == parentId)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
children: formatOrgData(data, item.organizationId),
|
||||
}));
|
||||
}
|
||||
106
server/api/page/function.js
Normal file
106
server/api/page/function.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/** 页面管理基础接口 **/
|
||||
|
||||
TABLE_NAME = 'sys_pages';
|
||||
COLUMN_KEY = 'page_id';
|
||||
TABLE_FIELDS = '*';
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js';
|
||||
switch (payload.get().method) {
|
||||
case "GET":
|
||||
return list();
|
||||
case "POST":
|
||||
return insert();
|
||||
case "PUT":
|
||||
return sql_update();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'title') {
|
||||
placeholders.push(`title LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
function list() {
|
||||
const queryPa = payload.get().query;
|
||||
if (!Object.keys(queryPa).length) {
|
||||
// 根据id查询详情
|
||||
var useParent = scriptPath != payload.get().path.substring(1);
|
||||
if (useParent) {
|
||||
let value = payload.get().path.split('/').pop();
|
||||
const result = SQL.query(
|
||||
'system_sql',
|
||||
`SELECT * FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
|
||||
value
|
||||
);
|
||||
if (result.length) {
|
||||
return okMsg(result);
|
||||
}
|
||||
return errMsg('获取失败');
|
||||
}
|
||||
}
|
||||
// 格式化这个query参数
|
||||
let newData = {};
|
||||
Object.keys(queryPa).forEach((item) => {
|
||||
newData[item] = queryPa[item][0];
|
||||
});
|
||||
|
||||
// 总数据
|
||||
const COUNT_TOTAL = countTotal(newData);
|
||||
const total = SQL.query('system_sql', COUNT_TOTAL.query, ...COUNT_TOTAL.params);
|
||||
// 分页数据
|
||||
const LIST_DATA = selectData(newData);
|
||||
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
|
||||
|
||||
return okMsg({ result, count: total[0].count, pageNum: Number(newData.pageNum), pageSize: Number(newData.pageSize) });
|
||||
}
|
||||
|
||||
function insert() {
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = { ...data };
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = Object.values(newData);
|
||||
const placeholders = [];
|
||||
|
||||
columnsArr.push(COLUMN_KEY)
|
||||
columnsArr.push('proprietor_id')
|
||||
columnsArr.push('proprietor_name')
|
||||
|
||||
|
||||
const suid = ex.suid().base58()
|
||||
dataArr.push(suid)
|
||||
dataArr.push(USER_INFO.user_id)
|
||||
dataArr.push(USER_INFO.nickname)
|
||||
|
||||
dataArr.forEach(() => {
|
||||
placeholders.push('?');
|
||||
});
|
||||
placeholders.join(',');
|
||||
|
||||
const columnsStr = columnsArr.join(',');
|
||||
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
|
||||
const res = SQL.exec('system_sql', query, ...dataArr);
|
||||
if (res) {
|
||||
return okMsg(suid);
|
||||
}
|
||||
return errMsg(507, '添加失败');
|
||||
}
|
||||
39
server/api/post/list/function.js
Normal file
39
server/api/post/list/function.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// ==========================================================================
|
||||
// 数据表:oa_posts - 系统接口数据
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'oa_posts';
|
||||
COLUMN_KEY = 'post_id';
|
||||
TABLE_FIELDS = '*';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'postName') {
|
||||
placeholders.push(`post_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'postCode') {
|
||||
placeholders.push(`post_id = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
return base_curd();
|
||||
}
|
||||
12
server/api/role/changeStatus/function.js
Normal file
12
server/api/role/changeStatus/function.js
Normal file
@@ -0,0 +1,12 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const data = payload.get().body?.toObject();
|
||||
const query = `UPDATE sys_roles SET status = ? WHERE role_id = ?`;
|
||||
const res = SQL.exec('system_sql', query, data.status, data.roleId);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
75
server/api/role/dataScope/function.js
Normal file
75
server/api/role/dataScope/function.js
Normal file
@@ -0,0 +1,75 @@
|
||||
// 角色数据权限修改
|
||||
// 对应字典:
|
||||
//[
|
||||
// {
|
||||
// value: '0', // 本组织id
|
||||
// label: '仅本人数据权限',
|
||||
// },
|
||||
// {
|
||||
// value: '1', // 所有组织id
|
||||
// label: '全部数据权限',
|
||||
// },
|
||||
// {
|
||||
// value: '2', // 参数组织id
|
||||
// label: '自定数据权限',
|
||||
// },
|
||||
// {
|
||||
// value: '3', // 本组织id
|
||||
// label: '本组织数据权限',
|
||||
// },
|
||||
// {
|
||||
// value: '4', // 不存
|
||||
// label: '本组织及以下数据权限',
|
||||
// },
|
||||
// ],
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const data = payload.get().body?.toObject();
|
||||
|
||||
// 更新role表
|
||||
const query = `UPDATE sys_roles SET data_scope = ? WHERE role_id = ?`;
|
||||
const res = SQL.exec('system_sql', query, data.dataScope, data.roleId);
|
||||
let orgRes = true;
|
||||
if (!res) {
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
// 更新role_org表
|
||||
let orgIds = [];
|
||||
if (data.dataScope == '0') {
|
||||
// 通过本组织id创建
|
||||
} else if (data.dataScope == '1') {
|
||||
// 查询所有组织id
|
||||
orgIds = SQL.query('system_sql', 'SELECT organization_id FROM sys_organizations ');
|
||||
} else if (data.dataScope == '2') {
|
||||
// 参数组织id
|
||||
orgIds = data.organizationIds;
|
||||
} else if (data.dataScope == '3') {
|
||||
// 通过本组织id创建
|
||||
} else if (data.dataScope == '4') {
|
||||
// 不存
|
||||
orgIds = [];
|
||||
}
|
||||
|
||||
// 删除旧数据
|
||||
const delRes = SQL.exec('system_sql', 'DELETE FROM sys_role_organizations_rel WHERE role_id = ?', data.roleId);
|
||||
if (orgIds.length) {
|
||||
let insertStr = '';
|
||||
let valueStrArr = [];
|
||||
let valueArr = [];
|
||||
for (i = 0; i < orgIds.length; i++) {
|
||||
valueStrArr.push(`(?,?,?,?,?)`);
|
||||
valueArr.push(ex.suid().base58(), data.roleId, orgIds[i], USER_INFO.user_id, USER_INFO.nickname);
|
||||
}
|
||||
|
||||
insertStr += `INSERT INTO sys_role_organizations_rel (rule_id, role_id, organization_id, proprietor_id, proprietor_name) VALUES ${valueStrArr.join(',')};`;
|
||||
orgRes = SQL.exec('system_sql', insertStr, ...valueArr);
|
||||
}
|
||||
|
||||
if (orgRes) {
|
||||
return okMsg(orgRes);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
165
server/api/role/list/function.js
Normal file
165
server/api/role/list/function.js
Normal file
@@ -0,0 +1,165 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:SysRole
|
||||
// 生成日期:2024-03-24 16:10:31 +0800 CST
|
||||
// 生成路径: /role/list/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:sys_roles - 角色表
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'sys_roles';
|
||||
COLUMN_KEY = 'role_id';
|
||||
TABLE_FIELDS = '*';
|
||||
var selectDetailValue = 'role_id,role_name,status,role_key,data_scope,role_sort,update_by,remark';
|
||||
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'roleName') {
|
||||
placeholders.push(`role_name LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'roleKey') {
|
||||
placeholders.push(`role_key LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function update() {
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
});
|
||||
let filter = payload.get().path.split('/').pop();
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = [];
|
||||
const placeholders = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (newData[item] && item != 'menu_ids' && item != 'api_ids') {
|
||||
placeholders.push(`${item} = ?`);
|
||||
dataArr.push(newData[item]);
|
||||
} else if (!newData[item]) {
|
||||
placeholders.push(`${item} = NULL`);
|
||||
}
|
||||
});
|
||||
const newPlaceholders = placeholders.join(',');
|
||||
// 更新role表
|
||||
const query = `UPDATE sys_roles SET ${newPlaceholders} WHERE role_id = ?`;
|
||||
const res = SQL.exec('system_sql', query, ...[...dataArr, filter]);
|
||||
let menuRes = true;
|
||||
let apiRes = true;
|
||||
// 更新role_menu表
|
||||
if (newData.menu_ids.length && res) {
|
||||
let delRes;
|
||||
const delPre = SQL.query('system_sql', 'SELECT role_id FROM sys_role_menus_rel WHERE role_id = ?', filter);
|
||||
if (delPre.length) {
|
||||
delRes = SQL.exec('system_sql', 'DELETE FROM sys_role_menus_rel WHERE role_id = ?', filter);
|
||||
}
|
||||
let valueStrArr = [];
|
||||
const valueArr = [];
|
||||
for (let i = 0; i < newData.menu_ids.length; i++) {
|
||||
valueStrArr.push(`(?,?,?)`);
|
||||
valueArr.push(ex.suid().base58(), filter, newData.menu_ids[i]);
|
||||
}
|
||||
menuRes = SQL.exec('system_sql', `INSERT INTO sys_role_menus_rel (rule_id, role_id, menu_id) VALUES ${valueStrArr.join(',')}`, ...valueArr);
|
||||
}
|
||||
// 更新api权限相关表
|
||||
if (newData.api_ids.length && res) {
|
||||
SQL.exec('system_sql', 'DELETE FROM casbin_rule WHERE role_id = ?', newData.role_id);
|
||||
let valueStrArr = [];
|
||||
const valueArr = [];
|
||||
for (let i = 0; i < newData.api_ids.length; i++) {
|
||||
valueStrArr.push(`(?,?,?,?,?)`);
|
||||
valueArr.push(ex.suid().base58(), newData.role_id, newData.role_key, newData.api_ids[i].method, newData.api_ids[i].path);
|
||||
}
|
||||
apiRes = SQL.exec('system_sql', `INSERT INTO casbin_rule (rule_id, role_id, role_key, method, patterns) VALUES ${valueStrArr.join(',')};`, ...valueArr);
|
||||
}
|
||||
|
||||
if (res && menuRes && apiRes) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
|
||||
function insert() {
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
});
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = Object.values(newData);
|
||||
const placeholders = ['?'];
|
||||
const newColumnsArr = ['role_id'];
|
||||
const newDataArr = [ex.suid().base58()];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item != 'menu_ids' && item != 'api_ids') {
|
||||
newColumnsArr.push(item);
|
||||
placeholders.push('?');
|
||||
newDataArr.push(newData[item]);
|
||||
}
|
||||
});
|
||||
|
||||
placeholders.join(',');
|
||||
const columnsStr = newColumnsArr.join(',');
|
||||
const query = `INSERT INTO sys_roles (${columnsStr}) VALUES (${placeholders})`;
|
||||
const res = SQL.exec('system_sql', query, ...newDataArr);
|
||||
let menuRes = true;
|
||||
let apiRes = true;
|
||||
// role_menu表插入
|
||||
if (newData.menu_ids.length && res) {
|
||||
let valueStrArr = [];
|
||||
const valueArr = [];
|
||||
for (i = 0; i < newData.menu_ids.length; i++) {
|
||||
valueStrArr.push(`(?,?,?)`);
|
||||
valueArr.push(ex.suid().base58(), res, newData.menu_ids[i]);
|
||||
}
|
||||
menuRes = SQL.exec('system_sql', `INSERT INTO sys_role_menus_rel (rule_id, role_id, menu_id) VALUES ${valueStrArr.join(',')}`, ...valueArr);
|
||||
}
|
||||
// api权限相关表插入数据
|
||||
if (newData.api_ids.length && res) {
|
||||
let valueStrArr = [];
|
||||
const valueArr = [];
|
||||
for (i = 0; i < newData.api_ids.length; i++) {
|
||||
valueStrArr.push(`(?,?,?,?,?)`);
|
||||
valueArr.push(ex.suid().base58(), newData.role_id, newData.role_key, newData.api_ids[i].method, newData.api_ids[i].path,);
|
||||
}
|
||||
apiRes = SQL.exec('system_sql', `INSERT INTO casbin_rule (rule_id, role_id, role_key, method, patterns) VALUES ${valueStrArr.join(',')};`, ...valueArr);
|
||||
}
|
||||
|
||||
if (res && menuRes && apiRes) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '添加失败');
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return sql_select();
|
||||
case 'PUT':
|
||||
return update();
|
||||
case 'POST':
|
||||
return insert();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
34
server/api/temp/budget/function.js
Normal file
34
server/api/temp/budget/function.js
Normal file
@@ -0,0 +1,34 @@
|
||||
function main() {
|
||||
|
||||
function upInsert() {
|
||||
const data = payload.get().body?.toObject()
|
||||
// 查询是否已存在
|
||||
const sqlSelect = `SELECT * FROM oa_pm_project_budget WHERE sub_id = '${data.sub_id}' AND project_id = '${data.project_id}';`
|
||||
const selectRes = SQL.query(DB_NAME, sqlSelect)
|
||||
if (selectRes && selectRes.length) {
|
||||
const sqlUpdate = `UPDATE oa_pm_project_budget
|
||||
SET project_name = '${data.project_name}', net_value = ${data.net_value}, tax_value = ${data.tax_value}, tip = '${data.tip}'
|
||||
WHERE sub_id = '${data.sub_id}' AND project_id = '${data.project_id}'`
|
||||
|
||||
const updateRes = SQL.exec(DB_NAME, sqlUpdate)
|
||||
if (updateRes) {
|
||||
return okMsg(updateRes)
|
||||
} else {
|
||||
return errMsg(insertRes)
|
||||
}
|
||||
}
|
||||
const sqlInsert = `INSERT INTO oa_pm_project_budget (sub_id, project_id, project_name, net_value, tax_value, tip) VALUES (?, ?, ?, ?, ?, ?)`
|
||||
const insertRes = SQL.exec(DB_NAME, sqlInsert, ...[data.sub_id, data.project_id, data.project_name, data.net_value, data.tax_value, data.tip])
|
||||
if (insertRes) {
|
||||
return okMsg(insertRes)
|
||||
} else {
|
||||
return errMsg(insertRes)
|
||||
}
|
||||
}
|
||||
switch (payload.get().method) {
|
||||
case 'POST':
|
||||
return upInsert()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
40
server/api/upload/complete/function.js
Normal file
40
server/api/upload/complete/function.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:Upload/Token
|
||||
// 生成日期:2024-02-08 17:09:21 +0800 CST
|
||||
// 生成路径: upload/token/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:sys_files - 文件上传(又拍云)
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'upload-1.0');
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
const file_id = payload.get().path.split('/').pop();
|
||||
const params = payload.get().query; // 获取请求参数
|
||||
if (!file_id || !params.signature) {
|
||||
return errMsg(400, '缺少参数');
|
||||
}
|
||||
const sql = `INSERT INTO sys_files (file_id, file_name, file_path, file_type, file_size, file_hash, proprietor_id, proprietor_name, privacy,is_folder,parent_id) VALUES (?, ?, ?, ? , ? , ?, ?, ?, ?, ?, ?);`;
|
||||
let result = SQL.exec(
|
||||
'system_sql',
|
||||
sql,
|
||||
file_id,
|
||||
params.name,
|
||||
params.path,
|
||||
params.type,
|
||||
params.size,
|
||||
params.signature,
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.nickname,
|
||||
params.privacy ? params.privacy : 0,
|
||||
params.isFolder ? params.isFolder : 0,
|
||||
params.parentId ? params.parentId : 0
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return okMsg(true)
|
||||
}
|
||||
return errMsg(400, '上传失败')
|
||||
}
|
||||
99
server/api/upload/function.js
Normal file
99
server/api/upload/function.js
Normal file
@@ -0,0 +1,99 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:Uploads
|
||||
// 生成日期:2024-02-08 17:09:21 +0800 CST
|
||||
// 生成路径: uploads/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:sys_files - 文件上传
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'uploads-1.0');
|
||||
|
||||
function upload() {
|
||||
const params = payload.get().query;
|
||||
const file_id = ex.suid().base58();
|
||||
let file_path = '/uploads/' + file_id;
|
||||
let upload_result
|
||||
if (!params.signature) {
|
||||
return errMsg(400, '缺少参数');
|
||||
}
|
||||
const sql = `SELECT * FROM sys_files WHERE file_hash =? LIMIT 1;`;
|
||||
let res = SQL.query('system_sql', sql, params.signature);
|
||||
if (res.length > 0) {
|
||||
res = res[0]
|
||||
let file_name = payload.get().path.split('/').pop()
|
||||
file_path = res.file_path;
|
||||
|
||||
upload_result = {
|
||||
name: file_name,
|
||||
type: res.file_type,
|
||||
size: res.file_size,
|
||||
};
|
||||
}
|
||||
else {
|
||||
upload_result = response.upload(rootPath + file_path);
|
||||
upload_result.new = true;
|
||||
}
|
||||
|
||||
if (upload_result.name) {
|
||||
const sql = `INSERT INTO sys_files (file_id, file_name, file_path, file_type, file_size, file_hash, proprietor_id, proprietor_name, privacy,is_folder,parent_id) VALUES (?, ?, ?, ? , ? , ?, ?, ?, ?, ?, ?);`;
|
||||
SQL.exec(
|
||||
'system_sql',
|
||||
sql,
|
||||
file_id,
|
||||
upload_result.name,
|
||||
file_path,
|
||||
upload_result.type,
|
||||
upload_result.size,
|
||||
params.signature,
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.nickname,
|
||||
params.privacy ? params.privacy : 0,
|
||||
params.isFolder ? params.isFolder : 0,
|
||||
params.parentId ? params.parentId : 0
|
||||
);
|
||||
|
||||
// if (upload_result.new) {
|
||||
// return ''
|
||||
// }
|
||||
return okMsg({ id: file_id, name: upload_result.name, type: upload_result.type, size: upload_result.size })
|
||||
}
|
||||
return errMsg(507, '上传失败');
|
||||
}
|
||||
|
||||
function info() {
|
||||
const fileId = payload.get().query.fileId;
|
||||
const res = SQL.query('system_sql', `SELECT * FROM sys_files WHERE delete_time IS NULL AND file_id = ?;`, fileId);
|
||||
|
||||
return JSON.stringify({
|
||||
filePath: res[0].file_path,
|
||||
fileType: res[0].file_type,
|
||||
fileSize: res[0].file_size,
|
||||
});
|
||||
}
|
||||
|
||||
function del() {
|
||||
const fileId = payload.get().query.fileId;
|
||||
const file_path = rootPath + '/uploads/' + fileId;
|
||||
// 数据库更新
|
||||
const res = SQL.exec('system_sql', `UPDATE sys_files SET delete_time = NOW() WHERE file_id =?`, fileId);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
|
||||
return errMsg(507, '删除失败');
|
||||
}
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'POST':
|
||||
return upload();
|
||||
case 'DELETE':
|
||||
return del();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
42
server/api/upload/token/function.js
Normal file
42
server/api/upload/token/function.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:Upload/Token
|
||||
// 生成日期:2024-02-08 17:09:21 +0800 CST
|
||||
// 生成路径: upload/token/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:sys_files - 文件上传(又拍云)
|
||||
// ==========================================================================
|
||||
|
||||
response.headers.add('X-Script-Version', 'upload-1.0');
|
||||
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
let content_md5 = payload.get().path.split("/").pop()
|
||||
const sql = `SELECT * FROM sys_files WHERE file_hash =? LIMIT 1;`;
|
||||
let res = SQL.query('system_sql', sql, content_md5);
|
||||
if (res.length > 0) {
|
||||
res = res[0]
|
||||
okMsg({
|
||||
path: res.file_path,
|
||||
type: res.file_type,
|
||||
size: res.file_size,
|
||||
})
|
||||
}
|
||||
|
||||
let password = crypto.hash.md5("v5JuNat8fget0184tFTTRH2Nyap35DiS").toHEX.encoding().toString();
|
||||
// console.log(password);
|
||||
let method = `PUT`
|
||||
let file = `/${os.getEnv('BUCKET_NAME')}/${ex.suid().base58()}`
|
||||
let now = new Date(Date.now()).toUTCString();
|
||||
|
||||
let digest = crypto.hash.hmac.sha1(password, `${method}&${file}&${now}&${content_md5}`).toBase64.encoding().toString();
|
||||
|
||||
// console.log(digest);
|
||||
return okMsg({
|
||||
file: file,
|
||||
domain: 'cdn.p-q.co',
|
||||
token: `UPYUN xmagic:${digest}`,
|
||||
content_md5: content_md5
|
||||
});
|
||||
}
|
||||
70
server/api/user/acl/excerpt.js
Normal file
70
server/api/user/acl/excerpt.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:user - acl
|
||||
// 生成日期:2024-09-13 13:56:00 +0800 CST
|
||||
// 生成路径: user/acl.js
|
||||
// 生成人:Cato
|
||||
// 功能描述:通过用户token取用户权限组、通过权限组id取具体的权限,再判断用户是否有权限访问对应的路径。
|
||||
// ==========================================================================
|
||||
|
||||
// 对于权限的判断,先取Method,再取Path,最后取Token。
|
||||
// 通过Token取得用户加入的用户组和用户的权限信息,
|
||||
// 权限数据在数据库中的权限组表存储为 roleId、name、method、patterns。
|
||||
// 权限组在用户的存储为 UserID、roleIds(权限组ID数组)。
|
||||
// 取用户信息中的ruleIds字段,然后根据ruleIds查询用户权限信息
|
||||
// 判断用户是否有权限访问该路径,如果没有权限,则返回错误信息。
|
||||
|
||||
// method在数据库存储的格式为 GET、POST、PUT、DELETE
|
||||
// patterns在数据库存储的格式为 /api/v1/xxx/xxx
|
||||
// 当用户拥有更上一级目录的权限时,可以访问该目录下的所有子目录和文件。
|
||||
// 例如 用户拥有 /api/v1/xxx 的权限,则可以访问 /api/v1/xxx/xxx/xxx 的路径。
|
||||
|
||||
// 是否允许公开访问
|
||||
if (!ALLOW_PUBLIC_ACCESS) {
|
||||
// 取 Authorization - Bearer 后面的Token值
|
||||
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookie ? payload.get().cookie["token"] : null);
|
||||
if (!userToken) {
|
||||
return errMsg(413, "未登录");
|
||||
}
|
||||
|
||||
// 判断userToken是否是数组
|
||||
if (Array.isArray(userToken)) {
|
||||
// userToken = userToken[0]
|
||||
// userToken 取不为undefined的第一个值
|
||||
userToken = userToken.find(item => item !== undefined)
|
||||
}
|
||||
|
||||
// 删除 Bearer 部分
|
||||
userToken = userToken.replace("Bearer ", "").replace(/\s/g, "");
|
||||
|
||||
let tokenInfo = jwt_parse(userToken) // 这里需要密钥进行验证
|
||||
if (!tokenInfo) {
|
||||
return errMsg(413, "身份验证失败");
|
||||
}
|
||||
|
||||
// 判断tokenInfo.exp 是否过期
|
||||
if (tokenInfo.exp < Date.now() / 1000) {
|
||||
return errMsg(413, "身份凭证已过期");
|
||||
}
|
||||
|
||||
// response.cookies.setRaw(`token=${userToken}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; PartitionKeySite=Main; HttpOnly; Secure`)
|
||||
|
||||
|
||||
USER_INFO = SQL.query(
|
||||
'system_sql',
|
||||
"SELECT * FROM sys_users WHERE user_id = ?",
|
||||
tokenInfo.uid
|
||||
);
|
||||
if (USER_INFO.length == 0) {
|
||||
return errMsg(413, "用户不存在");
|
||||
}
|
||||
|
||||
USER_INFO = USER_INFO[0]
|
||||
|
||||
if (OPERATION_TITLE, OPERATION_TYPE) {
|
||||
let location = "未知"
|
||||
SQL.push(
|
||||
'system_sql', "INSERT INTO log_opers (oper_id, oper_name, business_type, title, method, oper_param, status, oper_ip, oper_url, oper_location) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||
ex.suid().base58(), tokenInfo.unm, OPERATION_TYPE, OPERATION_TITLE, payload.get().method, payload.get().body?.toString(), 0, payload.get().remote_addr, payload.get().path, location
|
||||
)
|
||||
}
|
||||
}
|
||||
12
server/api/user/changeStatus/function.js
Normal file
12
server/api/user/changeStatus/function.js
Normal file
@@ -0,0 +1,12 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const data = payload.get().body?.toObject();
|
||||
const query = `UPDATE sys_users SET status = ? WHERE user_id = ?`;
|
||||
const res = SQL.exec('system_sql', query, data.status, data.userId);
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '更新失败');
|
||||
}
|
||||
7
server/api/user/getInit/function.js
Normal file
7
server/api/user/getInit/function.js
Normal file
@@ -0,0 +1,7 @@
|
||||
function main() {
|
||||
|
||||
// const posts = SQL.query(DB_NAME, 'SELECT * FROM oa_posts WHERE delete_time IS NULL');
|
||||
const roles = SQL.query('system_sql', 'SELECT * FROM sys_roles WHERE delete_time IS NULL');
|
||||
|
||||
return okMsg({ roles: roles });
|
||||
}
|
||||
137
server/api/user/list/function.js
Normal file
137
server/api/user/list/function.js
Normal file
@@ -0,0 +1,137 @@
|
||||
// ==========================================================================
|
||||
// 所属模块:SysUsers
|
||||
// 生成日期:2024-03-24 16:10:31 +0800 CST
|
||||
// 生成路径: /system/users/users/function.js
|
||||
// 生成人:xScript_Engine
|
||||
// 数据表:sys_users - 用户表(系统账户列表使用)
|
||||
// ==========================================================================
|
||||
|
||||
TABLE_NAME = 'sys_users';
|
||||
COLUMN_KEY = 'user_id';
|
||||
TABLE_FIELDS = '*';
|
||||
'use api/lib/sql.js';
|
||||
|
||||
function getPlaceholders(condition) {
|
||||
const columnsArr = Object.keys(condition);
|
||||
const placeholders = [];
|
||||
const tempParams = [];
|
||||
columnsArr.forEach((item) => {
|
||||
if (item == 'nickname') {
|
||||
placeholders.push(`nickname LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'phone') {
|
||||
placeholders.push(`phone LIKE ?`);
|
||||
tempParams.push('%' + condition[item] + '%');
|
||||
} else if (item == 'status') {
|
||||
placeholders.push(`status = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
} else if (item == 'organizationId') {
|
||||
placeholders.push(`organization_id = ?`);
|
||||
tempParams.push(condition[item]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
placeholders: placeholders.join(' AND '),
|
||||
tempParams: tempParams,
|
||||
};
|
||||
}
|
||||
|
||||
function list() {
|
||||
// 验证用户权限
|
||||
// 'use api/user/acl/excerpt.js'
|
||||
|
||||
const data = payload.get().query;
|
||||
if (!Object.keys(data).length) {
|
||||
// 根据id查询详情
|
||||
var useParent = scriptPath != payload.get().path.substring(1);
|
||||
if (useParent) {
|
||||
let value = payload.get().path.split('/').pop();
|
||||
const result = SQL.query(
|
||||
'system_sql',
|
||||
`SELECT user_id,nickname,phone,role_id,role_key,role_name,email,organization_id,organization_name,post_id,post_name,remark,status,username,autograph,last_login_ip,last_login_time FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
|
||||
value
|
||||
);
|
||||
// 获取岗位和角色
|
||||
// const posts = SQL.query('system_sql', 'SELECT * FROM oa_posts WHERE delete_time IS NULL');
|
||||
const roles = SQL.query('system_sql', 'SELECT * FROM sys_roles WHERE delete_time IS NULL');
|
||||
if (result.length) {
|
||||
return okMsg({ data: result[0], roles });
|
||||
}
|
||||
return errMsg(507, '获取失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化这个query参数
|
||||
let newData = {};
|
||||
Object.keys(data).forEach((item) => {
|
||||
newData[item] = data[item][0];
|
||||
});
|
||||
|
||||
// 总数据
|
||||
const COUNT_TOTAL = countTotal(newData);
|
||||
const total = SQL.query('system_sql', COUNT_TOTAL.query, ...COUNT_TOTAL.params);
|
||||
// 分页数据
|
||||
const LIST_DATA = selectData(newData);
|
||||
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
|
||||
return okMsg({ data: result, total: total[0].count, pageNum: Number(newData.pageNum), pageSize: Number(newData.pageSize) });
|
||||
}
|
||||
|
||||
|
||||
|
||||
function insert() {
|
||||
const data = payload.get().body?.toObject();
|
||||
const newData = {};
|
||||
console.log("crypto.bcrypt", crypto.bcrypt)
|
||||
Object.keys(data).forEach((item) => {
|
||||
if (camelToUnderscore(item) == 'password') {
|
||||
newData[camelToUnderscore(item)] = crypto.bcrypt.encrypt(data[item]);
|
||||
} else {
|
||||
newData[camelToUnderscore(item)] = data[item];
|
||||
}
|
||||
});
|
||||
const columnsArr = Object.keys(newData);
|
||||
let dataArr = Object.values(newData);
|
||||
const placeholders = [];
|
||||
|
||||
columnsArr.push(COLUMN_KEY)
|
||||
columnsArr.push('proprietor_id')
|
||||
columnsArr.push('proprietor_name')
|
||||
|
||||
dataArr.push(ex.suid().base58())
|
||||
dataArr.push(USER_INFO.user_id)
|
||||
dataArr.push(USER_INFO.nickname)
|
||||
|
||||
dataArr.forEach(() => {
|
||||
placeholders.push('?');
|
||||
});
|
||||
placeholders.join(',');
|
||||
|
||||
const columnsStr = columnsArr.join(',');
|
||||
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
|
||||
const res = SQL.exec('system_sql', query, ...dataArr);
|
||||
|
||||
if (res) {
|
||||
return okMsg(res);
|
||||
}
|
||||
return errMsg(507, '添加失败');
|
||||
}
|
||||
|
||||
|
||||
function main() {
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return list();
|
||||
case 'PUT':
|
||||
return sql_update();
|
||||
case 'POST':
|
||||
return insert();
|
||||
case 'DELETE':
|
||||
return sql_delete();
|
||||
default:
|
||||
response.status.notFound();
|
||||
return `404 page not found`;
|
||||
}
|
||||
}
|
||||
95
server/api/user/login/function.js
Normal file
95
server/api/user/login/function.js
Normal file
@@ -0,0 +1,95 @@
|
||||
// ------ 用户登录 --------
|
||||
|
||||
function main() {
|
||||
if (!payload.get().headers['User-Agent']) {
|
||||
return errMsg(413, '参数错误');
|
||||
}
|
||||
|
||||
let uaInfo = ex.parseUserAgent(payload.get().headers['User-Agent'][0]);
|
||||
let ipAddr = payload.get().remote_addr.split(':')[0]
|
||||
|
||||
const data = payload.get().body?.toObject();
|
||||
// 校验验证码
|
||||
// if (!captcha.verify(data.captchaId, data.captcha)) {
|
||||
// return errMsg(511, '验证码错误');
|
||||
// }
|
||||
|
||||
let errorTime = cache.get('login', ipAddr)
|
||||
let userErrorTime = cache.get('login', data.username)
|
||||
errorTime = userErrorTime > errorTime ? userErrorTime : errorTime
|
||||
// 用户密码错误3次,封禁IP地址和用户名
|
||||
if (errorTime > 3) {
|
||||
return errMsg(403, '登录失败次数过多,请稍后再试');
|
||||
}
|
||||
|
||||
let loginType = 'username'
|
||||
|
||||
if (data.username.match(/^\d+$/)) {
|
||||
loginType = 'phone'
|
||||
}
|
||||
|
||||
// 查找用户表
|
||||
const res = SQL.query('system_sql', `SELECT * FROM sys_users WHERE ${loginType} = ?`, data.username);
|
||||
|
||||
if (!res || !res.length) {
|
||||
cache.set('login', data.username, errorTime + 1, 180 * 1000)
|
||||
cache.set('login', ipAddr, errorTime + 1, 180 * 1000)
|
||||
return errMsg(511, '用户或密码不正确');
|
||||
}
|
||||
const userData = res[0];
|
||||
// 校验密码
|
||||
if (!crypto.bcrypt.diff(data.password, userData.password)) {
|
||||
cache.set('login', data.username, errorTime + 1, 1800)
|
||||
cache.set('login', ipAddr, errorTime + 1, 1800 * 1000)
|
||||
log(uaInfo, userData, ipAddr, 1)
|
||||
return errMsg(511, '用户或密码不正确');
|
||||
}
|
||||
|
||||
// 登录成功,清除错误次数
|
||||
// cache.del('login', data.username)
|
||||
|
||||
// 根据角色id获取key
|
||||
// const roleRes = SQL.query('system_sql', 'SELECT role_key FROM sys_roles WHERE role_id = ?', userData.role_id);
|
||||
|
||||
// 生成Token / 7天后过期
|
||||
const sevenDays = parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000);
|
||||
|
||||
let token = jwt_issue(userData.user_id, userData.nickname);
|
||||
// 更新最后登录时间和IP
|
||||
SQL.push('system_sql', 'UPDATE sys_users SET last_login_time = NOW(),last_login_ip = ? WHERE user_id =?', ipAddr, userData.user_id);
|
||||
// 记录登录日志
|
||||
log(uaInfo, userData, ipAddr, 0)
|
||||
// response.headers.set('Authorization', 'Bearer ' + token)
|
||||
response.cookies.setRaw(`token=${token}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; Partitioned; HttpOnly; Secure`)
|
||||
// return okMsg({ expire: sevenDays, token: token, userId: userData.user_id });
|
||||
return okMsg({ expire: sevenDays, userId: userData.user_id });
|
||||
}
|
||||
|
||||
// expire: 1714754886;
|
||||
// token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIlRlbmFudElkIjowLCJPcmdhbml6YXRpb25JZCI6MiwiVXNlck5hbWUiOiJyb290IiwiUm9sZUlkIjoxLCJSb2xlS2V5IjoiYWRtaW4iLCJEZXB0SWQiOjAsIlBvc3RJZCI6MSwiZXhwIjoxNzE0NzU0ODg2LCJpc3MiOiJ4TWFnaWMiLCJuYmYiOjE3MTQxNDkwODZ9.ZRidybNoZWKhUXkCeqZHtixO_oyzG56ZFtRKHPSU4lc';
|
||||
|
||||
// captcha: '1604';
|
||||
// captchaId: 'BgsXvVYOUAD8dfKUfs2f';
|
||||
// password: '123456';
|
||||
// username: 'root';
|
||||
|
||||
|
||||
function log(uaInfo, USER_INFO, addr, status) {
|
||||
if (addr == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// 记录登录日志
|
||||
SQL.push('system_sql', 'INSERT INTO log_logins (info_id, user_id, username, status, ipaddr, login_location, browser, os, platform, ua) VALUES (?,?,?,?,?,?,?,?,?,?)',
|
||||
ex.suid().base58(),
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.username,
|
||||
status,
|
||||
addr,
|
||||
'未知',
|
||||
uaInfo.browser,
|
||||
uaInfo.os,
|
||||
uaInfo.deviceModel,
|
||||
payload.get().headers['User-Agent'][0],
|
||||
);
|
||||
}
|
||||
6
server/api/user/logout/function.js
Normal file
6
server/api/user/logout/function.js
Normal file
@@ -0,0 +1,6 @@
|
||||
function main() {
|
||||
response.headers.set("Clear-Site-Data", '"*"')
|
||||
// response.cookies.setRaw("token=null; Path=/; Max-Age=-1; SameSite=Strict; CrossSite=Strict; Priority=High; Partitioned; HttpOnly; Secure")
|
||||
// response.redirect('/')
|
||||
return okMsg(true);
|
||||
}
|
||||
83
server/api/user/me/function.js
Normal file
83
server/api/user/me/function.js
Normal file
@@ -0,0 +1,83 @@
|
||||
function main() {
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
// 获取角色和菜单
|
||||
const role = SQL.query('system_sql', 'SELECT * FROM sys_roles WHERE delete_time IS NULL AND role_id = ?', USER_INFO.role_id);
|
||||
const menus = SQL.query(
|
||||
'system_sql',
|
||||
`SELECT sm.*
|
||||
FROM sys_role_menus_rel srm
|
||||
JOIN sys_menus sm ON srm.menu_id = sm.menu_id
|
||||
WHERE srm.role_id = ? AND srm.delete_time IS NULL`,
|
||||
USER_INFO.role_id
|
||||
);
|
||||
const newUserInfo = {};
|
||||
const newRoles = {};
|
||||
const permissions = [];
|
||||
Object.keys(USER_INFO).forEach((item) => {
|
||||
newUserInfo[underscoreToCamel(item)] = USER_INFO[item];
|
||||
});
|
||||
|
||||
// 从 newUserInfo 中删除不需要的属性
|
||||
delete newUserInfo.password;
|
||||
delete newUserInfo.salt;
|
||||
|
||||
Object.keys(role[0]).forEach((item) => {
|
||||
newRoles[underscoreToCamel(item)] = role[0][item];
|
||||
});
|
||||
const newMenus = menus.map((itemO) => {
|
||||
if (itemO.permission) {
|
||||
permissions.push(itemO.permission);
|
||||
}
|
||||
const newObj = {};
|
||||
Object.keys(itemO).forEach((item) => {
|
||||
newObj[underscoreToCamel(item)] = itemO[item];
|
||||
});
|
||||
const meta = {
|
||||
title: newObj.menuName,
|
||||
isLink: newObj.isLink,
|
||||
isHide: newObj.isHide == 1,
|
||||
isAffix: newObj.isAffix != 1,
|
||||
isIframe: newObj.isIframe != 1,
|
||||
auth: [newObj.permission],
|
||||
icon: newObj.icon,
|
||||
};
|
||||
return {
|
||||
name: newObj.path,
|
||||
path: newObj.path,
|
||||
redirect: '',
|
||||
component: newObj.component,
|
||||
parentId: newObj.parentId == 0 ? null : newObj.parentId,
|
||||
menuId: newObj.menuId,
|
||||
menuType: newObj.menuType,
|
||||
meta,
|
||||
sort: newObj.sort
|
||||
};
|
||||
});
|
||||
newMenus.sort((a, b) => a.sort - b.sort)
|
||||
const formatRes = formatMenuData(newMenus);
|
||||
// 推送预热大文件
|
||||
// response.headers.set("Link", "<https://cdn.bootcdn.net/ajax/libs/font-awesome/6.6.0/css/all.css>; rel=preload; as=style")
|
||||
// response.headers.set("Link", "<https://cdn.bootcdn.net/ajax/libs/crypto-js/4.2.0/crypto-js.min.js>; rel=preload; as=script")
|
||||
// let dirInfo = fs.dir('app/assets')
|
||||
// for (let i = 0; i < dirInfo.length; i++) {
|
||||
// if (dirInfo[i].size > 1024 * 1000) { // 大小超过1MB
|
||||
// response.headers.add("Link", `</app/assets/${dirInfo[i].name}>; rel=preload; as=${dirInfo[i].name.split('.').pop() == 'css' ? 'style' : 'script'}`)
|
||||
// }
|
||||
// }
|
||||
return okMsg({ user: newUserInfo, role: newRoles, permissions: permissions, menus: formatRes });
|
||||
}
|
||||
|
||||
function formatMenuData(data, parentId = null) {
|
||||
const newData = data.filter((item) => item.parentId == parentId);
|
||||
const coverData = [];
|
||||
newData.forEach((item) => {
|
||||
if (item.menuType != 'F')
|
||||
coverData.push({
|
||||
...item,
|
||||
children: formatMenuData(data, item.menuId),
|
||||
});
|
||||
});
|
||||
return coverData;
|
||||
}
|
||||
22
server/api/user/oauth/dingtalk/bind/function.js
Normal file
22
server/api/user/oauth/dingtalk/bind/function.js
Normal file
@@ -0,0 +1,22 @@
|
||||
function main() {
|
||||
OPERATION_TITLE = "绑定钉钉账户"
|
||||
OPERATION_TYPE = 5 // 0 未知 1 新增 2 修改 3 删除 4 查看 5 授权 6 审批 7 发布 8 导入 9 导出 10 打印 11 其他
|
||||
// 验证用户权限
|
||||
'use api/user/acl/excerpt.js'
|
||||
|
||||
const chiper = payload.get().query["chiper"];
|
||||
if (!chiper) {
|
||||
return errMsg(507, "参数错误");
|
||||
}
|
||||
|
||||
// 解密chiper
|
||||
let res = crypto.aes.decrypt('ECB', 'xMagic-oAuth_Wechat.1', chiper)
|
||||
|
||||
// 还原用户信息为对象
|
||||
res = JSON.parse(res)
|
||||
|
||||
// 绑定用户
|
||||
SQL.push('system_sql', "INSERT INTO sys_users_oauth (relate_id, user_id, login_from, open_id, username, avatar_img, raw_info) VALUES (?,?,?,?,?,?)",
|
||||
ex.suid().base58(), tokenInfo.uid, 1, res.openid, res.nickname, res.headimgurl, JSON.stringify(res))
|
||||
return okMsg("绑定成功")
|
||||
}
|
||||
158
server/api/user/oauth/dingtalk/function.js
Normal file
158
server/api/user/oauth/dingtalk/function.js
Normal file
@@ -0,0 +1,158 @@
|
||||
function main() {
|
||||
const corpid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_corpid').value;
|
||||
const appkey = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appkey').value;
|
||||
const appid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_clientId').value;
|
||||
const appsecret = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appsecret').value;
|
||||
const accessToken = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_access_token').value;
|
||||
|
||||
let domain = payload.get().headers["Origin"]
|
||||
if (!domain) {
|
||||
domain = payload.get().headers["Referer"]
|
||||
}
|
||||
|
||||
if (payload.get().query.callback) {
|
||||
response.headers.set("Location", `https://login.dingtalk.com/oauth2/auth?redirect_uri=${payload.get().query.callback || 'https://l-l.cn/app/login'}&response_type=code&client_id=${appkey}&scope=openid&state=dingtalk&prompt=consent`)
|
||||
response.status.movedPermanently()
|
||||
return ''
|
||||
}
|
||||
|
||||
// 成功时跳转到:https://www.aaaaa.com/a/b?authCode=xxxx&state=dddd
|
||||
// 失败时跳转到:https://www.aaaaa.com/a/b?error=yyyyyy&state=dddd
|
||||
|
||||
// if (payload.get().query["error"]) {
|
||||
// return errMsg(511, `授权失败,错误: ${payload.get().query["error"]}`)
|
||||
// }
|
||||
|
||||
// 通过回调Code换取AccessToken
|
||||
let req = {
|
||||
url: `https://api.dingtalk.com/v1.0/oauth2/userAccessToken`,
|
||||
method: 'POST',
|
||||
content_type: 'application/json',
|
||||
data: JSON.stringify({
|
||||
clientId: appkey,
|
||||
clientSecret: appsecret,
|
||||
code: payload.get().path.split('/').pop(),
|
||||
// "refreshToken": "String",
|
||||
grantType: "authorization_code"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// console.log(request.toCURL(req))
|
||||
|
||||
let res = request.parse(req).body.toObject()
|
||||
// {
|
||||
// "accessToken" : "abcd",
|
||||
// "refreshToken" : "abcd",
|
||||
// "expireIn" : 7200,
|
||||
// "corpId" : "corpxxxx"
|
||||
// }
|
||||
|
||||
// console.log(JSON.stringify(res))
|
||||
|
||||
if (!res.accessToken) {
|
||||
// {"requestid":"AF130F96-1BB8-7FB1-A21B-2B29842D2D82","code":"invalidParameter.authCode.notFound","message":"不合法的临时授权码"}
|
||||
return errMsg(511, `Token获取失败,错误代码: ${res.code},错误信息: ${res.message}`)
|
||||
}
|
||||
|
||||
// 通过AccessToken换取用户信息
|
||||
req = {
|
||||
url: `https://api.dingtalk.com/v1.0/contact/users/me`,
|
||||
headers: {
|
||||
'x-acs-dingtalk-access-token': res.accessToken,
|
||||
}
|
||||
}
|
||||
|
||||
res = request.parse(req).body.toObject()
|
||||
// {
|
||||
// "nick" : "zhangsan",
|
||||
// "avatarUrl" : "https://xxx",
|
||||
// "mobile" : "150xxxx9144",
|
||||
// "openId" : "123",
|
||||
// "unionId" : "z21HjQliSzpw0Yxxxx",
|
||||
// "email" : "zhangsan@alibaba-inc.com",
|
||||
// "stateCode" : "86"
|
||||
// }
|
||||
|
||||
// console.log(JSON.stringify(res))
|
||||
// {"mobile":"17768938295","nick":"舒榆衡","openId":"XlH9GxRUh4oqoDBnf7OWWAiEiE","stateCode":"86","unionId":"ZQOFXUqiPZQcuSiPHZr45gJAiEiE","visitor":false}
|
||||
|
||||
if (!res.mobile) {
|
||||
return errMsg(511, `Token验证不通过,错误代码: ${res.code},错误信息: ${res.message}`)
|
||||
}
|
||||
|
||||
// 'use api/user/oauth/getUser.js'
|
||||
|
||||
// 绑定用户
|
||||
// SQL.push('system_sql', "INSERT INTO sys_users_oauth (relate_id, user_id, login_from, open_id, union_id, username, avatar_img, raw_info) VALUES (?,?,?,?,?,?,?)",
|
||||
// ex.suid().base58(), tokenInfo.uid, 1, res.openid, res.unionId, res.nick, res.avatarUrl, JSON.stringify(res))
|
||||
// return okMsg("绑定成功")
|
||||
|
||||
// 查找用户表
|
||||
res = SQL.query('system_sql', `SELECT * FROM sys_users WHERE phone = ?;`, res.mobile);
|
||||
|
||||
|
||||
// console.log(JSON.stringify(res))
|
||||
// 生成Token / 7天后过期
|
||||
const sevenDays = parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000);
|
||||
|
||||
let userData
|
||||
// 如果用户在系统内已存在
|
||||
if (res && res.length) {
|
||||
userData = res[0];
|
||||
// if (!userData.idno) {
|
||||
// res = SQL.push('system_sql', `UPDATE sys_users SET real_name = ?,idno = ? WHERE phone = ?`, userInfo.clientname, userInfo.idnumber, userInfo.mobile)
|
||||
// }
|
||||
} else {
|
||||
// if (!userInfo.mobile) {
|
||||
return errMsg(503, '未绑定用户信息,请选择其他登录方式')
|
||||
// }
|
||||
// let userId = ex.suid().base58()
|
||||
// res = SQL.exec('system_sql', `INSERT INTO sys_users (user_id,username,nickname,real_name,idno,phone) VALUES (?,?,?,?)`, userId, userInfo.idnumber, userInfo.clientname, userInfo.clientname, userInfo.idnumber, userInfo.mobile)
|
||||
// if (!res) {
|
||||
// return errMsg(500, '扫码注册失败')
|
||||
// }
|
||||
// userData = {
|
||||
// user_id: userId,
|
||||
// username: userInfo.idnumber,
|
||||
// nickname: userInfo.clientname,
|
||||
// phone: userInfo.mobile,
|
||||
// role_id: 'hShxtTny1zo',
|
||||
// role_key: '',
|
||||
// }
|
||||
}
|
||||
|
||||
let token = jwt_issue(userData.user_id, userData.nickname);
|
||||
// 更新最后登录时间
|
||||
SQL.push('system_sql', 'UPDATE sys_users SET last_login_time = NOW(),last_login_ip = ? WHERE user_id =?', userData.ip, userData.user_id);
|
||||
|
||||
let uaInfo = ex.parseUserAgent(payload.get().headers['User-Agent'][0]);
|
||||
let userIp = payload.get().remote_addr.split(':')[0]
|
||||
|
||||
log(uaInfo, userData, userIp, 0)
|
||||
|
||||
response.cookies.setRaw(`token=${token}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; PartitionKeySite=Main; HttpOnly; Secure`)
|
||||
// response.headers.set("Location", `${payload.get().query.ref}?secret=${encryptData(token)}`)
|
||||
// response.status.movedPermanently()
|
||||
return okMsg(jwt_parse(token))
|
||||
}
|
||||
|
||||
function log(uaInfo, USER_INFO, addr, status) {
|
||||
if (addr == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// 记录登录日志
|
||||
SQL.push(DB_NAME, 'INSERT INTO log_logins (info_id, user_id, username, status, ipaddr, login_location, browser, os, platform, ua) VALUES (?,?,?,?,?,?,?,?,?,?)',
|
||||
ex.suid().base58(),
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.username,
|
||||
status,
|
||||
addr,
|
||||
'未知',
|
||||
uaInfo.browser,
|
||||
uaInfo.os,
|
||||
uaInfo.deviceModel,
|
||||
payload.get().headers['User-Agent'][0],
|
||||
);
|
||||
}
|
||||
34
server/api/user/oauth/getUser.js
Normal file
34
server/api/user/oauth/getUser.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// 如果带token请求则为绑定,如果不带则为新用户扫码
|
||||
// 取 Authorization - Bearer 后面的Token值
|
||||
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookies ? payload.get().cookie["token"] : null);
|
||||
if (!userToken) {
|
||||
// 通过openid查询用户信息
|
||||
let ses = SQL.query('system_sql', 'SELECT * FROM sys_users WHERE user_id = (SELECT user_id FROM sys_users_oauth WHERE open_id = ?)', res.openid);
|
||||
if (ses && ses.length) {
|
||||
const userData = ses[0]
|
||||
|
||||
let token = jwt_issue(userData.user_id, userData.nickname);
|
||||
response.headers.set('Authorization', 'Bearer ' + token)
|
||||
return okMsg({ expire: sevenDays, token: token, userId: userData.user_id });
|
||||
}
|
||||
// SYSTEM_CONFIG.find(item => item.name === 'oauth_safe_secret').value
|
||||
const chiper = crypto.aes.encrypt('ECB', 'xMagic-oAuth.Secret.1', JSON.stringify(res))
|
||||
return okMsg({ create: true, token: chiper });
|
||||
}
|
||||
// 判断userToken是否是数组
|
||||
if (Array.isArray(userToken)) {
|
||||
userToken = userToken[0]
|
||||
}
|
||||
|
||||
// 删除 Bearer 部分
|
||||
userToken = userToken.replace("Bearer ", "").replace(/\s/g, "");
|
||||
|
||||
let tokenInfo = jwt_parse(userToken) // 这里需要密钥进行验证
|
||||
if (!tokenInfo) {
|
||||
return errMsg(511, "身份验证失败");
|
||||
}
|
||||
|
||||
// 判断tokenInfo.exp 是否过期
|
||||
if (tokenInfo.exp < Date.now() / 1000) {
|
||||
return errMsg(511, "身份凭证已过期");
|
||||
}
|
||||
94
server/api/user/oauth/jwt/jwt.js
Normal file
94
server/api/user/oauth/jwt/jwt.js
Normal file
@@ -0,0 +1,94 @@
|
||||
function jwt_issue(userId, userName) {
|
||||
|
||||
// flags: HS256:S RS256:I ES256:F
|
||||
// header: eyJhbGciOiJ{$flag}UzI1NiIsInR5cCI6IkpXVCJ9. let algo = 'ES256'
|
||||
|
||||
let alg
|
||||
let algo
|
||||
let typ = 'Uy' // JWS
|
||||
switch (SYSTEM_CONFIG.find(item => item.name === 'system_jwt_algo').value) {
|
||||
case 'HS256': // HMAC
|
||||
alg = 'I'
|
||||
typ = 'VC' // JWT
|
||||
break
|
||||
case 'RS256': // RSA
|
||||
alg = 'S'
|
||||
algo = crypto.rsa
|
||||
break
|
||||
case 'SS256': // SM2
|
||||
alg = 'T'
|
||||
algo = crypto.sm2
|
||||
break
|
||||
case 'ES256': // ED25519
|
||||
alg = 'F'
|
||||
algo = crypto.ed25519
|
||||
break
|
||||
default:
|
||||
throw new Error('alg not support')
|
||||
}
|
||||
|
||||
// header
|
||||
let header = `eyJhbGciOiJ${alg}UzI1NiIsInR5cCI6IkpX${typ}J9`
|
||||
|
||||
// payload
|
||||
let payload = encoding.from(JSON.stringify({
|
||||
uid: userId,
|
||||
unm: userName,
|
||||
iss: 'https://l-l.cn',
|
||||
sub: 'xMagic',
|
||||
jti: ex.suid().base58(),
|
||||
nbf: parseInt(new Date().getTime() / 1000),
|
||||
iat: parseInt(new Date().getTime() / 1000),
|
||||
exp: parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000),
|
||||
})).toBase64.url.encoding(true).toString()
|
||||
|
||||
// signature
|
||||
// let pair = algo.new()
|
||||
|
||||
// console.log(`{publicKey:'${pair.publicKey}',\nprivateKey:\n'${pair.privateKey}'}`)
|
||||
|
||||
let signature = algo.sign(SYSTEM_CONFIG.find(item => item.name === 'system_jwt_private').value, payload).toBase64.url.encoding(true).toString()
|
||||
|
||||
let token = `${header}.${payload}.${signature}`
|
||||
|
||||
return token
|
||||
|
||||
// console.log(`token:\n${token}`)
|
||||
|
||||
// let data = token.split('.')[1]
|
||||
// let code = token.split('.')[2]
|
||||
// console.log(`code:\n${code}`)
|
||||
// let isVerfied = algo.verify(pair.publicKey, data, encoding.from(code).toHEX.decoding().Bytes)
|
||||
// console.log(`isVerified:\n${isVerfied}`)
|
||||
// os.exit(0)
|
||||
}
|
||||
|
||||
function jwt_parse(token) {
|
||||
if (!token) return;
|
||||
let data = token.split('.')[1]
|
||||
let code = token.split('.')[2]
|
||||
let algo
|
||||
switch (SYSTEM_CONFIG.find(item => item.name === 'system_jwt_algo').value) {
|
||||
case 'HS256': // HMAC
|
||||
algo = crypto.rsa
|
||||
break
|
||||
case 'RS256': // RSA
|
||||
algo = crypto.rsa
|
||||
break
|
||||
case 'SS256': // SM2
|
||||
algo = crypto.sm2
|
||||
break
|
||||
case 'ES256': // ED25519
|
||||
algo = crypto.ed25519
|
||||
break
|
||||
default:
|
||||
throw new Error('alg not support')
|
||||
}
|
||||
let isVerify = algo.verify(SYSTEM_CONFIG.find(item => item.name === 'system_jwt_public').value, data, encoding.from(code).toBase64.url.decoding(true).Bytes)
|
||||
if (!isVerify) {
|
||||
return undefined
|
||||
}
|
||||
// console.log(data)
|
||||
data = encoding.from(data).toBase64.url.decoding(true).toObject()
|
||||
return data
|
||||
}
|
||||
129
server/api/user/oauth/link/function.js
Normal file
129
server/api/user/oauth/link/function.js
Normal file
@@ -0,0 +1,129 @@
|
||||
// ------ 用户登录 --------
|
||||
cache.new('link-auth')
|
||||
|
||||
'use api/lib/protect.js'
|
||||
|
||||
function main() {
|
||||
response.headers.set("Cache-Control", "no-cache, no-store, must-revalidate") // 禁用缓存
|
||||
switch (payload.get().method) {
|
||||
case 'GET':
|
||||
return auth()
|
||||
case 'POST':
|
||||
return login()
|
||||
default:
|
||||
response.status.notFound()
|
||||
return `404 page not found`
|
||||
}
|
||||
}
|
||||
|
||||
function login() {
|
||||
const username = payload.get().path.split('/').pop()
|
||||
// 设置登录缓存,60秒内仅允许请求一次
|
||||
// if (cache.get('link-auth', username)) {
|
||||
// return errMsg(511, '请勿重复请求');
|
||||
// }
|
||||
|
||||
let loginType = 'username'
|
||||
|
||||
// 手机号
|
||||
if (username.match(/^\d+$/)) {
|
||||
loginType = 'phone'
|
||||
}
|
||||
// 身份证号
|
||||
else if (username.match(/^\d{17}[\dXx]$/)) {
|
||||
loginType = 'idno'
|
||||
}
|
||||
// 邮箱
|
||||
else if (username.match(/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/)) {
|
||||
loginType = 'email'
|
||||
}
|
||||
|
||||
|
||||
// 查找用户表
|
||||
const res = SQL.query('system_sql', `SELECT * FROM sys_users WHERE ${loginType} = ?`, username);
|
||||
|
||||
if (res && res.length) {
|
||||
const userData = res[0];
|
||||
|
||||
userData.ip = payload.get().remote_addr.split(':')[0]
|
||||
|
||||
const id = ex.suid().base58();
|
||||
cache.set('link-auth', id, userData, 180 * 1000)
|
||||
|
||||
// let sendId = encoding.from(id).toBase58.encoding().toString()
|
||||
// 如果存在Origin头,则优先使用Origin头中的域名,否则使用Referer头中的域名。
|
||||
let sid = runtime.call('api/utils/sortUrl/function.js', `https://x.p-q.co/app/auth/confirm.html?code=${id}`).short_url.sid
|
||||
let msg = `【仓湖】您正在进行账户登陆操作,请通过 dx5.cn/${sid} 完成身份验证,若非您本人操作,请勿点击并警惕账户安全风险。`
|
||||
loginType = 'sms'
|
||||
let sendScript = 'api/utils/sms/send.js'
|
||||
let hasDingAccount = runtime.call('api/utils/yskt/checkPhone.js', userData.phone)
|
||||
if (hasDingAccount) {
|
||||
loginType = 'ding'
|
||||
sendScript = 'api/utils/yskt/sendMsg.js'
|
||||
msg = msg.replace('dx5.cn', 'https://dx5.cn')
|
||||
}
|
||||
|
||||
// 发送短信
|
||||
runtime.exec(sendScript, userData.phone, msg, userData.nickname)
|
||||
// console.log('send:', id)
|
||||
|
||||
const code = encryptData(id)
|
||||
return okMsg({ code: code, type: loginType, needVerify: !userData.idno });
|
||||
}
|
||||
|
||||
return errMsg(511, '用户不存在');
|
||||
}
|
||||
|
||||
function auth() {
|
||||
if (!payload.get().headers['User-Agent']) {
|
||||
return errMsg(413, '参数错误');
|
||||
}
|
||||
|
||||
let uaInfo = ex.parseUserAgent(payload.get().headers['User-Agent'][0]);
|
||||
|
||||
let id = payload.get().path.split('/').pop()
|
||||
|
||||
const userData = cache.get('link-auth', id)
|
||||
if (!userData) {
|
||||
return errMsg(511, '会话过期');
|
||||
}
|
||||
|
||||
// 根据角色id获取key
|
||||
// const roleRes = SQL.query(DB_NAME, 'SELECT role_key FROM sys_roles WHERE role_id = ?', userData.role_id);
|
||||
|
||||
// 生成Token / 7天后过期
|
||||
const sevenDays = parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000);
|
||||
|
||||
let token = jwt_issue(userData.user_id, userData.nickname);
|
||||
// 更新最后登录时间
|
||||
SQL.push('system_sql', 'UPDATE sys_users SET last_login_time = NOW(),last_login_ip = ? WHERE user_id =?', userData.ip, userData.user_id);
|
||||
log(uaInfo, userData, payload.get().remote_addr.split(':')[0], 0)
|
||||
// response.headers.set('Authorization', 'Bearer ' + token)
|
||||
// let sessionToken = `token=${token}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; Partitioned; HttpOnly; Secure`
|
||||
// response.cookies.setRaw(sessionToken)
|
||||
let result = { expire: sevenDays, secret: encryptData(token), userId: userData.user_id, sessionId: encryptData(id), needVerify: !userData.idno }
|
||||
// let result = { expire: sevenDays, userId: userData.user_id, ip: userData.ip, sessionId: encryptData(id) }
|
||||
sse.send(result.sessionId, 'login', JSON.stringify(result))
|
||||
// cache.set('link-auth', id, result, 180 * 1000)
|
||||
return okMsg(true);
|
||||
}
|
||||
|
||||
function log(uaInfo, USER_INFO, addr, status) {
|
||||
if (addr == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// 记录登录日志
|
||||
SQL.push(DB_NAME, 'INSERT INTO log_logins (info_id, user_id, username, status, ipaddr, login_location, browser, os, platform, ua) VALUES (?,?,?,?,?,?,?,?,?,?)',
|
||||
ex.suid().base58(),
|
||||
USER_INFO.user_id,
|
||||
USER_INFO.username,
|
||||
status,
|
||||
addr,
|
||||
'未知',
|
||||
uaInfo.browser,
|
||||
uaInfo.os,
|
||||
uaInfo.deviceModel,
|
||||
payload.get().headers['User-Agent'][0],
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user