This commit is contained in:
COOL 2025-01-09 17:14:34 +08:00
parent 13698b70cd
commit 74432bdadd
160 changed files with 0 additions and 9727 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,18 +0,0 @@
logs/
npm-debug.log
yarn-error.log
node_modules/
package-lock.json
yarn.lock
coverage/
dist/
.idea/
run/
.DS_Store
*.sw*
*.un~
.tsbuildinfo
.tsbuildinfo.*
.audit
public/uploads/
cache/

View File

@ -1,11 +0,0 @@
# 🎨 editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -1,30 +0,0 @@
{
"extends": "./node_modules/mwts/",
"ignorePatterns": [
"node_modules",
"dist",
"test",
"jest.config.js",
"typings",
"public/**/**",
"view/**/**",
"packages"
],
"env": {
"jest": true
},
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/ban-ts-comment": "off",
"node/no-extraneous-import": "off",
"no-empty": "off",
"node/no-extraneous-require": "off",
"node/no-unpublished-import": "off",
"eqeqeq": "off",
"node/no-unsupported-features/node-builtins": "off",
"@typescript-eslint/ban-types": "off",
"no-control-regex": "off",
"prefer-const": "off"
}
}

4
.gitattributes vendored
View File

@ -1,4 +0,0 @@
*.js text eol=lf
*.json text eol=lf
*.ts text eol=lf
*.code-snippets text eol=lf

20
.gitignore vendored
View File

@ -1,20 +0,0 @@
logs/
cache/
npm-debug.log
yarn-error.log
node_modules/
package-lock.json
yarn.lock
coverage/
dist/
.idea/
run/
.DS_Store
launch.json
*.sw*
*.un~
.tsbuildinfo
.tsbuildinfo.*
data/*
pnpm-lock.yaml
public/uploads/*

View File

@ -1,9 +0,0 @@
{
"extends": [
"development"
],
"hints": {
"typescript-config/consistent-casing": "off",
"typescript-config/strict": "off"
}
}

View File

@ -1,3 +0,0 @@
module.exports = {
...require('mwts/.prettierrc.json')
}

View File

@ -1,28 +0,0 @@
{
"config": {
"prefix": "config",
"body": [
"import { ModuleConfig } from '@cool-midway/core';",
"",
"/**",
" * 模块配置",
" */",
"export default () => {",
" return {",
" // 模块名称",
" name: 'xxx',",
" // 模块描述",
" description: 'xxx',",
" // 中间件,只对本模块有效",
" middlewares: [],",
" // 中间件,全局有效",
" globalMiddlewares: [],",
" // 模块加载顺序默认为0值越大越优先加载",
" order: 0,",
" } as ModuleConfig;",
"};",
""
],
"description": "cool-admin config代码片段"
}
}

View File

@ -1,19 +0,0 @@
{
"controller": {
"prefix": "controller",
"body": [
"import { CoolController, BaseController } from '@cool-midway/core';",
"",
"/**",
" * 描述",
" */",
"@CoolController({",
" api: ['add', 'delete', 'update', 'info', 'list', 'page'],",
" entity: 实体,",
"})",
"export class XxxController extends BaseController {}",
""
],
"description": "cool-admin controller代码片段"
}
}

View File

@ -1,20 +0,0 @@
{
"entity": {
"prefix": "entity",
"body": [
"import { BaseEntity } from '@cool-midway/core';",
"import { Column, Entity } from 'typeorm';",
"",
"/**",
" * 描述",
" */",
"@Entity('xxx_xxx_xxx')",
"export class XxxEntity extends BaseEntity {",
" @Column({ comment: '描述' })",
" xxx: string;",
"}",
""
],
"description": "cool-admin entity代码片段"
}
}

View File

@ -1,21 +0,0 @@
{
"event": {
"prefix": "event",
"body": [
"import { CoolEvent, Event } from '@cool-midway/core';",
"",
"/**",
" * 接收事件",
" */",
"@CoolEvent()",
"export class xxxEvent {",
" @Event('updateUser')",
" async updateUser(msg, a) {",
" console.log('ImEvent', 'updateUser', msg, a);",
" }",
"}",
""
],
"description": "cool-admin event代码片段"
}
}

View File

@ -1,29 +0,0 @@
{
"middleware": {
"prefix": "middleware",
"body": [
"import { Middleware } from '@midwayjs/decorator';",
"import { NextFunction, Context } from '@midwayjs/koa';",
"import { IMiddleware } from '@midwayjs/core';",
"",
"/**",
" * 描述",
" */",
"@Middleware()",
"export class XxxMiddleware implements IMiddleware<Context, NextFunction> {",
" resolve() {",
" return async (ctx: Context, next: NextFunction) => {",
" // 控制器前执行的逻辑",
" const startTime = Date.now();",
" // 执行下一个 Web 中间件,最后执行到控制器",
" await next();",
" // 控制器之后执行的逻辑",
" console.log(Date.now() - startTime);",
" };",
" }",
"}",
""
],
"description": "cool-admin middleware代码片段"
}
}

View File

@ -1,21 +0,0 @@
{
"queue": {
"prefix": "queue",
"body": [
"import { BaseCoolQueue, CoolQueue } from '@cool-midway/task';",
"",
"/**",
" * 队列",
" */",
"@CoolQueue()",
"export abstract class xxxQueue extends BaseCoolQueue {",
" async data(job: any, done: any) {",
" console.log('收到的数据', job.data);",
" done();",
" }",
"}",
""
],
"description": "cool-admin service代码片段"
}
}

View File

@ -1,33 +0,0 @@
{
"service": {
"prefix": "service",
"body": [
"import { Init, Provide } from '@midwayjs/decorator';",
"import { BaseService } from '@cool-midway/core';",
"import { InjectEntityModel } from '@midwayjs/typeorm';",
"import { Repository } from 'typeorm';",
"",
"/**",
" * 描述",
" */",
"@Provide()",
"export class XxxService extends BaseService {",
" @InjectEntityModel(实体)",
" xxxEntity: Repository<实体>;",
""
" @Init()"
" async init() {",
" await super.init();",
" this.setEntity(this.xxxEntity);",
" }",
"",
" /**",
" * 描述",
" */",
" async xxx() {}",
"}",
""
],
"description": "cool-admin service代码片段"
}
}

View File

@ -1,33 +0,0 @@
FROM node:lts-alpine
WORKDIR /app
# 配置alpine国内镜像加速
RUN sed -i "s@http://dl-cdn.alpinelinux.org/@https://repo.huaweicloud.com/@g" /etc/apk/repositories
# 安装tzdata,默认的alpine基础镜像不包含时区组件安装后可通过TZ环境变量配置时区
RUN apk add --no-cache tzdata
# 设置时区为中国东八区这里的配置可以被docker-compose.yml或docker run时指定的时区覆盖
ENV TZ="Asia/Shanghai"
# 如果各公司有自己的私有源可以替换registry地址,如使用官方源注释下一行
RUN npm config set registry https://registry.npmmirror.com
# 复制package.json
COPY package.json ./package.json
# 安装依赖
RUN npm install
# 构建项目
COPY . .
RUN npm run build
# 删除开发期依赖
RUN rm -rf node_modules && rm package-lock.json
# 安装生产环境依赖
RUN npm install
# 如果端口更换,这边可以更新一下
EXPOSE 8001
CMD ["npm", "run", "start"]

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 cool-team-official
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

191
README.md
View File

@ -1,191 +0,0 @@
<p align="center">
<a href="https://midwayjs.org/" target="blank"><img src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/logo.png" width="200" alt="Midway Logo" /></a>
</p>
<p align="center">cool-admin(midway版)一个很酷的后台权限管理系统开源免费模块化、插件化、极速开发CRUD方便快速构建迭代后台管理系统支持serverless、docker、普通服务器等多种方式部署
<a href="https://cool-js.com" target="_blank">官网</a> 进一步了解。
<p align="center">
<a href="https://github.com/cool-team-official/cool-admin-midway/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="GitHub license" />
<a href=""><img src="https://img.shields.io/github/package-json/v/cool-team-official/cool-admin-midway?style=flat-square" alt="GitHub tag"></a>
<img src="https://img.shields.io/github/last-commit/cool-team-official/cool-admin-midway?style=flat-square" alt="GitHub tag"></a>
</p>
## 特性
Ai时代很多老旧的框架已经无法满足现代化的开发需求Cool-Admin开发了一系列的功能让开发变得更简单、更快速、更高效。
- **Ai编码**通过微调大模型学习框架特有写法实现简单功能从Api接口到前端页面的一键生成
- **流程编排**:通过拖拽编排方式,即可实现类似像智能客服这样的功能
- **模块化**:代码是模块化的,清晰明了,方便维护
- **插件化**:插件化的设计,可以通过安装插件的方式扩展如:支付、短信、邮件等功能
![](https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/flow.png)
## 技术栈
- 后端:**`node.js` `midway.js` `koa.js` `typescript`**
- 前端:**`vue.js` `element-plus` `jsx` `pinia` `vue-router`**
- 数据库:**`mysql` `postgresql` `sqlite`**
如果你是前端,后端的这些技术选型对你是特别友好的,前端开发者可以较快速地上手。
如果你是后端Typescript 的语法又跟 java、php 等特别类似,一切看起来也是那么得熟悉。
如果你想使用 java 版本后端,请移步[cool-admin-java](https://cool-js.com/admin/java/introduce.html)
#### 官网
[https://cool-js.com](https://cool-js.com)
## 视频教程
[官方 B 站视频教程](https://www.bilibili.com/video/BV1j1421R7aB)
<!-- 在此次添加使用文档 -->
## 演示
[AI 极速编码](https://cool-js.com/ai/introduce.html)
[https://show.cool-admin.com](https://show.cool-admin.com)
- 账户admin
- 密码123456
<img src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/home-mini.png" alt="Admin Home"></a>
#### 项目前端
[https://github.com/cool-team-official/cool-admin-vue](https://github.com/cool-team-official/cool-admin-vue)
[https://gitee.com/cool-team-official/cool-admin-vue](https://gitee.com/cool-team-official/cool-admin-vue)
[https://gitcode.com/cool_team/cool-admin-vue](https://gitcode.com/cool_team/cool-admin-vue)
## 微信群
<img width="260" src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/wechat.jpeg?v=1" alt="Admin Wechat"></a>
## 运行
#### 修改数据库配置,配置文件位于`src/config/config.local.ts`
以 Mysql 为例,其他数据库请参考[数据库配置文档](https://cool-js.com/admin/node/quick.html#%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AE)
Mysql(`>=5.7版本`),建议 8.0node 版本(`>=16.x`),建议 18.x首次启动会自动初始化并导入数据
```ts
// mysql驱动已经内置无需安装
typeorm: {
dataSource: {
default: {
type: 'mysql',
host: '127.0.0.1',
port: 3306,
username: 'root',
password: '123456',
database: 'cool',
// 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
synchronize: true,
// 打印日志
logging: false,
// 字符集
charset: 'utf8mb4',
// 是否开启缓存
cache: true,
// 实体路径
entities: ['**/modules/*/entity'],
},
},
},
```
#### 安装依赖并运行
```bash
$ npm i
$ npm run dev
$ open http://localhost:8001/
```
注: `npm i`如果安装失败可以尝试使用[cnpm](https://developer.aliyun.com/mirror/NPM?from=tnpm),或者切换您的镜像源,推荐使用[pnpm](https://pnpm.io/)
## CURD(快速增删改查)
大部分的后台管理系统,或者 API 服务都是对数据进行管理,所以可以看到大量的 CRUD 场景(增删改查)cool-admin 对此进行了大量地封装,让这块的编码量变得极其地少。
#### 新建一个数据表
`src/modules/demo/entity/goods.ts`,项目启动数据库会自动创建该表,无需手动创建
```ts
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity, Index } from 'typeorm';
/**
* 商品
*/
@Entity('demo_app_goods')
export class DemoAppGoodsEntity extends BaseEntity {
@Column({ comment: '标题' })
title: string;
@Column({ comment: '图片' })
pic: string;
@Column({ comment: '价格', type: 'decimal', precision: 5, scale: 2 })
price: number;
}
```
#### 编写 api 接口
`src/modules/demo/controller/app/goods.ts`,快速编写 6 个 api 接口
```ts
import { CoolController, BaseController } from '@cool-midway/core';
import { DemoAppGoodsEntity } from '../../entity/goods';
/**
* 商品
*/
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DemoAppGoodsEntity,
})
export class DemoAppGoodsController extends BaseController {
/**
* 其他接口
*/
@Get('/other')
async other() {
return this.ok('hello, cool-admin!!!');
}
}
```
这样我们就完成了 6 个接口的编写,对应的接口如下:
- `POST /app/demo/goods/add` 新增
- `POST /app/demo/goods/delete` 删除
- `POST /app/demo/goods/update` 更新
- `GET /app/demo/goods/info` 单个信息
- `POST /app/demo/goods/list` 列表信息
- `POST /app/demo/goods/page` 分页查询(包含模糊查询、字段全匹配等)
### 部署
[部署教程](https://cool-js.com/admin/deploy.html)
### 内置指令
- 使用 `npm run lint` 来做代码风格检查。
[midway]: https://midwayjs.org
### 低价服务器
[阿里云、腾讯云、华为云低价云服务器,不限新老](https://cool-js.com/service/cloud)

2
bootstrap.js vendored
View File

@ -1,2 +0,0 @@
const { Bootstrap } = require('@midwayjs/bootstrap');
Bootstrap.run();

View File

@ -1,70 +0,0 @@
const fs = require('fs');
const path = require('path');
const JavaScriptObfuscator = require('javascript-obfuscator');
// 混淆配置
const obfuscatorOptions = {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.7,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.4,
debugProtection: false,
debugProtectionInterval: 0,
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: true,
renameGlobals: false,
rotateStringArray: true,
selfDefending: true,
shuffleStringArray: true,
splitStrings: true,
splitStringsChunkLength: 10,
stringArray: true,
stringArrayEncoding: ['base64'],
stringArrayThreshold: 0.75,
transformObjectKeys: true,
unicodeEscapeSequence: false,
};
// 处理单个文件的函数
function obfuscateFile(filePath) {
try {
const code = fs.readFileSync(filePath, 'utf8');
const obfuscationResult = JavaScriptObfuscator.obfuscate(
code,
obfuscatorOptions
);
fs.writeFileSync(filePath, obfuscationResult.getObfuscatedCode());
console.log(`成功混淆文件: ${filePath}`);
} catch (error) {
console.error(`处理文件 ${filePath} 时出错:`, error);
}
}
// 递归处理目录的函数
function processDirectory(directory) {
const files = fs.readdirSync(directory);
files.forEach(file => {
const fullPath = path.join(directory, file);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
processDirectory(fullPath);
} else if (path.extname(file) === '.js') {
obfuscateFile(fullPath);
}
});
}
// 开始处理
const distPath = path.join(process.cwd(), 'dist');
if (fs.existsSync(distPath)) {
console.log('开始混淆 dist 目录下的 JS 文件...');
processDirectory(distPath);
console.log('混淆完成!');
} else {
console.error('错误: dist 目录不存在!');
}

View File

@ -1,40 +0,0 @@
# 本地数据库环境
# 数据存放在当前目录下的 data里
# 推荐使用安装了docker扩展的vscode打开目录 在本文件上右键可以快速启动,停止
# 如不需要相关容器开机自启动,可注释掉 restart: always
# 如遇端口冲突 可调整ports下 :前面的端口号
version: "3.1"
services:
coolDB:
image: mysql
command:
--default-authentication-plugin=mysql_native_password
--sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
--group_concat_max_len=102400
restart: always
volumes:
- ./data/mysql/:/var/lib/mysql/
environment:
TZ: Asia/Shanghai # 指定时区
MYSQL_ROOT_PASSWORD: "123456" # 配置root用户密码
MYSQL_DATABASE: "cool" # 业务库名
MYSQL_USER: "root" # 业务库用户名
MYSQL_PASSWORD: "123456" # 业务库密码
networks:
- cool
ports:
- 3306:3306
coolRedis:
image: redis
#command: --requirepass "12345678" # redis库密码,不需要密码注释本行
restart: always
environment:
TZ: Asia/Shanghai # 指定时区
volumes:
- ./data/redis/:/data/
networks:
- cool
ports:
- 6379:6379

View File

@ -1,6 +0,0 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
coveragePathIgnorePatterns: ['<rootDir>/test/'],
};

View File

@ -1,80 +0,0 @@
{
"name": "cool-admin",
"version": "7.1.2",
"description": "一个项目用COOL就够了",
"private": true,
"dependencies": {
"@cool-midway/core": "^7.1.25",
"@cool-midway/rpc": "^7.0.0",
"@cool-midway/task": "^7.0.0",
"@midwayjs/bootstrap": "^3.16.0",
"@midwayjs/cache-manager": "^3.16.0",
"@midwayjs/core": "^3.16.0",
"@midwayjs/cron": "^3.16.0",
"@midwayjs/cross-domain": "^3.16.1",
"@midwayjs/decorator": "^3.16.0",
"@midwayjs/info": "^3.16.1",
"@midwayjs/koa": "^3.16.1",
"@midwayjs/logger": "^3.4.0",
"@midwayjs/static-file": "^3.16.1",
"@midwayjs/typeorm": "^3.16.0",
"@midwayjs/upload": "^3.16.1",
"@midwayjs/validate": "^3.16.1",
"@midwayjs/view-ejs": "^3.16.1",
"axios": "^1.6.8",
"cache-manager-ioredis-yet": "^2.0.4",
"decompress": "^4.2.1",
"download": "^8.0.0",
"ipip-ipdb": "^0.6.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"mini-svg-data-uri": "^1.4.4",
"moment": "^2.30.1",
"mysql2": "^3.9.7",
"svg-captcha": "^1.4.0",
"svg2png-wasm": "^1.4.1",
"typeorm": "^0.3.20",
"uuid": "^9.0.1",
"ws": "^8.17.0"
},
"devDependencies": {
"@midwayjs/mock": "^3.16.0",
"@types/jest": "^29.5.12",
"@types/koa": "^2.15.0",
"@types/node": "20",
"cross-env": "^7.0.3",
"javascript-obfuscator": "^4.1.1",
"jest": "^29.7.0",
"mwts": "^1.3.0",
"mwtsc": "^1.10.1",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"typescript": "~5.4.5"
},
"engines": {
"node": ">=12.0.0"
},
"scripts": {
"start": "NODE_ENV=production node ./bootstrap.js",
"dev": "cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js --keepalive",
"cov": "jest --coverage",
"lint": "mwts check",
"lint:fix": "mwts fix",
"ci": "npm run cov",
"build": "mwtsc --cleanOutDir",
"build:obfuscate": "mwtsc --cleanOutDir && node cool/obfuscate.js",
"pm2:start": "pm2 start ./bootstrap.js -i max --name cool-admin",
"pm2:stop": "pm2 stop cool-admin & pm2 delete cool-admin"
},
"midway-bin-clean": [
".vscode/.tsbuildinfo",
"dist"
],
"repository": {
"type": "git",
"url": "https://cool-js.com"
},
"author": "COOL",
"license": "MIT"
}

View File

@ -1,89 +0,0 @@
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
text-align: center;
background: #222;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.footer-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
color: #6ee1f5;
padding: 10px 0 20px 0;
text-align: center;
opacity: 0; /* 开始时隐藏 */
animation: fadeIn 5s forwards; /* 应用动画 */
}
.link {
color: #6ee1f5;
}
.reveal {
position: relative;
display: flex;
color: #6ee1f5;
font-size: 2em;
font-family: Raleway, sans-serif;
letter-spacing: 3px;
text-transform: uppercase;
white-space: pre;
}
.reveal span {
opacity: 0;
transform: scale(0);
animation: fadeIn 2.4s forwards;
}
.reveal::before, .reveal::after {
position: absolute;
content: "";
top: 0;
bottom: 0;
width: 2px;
height: 100%;
background: white;
opacity: 0;
transform: scale(0);
}
.reveal::before {
left: 50%;
animation: slideLeft 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards;
}
.reveal::after {
right: 50%;
animation: slideRight 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards;
}
@keyframes fadeIn {
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes slideLeft {
to {
left: -6%;
opacity: 1;
transform: scale(0.9);
}
}
@keyframes slideRight {
to {
right: -6%;
opacity: 1;
transform: scale(0.9);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,14 +0,0 @@
const duration = 0.8;
const delay = 0.3;
// eslint-disable-next-line no-undef
const revealText = document.querySelector('.reveal');
const letters = revealText.textContent.split('');
revealText.textContent = '';
const middle = letters.filter(e => e !== ' ').length / 2;
letters.forEach((letter, i) => {
// eslint-disable-next-line no-undef
const span = document.createElement('span');
span.textContent = letter;
span.style.animationDelay = `${delay + Math.abs(i - middle) * 0.1}s`;
revealText.append(span);
});

View File

@ -1 +0,0 @@
文件上传目录

View File

@ -1,138 +0,0 @@
import { Inject, Provide } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
import * as ipdb from 'ipip-ipdb';
import * as _ from 'lodash';
import * as moment from 'moment';
/**
*
*/
@Provide()
export class Utils {
@Inject()
baseDir;
/**
* IP
*/
async getReqIP(ctx: Context) {
const req = ctx.req;
return (
req.headers['x-forwarded-for'] ||
req.socket.remoteAddress.replace('::ffff:', '')
);
}
/**
*
* @param obj
*/
async removeEmptyP(obj) {
Object.keys(obj).forEach(key => {
if (obj[key] === null || obj[key] === '' || obj[key] === 'undefined') {
delete obj[key];
}
});
}
/**
* 线
* @param ms
*/
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
*
* @param recently
*/
getRecentlyDates(recently, format = 'YYYY-MM-DD') {
moment.locale('zh-cn');
const dates = [];
for (let i = 0; i < recently; i++) {
dates.push(moment().subtract(i, 'days').format(format));
}
return dates.reverse();
}
/**
*
* @param recently
*/
getRecentlyMonths(recently, format = 'YYYY-MM') {
moment.locale('zh-cn');
const dates = [];
const date = moment(Date.now()).format('YYYY-MM');
for (let i = 0; i < recently; i++) {
dates.push(moment(date).subtract(i, 'months').format(format));
}
return dates.reverse();
}
/**
*
* @param start
* @param end
*/
getBetweenDays(start, end, format = 'YYYY-MM-DD') {
moment.locale('zh-cn');
const dates = [];
const startTime = moment(start).format(format);
const endTime = moment(end).format(format);
const days = moment(endTime).diff(moment(startTime), 'days');
for (let i = 0; i <= days; i++) {
dates.push(moment(startTime).add(i, 'days').format(format));
}
return dates;
}
/**
*
* @param start
* @param end
*/
getBetweenMonths(start, end, format = 'YYYY-MM') {
moment.locale('zh-cn');
const dates = [];
const startTime = moment(start).format(format);
const endTime = moment(end).format(format);
const months = moment(endTime).diff(moment(startTime), 'months');
for (let i = 0; i <= months; i++) {
dates.push(moment(startTime).add(i, 'months').format(format));
}
return dates;
}
/**
*
* @param start
* @param end
*/
getBetweenHours(start, end, format = 'YYYY-MM-DD HH') {
moment.locale('zh-cn');
const dates = [];
const startTime = moment(start).format(format);
const endTime = moment(end).format(format);
const hours = moment(endTime).diff(moment(startTime), 'hours');
for (let i = 0; i <= hours; i++) {
dates.push(moment(startTime).add(i, 'hours').format(format));
}
return dates;
}
/**
*
* @param obj
* @returns
*/
toCamelCase(obj) {
let camelCaseObject = {};
for (let i in obj) {
let camelCase = i.replace(/([-_][a-z])/gi, $1 => {
return $1.toUpperCase().replace('-', '').replace('_', '');
});
camelCaseObject[camelCase] = obj[i];
}
return camelCaseObject;
}
}

View File

@ -1,66 +0,0 @@
import { CoolConfig } from '@cool-midway/core';
import { MidwayConfig } from '@midwayjs/core';
import { CoolCacheStore } from '@cool-midway/core';
// redis缓存
// import { redisStore } from 'cache-manager-ioredis-yet';
export default {
// use for cookie sign key, should change to your own and keep security
keys: 'cool-admin-keys-xxxxxx',
koa: {
port: 8001,
},
// 模板渲染
view: {
mapping: {
'.html': 'ejs',
},
},
// 静态文件配置
staticFile: {
buffer: true,
},
// 文件上传
upload: {
fileSize: '200mb',
whitelist: null,
},
// 缓存 可切换成其他缓存如redis http://www.midwayjs.org/docs/extensions/caching
cacheManager: {
clients: {
default: {
store: CoolCacheStore,
options: {
path: 'cache',
ttl: 0,
},
},
},
},
// cacheManager: {
// clients: {
// default: {
// store: redisStore,
// options: {
// port: 6379,
// host: '127.0.0.1',
// password: '',
// ttl: 0,
// db: 0,
// },
// },
// },
// },
cool: {
// 已经插件化,本地文件上传查看 plugin/config.ts其他云存储查看对应插件的使用
file: {},
// crud配置
crud: {
// 插入模式save不会校验字段(允许传入不存在的字段)insert会校验字段
upsert: 'save',
// 软删除
softDelete: true,
},
} as CoolConfig,
} as MidwayConfig;

View File

@ -1,40 +0,0 @@
import { CoolConfig } from '@cool-midway/core';
import { MidwayConfig } from '@midwayjs/core';
/**
* npm run dev
*/
export default {
typeorm: {
dataSource: {
default: {
type: 'mysql',
host: '127.0.0.1',
port: 3306,
username: 'root',
password: '123456',
database: 'cool',
// 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
synchronize: true,
// 打印日志
logging: false,
// 字符集
charset: 'utf8mb4',
// 是否开启缓存
cache: true,
// 实体路径
entities: ['**/modules/*/entity'],
},
},
},
cool: {
// 实体与路径跟生成代码、前端请求、swagger文档相关 注意:线上不建议开启,以免暴露敏感信息
eps: true,
// 是否自动导入模块数据库
initDB: true,
// 判断是否初始化的方式
initJudge: 'db',
// 是否自动导入模块菜单
initMenu: true,
} as CoolConfig,
} as MidwayConfig;

View File

@ -1,34 +0,0 @@
import { CoolConfig } from '@cool-midway/core';
import { MidwayConfig } from '@midwayjs/core';
/**
* npm run prod
*/
export default {
typeorm: {
dataSource: {
default: {
type: 'mysql',
host: '127.0.0.1',
port: 3306,
username: 'root',
password: '123456',
database: 'cool',
// 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
synchronize: false,
// 打印日志
logging: false,
// 字符集
charset: 'utf8mb4',
// 是否开启缓存
cache: true,
// 实体路径
entities: ['**/modules/*/entity'],
},
},
},
cool: {
// 是否自动导入数据库,生产环境不建议开,用本地的数据库手动初始化
initDB: false,
} as CoolConfig,
} as MidwayConfig;

View File

@ -1,60 +0,0 @@
import * as orm from '@midwayjs/typeorm';
import { Configuration, App, Inject } from '@midwayjs/decorator';
import * as koa from '@midwayjs/koa';
import * as validate from '@midwayjs/validate';
import * as info from '@midwayjs/info';
import { join } from 'path';
import * as view from '@midwayjs/view-ejs';
import * as staticFile from '@midwayjs/static-file';
import * as cron from '@midwayjs/cron';
// import * as crossDomain from '@midwayjs/cross-domain';
import * as cool from '@cool-midway/core';
import { ILogger } from '@midwayjs/logger';
import * as upload from '@midwayjs/upload';
import { IMidwayApplication } from '@midwayjs/core';
// import * as swagger from '@midwayjs/swagger';
// import * as rpc from '@cool-midway/rpc';
// import * as task from '@cool-midway/task';
@Configuration({
imports: [
// https://koajs.com/
koa,
// 是否开启跨域(注:顺序不能乱放!!!) http://www.midwayjs.org/docs/extensions/cross_domain
// crossDomain,
// 模板渲染 https://midwayjs.org/docs/extensions/render
view,
// 静态文件托管 https://midwayjs.org/docs/extensions/static_file
staticFile,
// orm https://midwayjs.org/docs/extensions/orm
orm,
// 参数验证 https://midwayjs.org/docs/extensions/validate
validate,
// 本地任务 http://www.midwayjs.org/docs/extensions/cron
cron,
// 文件上传
upload,
// cool-admin 官方组件 https://cool-js.com
cool,
// rpc 微服务 远程调用
// rpc,
// 任务与队列
// task,
// swagger 文档 http://www.midwayjs.org/docs/extensions/swagger
// swagger,
{
component: info,
enabledEnvironment: ['local'],
},
],
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
@App()
app: IMidwayApplication;
@Inject()
logger: ILogger;
async onReady() {}
}

View File

@ -1,6 +0,0 @@
/**
* @description User-Service parameters
*/
export interface IUserOptions {
uid: number;
}

View File

@ -1,35 +0,0 @@
import { BaseLogMiddleware } from './middleware/log';
import { BaseAuthorityMiddleware } from './middleware/authority';
import { ModuleConfig } from '@cool-midway/core';
/**
*
*/
export default () => {
return {
// 模块名称
name: '权限管理',
// 模块描述
description: '基础的权限管理功能,包括登录,权限校验',
// 中间件
globalMiddlewares: [BaseAuthorityMiddleware, BaseLogMiddleware],
// 模块加载顺序默认为0值越大越优先加载
order: 10,
// app参数配置允许读取的key
allowKeys: [],
// jwt 生成解密token的
jwt: {
// 单点登录
sso: false,
// 注意: 最好重新修改,防止破解
secret: 'cool-admin-xxxxxx',
// token
token: {
// 2小时过期需要用刷新token
expire: 2 * 3600,
// 15天内如果没操作过就需要重新登录
refreshExpire: 24 * 3600 * 15,
},
},
} as ModuleConfig;
};

View File

@ -1,99 +0,0 @@
import {
BaseController,
CoolController,
CoolTag,
CoolUrlTag,
TagTypes,
} from '@cool-midway/core';
import { ALL, Body, Get, Inject, Post, Provide } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
import { PluginService } from '../../../plugin/service/info';
import { BaseSysUserEntity } from '../../entity/sys/user';
import { BaseSysLoginService } from '../../service/sys/login';
import { BaseSysPermsService } from '../../service/sys/perms';
import { BaseSysUserService } from '../../service/sys/user';
/**
* Base
*/
@CoolUrlTag()
@Provide()
@CoolController()
export class BaseCommController extends BaseController {
@Inject()
baseSysUserService: BaseSysUserService;
@Inject()
baseSysPermsService: BaseSysPermsService;
@Inject()
baseSysLoginService: BaseSysLoginService;
@Inject()
ctx: Context;
@Inject()
pluginService: PluginService;
/**
*
*/
@Get('/person', { summary: '个人信息' })
async person() {
return this.ok(
await this.baseSysUserService.person(this.ctx.admin?.userId)
);
}
/**
*
*/
@Post('/personUpdate', { summary: '修改个人信息' })
async personUpdate(@Body(ALL) user: BaseSysUserEntity) {
await this.baseSysUserService.personUpdate(user);
return this.ok();
}
/**
*
*/
@Get('/permmenu', { summary: '权限与菜单' })
async permmenu() {
return this.ok(
await this.baseSysPermsService.permmenu(this.ctx.admin.roleIds)
);
}
/**
*
*/
@Post('/upload', { summary: '文件上传' })
async upload() {
const file = await this.pluginService.getInstance('upload');
return this.ok(await file.upload(this.ctx));
}
/**
*
*/
@Get('/uploadMode', { summary: '文件上传模式' })
async uploadMode() {
const file = await this.pluginService.getInstance('upload');
return this.ok(await file.getMode());
}
/**
* 退
*/
@Post('/logout', { summary: '退出' })
async logout() {
await this.baseSysLoginService.logout();
return this.ok();
}
@CoolTag(TagTypes.IGNORE_TOKEN)
@Get('/program', { summary: '编程' })
async program() {
return this.ok('Node');
}
}

View File

@ -1,99 +0,0 @@
import { Provide, Body, Inject, Post, Get, Query } from '@midwayjs/decorator';
import {
CoolController,
BaseController,
CoolEps,
CoolUrlTag,
CoolTag,
TagTypes,
RESCODE,
} from '@cool-midway/core';
import { LoginDTO } from '../../dto/login';
import { BaseSysLoginService } from '../../service/sys/login';
import { BaseSysParamService } from '../../service/sys/param';
import { Context } from '@midwayjs/koa';
import { Validate } from '@midwayjs/validate';
/**
*
*/
@Provide()
@CoolController({ description: '开放接口' })
@CoolUrlTag()
export class BaseOpenController extends BaseController {
@Inject()
baseSysLoginService: BaseSysLoginService;
@Inject()
baseSysParamService: BaseSysParamService;
@Inject()
ctx: Context;
@Inject()
eps: CoolEps;
/**
*
* @returns
*/
@CoolTag(TagTypes.IGNORE_TOKEN)
@Get('/eps', { summary: '实体信息与路径' })
public async getEps() {
return this.ok(this.eps.admin);
}
/**
* key获得网页内容()
*/
@CoolTag(TagTypes.IGNORE_TOKEN)
@Get('/html', { summary: '获得网页内容的参数值' })
async htmlByKey(@Query('key') key: string) {
this.ctx.body = await this.baseSysParamService.htmlByKey(key);
}
/**
*
* @param login
*/
@CoolTag(TagTypes.IGNORE_TOKEN)
@Post('/login', { summary: '登录' })
@Validate()
async login(@Body() login: LoginDTO) {
return this.ok(await this.baseSysLoginService.login(login));
}
/**
*
*/
@CoolTag(TagTypes.IGNORE_TOKEN)
@Get('/captcha', { summary: '验证码' })
async captcha(
@Query('type') type: string,
@Query('width') width: number,
@Query('height') height: number,
@Query('color') color: string
) {
return this.ok(
await this.baseSysLoginService.captcha(type, width, height, color)
);
}
/**
* token
*/
@CoolTag(TagTypes.IGNORE_TOKEN)
@Get('/refreshToken', { summary: '刷新token' })
async refreshToken(@Query('refreshToken') refreshToken: string) {
try {
const token = await this.baseSysLoginService.refreshToken(refreshToken);
return this.ok(token);
} catch (e) {
this.ctx.status = 401;
this.ctx.body = {
code: RESCODE.COMMFAIL,
message: '登录失效~',
};
}
}
}

View File

@ -1,27 +0,0 @@
import { ALL, Body, Inject, Post, Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { BaseSysDepartmentEntity } from '../../../entity/sys/department';
import { BaseSysDepartmentService } from '../../../service/sys/department';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'list'],
entity: BaseSysDepartmentEntity,
service: BaseSysDepartmentService,
})
export class BaseDepartmentController extends BaseController {
@Inject()
baseDepartmentService: BaseSysDepartmentService;
/**
*
*/
@Post('/order', { summary: '排序' })
async order(@Body(ALL) params: any) {
await this.baseDepartmentService.order(params);
return this.ok();
}
}

View File

@ -1,64 +0,0 @@
import { Provide, Post, Inject, Body, Get } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { BaseSysLogEntity } from '../../../entity/sys/log';
import { BaseSysUserEntity } from '../../../entity/sys/user';
import { BaseSysConfService } from '../../../service/sys/conf';
import { BaseSysLogService } from '../../../service/sys/log';
/**
*
*/
@Provide()
@CoolController({
api: ['page'],
entity: BaseSysLogEntity,
urlTag: {
name: 'a',
url: ['add'],
},
pageQueryOp: {
keyWordLikeFields: ['b.name', 'a.action', 'a.ip'],
select: ['a.*', 'b.name'],
join: [
{
entity: BaseSysUserEntity,
alias: 'b',
condition: 'a.userId = b.id',
type: 'leftJoin',
},
],
},
})
export class BaseSysLogController extends BaseController {
@Inject()
baseSysLogService: BaseSysLogService;
@Inject()
baseSysConfService: BaseSysConfService;
/**
*
*/
@Post('/clear', { summary: '清理' })
public async clear() {
await this.baseSysLogService.clear(true);
return this.ok();
}
/**
*
*/
@Post('/setKeep', { summary: '日志保存时间' })
public async setKeep(@Body('value') value: number) {
await this.baseSysConfService.updateVaule('logKeep', value);
return this.ok();
}
/**
*
*/
@Get('/getKeep', { summary: '获得日志保存时间' })
public async getKeep() {
return this.ok(await this.baseSysConfService.getValue('logKeep'));
}
}

View File

@ -1,46 +0,0 @@
import { Body, Inject, Post, Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { BaseSysMenuEntity } from '../../../entity/sys/menu';
import { BaseSysMenuService } from '../../../service/sys/menu';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: BaseSysMenuEntity,
service: BaseSysMenuService,
})
export class BaseSysMenuController extends BaseController {
@Inject()
baseSysMenuService: BaseSysMenuService;
@Post('/parse', { summary: '解析' })
async parse(
@Body('entity') entity: string,
@Body('controller') controller: string,
@Body('module') module: string
) {
return this.ok(
await this.baseSysMenuService.parse(entity, controller, module)
);
}
@Post('/create', { summary: '创建代码' })
async create(@Body() body) {
await this.baseSysMenuService.create(body);
return this.ok();
}
@Post('/export', { summary: '导出' })
async export(@Body('ids') ids: number[]) {
return this.ok(await this.baseSysMenuService.export(ids));
}
@Post('/import', { summary: '导入' })
async import(@Body('menus') menus: any[]) {
await this.baseSysMenuService.import(menus);
return this.ok();
}
}

View File

@ -1,34 +0,0 @@
import { Get, Inject, Provide, Query } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { BaseSysParamEntity } from '../../../entity/sys/param';
import { BaseSysParamService } from '../../../service/sys/param';
import { Context } from '@midwayjs/koa';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'page'],
entity: BaseSysParamEntity,
service: BaseSysParamService,
pageQueryOp: {
keyWordLikeFields: ['name', 'keyName'],
fieldEq: ['dataType'],
},
})
export class BaseSysParamController extends BaseController {
@Inject()
baseSysParamService: BaseSysParamService;
@Inject()
ctx: Context;
/**
* key获得网页内容()
*/
@Get('/html', { summary: '获得网页内容的参数值' })
async htmlByKey(@Query('key') key: string) {
this.ctx.body = await this.baseSysParamService.htmlByKey(key);
}
}

View File

@ -1,38 +0,0 @@
import { Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { Context } from 'vm';
import { BaseSysRoleEntity } from '../../../entity/sys/role';
import { BaseSysRoleService } from '../../../service/sys/role';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: BaseSysRoleEntity,
service: BaseSysRoleService,
// 新增的时候插入当前用户ID
insertParam: async (ctx: Context) => {
return {
userId: ctx.admin.userId,
};
},
pageQueryOp: {
keyWordLikeFields: ['a.name', 'a.label'],
where: async (ctx: Context) => {
const { userId, roleIds, username } = ctx.admin;
return [
// 超级管理员的角色不展示
['label != :label', { label: 'admin' }],
// 如果不是超管,只能看到自己新建的或者自己有的角色
[
`(userId=:userId or id in (${roleIds.join(',')}))`,
{ userId },
username !== 'admin',
],
];
},
},
})
export class BaseSysRoleController extends BaseController {}

View File

@ -1,30 +0,0 @@
import { Body, Inject, Post, Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { BaseSysUserEntity } from '../../../entity/sys/user';
import { BaseSysUserService } from '../../../service/sys/user';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: BaseSysUserEntity,
service: BaseSysUserService,
})
export class BaseSysUserController extends BaseController {
@Inject()
baseSysUserService: BaseSysUserService;
/**
*
*/
@Post('/move', { summary: '移动部门' })
async move(
@Body('departmentId') departmentId: number,
@Body('userIds') userIds: []
) {
await this.baseSysUserService.move(departmentId, userIds);
return this.ok();
}
}

View File

@ -1 +0,0 @@
这里写对外的api接口

View File

@ -1,72 +0,0 @@
import { Provide, Inject, Get, Post, Query, Config } from '@midwayjs/decorator';
import {
CoolController,
BaseController,
CoolEps,
TagTypes,
CoolUrlTag,
CoolTag,
} from '@cool-midway/core';
import { Context } from '@midwayjs/koa';
import { BaseSysParamService } from '../../service/sys/param';
import { PluginService } from '../../../plugin/service/info';
/**
*
*/
@CoolUrlTag()
@Provide()
@CoolController()
export class BaseAppCommController extends BaseController {
@Inject()
pluginService: PluginService;
@Inject()
ctx: Context;
@Config('module.base.allowKeys')
allowKeys: string[];
@Inject()
eps: CoolEps;
@Inject()
baseSysParamService: BaseSysParamService;
@CoolTag(TagTypes.IGNORE_TOKEN)
@Get('/param', { summary: '参数配置' })
async param(@Query('key') key: string) {
if (!this.allowKeys.includes(key)) {
return this.fail('非法操作');
}
return this.ok(await this.baseSysParamService.dataByKey(key));
}
/**
*
* @returns
*/
@CoolTag(TagTypes.IGNORE_TOKEN)
@Get('/eps', { summary: '实体信息与路径' })
public async getEps() {
return this.ok(this.eps.app);
}
/**
*
*/
@Post('/upload', { summary: '文件上传' })
async upload() {
const file = await this.pluginService.getInstance('upload');
return this.ok(await file.upload(this.ctx));
}
/**
*
*/
@Get('/uploadMode', { summary: '文件上传模式' })
async uploadMode() {
const file = await this.pluginService.getInstance('upload');
return this.ok(await file.getMode());
}
}

View File

@ -1,103 +0,0 @@
{
"base_sys_param": [
{
"keyName": "rich",
"name": "富文本参数",
"data": "<h3><strong>这是一个富文本</strong></h3><p>xxx</p><p>xxxxxxxxxx</p><p><br></p>",
"dataType": 1,
"remark": null
},
{
"keyName": "json",
"name": "JSON参数",
"data": "{\n \"code\": 111233\n}",
"dataType": 0,
"remark": null
},
{
"keyName": "file",
"name": "文件",
"data": "",
"dataType": 2,
"remark": null
},
{
"keyName": "text",
"name": "测试",
"data": "这是一段字符串",
"dataType": 0,
"remark": null
}
],
"base_sys_conf": [
{
"cKey": "logKeep",
"cValue": "31"
},
{
"cKey": "recycleKeep",
"cValue": "31"
}
],
"base_sys_department": [
{
"id": 1,
"name": "COOL",
"parentId": null,
"orderNum": 0
},
{
"id": 11,
"name": "开发",
"parentId": 12,
"orderNum": 2
},
{
"id": 12,
"name": "测试",
"parentId": 1,
"orderNum": 1
},
{
"id": 13,
"name": "游客",
"parentId": 1,
"orderNum": 3
}
],
"base_sys_role": [
{
"id": 1,
"userId": "1",
"name": "超管",
"label": "admin",
"remark": "最高权限的角色",
"relevance": 1,
"menuIdList": "null",
"departmentIdList": "null"
}
],
"base_sys_user": [
{
"id": 1,
"departmentId": 1,
"name": "超级管理员",
"username": "admin",
"password": "e10adc3949ba59abbe56e057f20f883e",
"passwordV": 7,
"nickName": "管理员",
"headImg": "https://cool-js.com/admin/headimg.jpg",
"phone": "18000000000",
"email": "team@cool-js.com",
"status": 1,
"remark": "拥有最高权限的用户",
"socketId": null
}
],
"base_sys_user_role": [
{
"userId": 1,
"roleId": 1
}
]
}

View File

@ -1,21 +0,0 @@
import { Rule, RuleType } from '@midwayjs/validate';
/**
*
*/
export class LoginDTO {
// 用户名
@Rule(RuleType.string().required())
username: string;
// 密码
@Rule(RuleType.string().required())
password: string;
// 验证码ID
@Rule(RuleType.string().required())
captchaId: string;
// 验证码
@Rule(RuleType.required())
verifyCode: number;
}

View File

@ -1,15 +0,0 @@
import { Column, Index, Entity } from 'typeorm';
import { BaseEntity } from '@cool-midway/core';
/**
*
*/
@Entity('base_sys_conf')
export class BaseSysConfEntity extends BaseEntity {
@Index({ unique: true })
@Column({ comment: '配置键' })
cKey: string;
@Column({ comment: '配置值' })
cValue: string;
}

View File

@ -1,19 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_department')
export class BaseSysDepartmentEntity extends BaseEntity {
@Column({ comment: '部门名称' })
name: string;
@Column({ comment: '上级部门ID', nullable: true })
parentId: number;
@Column({ comment: '排序', default: 0 })
orderNum: number;
// 父菜单名称
parentName: string;
}

View File

@ -1,23 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Index, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_log')
export class BaseSysLogEntity extends BaseEntity {
@Index()
@Column({ comment: '用户ID', nullable: true })
userId: number;
@Index()
@Column({ comment: '行为' })
action: string;
@Index()
@Column({ comment: 'ip', nullable: true })
ip: string;
@Column({ comment: '参数', nullable: true, type: 'json' })
params: string;
}

View File

@ -1,47 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_menu')
export class BaseSysMenuEntity extends BaseEntity {
@Column({ comment: '父菜单ID', nullable: true })
parentId: number;
@Column({ comment: '菜单名称' })
name: string;
@Column({ comment: '菜单地址', nullable: true })
router: string;
@Column({ comment: '权限标识', type: 'text', nullable: true })
perms: string;
@Column({
comment: '类型 0-目录 1-菜单 2-按钮',
default: 0,
})
type: number;
@Column({ comment: '图标', nullable: true })
icon: string;
@Column({ comment: '排序', default: 0 })
orderNum: number;
@Column({ comment: '视图地址', nullable: true })
viewPath: string;
@Column({ comment: '路由缓存', default: true })
keepAlive: boolean;
@Column({ comment: '是否显示', default: true })
isShow: boolean;
// 父菜单名称
parentName: string;
// 子菜单
childMenus: any;
}

View File

@ -1,27 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Index, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_param')
export class BaseSysParamEntity extends BaseEntity {
@Index({ unique: true })
@Column({ comment: '键' })
keyName: string;
@Column({ comment: '名称' })
name: string;
@Column({ comment: '数据', type: 'text' })
data: string;
@Column({
comment: '数据类型 0-字符串 1-富文本 2-文件 ',
default: 0,
})
dataType: number;
@Column({ comment: '备注', nullable: true })
remark: string;
}

View File

@ -1,31 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Index, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_role')
export class BaseSysRoleEntity extends BaseEntity {
@Column({ comment: '用户ID' })
userId: string;
@Index({ unique: true })
@Column({ comment: '名称' })
name: string;
@Index({ unique: true })
@Column({ comment: '角色标签', nullable: true, length: 50 })
label: string;
@Column({ comment: '备注', nullable: true })
remark: string;
@Column({ comment: '数据权限是否关联上下级', default: false })
relevance: boolean;
@Column({ comment: '菜单权限', type: 'json' })
menuIdList: number[];
@Column({ comment: '部门权限', type: 'json' })
departmentIdList: number[];
}

View File

@ -1,14 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_role_department')
export class BaseSysRoleDepartmentEntity extends BaseEntity {
@Column({ comment: '角色ID' })
roleId: number;
@Column({ comment: '部门ID' })
departmentId: number;
}

View File

@ -1,14 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_role_menu')
export class BaseSysRoleMenuEntity extends BaseEntity {
@Column({ comment: '角色ID' })
roleId: number;
@Column({ comment: '菜单ID' })
menuId: number;
}

View File

@ -1,54 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Index, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_user')
export class BaseSysUserEntity extends BaseEntity {
@Index()
@Column({ comment: '部门ID', nullable: true })
departmentId: number;
@Column({ comment: '姓名', nullable: true })
name: string;
@Index({ unique: true })
@Column({ comment: '用户名', length: 100 })
username: string;
@Column({ comment: '密码' })
password: string;
@Column({
comment: '密码版本, 作用是改完密码让原来的token失效',
default: 1,
})
passwordV: number;
@Column({ comment: '昵称', nullable: true })
nickName: string;
@Column({ comment: '头像', nullable: true })
headImg: string;
@Index()
@Column({ comment: '手机', nullable: true, length: 20 })
phone: string;
@Column({ comment: '邮箱', nullable: true })
email: string;
@Column({ comment: '备注', nullable: true })
remark: string;
@Column({ comment: '状态 0-禁用 1-启用', default: 1 })
status: number;
// 部门名称
departmentName: string;
// 角色ID列表
roleIdList: number[];
@Column({ comment: 'socketId', nullable: true })
socketId: string;
}

View File

@ -1,14 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('base_sys_user_role')
export class BaseSysUserRoleEntity extends BaseEntity {
@Column({ comment: '用户ID' })
userId: number;
@Column({ comment: '角色ID' })
roleId: number;
}

View File

@ -1,102 +0,0 @@
import { CoolEvent, Event } from '@cool-midway/core';
import { App, Config, ILogger, Logger } from '@midwayjs/core';
import { IMidwayKoaApplication } from '@midwayjs/koa';
import * as fs from 'fs';
import * as path from 'path';
import { v1 as uuid } from 'uuid';
/**
* jwt.secret
*/
@CoolEvent()
export class BaseAppEvent {
@Logger()
coreLogger: ILogger;
@Config('module')
config;
@Config('keys')
configKeys;
@Config('koa.port')
port;
@App()
app: IMidwayKoaApplication;
@Event('onMenuInit')
async onMenuInit() {
if (this.app.getEnv() != 'local') return;
this.checkConfig();
this.checkKeys();
}
@Event('onServerReady')
async onServerReady() {
this.coreLogger.info(`服务启动成功,端口:${this.port}`);
}
/**
*
*/
async checkConfig() {
if (this.config.base.jwt.secret == 'cool-admin-xxxxxx') {
this.coreLogger.warn(
'\x1B[36m 检测到模块[base] jwt.secret 配置是默认值,请不要关闭!即将自动修改... \x1B[0m'
);
setTimeout(() => {
const filePath = path.join(
this.app.getBaseDir(),
'..',
'src',
'modules',
'base',
'config.ts'
);
// 替换文件内容
let fileData = fs.readFileSync(filePath, 'utf8');
const secret = uuid().replace(/-/g, '');
this.config.base.jwt.secret = secret;
fs.writeFileSync(
filePath,
fileData.replace('cool-admin-xxxxxx', secret)
);
this.coreLogger.info(
'\x1B[36m [cool:module:base] midwayjs cool module base auto modify jwt.secret\x1B[0m'
);
}, 6000);
}
}
/**
* keys
*/
async checkKeys() {
if (this.configKeys == 'cool-admin-keys-xxxxxx') {
this.coreLogger.warn(
'\x1B[36m 检测到基础配置[Keys] 是默认值,请不要关闭!即将自动修改... \x1B[0m'
);
setTimeout(() => {
const filePath = path.join(
this.app.getBaseDir(),
'..',
'src',
'config',
'config.default.ts'
);
// 替换文件内容
let fileData = fs.readFileSync(filePath, 'utf8');
const secret = uuid().replace(/-/g, '');
this.config.base.jwt.secret = secret;
fs.writeFileSync(
filePath,
fileData.replace('cool-admin-keys-xxxxxx', secret)
);
this.coreLogger.info(
'\x1B[36m [cool:module:base] midwayjs cool keys auto modify \x1B[0m'
);
}, 6000);
}
}
}

View File

@ -1,40 +0,0 @@
import { CoolEvent, CoolEventManager, Event } from '@cool-midway/core';
import { BaseSysMenuService } from '../service/sys/menu';
import {
App,
ILogger,
IMidwayApplication,
Inject,
Logger,
} from '@midwayjs/core';
/**
*
*/
@CoolEvent()
export class BaseMenuEvent {
@Logger()
coreLogger: ILogger;
@Inject()
baseSysMenuService: BaseSysMenuService;
@App()
app: IMidwayApplication;
@Inject()
coolEventManager: CoolEventManager;
@Event('onMenuImport')
async onMenuImport(datas) {
for (const module in datas) {
await this.baseSysMenuService.import(datas[module]);
this.coreLogger.info(
'\x1B[36m [cool:module:base] midwayjs cool module base import [' +
module +
'] module menu success \x1B[0m'
);
}
this.coolEventManager.emit('onMenuInit', {});
}
}

View File

@ -1,25 +0,0 @@
import { Job, IJob } from '@midwayjs/cron';
import { FORMAT, ILogger, Inject } from '@midwayjs/core';
import { BaseSysLogService } from '../service/sys/log';
/**
*
*/
@Job({
cronTime: FORMAT.CRONTAB.EVERY_DAY,
start: true,
})
export class BaseLogJob implements IJob {
@Inject()
baseSysLogService: BaseSysLogService;
@Inject()
logger: ILogger;
async onTick() {
this.logger.info('清除日志定时任务开始执行');
const startTime = Date.now();
await this.baseSysLogService.clear();
this.logger.info(`清除日志定时任务结束,耗时:${Date.now() - startTime}ms`);
}
}

View File

@ -1,875 +0,0 @@
[
{
"name": "系统管理",
"router": "/sys",
"perms": null,
"type": 0,
"icon": "icon-system",
"orderNum": 2,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "权限管理",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-auth",
"orderNum": 1,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": [
{
"name": "菜单列表",
"router": "/sys/menu",
"perms": null,
"type": 1,
"icon": "icon-menu",
"orderNum": 2,
"viewPath": "modules/base/views/menu/index.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "新增",
"router": null,
"perms": "base:sys:menu:add",
"type": 2,
"icon": null,
"orderNum": 1,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
},
{
"name": "删除",
"router": null,
"perms": "base:sys:menu:delete",
"type": 2,
"icon": null,
"orderNum": 2,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
},
{
"name": "查询",
"router": null,
"perms": "base:sys:menu:page,base:sys:menu:list,base:sys:menu:info",
"type": 2,
"icon": null,
"orderNum": 4,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
},
{
"name": "参数",
"router": "/test/aa",
"perms": null,
"type": 1,
"icon": "icon-goods",
"orderNum": 0,
"viewPath": "modules/base/views/info.vue",
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "编辑",
"router": null,
"perms": "base:sys:menu:info,base:sys:menu:update",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
},
{
"name": "角色列表",
"router": "/sys/role",
"perms": null,
"type": 1,
"icon": "icon-dept",
"orderNum": 3,
"viewPath": "cool/modules/base/views/role.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "新增",
"router": null,
"perms": "base:sys:role:add",
"type": 2,
"icon": null,
"orderNum": 1,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
},
{
"name": "删除",
"router": null,
"perms": "base:sys:role:delete",
"type": 2,
"icon": null,
"orderNum": 2,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
},
{
"name": "修改",
"router": null,
"perms": "base:sys:role:update",
"type": 2,
"icon": null,
"orderNum": 3,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
},
{
"name": "查询",
"router": null,
"perms": "base:sys:role:page,base:sys:role:list,base:sys:role:info",
"type": 2,
"icon": null,
"orderNum": 4,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
}
]
},
{
"name": "用户列表",
"router": "/sys/user",
"perms": null,
"type": 1,
"icon": "icon-user",
"orderNum": 0,
"viewPath": "modules/base/views/user/index.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "部门列表",
"router": null,
"perms": "base:sys:department:list",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "新增部门",
"router": null,
"perms": "base:sys:department:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "更新部门",
"router": null,
"perms": "base:sys:department:update",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "删除部门",
"router": null,
"perms": "base:sys:department:delete",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "部门排序",
"router": null,
"perms": "base:sys:department:order",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "用户转移",
"router": null,
"perms": "base:sys:user:move",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "新增",
"router": null,
"perms": "base:sys:user:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "删除",
"router": null,
"perms": "base:sys:user:delete",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "修改",
"router": null,
"perms": "base:sys:user:delete,base:sys:user:update",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "查询",
"router": null,
"perms": "base:sys:user:page,base:sys:user:list,base:sys:user:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
}
]
},
{
"name": "参数配置",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-params",
"orderNum": 3,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "参数列表",
"router": "/sys/param",
"perms": null,
"type": 1,
"icon": "icon-menu",
"orderNum": 0,
"viewPath": "cool/modules/base/views/param.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "新增",
"router": null,
"perms": "base:sys:param:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "修改",
"router": null,
"perms": "base:sys:param:info,base:sys:param:update",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "删除",
"router": null,
"perms": "base:sys:param:delete",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "查看",
"router": null,
"perms": "base:sys:param:page,base:sys:param:list,base:sys:param:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
}
]
},
{
"name": "监控管理",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-monitor",
"orderNum": 9,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "请求日志",
"router": "/sys/log",
"perms": null,
"type": 1,
"icon": "icon-log",
"orderNum": 1,
"viewPath": "cool/modules/base/views/log.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "权限",
"router": null,
"perms": "base:sys:log:page,base:sys:log:clear,base:sys:log:getKeep,base:sys:log:setKeep",
"type": 2,
"icon": null,
"orderNum": 1,
"viewPath": null,
"keepAlive": false,
"isShow": true,
"childMenus": []
}
]
}
]
},
{
"name": "任务管理",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-activity",
"orderNum": 9,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "任务列表",
"router": "/task/list",
"perms": null,
"type": 1,
"icon": "icon-menu",
"orderNum": 0,
"viewPath": "modules/task/views/list.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "权限",
"router": null,
"perms": "task:info:page,task:info:list,task:info:info,task:info:add,task:info:delete,task:info:update,task:info:stop,task:info:start,task:info:once,task:info:log",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
}
]
}
]
},
{
"name": "框架教程",
"router": "/tutorial",
"perms": null,
"type": 0,
"icon": "icon-task",
"orderNum": 98,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "文档官网",
"router": "/tutorial/doc",
"perms": null,
"type": 1,
"icon": "icon-log",
"orderNum": 0,
"viewPath": "https://admin.cool-js.com",
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "crud 示例",
"router": "/demo/crud",
"perms": null,
"type": 1,
"icon": "icon-favor",
"orderNum": 1,
"viewPath": "modules/demo/views/crud/index.vue",
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
},
{
"name": "通用",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-radioboxfill",
"orderNum": 99,
"viewPath": null,
"keepAlive": true,
"isShow": false,
"childMenus": [
{
"name": "图片上传",
"router": null,
"perms": "space:info:page,space:info:list,space:info:info,space:info:add,space:info:delete,space:info:update,space:type:page,space:type:list,space:type:info,space:type:add,space:type:delete,space:type:update",
"type": 2,
"icon": null,
"orderNum": 1,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
},
{
"name": "首页",
"router": "/",
"perms": null,
"type": 1,
"icon": null,
"orderNum": 0,
"viewPath": "modules/demo/views/home/index.vue",
"keepAlive": true,
"isShow": false,
"childMenus": []
},
{
"name": "数据管理",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-data",
"orderNum": 7,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "字典管理",
"router": "/dict/list",
"perms": null,
"type": 1,
"icon": "icon-dict",
"orderNum": 3,
"viewPath": "modules/dict/views/list.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "删除",
"router": null,
"perms": "dict:info:delete",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "修改",
"router": null,
"perms": "dict:info:update,dict:info:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "获得字典数据",
"router": null,
"perms": "dict:info:data",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "单个信息",
"router": null,
"perms": "dict:info:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "列表查询",
"router": null,
"perms": "dict:info:list",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "分页查询",
"router": null,
"perms": "dict:info:page",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "新增",
"router": null,
"perms": "dict:info:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "组权限",
"router": null,
"perms": "dict:type:list,dict:type:update,dict:type:delete,dict:type:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "字典类型",
"router": null,
"perms": "dict:type:delete,dict:type:update,dict:type:info,dict:type:list,dict:type:page,dict:type:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
},
{
"name": "数据回收站",
"router": "/recycle/data",
"perms": null,
"type": 1,
"icon": "icon-delete",
"orderNum": 6,
"viewPath": "modules/recycle/views/data.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "恢复数据",
"router": null,
"perms": "recycle:data:restore",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "单个信息",
"router": null,
"perms": "recycle:data:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "分页查询",
"router": null,
"perms": "recycle:data:page",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
},
{
"name": "文件管理",
"router": "/upload/list",
"perms": null,
"type": 1,
"icon": "icon-log",
"orderNum": 5,
"viewPath": "modules/space/views/list.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "权限",
"router": null,
"perms": "space:type:delete,space:type:update,space:type:info,space:type:list,space:type:page,space:type:add,space:info:getConfig,space:info:delete,space:info:update,space:info:info,space:info:list,space:info:page,space:info:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
}
]
},
{
"name": "用户管理",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-user",
"orderNum": 11,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "用户列表",
"router": "/user/list",
"perms": null,
"type": 1,
"icon": "icon-menu",
"orderNum": 1,
"viewPath": "modules/user/views/list.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "删除",
"router": null,
"perms": "user:info:delete",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "修改",
"router": null,
"perms": "user:info:update,user:info:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "单个信息",
"router": null,
"perms": "user:info:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "列表查询",
"router": null,
"perms": "user:info:list",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "分页查询",
"router": null,
"perms": "user:info:page",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
},
{
"name": "新增",
"router": null,
"perms": "user:info:add",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
}
]
},
{
"name": "扩展管理",
"router": null,
"perms": null,
"type": 0,
"icon": "icon-favor",
"orderNum": 8,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "后端插件",
"router": "/helper/plugins/serve",
"perms": null,
"type": 1,
"icon": "icon-component",
"orderNum": 2,
"viewPath": "modules/helper/views/plugins/serve.vue",
"keepAlive": true,
"isShow": true,
"childMenus": [
{
"name": "权限",
"router": null,
"perms": "plugin:info:install,plugin:info:delete,plugin:info:update,plugin:info:page,plugin:info:info",
"type": 2,
"icon": null,
"orderNum": 0,
"viewPath": null,
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
},
{
"name": "前端插件",
"router": "/helper/plugins/vue",
"perms": null,
"type": 1,
"icon": "icon-vue",
"orderNum": 1,
"viewPath": "modules/helper/views/plugins/vue.vue",
"keepAlive": true,
"isShow": true,
"childMenus": []
}
]
}
]

View File

@ -1,185 +0,0 @@
import { App, Config, Inject, Middleware } from '@midwayjs/decorator';
import * as _ from 'lodash';
import { CoolUrlTagData, RESCODE, TagTypes } from '@cool-midway/core';
import * as jwt from 'jsonwebtoken';
import { NextFunction, Context } from '@midwayjs/koa';
import {
IMiddleware,
IMidwayApplication,
Init,
InjectClient,
} from '@midwayjs/core';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
/**
*
*/
@Middleware()
export class BaseAuthorityMiddleware
implements IMiddleware<Context, NextFunction>
{
@Config('koa.globalPrefix')
prefix;
@Config('module.base')
jwtConfig;
@InjectClient(CachingFactory, 'default')
midwayCache: MidwayCache;
@Inject()
coolUrlTagData: CoolUrlTagData;
@App()
app: IMidwayApplication;
ignoreUrls: string[] = [];
@Init()
async init() {
this.ignoreUrls = this.coolUrlTagData.byKey(TagTypes.IGNORE_TOKEN, 'admin');
}
resolve() {
return async (ctx: Context, next: NextFunction) => {
let statusCode = 200;
let { url } = ctx;
url = url.replace(this.prefix, '').split('?')[0];
const token = ctx.get('Authorization');
const adminUrl = '/admin/';
// 路由地址为 admin前缀的 需要权限校验
if (_.startsWith(url, adminUrl)) {
try {
ctx.admin = jwt.verify(token, this.jwtConfig.jwt.secret);
if (ctx.admin.isRefresh) {
ctx.status = 401;
ctx.body = {
code: RESCODE.COMMFAIL,
message: '登录失效~',
};
return;
}
} catch (error) {}
// 使用matchUrl方法来检查URL是否应该被忽略
const isIgnored = this.ignoreUrls.some(pattern =>
this.matchUrl(pattern, url)
);
if (isIgnored) {
await next();
return;
}
if (ctx.admin) {
const rToken = await this.midwayCache.get(
`admin:token:${ctx.admin.userId}`
);
// 判断密码版本是否正确
const passwordV = await this.midwayCache.get(
`admin:passwordVersion:${ctx.admin.userId}`
);
if (passwordV != ctx.admin.passwordVersion) {
ctx.status = 401;
ctx.body = {
code: RESCODE.COMMFAIL,
message: '登录失效~',
};
return;
}
// 超管拥有所有权限
if (ctx.admin.username == 'admin' && !ctx.admin.isRefresh) {
if (rToken !== token && this.jwtConfig.jwt.sso) {
ctx.status = 401;
ctx.body = {
code: RESCODE.COMMFAIL,
message: '登录失效~',
};
return;
} else {
await next();
return;
}
}
// 要登录每个人都有权限的接口
if (
new RegExp(`^${adminUrl}?.*/comm/`).test(url) ||
// 字典接口
url == '/admin/dict/info/data'
) {
await next();
return;
}
// 如果传的token是refreshToken则校验失败
if (ctx.admin.isRefresh) {
ctx.status = 401;
ctx.body = {
code: RESCODE.COMMFAIL,
message: '登录失效~',
};
return;
}
if (!rToken) {
ctx.status = 401;
ctx.body = {
code: RESCODE.COMMFAIL,
message: '登录失效或无权限访问~',
};
return;
}
if (rToken !== token && this.jwtConfig.jwt.sso) {
statusCode = 401;
} else {
let perms: string[] = await this.midwayCache.get(
`admin:perms:${ctx.admin.userId}`
);
if (!_.isEmpty(perms)) {
perms = perms.map(e => {
return e.replace(/:/g, '/');
});
if (!perms.includes(url.split('?')[0].replace('/admin/', ''))) {
statusCode = 403;
}
} else {
statusCode = 403;
}
}
} else {
statusCode = 401;
}
if (statusCode > 200) {
ctx.status = statusCode;
ctx.body = {
code: RESCODE.COMMFAIL,
message: '登录失效或无权限访问~',
};
return;
}
}
await next();
};
}
// 匹配URL的方法
matchUrl(pattern, url) {
const patternSegments = pattern.split('/').filter(Boolean);
const urlSegments = url.split('/').filter(Boolean);
// 如果段的数量不同,则无法匹配
if (patternSegments.length !== urlSegments.length) {
return false;
}
// 逐段进行匹配
for (let i = 0; i < patternSegments.length; i++) {
if (patternSegments[i].startsWith(':')) {
// 如果模式段以':'开始,我们认为它是一个参数,可以匹配任何内容
continue;
}
// 如果两个段不相同,则不匹配
if (patternSegments[i] !== urlSegments[i]) {
return false;
}
}
// 所有段都匹配
return true;
}
}

View File

@ -1,26 +0,0 @@
import { Middleware } from '@midwayjs/decorator';
import * as _ from 'lodash';
import { NextFunction, Context } from '@midwayjs/koa';
import { IMiddleware } from '@midwayjs/core';
import { BaseSysLogService } from '../service/sys/log';
/**
*
*/
@Middleware()
export class BaseLogMiddleware implements IMiddleware<Context, NextFunction> {
resolve() {
return async (ctx: Context, next: NextFunction) => {
const baseSysLogService = await ctx.requestContext.getAsync(
BaseSysLogService
);
baseSysLogService.record(
ctx,
ctx.url,
ctx.req.method === 'GET' ? ctx.request.query : ctx.request.body,
ctx.admin ? ctx.admin.userId : null
);
await next();
};
}
}

View File

@ -1,39 +0,0 @@
import { Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseSysConfEntity } from '../../entity/sys/conf';
/**
*
*/
@Provide()
export class BaseSysConfService extends BaseService {
@InjectEntityModel(BaseSysConfEntity)
baseSysConfEntity: Repository<BaseSysConfEntity>;
/**
*
* @param key
*/
async getValue(key) {
const conf = await this.baseSysConfEntity.findOneBy({ cKey: key });
if (conf) {
return conf.cValue;
}
}
/**
*
* @param cKey
* @param cValue
*/
async updateVaule(cKey, cValue) {
await this.baseSysConfEntity
.createQueryBuilder()
.update()
.where({ cKey })
.set({ cKey, cValue })
.execute();
}
}

View File

@ -1,10 +0,0 @@
import { DataSource } from 'typeorm';
export class TempDataSource extends DataSource {
/**
*
*/
async buildMetadatas() {
await super.buildMetadatas();
}
}

View File

@ -1,124 +0,0 @@
import { Inject, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, Repository } from 'typeorm';
import { BaseSysDepartmentEntity } from '../../entity/sys/department';
import * as _ from 'lodash';
import { BaseSysRoleDepartmentEntity } from '../../entity/sys/role_department';
import { BaseSysPermsService } from './perms';
import { BaseSysUserEntity } from '../../entity/sys/user';
/**
*
*/
@Provide()
export class BaseSysDepartmentService extends BaseService {
@InjectEntityModel(BaseSysDepartmentEntity)
baseSysDepartmentEntity: Repository<BaseSysDepartmentEntity>;
@InjectEntityModel(BaseSysUserEntity)
baseSysUserEntity: Repository<BaseSysUserEntity>;
@InjectEntityModel(BaseSysRoleDepartmentEntity)
baseSysRoleDepartmentEntity: Repository<BaseSysRoleDepartmentEntity>;
@Inject()
baseSysPermsService: BaseSysPermsService;
@Inject()
ctx;
/**
*
*/
async list() {
// 部门权限
const permsDepartmentArr = await this.baseSysPermsService.departmentIds(
this.ctx.admin.userId
);
// 过滤部门权限
const find = this.baseSysDepartmentEntity.createQueryBuilder('a');
if (this.ctx.admin.username !== 'admin')
find.andWhere('a.id in (:...ids)', {
ids: !_.isEmpty(permsDepartmentArr) ? permsDepartmentArr : [null],
});
find.addOrderBy('a.orderNum', 'ASC');
const departments: BaseSysDepartmentEntity[] = await find.getMany();
if (!_.isEmpty(departments)) {
departments.forEach(e => {
const parentMenu = departments.filter(m => {
e.parentId = parseInt(e.parentId + '');
if (e.parentId == m.id) {
return m.name;
}
});
if (!_.isEmpty(parentMenu)) {
e.parentName = parentMenu[0].name;
}
});
}
return departments;
}
/**
* ID获得部门权限信息
* @param {[]} roleIds
* @param isAdmin
*/
async getByRoleIds(roleIds: number[], isAdmin) {
if (!_.isEmpty(roleIds)) {
if (isAdmin) {
const result = await this.baseSysDepartmentEntity.find();
return result.map(e => {
return e.id;
});
}
const result = await this.baseSysRoleDepartmentEntity
.createQueryBuilder('a')
.where('a.roleId in (:...roleIds)', { roleIds })
.getMany();
if (!_.isEmpty(result)) {
return _.uniq(
result.map(e => {
return e.departmentId;
})
);
}
}
return [];
}
/**
*
* @param params
*/
async order(params) {
for (const e of params) {
await this.baseSysDepartmentEntity.update(e.id, e);
}
}
/**
*
*/
async delete(ids: number[]) {
const { deleteUser } = this.ctx.request.body;
await super.delete(ids);
if (deleteUser) {
await this.baseSysUserEntity.delete({ departmentId: In(ids) });
} else {
const topDepartment = await this.baseSysDepartmentEntity
.createQueryBuilder('a')
.where('a.parentId is null')
.getOne();
if (topDepartment) {
await this.baseSysUserEntity.update(
{ departmentId: In(ids) },
{ departmentId: topDepartment.id }
);
}
}
}
}

View File

@ -1,62 +0,0 @@
import { Inject, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { LessThan, Repository } from 'typeorm';
import * as _ from 'lodash';
import { BaseSysLogEntity } from '../../entity/sys/log';
import * as moment from 'moment';
import { Utils } from '../../../../comm/utils';
import { BaseSysConfService } from './conf';
import { Context } from '@midwayjs/koa';
/**
*
*/
@Provide()
export class BaseSysLogService extends BaseService {
@Inject()
ctx;
@Inject()
utils: Utils;
@InjectEntityModel(BaseSysLogEntity)
baseSysLogEntity: Repository<BaseSysLogEntity>;
@Inject()
baseSysConfService: BaseSysConfService;
/**
*
* @param url URL地址
* @param params
* @param userId ID
*/
async record(context: Context, url, params, userId) {
const ip = await this.utils.getReqIP(context);
const sysLog = new BaseSysLogEntity();
sysLog.userId = userId;
sysLog.ip = typeof ip === 'string' ? ip : ip.join(',');
sysLog.action = url.split('?')[0];
sysLog.params = params;
await this.baseSysLogEntity.insert(sysLog);
}
/**
*
* @param isAll
*/
async clear(isAll?) {
if (isAll) {
await this.baseSysLogEntity.clear();
return;
}
const keepDay = await this.baseSysConfService.getValue('logKeep');
if (keepDay) {
const beforeDate = moment().add(-keepDay, 'days').startOf('day').toDate();
await this.baseSysLogEntity.delete({ createTime: LessThan(beforeDate) });
} else {
await this.baseSysLogEntity.clear();
}
}
}

View File

@ -1,251 +0,0 @@
import { Inject, Provide, Config, InjectClient } from '@midwayjs/decorator';
import { BaseService, CoolCommException } from '@cool-midway/core';
import { LoginDTO } from '../../dto/login';
import * as svgCaptcha from 'svg-captcha';
import { v1 as uuid } from 'uuid';
import { BaseSysUserEntity } from '../../entity/sys/user';
import { Repository } from 'typeorm';
import { InjectEntityModel } from '@midwayjs/typeorm';
import * as md5 from 'md5';
import { BaseSysRoleService } from './role';
import * as _ from 'lodash';
import { BaseSysMenuService } from './menu';
import { BaseSysDepartmentService } from './department';
import * as jwt from 'jsonwebtoken';
import * as svgToDataURL from 'mini-svg-data-uri';
import { Context } from '@midwayjs/koa';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
import { readFileSync } from 'fs';
const { svg2png, initialize } = require('svg2png-wasm');
initialize(readFileSync('./node_modules/svg2png-wasm/svg2png_wasm_bg.wasm'));
/**
*
*/
@Provide()
export class BaseSysLoginService extends BaseService {
@InjectClient(CachingFactory, 'default')
midwayCache: MidwayCache;
@InjectEntityModel(BaseSysUserEntity)
baseSysUserEntity: Repository<BaseSysUserEntity>;
@Inject()
baseSysRoleService: BaseSysRoleService;
@Inject()
baseSysMenuService: BaseSysMenuService;
@Inject()
baseSysDepartmentService: BaseSysDepartmentService;
@Inject()
ctx: Context;
@Config('module.base')
coolConfig;
/**
*
* @param login
*/
async login(login: LoginDTO) {
const { username, captchaId, verifyCode, password } = login;
// 校验验证码
const checkV = await this.captchaCheck(captchaId, verifyCode);
if (checkV) {
const user = await this.baseSysUserEntity.findOneBy({ username });
// 校验用户
if (user) {
// 校验用户状态及密码
if (user.status === 0 || user.password !== md5(password)) {
throw new CoolCommException('账户或密码不正确~');
}
} else {
throw new CoolCommException('账户或密码不正确~');
}
// 校验角色
const roleIds = await this.baseSysRoleService.getByUser(user.id);
if (_.isEmpty(roleIds)) {
throw new CoolCommException('该用户未设置任何角色,无法登录~');
}
// 生成token
const { expire, refreshExpire } = this.coolConfig.jwt.token;
const result = {
expire,
token: await this.generateToken(user, roleIds, expire),
refreshExpire,
refreshToken: await this.generateToken(
user,
roleIds,
refreshExpire,
true
),
};
// 将用户相关信息保存到缓存
const perms = await this.baseSysMenuService.getPerms(roleIds);
const departments = await this.baseSysDepartmentService.getByRoleIds(
roleIds,
user.username === 'admin'
);
await this.midwayCache.set(`admin:department:${user.id}`, departments);
await this.midwayCache.set(`admin:perms:${user.id}`, perms);
await this.midwayCache.set(`admin:token:${user.id}`, result.token);
await this.midwayCache.set(
`admin:token:refresh:${user.id}`,
result.token
);
return result;
} else {
throw new CoolCommException('验证码不正确');
}
}
/**
*
* @param type svg
* @param width
* @param height
*/
async captcha(type: string, width = 150, height = 50, color = '#fff') {
const svg = svgCaptcha.create({
ignoreChars: 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM',
width,
height,
});
const result = {
captchaId: uuid(),
data: svg.data.replace(/"/g, "'"),
};
// 文字变白
const rpList = [
'#111',
'#222',
'#333',
'#444',
'#555',
'#666',
'#777',
'#888',
'#999',
];
rpList.forEach(rp => {
result.data = result.data['replaceAll'](rp, color);
});
if (type === 'base64') {
result.data = svgToDataURL(result.data);
}
if (type === 'png') {
result.data = await svg2png(result.data, {
scale: 2, // optional
width, // optional
height, // optional
backgroundColor: 'white', // optional
});
result.data =
'data:image/png;base64,' +
Buffer.from(result.data, 'binary').toString('base64');
}
// 半小时过期
await this.midwayCache.set(
`verify:img:${result.captchaId}`,
svg.text.toLowerCase(),
1800 * 1000
);
return result;
}
/**
* 退
*/
async logout() {
if (!this.coolConfig.jwt.sso) return;
const { userId } = this.ctx.admin;
await this.midwayCache.del(`admin:department:${userId}`);
await this.midwayCache.del(`admin:perms:${userId}`);
await this.midwayCache.del(`admin:token:${userId}`);
await this.midwayCache.del(`admin:token:refresh:${userId}`);
await this.midwayCache.del(`admin:passwordVersion:${userId}`);
}
/**
*
* @param captchaId ID
* @param value
*/
async captchaCheck(captchaId, value) {
const rv = await this.midwayCache.get(`verify:img:${captchaId}`);
if (!rv || !value || value.toLowerCase() !== rv) {
return false;
} else {
this.midwayCache.del(`verify:img:${captchaId}`);
return true;
}
}
/**
* token
* @param user
* @param roleIds
* @param expire
* @param isRefresh
*/
async generateToken(user, roleIds, expire, isRefresh?) {
await this.midwayCache.set(
`admin:passwordVersion:${user.id}`,
user.passwordV
);
const tokenInfo = {
isRefresh: false,
roleIds,
username: user.username,
userId: user.id,
passwordVersion: user.passwordV,
};
if (isRefresh) {
tokenInfo.isRefresh = true;
}
return jwt.sign(tokenInfo, this.coolConfig.jwt.secret, {
expiresIn: expire,
});
}
/**
* token
* @param token
*/
async refreshToken(token: string) {
const decoded = jwt.verify(token, this.coolConfig.jwt.secret);
if (decoded && decoded['isRefresh']) {
delete decoded['exp'];
delete decoded['iat'];
const { expire, refreshExpire } = this.coolConfig.jwt.token;
decoded['isRefresh'] = false;
const result = {
expire,
token: jwt.sign(decoded, this.coolConfig.jwt.secret, {
expiresIn: expire,
}),
refreshExpire,
refreshToken: '',
};
decoded['isRefresh'] = true;
result.refreshToken = jwt.sign(decoded, this.coolConfig.jwt.secret, {
expiresIn: refreshExpire,
});
await this.midwayCache.set(
`admin:passwordVersion:${decoded['userId']}`,
decoded['passwordVersion']
);
await this.midwayCache.set(
`admin:token:${decoded['userId']}`,
result.token
);
return result;
}
}
}

View File

@ -1,463 +0,0 @@
import { App, IMidwayApplication, Scope, ScopeEnum } from '@midwayjs/core';
import { ALL, Config, Inject, Provide } from '@midwayjs/decorator';
import { BaseService, CoolCommException } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, Repository } from 'typeorm';
import { BaseSysMenuEntity } from '../../entity/sys/menu';
import * as _ from 'lodash';
import { BaseSysPermsService } from './perms';
import { Context } from '@midwayjs/koa';
import { TempDataSource } from './data';
// eslint-disable-next-line node/no-unpublished-import
import * as ts from 'typescript';
import * as fs from 'fs';
import * as pathUtil from 'path';
import { BaseSysRoleMenuEntity } from '../../entity/sys/role_menu';
import { BaseSysUserRoleEntity } from '../../entity/sys/user_role';
/**
*
*/
@Scope(ScopeEnum.Request, { allowDowngrade: true })
@Provide()
export class BaseSysMenuService extends BaseService {
@Inject()
ctx: Context;
@InjectEntityModel(BaseSysMenuEntity)
baseSysMenuEntity: Repository<BaseSysMenuEntity>;
@InjectEntityModel(BaseSysRoleMenuEntity)
baseSysRoleMenuEntity: Repository<BaseSysRoleMenuEntity>;
@Inject()
baseSysPermsService: BaseSysPermsService;
@Config(ALL)
config;
@App()
app: IMidwayApplication;
/**
*
*/
async list() {
const menus = await this.getMenus(
this.ctx.admin.roleIds,
this.ctx.admin.username === 'admin'
);
if (!_.isEmpty(menus)) {
menus.forEach((e: any) => {
const parentMenu = menus.filter(m => {
e.parentId = parseInt(e.parentId);
if (e.parentId == m.id) {
return m.name;
}
});
if (!_.isEmpty(parentMenu)) {
e.parentName = parentMenu[0].name;
}
});
}
return menus;
}
/**
*
* @param param
*/
async modifyAfter(param) {
if (param.id) {
await this.refreshPerms(param.id);
}
}
/**
*
* @param {[]} roleIds
*/
async getPerms(roleIds) {
let perms = [];
if (!_.isEmpty(roleIds)) {
const find = await this.baseSysMenuEntity.createQueryBuilder('a');
if (!roleIds.includes(1)) {
find.innerJoinAndSelect(
BaseSysRoleMenuEntity,
'b',
'a.id = b.menuId AND b.roleId in (:...roleIds)',
{ roleIds }
);
}
find.where('a.perms is not NULL');
const result = await find.getMany();
if (result) {
result.forEach(d => {
if (d.perms) {
perms = perms.concat(d.perms.split(','));
}
});
}
perms = _.uniq(perms);
perms = _.remove(perms, n => {
return !_.isEmpty(n);
});
}
return _.uniq(perms);
}
/**
*
* @param roleIds
* @param isAdmin
*/
async getMenus(roleIds, isAdmin) {
const find = this.baseSysMenuEntity.createQueryBuilder('a');
if (!isAdmin) {
find.innerJoinAndSelect(
BaseSysRoleMenuEntity,
'b',
'a.id = b.menuId AND b.roleId in (:...roleIds)',
{ roleIds }
);
}
find.orderBy('a.orderNum', 'ASC');
const list = await find.getMany();
return _.uniqBy(list, 'id');
}
/**
*
* @param ids
*/
async delete(ids) {
let idArr;
if (ids instanceof Array) {
idArr = ids;
} else {
idArr = ids.split(',');
}
for (const id of idArr) {
await this.baseSysMenuEntity.delete({ id });
await this.delChildMenu(id);
}
}
/**
*
* @param id
*/
private async delChildMenu(id) {
await this.refreshPerms(id);
const delMenu = await this.baseSysMenuEntity.findBy({ parentId: id });
if (_.isEmpty(delMenu)) {
return;
}
const delMenuIds = delMenu.map(e => {
return e.id;
});
await this.baseSysMenuEntity.delete(delMenuIds);
for (const menuId of delMenuIds) {
await this.delChildMenu(menuId);
}
}
/**
*
* @param menuId
*/
async refreshPerms(menuId) {
const find = this.baseSysRoleMenuEntity.createQueryBuilder('a');
find.leftJoinAndSelect(BaseSysUserRoleEntity, 'b', 'a.roleId = b.roleId');
find.where('a.menuId = :menuId', { menuId: menuId });
find.select('b.userId', 'userId');
const users = await find.getRawMany();
// 刷新admin权限
await this.baseSysPermsService.refreshPerms(1);
if (!_.isEmpty(users)) {
// 刷新其他权限
for (const user of _.uniqBy(users, 'userId')) {
await this.baseSysPermsService.refreshPerms(user.userId);
}
}
}
/**
* Controller
* @param entityString
* @param controller
* @param module
*/
async parse(entityString: string, controller: string, module: string) {
const tempDataSource = new TempDataSource({
...this.config.typeorm.dataSource.default,
entities: [],
});
// 连接数据库
await tempDataSource.initialize();
const { newCode, className, oldTableName } = this.parseCode(entityString);
const code = ts.transpile(
`${newCode}
tempDataSource.options.entities.push(${className})
`,
{
emitDecoratorMetadata: true,
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2018,
removeComments: true,
experimentalDecorators: true,
noImplicitThis: true,
noUnusedLocals: true,
stripInternal: true,
skipLibCheck: true,
pretty: true,
declaration: true,
noImplicitAny: false,
}
);
eval(code);
await tempDataSource.buildMetadatas();
const meta = tempDataSource.getMetadata(className);
const columnArr = meta.columns;
await tempDataSource.destroy();
const commColums = [];
const columns = _.filter(
columnArr.map(e => {
return {
propertyName: e.propertyName,
type: typeof e.type == 'string' ? e.type : e.type.name.toLowerCase(),
length: e.length,
comment: e.comment,
nullable: e.isNullable,
};
}),
o => {
if (['createTime', 'updateTime'].includes(o.propertyName)) {
commColums.push(o);
}
return o && !['createTime', 'updateTime'].includes(o.propertyName);
}
).concat(commColums);
if (!controller) {
const tableNames = oldTableName.split('_');
const fileName = tableNames[tableNames.length - 1];
return {
columns,
className: className.replace('TEMP', ''),
tableName: oldTableName,
fileName,
path: `/admin/${module}/${fileName}`,
};
}
const fileName = await this.fileName(controller);
return {
columns,
path: `/admin/${module}/${fileName}`,
};
}
/**
* Entity类名
* @param code
* @returns
*/
parseCode(code: string) {
try {
const oldClassName = code
.match('class(.*)extends')[1]
.replace(/\s*/g, '');
const oldTableStart = code.indexOf('@Entity(');
const oldTableEnd = code.indexOf(')');
const oldTableName = code
.substring(oldTableStart + 9, oldTableEnd - 1)
.replace(/\s*/g, '')
// eslint-disable-next-line no-useless-escape
.replace(/\"/g, '')
// eslint-disable-next-line no-useless-escape
.replace(/\'/g, '');
const className = `${oldClassName}TEMP`;
return {
newCode: code
.replace(oldClassName, className)
.replace(oldTableName, `func_${oldTableName}`),
className,
tableName: `func_${oldTableName}`,
oldTableName,
};
} catch (err) {
throw new CoolCommException('代码结构不正确,请检查');
}
}
/**
*
* @param body body
*/
async create(body) {
const { module, entity, controller, service, fileName } = body;
const basePath = this.app.getBaseDir();
const modulePath = pathUtil.join(basePath, '..', 'src', 'modules', module);
// 生成Entity
const entityPath = pathUtil.join(modulePath, 'entity', `${fileName}.ts`);
// 生成Controller
const controllerPath = pathUtil.join(
modulePath,
'controller',
'admin',
`${fileName}.ts`
);
// 生成Service
const servicePath = pathUtil.join(modulePath, 'service', `${fileName}.ts`);
this.createConfigFile(module);
this.createFile(entityPath, entity);
this.createFile(controllerPath, controller);
this.createFile(servicePath, service);
}
/**
*
* @param module
*/
async createConfigFile(module: string) {
const basePath = this.app.getBaseDir();
const configFilePath = pathUtil.join(
basePath,
'..',
'src',
'modules',
module,
'config.ts'
);
if (!fs.existsSync(configFilePath)) {
const data = `import { ModuleConfig } from '@cool-midway/core';
/**
*
*/
export default () => {
return {
// 模块名称
name: 'xxx',
// 模块描述
description: 'xxx',
// 中间件,只对本模块有效
middlewares: [],
// 中间件,全局有效
globalMiddlewares: [],
// 模块加载顺序默认为0值越大越优先加载
order: 0,
} as ModuleConfig;
};
`;
await this.createFile(configFilePath, data);
}
}
/**
*
* @param controller
* @returns
*/
async fileName(controller: string) {
const regex = /import\s*{\s*\w+\s*}\s*from\s*'[^']*\/([\w-]+)';/;
const match = regex.exec(controller);
if (match && match.length > 1) {
return match[1];
}
return null;
}
/**
*
* @param filePath
* @param content
*/
async createFile(filePath: string, content: string) {
const folderPath = pathUtil.dirname(filePath);
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath, { recursive: true });
}
fs.writeFileSync(filePath, content);
}
/**
*
* @param ids
* @returns
*/
async export(ids: number[]) {
const result: any[] = [];
const menus = await this.baseSysMenuEntity.findBy({ id: In(ids) });
// 递归取出子菜单
const getChildMenus = (parentId: number): any[] => {
const children = _.remove(menus, e => e.parentId == parentId);
children.forEach(child => {
child.childMenus = getChildMenus(child.id);
// 删除不需要的字段
delete child.id;
delete child.createTime;
delete child.updateTime;
delete child.parentId;
});
return children;
};
// lodash取出父级菜单(parentId为 null) 并从menus 删除
const parentMenus = _.remove(menus, e => {
return e.parentId == null;
});
// 对于每个父级菜单,获取它的子菜单
parentMenus.forEach(parent => {
parent.childMenus = getChildMenus(parent.id);
// 删除不需要的字段
delete parent.id;
delete parent.createTime;
delete parent.updateTime;
delete parent.parentId;
result.push(parent);
});
return result;
}
/**
*
* @param menus
*/
async import(menus: any[]) {
// 递归保存子菜单
const saveChildMenus = async (parentMenu: any, parentId: number | null) => {
const children = parentMenu.childMenus || [];
for (let child of children) {
const childData = { ...child, parentId: parentId }; // 保持与数据库的parentId字段的一致性
delete childData.childMenus; // 删除childMenus属性因为我们不想将它保存到数据库中
// 保存子菜单并获取其ID以便为其子菜单设置parentId
const savedChild = await this.baseSysMenuEntity.save(childData);
if (!_.isEmpty(child.childMenus)) {
await saveChildMenus(child, savedChild.id);
}
}
};
for (let menu of menus) {
const menuData = { ...menu };
delete menuData.childMenus; // 删除childMenus属性因为我们不想将它保存到数据库中
// 保存主菜单并获取其ID
const savedMenu = await this.baseSysMenuEntity.save(menuData);
if (menu.childMenus && menu.childMenus.length > 0) {
await saveChildMenus(menu, savedMenu.id);
}
}
}
}

View File

@ -1,91 +0,0 @@
import { Inject, InjectClient, Provide } from '@midwayjs/decorator';
import { BaseService, CoolCommException } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Not, Repository } from 'typeorm';
import { BaseSysParamEntity } from '../../entity/sys/param';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
/**
*
*/
@Provide()
export class BaseSysParamService extends BaseService {
@InjectEntityModel(BaseSysParamEntity)
baseSysParamEntity: Repository<BaseSysParamEntity>;
@InjectClient(CachingFactory, 'default')
midwayCache: MidwayCache;
/**
* key获得对应的参数
* @param key
*/
async dataByKey(key) {
let result: any = await this.midwayCache.get(`param:${key}`);
if (!result) {
result = await this.baseSysParamEntity.findOneBy({ keyName: key });
this.midwayCache.set(`param:${key}`, result);
}
if (result) {
if (result.dataType == 0) {
try {
return JSON.parse(result.data);
} catch (error) {
return result.data;
}
}
if (result.dataType == 1) {
return result.data;
}
if (result.dataType == 2) {
return result.data.split(',');
}
}
return;
}
/**
* key获得对应的网页数据
* @param key
*/
async htmlByKey(key) {
let html = '<html><title>@title</title><body>@content</body></html>';
let result: any = await this.midwayCache.get(`param:${key}`);
if (result) {
html = html
.replace('@content', result.data)
.replace('@title', result.name);
} else {
html = html.replace('@content', 'key notfound');
}
return html;
}
/**
*
* @param param
*/
async addOrUpdate(param: any, type): Promise<void> {
const find = {
keyName: param.keyName,
};
if (param.id) {
find['id'] = Not(param.id);
}
const check = await this.baseSysParamEntity.findOneBy(find);
if (check) {
throw new CoolCommException('存在相同的keyName');
}
await super.addOrUpdate(param, type);
}
/**
*
*/
async modifyAfter() {
const params = await this.baseSysParamEntity.find();
for (const param of params) {
await this.midwayCache.set(`param:${param.keyName}`, param);
}
}
}

View File

@ -1,90 +0,0 @@
import { Inject, InjectClient, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { BaseSysMenuService } from './menu';
import { BaseSysRoleService } from './role';
import { BaseSysDepartmentService } from './department';
import { Context } from '@midwayjs/koa';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
import { BaseSysRoleEntity } from '../../entity/sys/role';
import { In, Repository } from 'typeorm';
import { InjectEntityModel } from '@midwayjs/typeorm';
/**
*
*/
@Provide()
export class BaseSysPermsService extends BaseService {
@InjectClient(CachingFactory, 'default')
midwayCache: MidwayCache;
@Inject()
baseSysMenuService: BaseSysMenuService;
@Inject()
baseSysRoleService: BaseSysRoleService;
@Inject()
baseSysDepartmentService: BaseSysDepartmentService;
@InjectEntityModel(BaseSysRoleEntity)
baseSysRoleEntity: Repository<BaseSysRoleEntity>;
@Inject()
ctx: Context;
base: any;
/**
*
* @param userId ID
*/
async refreshPerms(userId) {
const roleIds = await this.baseSysRoleService.getByUser(userId);
const perms = await this.baseSysMenuService.getPerms(roleIds);
await this.midwayCache.set(`admin:perms:${userId}`, perms);
// 更新部门权限
const departments = await this.baseSysDepartmentService.getByRoleIds(
roleIds,
await this.isAdmin(roleIds)
);
await this.midwayCache.set(`admin:department:${userId}`, departments);
}
/**
*
* @param roleIds
*/
async isAdmin(roleIds: number[]) {
const roles = await this.baseSysRoleEntity.findBy({ id: In(roleIds) });
const roleLabels = roles.map(item => item.label);
return roleLabels.includes('admin');
}
/**
*
* @param roleIds
*/
async permmenu(roleIds: number[]) {
const perms = await this.baseSysMenuService.getPerms(roleIds);
const menus = await this.baseSysMenuService.getMenus(
roleIds,
this.ctx.admin.username === 'admin'
);
return { perms, menus };
}
/**
* ID获得部门权限
* @param userId
* @return ID数组
*/
async departmentIds(userId: number) {
const department: any = await this.midwayCache.get(
`admin:department:${userId}`
);
if (department) {
return department;
} else {
return [];
}
}
}

View File

@ -1,136 +0,0 @@
import { Inject, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseSysRoleEntity } from '../../entity/sys/role';
import { BaseSysUserRoleEntity } from '../../entity/sys/user_role';
import * as _ from 'lodash';
import { BaseSysRoleMenuEntity } from '../../entity/sys/role_menu';
import { BaseSysRoleDepartmentEntity } from '../../entity/sys/role_department';
import { BaseSysPermsService } from './perms';
import { Brackets } from 'typeorm';
/**
*
*/
@Provide()
export class BaseSysRoleService extends BaseService {
@InjectEntityModel(BaseSysRoleEntity)
baseSysRoleEntity: Repository<BaseSysRoleEntity>;
@InjectEntityModel(BaseSysUserRoleEntity)
baseSysUserRoleEntity: Repository<BaseSysUserRoleEntity>;
@InjectEntityModel(BaseSysRoleMenuEntity)
baseSysRoleMenuEntity: Repository<BaseSysRoleMenuEntity>;
@InjectEntityModel(BaseSysRoleDepartmentEntity)
baseSysRoleDepartmentEntity: Repository<BaseSysRoleDepartmentEntity>;
@Inject()
baseSysPermsService: BaseSysPermsService;
@Inject()
ctx;
/**
* ID获得所有用户角色
* @param userId
*/
async getByUser(userId: number): Promise<number[]> {
const userRole = await this.baseSysUserRoleEntity.findBy({ userId });
if (!_.isEmpty(userRole)) {
return userRole.map(e => {
return e.roleId;
});
}
return [];
}
/**
*
* @param param
*/
async modifyAfter(param) {
if (param.id) {
this.updatePerms(param.id, param.menuIdList, param.departmentIdList);
}
}
/**
*
* @param roleId
* @param menuIdList
* @param departmentIds
*/
async updatePerms(roleId, menuIdList?, departmentIds = []) {
// 更新菜单权限
await this.baseSysRoleMenuEntity.delete({ roleId });
await Promise.all(
menuIdList.map(async e => {
return await this.baseSysRoleMenuEntity.save({ roleId, menuId: e });
})
);
// 更新部门权限
await this.baseSysRoleDepartmentEntity.delete({ roleId });
await Promise.all(
departmentIds.map(async e => {
return await this.baseSysRoleDepartmentEntity.save({
roleId,
departmentId: e,
});
})
);
// 刷新权限
const userRoles = await this.baseSysUserRoleEntity.findBy({ roleId });
for (const userRole of userRoles) {
await this.baseSysPermsService.refreshPerms(userRole.userId);
}
}
/**
*
* @param id
*/
async info(id) {
const info = await this.baseSysRoleEntity.findOneBy({ id });
if (info) {
const menus = await this.baseSysRoleMenuEntity.findBy(
id !== 1 ? { roleId: id } : {}
);
const menuIdList = menus.map(e => {
return parseInt(e.menuId + '');
});
const departments = await this.baseSysRoleDepartmentEntity.findBy(
id !== 1 ? { roleId: id } : {}
);
const departmentIdList = departments.map(e => {
return parseInt(e.departmentId + '');
});
return {
...info,
menuIdList,
departmentIdList,
};
}
return {};
}
async list() {
return this.baseSysRoleEntity
.createQueryBuilder('a')
.where(
new Brackets(qb => {
qb.where('a.id !=:id', { id: 1 }); // 超级管理员的角色不展示
// 如果不是超管,只能看到自己新建的或者自己有的角色
if (this.ctx.admin.username !== 'admin') {
qb.andWhere('(a.userId=:userId or a.id in (:...roleId))', {
userId: this.ctx.admin.userId,
roleId: this.ctx.admin.roleIds,
});
}
})
)
.getMany();
}
}

View File

@ -1,235 +0,0 @@
import { Inject, InjectClient, Provide } from '@midwayjs/decorator';
import { BaseService, CoolCommException } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Equal, In, Repository } from 'typeorm';
import { BaseSysUserEntity } from '../../entity/sys/user';
import { BaseSysPermsService } from './perms';
import * as _ from 'lodash';
import { BaseSysUserRoleEntity } from '../../entity/sys/user_role';
import * as md5 from 'md5';
import { BaseSysDepartmentEntity } from '../../entity/sys/department';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
/**
*
*/
@Provide()
export class BaseSysUserService extends BaseService {
@InjectEntityModel(BaseSysUserEntity)
baseSysUserEntity: Repository<BaseSysUserEntity>;
@InjectEntityModel(BaseSysUserRoleEntity)
baseSysUserRoleEntity: Repository<BaseSysUserRoleEntity>;
@InjectEntityModel(BaseSysDepartmentEntity)
baseSysDepartmentEntity: Repository<BaseSysDepartmentEntity>;
@InjectClient(CachingFactory, 'default')
midwayCache: MidwayCache;
@Inject()
baseSysPermsService: BaseSysPermsService;
@Inject()
ctx;
/**
*
* @param query
*/
async page(query) {
const { keyWord, status, departmentIds = [] } = query;
const permsDepartmentArr = await this.baseSysPermsService.departmentIds(
this.ctx.admin.userId
); // 部门权限
const sql = `
SELECT
a.id,a.name,a.nickName,a.headImg,a.email,a.remark,a.status,a.createTime,a.updateTime,a.username,a.phone,a.departmentId,
b.name as "departmentName"
FROM
base_sys_user a
LEFT JOIN base_sys_department b on a.departmentId = b.id
WHERE 1 = 1
${this.setSql(
!_.isEmpty(departmentIds),
'and a.departmentId in (?)',
[departmentIds]
)}
${this.setSql(status, 'and a.status = ?', [status])}
${this.setSql(keyWord, 'and (a.name LIKE ? or a.username LIKE ?)', [
`%${keyWord}%`,
`%${keyWord}%`,
])}
${this.setSql(true, 'and a.username != ?', ['admin'])}
${this.setSql(
this.ctx.admin.username !== 'admin',
'and a.departmentId in (?)',
[!_.isEmpty(permsDepartmentArr) ? permsDepartmentArr : [null]]
)} `;
const result = await this.sqlRenderPage(sql, query);
// 匹配角色
if (!_.isEmpty(result.list)) {
const userIds = result.list.map(e => e.id);
const roles = await this.nativeQuery(
'SELECT b.name, a.userId FROM base_sys_user_role a LEFT JOIN base_sys_role b ON a.roleId = b.id WHERE a.userId in (?) ',
[userIds]
);
result.list.forEach(e => {
e['roleName'] = roles
.filter(role => role.userId == e.id)
.map(role => role.name)
.join(',');
});
}
return result;
}
/**
*
* @param departmentId
* @param userIds
*/
async move(departmentId, userIds) {
await this.baseSysUserEntity.update({ id: In(userIds) }, { departmentId });
}
/**
*
*/
async person(userId) {
const info = await this.baseSysUserEntity.findOneBy({
id: Equal(userId),
});
delete info?.password;
return info;
}
/**
*
* @param user
*/
async updateUserRole(user) {
if (_.isEmpty(user.roleIdList)) {
return;
}
if (user.username === 'admin') {
throw new CoolCommException('非法操作~');
}
await this.baseSysUserRoleEntity.delete({ userId: user.id });
if (user.roleIdList) {
for (const roleId of user.roleIdList) {
await this.baseSysUserRoleEntity.save({ userId: user.id, roleId });
}
}
await this.baseSysPermsService.refreshPerms(user.id);
}
/**
*
* @param param
*/
async add(param) {
const exists = await this.baseSysUserEntity.findOneBy({
username: param.username,
});
if (!_.isEmpty(exists)) {
throw new CoolCommException('用户名已经存在~');
}
param.password = md5(param.password);
await this.baseSysUserEntity.save(param);
await this.updateUserRole(param);
return param.id;
}
/**
* ID获得信息
* @param id
*/
public async info(id) {
const info = await this.baseSysUserEntity.findOneBy({ id });
const userRoles = await this.nativeQuery(
'select a.roleId from base_sys_user_role a where a.userId = ?',
[id]
);
const department = await this.baseSysDepartmentEntity.findOneBy({
id: info.departmentId,
});
if (info) {
delete info.password;
if (userRoles) {
info.roleIdList = userRoles.map(e => {
return parseInt(e.roleId);
});
}
}
delete info.password;
if (department) {
info.departmentName = department.name;
}
return info;
}
/**
*
* @param param
*/
public async personUpdate(param) {
param.id = this.ctx.admin.userId;
if (!_.isEmpty(param.password)) {
param.password = md5(param.password);
const oldPassword = md5(param.oldPassword);
const userInfo = await this.baseSysUserEntity.findOneBy({ id: param.id });
if (!userInfo) {
throw new CoolCommException('用户不存在');
}
if (oldPassword !== userInfo.password) {
throw new CoolCommException('原密码错误');
}
param.passwordV = userInfo.passwordV + 1;
await this.midwayCache.set(
`admin:passwordVersion:${param.id}`,
param.passwordV
);
} else {
delete param.password;
}
await this.baseSysUserEntity.save(param);
}
/**
*
* @param param
*/
async update(param) {
if (param.id && param.username === 'admin') {
throw new CoolCommException('非法操作~');
}
if (!_.isEmpty(param.password)) {
param.password = md5(param.password);
const userInfo = await this.baseSysUserEntity.findOneBy({ id: param.id });
if (!userInfo) {
throw new CoolCommException('用户不存在');
}
param.passwordV = userInfo.passwordV + 1;
await this.midwayCache.set(
`admin:passwordVersion:${param.id}`,
param.passwordV
);
} else {
delete param.password;
}
if (param.status === 0) {
await this.forbidden(param.id);
}
await this.baseSysUserEntity.save(param);
await this.updateUserRole(param);
}
/**
*
* @param userId
*/
async forbidden(userId) {
await this.midwayCache.del(`admin:token:${userId}`);
}
}

View File

@ -1,19 +0,0 @@
import { ModuleConfig } from '@cool-midway/core';
/**
*
*/
export default () => {
return {
// 模块名称
name: 'demo模块',
// 模块描述
description: '演示用',
// 中间件,只对本模块有效
middlewares: [],
// 中间件,全局有效
globalMiddlewares: [],
// 模块加载顺序默认为0值越大越优先加载
order: 0,
} as ModuleConfig;
};

View File

@ -1,11 +0,0 @@
import { CoolController, BaseController } from '@cool-midway/core';
import { DemoGoodsEntity } from '../../entity/goods';
/**
* -
*/
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DemoGoodsEntity,
})
export class AdminDemoGoodsController extends BaseController {}

View File

@ -1,37 +0,0 @@
import { DemoCacheService } from '../../service/cache';
import { Inject, Post, Provide, Get, InjectClient } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
/**
*
*/
@CoolController()
export class OpenDemoCacheController extends BaseController {
@InjectClient(CachingFactory, 'default')
midwayCache: MidwayCache;
@Inject()
demoCacheService: DemoCacheService;
/**
*
* @returns
*/
@Post('/set')
async set() {
await this.midwayCache.set('a', 1);
// 缓存10秒
await this.midwayCache.set('a', 1, 10 * 1000);
return this.ok(await this.midwayCache.get('a'));
}
/**
*
* @returns
*/
@Get('/get')
async get() {
return this.ok(await this.demoCacheService.get());
}
}

View File

@ -1,27 +0,0 @@
import { Inject, Post } from '@midwayjs/decorator';
import {
CoolController,
BaseController,
CoolEventManager,
} from '@cool-midway/core';
/**
*
*/
@CoolController()
export class OpenDemoEventController extends BaseController {
@Inject()
coolEventManager: CoolEventManager;
@Post('/comm', { summary: '普通事件,本进程生效' })
async comm() {
await this.coolEventManager.emit('demo', { a: 2 }, 1);
return this.ok();
}
@Post('/global', { summary: '全局事件,多进程都有效' })
async global() {
await this.coolEventManager.globalEmit('demo', false, { a: 2 }, 1);
return this.ok();
}
}

View File

@ -1,32 +0,0 @@
import { DemoGoodsService } from '../../service/goods';
import { DemoGoodsEntity } from '../../entity/goods';
import { Body, Config, Inject, Post, Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
/**
*
*/
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DemoGoodsEntity,
service: DemoGoodsService,
})
export class OpenDemoGoodsController extends BaseController {
@InjectEntityModel(DemoGoodsEntity)
demoGoodsEntity: Repository<DemoGoodsEntity>;
@Inject()
demoGoodsService: DemoGoodsService;
@Post('/sqlPage', { summary: 'sql分页查询' })
async sqlPage(@Body() query) {
return this.ok(await this.demoGoodsService.sqlPage(query));
}
@Post('/entityPage', { summary: 'entity分页查询' })
async entityPage(@Body() query) {
return this.ok(await this.demoGoodsService.entityPage(query));
}
}

View File

@ -1,29 +0,0 @@
import { CoolController, BaseController } from '@cool-midway/core';
import { PluginService } from '../../../plugin/service/info';
import { Get, Inject } from '@midwayjs/core';
/**
*
*/
@CoolController()
export class OpenDemoPluginController extends BaseController {
@Inject()
pluginService: PluginService;
@Get('/invoke', { summary: '调用插件' })
async invoke() {
// 获取插件实例
const instance: any = await this.pluginService.getInstance('ollama');
// 调用chat
const messages = [
{ role: 'system', content: '你叫小酷,是一个智能助理' },
{ role: 'user', content: '写一个1000字的关于春天的文章' },
];
for (let i = 0; i < 3; i++) {
instance.chat(messages, { stream: true }, res => {
console.log(i, res.content);
});
}
return this.ok();
}
}

View File

@ -1,49 +0,0 @@
import { Get, Inject, Post, Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { DemoCommQueue } from '../../queue/comm';
import { DemoGetterQueue } from '../../queue/getter';
/**
*
*/
@CoolController()
export class OpenDemoQueueController extends BaseController {
// 普通队列
@Inject()
demoCommQueue: DemoCommQueue;
// 主动消费队列
@Inject()
demoGetterQueue: DemoGetterQueue;
/**
*
*/
@Post('/add', { summary: '发送队列数据' })
async queue() {
this.demoCommQueue.add({ a: 2 });
return this.ok();
}
@Post('/addGetter')
async addGetter() {
await this.demoGetterQueue.add({ a: new Date() });
return this.ok();
}
/**
* getter时有效
*/
@Get('/getter')
async getter() {
const job = await this.demoGetterQueue.getters.getJobs(
['wait'],
0,
0,
true
);
// 获得完将数据从队列移除
await job[0]?.remove();
return this.ok(job[0]?.data);
}
}

View File

@ -1,29 +0,0 @@
import { Inject, Provide, Get } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { DemoRpcService } from '../../service/rpc';
/**
* RPC调用
*/
@CoolController()
export class OpenDemoRpcController extends BaseController {
@Inject()
demoRpcService: DemoRpcService;
@Get('/call', { summary: '远程调用' })
async call() {
return this.ok(await this.demoRpcService.call());
}
@Get('/event', { summary: '集群事件' })
async event() {
await this.demoRpcService.event();
return this.ok();
}
@Get('/transaction', { summary: '分布式事务' })
async transaction() {
await this.demoRpcService.transaction({ a: 1 });
return this.ok();
}
}

View File

@ -1,49 +0,0 @@
import { CoolController, BaseController } from '@cool-midway/core';
import { Get, Inject } from '@midwayjs/core';
import { Context } from 'koa';
import { PluginService } from '../../../plugin/service/info';
import { PassThrough } from 'stream';
/**
*
*/
@CoolController()
export class OpenDemoSSEController extends BaseController {
@Inject()
ctx: Context;
@Inject()
pluginService: PluginService;
@Get('/call', { summary: '事件流 服务端主动推送' })
async call() {
// 设置响应头
this.ctx.set('Content-Type', 'text/event-stream');
this.ctx.set('Cache-Control', 'no-cache');
this.ctx.set('Connection', 'keep-alive');
const stream = new PassThrough();
// 发送数据
const send = (data: any) => {
stream.write(`data: ${JSON.stringify(data)}\n\n`);
};
// 获取插件实例
const instance: any = await this.pluginService.getInstance('ollama');
// 调用chat
const messages = [
{ role: 'system', content: '你叫小酷,是个编程助手' },
{ role: 'user', content: '用js写个Hello World' },
];
instance.chat(messages, { stream: true }, res => {
send(res);
if (res.isEnd) {
this.ctx.res.end();
}
});
this.ctx.status = 200;
this.ctx.body = stream;
}
}

View File

@ -1,14 +0,0 @@
import { DemoGoodsEntity } from '../../entity/goods';
import { Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { DemoTransactionService } from '../../service/transaction';
/**
*
*/
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DemoGoodsEntity,
service: DemoTransactionService,
})
export class OpenDemoTransactionController extends BaseController {}

View File

@ -1,32 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity, Index } from 'typeorm';
/**
* -
*/
@Entity('demo_goods')
export class DemoGoodsEntity extends BaseEntity {
@Index()
@Column({ comment: '标题', length: 50 })
title: string;
@Column({
comment: '价格',
type: 'decimal',
precision: 5,
scale: 2,
})
price: number;
@Column({ comment: '描述', nullable: true })
description: string;
@Column({ comment: '主图', nullable: true })
mainImage: string;
@Column({ comment: '示例图', nullable: true, type: 'json' })
exampleImages: string[];
@Column({ comment: '库存', default: 0 })
stock: number;
}

View File

@ -1,27 +0,0 @@
import { CoolEvent, Event } from '@cool-midway/core';
import { EVENT_PLUGIN_READY } from '../../plugin/service/center';
/**
*
*/
@CoolEvent()
export class DemoCommEvent {
/**
*
* @param msg
* @param a
*/
@Event('demo')
async demo(msg, a) {
console.log(`comm当前进程的ID是: ${process.pid}`);
console.log('comm收到消息', msg, a);
}
/**
*
*/
@Event(EVENT_PLUGIN_READY)
async pluginReady() {
// TODO 插件已就绪
}
}

View File

@ -1,20 +0,0 @@
import { BaseCoolQueue, CoolQueue } from '@cool-midway/task';
import { IMidwayApplication } from '@midwayjs/core';
import { App } from '@midwayjs/decorator';
/**
*
*/
@CoolQueue()
export class DemoCommQueue extends BaseCoolQueue {
@App()
app: IMidwayApplication;
async data(job: any, done: any): Promise<void> {
// 这边可以执行定时任务具体的业务或队列的业务
console.log('数据', job.data);
// 抛出错误 可以让队列重试默认重试5次
//throw new Error('错误');
done();
}
}

View File

@ -1,7 +0,0 @@
import { BaseCoolQueue, CoolQueue } from '@cool-midway/task';
/**
*
*/
@CoolQueue({ type: 'getter' })
export class DemoGetterQueue extends BaseCoolQueue {}

View File

@ -1,20 +0,0 @@
import { BaseCoolQueue, CoolQueue } from '@cool-midway/task';
import { IMidwayApplication } from '@midwayjs/core';
import { App } from '@midwayjs/decorator';
/**
* cluster
*/
@CoolQueue({ type: 'single' })
export class DemoSingleQueue extends BaseCoolQueue {
@App()
app: IMidwayApplication;
async data(job: any, done: any): Promise<void> {
// 这边可以执行定时任务具体的业务或队列的业务
console.log('数据', job.data);
// 抛出错误 可以让队列重试默认重试5次
//throw new Error('错误');
done();
}
}

View File

@ -1,18 +0,0 @@
import { Provide } from '@midwayjs/decorator';
import { CoolCache } from '@cool-midway/core';
/**
*
*/
@Provide()
export class DemoCacheService {
// 数据缓存5秒
@CoolCache(5)
async get() {
console.log('执行方法');
return {
a: 1,
b: 2,
};
}
}

View File

@ -1,40 +0,0 @@
import { DemoGoodsEntity } from './../entity/goods';
import { Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
/**
*
*/
@Provide()
export class DemoGoodsService extends BaseService {
@InjectEntityModel(DemoGoodsEntity)
demoGoodsEntity: Repository<DemoGoodsEntity>;
/**
* sql分页
*/
async sqlPage(query) {
await this.demoGoodsEntity.save({
id: 1,
title: '标题',
price: 99.0,
description: '商品描述',
mainImage: 'https://cool-js.com/logo.png',
});
return this.sqlRenderPage(
'select * from demo_goods ORDER BY id ASC',
query,
false
);
}
/**
* entity分页
*/
async entityPage(query) {
const find = this.demoGoodsEntity.createQueryBuilder();
return this.entityRenderPage(find, query);
}
}

View File

@ -1,70 +0,0 @@
import { App, Provide } from '@midwayjs/decorator';
import { DemoGoodsEntity } from '../entity/goods';
import { IMidwayApplication, Inject } from '@midwayjs/core';
import {
BaseRpcService,
CoolRpc,
CoolRpcService,
CoolRpcTransaction,
} from '@cool-midway/rpc';
import { QueryRunner } from 'typeorm';
@Provide()
@CoolRpcService({
entity: DemoGoodsEntity,
method: ['info', 'add', 'page'],
})
export class DemoRpcService extends BaseRpcService {
@App()
app: IMidwayApplication;
@Inject()
rpc: CoolRpc;
/**
*
* @returns
*/
async call() {
return await this.rpc.call('goods', 'demoGoodsService', 'test', {
a: 1,
});
}
/**
*
*/
async event() {
this.rpc.event('test', { a: 1 });
}
async info(params) {
return params;
}
async getUser() {
return {
uid: '123',
username: 'mockedName',
phone: '12345678901',
email: 'xxx.xxx@xxx.com',
};
}
@CoolRpcTransaction()
async transaction(params, rpcTransactionId?, queryRunner?: QueryRunner) {
console.log('获得的参数', params);
const data = {
title: '商品标题',
pic: 'https://xxx',
price: 99.0,
type: 1,
};
await queryRunner.manager.save(DemoGoodsEntity, data);
// 将事务id传给调用的远程服务方法
await this.rpc.call('goods', 'demoGoodsService', 'transaction', {
rpcTransactionId,
...params,
});
}
}

View File

@ -1,23 +0,0 @@
import { DemoGoodsEntity } from './../entity/goods';
import { Provide } from '@midwayjs/decorator';
import { BaseService, CoolTransaction } from '@cool-midway/core';
import { QueryRunner } from 'typeorm';
/**
*
*/
@Provide()
export class DemoTransactionService extends BaseService {
/**
*
*/
@CoolTransaction({
connectionName: 'default',
})
async add(param, queryRunner?: QueryRunner) {
await queryRunner.manager.insert<DemoGoodsEntity>(DemoGoodsEntity, param);
return {
id: param.id,
};
}
}

View File

@ -1,19 +0,0 @@
import { ModuleConfig } from '@cool-midway/core';
/**
*
*/
export default () => {
return {
// 模块名称
name: '字典管理',
// 模块描述
description: '数据字典等',
// 中间件,只对本模块有效
middlewares: [],
// 中间件,全局有效
globalMiddlewares: [],
// 模块加载顺序默认为0值越大越优先加载
order: 0,
} as ModuleConfig;
};

View File

@ -1,30 +0,0 @@
import { DictInfoEntity } from './../../entity/info';
import { Body, Inject, Post, Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { DictInfoService } from '../../service/info';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DictInfoEntity,
service: DictInfoService,
listQueryOp: {
fieldEq: ['typeId'],
keyWordLikeFields: ['name'],
addOrderBy: {
createTime: 'ASC',
},
},
})
export class AdminDictInfoController extends BaseController {
@Inject()
dictInfoService: DictInfoService;
@Post('/data', { summary: '获得字典数据' })
async data(@Body('types') types: string[] = []) {
return this.ok(await this.dictInfoService.data(types));
}
}

View File

@ -1,18 +0,0 @@
import { DictTypeEntity } from './../../entity/type';
import { Provide } from '@midwayjs/decorator';
import { CoolController, BaseController } from '@cool-midway/core';
import { DictTypeService } from '../../service/type';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DictTypeEntity,
service: DictTypeService,
listQueryOp: {
keyWordLikeFields: ['name'],
},
})
export class AdminDictTypeController extends BaseController {}

View File

@ -1,26 +0,0 @@
import { Body, Inject, Post, Provide } from '@midwayjs/decorator';
import {
CoolController,
BaseController,
CoolUrlTag,
TagTypes,
CoolTag,
} from '@cool-midway/core';
import { DictInfoService } from '../../service/info';
/**
*
*/
@Provide()
@CoolController()
@CoolUrlTag()
export class AppDictInfoController extends BaseController {
@Inject()
dictInfoService: DictInfoService;
@CoolTag(TagTypes.IGNORE_TOKEN)
@Post('/data', { summary: '获得字典数据' })
async data(@Body('types') types: string[] = []) {
return this.ok(await this.dictInfoService.data(types));
}
}

View File

@ -1,88 +0,0 @@
{
"dict_info": [
{
"id": 21,
"typeId": 19,
"name": "COOL",
"orderNum": 1,
"remark": null,
"parentId": null,
"value": "cool"
},
{
"id": 22,
"typeId": 19,
"name": "闪酷",
"orderNum": 2,
"remark": null,
"parentId": null,
"value": "https://show.cool-admin.com/api/public/uploads/20230308/c731b0cba84046268b10edbbcf36f948_315c243a448e1369fa145c5ea3f020da.gif"
},
{
"id": 23,
"typeId": 20,
"name": "法师",
"orderNum": 1,
"remark": null,
"parentId": null,
"value": "4"
},
{
"id": 24,
"typeId": 20,
"name": "战士",
"orderNum": 2,
"remark": null,
"parentId": null,
"value": "3"
},
{
"id": 25,
"typeId": 20,
"name": "坦克",
"orderNum": 3,
"remark": null,
"parentId": null,
"value": "2"
},
{
"id": 26,
"typeId": 20,
"name": "刺客",
"orderNum": 4,
"remark": null,
"parentId": null,
"value": "1"
},
{
"id": 27,
"typeId": 20,
"name": "射手",
"orderNum": 5,
"remark": null,
"parentId": null,
"value": "0"
},
{
"id": 30,
"typeId": 20,
"name": "幻影刺客",
"orderNum": 1,
"remark": null,
"parentId": 26,
"value": "5"
}
],
"dict_type": [
{
"id": 19,
"name": "品牌",
"key": "brand"
},
{
"id": 20,
"name": "职业",
"key": "occupation"
}
]
}

View File

@ -1,26 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('dict_info')
export class DictInfoEntity extends BaseEntity {
@Column({ comment: '类型ID' })
typeId: number;
@Column({ comment: '名称' })
name: string;
@Column({ comment: '值', nullable: true })
value: string;
@Column({ comment: '排序', default: 0 })
orderNum: number;
@Column({ comment: '备注', nullable: true })
remark: string;
@Column({ comment: '父ID', default: null })
parentId: number;
}

View File

@ -1,14 +0,0 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('dict_type')
export class DictTypeEntity extends BaseEntity {
@Column({ comment: '名称' })
name: string;
@Column({ comment: '标识' })
key: string;
}

View File

@ -1,137 +0,0 @@
import { DictTypeEntity } from './../entity/type';
import { DictInfoEntity } from './../entity/info';
import { Config, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository, In } from 'typeorm';
import * as _ from 'lodash';
/**
*
*/
@Provide()
export class DictInfoService extends BaseService {
@InjectEntityModel(DictInfoEntity)
dictInfoEntity: Repository<DictInfoEntity>;
@InjectEntityModel(DictTypeEntity)
dictTypeEntity: Repository<DictTypeEntity>;
@Config('typeorm.dataSource.default.type')
ormType: string;
/**
*
* @param types
*/
async data(types: string[]) {
const result = {};
let typeData = await this.dictTypeEntity.find();
if (!_.isEmpty(types)) {
typeData = await this.dictTypeEntity.findBy({ key: In(types) });
}
if (_.isEmpty(typeData)) {
return {};
}
const data = await this.dictInfoEntity
.createQueryBuilder('a')
.select([
'a.id',
'a.name',
'a.typeId',
'a.parentId',
'a.orderNum',
'a.value',
])
.where('a.typeId in(:...typeIds)', {
typeIds: typeData.map(e => {
return e.id;
}),
})
.orderBy('a.orderNum', 'ASC')
.addOrderBy('a.createTime', 'ASC')
.getMany();
for (const item of typeData) {
result[item.key] = _.filter(data, { typeId: item.id }).map(e => {
const value = e.value ? Number(e.value) : e.value;
return {
...e,
value: isNaN(value) ? e.value : value,
};
});
}
return result;
}
/**
*
* @param value
* @param key
* @returns
*/
async getValues(value: string | string[], key: string) {
// 获取字典类型
const type = await this.dictTypeEntity.findOneBy({ key });
if (!type) {
return null; // 或者适当的错误处理
}
// 根据typeId获取所有相关的字典信息
const dictValues = await this.dictInfoEntity.find({
where: { typeId: type.id },
});
// 如果value是字符串直接查找
if (typeof value === 'string') {
return this.findValueInDictValues(value, dictValues);
}
// 如果value是数组遍历数组对每个元素进行查找
return value.map(val => this.findValueInDictValues(val, dictValues));
}
/**
*
* @param value
* @param dictValues
* @returns
*/
findValueInDictValues(value: string, dictValues: any[]) {
let result = dictValues.find(dictValue => dictValue.value === value);
if (!result) {
result = dictValues.find(dictValue => dictValue.id === parseInt(value));
}
return result ? result.name : null; // 或者适当的错误处理
}
/**
*
* @param data
* @param type
*/
async modifyAfter(data: any, type: 'delete' | 'update' | 'add') {
if (type === 'delete') {
for (const id of data) {
await this.delChildDict(id);
}
}
}
/**
*
* @param id
*/
private async delChildDict(id) {
const delDict = await this.dictInfoEntity.findBy({ parentId: id });
if (_.isEmpty(delDict)) {
return;
}
const delDictIds = delDict.map(e => {
return e.id;
});
await this.dictInfoEntity.delete(delDictIds);
for (const dictId of delDictIds) {
await this.delChildDict(dictId);
}
}
}

View File

@ -1,25 +0,0 @@
import { DictInfoEntity } from './../entity/info';
import { Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository, In } from 'typeorm';
/**
*
*/
@Provide()
export class DictTypeService extends BaseService {
@InjectEntityModel(DictInfoEntity)
dictInfoEntity: Repository<DictInfoEntity>;
/**
*
* @param ids
*/
async delete(ids) {
super.delete(ids);
await this.dictInfoEntity.delete({
typeId: In(ids),
});
}
}

Some files were not shown because too many files have changed in this diff Show More