Compare commits

...

2 Commits

Author SHA1 Message Date
541d7842ce 2025-09-30
未读变已读
查询列表排序
2025-09-30 16:41:10 +08:00
51052e59a2 2025-09-30
redis存储未读数据
2025-09-30 13:41:41 +08:00
15 changed files with 461 additions and 51 deletions

193
package-lock.json generated
View File

@@ -9,7 +9,6 @@
"version": "0.0.1", "version": "0.0.1",
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"@nestjs/common": "^11.1.6",
"@nestjs/config": "^4.0.2", "@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.1", "@nestjs/core": "^11.0.1",
"@nestjs/platform-express": "^11.0.1", "@nestjs/platform-express": "^11.0.1",
@@ -17,6 +16,7 @@
"@nestjs/typeorm": "^11.0.0", "@nestjs/typeorm": "^11.0.0",
"@nestjs/websockets": "^11.1.6", "@nestjs/websockets": "^11.1.6",
"cors": "^2.8.5", "cors": "^2.8.5",
"date-fns": "^4.1.0",
"dotenv": "^17.2.2", "dotenv": "^17.2.2",
"minio": "^8.0.6", "minio": "^8.0.6",
"mysql2": "^3.15.0", "mysql2": "^3.15.0",
@@ -673,6 +673,7 @@
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz",
"integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==",
"peer": true,
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/Borewit" "url": "https://github.com/sponsors/Borewit"
@@ -1266,6 +1267,13 @@
} }
} }
}, },
"node_modules/@ioredis/commands": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz",
"integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==",
"optional": true,
"peer": true
},
"node_modules/@isaacs/balanced-match": { "node_modules/@isaacs/balanced-match": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
@@ -2162,6 +2170,7 @@
"version": "11.1.6", "version": "11.1.6",
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.6.tgz", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.6.tgz",
"integrity": "sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==", "integrity": "sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==",
"peer": true,
"dependencies": { "dependencies": {
"file-type": "21.0.0", "file-type": "21.0.0",
"iterare": "1.2.1", "iterare": "1.2.1",
@@ -2530,6 +2539,71 @@
"url": "https://opencollective.com/pkgr" "url": "https://opencollective.com/pkgr"
} }
}, },
"node_modules/@redis/bloom": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.2.tgz",
"integrity": "sha512-855DR0ChetZLarblio5eM0yLwxA9Dqq50t8StXKp5bAtLT0G+rZ+eRzzqxl37sPqQKjUudSYypz55o6nNhbz0A==",
"optional": true,
"peer": true,
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.2"
}
},
"node_modules/@redis/client": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.2.tgz",
"integrity": "sha512-WtMScno3+eBpTac1Uav2zugXEoXqaU23YznwvFgkPwBQVwEHTDgOG7uEAObtZ/Nyn8SmAMbqkEubJaMOvnqdsQ==",
"optional": true,
"peer": true,
"dependencies": {
"cluster-key-slot": "1.1.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@redis/json": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.2.tgz",
"integrity": "sha512-uxpVfas3I0LccBX9rIfDgJ0dBrUa3+0Gc8sEwmQQH0vHi7C1Rx1Qn8Nv1QWz5bohoeIXMICFZRcyDONvum2l/w==",
"optional": true,
"peer": true,
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.2"
}
},
"node_modules/@redis/search": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.2.tgz",
"integrity": "sha512-cNv7HlgayavCBXqPXgaS97DRPVWFznuzsAmmuemi2TMCx5scwLiP50TeZvUS06h/MG96YNPe6A0Zt57yayfxwA==",
"optional": true,
"peer": true,
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.2"
}
},
"node_modules/@redis/time-series": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.2.tgz",
"integrity": "sha512-g2NlHM07fK8H4k+613NBsk3y70R2JIM2dPMSkhIjl2Z17SYvaYKdusz85d7VYOrZBWtDrHV/WD2E3vGu+zni8A==",
"optional": true,
"peer": true,
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.2"
}
},
"node_modules/@sinclair/typebox": { "node_modules/@sinclair/typebox": {
"version": "0.34.41", "version": "0.34.41",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
@@ -2568,6 +2642,7 @@
"version": "0.2.7", "version": "0.2.7",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
"integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==",
"peer": true,
"dependencies": { "dependencies": {
"debug": "^4.4.0", "debug": "^4.4.0",
"fflate": "^0.8.2", "fflate": "^0.8.2",
@@ -2584,7 +2659,8 @@
"node_modules/@tokenizer/token": { "node_modules/@tokenizer/token": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"peer": true
}, },
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.11", "version": "1.0.11",
@@ -4416,6 +4492,16 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/co": { "node_modules/co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -4669,6 +4755,15 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/dayjs": { "node_modules/dayjs": {
"version": "1.11.18", "version": "1.11.18",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz",
@@ -5508,7 +5603,8 @@
"node_modules/fflate": { "node_modules/fflate": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"peer": true
}, },
"node_modules/file-entry-cache": { "node_modules/file-entry-cache": {
"version": "8.0.0", "version": "8.0.0",
@@ -5526,6 +5622,7 @@
"version": "21.0.0", "version": "21.0.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz", "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz",
"integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==", "integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==",
"peer": true,
"dependencies": { "dependencies": {
"@tokenizer/inflate": "^0.2.7", "@tokenizer/inflate": "^0.2.7",
"strtok3": "^10.2.2", "strtok3": "^10.2.2",
@@ -6192,6 +6289,31 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"node_modules/ioredis": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.0.tgz",
"integrity": "sha512-AUXbKn9gvo9hHKvk6LbZJQSKn/qIfkWXrnsyL9Yrf+oeXmla9Nmf6XEumOddyhM8neynpK5oAV6r9r99KBuwzA==",
"optional": true,
"peer": true,
"dependencies": {
"@ioredis/commands": "1.4.0",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.1.0",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
},
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ioredis"
}
},
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -7338,6 +7460,7 @@
"url": "https://buymeacoffee.com/borewit" "url": "https://buymeacoffee.com/borewit"
} }
], ],
"peer": true,
"engines": { "engines": {
"node": ">=13.2.0" "node": ">=13.2.0"
} }
@@ -7371,6 +7494,20 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}, },
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"optional": true,
"peer": true
},
"node_modules/lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
"optional": true,
"peer": true
},
"node_modules/lodash.memoize": { "node_modules/lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -8488,6 +8625,46 @@
"url": "https://paulmillr.com/funding/" "url": "https://paulmillr.com/funding/"
} }
}, },
"node_modules/redis": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-5.8.2.tgz",
"integrity": "sha512-31vunZj07++Y1vcFGcnNWEf5jPoTkGARgfWI4+Tk55vdwHxhAvug8VEtW7Cx+/h47NuJTEg/JL77zAwC6E0OeA==",
"optional": true,
"peer": true,
"dependencies": {
"@redis/bloom": "5.8.2",
"@redis/client": "5.8.2",
"@redis/json": "5.8.2",
"@redis/search": "5.8.2",
"@redis/time-series": "5.8.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"optional": true,
"peer": true,
"engines": {
"node": ">=4"
}
},
"node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"optional": true,
"peer": true,
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/reflect-metadata": { "node_modules/reflect-metadata": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
@@ -9098,6 +9275,13 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
"optional": true,
"peer": true
},
"node_modules/statuses": { "node_modules/statuses": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
@@ -9321,6 +9505,7 @@
"version": "10.3.4", "version": "10.3.4",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz",
"integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==",
"peer": true,
"dependencies": { "dependencies": {
"@tokenizer/token": "^0.3.0" "@tokenizer/token": "^0.3.0"
}, },
@@ -9657,6 +9842,7 @@
"version": "6.1.1", "version": "6.1.1",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz",
"integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==",
"peer": true,
"dependencies": { "dependencies": {
"@borewit/text-codec": "^0.1.0", "@borewit/text-codec": "^0.1.0",
"@tokenizer/token": "^0.3.0", "@tokenizer/token": "^0.3.0",
@@ -10207,6 +10393,7 @@
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
"integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==",
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },

View File

@@ -21,7 +21,6 @@
"generator:db": "typeorm-model-generator -h 192.168.0.4 -d test -p 3306 -u test -x n6M2XrssM2YpM5Mi -e mysql -o ./src/generator_entity" "generator:db": "typeorm-model-generator -h 192.168.0.4 -d test -p 3306 -u test -x n6M2XrssM2YpM5Mi -e mysql -o ./src/generator_entity"
}, },
"dependencies": { "dependencies": {
"@nestjs/common": "^11.1.6",
"@nestjs/config": "^4.0.2", "@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.1", "@nestjs/core": "^11.0.1",
"@nestjs/platform-express": "^11.0.1", "@nestjs/platform-express": "^11.0.1",
@@ -29,6 +28,7 @@
"@nestjs/typeorm": "^11.0.0", "@nestjs/typeorm": "^11.0.0",
"@nestjs/websockets": "^11.1.6", "@nestjs/websockets": "^11.1.6",
"cors": "^2.8.5", "cors": "^2.8.5",
"date-fns": "^4.1.0",
"dotenv": "^17.2.2", "dotenv": "^17.2.2",
"minio": "^8.0.6", "minio": "^8.0.6",
"mysql2": "^3.15.0", "mysql2": "^3.15.0",

View File

@@ -12,6 +12,7 @@ export class UploadController {
@Post('file') @Post('file')
@UseInterceptors(FileInterceptor('file')) @UseInterceptors(FileInterceptor('file'))
async uploadFile(@UploadedFile() file: any) { async uploadFile(@UploadedFile() file: any) {
console.log(typeof file)
let path = "app_records" let path = "app_records"
return await this.fileService.uploadFile( return await this.fileService.uploadFile(
'jurongquan', 'jurongquan',
@@ -21,19 +22,4 @@ export class UploadController {
); );
} }
@Post('files')
@UseInterceptors(FileInterceptor('file'))
async uploadFiles(@UploadedFile() files: any) {
let path = "app_records"
for (let file in files) {
console.log(file)
// this.fileService.uploadFile(
// 'jurongquan',
// file.fileName,
// file.buffer,
// path,
// )
}
}
} }

View File

@@ -1,2 +1,3 @@
export * from './programGroup.controller'; export * from './programGroup.controller';
export * from './file.controller'; export * from './file.controller';
export * from './message.controller'

View File

@@ -0,0 +1,23 @@
import {Body, Controller, Get, Post, Query} from "@nestjs/common";
import {MessageService} from "../service";
import {ProgramGroupMessageEntity, ReadDto} from "../entity";
@Controller('message')
export class MessageController {
constructor(private messageService: MessageService) {
}
// 获取未读消息
@Get("unread")
async getUnread(@Query() readDto: ReadDto) {
return await this.messageService.getUnread(readDto)
}
// 将未读消息置为已读
@Post('read')
async read(@Body() readDto: ReadDto) {
return await this.messageService.read(readDto)
}
}

View File

@@ -26,5 +26,4 @@ export class ProgramGroupController {
async getOne(@Param() programGroup: ProgramGroupEntity) { async getOne(@Param() programGroup: ProgramGroupEntity) {
return await this.programGroupService.getOne(programGroup); return await this.programGroupService.getOne(programGroup);
} }
} }

View File

@@ -0,0 +1,4 @@
export class ReadDto {
groupId: number;
userId: number;
}

View File

@@ -2,3 +2,5 @@ export * from './program.entity';
export * from './users.entity'; export * from './users.entity';
export * from './programGroup.entity'; export * from './programGroup.entity';
export * from './programGroupMessage.entity'; export * from './programGroupMessage.entity';
export * from './dto/read.dto'

View File

@@ -1,4 +1,5 @@
import {Column, Entity, ManyToOne, PrimaryGeneratedColumn} from "typeorm"; import {Column, Entity, ManyToOne, PrimaryGeneratedColumn} from "typeorm";
import {format} from "date-fns";
@Entity("program", {schema: "test"}) @Entity("program", {schema: "test"})
export class ProgramEntity { export class ProgramEntity {
@@ -38,10 +39,22 @@ export class ProgramEntity {
name: "start_date", name: "start_date",
nullable: true, nullable: true,
comment: "开始时间", comment: "开始时间",
transformer: {
to: (value: Date) => value,
from: (value: string) => value ? format(value, "yyyy-MM-dd") : null
}
}) })
startDate: Date | null; startDate: Date | null;
@Column("datetime", {name: "end_date", nullable: true, comment: "结束时间"}) @Column("datetime", {
name: "end_date",
nullable: true,
comment: "结束时间",
transformer: {
to: (value: Date) => value,
from: (value: string) => value ? format(value, "yyyy-MM-dd") : null
}
})
endDate: Date | null; endDate: Date | null;
@Column("varchar", { @Column("varchar", {

View File

@@ -1,21 +1,21 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; import {Column, Entity, PrimaryGeneratedColumn} from "typeorm";
import {ProgramEntity} from "./program.entity"; import {ProgramEntity} from "./program.entity";
@Entity("program_group", { schema: "test" }) @Entity("program_group", {schema: "test"})
export class ProgramGroupEntity { export class ProgramGroupEntity {
@PrimaryGeneratedColumn({ type: "int", name: "group_id", comment: "群组ID" }) @PrimaryGeneratedColumn({type: "int", name: "group_id", comment: "群组ID"})
groupId: number; groupId: number;
@Column("int", { name: "program_id", comment: "项目ID" }) @Column("int", {name: "program_id", comment: "项目ID"})
programId: number; programId: number;
@Column("int", { name: "charge_id", comment: "负责人ID" }) @Column("int", {name: "charge_id", comment: "负责人ID"})
chargeId: number; chargeId: number;
@Column("int", { name: "customer_id", comment: "客服ID" }) @Column("int", {name: "customer_id", comment: "客服ID"})
customerId: number; customerId: number;
@Column("int", { name: "user_id", comment: "用户ID" }) @Column("int", {name: "user_id", comment: "用户ID"})
userId: number; userId: number;
@Column("datetime", { @Column("datetime", {
@@ -25,8 +25,12 @@ export class ProgramGroupEntity {
}) })
createTime: Date; createTime: Date;
// 虚拟字段
program: ProgramEntity; program: ProgramEntity;
page: number = 1; page: number = 1;
size: number = 10; size: number = 10;
userUnread: any
chargeUnread: any
} }

View File

@@ -10,6 +10,7 @@ import {Server, Socket} from 'socket.io';
import {ProgramGroupEntity, ProgramGroupMessageEntity, UsersEntity} from "../entity"; import {ProgramGroupEntity, ProgramGroupMessageEntity, UsersEntity} from "../entity";
import {InjectRepository} from "@nestjs/typeorm"; import {InjectRepository} from "@nestjs/typeorm";
import {Repository} from "typeorm"; import {Repository} from "typeorm";
import {RedisService} from "./redis.service";
@WebSocketGateway({ @WebSocketGateway({
cors: { cors: {
@@ -22,6 +23,7 @@ export class ChatGateway implements OnGatewayInit {
@InjectRepository(ProgramGroupMessageEntity) private readonly messageRepository: Repository<ProgramGroupMessageEntity>, @InjectRepository(ProgramGroupMessageEntity) private readonly messageRepository: Repository<ProgramGroupMessageEntity>,
@InjectRepository(ProgramGroupEntity) private readonly groupRepository: Repository<ProgramGroupEntity>, @InjectRepository(ProgramGroupEntity) private readonly groupRepository: Repository<ProgramGroupEntity>,
@InjectRepository(UsersEntity) private readonly usersRepository: Repository<UsersEntity>, @InjectRepository(UsersEntity) private readonly usersRepository: Repository<UsersEntity>,
private readonly redisService: RedisService
) { ) {
} }
@@ -46,6 +48,7 @@ export class ChatGateway implements OnGatewayInit {
}) })
if (group != null) { if (group != null) {
client.join(group.groupId.toString()); client.join(group.groupId.toString());
console.log(`客户端 ${client.id} 加入连接`)
} }
} }
@@ -64,7 +67,96 @@ export class ChatGateway implements OnGatewayInit {
} }
}) })
// 不在线用户
let userIds = await this.activeUser(data.groupId)
// 存储未读消息
if (userIds.length != 0) {
// 保存未读消息 redis
console.log(userIds)
for (let i = 0; i < userIds.length; i++) {
let item = userIds[i]
// 先取再存
let redisData = await this.redisService.getValue(`${data.groupId}_${item}`)
if (redisData == null) {
this.redisService.setValue(`${data.groupId}_${item}`, JSON.stringify([saveMessage]))
} else {
let arr: any = []
arr = JSON.parse(redisData)
arr.push(saveMessage)
this.redisService.setValue(`${data.groupId}_${item}`, JSON.stringify(arr))
}
}
}
this.server.to(data.groupId.toString()).emit('serverMsg', saveMessage) this.server.to(data.groupId.toString()).emit('serverMsg', saveMessage)
} }
async activeUser(roomId: any) {
// 获取房间应该存在的用户
let roomUsers = await this.groupRepository.findOne({
where: {
groupId: roomId
}
})
let userIdArr = Object.values((this.server.engine as any).clients).map(item => {
return (item as any).request._query.userId;
});
userIdArr = Array.from(new Set(userIdArr));
if (roomUsers != null) {
if (userIdArr.includes(roomUsers.userId.toString())) {
userIdArr.splice(userIdArr.indexOf(roomUsers.userId.toString()), 1)
} else {
userIdArr.push(roomUsers.userId.toString())
}
if (userIdArr.includes(roomUsers.chargeId.toString())) {
userIdArr.splice(userIdArr.indexOf(roomUsers.chargeId.toString()), 1)
} else {
userIdArr.push(roomUsers.chargeId.toString())
}
if (userIdArr.includes(roomUsers.customerId.toString())) {
userIdArr.splice(userIdArr.indexOf(roomUsers.customerId.toString()), 1)
} else {
userIdArr.push(roomUsers.customerId.toString())
}
}
return userIdArr;
}
/**
* 断开连接
* @param client
*/
@SubscribeMessage('leaveGroup')
async handleLeaveGroup(@ConnectedSocket() client: Socket, @MessageBody() groupEntity: ProgramGroupEntity): Promise<any> {
client.leave(groupEntity.groupId.toString())
client.disconnect(true)
console.log(`客户端 ${client.id} 断开连接`)
// let userIdArr = Object.values((this.server.engine as any).clients).map(item => {
// return (item as any).request._query.userId;
// });
// console.log(userIdArr)
}
// 手动清理所有连接
cleanupConnections() {
const rooms = this.server.sockets.adapter.rooms;
const sids = this.server.sockets.adapter.sids;
console.log('Current rooms:', rooms.size);
console.log('Current sids:', sids.size);
// 强制断开所有连接
this.server.sockets.sockets.forEach((socket) => {
socket.disconnect(true);
});
}
@SubscribeMessage('cleanup')
handleCleanup(@ConnectedSocket() client: Socket) {
this.cleanupConnections();
client.emit('cleanup_complete');
}
} }

View File

@@ -1,3 +1,5 @@
export * from './programGroupService.service'; export * from './programGroup.service';
export * from './chat.gateway'; export * from './chat.gateway';
export * from './file.service'; export * from './file.service';
export * from './redis.service'
export * from './message.service'

View File

@@ -0,0 +1,23 @@
import {Injectable} from "@nestjs/common";
import {ReadDto} from "../entity";
import {RedisService} from "./redis.service";
import {ResultData} from "../const/result";
@Injectable()
export class MessageService {
constructor(private readonly redisService: RedisService) {
}
// 获取未读消息
async getUnread(readDto: ReadDto) {
let data = await this.redisService.getValue(`${readDto.groupId}_${readDto.userId}`)
data = data == null ? [] : JSON.parse(data)
return ResultData.success(data)
}
// 未读 -> 已读
async read(readDto: ReadDto) {
this.redisService.delValue(`${readDto.groupId}_${readDto.userId}`)
return ResultData.success()
}
}

View File

@@ -1,9 +1,10 @@
import {Injectable} from '@nestjs/common'; import {Injectable} from '@nestjs/common';
import {InjectRepository} from "@nestjs/typeorm"; import {InjectRepository} from "@nestjs/typeorm";
import {ProgramEntity, ProgramGroupEntity, UsersEntity} from "../entity"; import {ProgramEntity, ProgramGroupEntity, ProgramGroupMessageEntity, UsersEntity} from "../entity";
import {Repository} from "typeorm"; import {Repository} from "typeorm";
import {ResultData} from "../const/result"; import {ResultData} from "../const/result";
import {isEmptyString} from "../utils/common"; import {isEmptyString} from "../utils/common";
import {RedisService} from "./redis.service";
@Injectable() @Injectable()
export class ProgramGroupService { export class ProgramGroupService {
@@ -12,6 +13,7 @@ export class ProgramGroupService {
@InjectRepository(ProgramGroupEntity) private readonly programGroupRepository: Repository<ProgramGroupEntity>, @InjectRepository(ProgramGroupEntity) private readonly programGroupRepository: Repository<ProgramGroupEntity>,
@InjectRepository(ProgramEntity) private readonly programRepository: Repository<ProgramEntity>, @InjectRepository(ProgramEntity) private readonly programRepository: Repository<ProgramEntity>,
@InjectRepository(UsersEntity) private readonly usersRepository: Repository<UsersEntity>, @InjectRepository(UsersEntity) private readonly usersRepository: Repository<UsersEntity>,
private readonly redisService: RedisService,
) { ) {
} }
@@ -98,22 +100,52 @@ export class ProgramGroupService {
"program", "program",
"group.program_id = program.id" "group.program_id = program.id"
) )
queryBuilder.leftJoinAndMapOne(
"group.user",
UsersEntity,
"user",
"group.user_id = user.id"
)
if (programGroup.customerId != null){ // 查询最新消息
// queryBuilder.leftJoinAndMapOne(
// "group.latest_message",
// ProgramGroupMessageEntity,
// "message",
// `
// message.group_id = group.group_id and
// group.group_id = (
// select group_id from program_group_message
// where program_group_message.group_id = group.group_id
// order by create_time desc
// limit 1
// )
// `)
queryBuilder.addSelect(subQuery => {
return subQuery
.select("max(message.create_time)")
.from(ProgramGroupMessageEntity, "message")
.where("message.group_id = group.group_id");
}, "latest_message_time")
if (programGroup.customerId != null) {
queryBuilder.andWhere("group.customer_id = :customerId", {customerId: programGroup.customerId}) queryBuilder.andWhere("group.customer_id = :customerId", {customerId: programGroup.customerId})
} }
if (programGroup.userId != null){ if (programGroup.userId != null) {
queryBuilder.orWhere("group.user_id = :userId", {userId: programGroup.userId}) queryBuilder.orWhere("group.user_id = :userId", {userId: programGroup.userId})
queryBuilder.orWhere("group.charge_id = :charge_id", {charge_id: programGroup.userId}) queryBuilder.orWhere("group.charge_id = :charge_id", {charge_id: programGroup.userId})
} }
queryBuilder queryBuilder
.orderBy('program.id', 'DESC') .orderBy("latest_message_time", 'DESC')
.take(programGroup.size || 10) .take(programGroup.size || 10)
.skip(offset || 0); .skip(offset || 0);
const [items, total] = await queryBuilder.getManyAndCount(); let [items, total] = await queryBuilder.getManyAndCount();
// 将未读数据(redis)塞入数据(list)中
items = await this.searchUnRead(items)
let data = { let data = {
list: items, list: items,
@@ -141,4 +173,17 @@ export class ProgramGroupService {
return ResultData.fail(500, err); return ResultData.fail(500, err);
} }
} }
// 处理未读消息
// 返回用户和项目负责人的未读消息
async searchUnRead(list: ProgramGroupEntity[]) {
for (let i = 0; i < list.length; i++) {
let item = list[i];
let userUnreadValue = await this.redisService.getValue(`${item.groupId}_${item.userId}`)
let chargeUnreadValue = await this.redisService.getValue(`${item.groupId}_${item.chargeId}`)
item.userUnread = userUnreadValue == null ? [] : JSON.parse(userUnreadValue)
item.chargeUnread = chargeUnreadValue == null ? [] : JSON.parse(chargeUnreadValue)
}
return list
}
} }

View File

@@ -0,0 +1,29 @@
import {Injectable} from "@nestjs/common";
import Redis from 'ioredis';
@Injectable()
export class RedisService {
private readonly redisClient: Redis;
constructor() {
this.redisClient = new Redis({
host: '192.168.0.4',
port: 6379,
password: 'redis_P2bfmi',
username: 'default'
})
}
setValue(key: string, value: string){
return this.redisClient.set(key, value);
}
getValue(key: string) {
return this.redisClient.get(key);
}
delValue(key: string) {
return this.redisClient.del(key);
}
}