From 7dcc2997614f849258091f5d66e648317a40d323 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期五, 14 八月 2026 10:12:42 +0800
Subject: [PATCH] add shop module
---
src/modules/goods/service/comment.ts | 62
src/modules/market/controller/app/coupon/user.ts | 48
src/modules/count/service/order.ts | 94
src/modules/cs/service/msg.ts | 140 +
src/configuration.ts | 15
src/modules/market/entity/coupon/info.ts | 41
src/modules/cs/entity/conn.ts | 18
src/modules/count/service/goods.ts | 55
src/modules/info/controller/admin/banner.ts | 15
src/modules/goods/entity/searchKeyword.ts | 14
src/modules/goods/service/type.ts | 30
src/modules/app/controller/admin/goods.ts | 15
src/modules/goods/config.ts | 19
typings/upload-oss.d.ts | 59
src/modules/goods/entity/spec.ts | 32
src/modules/order/service/pay.ts | 238 +
src/modules/count/config.ts | 19
src/modules/info/entity/banner.ts | 23
src/modules/order/queue/order.ts | 33
src/modules/cs/config.ts | 19
src/modules/app/package.json | 5
src/modules/app/entity/complain.ts | 34
src/modules/goods/controller/admin/info.ts | 29
src/modules/goods/controller/app/spec.ts | 23
src/modules/base/entity/sys/log.ts | 9
src/modules/goods/controller/app/type.ts | 29
src/modules/goods/service/info.ts | 56
src/modules/app/db.json | 124 +
src/modules/cs/controller/app/session.ts | 25
src/modules/app/controller/admin/complain.ts | 32
src/modules/app/entity/version.ts | 32
src/modules/order/controller/app/pay.ts | 64
src/modules/market/controller/admin/coupon/user.ts | 26
src/modules/goods/controller/admin/searchKeyword.ts | 14
src/modules/base/db.json | 45
src/modules/cs/socket/middleware/token.ts | 34
src/modules/app/service/feedback.ts | 36
src/modules/user/config.ts | 2
src/modules/goods/controller/admin/comment.ts | 30
src/modules/goods/service/spec.ts | 71
src/modules/cs/controller/admin/msg.ts | 58
src/modules/app/service/complain.ts | 36
src/modules/base/controller/admin/sys/param.ts | 2
src/modules/app/menu.json | 270 ++
src/modules/goods/controller/app/searchKeyword.ts | 14
src/modules/user/controller/admin/info.ts | 2
src/modules/count/service/comm.ts | 71
typings/sms-ali.d.ts | 28
src/modules/app/controller/app/version.ts | 30
src/config/config.prod.ts | 15
src/modules/base/db/tenant.ts | 6
src/modules/cs/socket/io.ts | 46
src/modules/user/controller/app/login.ts | 11
src/modules/cs/service/conn.ts | 57
src/modules/user/service/info.ts | 2
typings/plugin.d.ts | 10
src/modules/order/config.ts | 19
src/modules/order/service/info.ts | 463 +++
src/config/config.local.ts | 2
src/modules/app/service/version.ts | 45
src/modules/base/menu.json | 2500 ++++++++++++-------
src/modules/market/controller/app/coupon/info.ts | 51
src/modules/goods/entity/info.ts | 50
src/modules/cs/entity/msg.ts | 36
src/modules/app/controller/app/feedback.ts | 38
src/modules/market/entity/coupon/user.ts | 22
src/modules/app/entity/goods.ts | 48
src/modules/goods/controller/app/info.ts | 45
src/modules/cs/controller/admin/session.ts | 22
src/modules/base/middleware/authority.ts | 4
src/modules/goods/controller/app/comment.ts | 55
src/modules/order/service/goods.ts | 80
src/modules/market/config.ts | 19
src/modules/order/entity/goods.ts | 51
src/modules/order/controller/admin/info.ts | 88
src/modules/market/controller/admin/coupon/info.ts | 17
src/modules/base/config.ts | 6
src/modules/info/controller/app/banner.ts | 27
src/modules/goods/entity/comment.ts | 29
src/modules/app/controller/admin/version.ts | 17
src/modules/app/controller/app/complain.ts | 38
src/modules/user/service/login.ts | 50
src/modules/count/controller/admin/home.ts | 69
src/modules/app/controller/admin/feedback.ts | 32
typings/pay-wx.d.ts | 53
src/modules/app/entity/feedback.ts | 33
src/modules/market/service/coupon/info.ts | 116
src/modules/market/service/coupon/user.ts | 74
src/config/config.default.ts | 53
src/modules/app/controller/app/goods.ts | 19
src/modules/cs/entity/session.ts | 19
src/modules/order/entity/info.ts | 126 +
src/modules/app/config.ts | 19
src/modules/info/config.ts | 19
src/modules/goods/entity/type.ts | 24
src/comm/utils.ts | 76
typings/wx.d.ts | 58
src/modules/cs/controller/app/msg.ts | 62
src/modules/cs/service/session.ts | 38
src/modules/order/controller/app/info.ts | 86
typings/wuliu.d.ts | 19
src/modules/goods/controller/admin/type.ts | 20
src/modules/goods/controller/admin/spec.ts | 14
src/modules/cs/menu.json | 27
package.json | 56
src/modules/count/service/user.ts | 69
106 files changed, 6,268 insertions(+), 1,102 deletions(-)
diff --git a/package.json b/package.json
index 3a7cf85..bb1553d 100644
--- a/package.json
+++ b/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"
}
diff --git a/src/comm/utils.ts b/src/comm/utils.ts
index e50b3b8..87b438d 100644
--- a/src/comm/utils.ts
+++ b/src/comm/utils.ts
@@ -192,80 +192,4 @@
// 濡傛灉 pattern 鍜� url 鐨勯儴鍒嗘暟閲忎竴鑷达紝鍒欏尮閰嶆垚鍔�
return patternParts.length === urlParts.length;
}
-
- /**
- * 浠庢枃鏈腑鎻愬彇 JSON 瀛楃涓插苟杞崲涓哄璞�
- * @param {string} text - 鍙兘鍖呭惈 JSON 鐨勬枃鏈�
- * @returns {Object|Array|null} - 瑙f瀽鍑虹殑 JSON 瀵硅薄锛屽鏋滄病鏈夋壘鍒版湁鏁� JSON 鍒欒繑鍥� null
- */
- extractJSONFromText(text) {
- if (!text || typeof text !== 'string') {
- return null;
- }
-
- try {
- // 灏濊瘯鐩存帴瑙f瀽鏁翠釜鏂囨湰
- 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
- }
}
diff --git a/src/config/config.default.ts b/src/config/config.default.ts
index 03a7323..1cc32d5 100644
--- a/src/config/config.default.ts
+++ b/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,
+ // 闇�瑕佽繃婊ゅ绉熸埛鐨剈rl, 鏀寔閫氶厤绗︼紝 濡�/admin/**/* 琛ㄧずadmin妯″潡涓嬬殑鎵�鏈夋帴鍙i兘杩涜澶氱鎴疯繃婊�
urls: [],
},
+ // 鍥介檯鍖栭厤缃�
i18n: {
- enable: true,
+ // 鏄惁寮�鍚�
+ enable: false,
+ // 璇█
languages: ['zh-cn', 'zh-tw', 'en'],
},
+ // crud閰嶇疆
crud: {
+ // 鎻掑叆妯″紡锛宻ave涓嶄細鏍¢獙瀛楁(鍏佽浼犲叆涓嶅瓨鍦ㄧ殑瀛楁)锛宨nsert浼氭牎楠屽瓧娈�
upsert: 'save',
+ // 杞垹闄�
softDelete: true,
- },
+ },
} as CoolConfig,
-} as MidwayConfig;
\ No newline at end of file
+} as MidwayConfig;
diff --git a/src/config/config.local.ts b/src/config/config.local.ts
index 3eabef6..80bbc57 100644
--- a/src/config/config.local.ts
+++ b/src/config/config.local.ts
@@ -14,7 +14,7 @@
port: 8806,
username: 'womp',
password: 'Sening@2023',
- database: 'womp',
+ database: 'womp-shop',
// 鑷姩寤鸿〃 娉ㄦ剰锛氱嚎涓婇儴缃茬殑鏃跺�欎笉瑕佷娇鐢紝鏈夊彲鑳藉鑷存暟鎹涪澶�
synchronize: true,
// 鎵撳嵃鏃ュ織
diff --git a/src/config/config.prod.ts b/src/config/config.prod.ts
index df19add..e8f6b4c 100644
--- a/src/config/config.prod.ts
+++ b/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: {
diff --git a/src/configuration.ts b/src/configuration.ts
index 0ba1d73..d367d50 100644
--- a/src/configuration.ts
+++ b/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'],
diff --git a/src/modules/app/config.ts b/src/modules/app/config.ts
new file mode 100644
index 0000000..d0eda0e
--- /dev/null
+++ b/src/modules/app/config.ts
@@ -0,0 +1,19 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 妯″潡閰嶇疆
+ */
+export default () => {
+ return {
+ // 妯″潡鍚嶇О
+ name: '搴旂敤绠$悊',
+ // 妯″潡鎻忚堪
+ description: '鎶曡瘔涓炬姤銆佹剰瑙佸弽棣堛�佹洿鏂板崌绾с�佸椁愯缃瓑',
+ // 涓棿浠讹紝鍙鏈ā鍧楁湁鏁�
+ middlewares: [],
+ // 涓棿浠讹紝鍏ㄥ眬鏈夋晥
+ globalMiddlewares: [],
+ // 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
+ order: 0,
+ } as ModuleConfig;
+};
diff --git a/src/modules/app/controller/admin/complain.ts b/src/modules/app/controller/admin/complain.ts
new file mode 100644
index 0000000..7098936
--- /dev/null
+++ b/src/modules/app/controller/admin/complain.ts
@@ -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 {}
diff --git a/src/modules/app/controller/admin/feedback.ts b/src/modules/app/controller/admin/feedback.ts
new file mode 100644
index 0000000..f15af95
--- /dev/null
+++ b/src/modules/app/controller/admin/feedback.ts
@@ -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 {}
diff --git a/src/modules/app/controller/admin/goods.ts b/src/modules/app/controller/admin/goods.ts
new file mode 100644
index 0000000..f147b42
--- /dev/null
+++ b/src/modules/app/controller/admin/goods.ts
@@ -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 {}
diff --git a/src/modules/app/controller/admin/version.ts b/src/modules/app/controller/admin/version.ts
new file mode 100644
index 0000000..9799680
--- /dev/null
+++ b/src/modules/app/controller/admin/version.ts
@@ -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 {}
diff --git a/src/modules/app/controller/app/complain.ts b/src/modules/app/controller/app/complain.ts
new file mode 100644
index 0000000..aeaaa33
--- /dev/null
+++ b/src/modules/app/controller/app/complain.ts
@@ -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();
+ }
+}
diff --git a/src/modules/app/controller/app/feedback.ts b/src/modules/app/controller/app/feedback.ts
new file mode 100644
index 0000000..17f4805
--- /dev/null
+++ b/src/modules/app/controller/app/feedback.ts
@@ -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();
+ }
+}
diff --git a/src/modules/app/controller/app/goods.ts b/src/modules/app/controller/app/goods.ts
new file mode 100644
index 0000000..d0dd0fb
--- /dev/null
+++ b/src/modules/app/controller/app/goods.ts
@@ -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 {}
diff --git a/src/modules/app/controller/app/version.ts b/src/modules/app/controller/app/version.ts
new file mode 100644
index 0000000..119476c
--- /dev/null
+++ b/src/modules/app/controller/app/version.ts
@@ -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);
+ }
+}
diff --git a/src/modules/app/db.json b/src/modules/app/db.json
new file mode 100644
index 0000000..53fde8a
--- /dev/null
+++ b/src/modules/app/db.json
@@ -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
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/modules/app/entity/complain.ts b/src/modules/app/entity/complain.ts
new file mode 100644
index 0000000..bfb52c6
--- /dev/null
+++ b/src/modules/app/entity/complain.ts
@@ -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: '澶勭悊浜篒D', nullable: true })
+ handlerId: number;
+
+ @Column({ comment: '澶囨敞', type: 'text', nullable: true })
+ remark: string;
+}
diff --git a/src/modules/app/entity/feedback.ts b/src/modules/app/entity/feedback.ts
new file mode 100644
index 0000000..5c5be76
--- /dev/null
+++ b/src/modules/app/entity/feedback.ts
@@ -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: '澶勭悊浜篒D', nullable: true })
+ handlerId: number;
+
+ @Column({ comment: '澶囨敞', type: 'text', nullable: true })
+ remark: string;
+}
diff --git a/src/modules/app/entity/goods.ts b/src/modules/app/entity/goods.ts
new file mode 100644
index 0000000..1ad6287
--- /dev/null
+++ b/src/modules/app/entity/goods.ts
@@ -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;
+}
diff --git a/src/modules/app/entity/version.ts b/src/modules/app/entity/version.ts
new file mode 100644
index 0000000..fa8c39a
--- /dev/null
+++ b/src/modules/app/entity/version.ts
@@ -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;
+}
diff --git a/src/modules/app/menu.json b/src/modules/app/menu.json
new file mode 100644
index 0000000..2906595
--- /dev/null
+++ b/src/modules/app/menu.json
@@ -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": []
+ }
+ ]
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/modules/app/package.json b/src/modules/app/package.json
new file mode 100644
index 0000000..726d311
--- /dev/null
+++ b/src/modules/app/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "semver": "^7.5.4"
+ }
+}
\ No newline at end of file
diff --git a/src/modules/app/service/complain.ts b/src/modules/app/service/complain.ts
new file mode 100644
index 0000000..e92c46e
--- /dev/null
+++ b/src/modules/app/service/complain.ts
@@ -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);
+ }
+}
diff --git a/src/modules/app/service/feedback.ts b/src/modules/app/service/feedback.ts
new file mode 100644
index 0000000..09f3d10
--- /dev/null
+++ b/src/modules/app/service/feedback.ts
@@ -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);
+ }
+}
diff --git a/src/modules/app/service/version.ts b/src/modules/app/service/version.ts
new file mode 100644
index 0000000..349ab31
--- /dev/null
+++ b/src/modules/app/service/version.ts
@@ -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 }
+ );
+ }
+ }
+ }
+}
diff --git a/src/modules/base/config.ts b/src/modules/base/config.ts
index 0218350..a419211 100644
--- a/src/modules/base/config.ts
+++ b/src/modules/base/config.ts
@@ -21,17 +21,17 @@
// 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
order: 10,
// app鍙傛暟閰嶇疆鍏佽璇诲彇鐨刱ey
- allowKeys: [],
+ allowKeys: ['privacyPolicy', 'userAgreement'],
// jwt 鐢熸垚瑙e瘑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,
},
diff --git a/src/modules/base/controller/admin/sys/param.ts b/src/modules/base/controller/admin/sys/param.ts
index 6c3ac88..70da1d4 100644
--- a/src/modules/base/controller/admin/sys/param.ts
+++ b/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);
}
diff --git a/src/modules/base/db.json b/src/modules/base/db.json
index 285598a..f5c591c 100644
--- a/src/modules/base/db.json
+++ b/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
}
]
-}
\ No newline at end of file
+}
diff --git a/src/modules/base/db/tenant.ts b/src/modules/base/db/tenant.ts
index 5b121c5..9683b85 100644
--- a/src/modules/base/db/tenant.ts
+++ b/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}'`);
}
}
diff --git a/src/modules/base/entity/sys/log.ts b/src/modules/base/entity/sys/log.ts
index fd298a5..1581c42 100644
--- a/src/modules/base/entity/sys/log.ts
+++ b/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;
}
diff --git a/src/modules/base/menu.json b/src/modules/base/menu.json
index ac91af3..9fb7d42 100644
--- a/src/modules/base/menu.json
+++ b/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": []
- }
- ]
- }
- ]
- }
-]
\ No newline at end of file
+ {
+ "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": []
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/src/modules/base/middleware/authority.ts b/src/modules/base/middleware/authority.ts
index 2515f94..d0fd5ed 100644
--- a/src/modules/base/middleware/authority.ts
+++ b/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鏂规硶鏉ユ鏌RL鏄惁搴旇琚拷鐣�
const isIgnored = this.ignoreUrls.some(pattern =>
diff --git a/src/modules/count/config.ts b/src/modules/count/config.ts
new file mode 100644
index 0000000..6412d7a
--- /dev/null
+++ b/src/modules/count/config.ts
@@ -0,0 +1,19 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 妯″潡閰嶇疆
+ */
+export default () => {
+ return {
+ // 妯″潡鍚嶇О
+ name: '缁熻鍒嗘瀽',
+ // 妯″潡鎻忚堪
+ description: '璁㈠崟銆佸晢鍝併�佺敤鎴风瓑缁熻',
+ // 涓棿浠讹紝鍙鏈ā鍧楁湁鏁�
+ middlewares: [],
+ // 涓棿浠讹紝鍏ㄥ眬鏈夋晥
+ globalMiddlewares: [],
+ // 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
+ order: 0,
+ } as ModuleConfig;
+};
diff --git a/src/modules/count/controller/admin/home.ts b/src/modules/count/controller/admin/home.ts
new file mode 100644
index 0000000..f50bd08
--- /dev/null
+++ b/src/modules/count/controller/admin/home.ts
@@ -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));
+ }
+}
diff --git a/src/modules/count/service/comm.ts b/src/modules/count/service/comm.ts
new file mode 100644
index 0000000..2de44fe
--- /dev/null
+++ b/src/modules/count/service/comm.ts
@@ -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;
+ }
+}
diff --git a/src/modules/count/service/goods.ts b/src/modules/count/service/goods.ts
new file mode 100644
index 0000000..9636db8
--- /dev/null
+++ b/src/modules/count/service/goods.ts
@@ -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);
+ }
+}
diff --git a/src/modules/count/service/order.ts b/src/modules/count/service/order.ts
new file mode 100644
index 0000000..31e234c
--- /dev/null
+++ b/src/modules/count/service/order.ts
@@ -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)'
+ );
+ }
+ }
+}
diff --git a/src/modules/count/service/user.ts b/src/modules/count/service/user.ts
new file mode 100644
index 0000000..50574d0
--- /dev/null
+++ b/src/modules/count/service/user.ts
@@ -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);
+ }
+}
diff --git a/src/modules/cs/config.ts b/src/modules/cs/config.ts
new file mode 100644
index 0000000..78d2201
--- /dev/null
+++ b/src/modules/cs/config.ts
@@ -0,0 +1,19 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 妯″潡閰嶇疆
+ */
+export default () => {
+ return {
+ // 妯″潡鍚嶇О
+ name: '瀹㈡湇妯″潡',
+ // 妯″潡鎻忚堪
+ description: '瀹㈡湇绯荤粺',
+ // 涓棿浠讹紝鍙鏈ā鍧楁湁鏁�
+ middlewares: [],
+ // 涓棿浠讹紝鍏ㄥ眬鏈夋晥
+ globalMiddlewares: [],
+ // 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
+ order: 0,
+ } as ModuleConfig;
+};
diff --git a/src/modules/cs/controller/admin/msg.ts b/src/modules/cs/controller/admin/msg.ts
new file mode 100644
index 0000000..19740c5
--- /dev/null
+++ b/src/modules/cs/controller/admin/msg.ts
@@ -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();
+ }
+}
diff --git a/src/modules/cs/controller/admin/session.ts b/src/modules/cs/controller/admin/session.ts
new file mode 100644
index 0000000..23817ca
--- /dev/null
+++ b/src/modules/cs/controller/admin/session.ts
@@ -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 {}
diff --git a/src/modules/cs/controller/app/msg.ts b/src/modules/cs/controller/app/msg.ts
new file mode 100644
index 0000000..9aea144
--- /dev/null
+++ b/src/modules/cs/controller/app/msg.ts
@@ -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();
+ }
+}
diff --git a/src/modules/cs/controller/app/session.ts b/src/modules/cs/controller/app/session.ts
new file mode 100644
index 0000000..fe73446
--- /dev/null
+++ b/src/modules/cs/controller/app/session.ts
@@ -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));
+ }
+}
diff --git a/src/modules/cs/entity/conn.ts b/src/modules/cs/entity/conn.ts
new file mode 100644
index 0000000..7577b57
--- /dev/null
+++ b/src/modules/cs/entity/conn.ts
@@ -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;
+}
diff --git a/src/modules/cs/entity/msg.ts b/src/modules/cs/entity/msg.ts
new file mode 100644
index 0000000..c879769
--- /dev/null
+++ b/src/modules/cs/entity/msg.ts
@@ -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;
+}
diff --git a/src/modules/cs/entity/session.ts b/src/modules/cs/entity/session.ts
new file mode 100644
index 0000000..8a83685
--- /dev/null
+++ b/src/modules/cs/entity/session.ts
@@ -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;
+}
diff --git a/src/modules/cs/menu.json b/src/modules/cs/menu.json
new file mode 100644
index 0000000..d2828cd
--- /dev/null
+++ b/src/modules/cs/menu.json
@@ -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": []
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/modules/cs/service/conn.ts b/src/modules/cs/service/conn.ts
new file mode 100644
index 0000000..8910e23
--- /dev/null
+++ b/src/modules/cs/service/conn.ts
@@ -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;
+ }
+}
diff --git a/src/modules/cs/service/msg.ts b/src/modules/cs/service/msg.ts
new file mode 100644
index 0000000..5d68aca
--- /dev/null
+++ b/src/modules/cs/service/msg.ts
@@ -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);
+ }
+ }
+}
diff --git a/src/modules/cs/service/session.ts b/src/modules/cs/service/session.ts
new file mode 100644
index 0000000..3994ad7
--- /dev/null
+++ b/src/modules/cs/service/session.ts
@@ -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;
+ }
+}
diff --git a/src/modules/cs/socket/io.ts b/src/modules/cs/socket/io.ts
new file mode 100644
index 0000000..4986c52
--- /dev/null
+++ b/src/modules/cs/socket/io.ts
@@ -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
+ );
+ }
+}
diff --git a/src/modules/cs/socket/middleware/token.ts b/src/modules/cs/socket/middleware/token.ts
new file mode 100644
index 0000000..42ac237
--- /dev/null
+++ b/src/modules/cs/socket/middleware/token.ts
@@ -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();
+ };
+ }
+}
diff --git a/src/modules/goods/config.ts b/src/modules/goods/config.ts
new file mode 100644
index 0000000..5e280ed
--- /dev/null
+++ b/src/modules/goods/config.ts
@@ -0,0 +1,19 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 妯″潡閰嶇疆
+ */
+export default () => {
+ return {
+ // 妯″潡鍚嶇О
+ name: '鍟嗗搧妯″潡',
+ // 妯″潡鎻忚堪
+ description: '鍟嗗搧銆佸垎绫荤鐞�',
+ // 涓棿浠讹紝鍙鏈ā鍧楁湁鏁�
+ middlewares: [],
+ // 涓棿浠讹紝鍏ㄥ眬鏈夋晥
+ globalMiddlewares: [],
+ // 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
+ order: 0,
+ } as ModuleConfig;
+};
diff --git a/src/modules/goods/controller/admin/comment.ts b/src/modules/goods/controller/admin/comment.ts
new file mode 100644
index 0000000..7b59f14
--- /dev/null
+++ b/src/modules/goods/controller/admin/comment.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/admin/info.ts b/src/modules/goods/controller/admin/info.ts
new file mode 100644
index 0000000..70829d8
--- /dev/null
+++ b/src/modules/goods/controller/admin/info.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/admin/searchKeyword.ts b/src/modules/goods/controller/admin/searchKeyword.ts
new file mode 100644
index 0000000..4f9882f
--- /dev/null
+++ b/src/modules/goods/controller/admin/searchKeyword.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/admin/spec.ts b/src/modules/goods/controller/admin/spec.ts
new file mode 100644
index 0000000..ca9b1d5
--- /dev/null
+++ b/src/modules/goods/controller/admin/spec.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/admin/type.ts b/src/modules/goods/controller/admin/type.ts
new file mode 100644
index 0000000..0801b46
--- /dev/null
+++ b/src/modules/goods/controller/admin/type.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/app/comment.ts b/src/modules/goods/controller/app/comment.ts
new file mode 100644
index 0000000..521c871
--- /dev/null
+++ b/src/modules/goods/controller/app/comment.ts
@@ -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)
+ );
+ }
+}
diff --git a/src/modules/goods/controller/app/info.ts b/src/modules/goods/controller/app/info.ts
new file mode 100644
index 0000000..430ff6b
--- /dev/null
+++ b/src/modules/goods/controller/app/info.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/app/searchKeyword.ts b/src/modules/goods/controller/app/searchKeyword.ts
new file mode 100644
index 0000000..dbbdced
--- /dev/null
+++ b/src/modules/goods/controller/app/searchKeyword.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/app/spec.ts b/src/modules/goods/controller/app/spec.ts
new file mode 100644
index 0000000..5b0829f
--- /dev/null
+++ b/src/modules/goods/controller/app/spec.ts
@@ -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 {}
diff --git a/src/modules/goods/controller/app/type.ts b/src/modules/goods/controller/app/type.ts
new file mode 100644
index 0000000..a6b35ae
--- /dev/null
+++ b/src/modules/goods/controller/app/type.ts
@@ -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 {}
diff --git a/src/modules/goods/entity/comment.ts b/src/modules/goods/entity/comment.ts
new file mode 100644
index 0000000..1f3c76a
--- /dev/null
+++ b/src/modules/goods/entity/comment.ts
@@ -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[];
+}
diff --git a/src/modules/goods/entity/info.ts b/src/modules/goods/entity/info.ts
new file mode 100644
index 0000000..d25d8f0
--- /dev/null
+++ b/src/modules/goods/entity/info.ts
@@ -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[];
+}
diff --git a/src/modules/goods/entity/searchKeyword.ts b/src/modules/goods/entity/searchKeyword.ts
new file mode 100644
index 0000000..4ea4faa
--- /dev/null
+++ b/src/modules/goods/entity/searchKeyword.ts
@@ -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;
+}
diff --git a/src/modules/goods/entity/spec.ts b/src/modules/goods/entity/spec.ts
new file mode 100644
index 0000000..bc29125
--- /dev/null
+++ b/src/modules/goods/entity/spec.ts
@@ -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[];
+}
diff --git a/src/modules/goods/entity/type.ts b/src/modules/goods/entity/type.ts
new file mode 100644
index 0000000..8c12dda
--- /dev/null
+++ b/src/modules/goods/entity/type.ts
@@ -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: '鐖禝D', 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;
+}
diff --git a/src/modules/goods/service/comment.ts b/src/modules/goods/service/comment.ts
new file mode 100644
index 0000000..46c51cc
--- /dev/null
+++ b/src/modules/goods/service/comment.ts
@@ -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);
+ }
+}
diff --git a/src/modules/goods/service/info.ts b/src/modules/goods/service/info.ts
new file mode 100644
index 0000000..7979d5c
--- /dev/null
+++ b/src/modules/goods/service/info.ts
@@ -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;
+ }
+}
diff --git a/src/modules/goods/service/spec.ts b/src/modules/goods/service/spec.ts
new file mode 100644
index 0000000..efa0ac6
--- /dev/null
+++ b/src/modules/goods/service/spec.ts
@@ -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,
+ });
+ }
+ }
+}
diff --git a/src/modules/goods/service/type.ts b/src/modules/goods/service/type.ts
new file mode 100644
index 0000000..8120209
--- /dev/null
+++ b/src/modules/goods/service/type.ts
@@ -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) });
+ }
+}
diff --git a/src/modules/info/config.ts b/src/modules/info/config.ts
new file mode 100644
index 0000000..00fdcaa
--- /dev/null
+++ b/src/modules/info/config.ts
@@ -0,0 +1,19 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 妯″潡閰嶇疆
+ */
+export default () => {
+ return {
+ // 妯″潡鍚嶇О
+ name: '淇℃伅妯″潡',
+ // 妯″潡鎻忚堪
+ description: '杞挱鍥剧瓑',
+ // 涓棿浠讹紝鍙鏈ā鍧楁湁鏁�
+ middlewares: [],
+ // 涓棿浠讹紝鍏ㄥ眬鏈夋晥
+ globalMiddlewares: [],
+ // 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
+ order: 0,
+ } as ModuleConfig;
+};
diff --git a/src/modules/info/controller/admin/banner.ts b/src/modules/info/controller/admin/banner.ts
new file mode 100644
index 0000000..e1d08de
--- /dev/null
+++ b/src/modules/info/controller/admin/banner.ts
@@ -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 {}
diff --git a/src/modules/info/controller/app/banner.ts b/src/modules/info/controller/app/banner.ts
new file mode 100644
index 0000000..3f5926d
--- /dev/null
+++ b/src/modules/info/controller/app/banner.ts
@@ -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 {}
diff --git a/src/modules/info/entity/banner.ts b/src/modules/info/entity/banner.ts
new file mode 100644
index 0000000..d22e1b2
--- /dev/null
+++ b/src/modules/info/entity/banner.ts
@@ -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;
+}
diff --git a/src/modules/market/config.ts b/src/modules/market/config.ts
new file mode 100644
index 0000000..a622185
--- /dev/null
+++ b/src/modules/market/config.ts
@@ -0,0 +1,19 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 妯″潡閰嶇疆
+ */
+export default () => {
+ return {
+ // 妯″潡鍚嶇О
+ name: '钀ラ攢妯″潡',
+ // 妯″潡鎻忚堪
+ description: '浼樻儬鍒�',
+ // 涓棿浠讹紝鍙鏈ā鍧楁湁鏁�
+ middlewares: [],
+ // 涓棿浠讹紝鍏ㄥ眬鏈夋晥
+ globalMiddlewares: [],
+ // 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
+ order: 0,
+ } as ModuleConfig;
+};
diff --git a/src/modules/market/controller/admin/coupon/info.ts b/src/modules/market/controller/admin/coupon/info.ts
new file mode 100644
index 0000000..68ffe09
--- /dev/null
+++ b/src/modules/market/controller/admin/coupon/info.ts
@@ -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 {}
diff --git a/src/modules/market/controller/admin/coupon/user.ts b/src/modules/market/controller/admin/coupon/user.ts
new file mode 100644
index 0000000..0ed0ce1
--- /dev/null
+++ b/src/modules/market/controller/admin/coupon/user.ts
@@ -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 {}
diff --git a/src/modules/market/controller/app/coupon/info.ts b/src/modules/market/controller/app/coupon/info.ts
new file mode 100644
index 0000000..bd996af
--- /dev/null
+++ b/src/modules/market/controller/app/coupon/info.ts
@@ -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 {}
diff --git a/src/modules/market/controller/app/coupon/user.ts b/src/modules/market/controller/app/coupon/user.ts
new file mode 100644
index 0000000..a0e6acd
--- /dev/null
+++ b/src/modules/market/controller/app/coupon/user.ts
@@ -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)
+ );
+ }
+}
diff --git a/src/modules/market/entity/coupon/info.ts b/src/modules/market/entity/coupon/info.ts
new file mode 100644
index 0000000..96adc00
--- /dev/null
+++ b/src/modules/market/entity/coupon/info.ts
@@ -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;
+ };
+}
diff --git a/src/modules/market/entity/coupon/user.ts b/src/modules/market/entity/coupon/user.ts
new file mode 100644
index 0000000..e32f162
--- /dev/null
+++ b/src/modules/market/entity/coupon/user.ts
@@ -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: '浼樻儬鍒窱D' })
+ couponId: number;
+
+ @Column({ comment: '鐘舵�� 0-鏈娇鐢� 1-宸蹭娇鐢�', default: 0 })
+ status: number;
+
+ @Column({ comment: '浣跨敤鏃堕棿', type: 'datetime', nullable: true })
+ useTime: Date;
+}
diff --git a/src/modules/market/service/coupon/info.ts b/src/modules/market/service/coupon/info.ts
new file mode 100644
index 0000000..b163a7e
--- /dev/null
+++ b/src/modules/market/service/coupon/info.ts
@@ -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;
+ }
+}
diff --git a/src/modules/market/service/coupon/user.ts b/src/modules/market/service/coupon/user.ts
new file mode 100644
index 0000000..566a4da
--- /dev/null
+++ b/src/modules/market/service/coupon/user.ts
@@ -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() }
+ );
+ }
+}
diff --git a/src/modules/order/config.ts b/src/modules/order/config.ts
new file mode 100644
index 0000000..5e11cc4
--- /dev/null
+++ b/src/modules/order/config.ts
@@ -0,0 +1,19 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 妯″潡閰嶇疆
+ */
+export default () => {
+ return {
+ // 妯″潡鍚嶇О
+ name: '璁㈠崟绠$悊妯″潡',
+ // 妯″潡鎻忚堪
+ description: '璁㈠崟銆侀��娆俱�佹敮浠樼瓑',
+ // 涓棿浠讹紝鍙鏈ā鍧楁湁鏁�
+ middlewares: [],
+ // 涓棿浠讹紝鍏ㄥ眬鏈夋晥
+ globalMiddlewares: [],
+ // 妯″潡鍔犺浇椤哄簭锛岄粯璁や负0锛屽�艰秺澶ц秺浼樺厛鍔犺浇
+ order: 0,
+ } as ModuleConfig;
+};
diff --git a/src/modules/order/controller/admin/info.ts b/src/modules/order/controller/admin/info.ts
new file mode 100644
index 0000000..21b0c05
--- /dev/null
+++ b/src/modules/order/controller/admin/info.ts
@@ -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));
+ }
+}
diff --git a/src/modules/order/controller/app/info.ts b/src/modules/order/controller/app/info.ts
new file mode 100644
index 0000000..0018a8b
--- /dev/null
+++ b/src/modules/order/controller/app/info.ts
@@ -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));
+ }
+}
diff --git a/src/modules/order/controller/app/pay.ts b/src/modules/order/controller/app/pay.ts
new file mode 100644
index 0000000..ffa1b39
--- /dev/null
+++ b/src/modules/order/controller/app/pay.ts
@@ -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)
+ );
+ }
+}
diff --git a/src/modules/order/entity/goods.ts b/src/modules/order/entity/goods.ts
new file mode 100644
index 0000000..0aa1212
--- /dev/null
+++ b/src/modules/order/entity/goods.ts
@@ -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;
+}
diff --git a/src/modules/order/entity/info.ts b/src/modules/order/entity/info.ts
new file mode 100644
index 0000000..ee48bb0
--- /dev/null
+++ b/src/modules/order/entity/info.ts
@@ -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[];
+}
diff --git a/src/modules/order/queue/order.ts b/src/modules/order/queue/order.ts
new file mode 100644
index 0000000..8e5058c
--- /dev/null
+++ b/src/modules/order/queue/order.ts
@@ -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();
+ }
+}
diff --git a/src/modules/order/service/goods.ts b/src/modules/order/service/goods.ts
new file mode 100644
index 0000000..1b50be6
--- /dev/null
+++ b/src/modules/order/service/goods.ts
@@ -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
+ );
+ }
+ }
+}
diff --git a/src/modules/order/service/info.ts b/src/modules/order/service/info.ts
new file mode 100644
index 0000000..ed47d6b
--- /dev/null
+++ b/src/modules/order/service/info.ts
@@ -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);
+ // 绛涢�夋潯浠剁殑闈炲垎椤祍ql
+ 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];
+ }
+}
diff --git a/src/modules/order/service/pay.ts b/src/modules/order/service/pay.ts
new file mode 100644
index 0000000..9f5d9f4
--- /dev/null
+++ b/src/modules/order/service/pay.ts
@@ -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);
+ }
+}
diff --git a/src/modules/user/config.ts b/src/modules/user/config.ts
index 6c8cf60..b14f922 100644
--- a/src/modules/user/config.ts
+++ b/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;
};
diff --git a/src/modules/user/controller/admin/info.ts b/src/modules/user/controller/admin/info.ts
index 19ef3f3..addf3c4 100644
--- a/src/modules/user/controller/admin/info.ts
+++ b/src/modules/user/controller/admin/info.ts
@@ -29,4 +29,4 @@
},
})
-export class AdminUserInfoController extends BaseController { }
+export class AdminUserInfoController extends BaseController {}
diff --git a/src/modules/user/controller/app/login.ts b/src/modules/user/controller/app/login.ts
index e089df4..3f76431 100644
--- a/src/modules/user/controller/app/login.ts
+++ b/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));
+ }
+
+
}
diff --git a/src/modules/user/service/info.ts b/src/modules/user/service/info.ts
index 58dd8fd..1331c45 100644
--- a/src/modules/user/service/info.ts
+++ b/src/modules/user/service/info.ts
@@ -85,7 +85,7 @@
uuid() + '.png'
);
}
- } catch (err) { }
+ } catch (err) {}
try {
return await this.userInfoEntity.update({ id }, param);
} catch (err) {
diff --git a/src/modules/user/service/login.ts b/src/modules/user/service/login.ts
index 440e5a9..0b8d2f2 100644
--- a/src/modules/user/service/login.ts
+++ b/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,
+ });
+ }
}
diff --git a/typings/pay-wx.d.ts b/typings/pay-wx.d.ts
new file mode 100644
index 0000000..035a8fc
--- /dev/null
+++ b/typings/pay-wx.d.ts
@@ -0,0 +1,53 @@
+import { BasePlugin } from '@cool-midway/plugin-cli';
+/**
+ * 寰俊鏀粯
+ */
+export declare class CoolPlugin extends BasePlugin {
+ /**
+ * 鐢熸垚璁㈠崟鍙凤紝鍩轰簬鏃堕棿鎴�+鍞竴瀛楃涓�+闅忔満鏁�+鍙�夌殑瀛怚D
+ * @param subId 鍙�夛紝濡備綘鐨勮鍗旾D, 鎴栬�呯敤鎴稩D鐨勪竴浜涚粍鍚�
+ * @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;
diff --git a/typings/plugin.d.ts b/typings/plugin.d.ts
index e2ae5a0..14b5d95 100644
--- a/typings/plugin.d.ts
+++ b/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;
}
diff --git a/typings/sms-ali.d.ts b/typings/sms-ali.d.ts
new file mode 100644
index 0000000..13e9b8f
--- /dev/null
+++ b/typings/sms-ali.d.ts
@@ -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;
diff --git a/typings/upload-oss.d.ts b/typings/upload-oss.d.ts
new file mode 100644
index 0000000..3adbe06
--- /dev/null
+++ b/typings/upload-oss.d.ts
@@ -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;
diff --git a/typings/wuliu.d.ts b/typings/wuliu.d.ts
new file mode 100644
index 0000000..188b77d
--- /dev/null
+++ b/typings/wuliu.d.ts
@@ -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;
diff --git a/typings/wx.d.ts b/typings/wx.d.ts
new file mode 100644
index 0000000..a1f8c73
--- /dev/null
+++ b/typings/wx.d.ts
@@ -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;
--
Gitblit v1.9.1