| | |
| | | "description": "一个很酷的Ai快速开发框架", |
| | | "private": true, |
| | | "dependencies": { |
| | | "@cool-midway/core": "^8.0.7", |
| | | "@cool-midway/rpc": "^8.0.1", |
| | | "@cool-midway/task": "^8.0.2", |
| | | "@midwayjs/bootstrap": "^3.20.3", |
| | | "@midwayjs/cache-manager": "^3.20.3", |
| | | "@midwayjs/core": "^3.20.3", |
| | | "@midwayjs/cron": "^3.20.3", |
| | | "@midwayjs/cross-domain": "^3.20.3", |
| | | "@midwayjs/info": "^3.20.3", |
| | | "@midwayjs/koa": "^3.20.3", |
| | | "@cool-midway/core": "8.0.1", |
| | | "@cool-midway/rpc": "8.0.1", |
| | | "@cool-midway/task": "8.0.1", |
| | | "@midwayjs/bootstrap": "^3.20.0", |
| | | "@midwayjs/cache-manager": "^3.20.0", |
| | | "@midwayjs/core": "^3.20.0", |
| | | "@midwayjs/cron": "^3.20.0", |
| | | "@midwayjs/cross-domain": "^3.20.0", |
| | | "@midwayjs/info": "^3.20.0", |
| | | "@midwayjs/koa": "^3.20.0", |
| | | "@midwayjs/logger": "^3.4.2", |
| | | "@midwayjs/static-file": "^3.20.3", |
| | | "@midwayjs/typeorm": "^3.20.3", |
| | | "@midwayjs/upload": "^3.20.3", |
| | | "@midwayjs/validate": "^3.20.3", |
| | | "@midwayjs/static-file": "^3.20.0", |
| | | "@midwayjs/typeorm": "^3.20.0", |
| | | "@midwayjs/upload": "^3.20.0", |
| | | "@midwayjs/validate": "^3.20.0", |
| | | "@midwayjs/socketio": "^3.15.2", |
| | | "@socket.io/redis-adapter": "^8.3.0", |
| | | "adm-zip": "^0.5.16", |
| | | "axios": "^1.8.4", |
| | | "cron": "^4.1.3", |
| | | "download": "^8.0.0", |
| | | "axios": "^1.7.9", |
| | | "bignumber.js": "^9.1.2", |
| | | "cache-manager-ioredis-yet": "^2.0.2", |
| | | "ioredis": "^5.3.2", |
| | | "cron": "^3.5.0", |
| | | "jsonwebtoken": "^9.0.2", |
| | | "lodash": "^4.17.21", |
| | | "md5": "^2.3.0", |
| | | "moment": "^2.30.1", |
| | | "mysql2": "^3.14.0", |
| | | "mysql2": "^3.12.0", |
| | | "semver": "^7.6.0", |
| | | "svg-captcha": "^1.4.0", |
| | | "tslib": "^2.8.1", |
| | | "typeorm": "npm:@cool-midway/typeorm@0.3.20", |
| | | "uuid": "^11.1.0", |
| | | "ws": "^8.18.1" |
| | | "uuid": "^11.0.5", |
| | | "ws": "^8.18.0" |
| | | }, |
| | | "devDependencies": { |
| | | "@midwayjs/bundle-helper": "^1.3.0", |
| | | "@midwayjs/mock": "^3.20.3", |
| | | "@midwayjs/mock": "^3.20.0", |
| | | "@types/jest": "^29.5.14", |
| | | "@types/node": "22", |
| | | "@yao-pkg/pkg": "^6.3.2", |
| | | "@yao-pkg/pkg": "^6.2.0", |
| | | "cross-env": "^7.0.3", |
| | | "jest": "^29.7.0", |
| | | "mwts": "^1.3.0", |
| | | "mwtsc": "^1.15.1", |
| | | "rimraf": "^6.0.1", |
| | | "ts-jest": "^29.3.0", |
| | | "typescript": "~5.8.2" |
| | | "ts-jest": "^29.2.5", |
| | | "typescript": "~5.7.3" |
| | | }, |
| | | "engines": { |
| | | "node": ">=18.0.0" |
| | |
| | | "url": "https://cool-js.com" |
| | | }, |
| | | "author": "COOL", |
| | | "license": "MIT", |
| | | "packageManager": "pnpm@11.12.0+sha512.820a6fbd0d9f04c226638002aead1e45340a9139dd5dc077c1d83ef44aa2481c8eb6637b4c9aa696a3c7e35ba818e49cf27213e5f2b91138d09b7a3e26e898ba" |
| | | "license": "MIT" |
| | | } |
| | |
| | | // 如果 pattern 和 url 的部分数量一致,则匹配成功 |
| | | return patternParts.length === urlParts.length; |
| | | } |
| | | |
| | | /** |
| | | * 从文本中提取 JSON 字符串并转换为对象 |
| | | * @param {string} text - 可能包含 JSON 的文本 |
| | | * @returns {Object|Array|null} - 解析出的 JSON 对象,如果没有找到有效 JSON 则返回 null |
| | | */ |
| | | extractJSONFromText(text) { |
| | | if (!text || typeof text !== 'string') { |
| | | return null; |
| | | } |
| | | |
| | | try { |
| | | // 尝试直接解析整个文本 |
| | | return JSON.parse(text); |
| | | } catch (e) { |
| | | // 整个文本不是有效的 JSON,尝试提取 JSON 部分 |
| | | } |
| | | |
| | | // 查找可能的 JSON 开始位置({ 或 [) |
| | | const possibleStarts = []; |
| | | for (let i = 0; i < text.length; i++) { |
| | | if (text[i] === '{' || text[i] === '[') { |
| | | possibleStarts.push(i); |
| | | } |
| | | } |
| | | |
| | | // 从每个可能的起始位置尝试提取 JSON |
| | | for (const startIndex of possibleStarts) { |
| | | let openBraces = 0; |
| | | let openBrackets = 0; |
| | | let inString = false; |
| | | let escapeNext = false; |
| | | |
| | | for (let i = startIndex; i < text.length; i++) { |
| | | const char = text[i]; |
| | | |
| | | // 处理转义字符 |
| | | if (inString && !escapeNext && char === '\\') { |
| | | escapeNext = true; |
| | | continue; |
| | | } |
| | | |
| | | // 处理字符串边界 |
| | | if (!escapeNext && char === '"') { |
| | | inString = !inString; |
| | | } |
| | | |
| | | if (!inString) { |
| | | // 只在不在字符串内部时才计算括号 |
| | | if (char === '{') openBraces++; |
| | | else if (char === '}') openBraces--; |
| | | else if (char === '[') openBrackets++; |
| | | else if (char === ']') openBrackets--; |
| | | } |
| | | |
| | | escapeNext = false; |
| | | |
| | | // 检查是否找到了完整的 JSON 结构 |
| | | if ( |
| | | (openBraces === 0 && text[startIndex] === '{' && char === '}') || |
| | | (openBrackets === 0 && text[startIndex] === '[' && char === ']') |
| | | ) { |
| | | const jsonStr = text.substring(startIndex, i + 1); |
| | | try { |
| | | const result = JSON.parse(jsonStr); |
| | | return result; |
| | | } catch (e) { |
| | | // 这个候选 JSON 无效,继续尝试下一个 |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return null; // 没有找到有效的 JSON |
| | | } |
| | | } |
| | |
| | | import { CoolConfig } from '@cool-midway/core'; |
| | | import { CoolCacheStore, CoolConfig } from '@cool-midway/core'; |
| | | import { MidwayConfig } from '@midwayjs/core'; |
| | | import { CoolCacheStore } from '@cool-midway/core'; |
| | | import * as path from 'path'; |
| | | import { pCachePath, pUploadPath } from '../comm/path'; |
| | | import { availablePort } from '../comm/port'; |
| | | |
| | | // redis缓存 |
| | | // import { redisStore } from 'cache-manager-ioredis-yet'; |
| | | |
| | | export default { |
| | | keys: 'f65bcafd-b72c-4f01-a451-333155097148', |
| | | // 确保每个项目唯一,项目首次启动会自动生成 |
| | | keys: '44ceca50-584c-44c4-8346-4b44471ea41f', |
| | | koa: { |
| | | port: availablePort(8001), |
| | | rawBody: true, |
| | | }, |
| | | // 开启异步上下文管理 |
| | | asyncContextManager: { |
| | | enable: true, |
| | | }, |
| | | // 静态文件配置 |
| | | staticFile: { |
| | | buffer: true, |
| | | dirs: { |
| | |
| | | }, |
| | | }, |
| | | }, |
| | | // 文件上传 |
| | | upload: { |
| | | fileSize: '200mb', |
| | | whitelist: null, |
| | | ignore: ['/open/push/yingshiyun', "/open/push/cameraEvent"], |
| | | }, |
| | | // 缓存 可切换成其他缓存如:redis http://www.midwayjs.org/docs/extensions/caching |
| | | cacheManager: { |
| | | clients: { |
| | | default: { |
| | |
| | | }, |
| | | }, |
| | | }, |
| | | // cacheManager: { |
| | | // clients: { |
| | | // default: { |
| | | // store: redisStore, |
| | | // options: { |
| | | // port: 6379, |
| | | // host: '127.0.0.1', |
| | | // password: '', |
| | | // ttl: 0, |
| | | // db: 0, |
| | | // }, |
| | | // }, |
| | | // }, |
| | | // }, |
| | | cool: { |
| | | redis: { |
| | | host: '127.0.0.1', |
| | | password: '', |
| | | port: 6379, |
| | | db: 10, |
| | | }, |
| | | // 已经插件化,本地文件上传查看 plugin/config.ts,其他云存储查看对应插件的使用 |
| | | file: {}, |
| | | // 是否开启多租户 |
| | | tenant: { |
| | | enable: true, |
| | | // 是否开启多租户 |
| | | enable: false, |
| | | // 需要过滤多租户的url, 支持通配符, 如/admin/**/* 表示admin模块下的所有接口都进行多租户过滤 |
| | | urls: [], |
| | | }, |
| | | // 国际化配置 |
| | | i18n: { |
| | | enable: true, |
| | | // 是否开启 |
| | | enable: false, |
| | | // 语言 |
| | | languages: ['zh-cn', 'zh-tw', 'en'], |
| | | }, |
| | | // crud配置 |
| | | crud: { |
| | | // 插入模式,save不会校验字段(允许传入不存在的字段),insert会校验字段 |
| | | upsert: 'save', |
| | | // 软删除 |
| | | softDelete: true, |
| | | }, |
| | | }, |
| | | } as CoolConfig, |
| | | } as MidwayConfig; |
| | | } as MidwayConfig; |
| | |
| | | port: 8806, |
| | | username: 'womp', |
| | | password: 'Sening@2023', |
| | | database: 'womp', |
| | | database: 'womp-shop', |
| | | // 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失 |
| | | synchronize: true, |
| | | // 打印日志 |
| | |
| | | import { MidwayConfig } from '@midwayjs/core'; |
| | | import { entities } from '../entities'; |
| | | import { TenantSubscriber } from '../modules/base/db/tenant'; |
| | | import { createAdapter } from '@socket.io/redis-adapter'; |
| | | import Redis from 'ioredis'; |
| | | |
| | | const redis = { |
| | | host: '127.0.0.1', |
| | | port: 6379, |
| | | password: '', |
| | | db: 0, |
| | | }; |
| | | const pubClient = new Redis(redis); |
| | | const subClient = pubClient.duplicate(); |
| | | |
| | | /** |
| | | * 本地开发 npm run prod 读取的配置文件 |
| | | */ |
| | | export default { |
| | | socketIO: { |
| | | upgrades: ['websocket'], // 可升级的协议 |
| | | adapter: createAdapter(pubClient, subClient), |
| | | }, |
| | | typeorm: { |
| | | dataSource: { |
| | | default: { |
| | |
| | | MidwayWebRouterService, |
| | | } from '@midwayjs/core'; |
| | | import * as koa from '@midwayjs/koa'; |
| | | import * as crossDomain from '@midwayjs/cross-domain'; |
| | | // import * as crossDomain from '@midwayjs/cross-domain'; |
| | | import * as validate from '@midwayjs/validate'; |
| | | import * as info from '@midwayjs/info'; |
| | | import * as staticFile from '@midwayjs/static-file'; |
| | |
| | | import * as ProdConfig from './config/config.prod'; |
| | | import * as cool from '@cool-midway/core'; |
| | | import * as upload from '@midwayjs/upload'; |
| | | //import * as task from '@cool-midway/task'; |
| | | //import * as rpc from '@cool-midway/rpc'; |
| | | import * as socketio from '@midwayjs/socketio'; |
| | | import * as task from '@cool-midway/task'; |
| | | // import * as rpc from '@cool-midway/rpc'; |
| | | |
| | | @Configuration({ |
| | | imports: [ |
| | | // https://koajs.com/ |
| | | koa, |
| | | // 是否开启跨域(注:顺序不能乱放!!!) http://www.midwayjs.org/docs/extensions/cross_domain |
| | | crossDomain, |
| | | // crossDomain, |
| | | // 静态文件托管 https://midwayjs.org/docs/extensions/static_file |
| | | staticFile, |
| | | // orm https://midwayjs.org/docs/extensions/orm |
| | |
| | | // cool-admin 官方组件 https://cool-js.com |
| | | cool, |
| | | // rpc 微服务 远程调用 |
| | | //rpc, |
| | | // rpc, |
| | | // 任务与队列 |
| | | //task, |
| | | task, |
| | | // 即时通讯 |
| | | socketio, |
| | | { |
| | | component: info, |
| | | enabledEnvironment: ['local', 'prod'], |
| New file |
| | |
| | | import { ModuleConfig } from '@cool-midway/core'; |
| | | |
| | | /** |
| | | * 模块配置 |
| | | */ |
| | | export default () => { |
| | | return { |
| | | // 模块名称 |
| | | name: '应用管理', |
| | | // 模块描述 |
| | | description: '投诉举报、意见反馈、更新升级、套餐设置等', |
| | | // 中间件,只对本模块有效 |
| | | middlewares: [], |
| | | // 中间件,全局有效 |
| | | globalMiddlewares: [], |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 0, |
| | | } as ModuleConfig; |
| | | }; |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { AppComplainEntity } from '../../entity/complain'; |
| | | import { BaseSysUserEntity } from '../../../base/entity/sys/user'; |
| | | import { AppComplainService } from '../../service/complain'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | |
| | | /** |
| | | * 意见反馈 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: AppComplainEntity, |
| | | service: AppComplainService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.contact', 'b.nickName', 'c.name'], |
| | | select: ['a.*', 'b.nickName', 'b.avatarUrl', 'c.name as handlerName'], |
| | | fieldEq: ['a.status', 'a.type'], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id', |
| | | }, |
| | | { |
| | | entity: BaseSysUserEntity, |
| | | alias: 'c', |
| | | condition: 'a.handlerId = c.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AdminAppComplainController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { AppFeedbackEntity } from '../../entity/feedback'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | import { BaseSysUserEntity } from '../../../base/entity/sys/user'; |
| | | import { AppFeedbackService } from '../../service/feedback'; |
| | | |
| | | /** |
| | | * 意见反馈 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: AppFeedbackEntity, |
| | | service: AppFeedbackService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.contact', 'b.nickName', 'c.name'], |
| | | select: ['a.*', 'b.nickName', 'b.avatarUrl', 'c.name as handlerName'], |
| | | fieldEq: ['a.status'], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id', |
| | | }, |
| | | { |
| | | entity: BaseSysUserEntity, |
| | | alias: 'c', |
| | | condition: 'a.handlerId = c.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AdminAppFeedbackController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { AppGoodsEntity } from '../../entity/goods'; |
| | | |
| | | /** |
| | | * 套餐 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: AppGoodsEntity, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.title'], |
| | | fieldEq: ['a.status', 'a.type'], |
| | | }, |
| | | }) |
| | | export class AdminAppGoodsController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { AppVersionEntity } from '../../entity/version'; |
| | | import { AppVersionService } from '../../service/version'; |
| | | |
| | | /** |
| | | * 版本 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: AppVersionEntity, |
| | | service: AppVersionService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.name', 'a.version'], |
| | | fieldEq: ['a.status', 'a.type'], |
| | | }, |
| | | }) |
| | | export class AdminAppVersionController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { Body, Inject, Post } from '@midwayjs/core'; |
| | | import { AppComplainService } from '../../service/complain'; |
| | | import { AppComplainEntity } from '../../entity/complain'; |
| | | |
| | | /** |
| | | * 意见反馈 |
| | | */ |
| | | @CoolController({ |
| | | api: ['page', 'info'], |
| | | entity: AppComplainEntity, |
| | | insertParam: ctx => { |
| | | return { |
| | | userId: ctx.user.id, |
| | | }; |
| | | }, |
| | | pageQueryOp: { |
| | | fieldEq: ['a.type'], |
| | | where: ctx => { |
| | | const userId = ctx.user.id; |
| | | return [['a.userId = :userId', { userId }]]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppAppComplainController extends BaseController { |
| | | @Inject() |
| | | appComplainService: AppComplainService; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Post('/submit', { summary: '提交投诉举报' }) |
| | | async submit(@Body() info) { |
| | | info.userId = this.ctx.user.id; |
| | | await this.appComplainService.submit(info); |
| | | return this.ok(); |
| | | } |
| | | } |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { Body, Inject, Post } from '@midwayjs/core'; |
| | | import { AppFeedbackService } from '../../service/feedback'; |
| | | import { AppFeedbackEntity } from '../../entity/feedback'; |
| | | |
| | | /** |
| | | * 意见反馈 |
| | | */ |
| | | @CoolController({ |
| | | api: ['page', 'info'], |
| | | entity: AppFeedbackEntity, |
| | | insertParam: ctx => { |
| | | return { |
| | | userId: ctx.user.id, |
| | | }; |
| | | }, |
| | | pageQueryOp: { |
| | | fieldEq: ['a.type'], |
| | | where: ctx => { |
| | | const userId = ctx.user.id; |
| | | return [['a.userId = :userId', { userId }]]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppAppFeedbackController extends BaseController { |
| | | @Inject() |
| | | appFeedbackService: AppFeedbackService; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Post('/submit', { summary: '提交意见反馈' }) |
| | | async submit(@Body() info) { |
| | | info.userId = this.ctx.user.id; |
| | | await this.appFeedbackService.submit(info); |
| | | return this.ok(); |
| | | } |
| | | } |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { AppGoodsEntity } from '../../entity/goods'; |
| | | |
| | | /** |
| | | * 套餐 |
| | | */ |
| | | @CoolController({ |
| | | api: ['list'], |
| | | entity: AppGoodsEntity, |
| | | listQueryOp: { |
| | | addOrderBy: { |
| | | sort: 'ASC', |
| | | }, |
| | | where: () => { |
| | | return [['a.status = :status', { status: 1 }]]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppAppGoodsController extends BaseController {} |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | CoolUrlTag, |
| | | CoolTag, |
| | | TagTypes, |
| | | } from '@cool-midway/core'; |
| | | import { Get, Inject, Query } from '@midwayjs/core'; |
| | | import { AppVersionService } from '../../service/version'; |
| | | import { AppVersionEntity } from '../../entity/version'; |
| | | |
| | | /** |
| | | * 版本 |
| | | */ |
| | | @CoolUrlTag() |
| | | @CoolController({ |
| | | api: [], |
| | | entity: AppVersionEntity, |
| | | }) |
| | | export class AppAppVersionController extends BaseController { |
| | | @Inject() |
| | | appVersionService: AppVersionService; |
| | | |
| | | @CoolTag(TagTypes.IGNORE_TOKEN) |
| | | @Get('/check', { summary: '检查版本' }) |
| | | async check(@Query('version') version: string, @Query('type') type = 0) { |
| | | const result = await this.appVersionService.check(version, type); |
| | | return this.ok(result); |
| | | } |
| | | } |
| New file |
| | |
| | | { |
| | | "dict_type": [ |
| | | { |
| | | "name": "升级类型", |
| | | "key": "upgradeType", |
| | | "@childDatas": { |
| | | "dict_info": [ |
| | | { |
| | | "typeId": "@id", |
| | | "name": "安卓", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null, |
| | | "value": "0" |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "IOS", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null, |
| | | "value": "1" |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | { |
| | | "name": "投诉类型", |
| | | "key": "complainType", |
| | | "@childDatas": { |
| | | "dict_info": [ |
| | | { |
| | | "typeId": "@id", |
| | | "name": "崩溃与错误", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null, |
| | | "value": "0" |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "支付问题", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null, |
| | | "value": "1" |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "体验不佳", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null, |
| | | "value": "2" |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "功能缺失", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null, |
| | | "value": "3" |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "其他", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null, |
| | | "value": "4" |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | { |
| | | "name": "反馈类型", |
| | | "key": "feedbackType", |
| | | "@childDatas": { |
| | | "dict_info": [ |
| | | { |
| | | "typeId": "@id", |
| | | "name": "崩溃与错误", |
| | | "value": "0", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "支付问题", |
| | | "value": "1", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "体验不佳", |
| | | "value": "2", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "功能缺失", |
| | | "value": "3", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null |
| | | }, |
| | | { |
| | | "typeId": "@id", |
| | | "name": "其他", |
| | | "value": "-1", |
| | | "orderNum": 1, |
| | | "remark": null, |
| | | "parentId": null |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | ] |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | |
| | | /** |
| | | * 举报投诉 |
| | | */ |
| | | @Entity('app_complain') |
| | | export class AppComplainEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Column({ comment: '类型' }) |
| | | type: number; |
| | | |
| | | @Column({ comment: '联系方式' }) |
| | | contact: string; |
| | | |
| | | @Column({ comment: '内容' }) |
| | | content: string; |
| | | |
| | | @Column({ comment: '图片', type: 'json', nullable: true }) |
| | | images: string[]; |
| | | |
| | | @Column({ comment: '状态 0-未处理 1-已处理', default: 0 }) |
| | | status: number; |
| | | |
| | | @Column({ comment: '处理人ID', nullable: true }) |
| | | handlerId: number; |
| | | |
| | | @Column({ comment: '备注', type: 'text', nullable: true }) |
| | | remark: string; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 意见反馈 |
| | | */ |
| | | @Entity('app_feedback') |
| | | export class AppFeedbackEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Column({ comment: '联系方式' }) |
| | | contact: string; |
| | | |
| | | @Column({ comment: '类型' }) |
| | | type: number; |
| | | |
| | | @Column({ comment: '内容' }) |
| | | content: string; |
| | | |
| | | @Column({ comment: '图片', type: 'json', nullable: true }) |
| | | images: string[]; |
| | | |
| | | @Column({ comment: '状态 0-未处理 1-已处理', default: 0 }) |
| | | status: number; |
| | | |
| | | @Column({ comment: '处理人ID', nullable: true }) |
| | | handlerId: number; |
| | | |
| | | @Column({ comment: '备注', type: 'text', nullable: true }) |
| | | remark: string; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity } from 'typeorm'; |
| | | |
| | | /** |
| | | * 套餐 |
| | | */ |
| | | @Entity('app_goods') |
| | | export class AppGoodsEntity extends BaseEntity { |
| | | @Column({ comment: '标题' }) |
| | | title: string; |
| | | |
| | | @Column({ |
| | | comment: '价格', |
| | | type: 'decimal', |
| | | precision: 12, |
| | | scale: 2, |
| | | }) |
| | | price: number; |
| | | |
| | | @Column({ |
| | | comment: '原价', |
| | | type: 'decimal', |
| | | precision: 12, |
| | | scale: 2, |
| | | }) |
| | | originalPrice: number; |
| | | |
| | | @Column({ comment: '描述', type: 'text', nullable: true }) |
| | | description: string; |
| | | |
| | | @Column({ comment: '状态 0-禁用 1-启用', default: 1 }) |
| | | status: number; |
| | | |
| | | @Column({ comment: '排序', default: 0 }) |
| | | sort: number; |
| | | |
| | | @Column({ comment: '类型 0-天 1-月 2-年 3-永久', default: 0 }) |
| | | type: number; |
| | | |
| | | @Column({ comment: '时长', default: 1 }) |
| | | duration: number; |
| | | |
| | | @Column({ comment: '标签', nullable: true }) |
| | | tag: string; |
| | | |
| | | @Column({ comment: '标签颜色', default: '#26A7FD' }) |
| | | tagColor: string; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity } from 'typeorm'; |
| | | |
| | | /** |
| | | * 应用版本 |
| | | */ |
| | | @Entity('app_version') |
| | | export class AppVersionEntity extends BaseEntity { |
| | | @Column({ comment: '名称' }) |
| | | name: string; |
| | | |
| | | @Column({ comment: '版本号' }) |
| | | version: string; |
| | | |
| | | @Column({ comment: '类型', default: 0 }) |
| | | type: number; |
| | | |
| | | @Column({ comment: '下载地址' }) |
| | | url: string; |
| | | |
| | | @Column({ comment: '强制更新 0-否 1-是', default: 0 }) |
| | | forceUpdate: number; |
| | | |
| | | @Column({ comment: '状态 0-禁用 1-启用', default: 1 }) |
| | | status: number; |
| | | |
| | | @Column({ comment: '热更新 0-否 1-是', default: 0 }) |
| | | hotUpdate: number; |
| | | |
| | | @Column({ comment: '描述', type: 'text' }) |
| | | description: string; |
| | | } |
| New file |
| | |
| | | [ |
| | | { |
| | | "name": "应用管理", |
| | | "router": null, |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": "icon-app", |
| | | "orderNum": 4, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "版本管理", |
| | | "router": "/app/version", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-tag", |
| | | "orderNum": 0, |
| | | "viewPath": "modules/app/views/version.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "app:version:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "app:version:update,app:version:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "app:version:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "app:version:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "app:version:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "app:version:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "意见反馈", |
| | | "router": "/app/feedback", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-info", |
| | | "orderNum": 0, |
| | | "viewPath": "modules/app/views/feedback.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "app:feedback:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "app:feedback:update,app:feedback:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "app:feedback:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "app:feedback:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "app:feedback:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "app:feedback:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "投诉举报", |
| | | "router": "/app/complain", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-new", |
| | | "orderNum": 0, |
| | | "viewPath": "modules/app/views/complain.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "app:complain:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "app:complain:update,app:complain:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "app:complain:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "app:complain:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "app:complain:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "app:complain:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| New file |
| | |
| | | { |
| | | "dependencies": { |
| | | "semver": "^7.5.4" |
| | | } |
| | | } |
| New file |
| | |
| | | import { Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { AppComplainEntity } from '../entity/complain'; |
| | | |
| | | /** |
| | | * 意见反馈 |
| | | */ |
| | | @Provide() |
| | | export class AppComplainService extends BaseService { |
| | | @InjectEntityModel(AppComplainEntity) |
| | | appComplainEntity: Repository<AppComplainEntity>; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | /** |
| | | * 提交 |
| | | * @param info |
| | | */ |
| | | async submit(info: AppComplainEntity) { |
| | | await this.appComplainEntity.insert(info); |
| | | } |
| | | |
| | | /** |
| | | * 更新 |
| | | * @param param |
| | | */ |
| | | async update(param: AppComplainEntity) { |
| | | if (param.status == 1) { |
| | | param.handlerId = this.ctx.admin.userId; |
| | | } |
| | | await super.update(param); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { AppFeedbackEntity } from '../entity/feedback'; |
| | | |
| | | /** |
| | | * 意见反馈 |
| | | */ |
| | | @Provide() |
| | | export class AppFeedbackService extends BaseService { |
| | | @InjectEntityModel(AppFeedbackEntity) |
| | | appFeedbackEntity: Repository<AppFeedbackEntity>; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | /** |
| | | * 提交 |
| | | * @param info |
| | | */ |
| | | async submit(info: AppFeedbackEntity) { |
| | | await this.appFeedbackEntity.insert(info); |
| | | } |
| | | |
| | | /** |
| | | * 更新 |
| | | * @param param |
| | | */ |
| | | async update(param: AppFeedbackEntity) { |
| | | if (param.status == 1) { |
| | | param.handlerId = this.ctx.admin.userId; |
| | | } |
| | | await super.update(param); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Not, Repository } from 'typeorm'; |
| | | import { AppVersionEntity } from '../entity/version'; |
| | | import * as semver from 'semver'; |
| | | |
| | | /** |
| | | * 应用版本 |
| | | */ |
| | | @Provide() |
| | | export class AppVersionService extends BaseService { |
| | | @InjectEntityModel(AppVersionEntity) |
| | | appVersionEntity: Repository<AppVersionEntity>; |
| | | |
| | | /** |
| | | * 检查更新 |
| | | * @param version |
| | | */ |
| | | async check(version: string, type = 0) { |
| | | const info = await this.appVersionEntity.findOneBy({ type, status: 1 }); |
| | | if (info && semver.gt(info.version, version)) { |
| | | return info; |
| | | } |
| | | return; |
| | | } |
| | | |
| | | /** |
| | | * 修改之后 |
| | | * @param data |
| | | * @param type |
| | | */ |
| | | async modifyAfter(data: any, type: 'add' | 'update' | 'delete') { |
| | | if (type == 'add' || type == 'update') { |
| | | const info = await this.appVersionEntity.findOneBy({ id: data.id }); |
| | | if (info.status == 1) { |
| | | // 将其他的版本设置为禁用 |
| | | await this.appVersionEntity.update( |
| | | { type: info.type, id: Not(info.id) }, |
| | | { status: 0 } |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 10, |
| | | // app参数配置允许读取的key |
| | | allowKeys: [], |
| | | allowKeys: ['privacyPolicy', 'userAgreement'], |
| | | // jwt 生成解密token的 |
| | | jwt: { |
| | | // 单点登录 |
| | | sso: false, |
| | | // 注意: 最好重新修改,防止破解 |
| | | secret: '9184c592-96a8-4db5-b722-65df1e892c83', |
| | | secret: '83cd61e9-8e10-4efb-8c48-af22d773d774', |
| | | // token |
| | | token: { |
| | | // 2小时过期,需要用刷新token |
| | | expire: 7 * 24 * 3600, |
| | | expire: 2 * 24 * 3600, |
| | | // 15天内,如果没操作过就需要重新登录 |
| | | refreshExpire: 24 * 3600 * 15, |
| | | }, |
| | |
| | | this.ctx.body = await this.baseSysParamService.htmlByKey(key); |
| | | } |
| | | |
| | | @Get('/stringbykey', { summary: '获得网页内容的参数值' }) |
| | | @Get('/stringbykey', { summary: '获得网页内容的参数值' }) |
| | | async stringByKey(@Query('key') key: string) { |
| | | this.ctx.body = await this.baseSysParamService.getByKey(key); |
| | | } |
| | |
| | | { |
| | | "base_sys_param": [ |
| | | { |
| | | "keyName": "rich", |
| | | "name": "富文本参数", |
| | | "data": "<h3><strong>这是一个富文本</strong></h3><p>xxx</p><p>xxxxxxxxxx</p><p><br></p>", |
| | | "keyName": "orderTimeout", |
| | | "name": "订单超时时间", |
| | | "data": "120", |
| | | "dataType": 0, |
| | | "remark": "订单超时未支付,自动关闭。单位:分钟" |
| | | }, |
| | | { |
| | | "keyName": "orderConfirm", |
| | | "name": "自动确认收货", |
| | | "data": "15", |
| | | "dataType": 0, |
| | | "remark": "自动确认收货时间,从支付成功起算,单位:天" |
| | | }, |
| | | { |
| | | "keyName": "userAgreement", |
| | | "name": "用户协议", |
| | | "data": "<h3 style=\"text-align: center;\"><strong>用户协议</strong></h3><p><br></p><p>xxxxxx</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, |
| | | "keyName": "privacyPolicy", |
| | | "name": "隐私政策", |
| | | "data": "<h3 style=\"text-align: center;\"><strong>隐私政策</strong></h3><p><br></p><p>xxxxxx</p>", |
| | | "dataType": 1, |
| | | "remark": null |
| | | } |
| | | ], |
| | |
| | | "password": "e10adc3949ba59abbe56e057f20f883e", |
| | | "passwordV": 7, |
| | | "nickName": "管理员", |
| | | "headImg": "https://cool-js.com/admin/headimg.jpg", |
| | | "phone": "18000000000", |
| | | "email": "team@cool-js.com", |
| | | "status": 1, |
| | |
| | | "roleId": 1 |
| | | } |
| | | ] |
| | | } |
| | | } |
| | |
| | | const tenantId = this.getTenantId(); |
| | | if (tenantId) { |
| | | queryBuilder.andWhere( |
| | | `${ |
| | | queryBuilder.alias ? queryBuilder.alias + '.' : '' |
| | | }tenantId = '${tenantId}'` |
| | | `${queryBuilder.alias ? queryBuilder.alias + '.' : '' |
| | | }tenantId = ${tenantId}` |
| | | ); |
| | | } |
| | | } |
| | |
| | | if (!this.tenant?.enable) return; |
| | | const tenantId = this.getTenantId(); |
| | | if (tenantId) { |
| | | //queryBuilder.set({ tenantId }); |
| | | queryBuilder.andWhere(`tenantId = '${tenantId}'`); |
| | | } |
| | | } |
| | |
| | | import { BaseEntity, transformerJson } from '../base'; |
| | | import { BaseEntity,transformerJson } from '../base'; |
| | | import { Column, Index, Entity } from 'typeorm'; |
| | | |
| | | /** |
| | |
| | | @Column({ comment: 'ip', nullable: true }) |
| | | ip: string; |
| | | |
| | | @Column({ |
| | | comment: '参数', |
| | | nullable: true, |
| | | type: 'json', |
| | | transformer: transformerJson, |
| | | }) |
| | | @Column({ comment: '参数', nullable: true, type: 'json',transformer: transformerJson, }) |
| | | params: string; |
| | | } |
| | |
| | | [ |
| | | { |
| | | "name": "系统管理", |
| | | "router": "/sys", |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": "icon-set", |
| | | "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": [ |
| | | { |
| | | "tenantId": null, |
| | | "name": "插件列表", |
| | | "router": "/helper/plugins", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-list", |
| | | "orderNum": 1, |
| | | "viewPath": "modules/helper/views/plugins.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "tenantId": null, |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "plugin:info:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "tenantId": null, |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "plugin:info:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "tenantId": null, |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "plugin:info:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "tenantId": null, |
| | | "name": "安装插件", |
| | | "router": null, |
| | | "perms": "plugin:info:install", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "tenantId": null, |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "plugin:info:update", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "tenantId": null, |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "plugin:info:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "tenantId": null, |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "plugin:info:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | { |
| | | "name": "系统管理", |
| | | "router": "/sys", |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": "icon-system", |
| | | "orderNum": 9, |
| | | "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": "/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.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": null, |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": "icon-goods", |
| | | "orderNum": 1, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "商品列表", |
| | | "router": "/goods/info", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-menu", |
| | | "orderNum": 1, |
| | | "viewPath": "modules/goods/views/info.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "goods:info:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "goods:info:update,goods:info:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "goods:info:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "goods:info:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "goods:info:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "goods:info:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "分类列表", |
| | | "router": "/goods/type", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-component", |
| | | "orderNum": 2, |
| | | "viewPath": "modules/goods/views/type.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "goods:type:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "goods:type:update,goods:type:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "goods:type:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "goods:type:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "goods:type:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "goods:type:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "评论列表", |
| | | "router": "/goods/comment", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-message", |
| | | "orderNum": 3, |
| | | "viewPath": "modules/goods/views/comment.vue", |
| | | "keepAlive": false, |
| | | "isShow": false, |
| | | "childMenus": [ |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "goods:comment:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "搜索关键词", |
| | | "router": "/goods/search-keyword", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-hot", |
| | | "orderNum": 4, |
| | | "viewPath": "modules/goods/views/search-keyword.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "goods:searchKeyword:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "goods:searchKeyword:update,goods:searchKeyword:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "goods:searchKeyword:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "goods:searchKeyword:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "goods:searchKeyword:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "goods:searchKeyword:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "订单管理", |
| | | "router": null, |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": "icon-log", |
| | | "orderNum": 2, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "订单列表", |
| | | "router": "/order/info", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-menu", |
| | | "orderNum": 1, |
| | | "viewPath": "modules/order/views/info.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "退款处理", |
| | | "router": null, |
| | | "perms": "order:info:refundHandle", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "物流信息", |
| | | "router": null, |
| | | "perms": "order:info:logistics", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "发货", |
| | | "router": null, |
| | | "perms": "order:info:deliver", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "order:info:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "order:info:update,order:info:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "order:info:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "order:info:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "order:info:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "退款列表", |
| | | "router": "/order/refund", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-amount", |
| | | "orderNum": 2, |
| | | "viewPath": "modules/order/views/refund.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "活动管理", |
| | | "router": null, |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": "icon-activity", |
| | | "orderNum": 3, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "优惠券列表", |
| | | "router": "/market/coupon/info", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-activity", |
| | | "orderNum": 1, |
| | | "viewPath": "modules/market/views/coupon/info.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "market:coupon:info:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "market:coupon:info:update,market:coupon:info:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "market:coupon:info:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "market:coupon:info:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "market:coupon:info:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "market:coupon:info:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "领取列表", |
| | | "router": "/market/coupon/user", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-menu", |
| | | "orderNum": 2, |
| | | "viewPath": "modules/market/views/coupon/user.vue", |
| | | "keepAlive": false, |
| | | "isShow": false, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "market:coupon:user:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "market:coupon:user:update,market:coupon:user:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "market:coupon:user:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "market:coupon:user:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "market:coupon:user:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "market:coupon:user:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "name": "信息管理", |
| | | "router": null, |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": "icon-dict", |
| | | "orderNum": 5, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "轮播图列表", |
| | | "router": "/info/banner", |
| | | "perms": null, |
| | | "type": 1, |
| | | "icon": "icon-pic", |
| | | "orderNum": 1, |
| | | "viewPath": "modules/info/views/banner.vue", |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [ |
| | | { |
| | | "name": "删除", |
| | | "router": null, |
| | | "perms": "info:banner:delete", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "修改", |
| | | "router": null, |
| | | "perms": "info:banner:update,info:banner:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "单个信息", |
| | | "router": null, |
| | | "perms": "info:banner:info", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "列表查询", |
| | | "router": null, |
| | | "perms": "info:banner:list", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "分页查询", |
| | | "router": null, |
| | | "perms": "info:banner:page", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | }, |
| | | { |
| | | "name": "新增", |
| | | "router": null, |
| | | "perms": "info:banner:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | |
| | | */ |
| | | @Middleware() |
| | | export class BaseAuthorityMiddleware |
| | | implements IMiddleware<Context, NextFunction> |
| | | { |
| | | implements IMiddleware<Context, NextFunction> { |
| | | @Config('koa.globalPrefix') |
| | | prefix; |
| | | |
| | |
| | | ? departmentIds |
| | | : []; |
| | | } |
| | | |
| | | } catch (error) { } |
| | | // 使用matchUrl方法来检查URL是否应该被忽略 |
| | | const isIgnored = this.ignoreUrls.some(pattern => |
| New file |
| | |
| | | import { ModuleConfig } from '@cool-midway/core'; |
| | | |
| | | /** |
| | | * 模块配置 |
| | | */ |
| | | export default () => { |
| | | return { |
| | | // 模块名称 |
| | | name: '统计分析', |
| | | // 模块描述 |
| | | description: '订单、商品、用户等统计', |
| | | // 中间件,只对本模块有效 |
| | | middlewares: [], |
| | | // 中间件,全局有效 |
| | | globalMiddlewares: [], |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 0, |
| | | } as ModuleConfig; |
| | | }; |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { Body, Inject, Post } from '@midwayjs/core'; |
| | | import { CountUserService } from '../../service/user'; |
| | | import { CountOrderService } from '../../service/order'; |
| | | import { CountGoodsService } from '../../service/goods'; |
| | | |
| | | /** |
| | | * 首页统计 |
| | | */ |
| | | @CoolController() |
| | | export class AdminCountHomeController extends BaseController { |
| | | @Inject() |
| | | countUserService: CountUserService; |
| | | |
| | | @Inject() |
| | | countOrderService: CountOrderService; |
| | | |
| | | @Inject() |
| | | countGoodsService: CountGoodsService; |
| | | |
| | | @Post('/userSummary', { summary: '用户概况' }) |
| | | async userSummary() { |
| | | return this.ok(await this.countUserService.summary()); |
| | | } |
| | | |
| | | @Post('/userChart', { summary: '用户图表' }) |
| | | async userChart( |
| | | // 天数 |
| | | @Body('dayCount') dayCount: number |
| | | ) { |
| | | return this.ok(await this.countUserService.chart(dayCount)); |
| | | } |
| | | |
| | | @Post('/orderSummary', { summary: '订单概况' }) |
| | | async orderSummary( |
| | | // 类型 count-数量 amount-金额 |
| | | @Body('type') type: 'count' | 'amount' |
| | | ) { |
| | | return this.ok(await this.countOrderService.summary(type)); |
| | | } |
| | | |
| | | @Post('/orderChart', { summary: '订单图表' }) |
| | | async orderChart( |
| | | // 天数 |
| | | @Body('dayCount') dayCount: number, |
| | | // 类型 count-数量 amount-金额 |
| | | @Body('type') type: 'count' | 'amount' |
| | | ) { |
| | | return this.ok(await this.countOrderService.chart(dayCount, type)); |
| | | } |
| | | |
| | | @Post('/goodsRank', { summary: '商品排行' }) |
| | | async goodsRank( |
| | | // 类型 count-数量 amount-金额 |
| | | @Body('type') type: 'count' | 'amount', |
| | | // 条数 |
| | | @Body('limit') limit: number |
| | | ) { |
| | | return this.ok(await this.countGoodsService.rank(type, limit)); |
| | | } |
| | | |
| | | @Post('/goodsCategory', { summary: '商品分类' }) |
| | | async goodsCategory( |
| | | // 类型 count-数量 amount-金额 |
| | | @Body('type') type: 'count' | 'amount' |
| | | ) { |
| | | return this.ok(await this.countGoodsService.category(type)); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import * as moment from 'moment'; |
| | | import { Utils } from '../../../comm/utils'; |
| | | |
| | | /** |
| | | * 通用 |
| | | */ |
| | | @Provide() |
| | | export class CountCommService extends BaseService { |
| | | @Inject() |
| | | utils: Utils; |
| | | |
| | | /** |
| | | * 获得时间范围 |
| | | * @param unit |
| | | */ |
| | | getTimeRange(unit: 'day' | 'week' | 'month' | 'year') { |
| | | let start, end; |
| | | if (unit === 'week') { |
| | | // 周的开始时间加一天,以匹配您的需求 |
| | | start = moment() |
| | | .startOf(unit) |
| | | .add(1, 'days') |
| | | .format('YYYY-MM-DD HH:mm:ss'); |
| | | } else { |
| | | start = moment().startOf(unit).format('YYYY-MM-DD HH:mm:ss'); |
| | | } |
| | | end = moment().endOf(unit).format('YYYY-MM-DD HH:mm:ss'); |
| | | return { start, end }; |
| | | } |
| | | |
| | | /** |
| | | * 图表 |
| | | * @param tableName |
| | | * @param dayCount |
| | | * @param column 统计列 |
| | | * @returns |
| | | */ |
| | | async chart(tableName: string, dayCount: number, column = 'count(a.id)') { |
| | | const result = { |
| | | datas: [], |
| | | dates: [], |
| | | }; |
| | | const dates = this.utils.getRecentlyDates(dayCount); |
| | | const count = await this.nativeQuery( |
| | | `SELECT |
| | | ${column} as total, |
| | | LEFT(a.createTime, 10) AS time |
| | | FROM |
| | | ${tableName} a |
| | | WHERE |
| | | DATE_SUB( CURDATE( ), INTERVAL ? DAY ) <= a.createTime |
| | | GROUP BY |
| | | LEFT(a.createTime, 10)`, |
| | | [dayCount] |
| | | ); |
| | | dates.forEach(date => { |
| | | let data = 0; |
| | | for (const item of count) { |
| | | if (date == item.time) { |
| | | data = item.total; |
| | | break; |
| | | } |
| | | } |
| | | result.dates.push(date); |
| | | result.datas.push(data); |
| | | }); |
| | | return result; |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { OrderGoodsEntity } from '../../order/entity/goods'; |
| | | |
| | | /** |
| | | * 商品统计 |
| | | */ |
| | | @Provide() |
| | | export class CountGoodsService extends BaseService { |
| | | @InjectEntityModel(OrderGoodsEntity) |
| | | orderGoodsEntity: Repository<OrderGoodsEntity>; |
| | | |
| | | /** |
| | | * 销售排行 |
| | | * @param type 类型 count-数量 amount-金额 |
| | | * @param limit 条数 |
| | | */ |
| | | async rank(type: 'count' | 'amount', limit = 10) { |
| | | const sql = `SELECT * FROM ( |
| | | SELECT |
| | | a.goodsId, |
| | | b.title, |
| | | b.mainPic, |
| | | ${type === 'count' ? 'SUM(a.count)' : 'SUM(a.price * a.count)'} AS total |
| | | FROM |
| | | order_goods a |
| | | LEFT JOIN goods_info b ON a.goodsId = b.id |
| | | GROUP BY |
| | | a.goodsId |
| | | ORDER BY |
| | | total DESC |
| | | ) a LIMIT ${parseInt(limit.toString())}`; |
| | | return this.nativeQuery(sql); |
| | | } |
| | | |
| | | /** |
| | | * 分类统计 |
| | | * @param type 类型 count-数量 amount-金额 |
| | | */ |
| | | async category(type: 'count' | 'amount') { |
| | | const sql = `SELECT |
| | | b.typeId, |
| | | c.name AS typeName, |
| | | ${type === 'count' ? 'SUM(a.count)' : 'SUM(a.price * a.count)'} AS total |
| | | FROM |
| | | order_goods a |
| | | LEFT JOIN goods_info b ON a.goodsId = b.id |
| | | LEFT JOIN goods_type c ON b.typeId = c.id |
| | | GROUP BY |
| | | b.typeId`; |
| | | return this.nativeQuery(sql); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { OrderInfoEntity } from '../../order/entity/info'; |
| | | import { CountCommService } from './comm'; |
| | | |
| | | /** |
| | | * 订单统计 |
| | | */ |
| | | @Provide() |
| | | export class CountOrderService extends BaseService { |
| | | @InjectEntityModel(OrderInfoEntity) |
| | | orderInfoEntity: Repository<OrderInfoEntity>; |
| | | |
| | | @Inject() |
| | | countCommService: CountCommService; |
| | | |
| | | /** |
| | | * 概况 |
| | | * @param type 类型 count-数量 amount-金额 |
| | | * @returns |
| | | */ |
| | | async summary(type: 'count' | 'amount' = 'count') { |
| | | // 定义一个函数来创建查询 |
| | | const createQuery = async (start, end) => { |
| | | if (type === 'count') { |
| | | return await this.orderInfoEntity |
| | | .createQueryBuilder('a') |
| | | .where('a.createTime >= :start', { start }) |
| | | .andWhere('a.createTime <= :end', { end }) |
| | | .getCount(); |
| | | } |
| | | if (type === 'amount') { |
| | | const result = await this.orderInfoEntity |
| | | .createQueryBuilder('a') |
| | | .select('SUM(a.price)', 'amount') |
| | | .where('a.createTime >= :start', { start }) |
| | | .andWhere('a.createTime <= :end', { end }) |
| | | .getRawOne(); |
| | | return result.amount || 0; |
| | | } |
| | | }; |
| | | |
| | | // 总数 |
| | | const total = await (type == 'amount' |
| | | ? this.orderInfoEntity.sum('price') |
| | | : this.orderInfoEntity.count()); |
| | | |
| | | // 获取今天的时间范围 |
| | | const { start: todayStart, end: todayEnd } = |
| | | this.countCommService.getTimeRange('day'); |
| | | // 今天 |
| | | const today = await createQuery(todayStart, todayEnd); |
| | | |
| | | // 获取本周的时间范围 |
| | | const { start: weekStart, end: weekEnd } = |
| | | this.countCommService.getTimeRange('week'); |
| | | // 本周 |
| | | const week = await createQuery(weekStart, weekEnd); |
| | | |
| | | // 获取本月的时间范围 |
| | | const { start: monthStart, end: monthEnd } = |
| | | this.countCommService.getTimeRange('month'); |
| | | // 本月 |
| | | const month = await createQuery(monthStart, monthEnd); |
| | | |
| | | // 获取年的时间范围 |
| | | const { start: yearStart, end: yearEnd } = |
| | | this.countCommService.getTimeRange('year'); |
| | | // 今年 |
| | | const year = await createQuery(yearStart, yearEnd); |
| | | |
| | | return { total, today, week, month, year }; |
| | | } |
| | | |
| | | /** |
| | | * 图表 |
| | | * @param dayCount 最近多少天 |
| | | * @param type 类型 count-数量 amount-金额 |
| | | */ |
| | | async chart(dayCount = 30, type: 'count' | 'amount' = 'count') { |
| | | if (type == 'count') { |
| | | return await this.countCommService.chart('order_info', dayCount); |
| | | } |
| | | if (type == 'amount') { |
| | | return await this.countCommService.chart( |
| | | 'order_info', |
| | | dayCount, |
| | | 'SUM(a.price)' |
| | | ); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { UserInfoEntity } from '../../user/entity/info'; |
| | | import { CountCommService } from './comm'; |
| | | |
| | | /** |
| | | * 用户统计 |
| | | */ |
| | | @Provide() |
| | | export class CountUserService extends BaseService { |
| | | @InjectEntityModel(UserInfoEntity) |
| | | userInfoEntity: Repository<UserInfoEntity>; |
| | | |
| | | @Inject() |
| | | countCommService: CountCommService; |
| | | |
| | | /** |
| | | * 概况 |
| | | */ |
| | | async summary() { |
| | | // 定义一个函数来创建查询 |
| | | const createQuery = async (start, end) => { |
| | | return await this.userInfoEntity |
| | | .createQueryBuilder('a') |
| | | .where('a.createTime >= :start', { start }) |
| | | .andWhere('a.createTime <= :end', { end }) |
| | | .getCount(); |
| | | }; |
| | | |
| | | // 总数 |
| | | const total = await this.userInfoEntity.count(); |
| | | |
| | | // 获取今天的时间范围 |
| | | const { start: todayStart, end: todayEnd } = |
| | | this.countCommService.getTimeRange('day'); |
| | | // 今天 |
| | | const today = await createQuery(todayStart, todayEnd); |
| | | |
| | | // 获取本周的时间范围 |
| | | const { start: weekStart, end: weekEnd } = |
| | | this.countCommService.getTimeRange('week'); |
| | | // 本周 |
| | | const week = await createQuery(weekStart, weekEnd); |
| | | |
| | | // 获取本月的时间范围 |
| | | const { start: monthStart, end: monthEnd } = |
| | | this.countCommService.getTimeRange('month'); |
| | | // 本月 |
| | | const month = await createQuery(monthStart, monthEnd); |
| | | |
| | | // 获取年的时间范围 |
| | | const { start: yearStart, end: yearEnd } = |
| | | this.countCommService.getTimeRange('year'); |
| | | // 今年 |
| | | const year = await createQuery(yearStart, yearEnd); |
| | | |
| | | return { total, today, week, month, year }; |
| | | } |
| | | |
| | | /** |
| | | * 图表 |
| | | * @param dayCount 最近多少天 |
| | | */ |
| | | async chart(dayCount = 30) { |
| | | return await this.countCommService.chart('user_info', dayCount); |
| | | } |
| | | } |
| New file |
| | |
| | | import { ModuleConfig } from '@cool-midway/core'; |
| | | |
| | | /** |
| | | * 模块配置 |
| | | */ |
| | | export default () => { |
| | | return { |
| | | // 模块名称 |
| | | name: '客服模块', |
| | | // 模块描述 |
| | | description: '客服系统', |
| | | // 中间件,只对本模块有效 |
| | | middlewares: [], |
| | | // 中间件,全局有效 |
| | | globalMiddlewares: [], |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 0, |
| | | } as ModuleConfig; |
| | | }; |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { CsMsgEntity } from '../../entity/msg'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | import { BaseSysUserEntity } from '../../../base/entity/sys/user'; |
| | | import { CsMsgService } from '../../service/msg'; |
| | | import { Body, Get, Inject, Post } from '@midwayjs/core'; |
| | | |
| | | /** |
| | | * 消息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['page'], |
| | | entity: CsMsgEntity, |
| | | service: CsMsgService, |
| | | pageQueryOp: { |
| | | select: [ |
| | | 'a.*', |
| | | 'b.nickName', |
| | | 'b.avatarUrl', |
| | | 'c.name as adminUserName', |
| | | 'c.headImg as adminUserHeadImg', |
| | | ], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id and a.type = 0', |
| | | }, |
| | | { |
| | | entity: BaseSysUserEntity, |
| | | alias: 'c', |
| | | condition: 'a.userId = c.id and a.type = 1', |
| | | }, |
| | | ], |
| | | where: ctx => { |
| | | const { sessionId } = ctx.request.body; |
| | | return [['a.sessionId = :sessionId', { sessionId }]]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AdminCsMsgController extends BaseController { |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Inject() |
| | | csMsgService: CsMsgService; |
| | | |
| | | @Get('/unreadCount', { summary: '未读消息数' }) |
| | | async unreadCount() { |
| | | return this.ok(await this.csMsgService.unreadCount(null, 0)); |
| | | } |
| | | |
| | | @Post('/read', { summary: '标记已读' }) |
| | | async read(@Body('msgIds') msgIds: number[]) { |
| | | this.csMsgService.read(msgIds); |
| | | return this.ok(); |
| | | } |
| | | } |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { CsSessionEntity } from '../../entity/session'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | |
| | | /** |
| | | * 客服会话 |
| | | */ |
| | | @CoolController({ |
| | | api: ['page'], |
| | | entity: CsSessionEntity, |
| | | pageQueryOp: { |
| | | select: ['a.*', 'b.nickName', 'b.avatarUrl'], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class Controller extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { CsMsgEntity } from '../../entity/msg'; |
| | | import { CsSessionEntity } from '../../entity/session'; |
| | | import { BaseSysUserEntity } from '../../../base/entity/sys/user'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | import { Body, Get, Inject, Post } from '@midwayjs/core'; |
| | | import { CsMsgService } from '../../service/msg'; |
| | | |
| | | /** |
| | | * 消息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['page'], |
| | | entity: CsMsgEntity, |
| | | pageQueryOp: { |
| | | select: [ |
| | | 'a.*', |
| | | 'b.nickName', |
| | | 'b.avatarUrl', |
| | | 'c.name as adminUserName', |
| | | 'c.headImg as adminUserHeadImg', |
| | | ], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id and a.type = 0', |
| | | }, |
| | | { |
| | | entity: BaseSysUserEntity, |
| | | alias: 'c', |
| | | condition: 'a.userId = c.id and a.type = 1', |
| | | }, |
| | | { |
| | | entity: CsSessionEntity, |
| | | alias: 'd', |
| | | condition: 'a.sessionId = d.id', |
| | | }, |
| | | ], |
| | | where: ctx => { |
| | | return [['d.userId = :userId', { userId: ctx.user?.id }]]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppCsMsgController extends BaseController { |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Inject() |
| | | csMsgService: CsMsgService; |
| | | |
| | | @Get('/unreadCount', { summary: '未读消息数' }) |
| | | async unreadCount() { |
| | | return this.ok(await this.csMsgService.unreadCount(this.ctx.user?.id, 1)); |
| | | } |
| | | |
| | | @Post('/read', { summary: '标记已读' }) |
| | | async read(@Body('msgIds') msgIds: number[]) { |
| | | this.csMsgService.read(msgIds); |
| | | return this.ok(); |
| | | } |
| | | } |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { Get, Inject, Post } from '@midwayjs/core'; |
| | | import { CsSessionService } from '../../service/session'; |
| | | |
| | | /** |
| | | * 客服会话 |
| | | */ |
| | | @CoolController() |
| | | export class AppCsSessionController extends BaseController { |
| | | @Inject() |
| | | csSessionService: CsSessionService; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Get('/detail', { summary: '会话详情' }) |
| | | async detail() { |
| | | return this.ok(await this.csSessionService.detail(this.ctx.user?.id)); |
| | | } |
| | | |
| | | @Post('/create', { summary: '创建会话' }) |
| | | async create() { |
| | | return this.ok(await this.csSessionService.create(this.ctx.user?.id)); |
| | | } |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 客服连接 |
| | | */ |
| | | @Entity('cs_conn') |
| | | export class CsConnEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Column({ comment: '连接ID' }) |
| | | connId: string; |
| | | |
| | | @Column({ comment: '类型 0-客户 1-后台', default: 0 }) |
| | | type: number; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 客服消息 |
| | | */ |
| | | @Entity('cs_msg') |
| | | export class CsMsgEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Index() |
| | | @Column({ comment: '会话ID' }) |
| | | sessionId: number; |
| | | |
| | | @Column({ comment: '消息内容', type: 'json' }) |
| | | content: { |
| | | type: |
| | | | 'text' |
| | | | 'image' |
| | | | 'voice' |
| | | | 'video' |
| | | | 'file' |
| | | | 'link' |
| | | | 'location' |
| | | | 'emoji'; |
| | | data: string; |
| | | }; |
| | | |
| | | @Column({ comment: '类型 0-反馈 1-回复', default: 0 }) |
| | | type: number; |
| | | |
| | | @Column({ comment: '状态 0-未读 1-已读', default: 0 }) |
| | | status: number; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | import { CsMsgEntity } from './msg'; |
| | | |
| | | /** |
| | | * 客服会话 |
| | | */ |
| | | @Entity('cs_session') |
| | | export class CsSessionEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Column({ comment: '最后一条消息', nullable: true, type: 'json' }) |
| | | lastMsg: CsMsgEntity; |
| | | |
| | | @Column({ comment: '客服未读消息数', default: 0 }) |
| | | adminUnreadCount: number; |
| | | } |
| New file |
| | |
| | | [ |
| | | { |
| | | "name": "客服模块", |
| | | "router": null, |
| | | "perms": null, |
| | | "type": 0, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": false, |
| | | "childMenus": [ |
| | | { |
| | | "name": "通用", |
| | | "router": null, |
| | | "perms": "cs:session:page,cs:session:update,cs:session:info,cs:session:list,cs:session:add,cs:msg:unreadCount,cs:msg:page,cs:msg:update,cs:msg:info,cs:msg:list,cs:msg:add", |
| | | "type": 2, |
| | | "icon": null, |
| | | "orderNum": 0, |
| | | "viewPath": null, |
| | | "keepAlive": true, |
| | | "isShow": true, |
| | | "childMenus": [] |
| | | } |
| | | ] |
| | | } |
| | | ] |
| New file |
| | |
| | | import { Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { CsConnEntity } from '../entity/conn'; |
| | | import { CsSessionEntity } from '../entity/session'; |
| | | |
| | | /** |
| | | * 连接 |
| | | */ |
| | | @Provide() |
| | | export class CsConnService extends BaseService { |
| | | @InjectEntityModel(CsConnEntity) |
| | | csConnEntity: Repository<CsConnEntity>; |
| | | |
| | | @InjectEntityModel(CsSessionEntity) |
| | | csSessionEntity: Repository<CsSessionEntity>; |
| | | |
| | | /** |
| | | * 绑定 |
| | | * @param isAdmin |
| | | * @param userId |
| | | * @param connId |
| | | */ |
| | | async binding(isAdmin: boolean, userId: number, connId: string) { |
| | | const info = await this.csConnEntity.findOneBy({ |
| | | userId, |
| | | type: isAdmin ? 1 : 0, |
| | | }); |
| | | if (info) { |
| | | info.connId = connId; |
| | | info.type = isAdmin ? 1 : 0; |
| | | await this.csConnEntity.update(info.id, { connId: connId }); |
| | | } else { |
| | | const entity = new CsConnEntity(); |
| | | entity.userId = userId; |
| | | entity.connId = connId; |
| | | entity.type = isAdmin ? 1 : 0; |
| | | await this.csConnEntity.insert(entity); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获得连接ID |
| | | * @param isAdmin |
| | | * @param sessionId |
| | | */ |
| | | async getConnId(isAdmin: boolean, sessionId: number) { |
| | | const session = await this.csSessionEntity.findOneBy({ id: sessionId }); |
| | | if (!session) return; |
| | | const info = await this.csConnEntity.findOneBy({ |
| | | userId: session.userId, |
| | | type: isAdmin ? 1 : 0, |
| | | }); |
| | | return info ? info.connId : null; |
| | | } |
| | | } |
| New file |
| | |
| | | import { App, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { In, Repository } from 'typeorm'; |
| | | import { CsMsgEntity } from '../entity/msg'; |
| | | import { CsSessionEntity } from '../entity/session'; |
| | | import { CsConnService } from './conn'; |
| | | import { Application as SocketApplication } from '@midwayjs/socketio'; |
| | | import * as _ from 'lodash'; |
| | | import { UserInfoEntity } from '../../user/entity/info'; |
| | | import { BaseSysUserEntity } from '../../base/entity/sys/user'; |
| | | |
| | | /** |
| | | * 消息 |
| | | */ |
| | | @Provide() |
| | | export class CsMsgService extends BaseService { |
| | | @InjectEntityModel(CsMsgEntity) |
| | | csMsgEntity: Repository<CsMsgEntity>; |
| | | |
| | | @InjectEntityModel(CsSessionEntity) |
| | | csSessionEntity: Repository<CsSessionEntity>; |
| | | |
| | | @InjectEntityModel(UserInfoEntity) |
| | | userInfoEntity: Repository<UserInfoEntity>; |
| | | |
| | | @InjectEntityModel(BaseSysUserEntity) |
| | | baseSysUserEntity: Repository<BaseSysUserEntity>; |
| | | |
| | | @Inject() |
| | | csConnService: CsConnService; |
| | | |
| | | @App('socketIO') |
| | | socketApp: SocketApplication; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | /** |
| | | * 消息标记为已读 |
| | | * @param msgIds |
| | | */ |
| | | async read(msgIds: number[]) { |
| | | await this.csMsgEntity.update({ id: In(msgIds) }, { status: 1 }); |
| | | } |
| | | |
| | | /** |
| | | * 未读消息数 |
| | | * @param userId |
| | | * @param type |
| | | */ |
| | | async unreadCount(userId: number, type: number) { |
| | | if (userId) { |
| | | return this.csMsgEntity.countBy({ userId, type, status: 0 }); |
| | | } |
| | | return this.csMsgEntity.countBy({ type, status: 0 }); |
| | | } |
| | | |
| | | /** |
| | | * 更新客户未读消息数 |
| | | * @param sessionId |
| | | */ |
| | | async updateAdminUnreadCount(sessionId: number) { |
| | | const count = await this.csMsgEntity.countBy({ |
| | | sessionId, |
| | | type: 0, |
| | | status: 0, |
| | | }); |
| | | await this.csSessionEntity.update(sessionId, { adminUnreadCount: count }); |
| | | } |
| | | |
| | | /** |
| | | * 分页查询 |
| | | * @param query |
| | | * @param option |
| | | * @param connectionName |
| | | */ |
| | | async page(query: any, option: any, connectionName?: any) { |
| | | const { sessionId } = query; |
| | | const result = await super.page(query, option, connectionName); |
| | | // 消息置为已读 |
| | | const type = this.ctx.user?.id ? 1 : 0; |
| | | await this.csMsgEntity.update({ sessionId, type }, { status: 1 }); |
| | | // 更新未读消息数 |
| | | await this.updateAdminUnreadCount(sessionId); |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 消息发送 |
| | | * @param msg |
| | | */ |
| | | async send(msg: any, isAdmin: boolean) { |
| | | let session = await this.csSessionEntity.findOneBy({ id: msg.sessionId }); |
| | | if (isAdmin) { |
| | | msg.type = 1; |
| | | } else { |
| | | msg.type = 0; |
| | | if (!session) { |
| | | session = new CsSessionEntity(); |
| | | session.userId = msg.userId; |
| | | await this.csSessionEntity.insert(session); |
| | | } |
| | | } |
| | | await this.csMsgEntity.insert(msg); |
| | | // 更新未读消息数 |
| | | await this.updateAdminUnreadCount(msg.sessionId); |
| | | // 更新最后一条消息 |
| | | await this.csSessionEntity.update(session.id, { lastMsg: msg }); |
| | | |
| | | // 完善消息内容 |
| | | if (msg.type == 0) { |
| | | const user = await this.userInfoEntity.findOneBy({ id: msg.userId }); |
| | | msg.user = { |
| | | userId: user.id, |
| | | nickName: user.nickName, |
| | | avatarUrl: user.avatarUrl, |
| | | }; |
| | | } |
| | | if (msg.type == 1) { |
| | | const user = await this.baseSysUserEntity.findOneBy({ id: msg.userId }); |
| | | msg.user = { |
| | | userId: user.id, |
| | | nickName: user.name, |
| | | avatarUrl: user.headImg, |
| | | }; |
| | | } |
| | | // 发送给客户端 |
| | | if (isAdmin) { |
| | | // 获得连接ID |
| | | const connId = await this.csConnService.getConnId(false, msg.sessionId); |
| | | // 发送消息 |
| | | this.socketApp.of('/cs').to(connId).emit('msg', msg); |
| | | } else { |
| | | // 广播给所有的后端客服 |
| | | this.socketApp.of('/cs').emit('msg', msg); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | import { Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { CsSessionEntity } from '../entity/session'; |
| | | |
| | | /** |
| | | * 客服会话 |
| | | */ |
| | | @Provide() |
| | | export class CsSessionService extends BaseService { |
| | | @InjectEntityModel(CsSessionEntity) |
| | | csSessionEntity: Repository<CsSessionEntity>; |
| | | |
| | | /** |
| | | * 会话详情 |
| | | * @param userId |
| | | */ |
| | | async detail(userId: number) { |
| | | return await this.csSessionEntity.findOneBy({ userId }); |
| | | } |
| | | |
| | | /** |
| | | * 创建会话 |
| | | * @param userId |
| | | * @returns |
| | | */ |
| | | async create(userId: number) { |
| | | const check = await this.detail(userId); |
| | | if (check) { |
| | | return check; |
| | | } |
| | | const session = new CsSessionEntity(); |
| | | session.userId = userId; |
| | | await this.csSessionEntity.insert(session); |
| | | return session; |
| | | } |
| | | } |
| New file |
| | |
| | | import { |
| | | WSController, |
| | | OnWSConnection, |
| | | Inject, |
| | | OnWSMessage, |
| | | } from '@midwayjs/core'; |
| | | import { CsConnService } from '../service/conn'; |
| | | import { CsSocketTokenMiddleware } from './middleware/token'; |
| | | import { CsMsgService } from '../service/msg'; |
| | | /** |
| | | * code-socket |
| | | */ |
| | | @WSController('/cs') |
| | | export class CsSocketIoController { |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Inject() |
| | | csConnService: CsConnService; |
| | | |
| | | @Inject() |
| | | csMsgService: CsMsgService; |
| | | |
| | | // 客户端连接 |
| | | @OnWSConnection({ middleware: [CsSocketTokenMiddleware] }) |
| | | async onConnectionMethod() { |
| | | const { isAdmin, userId } = this.ctx.connData; |
| | | await this.csConnService.binding(isAdmin, userId, this.ctx.id); |
| | | this.ctx.emit('message', '连接成功'); |
| | | } |
| | | |
| | | // 发送消息 |
| | | @OnWSMessage('send', { middleware: [CsSocketTokenMiddleware] }) |
| | | async send(data) { |
| | | const { isAdmin, userId } = this.ctx.connData; |
| | | const { sessionId, content } = data; |
| | | await this.csMsgService.send( |
| | | { |
| | | userId, |
| | | sessionId, |
| | | content, |
| | | }, |
| | | isAdmin |
| | | ); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Config, Middleware } from '@midwayjs/core'; |
| | | import { NextFunction } from '@midwayjs/socketio'; |
| | | import * as jwt from 'jsonwebtoken'; |
| | | |
| | | @Middleware() |
| | | export class CsSocketTokenMiddleware { |
| | | @Config('module.user.jwt') |
| | | appJwtConfig; |
| | | |
| | | @Config('module.base.jwt') |
| | | adminJwtConfig; |
| | | |
| | | resolve() { |
| | | return async (ctx, next: NextFunction) => { |
| | | try { |
| | | // 获得连接参数 |
| | | const isAdmin = ctx.handshake.auth.isAdmin; |
| | | const token = ctx.handshake.auth.token; |
| | | const data = jwt.verify( |
| | | token, |
| | | isAdmin ? this.adminJwtConfig.secret : this.appJwtConfig.secret |
| | | ); |
| | | ctx.connData = { |
| | | userId: isAdmin ? data.userId : data.id, |
| | | isAdmin, |
| | | }; |
| | | } catch (error) { |
| | | ctx.emit('sys', '连接失败'); |
| | | return; |
| | | } |
| | | return await next(); |
| | | }; |
| | | } |
| | | } |
| New file |
| | |
| | | import { ModuleConfig } from '@cool-midway/core'; |
| | | |
| | | /** |
| | | * 模块配置 |
| | | */ |
| | | export default () => { |
| | | return { |
| | | // 模块名称 |
| | | name: '商品模块', |
| | | // 模块描述 |
| | | description: '商品、分类管理', |
| | | // 中间件,只对本模块有效 |
| | | middlewares: [], |
| | | // 中间件,全局有效 |
| | | globalMiddlewares: [], |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 0, |
| | | } as ModuleConfig; |
| | | }; |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { GoodsCommentEntity } from '../../entity/comment'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | import { GoodsInfoEntity } from '../../entity/info'; |
| | | |
| | | /** |
| | | * 商品评论 |
| | | */ |
| | | @CoolController({ |
| | | api: ['page'], |
| | | entity: GoodsCommentEntity, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.content', 'b.nickName'], |
| | | fieldEq: ['a.goodsId'], |
| | | select: ['a.*', 'b.nickName', 'b.avatarUrl', 'c.title', 'c.mainPic'], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id', |
| | | }, |
| | | { |
| | | entity: GoodsInfoEntity, |
| | | alias: 'c', |
| | | condition: 'a.goodsId = c.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AdminGoodsCommentController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { GoodsInfoEntity } from '../../entity/info'; |
| | | import { GoodsInfoService } from '../../service/info'; |
| | | import { GoodsTypeEntity } from '../../entity/type'; |
| | | |
| | | /** |
| | | * 商品信息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: GoodsInfoEntity, |
| | | service: GoodsInfoService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.title', 'a.subTitle'], |
| | | select: ['a.*', 'b.name as typeName'], |
| | | fieldEq: ['a.status', 'a.typeId'], |
| | | addOrderBy: { |
| | | sortNum: 'desc', |
| | | }, |
| | | join: [ |
| | | { |
| | | entity: GoodsTypeEntity, |
| | | alias: 'b', |
| | | condition: 'a.typeId = b.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AdminGoodsInfoController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { GoodsSearchKeywordEntity } from '../../entity/searchKeyword'; |
| | | |
| | | /** |
| | | * 商品信息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: GoodsSearchKeywordEntity, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['name'], |
| | | }, |
| | | }) |
| | | export class AdminGoodsSearchKeywordController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { GoodsSpecEntity } from '../../entity/spec'; |
| | | |
| | | /** |
| | | * 分类 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: GoodsSpecEntity, |
| | | listQueryOp: { |
| | | fieldEq: ['goodsId'], |
| | | }, |
| | | }) |
| | | export class AdminGoodsSpecController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { GoodsTypeEntity } from '../../entity/type'; |
| | | import { GoodsTypeService } from '../../service/type'; |
| | | |
| | | /** |
| | | * 分类 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: GoodsTypeEntity, |
| | | service: GoodsTypeService, |
| | | listQueryOp: { |
| | | keyWordLikeFields: ['name'], |
| | | }, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['name'], |
| | | fieldEq: ['parentId'], |
| | | }, |
| | | }) |
| | | export class AdminGoodsTypeController extends BaseController {} |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | TagTypes, |
| | | CoolUrlTag, |
| | | } from '@cool-midway/core'; |
| | | import { GoodsCommentEntity } from '../../entity/comment'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | import { Body, Inject, Post } from '@midwayjs/core'; |
| | | import { GoodsCommentService } from '../../service/comment'; |
| | | |
| | | /** |
| | | * 商品评论 |
| | | */ |
| | | @CoolUrlTag({ |
| | | key: TagTypes.IGNORE_TOKEN, |
| | | value: ['page'], |
| | | }) |
| | | @CoolController({ |
| | | api: ['page'], |
| | | entity: GoodsCommentEntity, |
| | | insertParam: ctx => { |
| | | return { |
| | | userId: ctx.user.id, |
| | | }; |
| | | }, |
| | | pageQueryOp: { |
| | | fieldEq: ['a.goodsId', 'a.orderId'], |
| | | select: ['a.*', 'b.nickName', 'b.avatarUrl'], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AppGoodsCommentController extends BaseController { |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Inject() |
| | | goodsCommentService: GoodsCommentService; |
| | | |
| | | @Post('/submit', { summary: '提交评论' }) |
| | | async submit( |
| | | @Body('data') data: GoodsCommentEntity, |
| | | @Body('orderId') orderId: number |
| | | ) { |
| | | return this.ok( |
| | | await this.goodsCommentService.submit(this.ctx.user.id, orderId, data) |
| | | ); |
| | | } |
| | | } |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | CoolUrlTag, |
| | | TagTypes, |
| | | } from '@cool-midway/core'; |
| | | import { GoodsInfoEntity } from '../../entity/info'; |
| | | import { GoodsInfoService } from '../../service/info'; |
| | | import { GoodsTypeEntity } from '../../entity/type'; |
| | | |
| | | /** |
| | | * 商品信息 |
| | | */ |
| | | @CoolUrlTag({ |
| | | key: TagTypes.IGNORE_TOKEN, |
| | | value: ['page', 'info'], |
| | | }) |
| | | @CoolController({ |
| | | api: ['page', 'info'], |
| | | entity: GoodsInfoEntity, |
| | | service: GoodsInfoService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['title'], |
| | | fieldEq: ['a.typeId'], |
| | | select: ['a.*', 'b.name as typeName'], |
| | | where: ctx => { |
| | | const { minPrice, maxPrice } = ctx.request.body; |
| | | return [ |
| | | // 过滤掉已下架商品 |
| | | ['a.status = :status', { status: 1 }], |
| | | // 价格区间 |
| | | ['a.price >= :minPrice', { minPrice }, minPrice], |
| | | ['a.price <= :maxPrice', { maxPrice }, maxPrice], |
| | | ]; |
| | | }, |
| | | join: [ |
| | | { |
| | | entity: GoodsTypeEntity, |
| | | alias: 'b', |
| | | condition: 'a.typeId = b.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AppGoodsInfoController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { GoodsSearchKeywordEntity } from '../../entity/searchKeyword'; |
| | | |
| | | /** |
| | | * 商品信息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: GoodsSearchKeywordEntity, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['name'], |
| | | }, |
| | | }) |
| | | export class AppGoodsSearchKeywordController extends BaseController {} |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | TagTypes, |
| | | CoolUrlTag, |
| | | } from '@cool-midway/core'; |
| | | import { GoodsSpecEntity } from '../../entity/spec'; |
| | | |
| | | /** |
| | | * 分类 |
| | | */ |
| | | @CoolUrlTag({ |
| | | key: TagTypes.IGNORE_TOKEN, |
| | | value: ['list'], |
| | | }) |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: GoodsSpecEntity, |
| | | listQueryOp: { |
| | | fieldEq: ['goodsId'], |
| | | }, |
| | | }) |
| | | export class AppGoodsSpecController extends BaseController {} |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | TagTypes, |
| | | CoolUrlTag, |
| | | } from '@cool-midway/core'; |
| | | import { GoodsTypeEntity } from '../../entity/type'; |
| | | |
| | | /** |
| | | * 分类 |
| | | */ |
| | | @CoolUrlTag({ |
| | | key: TagTypes.IGNORE_TOKEN, |
| | | value: ['list'], |
| | | }) |
| | | @CoolController({ |
| | | api: ['list'], |
| | | entity: GoodsTypeEntity, |
| | | listQueryOp: { |
| | | keyWordLikeFields: ['a.name'], |
| | | addOrderBy: { |
| | | sortNum: 'desc', |
| | | }, |
| | | where: () => { |
| | | return [['a.status =:status', { status: 1 }]]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppGoodsTypeController extends BaseController {} |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 商品信息 |
| | | */ |
| | | @Entity('goods_comment') |
| | | export class GoodsCommentEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Index() |
| | | @Column({ comment: '商品ID' }) |
| | | goodsId: number; |
| | | |
| | | @Index() |
| | | @Column({ comment: '订单ID' }) |
| | | orderId: number; |
| | | |
| | | @Column({ comment: '内容' }) |
| | | content: string; |
| | | |
| | | @Column({ comment: '星数', default: 5 }) |
| | | starCount: number; |
| | | |
| | | @Column({ comment: '图片', type: 'json', nullable: true }) |
| | | pics: string[]; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | import { GoodsSpecEntity } from './spec'; |
| | | |
| | | /** |
| | | * 商品信息 |
| | | */ |
| | | @Entity('goods_info') |
| | | export class GoodsInfoEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '类型ID' }) |
| | | typeId: number; |
| | | |
| | | @Index() |
| | | @Column({ comment: '标题' }) |
| | | title: string; |
| | | |
| | | @Column({ comment: '副标题', nullable: true }) |
| | | subTitle: string; |
| | | |
| | | @Column({ comment: '主图' }) |
| | | mainPic: string; |
| | | |
| | | @Column({ comment: '图片', type: 'json', nullable: true }) |
| | | pics: string[]; |
| | | |
| | | @Column({ |
| | | comment: '价格', |
| | | type: 'decimal', |
| | | precision: 12, |
| | | scale: 2, |
| | | }) |
| | | price: number; |
| | | |
| | | @Column({ comment: '已售', default: 0 }) |
| | | sold: number; |
| | | |
| | | @Column({ comment: '详情', type: 'text', nullable: true }) |
| | | content: string; |
| | | |
| | | @Index() |
| | | @Column({ comment: '状态 0-下架 1-上架', default: 0 }) |
| | | status: number; |
| | | |
| | | @Column({ comment: '排序', default: 0, nullable: true }) |
| | | sortNum: number; |
| | | |
| | | // 规格,非表字段 |
| | | specs: GoodsSpecEntity[]; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 搜索关键词 |
| | | */ |
| | | @Entity('goods_search_keyword') |
| | | export class GoodsSearchKeywordEntity extends BaseEntity { |
| | | @Column({ comment: '名称' }) |
| | | name: string; |
| | | |
| | | @Column({ comment: '排序', default: 0, nullable: true }) |
| | | sortNum: number; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 规格 |
| | | */ |
| | | @Entity('goods_spec') |
| | | export class GoodsSpecEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '商品ID' }) |
| | | goodsId: number; |
| | | |
| | | @Column({ comment: '名称' }) |
| | | name: string; |
| | | |
| | | @Column({ |
| | | comment: '价格', |
| | | type: 'decimal', |
| | | precision: 12, |
| | | scale: 2, |
| | | }) |
| | | price: number; |
| | | |
| | | @Column({ comment: '库存', default: 0 }) |
| | | stock: number; |
| | | |
| | | @Column({ comment: '排序', default: 0, nullable: true }) |
| | | sortNum: number; |
| | | |
| | | @Column({ comment: '图片', type: 'json', nullable: true }) |
| | | images: string[]; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 规格 |
| | | */ |
| | | @Entity('goods_type') |
| | | export class GoodsTypeEntity extends BaseEntity { |
| | | @Column({ comment: '名称' }) |
| | | name: string; |
| | | |
| | | @Index() |
| | | @Column({ comment: '父ID', nullable: true }) |
| | | parentId: number; |
| | | |
| | | @Column({ comment: '排序', default: 0, nullable: true }) |
| | | sortNum: number; |
| | | |
| | | @Column({ comment: '图片', nullable: true }) |
| | | pic: string; |
| | | |
| | | @Column({ comment: '状态 0-禁用 1-启用', nullable: true, default: 1 }) |
| | | status: number; |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService, CoolCommException } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Equal, Repository } from 'typeorm'; |
| | | import { GoodsCommentEntity } from '../entity/comment'; |
| | | import { OrderInfoService } from '../../order/service/info'; |
| | | import { OrderGoodsEntity } from '../../order/entity/goods'; |
| | | |
| | | /** |
| | | * 商品评论 |
| | | */ |
| | | @Provide() |
| | | export class GoodsCommentService extends BaseService { |
| | | @InjectEntityModel(GoodsCommentEntity) |
| | | goodsCommentEntity: Repository<GoodsCommentEntity>; |
| | | |
| | | @InjectEntityModel(OrderGoodsEntity) |
| | | orderGoodsEntity: Repository<OrderGoodsEntity>; |
| | | |
| | | @Inject() |
| | | orderInfoService: OrderInfoService; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.goodsCommentEntity); |
| | | } |
| | | |
| | | /** |
| | | * 提交评论 |
| | | * @param userId |
| | | * @param orderId |
| | | * @param comment |
| | | */ |
| | | async submit(userId: number, orderId: number, comment: GoodsCommentEntity) { |
| | | const order = await this.orderInfoService.info(orderId); |
| | | if (order && order.userId != userId) { |
| | | throw new CoolCommException('无权限'); |
| | | } |
| | | if (![3, 4].includes(order.status)) { |
| | | throw new CoolCommException('不是可评价的订单状态'); |
| | | } |
| | | const goods = await this.orderGoodsEntity.findOneBy({ |
| | | goodsId: Equal(comment.goodsId), |
| | | orderId: Equal(orderId), |
| | | }); |
| | | if (!goods) { |
| | | throw new CoolCommException('商品不存在'); |
| | | } |
| | | if (goods.isComment) { |
| | | throw new CoolCommException('已评价过,不能重复评价'); |
| | | } |
| | | // 更新订单商品状态 |
| | | await this.orderGoodsEntity.update(goods.id, { isComment: 1 }); |
| | | // 订单状态改为交易完成 |
| | | if (order.status == 3) { |
| | | await this.orderInfoService.changeStatus(orderId, 4); |
| | | } |
| | | comment.userId = userId; |
| | | await this.add(comment); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { GoodsInfoEntity } from '../entity/info'; |
| | | import { GoodsSpecService } from './spec'; |
| | | |
| | | /** |
| | | * 商品信息 |
| | | */ |
| | | @Provide() |
| | | export class GoodsInfoService extends BaseService { |
| | | @InjectEntityModel(GoodsInfoEntity) |
| | | goodsInfoEntity: Repository<GoodsInfoEntity>; |
| | | |
| | | @Inject() |
| | | goodsSpecService: GoodsSpecService; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.goodsInfoEntity); |
| | | } |
| | | |
| | | /** |
| | | * 修改之后 |
| | | * @param data |
| | | * @param type |
| | | */ |
| | | async modifyAfter(data: any, type: 'add' | 'update' | 'delete') { |
| | | if ((type == 'add' || type == 'update') && data.specs) { |
| | | // 保存规格 |
| | | await this.goodsSpecService.save(data.id, data.specs); |
| | | } |
| | | if (type == 'delete') { |
| | | // 删除规格 |
| | | await this.goodsSpecService.removeByGoodsId([data.id]); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 详情 |
| | | * @param id |
| | | * @param infoIgnoreProperty |
| | | * @returns |
| | | */ |
| | | async info(id: any, infoIgnoreProperty?: string[]) { |
| | | if (!id) return; |
| | | const info = await super.info(id, infoIgnoreProperty); |
| | | if (info) { |
| | | // 获取规格 |
| | | info.specs = await this.goodsSpecService.getByGoodsId(info.id); |
| | | } |
| | | return info; |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Equal, In, Repository } from 'typeorm'; |
| | | import { GoodsSpecEntity } from '../entity/spec'; |
| | | |
| | | /** |
| | | * 规格 |
| | | */ |
| | | @Provide() |
| | | export class GoodsSpecService extends BaseService { |
| | | @InjectEntityModel(GoodsSpecEntity) |
| | | goodsSpecEntity: Repository<GoodsSpecEntity>; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.goodsSpecEntity); |
| | | } |
| | | |
| | | /** |
| | | * 保持规格 |
| | | * @param goodsId |
| | | * @param specs |
| | | */ |
| | | async save(goodsId: number, specs: GoodsSpecEntity[]) { |
| | | // 先删除原来的规格 |
| | | await this.goodsSpecEntity.delete({ goodsId }); |
| | | // 保存新的规格 |
| | | await this.goodsSpecEntity.save( |
| | | specs.map(item => { |
| | | item.goodsId = goodsId; |
| | | return item; |
| | | }) |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 通过商品ID获取规格 |
| | | * @param goodsId |
| | | */ |
| | | async removeByGoodsId(goodsIds: number[]) { |
| | | await this.goodsSpecEntity.delete({ goodsId: In(goodsIds) }); |
| | | } |
| | | |
| | | /** |
| | | * 通过商品ID获取规格 |
| | | * @param goodsId |
| | | * @returns |
| | | */ |
| | | async getByGoodsId(goodsId: number) { |
| | | return await this.goodsSpecEntity.findBy({ goodsId: Equal(goodsId) }); |
| | | } |
| | | |
| | | /** |
| | | * 更新库存 |
| | | * @param specId 规格ID |
| | | * @param count 数量 |
| | | */ |
| | | async updateStock(specId: number, count: number) { |
| | | await this.goodsSpecEntity.increment({ id: specId }, 'stock', count); |
| | | |
| | | // 更新后检查库存,如果小于0则设置为0 |
| | | const spec = await this.goodsSpecEntity.findOneBy({ id: Equal(specId) }); |
| | | if (spec && spec.stock < 0) { |
| | | await this.goodsSpecEntity.update(spec.id, { |
| | | stock: 0, |
| | | }); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { In, Repository } from 'typeorm'; |
| | | import { GoodsTypeEntity } from '../entity/type'; |
| | | |
| | | /** |
| | | * 商品类型 |
| | | */ |
| | | @Provide() |
| | | export class GoodsTypeService extends BaseService { |
| | | @InjectEntityModel(GoodsTypeEntity) |
| | | goodsTypeEntity: Repository<GoodsTypeEntity>; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.goodsTypeEntity); |
| | | } |
| | | |
| | | /** |
| | | * 删除 |
| | | * @param ids |
| | | */ |
| | | async delete(ids: any) { |
| | | await super.delete(ids); |
| | | // 删除子集 |
| | | await this.goodsTypeEntity.delete({ parentId: In(ids) }); |
| | | } |
| | | } |
| New file |
| | |
| | | import { ModuleConfig } from '@cool-midway/core'; |
| | | |
| | | /** |
| | | * 模块配置 |
| | | */ |
| | | export default () => { |
| | | return { |
| | | // 模块名称 |
| | | name: '信息模块', |
| | | // 模块描述 |
| | | description: '轮播图等', |
| | | // 中间件,只对本模块有效 |
| | | middlewares: [], |
| | | // 中间件,全局有效 |
| | | globalMiddlewares: [], |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 0, |
| | | } as ModuleConfig; |
| | | }; |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { InfoBannerEntity } from '../../entity/banner'; |
| | | |
| | | /** |
| | | * 轮播图 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: InfoBannerEntity, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.title'], |
| | | fieldEq: ['a.status'], |
| | | }, |
| | | }) |
| | | export class AdminInfoBannerController extends BaseController {} |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | CoolUrlTag, |
| | | TagTypes, |
| | | } from '@cool-midway/core'; |
| | | import { InfoBannerEntity } from '../../entity/banner'; |
| | | |
| | | /** |
| | | * 轮播图 |
| | | */ |
| | | @CoolUrlTag({ |
| | | key: TagTypes.IGNORE_TOKEN, |
| | | value: ['list'], |
| | | }) |
| | | @CoolController({ |
| | | api: ['list'], |
| | | entity: InfoBannerEntity, |
| | | listQueryOp: { |
| | | keyWordLikeFields: ['a.title'], |
| | | fieldEq: ['a.status'], |
| | | where: () => { |
| | | return [['a.status =:status', { status: 1 }]]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppInfoBannerController extends BaseController {} |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity } from 'typeorm'; |
| | | |
| | | /** |
| | | * 轮播图 |
| | | */ |
| | | @Entity('info_banner') |
| | | export class InfoBannerEntity extends BaseEntity { |
| | | @Column({ comment: '描述', nullable: true }) |
| | | description: string; |
| | | |
| | | @Column({ comment: '跳转路径', nullable: true }) |
| | | path: string; |
| | | |
| | | @Column({ comment: '图片' }) |
| | | pic: string; |
| | | |
| | | @Column({ comment: '排序', default: 0, nullable: true }) |
| | | sortNum: number; |
| | | |
| | | @Column({ comment: '状态 1:启用 2:禁用', default: 1 }) |
| | | status: number; |
| | | } |
| New file |
| | |
| | | import { ModuleConfig } from '@cool-midway/core'; |
| | | |
| | | /** |
| | | * 模块配置 |
| | | */ |
| | | export default () => { |
| | | return { |
| | | // 模块名称 |
| | | name: '营销模块', |
| | | // 模块描述 |
| | | description: '优惠券', |
| | | // 中间件,只对本模块有效 |
| | | middlewares: [], |
| | | // 中间件,全局有效 |
| | | globalMiddlewares: [], |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 0, |
| | | } as ModuleConfig; |
| | | }; |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { MarketCouponInfoEntity } from '../../../entity/coupon/info'; |
| | | import { MarketCouponInfoService } from '../../../service/coupon/info'; |
| | | |
| | | /** |
| | | * 优惠券信息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: MarketCouponInfoEntity, |
| | | service: MarketCouponInfoService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.title'], |
| | | fieldEq: ['a.status'], |
| | | }, |
| | | }) |
| | | export class AdminMarketCouponInfoController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { MarketCouponUserEntity } from '../../../entity/coupon/user'; |
| | | import { MarketCouponUserService } from '../../../service/coupon/user'; |
| | | import { UserInfoEntity } from '../../../../user/entity/info'; |
| | | |
| | | /** |
| | | * 优惠券用户 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: MarketCouponUserEntity, |
| | | service: MarketCouponUserService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.title', 'b.nickName'], |
| | | fieldEq: ['a.status', 'a.couponId', 'a.userId'], |
| | | select: ['a.*', 'b.nickName', 'b.avatarUrl'], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AdminMarketCouponUserController extends BaseController {} |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | QueryOp, |
| | | CoolUrlTag, |
| | | TagTypes, |
| | | } from '@cool-midway/core'; |
| | | import { MarketCouponInfoEntity } from '../../../entity/coupon/info'; |
| | | import { MarketCouponInfoService } from '../../../service/coupon/info'; |
| | | import * as moment from 'moment'; |
| | | import { MarketCouponUserEntity } from '../../../entity/coupon/user'; |
| | | |
| | | /** |
| | | * 优惠券信息 |
| | | */ |
| | | @CoolUrlTag({ |
| | | key: TagTypes.IGNORE_TOKEN, |
| | | value: ['page'], |
| | | }) |
| | | @CoolController({ |
| | | api: ['page'], |
| | | entity: MarketCouponInfoEntity, |
| | | service: MarketCouponInfoService, |
| | | // 获得ctx对象 |
| | | pageQueryOp: ctx => { |
| | | return new Promise<QueryOp>(res => { |
| | | res({ |
| | | keyWordLikeFields: ['a.title'], |
| | | fieldEq: ['a.status'], |
| | | select: ['a.*', 'b.userId'], |
| | | where: () => { |
| | | return [ |
| | | // 只返回启用的优惠券 |
| | | ['a.status =:status', { status: 1 }], |
| | | // 只返回已开始和未结束的优惠券 |
| | | ['a.startTime <=:startTime', { startTime: moment().toDate() }], |
| | | ['a.endTime >=:endTime', { endTime: moment().toDate() }], |
| | | ]; |
| | | }, |
| | | join: [ |
| | | { |
| | | entity: MarketCouponUserEntity, |
| | | alias: 'b', |
| | | condition: 'a.id = b.couponId and b.userId =' + (ctx?.user?.id ?? 0), |
| | | }, |
| | | ], |
| | | }); |
| | | }); |
| | | }, |
| | | }) |
| | | export class AppMarketCouponInfoController extends BaseController {} |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { MarketCouponUserEntity } from '../../../entity/coupon/user'; |
| | | import { MarketCouponUserService } from '../../../service/coupon/user'; |
| | | import { MarketCouponInfoService } from '../../../service/coupon/info'; |
| | | import { Inject, Post, Body } from '@midwayjs/core'; |
| | | import { MarketCouponInfoEntity } from '../../../entity/coupon/info'; |
| | | |
| | | /** |
| | | * 优惠券用户 |
| | | */ |
| | | @CoolController({ |
| | | api: ['add', 'delete', 'update', 'info', 'list', 'page'], |
| | | entity: MarketCouponUserEntity, |
| | | service: MarketCouponUserService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.title'], |
| | | fieldEq: ['a.status'], |
| | | select: ['b.*', 'a.status as useStatus'], |
| | | where: ctx => { |
| | | const userId = ctx.user.id; |
| | | return [ |
| | | // 只返回当前用户的优惠券 |
| | | ['a.userId =:userId', { userId }], |
| | | ]; |
| | | }, |
| | | join: [ |
| | | { |
| | | entity: MarketCouponInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.couponId = b.id', |
| | | }, |
| | | ], |
| | | }, |
| | | }) |
| | | export class AppMarketCouponUserController extends BaseController { |
| | | @Inject() |
| | | marketCouponInfoService: MarketCouponInfoService; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Post('/receive', { summary: '领取优惠券' }) |
| | | async receive(@Body('couponId') couponId: number) { |
| | | return this.ok( |
| | | await this.marketCouponInfoService.receive(couponId, this.ctx.user.id) |
| | | ); |
| | | } |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity } from 'typeorm'; |
| | | |
| | | /** |
| | | * 优惠券信息 |
| | | */ |
| | | @Entity('market_coupon_info') |
| | | export class MarketCouponInfoEntity extends BaseEntity { |
| | | @Column({ comment: '标题' }) |
| | | title: string; |
| | | |
| | | @Column({ comment: '描述' }) |
| | | description: string; |
| | | |
| | | @Column({ comment: '类型 0-满减', default: 0 }) |
| | | type: number; |
| | | |
| | | @Column({ comment: '金额', type: 'decimal', precision: 12, scale: 2 }) |
| | | amount: number; |
| | | |
| | | @Column({ comment: '数量' }) |
| | | num: number; |
| | | |
| | | @Column({ comment: '已领取' }) |
| | | receivedNum: number; |
| | | |
| | | @Column({ comment: '开始时间' }) |
| | | startTime: Date; |
| | | |
| | | @Column({ comment: '结束时间' }) |
| | | endTime: Date; |
| | | |
| | | @Column({ comment: '状态 0-禁用 1-启用', default: 0 }) |
| | | status: number; |
| | | |
| | | @Column({ comment: '条件', type: 'json' }) |
| | | condition: { |
| | | // 满多少金额 |
| | | fullAmount: number; |
| | | }; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | |
| | | /** |
| | | * 优惠券用户 |
| | | */ |
| | | @Entity('market_coupon_user') |
| | | export class MarketCouponUserEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Index() |
| | | @Column({ comment: '优惠券ID' }) |
| | | couponId: number; |
| | | |
| | | @Column({ comment: '状态 0-未使用 1-已使用', default: 0 }) |
| | | status: number; |
| | | |
| | | @Column({ comment: '使用时间', type: 'datetime', nullable: true }) |
| | | useTime: Date; |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService, CoolCommException } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { MarketCouponInfoEntity } from '../../entity/coupon/info'; |
| | | import { MarketCouponUserService } from './user'; |
| | | import { OrderInfoEntity } from '../../../order/entity/info'; |
| | | import * as moment from 'moment'; |
| | | |
| | | /** |
| | | * 优惠券信息 |
| | | */ |
| | | @Provide() |
| | | export class MarketCouponInfoService extends BaseService { |
| | | @InjectEntityModel(MarketCouponInfoEntity) |
| | | marketCouponInfoEntity: Repository<MarketCouponInfoEntity>; |
| | | |
| | | @Inject() |
| | | marketCouponUserService: MarketCouponUserService; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.marketCouponInfoEntity); |
| | | } |
| | | |
| | | /** |
| | | * 检查优惠券状态 |
| | | * @param couponId |
| | | */ |
| | | async check(couponId: number) { |
| | | const couponInfo: MarketCouponInfoEntity = await super.info(couponId); |
| | | if (!couponInfo) { |
| | | throw new CoolCommException('优惠券不存在'); |
| | | } |
| | | // 判断数量 |
| | | if (couponInfo.receivedNum >= couponInfo.num) { |
| | | throw new CoolCommException('优惠券已领完'); |
| | | } |
| | | // 判断时间 |
| | | if ( |
| | | moment().isBefore(couponInfo.startTime) || |
| | | moment().isAfter(couponInfo.endTime) |
| | | ) { |
| | | throw new CoolCommException('优惠券未开始或已结束'); |
| | | } |
| | | // 判断状态 |
| | | if (couponInfo.status !== 1) { |
| | | throw new CoolCommException('优惠券未启用'); |
| | | } |
| | | return couponInfo; |
| | | } |
| | | |
| | | /** |
| | | * 领取优惠券 |
| | | * @param couponId |
| | | * @param userId |
| | | */ |
| | | async receive(couponId: number, userId: number) { |
| | | // 检查优惠券 |
| | | await this.check(couponId); |
| | | // 检查用户是否已领取 |
| | | if (await this.marketCouponUserService.checkExist(couponId, userId)) { |
| | | throw new CoolCommException('已领取过该优惠券'); |
| | | } |
| | | // 保存 |
| | | await this.marketCouponUserService.save(couponId, userId); |
| | | // 增加领取数量 |
| | | await this.marketCouponInfoEntity.increment( |
| | | { id: couponId }, |
| | | 'receivedNum', |
| | | 1 |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 检查优惠券是否满足条件,满足的话使用并设置和返回订单优惠金额 |
| | | * @param couponId |
| | | * @param userId |
| | | * @param order |
| | | */ |
| | | async checkAndUse(couponId: number, userId: number, order: OrderInfoEntity) { |
| | | // 判断优惠券 |
| | | const couponInfo = await this.check(couponId); |
| | | // 判断用户 |
| | | await this.marketCouponUserService.check(couponId, userId); |
| | | // 判断条件 |
| | | if (couponInfo.type == 0) { |
| | | // 满减 |
| | | if (order.price < couponInfo.condition.fullAmount) { |
| | | throw new CoolCommException('未满足优惠券使用条件'); |
| | | } |
| | | // 设置订单的优惠金额 |
| | | order.discountPrice = couponInfo.amount; |
| | | // 设置优惠来源 |
| | | order.discountSource = { |
| | | type: 0, |
| | | objectId: couponId, |
| | | info: couponInfo, |
| | | }; |
| | | // 设置商品的优惠金额 |
| | | if (order.price > 0) { |
| | | order.goodsList.forEach(goods => { |
| | | // 优惠金额 = 商品价格 / 订单总价 * 优惠金额 保留两位小数 |
| | | goods.discountPrice = Number( |
| | | ((goods.price / order.price) * couponInfo.amount).toFixed(2) |
| | | ); |
| | | }); |
| | | } |
| | | // 使用优惠券 |
| | | await this.marketCouponUserService.use(couponId, userId); |
| | | return couponInfo.amount; |
| | | } |
| | | return 0; |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Provide } from '@midwayjs/core'; |
| | | import { BaseService, CoolCommException } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Equal, Repository } from 'typeorm'; |
| | | import { MarketCouponUserEntity } from '../../entity/coupon/user'; |
| | | |
| | | /** |
| | | * 优惠券用户 |
| | | */ |
| | | @Provide() |
| | | export class MarketCouponUserService extends BaseService { |
| | | @InjectEntityModel(MarketCouponUserEntity) |
| | | marketCouponUserEntity: Repository<MarketCouponUserEntity>; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.marketCouponUserEntity); |
| | | } |
| | | |
| | | /** |
| | | * 保存 |
| | | * @param idcouponId |
| | | * @param userId |
| | | */ |
| | | async save(couponId: number, userId: number) { |
| | | const couponUser = new MarketCouponUserEntity(); |
| | | couponUser.userId = userId; |
| | | couponUser.couponId = couponId; |
| | | await this.marketCouponUserEntity.save(couponUser); |
| | | } |
| | | |
| | | /** |
| | | * 检查优惠券是否可用 |
| | | * @param couponId |
| | | * @param userId |
| | | */ |
| | | async check(couponId: number, userId: number) { |
| | | const info = await this.marketCouponUserEntity.findOneBy({ |
| | | couponId: Equal(couponId), |
| | | userId: Equal(userId), |
| | | status: 0, |
| | | }); |
| | | if (!info) { |
| | | throw new CoolCommException('优惠券未领取或已使用'); |
| | | } |
| | | return info; |
| | | } |
| | | |
| | | /** |
| | | * 检查优惠券是否存在 |
| | | * @param couponId |
| | | * @param userId |
| | | */ |
| | | async checkExist(couponId: number, userId: number) { |
| | | const info = await this.marketCouponUserEntity.findOneBy({ |
| | | couponId: Equal(couponId), |
| | | userId: Equal(userId), |
| | | }); |
| | | return !!info; |
| | | } |
| | | |
| | | /** |
| | | * 使用优惠券 |
| | | * @param couponId |
| | | * @param userId |
| | | */ |
| | | async use(couponId: number, userId: number) { |
| | | await this.marketCouponUserEntity.update( |
| | | { couponId: Equal(couponId), userId: Equal(userId) }, |
| | | { status: 1, useTime: new Date() } |
| | | ); |
| | | } |
| | | } |
| New file |
| | |
| | | import { ModuleConfig } from '@cool-midway/core'; |
| | | |
| | | /** |
| | | * 模块配置 |
| | | */ |
| | | export default () => { |
| | | return { |
| | | // 模块名称 |
| | | name: '订单管理模块', |
| | | // 模块描述 |
| | | description: '订单、退款、支付等', |
| | | // 中间件,只对本模块有效 |
| | | middlewares: [], |
| | | // 中间件,全局有效 |
| | | globalMiddlewares: [], |
| | | // 模块加载顺序,默认为0,值越大越优先加载 |
| | | order: 0, |
| | | } as ModuleConfig; |
| | | }; |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { OrderInfoEntity } from '../../entity/info'; |
| | | import { OrderInfoService } from '../../service/info'; |
| | | import { UserInfoEntity } from '../../../user/entity/info'; |
| | | import { Body, Get, Inject, Post, Query } from '@midwayjs/core'; |
| | | |
| | | /** |
| | | * 订单信息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['delete', 'update', 'info', 'list', 'page'], |
| | | entity: OrderInfoEntity, |
| | | service: OrderInfoService, |
| | | pageQueryOp: { |
| | | keyWordLikeFields: ['a.title', 'a.orderNum', 'b.nickName'], |
| | | select: ['a.*', 'b.nickName', 'b.avatarUrl'], |
| | | fieldEq: ['a.status', 'a.payType'], |
| | | join: [ |
| | | { |
| | | entity: UserInfoEntity, |
| | | alias: 'b', |
| | | condition: 'a.userId = b.id', |
| | | }, |
| | | ], |
| | | where: ctx => { |
| | | const { startTime, endTime, refundApplyStartTime, refundApplyEndTime } = |
| | | ctx.request.body; |
| | | return [ |
| | | // 过滤创建时间 |
| | | ['a.createTime >= :startTime', { startTime }, startTime], |
| | | ['a.createTime <= :endTime', { endTime }, endTime], |
| | | // 过滤退款时间 |
| | | [ |
| | | 'a.refundApplyTime >= :refundApplyStartTime', |
| | | { refundApplyStartTime }, |
| | | refundApplyStartTime, |
| | | ], |
| | | [ |
| | | 'a.refundApplyTime <= :refundApplyEndTime', |
| | | { refundApplyEndTime }, |
| | | refundApplyEndTime, |
| | | ], |
| | | ]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AdminOrderInfoController extends BaseController { |
| | | @Inject() |
| | | orderInfoService: OrderInfoService; |
| | | |
| | | @Post('/refundHandle', { summary: '退款处理' }) |
| | | async refundHandle( |
| | | @Body('orderId') orderId: number, |
| | | // 0-拒绝 1-同意 |
| | | @Body('action') action: number, |
| | | // 拒绝原因 |
| | | @Body('refuseReason') refuseReason: string, |
| | | // 退款金额 |
| | | @Body('amount') amount: number |
| | | ) { |
| | | await this.orderInfoService.refundHandle( |
| | | orderId, |
| | | action, |
| | | refuseReason, |
| | | amount |
| | | ); |
| | | return this.ok(); |
| | | } |
| | | |
| | | @Get('/logistics', { summary: '物流信息' }) |
| | | async logistics(@Query('orderId') orderId: number) { |
| | | return this.ok(await this.orderInfoService.logistics(orderId)); |
| | | } |
| | | |
| | | @Post('/deliver', { summary: '发货' }) |
| | | async deliver( |
| | | @Body('orderId') orderId: number, |
| | | @Body('logistics') |
| | | logistics: { |
| | | // 物流公司 |
| | | company: string; |
| | | // 物流单号 |
| | | num: string; |
| | | } |
| | | ) { |
| | | return this.ok(await this.orderInfoService.deliver(orderId, logistics)); |
| | | } |
| | | } |
| New file |
| | |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { OrderInfoEntity } from '../../entity/info'; |
| | | import { OrderInfoService } from '../../service/info'; |
| | | import { Body, Get, Inject, Post, Query } from '@midwayjs/core'; |
| | | import { OrderGoodsEntity } from '../../entity/goods'; |
| | | |
| | | /** |
| | | * 订单信息 |
| | | */ |
| | | @CoolController({ |
| | | api: ['info', 'page', 'update'], |
| | | entity: OrderInfoEntity, |
| | | service: OrderInfoService, |
| | | pageQueryOp: { |
| | | fieldEq: ['status'], |
| | | where: ctx => { |
| | | return [ |
| | | // 过滤用户ID |
| | | ['a.userId = :userId', { userId: ctx.user.id }], |
| | | ]; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppOrderInfoController extends BaseController { |
| | | @Inject() |
| | | orderInfoService: OrderInfoService; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @Post('/create', { summary: '创建订单' }) |
| | | async create( |
| | | @Body('data') |
| | | data: { |
| | | goodsList: OrderGoodsEntity[]; |
| | | addressId: number; |
| | | remark: string; |
| | | title: string; |
| | | couponId: number; |
| | | } |
| | | ) { |
| | | return this.ok( |
| | | await this.orderInfoService.create({ |
| | | ...data, |
| | | userId: this.ctx.user.id, |
| | | }) |
| | | ); |
| | | } |
| | | |
| | | @Post('/cancel', { summary: '取消订单' }) |
| | | async cancel( |
| | | @Body('orderId') orderId: number, |
| | | @Body('remark') remark: string |
| | | ) { |
| | | return this.ok(await this.orderInfoService.close(orderId, remark)); |
| | | } |
| | | |
| | | @Post('/refund', { summary: '退款' }) |
| | | async refund( |
| | | @Body('orderId') orderId: number, |
| | | @Body('reason') reason: string |
| | | ) { |
| | | return this.ok( |
| | | await this.orderInfoService.refund(this.ctx.user.id, orderId, reason) |
| | | ); |
| | | } |
| | | |
| | | @Get('/confirm', { summary: '确认收货' }) |
| | | async confirm(@Query('orderId') orderId: number) { |
| | | return this.ok( |
| | | await this.orderInfoService.confirm(orderId, this.ctx.user.id) |
| | | ); |
| | | } |
| | | |
| | | @Get('/logistics', { summary: '物流信息' }) |
| | | async logistics(@Query('orderId') orderId: number) { |
| | | return this.ok( |
| | | await this.orderInfoService.logistics(orderId, this.ctx.user.id) |
| | | ); |
| | | } |
| | | |
| | | @Get('/userCount', { summary: '用户订单统计' }) |
| | | async userCount() { |
| | | return this.ok(await this.orderInfoService.userCount(this.ctx.user.id)); |
| | | } |
| | | } |
| New file |
| | |
| | | import { |
| | | CoolController, |
| | | BaseController, |
| | | CoolUrlTag, |
| | | CoolTag, |
| | | TagTypes, |
| | | } from '@cool-midway/core'; |
| | | import { Body, Inject, Post } from '@midwayjs/core'; |
| | | import { PluginService } from '../../../plugin/service/info'; |
| | | import { OrderPayService } from '../../service/pay'; |
| | | |
| | | /** |
| | | * 支付 |
| | | */ |
| | | @CoolUrlTag() |
| | | @CoolController() |
| | | export class OrderPayController extends BaseController { |
| | | @Inject() |
| | | pluginService: PluginService; |
| | | |
| | | @Inject() |
| | | orderPayService: OrderPayService; |
| | | |
| | | @Inject() |
| | | ctx; |
| | | |
| | | @CoolTag(TagTypes.IGNORE_TOKEN) |
| | | @Post('/wxNotify', { summary: '微信支付回调' }) |
| | | async wxNotify(@Body() body) { |
| | | // 获得插件实例 |
| | | const plugin: any = await this.pluginService.getInstance('pay-wx'); |
| | | // 获得微信支付 SDK 实例 |
| | | const instance = await plugin.getInstance(); |
| | | const { ciphertext, associated_data, nonce } = body.resource; |
| | | const data: any = instance.decipher_gcm(ciphertext, associated_data, nonce); |
| | | const check = await plugin.signVerify(this.ctx); |
| | | // 验签通过,处理业务逻辑 |
| | | if (check && data.trade_state == 'SUCCESS') { |
| | | return await this.orderPayService.paySuccess(data.out_trade_no, 1); |
| | | } |
| | | return 'fail'; |
| | | } |
| | | |
| | | @Post('/wxMiniPay', { summary: '微信小程序支付' }) |
| | | async wxMiniPay(@Body('orderId') orderId) { |
| | | return this.ok( |
| | | await this.orderPayService.wxMiniPay(orderId, this.ctx.user.id) |
| | | ); |
| | | } |
| | | |
| | | @Post('/wxMpPay', { summary: '微信公众号支付' }) |
| | | async wxMpPay(@Body('orderId') orderId) { |
| | | return this.ok( |
| | | await this.orderPayService.wxMpPay(orderId, this.ctx.user.id) |
| | | ); |
| | | } |
| | | |
| | | @Post('/wxAppPay', { summary: '微信APP支付' }) |
| | | async wxAppPay(@Body('orderId') orderId) { |
| | | return this.ok( |
| | | await this.orderPayService.wxAppPay(orderId, this.ctx.user.id) |
| | | ); |
| | | } |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | import { GoodsInfoEntity } from '../../goods/entity/info'; |
| | | import { GoodsSpecEntity } from '../../goods/entity/spec'; |
| | | |
| | | /** |
| | | * 订单商品 |
| | | */ |
| | | @Entity('order_goods') |
| | | export class OrderGoodsEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '订单ID' }) |
| | | orderId: number; |
| | | |
| | | @Index() |
| | | @Column({ comment: '商品ID' }) |
| | | goodsId: number; |
| | | |
| | | @Column({ |
| | | comment: '价格', |
| | | type: 'decimal', |
| | | precision: 12, |
| | | scale: 2, |
| | | }) |
| | | price: number; |
| | | |
| | | @Column({ |
| | | comment: '优惠金额', |
| | | type: 'decimal', |
| | | scale: 2, |
| | | precision: 12, |
| | | default: 0, |
| | | }) |
| | | discountPrice: number; |
| | | |
| | | @Column({ comment: '数量' }) |
| | | count: number; |
| | | |
| | | @Column({ comment: '其他信息', nullable: true }) |
| | | remark: string; |
| | | |
| | | // 随着时间的推移,商品信息会变,这边必须记录购买时的商品信息 |
| | | @Column({ comment: '商品信息', type: 'json' }) |
| | | goodsInfo: GoodsInfoEntity; |
| | | |
| | | @Column({ comment: '规格', type: 'json', nullable: true }) |
| | | spec: GoodsSpecEntity; |
| | | |
| | | @Column({ comment: '是否评价 0-否 1-是', default: 0 }) |
| | | isComment: number; |
| | | } |
| New file |
| | |
| | | import { BaseEntity } from '@cool-midway/core'; |
| | | import { Column, Entity, Index } from 'typeorm'; |
| | | import { UserAddressEntity } from '../../user/entity/address'; |
| | | import { OrderGoodsEntity } from './goods'; |
| | | |
| | | /** |
| | | * 订单信息 |
| | | */ |
| | | @Entity('order_info') |
| | | export class OrderInfoEntity extends BaseEntity { |
| | | @Index() |
| | | @Column({ comment: '用户ID' }) |
| | | userId: number; |
| | | |
| | | @Column({ comment: '标题', nullable: true }) |
| | | title: string; |
| | | |
| | | @Column({ comment: '支付方式 0-待支付 1-微信 2-支付宝', default: 0 }) |
| | | payType: number; |
| | | |
| | | @Column({ comment: '支付时间', type: 'datetime', nullable: true }) |
| | | payTime: Date; |
| | | |
| | | @Index() |
| | | @Column({ comment: '订单号', nullable: true }) |
| | | orderNum: string; |
| | | |
| | | @Column({ |
| | | comment: |
| | | '状态 0-待付款 1-待发货 2-待收货 3-待评价 4-交易完成 5-退款中 6-已退款 7-已关闭', |
| | | type: 'tinyint', |
| | | default: 0, |
| | | }) |
| | | status: number; |
| | | |
| | | @Column({ |
| | | comment: '价格', |
| | | type: 'decimal', |
| | | precision: 12, |
| | | scale: 2, |
| | | }) |
| | | price: number; |
| | | |
| | | @Column({ |
| | | comment: '优惠金额', |
| | | type: 'decimal', |
| | | scale: 2, |
| | | precision: 12, |
| | | default: 0, |
| | | }) |
| | | discountPrice: number; |
| | | |
| | | @Column({ comment: '优惠来源', type: 'json', nullable: true }) |
| | | discountSource: { |
| | | // 0-优惠券 |
| | | type: number; |
| | | // 对象ID |
| | | objectId: number; |
| | | // 信息 |
| | | info: any; |
| | | }; |
| | | |
| | | @Column({ comment: '地址', type: 'json', nullable: true }) |
| | | address: UserAddressEntity; |
| | | |
| | | @Column({ comment: '物流信息', type: 'json', nullable: true }) |
| | | logistics: { |
| | | // 物流公司 |
| | | company: string; |
| | | // 物流单号 |
| | | num: string; |
| | | }; |
| | | |
| | | @Column({ comment: '退款', type: 'json', nullable: true }) |
| | | refund: { |
| | | // 退款单号 |
| | | orderNum: string; |
| | | // 金额 |
| | | amount: number; |
| | | // 实际退款金额 |
| | | realAmount: number; |
| | | // 状态 0-申请中 1-已退款 2-拒绝 |
| | | status: number; |
| | | // 申请时间 |
| | | applyTime: Date; |
| | | // 退款时间 |
| | | time: Date; |
| | | // 退款原因 |
| | | reason: string; |
| | | // 拒绝原因 |
| | | refuseReason: string; |
| | | }; |
| | | |
| | | @Index() |
| | | @Column({ |
| | | asExpression: "JSON_EXTRACT(refund, '$.status')", |
| | | generatedType: 'VIRTUAL', |
| | | comment: '退款状态', |
| | | nullable: true, |
| | | }) |
| | | refundStatus: number; |
| | | |
| | | @Index() |
| | | @Column({ |
| | | asExpression: "JSON_EXTRACT(refund, '$.applyTime')", |
| | | generatedType: 'VIRTUAL', |
| | | comment: '退款申请时间', |
| | | nullable: true, |
| | | }) |
| | | refundApplyTime: Date; |
| | | |
| | | @Column({ comment: '备注', nullable: true }) |
| | | remark: string; |
| | | |
| | | @Column({ comment: '关闭备注', nullable: true }) |
| | | closeRemark: string; |
| | | |
| | | @Column({ comment: '已开票: 0-未开票 1-已开票', default: 0 }) |
| | | invoice: number; |
| | | |
| | | @Column({ comment: '微信类型 0-小程序 1-公众号 2-App', default: 0 }) |
| | | wxType: number; |
| | | |
| | | // 订单商品 |
| | | goodsList: OrderGoodsEntity[]; |
| | | } |
| New file |
| | |
| | | import { BaseCoolQueue, CoolQueue } from '@cool-midway/task'; |
| | | import { Inject } from '@midwayjs/core'; |
| | | import { OrderInfoService } from '../service/info'; |
| | | |
| | | // 行为 |
| | | export enum Action { |
| | | // 超时未支付 |
| | | TIMEOUT = 0, |
| | | // 自动确认收货 |
| | | CONFIRM = 1, |
| | | } |
| | | |
| | | /** |
| | | * 队列 |
| | | */ |
| | | @CoolQueue() |
| | | export abstract class OrderQueue extends BaseCoolQueue { |
| | | @Inject() |
| | | orderInfoService: OrderInfoService; |
| | | |
| | | async data(job: any, done: any) { |
| | | const action = job.data.action; |
| | | if (action == Action.TIMEOUT) { |
| | | // 关闭订单 |
| | | await this.orderInfoService.close(job.data.orderId, '超时未支付'); |
| | | } |
| | | if (action == Action.CONFIRM) { |
| | | // 自动确认收货 |
| | | await this.orderInfoService.autoConfirm(job.data.orderId); |
| | | } |
| | | done(); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Equal, In, Repository } from 'typeorm'; |
| | | import { OrderGoodsEntity } from '../entity/goods'; |
| | | import BigNumber from 'bignumber.js'; |
| | | import { GoodsSpecService } from '../../goods/service/spec'; |
| | | |
| | | /** |
| | | * 订单商品 |
| | | */ |
| | | @Provide() |
| | | export class OrderGoodsService extends BaseService { |
| | | @InjectEntityModel(OrderGoodsEntity) |
| | | orderGoodsEntity: Repository<OrderGoodsEntity>; |
| | | |
| | | @Inject() |
| | | goodsSpecService: GoodsSpecService; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.orderGoodsEntity); |
| | | } |
| | | |
| | | /** |
| | | * 保存 |
| | | * @param orderId |
| | | * @param goodsList |
| | | */ |
| | | async save(orderId: number, goodsList: OrderGoodsEntity[]) { |
| | | await this.orderGoodsEntity.save( |
| | | goodsList.map(item => { |
| | | item.orderId = orderId; |
| | | return item; |
| | | }) |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 获得总价 |
| | | * @param goodsList |
| | | */ |
| | | async getTotalPrice(goodsList: OrderGoodsEntity[]) { |
| | | let totalPrice = new BigNumber(0); |
| | | for (const goods of goodsList) { |
| | | totalPrice = totalPrice.plus(goods.price * goods.count); |
| | | } |
| | | return totalPrice.toNumber(); |
| | | } |
| | | |
| | | /** |
| | | * 通过订单ID获取商品 |
| | | * @param orderId |
| | | */ |
| | | async getByOrderId(orderId: number) { |
| | | return await this.orderGoodsEntity.findBy({ orderId: Equal(orderId) }); |
| | | } |
| | | |
| | | /** |
| | | * 通过订单ID获取商品 |
| | | * @param orderId |
| | | */ |
| | | async getByOrderIds(orderIds: number[]) { |
| | | return await this.orderGoodsEntity.findBy({ orderId: In(orderIds) }); |
| | | } |
| | | |
| | | // 更新库存 |
| | | async updateStock( |
| | | goodsList: OrderGoodsEntity[], |
| | | type: 'add' | 'sub' = 'sub' |
| | | ) { |
| | | for (const goods of goodsList) { |
| | | await this.goodsSpecService.updateStock( |
| | | goods.spec.id, |
| | | type == 'add' ? goods.count : -goods.count |
| | | ); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService, CoolCommException } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Equal, In, Repository } from 'typeorm'; |
| | | import { OrderInfoEntity } from '../entity/info'; |
| | | import { OrderGoodsService } from './goods'; |
| | | import { OrderGoodsEntity } from '../entity/goods'; |
| | | import * as moment from 'moment'; |
| | | import { UserAddressService } from '../../user/service/address'; |
| | | import { Action, OrderQueue } from '../queue/order'; |
| | | import { OrderPayService } from './pay'; |
| | | import { PluginService } from '../../plugin/service/info'; |
| | | import { GoodsSpecEntity } from '../../goods/entity/spec'; |
| | | import { MarketCouponInfoService } from '../../market/service/coupon/info'; |
| | | import { MarketCouponUserEntity } from '../../market/entity/coupon/user'; |
| | | import { BaseSysParamService } from '../../base/service/sys/param'; |
| | | import BigNumber from 'bignumber.js'; |
| | | |
| | | /** |
| | | * 订单信息 |
| | | */ |
| | | @Provide() |
| | | export class OrderInfoService extends BaseService { |
| | | @InjectEntityModel(OrderInfoEntity) |
| | | orderInfoEntity: Repository<OrderInfoEntity>; |
| | | |
| | | @InjectEntityModel(GoodsSpecEntity) |
| | | goodsSpecEntity: Repository<GoodsSpecEntity>; |
| | | |
| | | @InjectEntityModel(MarketCouponUserEntity) |
| | | marketCouponUserEntity: Repository<MarketCouponUserEntity>; |
| | | |
| | | @Inject() |
| | | orderGoodsService: OrderGoodsService; |
| | | |
| | | @Inject() |
| | | userAddressService: UserAddressService; |
| | | |
| | | @Inject() |
| | | orderPayService: OrderPayService; |
| | | |
| | | @Inject() |
| | | orderQueue: OrderQueue; |
| | | |
| | | @Inject() |
| | | pluginService: PluginService; |
| | | |
| | | @Inject() |
| | | marketCouponInfoService: MarketCouponInfoService; |
| | | |
| | | @Inject() |
| | | baseSysParamService: BaseSysParamService; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | this.setEntity(this.orderInfoEntity); |
| | | } |
| | | |
| | | /** |
| | | * 修改前 |
| | | * @param data |
| | | * @param type |
| | | */ |
| | | async modifyBefore(data: any, type: 'add' | 'update' | 'delete') { |
| | | if (type == 'add' || type == 'update') { |
| | | delete data.refundStatus; |
| | | delete data.refundApplyTime; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 分页查询 |
| | | * @param query |
| | | * @param option |
| | | * @param connectionName |
| | | */ |
| | | async page(query: any, option: any, connectionName?: any) { |
| | | const result = await super.page(query, option, connectionName); |
| | | // 筛选条件的非分页sql |
| | | const countSql = await super.getOptionFind(query, option); |
| | | |
| | | const countResult = await this.nativeQuery( |
| | | `SELECT COUNT(a.id) as totalCount, SUM(a.price) as totalPrice FROM ${ |
| | | countSql.split('FROM')[1].split('ORDER BY')[0] |
| | | }` |
| | | ); |
| | | |
| | | result['subData'] = { |
| | | totalCount: countResult[0].totalCount, |
| | | totalPrice: countResult[0].totalPrice, |
| | | }; |
| | | |
| | | const goodsList = await this.orderGoodsService.getByOrderIds( |
| | | result.list.map(e => e.id) |
| | | ); |
| | | for (const item of result.list) { |
| | | item.goodsList = goodsList.filter(e => e.orderId == item.id); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单号获取订单 |
| | | * @param orderNum |
| | | * @returns |
| | | */ |
| | | async getByOrderNum(orderNum: string) { |
| | | return this.orderInfoEntity.findOneBy({ |
| | | orderNum: Equal(orderNum), |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 修改订单状态 |
| | | * @param id |
| | | * @param status |
| | | */ |
| | | async changeStatus(id: number, status: number) { |
| | | await this.orderInfoEntity.update({ id }, { status }); |
| | | } |
| | | |
| | | /** |
| | | * 关闭订单 |
| | | * @param orderId |
| | | * @param remark |
| | | */ |
| | | async close(orderId: number, remark: string) { |
| | | const order = await this.info(orderId); |
| | | if (!order || !remark) |
| | | throw new CoolCommException('订单不存在或备注不能为空'); |
| | | if (order.status != 0) { |
| | | throw new CoolCommException('订单状态不允许关闭'); |
| | | } |
| | | |
| | | // 退回优惠券 |
| | | if (order.discountSource && order.discountPrice > 0) { |
| | | if (order.discountSource.type == 0) { |
| | | this.marketCouponUserEntity.update( |
| | | { |
| | | id: order.discountSource.objectId, |
| | | }, |
| | | { |
| | | status: 0, |
| | | } |
| | | ); |
| | | } |
| | | } |
| | | |
| | | await this.orderInfoEntity.update( |
| | | { id: orderId }, |
| | | { status: 7, closeRemark: remark } |
| | | ); |
| | | |
| | | // 释放库存 |
| | | await this.orderGoodsService.updateStock(order.goodsList, 'add'); |
| | | } |
| | | |
| | | /** |
| | | * 订单详情 |
| | | * @param id |
| | | * @param infoIgnoreProperty |
| | | */ |
| | | async info(id: any, infoIgnoreProperty?: string[]) { |
| | | const info = await super.info(id, infoIgnoreProperty); |
| | | if (!info) { |
| | | throw new CoolCommException('订单不存在'); |
| | | } |
| | | if (info) { |
| | | // 获取商品 |
| | | info.goodsList = await this.orderGoodsService.getByOrderId(info.id); |
| | | } |
| | | return info; |
| | | } |
| | | |
| | | /** |
| | | * 创建订单 |
| | | * @param data |
| | | */ |
| | | async create(data: { |
| | | userId: number; |
| | | goodsList: OrderGoodsEntity[]; |
| | | addressId: number; |
| | | remark: string; |
| | | title: string; |
| | | couponId?: number; |
| | | }) { |
| | | const address = await this.userAddressService.info(data.addressId); |
| | | const order = { |
| | | userId: data.userId, |
| | | address, |
| | | remark: data.remark, |
| | | title: data.title, |
| | | goodsList: data.goodsList, |
| | | } as OrderInfoEntity; |
| | | // 检查库存 |
| | | await this.checkStock(data.goodsList); |
| | | order.price = await this.orderGoodsService.getTotalPrice(data.goodsList); |
| | | order.goodsList = data.goodsList; |
| | | |
| | | // 使用优惠券 |
| | | if (data.couponId) { |
| | | await this.marketCouponInfoService.checkAndUse( |
| | | data.couponId, |
| | | data.userId, |
| | | order |
| | | ); |
| | | } |
| | | await this.orderInfoEntity.insert(order); |
| | | |
| | | // 生成订单号 |
| | | const orderNum = await this.generateOrderNum(order.id); |
| | | |
| | | // 更新订单 |
| | | await this.orderInfoEntity.update(order.id, { |
| | | orderNum, |
| | | }); |
| | | |
| | | // 保存订单商品 |
| | | await this.orderGoodsService.save(order.id, data.goodsList); |
| | | |
| | | // 更新商品库存 |
| | | await this.orderGoodsService.updateStock(data.goodsList); |
| | | |
| | | const orderTimeout = await this.baseSysParamService.dataByKey( |
| | | 'orderTimeout' |
| | | ); |
| | | // 发送订单创建消息 |
| | | this.orderQueue.add( |
| | | { orderId: order.id, action: Action.TIMEOUT }, |
| | | { |
| | | // 超时关闭订单 |
| | | delay: orderTimeout * 60 * 1000, |
| | | } |
| | | ); |
| | | return order; |
| | | } |
| | | |
| | | /** |
| | | * 生成订单号 |
| | | * @param orderId |
| | | */ |
| | | async generateOrderNum(orderId: number, label = 'U') { |
| | | const orderNum = |
| | | moment().format('YYYYMMDDHHmmss') + |
| | | Math.floor(Math.random() * 10000).toString() + |
| | | orderId.toString(); |
| | | return label + orderNum; |
| | | } |
| | | |
| | | /** |
| | | * 退款 |
| | | * @param userId |
| | | * @param orderId |
| | | * @param goodsId |
| | | * @param reason |
| | | */ |
| | | async refund(userId: number, orderId: number, reason: string) { |
| | | const order = await this.info(orderId); |
| | | if (order && order.userId != userId) { |
| | | throw new CoolCommException('非法操作'); |
| | | } |
| | | if (![1, 2].includes(order.status)) { |
| | | throw new CoolCommException('订单状态不允许退款'); |
| | | } |
| | | |
| | | await this.orderInfoEntity.update( |
| | | { id: Equal(orderId) }, |
| | | { |
| | | status: 5, |
| | | refund: { |
| | | amount: new BigNumber(order.price) |
| | | .minus(order.discountPrice) |
| | | .toNumber(), |
| | | status: 0, |
| | | applyTime: new Date(), |
| | | reason, |
| | | orderNum: 'R' + order.orderNum.slice(1), |
| | | }, |
| | | } |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 退款处理 |
| | | * @param orderId |
| | | * @param action 0-拒绝 1-同意 |
| | | * @param refuseReason 0-拒绝 1-同意 |
| | | * @param amount |
| | | */ |
| | | async refundHandle( |
| | | orderId: number, |
| | | action: number, |
| | | refuseReason: string, |
| | | amount: number |
| | | ) { |
| | | const order = await this.info(orderId); |
| | | if (order.status != 5 || !order.refund) { |
| | | throw new CoolCommException('订单状态不允许退款处理'); |
| | | } |
| | | // 拒绝退款 |
| | | if (action == 0) { |
| | | await this.orderInfoEntity.update( |
| | | { id: Equal(orderId) }, |
| | | { |
| | | status: 4, |
| | | refund: { |
| | | ...order.refund, |
| | | status: 2, |
| | | refuseReason: refuseReason, |
| | | }, |
| | | } |
| | | ); |
| | | } |
| | | // 同意退款 |
| | | if (action == 1) { |
| | | if (amount > order.price) { |
| | | throw new CoolCommException('退款金额不能大于订单金额'); |
| | | } |
| | | // 执行退款操作 |
| | | const result = await this.orderPayService.wxRefund(order, amount); |
| | | |
| | | if (result) { |
| | | await this.orderInfoEntity.update( |
| | | { id: Equal(orderId) }, |
| | | { |
| | | status: 6, |
| | | refund: { |
| | | ...order.refund, |
| | | status: 1, |
| | | realAmount: amount, |
| | | time: new Date(), |
| | | }, |
| | | } |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 确认收货 |
| | | * @param userId |
| | | * @param orderId |
| | | */ |
| | | async confirm(orderId: number, userId: number) { |
| | | const order = await this.info(orderId); |
| | | if (order && order.userId != userId) { |
| | | throw new CoolCommException('非法操作'); |
| | | } |
| | | if (![2].includes(order.status)) { |
| | | throw new CoolCommException('订单状态不允许退款'); |
| | | } |
| | | await this.orderInfoEntity.update( |
| | | { id: Equal(orderId) }, |
| | | { |
| | | status: 3, |
| | | } |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 自动确认收货 |
| | | * @param orderId |
| | | * @returns |
| | | */ |
| | | async autoConfirm(orderId: number) { |
| | | const info = await this.orderInfoEntity.findOneBy({ id: Equal(orderId) }); |
| | | if (info.status != 2) { |
| | | return; |
| | | } |
| | | await this.orderInfoEntity.update({ id: Equal(orderId) }, { status: 3 }); |
| | | } |
| | | |
| | | /** |
| | | * 物流信息 |
| | | * @param orderId |
| | | */ |
| | | async logistics(orderId: number, userId?: number) { |
| | | const order: OrderInfoEntity = await this.info(orderId); |
| | | if (userId && order.userId != userId) { |
| | | throw new CoolCommException('非法操作'); |
| | | } |
| | | const no = order.logistics?.num; |
| | | |
| | | if (!no) { |
| | | return null; |
| | | } |
| | | |
| | | const instance: any = await this.pluginService.getInstance('wuliu'); |
| | | return await instance.query(no); |
| | | } |
| | | |
| | | /** |
| | | * 发货 |
| | | * @param orderId |
| | | * @param logistics |
| | | */ |
| | | async deliver( |
| | | orderId: number, |
| | | logistics: { |
| | | // 物流公司 |
| | | company: string; |
| | | // 物流单号 |
| | | num: string; |
| | | } |
| | | ) { |
| | | const order: OrderInfoEntity = await this.info(orderId); |
| | | if (order.status != 1) { |
| | | throw new CoolCommException('订单状态不允许发货'); |
| | | } |
| | | await this.orderInfoEntity.update(orderId, { |
| | | status: 2, |
| | | logistics, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 检查库存 |
| | | * @param goodsList |
| | | */ |
| | | async checkStock(goodsList: OrderGoodsEntity[]) { |
| | | const specs = goodsList.map(item => item.spec); |
| | | const specList = await this.goodsSpecEntity.findBy({ |
| | | id: In(specs.map(e => e.id)), |
| | | }); |
| | | for (const spec of specList) { |
| | | const goods = goodsList.find(e => e.spec.id == spec.id); |
| | | // 设置商品价格 |
| | | goods.price = spec.price; |
| | | if (goods.count > spec.stock) { |
| | | throw new CoolCommException(`商品[${goods.goodsInfo.title}],库存不足`); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 用户订单数量 |
| | | * @param userId |
| | | */ |
| | | async userCount(userId: number) { |
| | | const statusLabels = [ |
| | | '待付款', |
| | | '待发货', |
| | | '待收货', |
| | | '待评价', |
| | | '交易完成', |
| | | '退款中', |
| | | '已退款', |
| | | '已关闭', |
| | | ]; |
| | | // 生成查询字符串 |
| | | const selectQueries = statusLabels.map( |
| | | (label, index) => |
| | | `SUM(CASE WHEN status = ${index} THEN 1 ELSE 0 END) AS '${label}'` |
| | | ); |
| | | const list = await this.orderInfoEntity |
| | | .createQueryBuilder('a') |
| | | .select(selectQueries) |
| | | .where('a.userId = :userId', { userId }) |
| | | .getRawMany(); |
| | | return list[0]; |
| | | } |
| | | } |
| New file |
| | |
| | | import { Init, Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService, CoolCommException } from '@cool-midway/core'; |
| | | import { PluginService } from '../../plugin/service/info'; |
| | | import { OrderInfoService } from './info'; |
| | | import { UserWxService } from '../../user/service/wx'; |
| | | import BigNumber from 'bignumber.js'; |
| | | import { OrderInfoEntity } from '../entity/info'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { Action, OrderQueue } from '../queue/order'; |
| | | import { BaseSysParamService } from '../../base/service/sys/param'; |
| | | |
| | | /** |
| | | * 支付 |
| | | */ |
| | | @Provide() |
| | | export class OrderPayService extends BaseService { |
| | | @InjectEntityModel(OrderInfoEntity) |
| | | orderInfoEntity: Repository<OrderInfoEntity>; |
| | | |
| | | @Inject() |
| | | pluginService: PluginService; |
| | | |
| | | @Inject() |
| | | orderInfoService: OrderInfoService; |
| | | |
| | | @Inject() |
| | | userWxService: UserWxService; |
| | | |
| | | @Inject() |
| | | orderQueue: OrderQueue; |
| | | |
| | | @Inject() |
| | | baseSysParamService: BaseSysParamService; |
| | | |
| | | @Init() |
| | | async init() { |
| | | await super.init(); |
| | | } |
| | | |
| | | /** |
| | | * 支付成功 |
| | | * @param orderNum 订单号 |
| | | * @param payType 支付方式 0-待支付 1-微信 2-支付宝 |
| | | */ |
| | | async paySuccess(orderNum: string, payType: number) { |
| | | const order = await this.orderInfoService.getByOrderNum(orderNum); |
| | | if (order && order.status == 0) { |
| | | await this.orderInfoEntity.update(order.id, { |
| | | payType, |
| | | status: 1, |
| | | payTime: order.payTime, |
| | | }); |
| | | // 发送自动确认收货队列 |
| | | const orderConfirm = await this.baseSysParamService.dataByKey( |
| | | 'orderConfirm' |
| | | ); |
| | | this.orderQueue.add( |
| | | { orderId: order.id, action: Action.CONFIRM }, |
| | | { |
| | | // 自动确认收货时间 |
| | | delay: orderConfirm * 60 * 60 * 1000, |
| | | } |
| | | ); |
| | | } |
| | | return 'success'; |
| | | } |
| | | |
| | | /** |
| | | * 微信小程序支付 |
| | | * @param userId |
| | | * @param orderId |
| | | */ |
| | | async wxMiniPay(orderId: number, userId: number) { |
| | | await this.orderInfoEntity.update(orderId, { wxType: 0 }); |
| | | return await this.wxJSAPI(orderId, userId, 0); |
| | | } |
| | | |
| | | /** |
| | | * 微信公众号支付 |
| | | * @param userId |
| | | * @param orderId |
| | | */ |
| | | async wxMpPay(orderId: number, userId: number) { |
| | | await this.orderInfoEntity.update(orderId, { wxType: 1 }); |
| | | return await this.wxJSAPI(orderId, userId, 1); |
| | | } |
| | | |
| | | /** |
| | | * 获得appid |
| | | * @param type 0-小程序 1-公众号 2-App |
| | | */ |
| | | async getAppidByType(type: number) { |
| | | let account; |
| | | const plugin: any = await this.pluginService.getInstance('wx'); |
| | | // 小程序 |
| | | if (type == 0) { |
| | | account = (await plugin.MiniApp()).getAccount(); |
| | | } |
| | | // 公众号 |
| | | if (type == 1) { |
| | | account = (await plugin.OfficialAccount()).getAccount(); |
| | | } |
| | | // App |
| | | if (type == 2) { |
| | | account = (await plugin.OpenPlatform()).getAccount(); |
| | | } |
| | | // 获得appid |
| | | const appid = account.getAppId(); |
| | | return appid; |
| | | } |
| | | |
| | | /** |
| | | * 获得微信支付 SDK 实例 |
| | | * @param type 0-小程序 1-公众号 2-App |
| | | * @returns |
| | | */ |
| | | async wxPayInstance(appid: string) { |
| | | // 获得插件实例 |
| | | const plugin: any = await this.pluginService.getInstance('pay-wx'); |
| | | // 获得插件配置 |
| | | const config = await plugin.getConfig(); |
| | | // 获得微信支付 SDK 实例 |
| | | const instance = await plugin.getInstance({ |
| | | ...config, |
| | | appid, |
| | | }); |
| | | return { config, instance }; |
| | | } |
| | | |
| | | /** |
| | | * 微信APP支付 |
| | | * @param orderId |
| | | * @param userId |
| | | * @returns |
| | | */ |
| | | async wxAppPay(orderId: number, userId: number) { |
| | | const appid = await this.getAppidByType(2); |
| | | |
| | | const { config, instance } = await this.wxPayInstance(appid); |
| | | |
| | | const order = await this.getOrder(orderId, userId); |
| | | const params = { |
| | | description: '商品采购', |
| | | out_trade_no: order?.orderNum, |
| | | notify_url: config.notify_url, |
| | | amount: { |
| | | total: new BigNumber(order.price) |
| | | .minus(order.discountPrice || 0) |
| | | .multipliedBy(100) |
| | | .toNumber(), |
| | | }, |
| | | }; |
| | | |
| | | const result = await instance.transactions_app(params); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 获得订单 |
| | | * @param orderId |
| | | * @param userId |
| | | * @returns |
| | | */ |
| | | async getOrder(orderId: number, userId: number) { |
| | | const order: OrderInfoEntity = await this.orderInfoService.info(orderId); |
| | | if (!order || order.status != 0 || order.userId != userId) { |
| | | throw new CoolCommException('订单不存在或不是可以支付的状态'); |
| | | } |
| | | return order; |
| | | } |
| | | |
| | | /** |
| | | * 微信JSAPI |
| | | * @param orderId |
| | | * @param userId |
| | | * @param type 0-小程序 1-公众号 2-App |
| | | * @returns |
| | | */ |
| | | async wxJSAPI(orderId: number, userId: number, type = 0) { |
| | | const order = await this.getOrder(orderId, userId); |
| | | const openid = await this.userWxService.getOpenid(userId, type); |
| | | |
| | | const appid = await this.getAppidByType(type); |
| | | |
| | | const { config, instance } = await this.wxPayInstance(appid); |
| | | |
| | | const params = { |
| | | description: '商品采购', |
| | | out_trade_no: order.orderNum, |
| | | notify_url: config.notify_url, |
| | | amount: { |
| | | total: new BigNumber(order.price) |
| | | .minus(order.discountPrice || 0) |
| | | .multipliedBy(100) |
| | | .toNumber(), |
| | | }, |
| | | payer: { |
| | | openid, |
| | | }, |
| | | scene_info: { |
| | | payer_client_ip: '127.0.0.1', |
| | | }, |
| | | }; |
| | | const result = await instance.transactions_jsapi(params); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 微信退款 |
| | | * @param order |
| | | * @param amount |
| | | * @returns |
| | | */ |
| | | async wxRefund(order: OrderInfoEntity, amount: number) { |
| | | const appid = await this.getAppidByType(order.wxType); |
| | | const { config, instance } = await this.wxPayInstance(appid); |
| | | const params = { |
| | | out_trade_no: order.orderNum, |
| | | out_refund_no: order.refund.orderNum, |
| | | notify_url: config.notify_url, |
| | | amount: { |
| | | refund: new BigNumber(amount).multipliedBy(100).toNumber(), |
| | | total: new BigNumber(order.price).multipliedBy(100).toNumber(), |
| | | currency: 'CNY', |
| | | }, |
| | | }; |
| | | const result = await instance.refunds(params); |
| | | if ( |
| | | result.status == 200 || |
| | | result.status == 'SUCCESS' || |
| | | result.status == 'PROCESSING' |
| | | ) { |
| | | return true; |
| | | } |
| | | throw new CoolCommException(result.message); |
| | | } |
| | | } |
| New file |
| | |
| | | import { Inject } from "@midwayjs/core"; |
| | | import { CoolController, BaseController } from "@cool-midway/core"; |
| | | import { RecycleAppointmentEntity } from "../../entity/recycleappointment"; |
| | | import { RecycleAppointmentService } from "../../service/recycleappointment"; |
| | | |
| | | /** |
| | | * 回收预约-应用端 |
| | | */ |
| | | @CoolController({ |
| | | api: ["add", "info", "page"], |
| | | entity: RecycleAppointmentEntity, |
| | | service: RecycleAppointmentService, |
| | | pageQueryOp: { |
| | | fieldEq: ["a.status", "a.campusUserId"], |
| | | addOrderBy: { |
| | | createTime: "DESC", |
| | | }, |
| | | where: async (ctx) => { |
| | | const { tab } = ctx.request.body; |
| | | if (tab === "done") { |
| | | return [["a.status = :st", { st: "已完成" }]]; |
| | | } |
| | | if (tab === "booked") { |
| | | return [["a.status != :st", { st: "已完成" }]]; |
| | | } |
| | | return []; |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppRecycleAppointmentController extends BaseController { |
| | | @Inject() |
| | | recycleAppointmentService: RecycleAppointmentService; |
| | | } |
| | |
| | | entity: RecycleOrderEntity, |
| | | service: RecycleOrderService, |
| | | pageQueryOp: { |
| | | fieldEq: ["a.status"], |
| | | where: async (ctx) => { |
| | | return [ |
| | | ["a.campusUserId = :userId", { userId: ctx.user.id }] |
| | | ]; |
| | | } |
| | | fieldEq: ["a.status","a.campusUserId","a.orderType"], |
| | | addOrderBy:{createTime:'DESC'} |
| | | } |
| | | }) |
| | | export class AppRecycleOrderController extends BaseController { |
| | |
| | | import { CoolController, BaseController } from "@cool-midway/core"; |
| | | import { RecycleOrderItemEntity } from "../../entity/recycleorderitem"; |
| | | import { RecycleOrderItemService } from "../../service/recycleorderitem"; |
| | | import { RecycleItemEntity } from "../../entity/recycleitem"; |
| | | |
| | | /** |
| | | * 回收订单条目-应用端 |
| | |
| | | entity: RecycleOrderItemEntity, |
| | | service: RecycleOrderItemService, |
| | | pageQueryOp: { |
| | | select: ["a.*", "b.mainPic"], |
| | | fieldEq: ["a.orderId"], |
| | | join: [ |
| | | { |
| | | entity: RecycleItemEntity, |
| | | alias: "b", |
| | | condition: "a.itemId = b.id", |
| | | }, |
| | | ], |
| | | fieldEq: ["a.orderId"] |
| | | }, |
| | | }) |
| | | export class AppRecycleOrderitemController extends BaseController { |
| New file |
| | |
| | | import { Body, Inject, Post } from '@midwayjs/core'; |
| | | import { CoolController, BaseController } from '@cool-midway/core'; |
| | | import { RecycleTransactionEntity } from '../../entity/transaction'; |
| | | import { RecycleTransactionService } from '../../service/transaction'; |
| | | |
| | | /** |
| | | * 碳积分流水-应用端 |
| | | */ |
| | | @CoolController({ |
| | | api: ['page'], |
| | | entity: RecycleTransactionEntity, |
| | | service: RecycleTransactionService, |
| | | pageQueryOp: { |
| | | addOrderBy: { |
| | | createTime: 'DESC', |
| | | }, |
| | | }, |
| | | }) |
| | | export class AppRecycleTransactionController extends BaseController { |
| | | @Inject() |
| | | recycleTransactionService: RecycleTransactionService; |
| | | |
| | | @Post('/checkUser', { summary: '校验转赠接收人' }) |
| | | async checkUser(@Body('unionid') unionid: string) { |
| | | return this.ok(await this.recycleTransactionService.checkUser(unionid)); |
| | | } |
| | | |
| | | @Post('/transfer', { summary: '碳积分转赠' }) |
| | | async transfer( |
| | | @Body() |
| | | body: { |
| | | fromUnionid: string; |
| | | toUnionid: string; |
| | | amount: number; |
| | | } |
| | | ) { |
| | | return this.ok(await this.recycleTransactionService.transfer(body)); |
| | | } |
| | | } |
| | |
| | | }); |
| | | |
| | | if (order?.campusUserId) { |
| | | await UserInfoEntity.update( |
| | | await UserInfoEntity.getRepository().increment( |
| | | { unionid: order.campusUserId }, |
| | | { |
| | | carbonBalance: () => `carbonBalance + ${totalCarbonPoint}` |
| | | } |
| | | 'carbonBalance', |
| | | totalCarbonPoint |
| | | ); |
| | | } |
| | | |
| | |
| | | import { Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { BaseService, CoolCommException, CoolTransaction } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository } from 'typeorm'; |
| | | import { QueryRunner, Repository } from 'typeorm'; |
| | | import { RecycleTransactionEntity } from '../entity/transaction'; |
| | | import { UserInfoEntity } from '../../user/entity/info'; |
| | | |
| | | /** |
| | | * 碳积分流转记录 |
| | |
| | | @InjectEntityModel(RecycleTransactionEntity) |
| | | recycleTransactionEntity: Repository<RecycleTransactionEntity>; |
| | | |
| | | } |
| | | @InjectEntityModel(UserInfoEntity) |
| | | userInfoEntity: Repository<UserInfoEntity>; |
| | | |
| | | /** |
| | | * App 端分页:转赠 tab 只看赠送/接受,收支 tab 看其余类型 |
| | | */ |
| | | async page(query: any) { |
| | | const { userId, tab, page = 1, size = 10 } = query || {}; |
| | | const find = this.recycleTransactionEntity.createQueryBuilder('a'); |
| | | |
| | | if (userId) { |
| | | find.andWhere('a.userId = :uid', { uid: String(userId) }); |
| | | } |
| | | |
| | | if (tab === 'io') { |
| | | find.andWhere('(a.sourceType != :t1 AND a.sourceType != :t2)', { |
| | | t1: '赠送', |
| | | t2: '接受', |
| | | }); |
| | | } else { |
| | | find.andWhere('(a.sourceType = :t1 OR a.sourceType = :t2)', { |
| | | t1: '赠送', |
| | | t2: '接受', |
| | | }); |
| | | } |
| | | |
| | | find.orderBy('a.createTime', 'DESC'); |
| | | find.addOrderBy('a.id', 'DESC'); |
| | | |
| | | console.log(find) |
| | | |
| | | return this.entityRenderPage(find, { ...query, page, size }, false); |
| | | } |
| | | |
| | | async checkUser(unionid: string) { |
| | | if (!unionid) { |
| | | throw new CoolCommException('请选择系统用户作为接受转赠对象'); |
| | | } |
| | | const user = await this.userInfoEntity.findOneBy({ unionid }); |
| | | if (!user) { |
| | | throw new CoolCommException('请选择系统用户作为接受转赠对象'); |
| | | } |
| | | return { |
| | | unionid: user.unionid, |
| | | nickName: user.nickName, |
| | | phone: user.phone, |
| | | }; |
| | | } |
| | | |
| | | @CoolTransaction() |
| | | async transfer( |
| | | param: { fromUnionid: string; toUnionid: string; amount: number }, |
| | | queryRunner?: QueryRunner |
| | | ) { |
| | | if (!queryRunner) { |
| | | throw new CoolCommException('事务启动失败'); |
| | | } |
| | | |
| | | const fromUnionid = String(param.fromUnionid || '').trim(); |
| | | const toUnionid = String(param.toUnionid || '').trim(); |
| | | const amount = Number(param.amount); |
| | | |
| | | if (!fromUnionid || !toUnionid) { |
| | | throw new CoolCommException('转赠参数不完整'); |
| | | } |
| | | if (fromUnionid === toUnionid) { |
| | | throw new CoolCommException('不能转赠给自己'); |
| | | } |
| | | if (!amount || amount <= 0) { |
| | | throw new CoolCommException('转赠金额必须大于0'); |
| | | } |
| | | |
| | | const manager = queryRunner.manager; |
| | | const fromUser = await manager.findOne(UserInfoEntity, { |
| | | where: { unionid: fromUnionid }, |
| | | }); |
| | | const toUser = await manager.findOne(UserInfoEntity, { |
| | | where: { unionid: toUnionid }, |
| | | }); |
| | | |
| | | if (!toUser) { |
| | | throw new CoolCommException('请选择系统用户作为接受转赠对象'); |
| | | } |
| | | if (!fromUser) { |
| | | throw new CoolCommException('当前用户不存在'); |
| | | } |
| | | if (Number(fromUser.carbonBalance || 0) < amount) { |
| | | throw new CoolCommException('碳积分不足'); |
| | | } |
| | | |
| | | const dec = await manager |
| | | .createQueryBuilder() |
| | | .update(UserInfoEntity) |
| | | .set({ carbonBalance: () => `carbonBalance - ${amount}` }) |
| | | .where('unionid = :unionid AND carbonBalance >= :amount', { |
| | | unionid: fromUnionid, |
| | | amount, |
| | | }) |
| | | .execute(); |
| | | |
| | | if (!dec.affected) { |
| | | throw new CoolCommException('碳积分不足'); |
| | | } |
| | | |
| | | await manager |
| | | .createQueryBuilder() |
| | | .update(UserInfoEntity) |
| | | .set({ carbonBalance: () => `carbonBalance + ${amount}` }) |
| | | .where('unionid = :unionid', { unionid: toUnionid }) |
| | | .execute(); |
| | | |
| | | const fromName = fromUser.nickName || fromUser.phone || fromUnionid; |
| | | const toName = toUser.nickName || toUser.phone || toUnionid; |
| | | |
| | | await manager.save(RecycleTransactionEntity, [ |
| | | { |
| | | userId: fromUnionid, |
| | | departmentId: fromUser.departmentId, |
| | | type: '支出', |
| | | amount, |
| | | sourceType: '赠送', |
| | | remark: `转赠给${toName}`, |
| | | }, |
| | | { |
| | | userId: toUnionid, |
| | | departmentId: toUser.departmentId, |
| | | type: '收入', |
| | | amount, |
| | | sourceType: '接受', |
| | | remark: `从${fromName}获赠`, |
| | | }, |
| | | ]); |
| | | |
| | | return true; |
| | | } |
| | | } |
| | |
| | | // 刷新token 过期时间,单位秒 |
| | | refreshExpire: 60 * 60 * 24 * 30, |
| | | // jwt 秘钥 |
| | | secret: 'b4652251-d174-4e8e-9c61-25a091afb328x', |
| | | secret: '5b81d7bc-11cc-485d-8d6b-39d3e7c48f74x', |
| | | }, |
| | | } as ModuleConfig; |
| | | }; |
| | |
| | | |
| | | }, |
| | | }) |
| | | export class AdminUserInfoController extends BaseController { } |
| | | export class AdminUserInfoController extends BaseController {} |
| | |
| | | ) |
| | | ); |
| | | } |
| | | |
| | | @Post('/dashboard', { summary: '获取用户碳积分信息' }) |
| | | async dashboard(@Body() body) { |
| | | console.log(body) |
| | | const {unionid} = body; |
| | | return this.ok( |
| | | await this.userInfoService.dashboard( |
| | | unionid |
| | | ) |
| | | ); |
| | | } |
| | | } |
| | |
| | | ) { |
| | | return this.ok(await this.userLoginService.password(phone, password)); |
| | | } |
| | | |
| | | @CoolTag(TagTypes.IGNORE_TOKEN) |
| | | @Post('/sorterPassword', { summary: '密码登录' }) |
| | | async sorterPassword( |
| | | @Body('phone') phone: string, |
| | | @Body('password') password: string |
| | | ) { |
| | | return this.ok(await this.userLoginService.sorterPassword(phone, password)); |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | import { UserInfoEntity } from '../entity/info'; |
| | | import { UserSmsService } from './sms'; |
| | | import { UserWxService } from './wx'; |
| | | import { RecycleOrderEntity } from '../../shop/entity/recycleorder'; |
| | | |
| | | |
| | | /** |
| | | * 用户信息 |
| | |
| | | export class UserInfoService extends BaseService { |
| | | @InjectEntityModel(UserInfoEntity) |
| | | userInfoEntity: Repository<UserInfoEntity>; |
| | | |
| | | @InjectEntityModel(RecycleOrderEntity) |
| | | recycleOrderEntity: Repository<RecycleOrderEntity>; |
| | | |
| | | @Inject() |
| | | pluginService: PluginService; |
| | |
| | | } |
| | | await this.userInfoEntity.update({ id: userId }, { phone }); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户的碳积分信息 |
| | | * @param unionid |
| | | */ |
| | | async dashboard(unionid) { |
| | | const info = await this.userInfoEntity.findOneBy({ unionid: unionid }); |
| | | if (!info) { |
| | | throw new CoolCommException('查询无结果'); |
| | | } |
| | | |
| | | const count_ = await this.recycleOrderEntity.countBy({ |
| | | campusUserId: unionid |
| | | }); |
| | | |
| | | |
| | | return { carbonPoints: info.carbonBalance, recycleCount:count_, overPercent: 90 } |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Equal, Repository } from 'typeorm'; |
| | | import { UserInfoEntity } from '../entity/info'; |
| | | import { BasicdataSorterEntity } from '../../basicdata/entity/sorter'; |
| | | import { UserWxService } from './wx'; |
| | | import * as jwt from 'jsonwebtoken'; |
| | | import { UserWxEntity } from '../entity/wx'; |
| | |
| | | export class UserLoginService extends BaseService { |
| | | @InjectEntityModel(UserInfoEntity) |
| | | userInfoEntity: Repository<UserInfoEntity>; |
| | | |
| | | @InjectEntityModel(BasicdataSorterEntity) |
| | | basicdataSorterEntity: Repository<BasicdataSorterEntity>; |
| | | |
| | | @InjectEntityModel(UserWxEntity) |
| | | userWxEntity: Repository<UserWxEntity>; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 分拣人员-密码登录 |
| | | * @param phone |
| | | * @param password |
| | | */ |
| | | async sorterPassword(phone, password) { |
| | | const user = await this.basicdataSorterEntity.findOneBy({ phone }); |
| | | |
| | | if (user && user.password == md5(password)) { |
| | | |
| | | const info = { |
| | | id: user.businessId, |
| | | } |
| | | const { expire, refreshExpire } = this.jwtConfig; |
| | | return { |
| | | expire, |
| | | token: await this.generateSorterToken(info), |
| | | refreshExpire, |
| | | refreshToken: await this.generateSorterToken(info, true), |
| | | }; |
| | | |
| | | } else { |
| | | throw new CoolCommException('账号或密码错误'); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 获得token |
| | | * @param info |
| | | * @returns |
| | |
| | | expiresIn: isRefresh ? refreshExpire : expire, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 生成token |
| | | * @param tokenInfo 信息 |
| | | * @param roleIds 角色集合 |
| | | */ |
| | | async generateSorterToken(info, isRefresh = false) { |
| | | const { expire, refreshExpire, secret } = this.jwtConfig; |
| | | const user = await this.basicdataSorterEntity.findOneBy({ businessId: Equal(info.id) }); |
| | | const tokenInfo = { |
| | | isRefresh, |
| | | ...info, |
| | | tenantId: user?.tenantId, |
| | | }; |
| | | return jwt.sign(tokenInfo, secret, { |
| | | expiresIn: isRefresh ? refreshExpire : expire, |
| | | }); |
| | | } |
| | | } |
| New file |
| | |
| | | import { BasePlugin } from '@cool-midway/plugin-cli'; |
| | | /** |
| | | * 微信支付 |
| | | */ |
| | | export declare class CoolPlugin extends BasePlugin { |
| | | /** |
| | | * 生成订单号,基于时间戳+唯一字符串+随机数+可选的子ID |
| | | * @param subId 可选,如你的订单ID, 或者用户ID的一些组合 |
| | | * @returns 订单号 |
| | | */ |
| | | createOrderNum(subId?: string): string; |
| | | /** |
| | | * 获得配置 |
| | | */ |
| | | getConfig(): Promise<any>; |
| | | /** |
| | | * 获得微信支付SDK实例 |
| | | * @param config 动态配置 |
| | | * @returns |
| | | */ |
| | | getInstance(config?: { |
| | | appid: string; |
| | | mchid: string; |
| | | key: string; |
| | | notify_url: string; |
| | | publicKey: Buffer; |
| | | privateKey: Buffer; |
| | | }): Promise<any>; |
| | | /** |
| | | * 根据给定的 URL 下载文件并返回一个 Buffer。 |
| | | * @param {string} url 文件的 URL |
| | | * @return {Promise<Buffer>} 返回一个 Promise,解析为文件内容的 Buffer |
| | | */ |
| | | downloadFileAsBuffer(url: any): Promise<Buffer<any>>; |
| | | /** |
| | | * 签名 |
| | | * @param ctx 请求上下文 |
| | | * @param config 动态配置 |
| | | * @returns |
| | | */ |
| | | signVerify( |
| | | ctx: any, |
| | | config?: { |
| | | appid: string; |
| | | mchid: string; |
| | | key: string; |
| | | notify_url: string; |
| | | publicKey: Buffer; |
| | | privateKey: Buffer; |
| | | }, |
| | | ): Promise<any>; |
| | | } |
| | | export declare const Plugin: typeof CoolPlugin; |
| | |
| | | import * as wuliu from './wuliu'; |
| | | import * as pay_wx from './pay-wx'; |
| | | import * as wx from './wx'; |
| | | import * as upload_oss from './upload-oss'; |
| | | import * as sms_ali from './sms-ali'; |
| | | import { BaseUpload, MODETYPE } from './upload'; |
| | | type AnyString = string & {}; |
| | | /** |
| | |
| | | */ |
| | | interface PluginMap { |
| | | upload: BaseUpload; |
| | | 'sms-ali': sms_ali.CoolPlugin; |
| | | 'upload-oss': upload_oss.CoolPlugin; |
| | | wx: wx.CoolPlugin; |
| | | 'pay-wx': pay_wx.CoolPlugin; |
| | | wuliu: wuliu.CoolPlugin; |
| | | } |
| New file |
| | |
| | | import { BasePlugin } from '@cool-midway/plugin-cli'; |
| | | /** |
| | | * 阿里云短信 |
| | | */ |
| | | export declare class CoolPlugin extends BasePlugin { |
| | | /** |
| | | * 发送 |
| | | * @param phone 手机号数组 最多不要超过200个手机号 |
| | | * @param params 参数数组 短信模板参数, 例如: {code: '1234', product: 'yours'} |
| | | * @param config 配置 signName 签名 template 模板 options 其他配置 countryCode 国家区号 默认 +86 |
| | | * @returns 返回结果 |
| | | */ |
| | | send( |
| | | phone: string[], |
| | | params: { |
| | | [key: string]: any; |
| | | }, |
| | | config?: { |
| | | signName: string; |
| | | template: string; |
| | | countryCode?: string; |
| | | options?: { |
| | | [key: string]: any; |
| | | }; |
| | | }, |
| | | ): Promise<any>; |
| | | } |
| | | export declare const Plugin: typeof CoolPlugin; |
| New file |
| | |
| | | import { |
| | | BasePlugin, |
| | | BaseUpload, |
| | | Mode, |
| | | PluginInfo, |
| | | } from '@cool-midway/plugin-cli'; |
| | | import OSS from 'ali-oss'; |
| | | /** |
| | | * 描述 |
| | | */ |
| | | export declare class CoolPlugin extends BasePlugin implements BaseUpload { |
| | | client: OSS; |
| | | /** |
| | | * 初始化 |
| | | * @param pluginInfo |
| | | */ |
| | | init(pluginInfo: PluginInfo): Promise<void>; |
| | | /** |
| | | * 获得上传模式 |
| | | */ |
| | | getMode(): Promise<Mode>; |
| | | /** |
| | | * 获得原始操作对象 |
| | | * @returns |
| | | */ |
| | | getMetaFileObj(): OSS; |
| | | /** |
| | | * 根据给定的 URL 下载文件并返回一个 Buffer。 |
| | | * @param {string} url 文件的 URL |
| | | * @return {Promise<Buffer>} 返回一个 Promise,解析为文件内容的 Buffer |
| | | */ |
| | | downloadFileAsBuffer(url: any): Promise<Buffer<any>>; |
| | | /** |
| | | * 下载并上传 |
| | | * @param url |
| | | * @param fileName 文件名 |
| | | */ |
| | | downAndUpload(url: string, fileName?: string): Promise<string>; |
| | | /** |
| | | * 指定Key(路径)上传 |
| | | * @param filePath 文件路径 |
| | | * @param key 路径一致会覆盖源文件 |
| | | */ |
| | | uploadWithKey(filePath: any, key: any): Promise<string>; |
| | | /** |
| | | * 获得签名 |
| | | * @param ctx |
| | | * @returns |
| | | */ |
| | | upload(ctx?: any): Promise<{ |
| | | signature: string; |
| | | policy: string; |
| | | host: any; |
| | | publicDomain: any; |
| | | OSSAccessKeyId: any; |
| | | success_action_status: number; |
| | | }>; |
| | | } |
| | | export declare const Plugin: typeof CoolPlugin; |
| New file |
| | |
| | | import { BasePlugin } from '@cool-midway/plugin-cli'; |
| | | /** |
| | | * 物料查询 |
| | | */ |
| | | export declare class CoolPlugin extends BasePlugin { |
| | | host: string; |
| | | /** |
| | | * 阿里云市场物流查询 https://market.aliyun.com/products/56928004/cmapi021863.html |
| | | * @param no 快递单号 |
| | | * @param type 快递公司编码,不传则自动识别(准确率95%) |
| | | */ |
| | | query(no: string, type?: string): Promise<any>; |
| | | /** |
| | | * 获得所有快递公司 |
| | | * @returns |
| | | */ |
| | | companyList(): Promise<any>; |
| | | } |
| | | export declare const Plugin: typeof CoolPlugin; |
| New file |
| | |
| | | import { BasePlugin } from '@cool-midway/plugin-cli'; |
| | | import { |
| | | OfficialAccount, |
| | | MiniApp, |
| | | Pay, |
| | | OpenPlatform, |
| | | Work, |
| | | OpenWork, |
| | | } from 'node-easywechat'; |
| | | /** |
| | | * 微信 |
| | | */ |
| | | export declare class CoolPlugin extends BasePlugin { |
| | | /** |
| | | * 公众号 |
| | | * @param config |
| | | * @returns |
| | | */ |
| | | OfficialAccount(config?: any): Promise<any>; |
| | | /** |
| | | * 小程序 |
| | | * @param config |
| | | * @returns |
| | | */ |
| | | MiniApp(config?: any): Promise<any>; |
| | | /** |
| | | * 支付 |
| | | * @param config |
| | | * @returns |
| | | */ |
| | | Pay(config?: any): Promise<any>; |
| | | /** |
| | | * 开放平台 |
| | | * @param config |
| | | * @returns |
| | | */ |
| | | OpenPlatform(config?: any): Promise<any>; |
| | | /** |
| | | * 企业微信 |
| | | * @param config |
| | | * @returns |
| | | */ |
| | | Work(config?: any): Promise<any>; |
| | | /** |
| | | * 企业微信开放平台 |
| | | * @param config |
| | | * @returns |
| | | */ |
| | | OpenWork(config?: any): Promise<any>; |
| | | /** |
| | | * 设置缓存 |
| | | * @param app |
| | | */ |
| | | setCache( |
| | | app: OfficialAccount | MiniApp | Pay | OpenPlatform | Work | OpenWork, |
| | | ): Promise<void>; |
| | | } |
| | | export declare const Plugin: typeof CoolPlugin; |