665ba0f9ae078c2859209451a163d10515171d11..3f249e399659dcd09d3fe58071b146e50e45c17f
6 天以前 wangzhibo
为小程序端增加接口
3f249e 对比 | 目录
6 天以前 wangzhibo
add womp-shop database backup
e0b225 对比 | 目录
6 天以前 wangzhibo
add shop module
7dcc29 对比 | 目录
90个文件已添加
24个文件已修改
11816 ■■■■■ 已修改文件
package.json 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/comm/utils.ts 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/config.default.ts 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/config.local.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/config.prod.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/configuration.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/admin/complain.ts 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/admin/feedback.ts 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/admin/goods.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/admin/version.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/app/complain.ts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/app/feedback.ts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/app/goods.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/controller/app/version.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/db.json 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/entity/complain.ts 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/entity/feedback.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/entity/goods.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/entity/version.ts 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/menu.json 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/package.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/service/complain.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/service/feedback.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/service/version.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/config.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/controller/admin/sys/param.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/db.json 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/db/tenant.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/entity/sys/log.ts 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/menu.json 2500 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/middleware/authority.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/count/config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/count/controller/admin/home.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/count/service/comm.ts 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/count/service/goods.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/count/service/order.ts 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/count/service/user.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/controller/admin/msg.ts 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/controller/admin/session.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/controller/app/msg.ts 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/controller/app/session.ts 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/entity/conn.ts 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/entity/msg.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/entity/session.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/menu.json 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/service/conn.ts 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/service/msg.ts 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/service/session.ts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/socket/io.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/socket/middleware/token.ts 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/admin/comment.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/admin/info.ts 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/admin/searchKeyword.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/admin/spec.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/admin/type.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/app/comment.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/app/info.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/app/searchKeyword.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/app/spec.ts 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/controller/app/type.ts 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/entity/comment.ts 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/entity/info.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/entity/searchKeyword.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/entity/spec.ts 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/entity/type.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/service/comment.ts 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/service/info.ts 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/service/spec.ts 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/service/type.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/info/config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/info/controller/admin/banner.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/info/controller/app/banner.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/info/entity/banner.ts 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/controller/admin/coupon/info.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/controller/admin/coupon/user.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/controller/app/coupon/info.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/controller/app/coupon/user.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/entity/coupon/info.ts 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/entity/coupon/user.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/service/coupon/info.ts 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/service/coupon/user.ts 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/controller/admin/info.ts 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/controller/app/info.ts 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/controller/app/pay.ts 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/entity/goods.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/entity/info.ts 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/queue/order.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/service/goods.ts 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/service/info.ts 463 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/service/pay.ts 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/shop/controller/app/recycleappointment.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/shop/controller/app/recycleorder.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/shop/controller/app/recycleorderitem.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/shop/controller/app/transaction.ts 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/shop/service/recycleorder.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/shop/service/transaction.ts 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/user/config.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/user/controller/admin/info.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/user/controller/app/info.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/user/controller/app/login.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/user/service/info.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/user/service/login.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
typings/pay-wx.d.ts 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
typings/plugin.d.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
typings/sms-ali.d.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
typings/upload-oss.d.ts 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
typings/wuliu.d.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
typings/wx.d.ts 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
womp-shop-20260814101500.sql 4169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json
@@ -4,49 +4,54 @@
  "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"
@@ -86,6 +91,5 @@
    "url": "https://cool-js.com"
  },
  "author": "COOL",
  "license": "MIT",
  "packageManager": "pnpm@11.12.0+sha512.820a6fbd0d9f04c226638002aead1e45340a9139dd5dc077c1d83ef44aa2481c8eb6637b4c9aa696a3c7e35ba818e49cf27213e5f2b91138d09b7a3e26e898ba"
  "license": "MIT"
}
src/comm/utils.ts
@@ -192,80 +192,4 @@
    // 如果 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
  }
}
src/config/config.default.ts
@@ -1,19 +1,23 @@
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: {
@@ -27,11 +31,12 @@
      },
    },
  },
  // 文件上传
  upload: {
    fileSize: '200mb',
    whitelist: null,
    ignore: ['/open/push/yingshiyun', "/open/push/cameraEvent"],
  },
  // 缓存 可切换成其他缓存如:redis http://www.midwayjs.org/docs/extensions/caching
  cacheManager: {
    clients: {
      default: {
@@ -43,19 +48,49 @@
      },
    },
  },
  // 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;
src/config/config.local.ts
@@ -14,7 +14,7 @@
        port: 8806,
        username: 'womp',
        password: 'Sening@2023',
        database: 'womp',
        database: 'womp-shop',
        // 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
        synchronize: true,
        // 打印日志
src/config/config.prod.ts
@@ -2,11 +2,26 @@
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: {
src/configuration.ts
@@ -8,7 +8,7 @@
  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';
@@ -18,15 +18,16 @@
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
@@ -40,9 +41,11 @@
    // cool-admin 官方组件 https://cool-js.com
    cool,
    // rpc 微服务 远程调用
    //rpc,
    // rpc,
    // 任务与队列
    //task,
    task,
    // 即时通讯
    socketio,
    {
      component: info,
      enabledEnvironment: ['local', 'prod'],
src/modules/app/config.ts
New file
@@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
 * 模块配置
 */
export default () => {
  return {
    // 模块名称
    name: '应用管理',
    // 模块描述
    description: '投诉举报、意见反馈、更新升级、套餐设置等',
    // 中间件,只对本模块有效
    middlewares: [],
    // 中间件,全局有效
    globalMiddlewares: [],
    // 模块加载顺序,默认为0,值越大越优先加载
    order: 0,
  } as ModuleConfig;
};
src/modules/app/controller/admin/complain.ts
New file
@@ -0,0 +1,32 @@
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 {}
src/modules/app/controller/admin/feedback.ts
New file
@@ -0,0 +1,32 @@
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 {}
src/modules/app/controller/admin/goods.ts
New file
@@ -0,0 +1,15 @@
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 {}
src/modules/app/controller/admin/version.ts
New file
@@ -0,0 +1,17 @@
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 {}
src/modules/app/controller/app/complain.ts
New file
@@ -0,0 +1,38 @@
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();
  }
}
src/modules/app/controller/app/feedback.ts
New file
@@ -0,0 +1,38 @@
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();
  }
}
src/modules/app/controller/app/goods.ts
New file
@@ -0,0 +1,19 @@
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 {}
src/modules/app/controller/app/version.ts
New file
@@ -0,0 +1,30 @@
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);
  }
}
src/modules/app/db.json
New file
@@ -0,0 +1,124 @@
{
    "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
                    }
                ]
            }
        }
    ]
}
src/modules/app/entity/complain.ts
New file
@@ -0,0 +1,34 @@
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;
}
src/modules/app/entity/feedback.ts
New file
@@ -0,0 +1,33 @@
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;
}
src/modules/app/entity/goods.ts
New file
@@ -0,0 +1,48 @@
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;
}
src/modules/app/entity/version.ts
New file
@@ -0,0 +1,32 @@
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;
}
src/modules/app/menu.json
New file
@@ -0,0 +1,270 @@
[
    {
        "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": []
                    }
                ]
            }
        ]
    }
]
src/modules/app/package.json
New file
@@ -0,0 +1,5 @@
{
    "dependencies": {
        "semver": "^7.5.4"
    }
}
src/modules/app/service/complain.ts
New file
@@ -0,0 +1,36 @@
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);
  }
}
src/modules/app/service/feedback.ts
New file
@@ -0,0 +1,36 @@
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);
  }
}
src/modules/app/service/version.ts
New file
@@ -0,0 +1,45 @@
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 }
        );
      }
    }
  }
}
src/modules/base/config.ts
@@ -21,17 +21,17 @@
    // 模块加载顺序,默认为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,
      },
src/modules/base/controller/admin/sys/param.ts
@@ -32,7 +32,7 @@
    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);
  }
src/modules/base/db.json
@@ -1,31 +1,31 @@
{
  "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
      }
  ],
@@ -86,6 +86,7 @@
          "password": "e10adc3949ba59abbe56e057f20f883e",
          "passwordV": 7,
          "nickName": "管理员",
          "headImg": "https://cool-js.com/admin/headimg.jpg",
          "phone": "18000000000",
          "email": "team@cool-js.com",
          "status": 1,
@@ -99,4 +100,4 @@
          "roleId": 1
      }
  ]
}
}
src/modules/base/db/tenant.ts
@@ -163,9 +163,8 @@
    const tenantId = this.getTenantId();
    if (tenantId) {
      queryBuilder.andWhere(
        `${
          queryBuilder.alias ? queryBuilder.alias + '.' : ''
        }tenantId = '${tenantId}'`
        `${queryBuilder.alias ? queryBuilder.alias + '.' : ''
        }tenantId = ${tenantId}`
      );
    }
  }
@@ -195,6 +194,7 @@
    if (!this.tenant?.enable) return;
    const tenantId = this.getTenantId();
    if (tenantId) {
      //queryBuilder.set({ tenantId });
      queryBuilder.andWhere(`tenantId = '${tenantId}'`);
    }
  }
src/modules/base/entity/sys/log.ts
@@ -1,4 +1,4 @@
import { BaseEntity, transformerJson } from '../base';
import { BaseEntity,transformerJson } from '../base';
import { Column, Index, Entity } from 'typeorm';
/**
@@ -18,11 +18,6 @@
  @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;
}
src/modules/base/menu.json
@@ -1,943 +1,1559 @@
[
    {
        "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": []
                  }
              ]
          }
      ]
  }
]
src/modules/base/middleware/authority.ts
@@ -17,8 +17,7 @@
 */
@Middleware()
export class BaseAuthorityMiddleware
  implements IMiddleware<Context, NextFunction>
{
  implements IMiddleware<Context, NextFunction> {
  @Config('koa.globalPrefix')
  prefix;
@@ -68,7 +67,6 @@
              ? departmentIds
              : [];
          }
        } catch (error) { }
        // 使用matchUrl方法来检查URL是否应该被忽略
        const isIgnored = this.ignoreUrls.some(pattern =>
src/modules/count/config.ts
New file
@@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
 * 模块配置
 */
export default () => {
  return {
    // 模块名称
    name: '统计分析',
    // 模块描述
    description: '订单、商品、用户等统计',
    // 中间件,只对本模块有效
    middlewares: [],
    // 中间件,全局有效
    globalMiddlewares: [],
    // 模块加载顺序,默认为0,值越大越优先加载
    order: 0,
  } as ModuleConfig;
};
src/modules/count/controller/admin/home.ts
New file
@@ -0,0 +1,69 @@
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));
  }
}
src/modules/count/service/comm.ts
New file
@@ -0,0 +1,71 @@
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;
  }
}
src/modules/count/service/goods.ts
New file
@@ -0,0 +1,55 @@
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);
  }
}
src/modules/count/service/order.ts
New file
@@ -0,0 +1,94 @@
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)'
      );
    }
  }
}
src/modules/count/service/user.ts
New file
@@ -0,0 +1,69 @@
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);
  }
}
src/modules/cs/config.ts
New file
@@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
 * 模块配置
 */
export default () => {
  return {
    // 模块名称
    name: '客服模块',
    // 模块描述
    description: '客服系统',
    // 中间件,只对本模块有效
    middlewares: [],
    // 中间件,全局有效
    globalMiddlewares: [],
    // 模块加载顺序,默认为0,值越大越优先加载
    order: 0,
  } as ModuleConfig;
};
src/modules/cs/controller/admin/msg.ts
New file
@@ -0,0 +1,58 @@
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();
  }
}
src/modules/cs/controller/admin/session.ts
New file
@@ -0,0 +1,22 @@
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 {}
src/modules/cs/controller/app/msg.ts
New file
@@ -0,0 +1,62 @@
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();
  }
}
src/modules/cs/controller/app/session.ts
New file
@@ -0,0 +1,25 @@
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));
  }
}
src/modules/cs/entity/conn.ts
New file
@@ -0,0 +1,18 @@
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;
}
src/modules/cs/entity/msg.ts
New file
@@ -0,0 +1,36 @@
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;
}
src/modules/cs/entity/session.ts
New file
@@ -0,0 +1,19 @@
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;
}
src/modules/cs/menu.json
New file
@@ -0,0 +1,27 @@
[
    {
        "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": []
            }
        ]
    }
]
src/modules/cs/service/conn.ts
New file
@@ -0,0 +1,57 @@
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;
  }
}
src/modules/cs/service/msg.ts
New file
@@ -0,0 +1,140 @@
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);
    }
  }
}
src/modules/cs/service/session.ts
New file
@@ -0,0 +1,38 @@
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;
  }
}
src/modules/cs/socket/io.ts
New file
@@ -0,0 +1,46 @@
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
    );
  }
}
src/modules/cs/socket/middleware/token.ts
New file
@@ -0,0 +1,34 @@
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();
    };
  }
}
src/modules/goods/config.ts
New file
@@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
 * 模块配置
 */
export default () => {
  return {
    // 模块名称
    name: '商品模块',
    // 模块描述
    description: '商品、分类管理',
    // 中间件,只对本模块有效
    middlewares: [],
    // 中间件,全局有效
    globalMiddlewares: [],
    // 模块加载顺序,默认为0,值越大越优先加载
    order: 0,
  } as ModuleConfig;
};
src/modules/goods/controller/admin/comment.ts
New file
@@ -0,0 +1,30 @@
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 {}
src/modules/goods/controller/admin/info.ts
New file
@@ -0,0 +1,29 @@
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 {}
src/modules/goods/controller/admin/searchKeyword.ts
New file
@@ -0,0 +1,14 @@
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 {}
src/modules/goods/controller/admin/spec.ts
New file
@@ -0,0 +1,14 @@
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 {}
src/modules/goods/controller/admin/type.ts
New file
@@ -0,0 +1,20 @@
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 {}
src/modules/goods/controller/app/comment.ts
New file
@@ -0,0 +1,55 @@
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)
    );
  }
}
src/modules/goods/controller/app/info.ts
New file
@@ -0,0 +1,45 @@
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 {}
src/modules/goods/controller/app/searchKeyword.ts
New file
@@ -0,0 +1,14 @@
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 {}
src/modules/goods/controller/app/spec.ts
New file
@@ -0,0 +1,23 @@
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 {}
src/modules/goods/controller/app/type.ts
New file
@@ -0,0 +1,29 @@
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 {}
src/modules/goods/entity/comment.ts
New file
@@ -0,0 +1,29 @@
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[];
}
src/modules/goods/entity/info.ts
New file
@@ -0,0 +1,50 @@
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[];
}
src/modules/goods/entity/searchKeyword.ts
New file
@@ -0,0 +1,14 @@
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;
}
src/modules/goods/entity/spec.ts
New file
@@ -0,0 +1,32 @@
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[];
}
src/modules/goods/entity/type.ts
New file
@@ -0,0 +1,24 @@
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;
}
src/modules/goods/service/comment.ts
New file
@@ -0,0 +1,62 @@
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);
  }
}
src/modules/goods/service/info.ts
New file
@@ -0,0 +1,56 @@
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;
  }
}
src/modules/goods/service/spec.ts
New file
@@ -0,0 +1,71 @@
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,
      });
    }
  }
}
src/modules/goods/service/type.ts
New file
@@ -0,0 +1,30 @@
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) });
  }
}
src/modules/info/config.ts
New file
@@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
 * 模块配置
 */
export default () => {
  return {
    // 模块名称
    name: '信息模块',
    // 模块描述
    description: '轮播图等',
    // 中间件,只对本模块有效
    middlewares: [],
    // 中间件,全局有效
    globalMiddlewares: [],
    // 模块加载顺序,默认为0,值越大越优先加载
    order: 0,
  } as ModuleConfig;
};
src/modules/info/controller/admin/banner.ts
New file
@@ -0,0 +1,15 @@
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 {}
src/modules/info/controller/app/banner.ts
New file
@@ -0,0 +1,27 @@
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 {}
src/modules/info/entity/banner.ts
New file
@@ -0,0 +1,23 @@
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;
}
src/modules/market/config.ts
New file
@@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
 * 模块配置
 */
export default () => {
  return {
    // 模块名称
    name: '营销模块',
    // 模块描述
    description: '优惠券',
    // 中间件,只对本模块有效
    middlewares: [],
    // 中间件,全局有效
    globalMiddlewares: [],
    // 模块加载顺序,默认为0,值越大越优先加载
    order: 0,
  } as ModuleConfig;
};
src/modules/market/controller/admin/coupon/info.ts
New file
@@ -0,0 +1,17 @@
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 {}
src/modules/market/controller/admin/coupon/user.ts
New file
@@ -0,0 +1,26 @@
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 {}
src/modules/market/controller/app/coupon/info.ts
New file
@@ -0,0 +1,51 @@
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 {}
src/modules/market/controller/app/coupon/user.ts
New file
@@ -0,0 +1,48 @@
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)
    );
  }
}
src/modules/market/entity/coupon/info.ts
New file
@@ -0,0 +1,41 @@
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;
  };
}
src/modules/market/entity/coupon/user.ts
New file
@@ -0,0 +1,22 @@
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;
}
src/modules/market/service/coupon/info.ts
New file
@@ -0,0 +1,116 @@
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;
  }
}
src/modules/market/service/coupon/user.ts
New file
@@ -0,0 +1,74 @@
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() }
    );
  }
}
src/modules/order/config.ts
New file
@@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
 * 模块配置
 */
export default () => {
  return {
    // 模块名称
    name: '订单管理模块',
    // 模块描述
    description: '订单、退款、支付等',
    // 中间件,只对本模块有效
    middlewares: [],
    // 中间件,全局有效
    globalMiddlewares: [],
    // 模块加载顺序,默认为0,值越大越优先加载
    order: 0,
  } as ModuleConfig;
};
src/modules/order/controller/admin/info.ts
New file
@@ -0,0 +1,88 @@
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));
  }
}
src/modules/order/controller/app/info.ts
New file
@@ -0,0 +1,86 @@
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));
  }
}
src/modules/order/controller/app/pay.ts
New file
@@ -0,0 +1,64 @@
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)
    );
  }
}
src/modules/order/entity/goods.ts
New file
@@ -0,0 +1,51 @@
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;
}
src/modules/order/entity/info.ts
New file
@@ -0,0 +1,126 @@
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[];
}
src/modules/order/queue/order.ts
New file
@@ -0,0 +1,33 @@
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();
  }
}
src/modules/order/service/goods.ts
New file
@@ -0,0 +1,80 @@
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
      );
    }
  }
}
src/modules/order/service/info.ts
New file
@@ -0,0 +1,463 @@
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];
  }
}
src/modules/order/service/pay.ts
New file
@@ -0,0 +1,238 @@
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);
  }
}
src/modules/shop/controller/app/recycleappointment.ts
New file
@@ -0,0 +1,33 @@
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;
}
src/modules/shop/controller/app/recycleorder.ts
@@ -11,12 +11,8 @@
  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 {
src/modules/shop/controller/app/recycleorderitem.ts
@@ -2,7 +2,6 @@
import { CoolController, BaseController } from "@cool-midway/core";
import { RecycleOrderItemEntity } from "../../entity/recycleorderitem";
import { RecycleOrderItemService } from "../../service/recycleorderitem";
import { RecycleItemEntity } from "../../entity/recycleitem";
/**
 * 回收订单条目-应用端
@@ -12,15 +11,7 @@
  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 {
src/modules/shop/controller/app/transaction.ts
New file
@@ -0,0 +1,39 @@
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));
  }
}
src/modules/shop/service/recycleorder.ts
@@ -119,11 +119,10 @@
    });
    if (order?.campusUserId) {
      await UserInfoEntity.update(
      await UserInfoEntity.getRepository().increment(
        { unionid: order.campusUserId },
        {
          carbonBalance: () => `carbonBalance + ${totalCarbonPoint}`
        }
        'carbonBalance',
        totalCarbonPoint
      );
    }
src/modules/shop/service/transaction.ts
@@ -1,8 +1,9 @@
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';
/**
 * 碳积分流转记录
@@ -12,4 +13,139 @@
  @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;
  }
}
src/modules/user/config.ts
@@ -28,7 +28,7 @@
      // 刷新token 过期时间,单位秒
      refreshExpire: 60 * 60 * 24 * 30,
      // jwt 秘钥
      secret: 'b4652251-d174-4e8e-9c61-25a091afb328x',
      secret: '5b81d7bc-11cc-485d-8d6b-39d3e7c48f74x',
    },
  } as ModuleConfig;
};
src/modules/user/controller/admin/info.ts
@@ -29,4 +29,4 @@
  },
})
export class AdminUserInfoController extends BaseController { }
export class AdminUserInfoController extends BaseController {}
src/modules/user/controller/app/info.ts
@@ -62,4 +62,15 @@
      )
    );
  }
   @Post('/dashboard', { summary: '获取用户碳积分信息' })
  async dashboard(@Body() body) {
    console.log(body)
    const {unionid} = body;
    return this.ok(
      await this.userInfoService.dashboard(
        unionid
      )
    );
  }
}
src/modules/user/controller/app/login.ts
@@ -103,4 +103,15 @@
  ) {
    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));
  }
}
src/modules/user/service/info.ts
@@ -8,6 +8,8 @@
import { UserInfoEntity } from '../entity/info';
import { UserSmsService } from './sms';
import { UserWxService } from './wx';
import { RecycleOrderEntity } from '../../shop/entity/recycleorder';
/**
 * 用户信息
@@ -16,6 +18,9 @@
export class UserInfoService extends BaseService {
  @InjectEntityModel(UserInfoEntity)
  userInfoEntity: Repository<UserInfoEntity>;
  @InjectEntityModel(RecycleOrderEntity)
  recycleOrderEntity: Repository<RecycleOrderEntity>;
  @Inject()
  pluginService: PluginService;
@@ -129,4 +134,27 @@
    }
    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 }
  }
}
src/modules/user/service/login.ts
@@ -3,6 +3,7 @@
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';
@@ -19,6 +20,9 @@
export class UserLoginService extends BaseService {
  @InjectEntityModel(UserInfoEntity)
  userInfoEntity: Repository<UserInfoEntity>;
  @InjectEntityModel(BasicdataSorterEntity)
  basicdataSorterEntity: Repository<BasicdataSorterEntity>;
  @InjectEntityModel(UserWxEntity)
  userWxEntity: Repository<UserWxEntity>;
@@ -275,6 +279,34 @@
  }
  /**
   * 分拣人员-密码登录
   * @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
@@ -306,4 +338,22 @@
      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,
    });
  }
}
typings/pay-wx.d.ts
New file
@@ -0,0 +1,53 @@
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;
typings/plugin.d.ts
@@ -1,3 +1,8 @@
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 & {};
/**
@@ -5,4 +10,9 @@
 */
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;
}
typings/sms-ali.d.ts
New file
@@ -0,0 +1,28 @@
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;
typings/upload-oss.d.ts
New file
@@ -0,0 +1,59 @@
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;
typings/wuliu.d.ts
New file
@@ -0,0 +1,19 @@
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;
typings/wx.d.ts
New file
@@ -0,0 +1,58 @@
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;
womp-shop-20260814101500.sql
New file
Diff too large