├── .gitignore ├── .snyk ├── LICENSE ├── README.md ├── ormconfig.js ├── package-lock.json ├── package.json ├── renovate.json ├── src ├── articles │ ├── controllers │ │ ├── article.controller.ts │ │ ├── classify-item.controller.ts │ │ ├── classify.controller.ts │ │ ├── item.controller.ts │ │ ├── message.controller.ts │ │ └── user-message.controller.ts │ ├── entities │ │ ├── art-info.entity.ts │ │ ├── article.entity.ts │ │ ├── classify-item.entity.ts │ │ ├── classify.entity.ts │ │ ├── item.entity.ts │ │ ├── message.entity.ts │ │ └── user-message.entity.ts │ ├── interfaces │ │ ├── article.interface.ts │ │ ├── classify-item.interface.ts │ │ ├── classify.interface.ts │ │ ├── item.interface.ts │ │ └── user.interface.ts │ └── services │ │ ├── article.service.ts │ │ ├── classify-item.service.ts │ │ ├── classify.service.ts │ │ ├── item.service.ts │ │ ├── message.service.ts │ │ └── user-message.service.ts ├── cms.module.ts ├── grpc.client-factory.ts ├── grpc │ ├── generated.ts │ └── protobufs │ │ └── nt_module_user.proto ├── main.ts ├── nt_module_cms.proto ├── pages │ ├── controllers │ │ ├── page-sort.controller.ts │ │ └── page.controller.ts │ ├── entities │ │ ├── content.entity.ts │ │ ├── page-sort.entity.ts │ │ └── page.entity.ts │ ├── interfaces │ │ ├── page-sort.interface.ts │ │ └── page.interface.ts │ └── services │ │ ├── page-sort.service.ts │ │ └── page.service.ts └── plugin │ ├── comment │ ├── controllers │ │ ├── comment-properties.controller.ts │ │ └── discuss.controller.ts │ ├── entities │ │ ├── comment-properties.entity.ts │ │ └── discuss.entity.ts │ ├── interfaces │ │ └── discuss.interface.ts │ └── services │ │ ├── comment-properties.service.ts │ │ └── discuss.service.ts │ ├── message-board │ ├── controllers │ │ ├── board-item.controller.ts │ │ ├── leaveword.conteroller.ts │ │ └── message-board.controller.ts │ ├── entities │ │ ├── board-item.entity.ts │ │ ├── leaveword-info.entity.ts │ │ ├── leaveword.entity.ts │ │ └── message-board.entity.ts │ ├── interfaces │ │ ├── leaveword.interface.ts │ │ └── message-board.interface.ts │ └── services │ │ ├── board-item.service.ts │ │ ├── leaveword.service.ts │ │ └── message-board.service.ts │ └── upyun │ ├── controllers │ └── upyun.controller.ts │ ├── entities │ ├── abstract.file.ts │ ├── audio.config.entity.ts │ ├── audio.entity.ts │ ├── bucket.entity.ts │ ├── document.entity.ts │ ├── file.entity.ts │ ├── image.config.entity.ts │ ├── image.entity.ts │ ├── video.config.entity.ts │ └── video.entity.ts │ ├── interface │ ├── config │ │ ├── audio.format.config.ts │ │ ├── bucket.config.ts │ │ ├── enable.image.watermark.config.ts │ │ ├── image.format.config.ts │ │ ├── image.watermark.config.ts │ │ └── video.format.config.ts │ ├── data.ts │ └── file │ │ ├── all.body.ts │ │ ├── all.data.ts │ │ ├── download.process.data.ts │ │ ├── file.location.body.ts │ │ ├── image.process.info.ts │ │ ├── one.body.ts │ │ ├── one.data.ts │ │ ├── policy.ts │ │ ├── upload.process.body.ts │ │ └── upload.process.data.ts │ ├── services │ ├── config.service.ts │ └── upyun.service.ts │ └── util │ ├── allow.extension.ts │ ├── auth.util.ts │ ├── file.util.ts │ ├── kind.util.ts │ ├── process.string.util.ts │ └── restful.util.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node,webstorm 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | ### WebStorm ### 103 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 104 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 105 | 106 | # User-specific stuff 107 | .idea/**/workspace.xml 108 | .idea/**/tasks.xml 109 | .idea/**/usage.statistics.xml 110 | .idea/**/dictionaries 111 | .idea/**/shelf 112 | 113 | # Sensitive or high-churn files 114 | .idea/**/dataSources/ 115 | .idea/**/dataSources.ids 116 | .idea/**/dataSources.local.xml 117 | .idea/**/sqlDataSources.xml 118 | .idea/**/dynamic.xml 119 | .idea/**/uiDesigner.xml 120 | .idea/**/dbnavigator.xml 121 | 122 | # Gradle 123 | .idea/**/gradle.xml 124 | .idea/**/libraries 125 | 126 | # Gradle and Maven with auto-import 127 | # When using Gradle or Maven with auto-import, you should exclude module files, 128 | # since they will be recreated, and may cause churn. Uncomment if using 129 | # auto-import. 130 | # .idea/modules.xml 131 | # .idea/*.iml 132 | # .idea/modules 133 | 134 | # CMake 135 | cmake-build-*/ 136 | 137 | # Mongo Explorer plugin 138 | .idea/**/mongoSettings.xml 139 | 140 | # File-based project format 141 | *.iws 142 | 143 | # IntelliJ 144 | out/ 145 | 146 | # mpeltonen/sbt-idea plugin 147 | .idea_modules/ 148 | 149 | # JIRA plugin 150 | atlassian-ide-plugin.xml 151 | 152 | # Cursive Clojure plugin 153 | .idea/replstate.xml 154 | 155 | # Crashlytics plugin (for Android Studio and IntelliJ) 156 | com_crashlytics_export_strings.xml 157 | crashlytics.properties 158 | crashlytics-build.properties 159 | fabric.properties 160 | 161 | # Editor-based Rest Client 162 | .idea/httpRequests 163 | 164 | ### WebStorm Patch ### 165 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 166 | 167 | # *.iml 168 | # modules.xml 169 | # .idea/misc.xml 170 | # *.ipr 171 | 172 | # Sonarlint plugin 173 | .idea/sonarlint 174 | 175 | 176 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node,webstorm 177 | 178 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 179 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.13.3 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | 'npm:lodash:20180130': 7 | - rxjs-grpc > jscodeshift > babel-core > lodash: 8 | patched: '2019-05-05T08:26:16.867Z' 9 | - rxjs-grpc > jscodeshift > babel-core > babel-plugin-proto-to-assign > lodash: 10 | patched: '2019-05-05T08:26:16.867Z' 11 | 'npm:minimatch:20160620': 12 | - rxjs-grpc > jscodeshift > babel-core > minimatch: 13 | patched: '2019-05-05T08:26:16.867Z' 14 | SNYK-JS-AXIOS-174505: 15 | - '@nestjs/common > axios': 16 | patched: '2019-05-06T05:55:23.872Z' 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Shaanxi Benchu Network Technology Co., Ltd 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notadd CMS模块(Grpc 版本) 2 | 3 | ## 功能 4 | 5 | - [x] 文章管理 6 | - [x] 文章分类管理 7 | - [x] 系统消息管理 8 | 9 | ## 使用说明 10 | 11 | 在应用程序中的GRPC客户端文件中注入CMS模块并导入proto文件,使用GRPC通信,再由客户端提供Graphql接口即可调用CMS模块的所有接口。 12 | 13 | CMS模块目前所有接口均无限制,如需要权限限制,可以参考用户中心模块的资源定义及权限定义。(https://github.com/notadd/nt-module-user/blob/graphql-api/README_zh.md) 14 | 15 | ##接口说明 16 | 17 | 当前版本CMS模块提供了管理功能所需的基本接口,以下介绍常用的接口逻辑 18 | 19 | ### 文章分类 20 | 21 | - `AddClassify` 添加文章分类 22 | - `DelClassify` 删除指定分类 23 | - `UpdateClassify` 更新指定分类信息 24 | - `GetAllClassify` 获得所有分类 25 | 26 | > tip:系统已自动创建了一个最顶级分类即总分类,再创建分类时上级分类id传1即可。 27 | 28 | ### 文章 29 | 30 | - `CreateArticle` 添加文章 31 | - `UpdateArticle` 修改文章 32 | - `RecycleArticleByIds` 将指定文章放入回收站 33 | - `DeleteArticleByIds` 将回收站中指定文章永久删除 34 | - `RecoverArticleByIds` 将回收站中指定文章恢复 35 | - `AuditArticle` 审核文章 36 | - `GetAllArticle` 查看所有文章 37 | - `GetArticleById` 查看指定文章 38 | 39 | ### 消息通知 40 | 41 | - `CreateMessage` 创建消息 42 | - `DeleteMessageById` 批量删除指定消息 43 | - `GetAllMessage` 查看所有消息 -------------------------------------------------------------------------------- /ormconfig.js: -------------------------------------------------------------------------------- 1 | const SOURCE_PATH = process.env.NODE_ENV === 'development' ? 'packages' : 'src'; 2 | module.exports= { 3 | "type": "postgres", 4 | "host": "localhost", 5 | "port": 5432, 6 | "username": "postgres", 7 | "password": "123456", 8 | "database": "grpc", 9 | "entities": [`${SOURCE_PATH}/**/**.entity{.ts,.js}`], 10 | "synchronize": true, 11 | "logging":null, 12 | "logger":"simple-console" 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nt-module-cms", 3 | "version": "1.0.0", 4 | "description": "The cms module for notadd application.", 5 | "scripts": { 6 | "start": "ts-node -r tsconfig-paths/register src/main.ts", 7 | "start:watch": "nodemon", 8 | "debug": "node --inspect-brk -r ts-node/register src/main.ts", 9 | "debug:watch": "nodemon --config nodemon-debug.json", 10 | "check": "tslint -p tsconfig.json -c tslint.json", 11 | "fix": "tslint -p tsconfig.json -c tslint.json --fix", 12 | "grpc:gen": "./node_modules/.bin/rxjs-grpc -o src/grpc/generated.ts src/grpc/protobufs/*.proto", 13 | "snyk-protect": "snyk protect", 14 | "prepare": "npm run snyk-protect" 15 | }, 16 | "author": "notadd", 17 | "license": "Apache-2.0", 18 | "private": false, 19 | "dependencies": { 20 | "@grpc/proto-loader": "0.5.0", 21 | "@nestjs/common": "5.4.1", 22 | "@nestjs/core": "5.4.1", 23 | "@nestjs/microservices": "5.4.1", 24 | "@nestjs/typeorm": "5.2.2", 25 | "grpc": "1.17.0-pre1", 26 | "moment": "2.22.2", 27 | "pg": "7.7.1", 28 | "reflect-metadata": "0.1.12", 29 | "rxjs": "6.3.3", 30 | "rxjs-grpc": "1.0.0-rxjs6.0", 31 | "typeorm": "0.2.17", 32 | "typescript": "3.2.2", 33 | "underscore": "1.9.1", 34 | "snyk": "1.163.3" 35 | }, 36 | "devDependencies": { 37 | "@types/node": "10.12.12", 38 | "nodemon": "1.18.7", 39 | "ts-node": "7.0.1", 40 | "tsconfig-paths": "3.7.0", 41 | "tslint": "5.11.0" 42 | }, 43 | "snyk": true 44 | } 45 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/articles/controllers/article.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { ArticleService } from "../services/article.service"; 4 | import { inputArticle, updateArticle } from "../interfaces/article.interface"; 5 | 6 | @Controller() 7 | export class ArticleController { 8 | constructor( 9 | @Inject(ArticleService) private readonly artService: ArticleService 10 | ) { } 11 | 12 | @GrpcMethod('ArticleService') 13 | async createArticle(body: { createArtInput: inputArticle }) { 14 | await this.artService.createArticle(body.createArtInput); 15 | return { code: 200, message: '新建成功!' }; 16 | } 17 | 18 | @GrpcMethod('ArticleService') 19 | async updateArticle(body: { updateArtInput: updateArticle }) { 20 | await this.artService.updateArticle(body.updateArtInput); 21 | return { code: 200, message: '修改成功!' }; 22 | } 23 | 24 | @GrpcMethod('ArticleService') 25 | async recycleArticleByIds(body: { ids: number[] }) { 26 | await this.artService.recycleArticleByIds(body.ids); 27 | return { code: 200, message: '移至回收站成功!' }; 28 | } 29 | 30 | @GrpcMethod('ArticleService') 31 | async deleteArticleByIds(body: { ids: number[] }) { 32 | await this.artService.deleteArticleByIds(body.ids); 33 | return { code: 200, message: '删除成功!' } 34 | } 35 | 36 | @GrpcMethod('ArticleService') 37 | async recoverArticleByIds(body: { ids: number[] }) { 38 | await this.artService.recoverArticleByIds(body.ids); 39 | return { code: 200, message: '恢复文章成功!' }; 40 | } 41 | 42 | @GrpcMethod('ArticleService') 43 | async auditArticle(body: { ids: number[], op: number, refuseReason: string }) { 44 | await this.artService.auditArticle(body.ids, body.op, body.refuseReason); 45 | return { code: 200, message: '审核成功!' }; 46 | } 47 | 48 | @GrpcMethod('ArticleService') 49 | async getAllArticle(body: { classifyId: number, createdAt: string, endTime: string, title: string, username: string, top:boolean, pageNumber: number, pageSize: number }) { 50 | const data = (await this.artService.getAllArticle(body.classifyId, body.createdAt, body.endTime, body.title, body.username, body.top, body.pageNumber, body.pageSize)).exist; 51 | const total = (await this.artService.getAllArticle(body.classifyId, body.createdAt, body.endTime, body.title, body.username, body.top, body.pageNumber, body.pageSize)).total; 52 | return { code: 200, message: '查询成功', data, total }; 53 | } 54 | 55 | @GrpcMethod('ArticleService') 56 | async userGetArticles(body: { classifyId: number, pageNumber: number, pageSize: number }) { 57 | const data = (await this.artService.userGetArticles(body.classifyId, body.pageNumber, body.pageSize)).exist; 58 | const total = (await this.artService.userGetArticles(body.classifyId, body.pageNumber, body.pageSize)).total; 59 | return { code: 200, message: '查询成功', data, total }; 60 | } 61 | 62 | @GrpcMethod('ArticleService') 63 | async getRecycleArticle(body: { classifyId: number, createdAt: string, endTime: string, title: string, username: string, top:boolean, pageNumber: number, pageSize: number }) { 64 | const data = (await this.artService.getRecycleArticle(body.classifyId, body.createdAt, body.endTime, body.title, body.username, body.top, body.pageNumber, body.pageSize)).exist; 65 | const total = (await this.artService.getRecycleArticle(body.classifyId, body.createdAt, body.endTime, body.title, body.username, body.top, body.pageNumber, body.pageSize)).total; 66 | return { code: 200, message: '查询成功!', data, total }; 67 | } 68 | 69 | @GrpcMethod('ArticleService') 70 | async getArticleById(body: { id: number }) { 71 | const data = await this.artService.getArticleById(body.id); 72 | return { code: 200, message: '查询成功!', data } 73 | } 74 | 75 | @GrpcMethod('ArticleService') 76 | async getCheckArticle(body: { classifyId: number, createdAt: string, endTime: string, title: string, username: string, top:boolean, pageNumber: number, pageSize: number }) { 77 | const data = (await this.artService.getCheckArticle(body.classifyId, body.createdAt, body.endTime, body.title, body.username, body.top, body.pageNumber, body.pageSize)).exist; 78 | const total = (await this.artService.getCheckArticle(body.classifyId, body.createdAt, body.endTime, body.title, body.username, body.top, body.pageNumber, body.pageSize)).total; 79 | return { code: 200, message: '查询成功!', data, total } 80 | } 81 | 82 | 83 | } -------------------------------------------------------------------------------- /src/articles/controllers/classify-item.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { ClassifyItemService } from "../services/classify-item.service"; 3 | import { GrpcMethod } from "@nestjs/microservices"; 4 | import { ClassifyItemInput, CreateClassifyItem } from "../interfaces/classify-item.interface"; 5 | 6 | @Controller() 7 | export class ClassifyItemController { 8 | constructor( 9 | @Inject(ClassifyItemService) private readonly ciService: ClassifyItemService 10 | ) { } 11 | 12 | @GrpcMethod('ClassifyItemService') 13 | async updateClassifyItem(body: { classifyItem: ClassifyItemInput }) { 14 | await this.ciService.updateClassifyItem(body.classifyItem); 15 | return { code: 200, message: '修改成功!' }; 16 | } 17 | 18 | @GrpcMethod('ClassifyItemService') 19 | async deleteClassifyItem(body: {id: number}) { 20 | await this.ciService.deleteClassifyItem(body.id); 21 | return {code:200,message: '删除成功!'}; 22 | } 23 | 24 | @GrpcMethod('ClassifyItemService') 25 | async createClassifyItem(body: {classifyItem: CreateClassifyItem}){ 26 | await this.ciService.createClassifyItem(body.classifyItem); 27 | return {code:200,message:'创建成功!'}; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/articles/controllers/classify.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from '@nestjs/microservices'; 3 | import { CreateClassify } from "../interfaces/classify.interface"; 4 | import { ClassifyService } from "../services/classify.service"; 5 | import { Classify } from "../entities/classify.entity"; 6 | 7 | @Controller() 8 | export class ClassifyController { 9 | constructor( 10 | @Inject(ClassifyService) private readonly classifyService: ClassifyService 11 | ) { } 12 | 13 | @GrpcMethod('ClassifyService') 14 | async addClassify(body: { classify: CreateClassify }) { 15 | await this.classifyService.addClassify(body.classify); 16 | return { code: 200, message: '添加成功!' }; 17 | } 18 | 19 | @GrpcMethod('ClassifyService') 20 | async delClassify(body: { id: number }) { 21 | await this.classifyService.delClassify(body.id); 22 | return { code: 200, message: '删除成功!' }; 23 | } 24 | 25 | @GrpcMethod('ClassifyService') 26 | async updateClassify(body: { classify: Classify }) { 27 | await this.classifyService.updateClassify(body.classify); 28 | return { code: 200, message: '修改成功!' }; 29 | } 30 | 31 | @GrpcMethod('ClassifyService') 32 | async getAllClassify(body: { id: number }) { 33 | const data = await this.classifyService.getAllClassify(body.id); 34 | return { code: 200, message: '查询成功!', data: JSON.stringify(data) }; 35 | } 36 | 37 | @GrpcMethod('ClassifyService') 38 | async getOneClassify(body: { id: number }) { 39 | const data = await this.classifyService.getOneClassify(body.id); 40 | return { code: 200, message: '查询成功!', data: JSON.stringify(data) }; 41 | } 42 | 43 | @GrpcMethod('ClassifyService') 44 | async getParentClassify(body: { id: number }) { 45 | const data = await this.classifyService.getParentClassify(body.id); 46 | return { code: 200, message: '查询成功!', data }; 47 | } 48 | 49 | @GrpcMethod('ClassifyService') 50 | async MobileArticles(body: { classifyId: number, newClassifyId: number }) { 51 | await this.classifyService.mobileArticles(body.classifyId, body.newClassifyId); 52 | return { code: 200, message: '修改成功!' }; 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/articles/controllers/item.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { ItemService } from "../services/item.service"; 4 | import { Item } from "../entities/item.entity"; 5 | import { inputItem } from "../interfaces/item.interface"; 6 | 7 | @Controller() 8 | export class ItemController { 9 | constructor( 10 | @Inject(ItemService) private readonly itemService: ItemService, 11 | ) { } 12 | 13 | @GrpcMethod('ItemService') 14 | async createItem(body: { createItemInput: inputItem }) { 15 | await this.itemService.createItem(body.createItemInput); 16 | return { code: 200, message: '新建信息项成功!' }; 17 | } 18 | 19 | @GrpcMethod('ItemService') 20 | async updateItem(body: { updateItemInput: Item }) { 21 | await this.itemService.updateItem(body.updateItemInput); 22 | return { code: 200, message: '修改成功!' }; 23 | } 24 | 25 | @GrpcMethod('ItemService') 26 | async deleteItem(body: { id: number }) { 27 | await this.itemService.deleteItem(body.id); 28 | return {code:200,message: '删除成功!'}; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/articles/controllers/message.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from '@nestjs/microservices'; 3 | import { MessageService } from "../services/message.service"; 4 | 5 | @Controller() 6 | export class MessageController { 7 | constructor( 8 | @Inject(MessageService) private readonly messageService: MessageService 9 | ) { } 10 | 11 | @GrpcMethod('MessageService') 12 | async createMessage(body: { content: string, owner: number }) { 13 | await this.messageService.createMessage(body.content, body.owner); 14 | return { code: 200, message: '发送成功!' }; 15 | } 16 | 17 | @GrpcMethod('MessageService') 18 | async deleteMessageByIds(body: { ids: number[] }) { 19 | await this.messageService.deleteMessageById(body.ids); 20 | return { code: 200, message: '删除成功!' }; 21 | } 22 | 23 | @GrpcMethod('MessageService') 24 | async getAllMessage(body: { pageNumber: number, pageSize: number, startTime: string, endTime: string }) { 25 | const data = (await this.messageService.getAllMessage(body.pageNumber, body.pageSize, body.startTime, body.endTime)).data; 26 | const total = (await this.messageService.getAllMessage(body.pageNumber, body.pageSize, body.startTime, body.endTime)).total; 27 | return { code: 200, message: '查询成功!', data, total }; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/articles/controllers/user-message.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from '@nestjs/microservices'; 3 | import { UserMessageService } from "../services/user-message.service"; 4 | 5 | @Controller() 6 | export class UserMessageController { 7 | constructor( 8 | @Inject(UserMessageService) private readonly userMessageService: UserMessageService 9 | ) { } 10 | 11 | @GrpcMethod('UserMessageService') 12 | async getMessageByUserId(body: { pageNumber: number, pageSize: number, id: number }) { 13 | const data = (await this.userMessageService.getMessageByUserId(body.pageNumber, body.pageSize, body.id)).data; 14 | const total = (await this.userMessageService.getMessageByUserId(body.pageNumber, body.pageSize, body.id)).total; 15 | return { code: 200, message: '删除成功!', data, total }; 16 | } 17 | 18 | @GrpcMethod('UserMessageService') 19 | async getMessageById(body: { id: number }) { 20 | const data = await this.userMessageService.getMessageById(body.id); 21 | return { code: 200, message: '查找成功!', data }; 22 | } 23 | 24 | @GrpcMethod('UserMessageService') 25 | async delMessageByIds(body: { ids: number[] }) { 26 | await this.userMessageService.deleteMessageById(body.ids); 27 | return { code: 200, message: '删除成功!' }; 28 | } 29 | 30 | @GrpcMethod('UserMessageService') 31 | async readMessageById(body: { id: number }) { 32 | await this.userMessageService.readMessageById(body.id); 33 | return { code: 200, message: '设置已读成功!' }; 34 | } 35 | 36 | @GrpcMethod('UserMessageService') 37 | async readAll(body: { id: number }) { 38 | await this.userMessageService.readAll(body.id); 39 | return { code: 200, message: '设置已读成功!' }; 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/articles/entities/art-info.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; 2 | import { Article } from "./article.entity"; 3 | import { Item } from "./item.entity"; 4 | 5 | @Entity('art_info') 6 | export class ArtInfo { 7 | 8 | @PrimaryGeneratedColumn() 9 | id: number; 10 | 11 | @Column({ 12 | comment: '值' 13 | }) 14 | value: string; 15 | 16 | @ManyToOne(type => Article, article => article.artInfos, { 17 | onDelete: 'CASCADE' 18 | }) 19 | article: Article; 20 | 21 | @ManyToOne(type => Item, item => item.artInfos, { 22 | onDelete: 'CASCADE' 23 | }) 24 | item: Item; 25 | 26 | } -------------------------------------------------------------------------------- /src/articles/entities/article.entity.ts: -------------------------------------------------------------------------------- 1 | import { Column, JoinColumn, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, OneToMany } from 'typeorm'; 2 | import * as moment from 'moment'; 3 | import { Classify } from './classify.entity'; 4 | import { ArtInfo } from './art-info.entity'; 5 | import { Discuss } from 'src/plugin/comment/entities/discuss.entity'; 6 | @Entity('article') 7 | export class Article { 8 | /*文章Id*/ 9 | @PrimaryGeneratedColumn() 10 | id: number; 11 | 12 | /* 作者id*/ 13 | @Column({ 14 | comment: '作者id', 15 | nullable: true 16 | }) 17 | userId: number; 18 | 19 | /*文章标题*/ 20 | @Column({ 21 | nullable: true, 22 | length: 120, 23 | }) 24 | title: string; 25 | 26 | /*关键词*/ 27 | @Column({ 28 | nullable: true 29 | }) 30 | keywords: string; 31 | 32 | @Column({ 33 | default:0 34 | }) 35 | like: number; 36 | 37 | /* 访问量*/ 38 | @Column({ 39 | default: 0 40 | }) 41 | views: number; 42 | 43 | /*封面图片地址*/ 44 | @Column({ 45 | nullable: true, 46 | length: 500, 47 | }) 48 | cover: string; 49 | 50 | 51 | /*摘要*/ 52 | @Column({ 53 | nullable: true, 54 | length: 500, 55 | }) 56 | abstract: string; 57 | 58 | /*内容*/ 59 | @Column({ 60 | nullable: true, 61 | type: 'text', 62 | }) 63 | content: string; 64 | 65 | /*置顶*/ 66 | @Column({ 67 | default: false 68 | }) 69 | top: boolean; 70 | 71 | /*来源*/ 72 | @Column({ 73 | nullable: true, 74 | length: 120, 75 | }) 76 | source: string; 77 | 78 | /*文章状态, 0 待审核 1 审核通过 2 被拒绝 */ 79 | @Column({ 80 | default: 0 81 | }) 82 | status: number; 83 | 84 | /*拒绝原因*/ 85 | @Column({ 86 | type: 'text', 87 | nullable: true 88 | }) 89 | refuseReason: string; 90 | 91 | /*来源链接*/ 92 | @Column({ 93 | nullable: true, 94 | length: 200, 95 | }) 96 | sourceUrl: string; 97 | 98 | /*删除(回收站)*/ 99 | @Column({ 100 | nullable: true, 101 | default: false 102 | }) 103 | recycling: boolean; 104 | 105 | /*发布时间*/ 106 | @Column({ 107 | nullable: true, 108 | default: () => 'NOW ()', 109 | transformer: { 110 | from: (date) => { 111 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 112 | }, 113 | to: (date) => { 114 | date = date ? date : new Date(); 115 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 116 | } 117 | } 118 | }) 119 | createdAt: string; 120 | 121 | /*修改时间*/ 122 | @Column({ 123 | nullable: true, 124 | default: () => 'NOW ()', 125 | transformer: { 126 | from: (date) => { 127 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 128 | }, 129 | to: (date) => { 130 | date = date ? date : new Date(); 131 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 132 | } 133 | } 134 | }) 135 | modifyAt: string; 136 | 137 | /*分类Id*/ 138 | @ManyToOne(type => Classify, classify => classify.articles, { onDelete: 'CASCADE', cascade: true }) 139 | @JoinColumn({ 140 | name: 'classifyId', 141 | referencedColumnName: 'id' 142 | }) 143 | classify: Classify; 144 | 145 | @OneToMany(type => ArtInfo, artInfo => artInfo.article) 146 | artInfos: ArtInfo[]; 147 | 148 | @OneToMany(type=>Discuss,discuss=>discuss.article) 149 | discuss: Discuss[]; 150 | 151 | } 152 | 153 | -------------------------------------------------------------------------------- /src/articles/entities/classify-item.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable, ManyToOne } from "typeorm"; 2 | import { Classify } from "./classify.entity"; 3 | import { Item } from "./item.entity"; 4 | 5 | @Entity('classify_item') 6 | export class ClassifyItem { 7 | @PrimaryGeneratedColumn({ 8 | comment: '自增id' 9 | }) 10 | id: number; 11 | 12 | @Column({ 13 | comment: '显示名称' 14 | }) 15 | name: string; 16 | 17 | @Column({ 18 | comment: '别名' 19 | }) 20 | alias: string; 21 | 22 | @Column({ 23 | comment: '是否必填', 24 | default: false 25 | }) 26 | required: boolean; 27 | 28 | @Column({ 29 | comment: '排序' 30 | }) 31 | order: number; 32 | 33 | @ManyToOne(type => Classify, classify => classify.classifyItems) 34 | classify: Classify; 35 | 36 | @ManyToOne(type => Item, item => item.itemClassifys) 37 | item: Item; 38 | 39 | } -------------------------------------------------------------------------------- /src/articles/entities/classify.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | Column, 4 | Entity, 5 | OneToMany, Tree, TreeChildren, TreeParent, ManyToOne 6 | } from 'typeorm'; 7 | import { Article } from './article.entity'; 8 | import { ClassifyItem } from './classify-item.entity'; 9 | 10 | @Entity('classify') 11 | @Tree('nested-set') 12 | export class Classify { 13 | /*分类Id*/ 14 | @PrimaryGeneratedColumn() 15 | id: number; 16 | 17 | /*分类名称*/ 18 | @Column({ 19 | nullable: false, 20 | length: 120, 21 | }) 22 | name: string; 23 | 24 | @Column({ 25 | comment: '分类别名', 26 | unique: true, 27 | nullable: true 28 | }) 29 | alias: string; 30 | 31 | @Column({ 32 | comment: '只显示子级分类文章', 33 | default: false 34 | }) 35 | onlyChildrenArt: boolean; 36 | 37 | @TreeChildren() 38 | children: Classify[]; 39 | 40 | @TreeParent() 41 | parent: Classify; 42 | 43 | 44 | @OneToMany(type => Article,article => article.classify) 45 | articles: Article[]; 46 | 47 | @OneToMany(type => ClassifyItem,classifyItem=>classifyItem.classify) 48 | classifyItems: ClassifyItem[]; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/articles/entities/item.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from "typeorm"; 2 | import { ClassifyItem } from "./classify-item.entity"; 3 | import { ArtInfo } from "./art-info.entity"; 4 | import { BoardItem } from "src/plugin/message-board/entities/board-item.entity"; 5 | import { LeavewordInfo } from "src/plugin/message-board/entities/leaveword-info.entity"; 6 | 7 | @Entity('item') 8 | export class Item{ 9 | 10 | @PrimaryGeneratedColumn({ 11 | comment: '自增id' 12 | }) 13 | id: number; 14 | 15 | @Column({ 16 | comment: '名称' 17 | }) 18 | name: string; 19 | 20 | @Column({ 21 | comment: '说明', 22 | nullable: true 23 | }) 24 | explain: string; 25 | 26 | @Column({ 27 | comment: '类型' 28 | }) 29 | style: string; 30 | 31 | @Column({ 32 | comment: '正则表达式', 33 | nullable: true 34 | }) 35 | regular: string; 36 | 37 | @Column({ 38 | comment: '信息', 39 | nullable: true 40 | }) 41 | info: string; 42 | 43 | @OneToMany(type=>ClassifyItem,classifyItem=>classifyItem.item) 44 | itemClassifys: ClassifyItem[]; 45 | 46 | @OneToMany(type=>ArtInfo,artInfo=>artInfo.item) 47 | artInfos: ArtInfo[]; 48 | 49 | @OneToMany(type=>BoardItem,boardItem=>boardItem.item) 50 | boardItems: BoardItem[]; 51 | 52 | @OneToMany(type=>LeavewordInfo,leavewordInfo=>leavewordInfo.item) 53 | leavewordInfos: LeavewordInfo[]; 54 | 55 | } -------------------------------------------------------------------------------- /src/articles/entities/message.entity.ts: -------------------------------------------------------------------------------- 1 | import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn} from 'typeorm'; 2 | const moment = require('moment'); 3 | 4 | @Entity('message') 5 | export class Message { 6 | 7 | @PrimaryGeneratedColumn() 8 | id: number; 9 | 10 | /*内容*/ 11 | @Column({ 12 | name: 'content', 13 | type: 'text' 14 | }) 15 | content: string; 16 | 17 | /*发布时间*/ 18 | @CreateDateColumn({ 19 | transformer: { 20 | from(raw: Date): string { 21 | return moment(raw).format('YYYY-MM-DD HH:mm:ss'); 22 | }, 23 | to(): Date { 24 | return new Date(); 25 | } 26 | } 27 | }) 28 | createdAt: string; 29 | 30 | /* 消息所属人 */ 31 | @Column({ 32 | nullable: true 33 | }) 34 | owner: number; 35 | } -------------------------------------------------------------------------------- /src/articles/entities/user-message.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; 2 | import * as moment from 'moment'; 3 | 4 | @Entity('user_message') 5 | export class UserMessage { 6 | 7 | @PrimaryGeneratedColumn() 8 | id: number; 9 | 10 | /*内容*/ 11 | @Column({ 12 | name: 'content', 13 | type: 'text' 14 | }) 15 | content: string; 16 | 17 | /*发布时间*/ 18 | @Column({ 19 | transformer: { 20 | from(raw: Date): string { 21 | return moment(raw).format('YYYY-MM-DD HH:mm:ss'); 22 | }, 23 | to(): Date { 24 | return new Date(); 25 | } 26 | } 27 | }) 28 | createdAt: string; 29 | 30 | /* 消息所属人 */ 31 | @Column({ 32 | nullable: true 33 | }) 34 | owner: number; 35 | 36 | /* 是否已读 */ 37 | @Column({ 38 | default: false 39 | }) 40 | state: boolean; 41 | 42 | 43 | } -------------------------------------------------------------------------------- /src/articles/interfaces/article.interface.ts: -------------------------------------------------------------------------------- 1 | export interface inputArticle { 2 | title: string; 3 | userId: number; 4 | classifyId: number; 5 | cover: string; 6 | abstract: string; 7 | content: string; 8 | top: boolean; 9 | source: string; 10 | sourceUrl: string; 11 | createAt: string; 12 | infoKVs?: { 13 | artInfoId: number; 14 | artInfoValue: string; 15 | infoItemId?: number }[]; 16 | } 17 | 18 | export interface updateArticle { 19 | id: number; 20 | title: string; 21 | classifyId: number; 22 | sourceUrl: string; 23 | cover: string; 24 | abstract: string; 25 | content: string; 26 | top: boolean; 27 | source: string; 28 | modifyAt?:string; 29 | status?: number; 30 | infoKVs?: { 31 | artInfoId: number; 32 | artInfoValue: string; 33 | infoItemId?: number 34 | }[]; 35 | userId: number; 36 | } 37 | 38 | export interface artResult { 39 | id: number; 40 | title: string; 41 | classify: { 42 | id: number; 43 | name: string; 44 | alias: string; 45 | onlyChildrenArt: boolean; 46 | }; 47 | sourceUrl: string; 48 | cover: string; 49 | abstract: string; 50 | content: string; 51 | top: boolean; 52 | source: string; 53 | userId: number; 54 | username: string; 55 | } -------------------------------------------------------------------------------- /src/articles/interfaces/classify-item.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ClassifyItemInput { 2 | id: number; 3 | name: string; 4 | alias: string; 5 | required: boolean; 6 | order: number; 7 | itemId: number; 8 | classifyId: number; 9 | } 10 | 11 | export interface CreateClassifyItem { 12 | name: string; 13 | alias: string; 14 | required: boolean; 15 | order: number; 16 | itemId: number; 17 | classifyId: number; 18 | } -------------------------------------------------------------------------------- /src/articles/interfaces/classify.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreateClassify { 2 | name: string; 3 | alias: string; 4 | parent: { id: number }; 5 | onlyChildrenArt: boolean; 6 | order: number; 7 | classifyItem?: { 8 | name: string; 9 | alias: string; 10 | required: boolean; 11 | order: number; 12 | itemId: number; 13 | }[]; 14 | } -------------------------------------------------------------------------------- /src/articles/interfaces/item.interface.ts: -------------------------------------------------------------------------------- 1 | export interface inputItem{ 2 | name: string; 3 | explain: string; 4 | style: string; 5 | regular: string; 6 | info: string; 7 | } -------------------------------------------------------------------------------- /src/articles/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UserInfoData { 2 | id: number; 3 | username: string; 4 | email: string; 5 | mobile: string; 6 | banned: boolean; 7 | recycle: boolean; 8 | createdAt: string; 9 | updatedAt: string; 10 | userRoles: { 11 | id: number; 12 | name: string 13 | }[]; 14 | userOrganizations: { 15 | id: number; 16 | name: string; 17 | }[]; 18 | userInfos: { 19 | id: number; 20 | order: number; 21 | infoItemId: number; 22 | type: string; 23 | name: string; 24 | value: string; 25 | description: string; 26 | registerDisplay: boolean; 27 | informationDisplay: boolean; 28 | }[]; 29 | } 30 | 31 | export interface CreateUserInput { 32 | username?: string; 33 | email?: string; 34 | mobile?: string; 35 | password: string; 36 | infoKVs?: { key: number; value: string }[]; 37 | roleIds?: number[]; 38 | organizationIds?: number[]; 39 | } 40 | 41 | export interface UpdateUserInput { 42 | username?: string; 43 | email?: string; 44 | mobile?: string; 45 | password?: string; 46 | status: number; 47 | certification: number; 48 | infoKVs?: { 49 | key: number; 50 | value: string; 51 | relationId?: number 52 | }[]; 53 | roleIds?: { 54 | before: number; 55 | after: number; 56 | }[]; 57 | organizationIds?: { 58 | before: number; 59 | after: number; 60 | }[]; 61 | } -------------------------------------------------------------------------------- /src/articles/services/classify-item.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { Repository } from "typeorm"; 3 | import { InjectRepository } from "@nestjs/typeorm"; 4 | import { ClassifyItem } from "../entities/classify-item.entity"; 5 | import { ClassifyItemInput, CreateClassifyItem } from "../interfaces/classify-item.interface"; 6 | import { RpcException } from "@nestjs/microservices"; 7 | import { ArtInfo } from "../entities/art-info.entity"; 8 | import { Item } from "../entities/item.entity"; 9 | import { Article } from "../entities/article.entity"; 10 | import { Classify } from "../entities/classify.entity"; 11 | 12 | @Injectable() 13 | export class ClassifyItemService { 14 | constructor( 15 | @InjectRepository(ClassifyItem) private readonly ciRepo: Repository, 16 | @InjectRepository(Item) private readonly itemRepo: Repository, 17 | @InjectRepository(Classify) private readonly claRepo: Repository, 18 | @InjectRepository(ArtInfo) private readonly aiRepo: Repository, 19 | @InjectRepository(Article) private readonly artRepo: Repository
, 20 | ) { } 21 | 22 | 23 | /** 24 | * 修改文章分类中的信息项 25 | * 26 | * @param classifyItem 需要修改的实体 27 | */ 28 | async updateClassifyItem(classifyItem: ClassifyItemInput) { 29 | const exist = await this.ciRepo.findOne(classifyItem.id); 30 | const classify = await this.claRepo.findOne(classifyItem.classifyId); 31 | if (!exist) { 32 | throw new RpcException({ code: 404, message: '该信息项不存在!' }); 33 | } 34 | if (!classify) { 35 | throw new RpcException({ code: 404, message: '该文章分类不存在!' }); 36 | } 37 | if (classifyItem.alias && classifyItem.alias !== exist.alias) { 38 | if (await this.ciRepo.findOne({ where: { alias: classifyItem.alias, classify: classify } })) { 39 | throw new RpcException({ code: 406, message: '别名重复!' }); 40 | } 41 | } 42 | const item = await this.itemRepo.findOne(classifyItem.itemId); 43 | await this.ciRepo.save(this.ciRepo.create({ 44 | id: classifyItem.id, 45 | name: classifyItem.name, 46 | alias: classifyItem.alias, 47 | item, 48 | order: classifyItem.order, 49 | required: classifyItem.required, 50 | classify 51 | })); 52 | } 53 | 54 | /** 55 | * 删除文章分类中的信息项 56 | * 57 | * @param id 文章中信息项的id 58 | * 59 | */ 60 | async deleteClassifyItem(id: number) { 61 | const exist = await this.ciRepo.findOne(id,{relations:['classify','item']}); 62 | if (!exist) { 63 | throw new RpcException({ code: 404, message: '该信息项不存在!' }); 64 | } 65 | const arts = await this.artRepo.find({ where: { classify: exist.classify.id } }); 66 | const ids = arts.map(item => item.id); 67 | const infos = await this.aiRepo.createQueryBuilder('artInfo') 68 | .leftJoinAndSelect('artInfo.article', 'Article') 69 | .where('artInfo.item = :item', {item:exist.item.id}) 70 | .andWhere('Article.id IN(:...ids)', { ids }) 71 | .getMany(); 72 | await this.aiRepo.remove(infos); 73 | await this.ciRepo.remove(exist); 74 | } 75 | 76 | async createClassifyItem(classifyItem: CreateClassifyItem) { 77 | await this.ciRepo.save(this.ciRepo.create({ 78 | name: classifyItem.name, 79 | alias: classifyItem.alias, 80 | item: await this.itemRepo.findOne(classifyItem.itemId), 81 | order: classifyItem.order, 82 | required: classifyItem.required, 83 | classify: await this.claRepo.findOne(classifyItem.classifyId) 84 | })) 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /src/articles/services/classify.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { CreateClassify } from "../interfaces/classify.interface"; 3 | import { InjectRepository } from '@nestjs/typeorm'; 4 | import { Classify } from "../entities/classify.entity"; 5 | import { Article } from "../entities/article.entity"; 6 | import { TreeRepository, Repository, In } from "typeorm"; 7 | import { RpcException } from "@nestjs/microservices"; 8 | import { ClassifyItem } from "../entities/classify-item.entity"; 9 | import { Item } from "../entities/item.entity"; 10 | 11 | @Injectable() 12 | export class ClassifyService { 13 | constructor( 14 | @InjectRepository(Classify) private readonly claRepository: TreeRepository, 15 | @InjectRepository(Article) private readonly artRepository: Repository
, 16 | @InjectRepository(ClassifyItem) private readonly ciRepository: Repository, 17 | @InjectRepository(Item) private readonly itemRepository: Repository 18 | ) { } 19 | 20 | /** 21 | * 新增文章分类 22 | * 23 | * @param classify 新增分类实体 24 | */ 25 | async addClassify(classify: CreateClassify) { 26 | try { 27 | const ignore = await this.claRepository.count(); 28 | if (!classify.parent.id || ignore <= 0) { 29 | await this.claRepository.save(this.claRepository.create({ name: '总分类', alias: '总分类', onlyChildrenArt: true })); 30 | return { code: 200, message: '创建成功' }; 31 | } 32 | if (classify.parent) { 33 | const exist = await this.claRepository.findOne({ id: classify.parent.id }); 34 | if (!exist) { 35 | return { code: 405, message: '当前分类父节点不存在' }; 36 | } 37 | classify.parent = exist; 38 | } 39 | const result = await this.claRepository.findOne({ where: { alias: classify.alias } }); 40 | if (result) { 41 | throw new RpcException({ code: 406, message: '别名重复!' }); 42 | } 43 | const exist = await this.claRepository.save(await this.claRepository.create(classify)); 44 | if (classify.classifyItem) { 45 | for (const i of classify.classifyItem) { 46 | await this.ciRepository.save(this.ciRepository.create({ 47 | name: i.name, 48 | alias: i.alias, 49 | required: i.required, 50 | order: i.order, 51 | classify: exist, 52 | item: await this.itemRepository.findOne(i.itemId) 53 | })); 54 | } 55 | } 56 | 57 | } catch (err) { 58 | throw new RpcException({ code: 500, message: err.toString() }); 59 | } 60 | } 61 | 62 | /** 63 | * 删除文章分类 64 | * 65 | * @param id 文章分类id 66 | */ 67 | async delClassify(id: number) { 68 | const classify: Classify = await this.claRepository.findOne({ id }); 69 | if (!classify) { 70 | return { code: 404, message: '当前分类不存在' }; 71 | } 72 | const array = await this.getAllClassifyIds(id); 73 | const articles = await this.artRepository.count({ where: { classify: In(array) } }); 74 | if (articles > 0) { 75 | throw new RpcException({ code: 403, message: '当前分类下有文章,不能删除' }) 76 | } 77 | array.splice(array.indexOf(id), 1); 78 | if (array.length) { 79 | throw new RpcException({ code: 403, message: '当前分类下有子分类,不能删除' }); 80 | } 81 | await this.claRepository.remove(classify); 82 | return { code: 200, message: '删除成功' }; 83 | } 84 | 85 | /** 86 | * 获取该分类所有子分类id 87 | * 88 | * @param idNum 指定分类id 89 | */ 90 | async getAllClassifyIds(idNum: number): Promise { 91 | const array: number[] = []; 92 | const classify = await this.claRepository.findOne({ id: idNum }); 93 | await this.claRepository.findDescendants(classify).then(a => { 94 | if (a) { 95 | a.map(a => { 96 | array.push(a.id); 97 | }); 98 | } 99 | }); 100 | return array; 101 | } 102 | 103 | /** 104 | * 修改分类 105 | * 106 | * @param classify 被修改分类实体 107 | */ 108 | async updateClassify(classify: Classify) { 109 | const exist = await this.claRepository.findOne({ id: classify.id }); 110 | if (!exist) { 111 | return { code: 404, message: '当前分类不存在!' }; 112 | } 113 | if (classify.alias && classify.alias !== exist.alias) { 114 | if (await this.claRepository.findOne({ where: { alias: classify.alias } })) { 115 | throw new RpcException({ code: 409, message: '该分类别名已存在!' }); 116 | } 117 | } 118 | const parent = await this.claRepository.findOne({ id: classify.parent.id }); 119 | if (!parent) { 120 | throw new RpcException({ code: 404, message: '该上级分类不存在' }); 121 | } 122 | try { 123 | await this.claRepository.save(await this.claRepository.create(classify)); 124 | } catch (err) { 125 | throw new RpcException({ code: 500, message: err.toString() }); 126 | } 127 | } 128 | 129 | 130 | /** 131 | * 查询所有分类 132 | * 133 | * @param id 不传时查询所有分类,传了则只查询该分类及其子分类 134 | */ 135 | async getAllClassify(id: number) { 136 | if (id) { 137 | const exist = await this.claRepository.findOne(id); 138 | if (!exist) { 139 | throw new RpcException({ code: 404, message: '该分类不存在!' }); 140 | } 141 | // const result = await this.claRepository.createDescendantsQueryBuilder('classify','classify',exist).orderBy('classify.order','ASC').getMany(); 142 | // console.log(result); 143 | // return result; 144 | return await this.claRepository.findDescendantsTree(exist); 145 | } else { 146 | return await this.claRepository.findTrees(); 147 | } 148 | } 149 | 150 | /** 151 | * 查询分类详情 152 | * 153 | * @param id 指定分类id 154 | */ 155 | async getOneClassify(id: number) { 156 | const exist = await this.claRepository.findOne({ id }); 157 | if (!exist) { 158 | throw new RpcException({ code: 200, message: '该分类不存在!' }); 159 | } 160 | const data1 = await this.claRepository.findAncestorsTree(exist); 161 | const data2 = await this.claRepository.createQueryBuilder('classify').relation(Classify, 'classifyItems').of(id).loadMany(); 162 | const data = { 163 | id: data1.id, 164 | name: data1.name, 165 | alias: data1.alias, 166 | parent: data1.parent, 167 | onlyChildrenArt: data1.onlyChildrenArt, 168 | clasifyItem: data2 169 | }; 170 | return data ; 171 | } 172 | 173 | /** 174 | * 获取上级分类 175 | * 176 | * @param id 指定分类id 177 | */ 178 | async getParentClassify(id: number) { 179 | const exist = await this.claRepository.findOne({ id }); 180 | if (!exist) { 181 | throw new RpcException({ code: 404, message: '该分类不存在!' }); 182 | } 183 | const data = await this.claRepository.findAncestorsTree(exist); 184 | return { code: 200, message: '查询成功!', data: [data] } 185 | } 186 | 187 | /** 188 | * 移动分类下的文章至另一分类 189 | * 190 | * @param classifyId 原分类id 191 | * @param newClassifyId 需要移至分类id 192 | */ 193 | async mobileArticles(classifyId: number, newClassifyId: number) { 194 | const exist = await this.claRepository.findOne({ where: { id: classifyId } }); 195 | if (!exist) { 196 | return { code: 404, message: '原分类不存在!' }; 197 | } 198 | const newClassify = await this.claRepository.findOne({ where: { id: newClassifyId } }); 199 | if (!newClassify) { 200 | return { code: 404, message: '所选分类不存在!' }; 201 | } 202 | const array = await this.getAllClassifyIds(classifyId); 203 | const ids = await this.artRepository.createQueryBuilder('art') 204 | .where('"art"."classifyId" in(:...id)', { 205 | id: array 206 | }) 207 | .getMany(); 208 | if (!ids.length) { 209 | return { code: 404, message: '原分类下不存在文章!' }; 210 | } 211 | try { 212 | // 修改文章分类 213 | for (const i of ids) { 214 | await this.artRepository.update(i.id, { classify: newClassify }); 215 | } 216 | } catch (err) { 217 | throw new RpcException({ code: 500, message: err.toString() }); 218 | } 219 | } 220 | 221 | } -------------------------------------------------------------------------------- /src/articles/services/item.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { Repository } from "typeorm"; 3 | import { InjectRepository } from "@nestjs/typeorm"; 4 | import { RpcException } from "@nestjs/microservices"; 5 | import { inputItem } from "../interfaces/item.interface"; 6 | import { Item } from "../entities/item.entity"; 7 | 8 | @Injectable() 9 | export class ItemService { 10 | constructor( 11 | @InjectRepository(Item) private readonly itemRepo: Repository, 12 | ) { } 13 | 14 | /** 15 | * 创建信息项 16 | * 17 | * @param item 创建的信息项实体 18 | */ 19 | async createItem(item: inputItem) { 20 | try { 21 | await this.itemRepo.save(this.itemRepo.create(item)); 22 | } catch (error) { 23 | throw new RpcException({ code: 500, message: '出现了意外错误' + error.toString() }); 24 | } 25 | } 26 | 27 | /** 28 | * 修改信息项 29 | * 30 | * @param item 修改的信息项实体 31 | */ 32 | async updateItem(item: Item) { 33 | const result = await this.itemRepo.findOne(item.id); 34 | if (!result) { 35 | throw new RpcException({ code: 404, message: '该信息项不存在!' }); 36 | } 37 | try { 38 | await this.itemRepo.update(item.id, item); 39 | } catch (error) { 40 | throw new RpcException({ code: 500, message: '出现了意外错误' + error.toString() }); 41 | } 42 | } 43 | 44 | /** 45 | * 删除信息项 46 | * 47 | * @param id 删除的信息项id 48 | */ 49 | async deleteItem(id: number) { 50 | try { 51 | await this.itemRepo.delete(id); 52 | } catch (error) { 53 | throw new RpcException({ code: 500, message: '出现了意外错误' + error.toString() }); 54 | } 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src/articles/services/message.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TreeRepository, Repository, In } from "typeorm"; 4 | import { RpcException } from "@nestjs/microservices"; 5 | import { Message } from "../entities/message.entity"; 6 | import { UserMessage } from "../entities/user-message.entity"; 7 | 8 | 9 | @Injectable() 10 | export class MessageService { 11 | constructor( 12 | @InjectRepository(Message) private readonly mesRepo: Repository, 13 | @InjectRepository(UserMessage) private readonly umesRepo: Repository, 14 | ) { } 15 | 16 | /** 17 | * 创建通知信息 18 | * 19 | * @param content 信息内容 20 | * @param owner 所属用户 21 | */ 22 | async createMessage(content: string, owner: number) { 23 | try { 24 | await this.mesRepo.save(this.mesRepo.create({ 25 | content, owner 26 | })) 27 | await this.umesRepo.save(this.umesRepo.create({ 28 | 29 | })) 30 | } catch (err) { 31 | throw new RpcException({ code: 400, message: err.toString() }); 32 | } 33 | } 34 | 35 | /** 36 | * 批量删除通知信息 37 | * 38 | * @param ids 批量删除的通知信息id 39 | */ 40 | async deleteMessageById(ids: number[]) { 41 | try { 42 | const exist = await this.mesRepo.findByIds(ids); 43 | await this.mesRepo.remove(exist); 44 | } catch (err) { 45 | throw new RpcException({ code: 400, message: err.toString() }); 46 | } 47 | } 48 | 49 | /** 50 | * 后台获取所有通知消息 51 | * 52 | * @param pageNumber 当前页码 53 | * @param pageSize 每页显示数量 54 | * @param startTime 起始时间 55 | * @param endTime 截止时间 56 | */ 57 | async getAllMessage(pageNumber: number, pageSize: number, startTime: string, endTime: string) { 58 | try { 59 | const exist = await this.mesRepo.createQueryBuilder('message') 60 | .where('case when :startTime1 <> \'\' then "createdAt" >= :startTime2 and "createdAt" <= :endTime else "createdAt" is not null end', { 61 | startTime1: startTime ? startTime : '', 62 | startTime2: startTime ? startTime : '1970-1-1 00:00:00', 63 | endTime: endTime ? endTime : new Date(), 64 | }) 65 | .orderBy({ '"message"."createdAt"': 'DESC' }) 66 | .skip(pageSize * (pageNumber - 1)) 67 | .take(pageSize) 68 | .getManyAndCount(); 69 | return { data: exist[0], total: exist[1] }; 70 | } catch (err) { 71 | throw new RpcException({ code: 400, message: err.toString() }); 72 | } 73 | } 74 | 75 | /** 76 | * 获取指定用户的所有通知信息 77 | * 78 | * @param pageNumber 当前页码 79 | * @param pageSize 每页显示数量 80 | * @param startTime 起始时间 81 | * @param endTime 截止时间 82 | * @param id 用户id 83 | */ 84 | async getMessageByUserId(pageNumber: number, pageSize: number, startTime: string, endTime: string, id: number) { 85 | const s = []; 86 | s.push(id); 87 | const exist = await this.mesRepo.createQueryBuilder('message') 88 | .where('message.owner IN(' + s + ')') 89 | .andWhere('case when :startTime1 <> \'\' then "createdAt" >= :startTime2 and "createdAt" <= :endTime else "createdAt" is not null end', { 90 | startTime1: startTime ? startTime : '', 91 | startTime2: startTime ? startTime : '1970-1-1 00:00:00', 92 | endTime: endTime ? endTime : new Date(), 93 | }) 94 | .orderBy({ '"message"."createdAt"': 'DESC' }) 95 | .skip(pageSize * (pageNumber - 1)) 96 | .take(pageSize) 97 | .getManyAndCount(); 98 | return { data: exist[0], total: exist[1] }; 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /src/articles/services/user-message.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TreeRepository, Repository, In } from "typeorm"; 4 | import { RpcException } from "@nestjs/microservices"; 5 | import { UserMessage } from "../entities/user-message.entity"; 6 | 7 | 8 | @Injectable() 9 | export class UserMessageService { 10 | constructor( 11 | @InjectRepository(UserMessage) private readonly umesRepo: Repository, 12 | ) { } 13 | 14 | async getMessageByUserId(pageNumber: number, pageSize: number, id: number) { 15 | try { 16 | const exist = await this.umesRepo.createQueryBuilder('message') 17 | .orderBy({ '"message"."createdAt"': 'DESC' }) 18 | .skip(pageSize * (pageNumber - 1)) 19 | .take(pageSize) 20 | .getManyAndCount(); 21 | return { data: exist[0], total: exist[1] }; 22 | } catch (err) { 23 | throw new RpcException({ code: 400, message: err.toString() }); 24 | } 25 | } 26 | 27 | async getMessageById(id: number) { 28 | try { 29 | const data = await this.umesRepo.findOne(id); 30 | return data; 31 | } catch (err) { 32 | throw new RpcException({ code: 400, message: err.toString() }); 33 | } 34 | } 35 | 36 | async deleteMessageById(ids: number[]) { 37 | try { 38 | const exist = await this.umesRepo.findByIds(ids); 39 | await this.umesRepo.remove(exist); 40 | } catch (err) { 41 | throw new RpcException({ code: 400, message: err.toString() }); 42 | } 43 | } 44 | 45 | async readMessageById(id: number) { 46 | const news = await this.umesRepo.findOne({ id }); 47 | news.state = true; 48 | await this.umesRepo.save(this.umesRepo.create(news)); 49 | return 'success'; 50 | } 51 | 52 | async readAll(id: number) { 53 | await this.umesRepo.createQueryBuilder().update(UserMessage).set({ state: true }).where({ owner: id }).execute(); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/cms.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, OnModuleInit, Global } from "@nestjs/common"; 2 | import { TypeOrmModule, InjectRepository } from "@nestjs/typeorm"; 3 | import { NotaddGrpcClientFactory } from "./grpc.client-factory"; 4 | import { TreeRepository, Repository } from "typeorm"; 5 | import { Classify } from "./articles/entities/classify.entity"; 6 | import { Article } from "./articles/entities/article.entity"; 7 | import { ArticleController } from "./articles/controllers/article.controller"; 8 | import { ClassifyController } from "./articles/controllers/classify.controller"; 9 | import { MessageController } from "./articles/controllers/message.controller"; 10 | import { UserMessageController } from "./articles/controllers/user-message.controller"; 11 | import { ItemController } from "./articles/controllers/item.controller"; 12 | import { ArticleService } from "./articles/services/article.service"; 13 | import { MessageService } from "./articles/services/message.service"; 14 | import { ClassifyService } from "./articles/services/classify.service"; 15 | import { UserMessageService } from "./articles/services/user-message.service"; 16 | import { ItemService } from "./articles/services/item.service"; 17 | import { Item } from "./articles/entities/item.entity"; 18 | import { PageSort } from "./pages/entities/page-sort.entity"; 19 | import { Page } from "./pages/entities/page.entity"; 20 | import { PageController } from "./pages/controllers/page.controller"; 21 | import { PageSortService } from "./pages/services/page-sort.service"; 22 | import { PageService } from "./pages/services/page.service"; 23 | import { PageSortController } from "./pages/controllers/page-sort.controller"; 24 | import { Content } from "./pages/entities/content.entity"; 25 | import { DiscussController } from "./plugin/comment/controllers/discuss.controller"; 26 | import { DiscussService } from "./plugin/comment/services/discuss.service"; 27 | import { CommentProperties } from "./plugin/comment/entities/comment-properties.entity"; 28 | import { Discuss } from "./plugin/comment/entities/discuss.entity"; 29 | import { CommentPropertiesController } from "./plugin/comment/controllers/comment-properties.controller"; 30 | import { CommentPropertiesService } from "./plugin/comment/services/comment-properties.service"; 31 | import { MessageBoard } from "./plugin/message-board/entities/message-board.entity"; 32 | import { ArtInfo } from "./articles/entities/art-info.entity"; 33 | import { ClassifyItem } from "./articles/entities/classify-item.entity"; 34 | import { Leaveword } from "./plugin/message-board/entities/leaveword.entity"; 35 | import { BoardItem } from "./plugin/message-board/entities/board-item.entity"; 36 | import { LeavewordInfo } from "./plugin/message-board/entities/leaveword-info.entity"; 37 | import { ClassifyItemController } from "./articles/controllers/classify-item.controller"; 38 | import { ClassifyItemService } from "./articles/services/classify-item.service"; 39 | import { MessageBoardController } from "./plugin/message-board/controllers/message-board.controller"; 40 | import { LeavewordController } from "./plugin/message-board/controllers/leaveword.conteroller"; 41 | import { BoardItemController } from "./plugin/message-board/controllers/board-item.controller"; 42 | import { MessageBoardService } from "./plugin/message-board/services/message-board.service"; 43 | import { LeavewordService } from "./plugin/message-board/services/leaveword.service"; 44 | import { BoardItemService } from "./plugin/message-board/services/board-item.service"; 45 | import { Message } from "./articles/entities/message.entity"; 46 | import { UserMessage } from "./articles/entities/user-message.entity"; 47 | import { UpYunController } from "./plugin/upyun/controllers/upyun.controller"; 48 | import { UpYunService } from "./plugin/upyun/services/upyun.service"; 49 | import { Bucket } from "./plugin/upyun/entities/bucket.entity"; 50 | import { ConfigService } from "./plugin/upyun/services/config.service"; 51 | import { RestfulUtil } from "./plugin/upyun/util/restful.util"; 52 | import { KindUtil } from "./plugin/upyun/util/kind.util"; 53 | import { ProcessStringUtil } from "./plugin/upyun/util/process.string.util"; 54 | import { AuthUtil } from "./plugin/upyun/util/auth.util"; 55 | import { Image } from "./plugin/upyun/entities/image.entity"; 56 | import { FileUtil } from "./plugin/upyun/util/file.util"; 57 | import { ImageConfig } from "./plugin/upyun/entities/image.config.entity"; 58 | import { VideoConfig } from "./plugin/upyun/entities/video.config.entity"; 59 | import { AudioConfig } from "./plugin/upyun/entities/audio.config.entity"; 60 | 61 | @Global() 62 | @Module({ 63 | imports: [ 64 | TypeOrmModule.forRoot({ 65 | 66 | }), 67 | TypeOrmModule.forFeature([ 68 | Classify, ClassifyItem, Message, UserMessage, Article, ArtInfo, Item, PageSort, Page, Content, Discuss, VideoConfig, 69 | CommentProperties, MessageBoard, Leaveword, BoardItem, LeavewordInfo, Bucket,Image,ImageConfig,AudioConfig 70 | ]), 71 | ], 72 | controllers: [ 73 | ArticleController, 74 | ClassifyController, 75 | ClassifyItemController, 76 | MessageController, 77 | UserMessageController, 78 | ItemController, 79 | PageSortController, 80 | PageController, 81 | DiscussController, 82 | CommentPropertiesController, 83 | MessageBoardController, 84 | LeavewordController, 85 | BoardItemController, 86 | UpYunController 87 | ], 88 | providers: [ 89 | ArticleService, 90 | MessageService, 91 | ClassifyService, 92 | ClassifyItemService, 93 | UserMessageService, 94 | NotaddGrpcClientFactory, 95 | ItemService, 96 | PageSortService, 97 | PageService, 98 | DiscussService, 99 | CommentPropertiesService, 100 | MessageBoardService, 101 | LeavewordService, 102 | BoardItemService, 103 | UpYunService, 104 | ConfigService, 105 | RestfulUtil, 106 | KindUtil, 107 | ProcessStringUtil, 108 | AuthUtil, 109 | FileUtil 110 | ] 111 | }) 112 | 113 | export class CmsModule implements OnModuleInit { 114 | constructor( 115 | @InjectRepository(Classify) private readonly claRepository: TreeRepository, 116 | @InjectRepository(PageSort) private readonly psRepository: TreeRepository, 117 | @InjectRepository(CommentProperties) private readonly cpRepo: Repository, 118 | private readonly classifyService: ClassifyService, 119 | private readonly pageSortService: PageSortService, 120 | ) { } 121 | 122 | async onModuleInit() { 123 | await this.createRootClassify(); 124 | await this.createPageSortClassify(); 125 | await this.createProperties(); 126 | } 127 | 128 | private async createRootClassify() { 129 | const root = await this.claRepository.findOne({ where: { alias: '总分类' } }); 130 | if (!root) { 131 | await this.classifyService.addClassify({ name: '总分类', alias: '总分类', parent: { id: 0 }, onlyChildrenArt: true, order: 1 }); 132 | } 133 | } 134 | 135 | private async createPageSortClassify() { 136 | const root = await this.psRepository.findOne({ where: { alias: '总分类' } }); 137 | if (!root) { 138 | await this.pageSortService.createPageSort({ name: '总分类', alias: '总分类', parent: { id: 0 } }); 139 | } 140 | } 141 | 142 | private async createProperties() { 143 | const exist = await this.cpRepo.find(); 144 | if (!exist.length) { 145 | await this.cpRepo.save(this.cpRepo.create({})); 146 | } 147 | } 148 | 149 | } -------------------------------------------------------------------------------- /src/grpc.client-factory.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { Client, ClientGrpc, GrpcOptions, Transport } from '@nestjs/microservices'; 3 | import { join } from 'path'; 4 | 5 | @Injectable() 6 | export class NotaddGrpcClientFactory { 7 | @Client(generateGrpcOptions('localhost:50051', 'nt_module_user', 'nt_module_user.proto')) 8 | public readonly userModuleClient: ClientGrpc; 9 | } 10 | 11 | export function generateGrpcOptions(url: string, packageName: string, protoFileName: string): GrpcOptions { 12 | return { 13 | transport: Transport.GRPC, 14 | options: { 15 | url, 16 | package: packageName, 17 | protoPath: join(__dirname, 'grpc/protobufs/' + protoFileName), 18 | loader: { 19 | arrays: true 20 | } 21 | } 22 | }; 23 | } -------------------------------------------------------------------------------- /src/grpc/protobufs/nt_module_user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package nt_module_user; 4 | 5 | message PlaceholderRequest {} 6 | 7 | message StringDataResponse { 8 | int32 code = 1; 9 | string message = 2; 10 | string data = 3; 11 | } 12 | 13 | message FindDataByPageRequest { 14 | int32 pageNumber = 1; 15 | int32 pageSize = 2; 16 | } 17 | 18 | service InfoGroupService { 19 | rpc CreateInfoGroup(CreateInfoGroupRequest) returns (StringDataResponse) {} 20 | rpc DeleteInfoGroup(DeleteInfoGroupRequest) returns (StringDataResponse) {} 21 | rpc UpdateInfoGroup(UpdateInfoGroupRequest) returns (StringDataResponse) {} 22 | rpc AddInfoItemToInfoGroup(AddInfoItemToInfoGroupRequest) returns (StringDataResponse) {} 23 | rpc DeleteIntoItemFromInfoGroup(DeleteIntoItemFromInfoGroupRequest) returns (StringDataResponse) {} 24 | rpc FindAllInfoGroup(FindDataByPageRequest) returns (FindAllInfoGroupResponse) {} 25 | rpc FindInfoItemsByGroupId(FindInfoItemsByGroupIdRequest) returns (FindInfoItemsByGroupIdResponse) {} 26 | } 27 | 28 | message CreateInfoGroupRequest { 29 | string name = 1; 30 | int32 roleId = 2; 31 | } 32 | 33 | message DeleteInfoGroupRequest { int32 groupId = 1; } 34 | 35 | message UpdateInfoGroupRequest { 36 | int32 groupId = 1; 37 | string name = 2; 38 | int32 roleId = 3; 39 | } 40 | 41 | message AddInfoItemToInfoGroupRequest { 42 | int32 infoGroupId = 1; 43 | repeated int32 infoItemIds = 2; 44 | } 45 | 46 | message DeleteIntoItemFromInfoGroupRequest { 47 | int32 infoGroupId = 1; 48 | repeated int32 infoItemIds = 2; 49 | } 50 | 51 | message FindAllInfoGroupResponse { 52 | int32 code = 1; 53 | string message = 2; 54 | repeated InfoGroup data = 3; 55 | int32 count = 4; 56 | } 57 | message InfoGroup { 58 | int32 id = 1; 59 | string name = 2; 60 | } 61 | 62 | message FindInfoItemsByGroupIdRequest { int32 infoGroupId = 1; } 63 | 64 | message FindInfoItemsByGroupIdResponse { 65 | int32 code = 1; 66 | string message = 2; 67 | repeated InfoItem data = 3; 68 | } 69 | message InfoItem { 70 | int32 id = 1; 71 | string name = 2; 72 | string description = 3; 73 | string type = 4; 74 | bool registerDisplay = 5; 75 | bool informationDisplay = 6; 76 | int32 order = 7; 77 | } 78 | 79 | service InfoItemService { 80 | rpc CreateInfoItem(CreateInfoItemRequest) returns (StringDataResponse) {} 81 | rpc DeleteInfoItem(DeleteInfoItemRequest) returns (StringDataResponse) {} 82 | rpc UpdateInfoItem(UpdateInfoItemRequest) returns (StringDataResponse) {} 83 | rpc FindAllInfoItem(FindDataByPageRequest) returns (FindAllInfoItemResponse) {} 84 | } 85 | 86 | message CreateInfoItemRequest { 87 | InfoItem infoItemInput = 1; 88 | message InfoItemInput { 89 | string name = 1; 90 | string description = 2; 91 | string type = 3; 92 | bool registerDisplay = 4; 93 | bool informationDisplay = 5; 94 | int32 order = 6; 95 | } 96 | } 97 | 98 | message DeleteInfoItemRequest { int32 infoItemId = 1; } 99 | 100 | message UpdateInfoItemRequest { InfoItem updateInfoItemInput = 1; } 101 | 102 | message FindAllInfoItemResponse { 103 | int32 code = 1; 104 | string message = 2; 105 | repeated InfoItem data = 3; 106 | int32 count = 4; 107 | } 108 | 109 | service OrganizationService { 110 | rpc CreateOrganization(CreateOrganizationRequest) returns (StringDataResponse) {} 111 | rpc AddUsersToOrganization(AddUsersToOrganizationRequest) returns (StringDataResponse) {} 112 | rpc DeleteOrganization(DeleteOrganizationRequest) returns (StringDataResponse) {} 113 | rpc DeleteUserFromOrganization(DeleteUserFromOrganizationRequest) returns (StringDataResponse) {} 114 | rpc UpdateOrganization(UpdateOrganizationRequest) returns (StringDataResponse) {} 115 | rpc FindRootOrganizations(PlaceholderRequest) returns (FindRootOrganizationsResponse) {} 116 | rpc FindAllOrganizations(PlaceholderRequest) returns (StringDataResponse) {} 117 | rpc FindChildrenOrganizations(FindChildrenOrganizationsRequest) returns (StringDataResponse) {} 118 | } 119 | 120 | message CreateOrganizationRequest { 121 | string name = 1; 122 | int32 parentId = 2; 123 | } 124 | 125 | message AddUsersToOrganizationRequest { 126 | int32 id = 1; 127 | repeated int32 userIds = 2; 128 | } 129 | 130 | message DeleteOrganizationRequest { int32 id = 1; } 131 | 132 | message DeleteUserFromOrganizationRequest { 133 | int32 id = 1; 134 | repeated int32 userIds = 2; 135 | } 136 | 137 | message UpdateOrganizationRequest { 138 | int32 id = 1; 139 | string name = 2; 140 | int32 parentId = 3; 141 | } 142 | 143 | message FindRootOrganizationsResponse { 144 | int32 code = 1; 145 | string message = 2; 146 | repeated Organization data = 3; 147 | } 148 | message Organization { 149 | int32 id = 1; 150 | string name = 2; 151 | } 152 | 153 | message FindChildrenOrganizationsRequest { int32 id = 1; } 154 | 155 | service SystemModuleService { 156 | rpc FindSystemModules(FindDataByPageRequest) returns (FindSystemModuleResponse) {} 157 | } 158 | 159 | message FindSystemModuleResponse { 160 | int32 code = 1; 161 | string message = 2; 162 | repeated SystemModule data = 3; 163 | message SystemModule { 164 | int32 id = 1; 165 | string name = 2; 166 | } 167 | int32 count = 4; 168 | } 169 | 170 | service ResourceService { 171 | rpc FindResources(FindResourcesRequest) returns (FindResourcesResponse) {} 172 | rpc SaveResourcesAndPermissions(SaveResourcesAndPermissionsRequest) returns (StringDataResponse) {} 173 | } 174 | 175 | message FindResourcesRequest { 176 | int32 systemModuleId = 1; 177 | int32 pageNumber = 2; 178 | int32 pageSize = 3; 179 | } 180 | 181 | message SaveResourcesAndPermissionsRequest { string metadata = 1; } 182 | 183 | message FindResourcesResponse { 184 | int32 code = 1; 185 | string message = 2; 186 | repeated Resource data = 3; 187 | message Resource { 188 | int32 id = 1; 189 | string name = 2; 190 | string identify = 3; 191 | repeated Permission permissions = 4; 192 | } 193 | int32 count = 4; 194 | } 195 | message Permission { 196 | int32 id = 1; 197 | string name = 2; 198 | string action = 3; 199 | string identify = 4; 200 | } 201 | 202 | service RoleService { 203 | rpc CreateRole(CreateRoleRequest) returns (StringDataResponse) {} 204 | rpc DeleteRole(DeleteRoleRequest) returns (StringDataResponse) {} 205 | rpc UpdateRole(UpdateRoleRequest) returns (StringDataResponse) {} 206 | rpc RemovePermissionOfRole(RemovePermissionRequest) returns (StringDataResponse); 207 | rpc SetPermissionsToRole(SetPermissionToRoleRequest) returns (StringDataResponse) {} 208 | rpc FindRoles(FindDataByPageRequest) returns (FindRolesResponse) {} 209 | rpc FindOneRoleInfo(FindOneRoleInfoRequest) returns (FindOneRoleInfoResponse) {} 210 | } 211 | 212 | message CreateRoleRequest { string name = 1; } 213 | 214 | message DeleteRoleRequest { int32 id = 1; } 215 | 216 | message UpdateRoleRequest { 217 | int32 id = 1; 218 | string name = 2; 219 | } 220 | 221 | message RemovePermissionRequest { 222 | int32 roleId = 1; 223 | int32 permissionId = 2; 224 | } 225 | 226 | message SetPermissionToRoleRequest { 227 | int32 roleId = 1; 228 | repeated int32 permissionIds = 2; 229 | } 230 | 231 | message FindRolesResponse { 232 | int32 code = 1; 233 | string message = 2; 234 | repeated RoleData data = 3; 235 | int32 count = 4; 236 | } 237 | message RoleData { 238 | int32 id = 1; 239 | string name = 2; 240 | } 241 | 242 | message FindOneRoleInfoRequest { int32 roleId = 1; } 243 | 244 | message FindOneRoleInfoResponse { 245 | int32 code = 1; 246 | string message = 2; 247 | OneRoleInfoData data = 3; 248 | } 249 | message OneRoleInfoData { 250 | int32 id = 1; 251 | string name = 2; 252 | repeated Permission permissions = 3; 253 | repeated InfoItem infoItems = 4; 254 | } 255 | 256 | service UserService { 257 | rpc Login(LoginRequest) returns (LoginResponse) {} 258 | rpc Register(RegisterRequest) returns (StringDataResponse) {} 259 | rpc CreateUser(CreateUserRequest) returns (StringDataResponse) {} 260 | rpc AddUserRole(AddOrDeleteUserRoleRequest) returns (StringDataResponse) {} 261 | rpc DeleteUserRole(AddOrDeleteUserRoleRequest) returns (StringDataResponse) {} 262 | rpc BanUser(DeleteUserRequest) returns (StringDataResponse) {} 263 | rpc RecycleUser(DeleteUserRequest) returns (StringDataResponse) {} 264 | rpc DeleteRecycledUser(DeleteUserRequest) returns (StringDataResponse) {} 265 | rpc RevertBannedUser(DeleteUserRequest) returns (StringDataResponse) {} 266 | rpc RevertRecycledUser(DeleteUserRequest) returns (StringDataResponse) {} 267 | rpc UpdateUserInfoById(UpdateUserInfoByIdRequest) returns (StringDataResponse) {} 268 | rpc UpdateCurrentUserInfo(UpdateCurrentUserInfoRequest) returns (StringDataResponse) {} 269 | rpc FindUserInfoByIds(FindUserInfoByIdsRequest) returns (FindUserInfoByIdsResponse) {} 270 | rpc FindCurrentUserInfo(FindCurrentUserInfoRequest) returns (FindCurrentUserInfoResponse) {} 271 | rpc FindRegisterUserInputInfo(PlaceholderRequest) returns (FindRegisterUserInputInfoResponse) {} 272 | rpc FindAllUsers(FindDataByPageRequest) returns (FindAllUsersResponse) {} 273 | rpc FindUsersInRole(FindUsersInRoleRequest) returns (FindUsersInRoleResponse) {} 274 | rpc FindUsersInOrganization(FindUsersInOrganizationRequest) returns (FindUsersInOrganizationResponse) {} 275 | rpc FindOneWithRolesAndPermissions(FindOneWithRolesAndPermissionsRequest) returns (FindOneWithRolesAndPermissionsResponse) {} 276 | rpc AddPermissionToUser(AddOrDeleteUserPermRequest) returns (StringDataResponse) {} 277 | rpc DeletePermissionOfUser(AddOrDeleteUserPermRequest) returns (StringDataResponse) {} 278 | } 279 | 280 | message LoginRequest { 281 | string username = 1; 282 | string password = 2; 283 | } 284 | 285 | message LoginResponse { 286 | int32 code = 1; 287 | string message = 2; 288 | LoginResponseData data = 3; 289 | message LoginResponseData { 290 | TokenInfo tokenInfo = 1; 291 | UserData userInfoData = 2; 292 | } 293 | message TokenInfo { 294 | string accessToken = 1; 295 | int32 expiresIn = 2; 296 | } 297 | } 298 | 299 | message RegisterRequest { 300 | RegisterUserInput registerUserInput = 1; 301 | message RegisterUserInput { 302 | string username = 1; 303 | string email = 2; 304 | string mobile = 3; 305 | string password = 4; 306 | repeated CreateUserInfoInfoKV infoKVs = 5; 307 | } 308 | } 309 | 310 | message CreateUserRequest { 311 | CreateUserInput createUserInput = 1; 312 | message CreateUserInput { 313 | string username = 1; 314 | string email = 2; 315 | string mobile = 3; 316 | string password = 4; 317 | repeated CreateUserInfoInfoKV infoKVs = 5; 318 | repeated int32 roleIds = 6; 319 | repeated int32 organizationIds = 7; 320 | } 321 | } 322 | 323 | message CreateUserInfoInfoKV { 324 | int32 infoItemId = 1; 325 | string userInfoValue = 2; 326 | } 327 | 328 | message AddOrDeleteUserRoleRequest { 329 | int32 userId = 1; 330 | int32 roleId = 2; 331 | } 332 | 333 | message DeleteUserRequest { int32 userId = 1; } 334 | 335 | message UpdateUserInfoByIdRequest { 336 | int32 userId = 1; 337 | UpdateUserInput updateUserInput = 2; 338 | } 339 | 340 | message UpdateUserInput { 341 | string username = 1; 342 | string email = 2; 343 | string mobile = 3; 344 | string password = 4; 345 | bool banned = 5; 346 | repeated UpdateUserInfoKV infoKVs = 6; 347 | message UpdateUserInfoKV { 348 | int32 userInfoId = 1; 349 | string userInfoValue = 2; 350 | int32 infoItemId = 3; 351 | } 352 | repeated ChangedUserRoleOrOrganization roleIds = 7; 353 | repeated ChangedUserRoleOrOrganization organizationIds = 8; 354 | message ChangedUserRoleOrOrganization { 355 | int32 before = 1; 356 | int32 after = 2; 357 | } 358 | } 359 | 360 | message UpdateCurrentUserInfoRequest { 361 | int32 userId = 1; 362 | UpdateUserInput updateCurrentUserInput = 2; 363 | } 364 | 365 | message FindUserInfoByIdsRequest { repeated int32 userIds = 1; } 366 | 367 | message FindUserInfoByIdsResponse { 368 | int32 code = 1; 369 | string message = 2; 370 | repeated UserData data = 3; 371 | } 372 | message UserData { 373 | int32 id = 1; 374 | string username = 2; 375 | string email = 3; 376 | string mobile = 4; 377 | bool banned = 5; 378 | bool recycle = 6; 379 | string createdAt = 7; 380 | string updatedAt = 8; 381 | repeated UserRoleData userRoles = 9; 382 | repeated UserOrganizationData userOrganizations = 10; 383 | repeated UserInfoData userInfos = 11; 384 | } 385 | message UserRoleData { 386 | int32 id = 1; 387 | string name = 2; 388 | } 389 | message UserOrganizationData { 390 | int32 id = 1; 391 | string name = 2; 392 | } 393 | message UserInfoData { 394 | int32 id = 1; 395 | int32 order = 2; 396 | int32 infoItemId = 3; 397 | string type = 4; 398 | string name = 5; 399 | string value = 6; 400 | string description = 7; 401 | bool registerDisplay = 8; 402 | bool informationDisplay = 9; 403 | } 404 | 405 | message FindCurrentUserInfoRequest { int32 userId = 1; } 406 | 407 | message FindCurrentUserInfoResponse { 408 | int32 code = 1; 409 | string message = 2; 410 | UserData data = 3; 411 | } 412 | 413 | message FindRegisterUserInputInfoResponse { 414 | int32 code = 1; 415 | string message = 2; 416 | repeated InfoItem data = 3; 417 | } 418 | 419 | message FindAllUsersResponse { 420 | int32 code = 1; 421 | string message = 2; 422 | repeated UserData data = 3; 423 | int32 count = 4; 424 | } 425 | 426 | message FindUsersInRoleRequest { 427 | int32 roleId = 1; 428 | int32 pageNumber = 2; 429 | int32 pageSize = 3; 430 | } 431 | 432 | message FindUsersInRoleResponse { 433 | int32 code = 1; 434 | string message = 2; 435 | repeated UserData data = 3; 436 | int32 count = 4; 437 | } 438 | 439 | message FindUsersInOrganizationRequest { 440 | int32 organizationId = 1; 441 | int32 pageNumber = 2; 442 | int32 pageSize = 3; 443 | } 444 | 445 | message FindUsersInOrganizationResponse { 446 | int32 code = 1; 447 | string message = 2; 448 | repeated UserData data = 3; 449 | int32 count = 4; 450 | } 451 | 452 | message FindOneWithRolesAndPermissionsRequest { string username = 1; } 453 | 454 | message FindOneWithRolesAndPermissionsResponse { 455 | int32 code = 1; 456 | string message = 2; 457 | UserRoleAndPermissionData data = 3; 458 | message UserRoleAndPermissionData { 459 | int32 id = 1; 460 | string username = 2; 461 | string email = 3; 462 | string mobile = 4; 463 | bool banned = 5; 464 | bool recycle = 6; 465 | string createdAt = 7; 466 | string updatedAt = 8; 467 | repeated UserRole roles = 9; 468 | message UserRole { 469 | int32 id = 1; 470 | string name = 2; 471 | repeated Permission permissions = 3; 472 | } 473 | repeated PersonalPermission personalPermissions = 10; 474 | message PersonalPermission { 475 | int32 id = 1; 476 | string status = 2; 477 | Permission permission = 3; 478 | } 479 | } 480 | } 481 | 482 | message AddOrDeleteUserPermRequest { 483 | int32 userId = 1; 484 | int32 permissionId = 2; 485 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | 3 | import { CmsModule } from './cms.module'; 4 | import { Transport } from '@nestjs/common/enums/transport.enum'; 5 | import { join } from 'path'; 6 | 7 | async function bootstrap() { 8 | const app = await NestFactory.createMicroservice(CmsModule, { 9 | transport: Transport.GRPC, 10 | options: { 11 | url: 'localhost:50052', 12 | package: 'nt_module_cms', 13 | protoPath: join(__dirname, 'nt_module_cms.proto'), 14 | loader: { 15 | arrays: true 16 | } 17 | } 18 | }); 19 | await app.listenAsync(); 20 | } 21 | 22 | bootstrap(); -------------------------------------------------------------------------------- /src/pages/controllers/page-sort.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { PageSort } from "../entities/page-sort.entity"; 4 | import { PageSortService } from "../services/page-sort.service"; 5 | import { CreatePageSort } from "../interfaces/page-sort.interface"; 6 | 7 | @Controller() 8 | export class PageSortController { 9 | constructor( 10 | @Inject(PageSortService) private readonly psService: PageSortService 11 | ) { } 12 | 13 | @GrpcMethod('PageSortService') 14 | async createPageSort(body: { createPageSortInput: CreatePageSort }) { 15 | await this.psService.createPageSort(body.createPageSortInput); 16 | return { code: 200, message: '创建页面分类成功!' }; 17 | } 18 | 19 | @GrpcMethod('PageSortService') 20 | async updatePageSort(body: { pageSort: PageSort }) { 21 | await this.psService.updatePageSort(body.pageSort); 22 | return { code: 200, message: '修改页面分类成功!' }; 23 | } 24 | 25 | @GrpcMethod('PageSortService') 26 | async deletePageSort(body: { id: number }) { 27 | await this.psService.deletePageSort(body.id); 28 | return { code: 200, message: '删除页面分类成功!' }; 29 | } 30 | 31 | @GrpcMethod('PageSortService') 32 | async getAllPageSort() { 33 | const data = await this.psService.getAllPageSort(); 34 | return { code: 200, message: '查询成功!', data: JSON.stringify(data)}; 35 | } 36 | 37 | @GrpcMethod('PageSortService') 38 | async getOnePageSort(body: { id: number }) { 39 | const data = await this.psService.getOnePageSort(body.id); 40 | return { code: 200, message: '查询成功!', data } 41 | } 42 | } -------------------------------------------------------------------------------- /src/pages/controllers/page.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { Page } from "../entities/page.entity"; 3 | import { GrpcMethod } from "@nestjs/microservices"; 4 | import { PageService } from "../services/page.service"; 5 | import { pageInput } from "../interfaces/page.interface"; 6 | 7 | @Controller() 8 | export class PageController { 9 | constructor( 10 | @Inject(PageService) private readonly pageService: PageService 11 | ) { } 12 | 13 | @GrpcMethod('PageService') 14 | async createPage(body: { page: pageInput }) { 15 | await this.pageService.createPage(body.page); 16 | return { code: 200, message: '创建页面成功!' }; 17 | } 18 | 19 | @GrpcMethod('PageService') 20 | async updatePage(body: { page: Page }) { 21 | await this.pageService.updatePage(body.page); 22 | return { code: 200, message: '修改页面成功!' }; 23 | } 24 | 25 | @GrpcMethod('PageService') 26 | async deletePage(body: { id: number }) { 27 | await this.pageService.deletePage(body.id); 28 | return { code: 200, message: '删除成功!' }; 29 | } 30 | 31 | @GrpcMethod('PageService') 32 | async getAllPage(body: { pageNumber: number, pageSize: number, name: string }) { 33 | const data = (await this.pageService.getAllPage(body.pageNumber, body.pageSize, body.name)).data; 34 | const total = (await this.pageService.getAllPage(body.pageNumber, body.pageSize, body.name)).total; 35 | return { code: 200, message: '查询成功!', data, total } 36 | } 37 | 38 | @GrpcMethod('PageService') 39 | async getOnePage(body: { alias: string }) { 40 | const data = await this.pageService.getOnePage(body.alias); 41 | return { code: 200, message: '查询成功!', data }; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/pages/entities/content.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm"; 2 | import { Page } from "./page.entity"; 3 | 4 | @Entity('content') 5 | export class Content{ 6 | @PrimaryGeneratedColumn({ 7 | comment: '自增id' 8 | }) 9 | id: number; 10 | 11 | @Column({ 12 | comment: '显示名称', 13 | nullable:true 14 | }) 15 | name: string; 16 | 17 | @Column({ 18 | comment: '别名', 19 | unique: true 20 | }) 21 | alias: string; 22 | 23 | @Column({ 24 | comment: '页面内容', 25 | type: 'text', 26 | nullable: true 27 | }) 28 | value: string; 29 | 30 | @ManyToOne(type=>Page,page=>page.contents, { onDelete: 'CASCADE', cascade: true }) 31 | page: Page; 32 | 33 | } -------------------------------------------------------------------------------- /src/pages/entities/page-sort.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | Column, 4 | Entity, 5 | OneToMany, Tree, TreeChildren, TreeParent 6 | } from 'typeorm'; 7 | import { Page } from './page.entity'; 8 | 9 | @Entity('page_sort') 10 | @Tree('nested-set') 11 | export class PageSort { 12 | /*分类Id*/ 13 | @PrimaryGeneratedColumn() 14 | id: number; 15 | 16 | /*分类名称*/ 17 | @Column({ 18 | nullable: false, 19 | length: 120, 20 | }) 21 | name: string; 22 | 23 | @Column({ 24 | comment: '分类别名', 25 | unique: true 26 | }) 27 | alias: string; 28 | 29 | @TreeChildren() 30 | children: PageSort[]; 31 | 32 | @TreeParent() 33 | parent: PageSort; 34 | 35 | @OneToMany(type => Page,page => page.pageSort) 36 | pages: Page[]; 37 | } 38 | -------------------------------------------------------------------------------- /src/pages/entities/page.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm"; 2 | import { Content } from "./content.entity"; 3 | import { PageSort } from "./page-sort.entity"; 4 | 5 | @Entity('page') 6 | export class Page { 7 | @PrimaryGeneratedColumn({ 8 | comment: '自增id' 9 | }) 10 | id: number; 11 | 12 | @Column({ 13 | comment: '页面名称' 14 | }) 15 | name: string; 16 | 17 | @Column({ 18 | comment: '页面别名' 19 | }) 20 | alias: string; 21 | 22 | @Column({ 23 | comment: '最后修改时间', 24 | nullable: true 25 | }) 26 | lastUpdateTime: string; 27 | 28 | @OneToMany(type => Content, content => content.page, { cascade: ['insert', 'update'] }) 29 | contents: Content[]; 30 | 31 | @ManyToOne(type => PageSort, pageSort => pageSort.pages) 32 | pageSort: PageSort; 33 | 34 | } -------------------------------------------------------------------------------- /src/pages/interfaces/page-sort.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreatePageSort { 2 | name: string; 3 | alias: string; 4 | parent: { id: number }; 5 | } -------------------------------------------------------------------------------- /src/pages/interfaces/page.interface.ts: -------------------------------------------------------------------------------- 1 | export interface pageInput { 2 | name: string; 3 | alias: string; 4 | pageSortId: number; 5 | contents: { name: string, alias: string, value: string }[]; 6 | } -------------------------------------------------------------------------------- /src/pages/services/page-sort.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { InjectRepository } from "@nestjs/typeorm"; 3 | import { PageSort } from "../entities/page-sort.entity"; 4 | import { TreeRepository, In } from "typeorm"; 5 | import { RpcException } from "@nestjs/microservices"; 6 | import { Page } from "../entities/page.entity"; 7 | import { CreatePageSort } from "../interfaces/page-sort.interface"; 8 | 9 | @Injectable() 10 | export class PageSortService { 11 | constructor( 12 | @InjectRepository(PageSort) private readonly psRepo: TreeRepository, 13 | @InjectRepository(Page) private readonly pageRepo: TreeRepository, 14 | ) { } 15 | 16 | async createPageSort(pageSort: CreatePageSort) { 17 | try { 18 | const ignore = await this.psRepo.count(); 19 | if (!pageSort.parent.id || ignore <= 0) { 20 | await this.psRepo.save({ name: pageSort.name, alias: pageSort.alias }); 21 | return { code: 200, message: '创建成功' }; 22 | } 23 | if (pageSort.parent) { 24 | const exist = await this.psRepo.findOne(pageSort.parent.id); 25 | if (!exist) { 26 | throw new RpcException({ code: 406, message: '当前分类父节点不存在!' }); 27 | } 28 | pageSort.parent = exist; 29 | } 30 | const result = await this.psRepo.findOne({ where: { alias: pageSort.alias } }); 31 | if (result) { 32 | throw new RpcException({ code: 406, message: '别名重复!' }); 33 | } 34 | await this.psRepo.save(this.psRepo.create(pageSort)); 35 | } catch (error) { 36 | throw new RpcException({ code: 500, message: error.toString() }); 37 | } 38 | } 39 | 40 | async updatePageSort(pageSort: PageSort) { 41 | const exist = await this.psRepo.findOne(pageSort.id); 42 | if (!exist) { 43 | throw new RpcException({ code: 404, message: '该页面分类不存在!' }); 44 | } 45 | if (pageSort.alias && pageSort.alias !== exist.alias) { 46 | if (await this.psRepo.findOne({ alias: pageSort.alias })) { 47 | throw new RpcException({ code: 406, message: '别名重复!' }); 48 | } 49 | } 50 | const parent = await this.psRepo.findOne(pageSort.parent.id); 51 | if (!parent) { 52 | throw new RpcException({ code: 404, message: '该上级分类不存在!' }); 53 | } 54 | try { 55 | await this.psRepo.save(await this.psRepo.create(pageSort)); 56 | } catch (error) { 57 | throw new RpcException({ code: 500, message: error.toString() }); 58 | } 59 | } 60 | 61 | async deletePageSort(id: number) { 62 | const pageSort = await this.psRepo.findOne(id); 63 | if (!pageSort) { 64 | throw new RpcException({ code: 404, message: '该页面分类不存在!' }); 65 | } 66 | const array = await this.getAllClassifyIds(id); 67 | const pages = await this.pageRepo.count({ where: { pageSort: In(array) } }); 68 | if (pages > 0) { 69 | throw new RpcException({ code: 403, message: '当前分类下有页面,不能删除' }) 70 | } 71 | array.splice(array.indexOf(id), 1); 72 | if (array.length) { 73 | throw new RpcException({ code: 403, message: '当前分类下有子分类,不能删除' }); 74 | } 75 | await this.psRepo.remove(pageSort); 76 | } 77 | 78 | async getAllPageSort() { 79 | return await this.psRepo.findTrees(); 80 | } 81 | 82 | async getOnePageSort(id:number) { 83 | const exist = await this.psRepo.findOne(id); 84 | if(!exist){ 85 | throw new RpcException({code:200,message:'该页面分类不存在!'}); 86 | } 87 | const data = await this.psRepo.findDescendantsTree(exist); 88 | return data; 89 | } 90 | 91 | async getAllClassifyIds(idNum: number): Promise { 92 | const array: number[] = []; 93 | const classify = await this.psRepo.findOne({ id: idNum }); 94 | await this.psRepo.findDescendants(classify).then(a => { 95 | if (a) { 96 | a.map(a => { 97 | array.push(a.id); 98 | }); 99 | } 100 | }); 101 | return array; 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /src/pages/services/page.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { InjectRepository } from "@nestjs/typeorm"; 3 | import { Page } from "../entities/page.entity"; 4 | import { Repository, Like } from "typeorm"; 5 | import { RpcException } from "@nestjs/microservices"; 6 | import { Content } from "../entities/content.entity"; 7 | import * as moment from 'moment'; 8 | import { PageSort } from "../entities/page-sort.entity"; 9 | import { pageInput } from "../interfaces/page.interface"; 10 | 11 | @Injectable() 12 | export class PageService { 13 | constructor( 14 | @InjectRepository(Page) private readonly pageRepo: Repository, 15 | @InjectRepository(PageSort) private readonly psRepo: Repository, 16 | @InjectRepository(Content) private readonly contentRepo: Repository, 17 | ) { } 18 | 19 | async createPage(page: pageInput) { 20 | const ps = await this.psRepo.findOne(page.pageSortId); 21 | if (!ps) { 22 | throw new RpcException({ code: 404, message: '该页面分类不存在!' }); 23 | } 24 | const exist = await this.pageRepo.findOne({ where: { alias: page.alias } }); 25 | if (exist) { 26 | throw new RpcException({ code: 406, message: '别名重复!' }); 27 | } 28 | const time = moment().format('YYYY-MM-DD HH:mm:ss'); 29 | try { 30 | const result = await this.pageRepo.save(this.pageRepo.create({ 31 | name: page.name, 32 | alias: page.alias, 33 | lastUpdateTime: time, 34 | pageSort: ps 35 | })); 36 | for (const i of page.contents) { 37 | await this.contentRepo.save(this.contentRepo.create({ 38 | name: i.name, 39 | alias: i.alias, 40 | value: i.value, 41 | page: result 42 | })); 43 | } 44 | } catch (error) { 45 | throw new RpcException({ code: 400, message: error.toString() }); 46 | } 47 | } 48 | 49 | async updatePage(page: Page) { 50 | const exist = await this.pageRepo.findOne(page.id, { relations: ['contents'] }); 51 | const pageSort = await this.psRepo.findOne({ where: { id: page.pageSort } }); 52 | if (!pageSort) { 53 | throw new RpcException({ code: 404, message: '该页面分类不存在!' }); 54 | } 55 | exist.pageSort = pageSort; 56 | if (page.alias && page.alias !== exist.alias) { 57 | if (await this.pageRepo.findOne({ where: { alias: page.alias, pageSort: page.pageSort } })) { 58 | throw new RpcException({ code: 406, message: '页面别名重复!' }); 59 | } 60 | } 61 | if (page.contents) { 62 | const same = await this.contentRepo.find({ where: { page: page.id } }); 63 | await this.contentRepo.remove(same); 64 | } 65 | await this.pageRepo.save(this.pageRepo.create(page)); 66 | } 67 | 68 | async deletePage(id: number) { 69 | const exist = await this.pageRepo.findOne(id); 70 | await this.pageRepo.remove(exist); 71 | } 72 | 73 | async getAllPage(pageNumber: number, pageSize: number, name: string) { 74 | const result = await this.pageRepo.findAndCount({ 75 | where: { 76 | name: Like(`%${name ? name : ''}%`) 77 | }, 78 | order: { lastUpdateTime: 'DESC' }, 79 | relations: ['contents'], 80 | skip: pageSize * (pageNumber - 1), 81 | take: pageSize 82 | }) 83 | return { data: result[0], total: result[1] }; 84 | } 85 | 86 | async getOnePage(alias: string) { 87 | const result = await this.pageRepo.findOne({ where: { alias }, relations: ['contents', 'pageSort'] }); 88 | const a = { 89 | id: result.id, 90 | name: result.name, 91 | alias: result.alias, 92 | lastUpdateTime: result.lastUpdateTime, 93 | contents: result.contents.length ? result.contents.map(item => { 94 | return { 95 | id: item.id, 96 | name: item.name, 97 | alias: item.alias, 98 | value: item.value 99 | } 100 | }) : [], 101 | pageSortId: result.pageSort.id 102 | }; 103 | return a; 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/plugin/comment/controllers/comment-properties.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { CommentPropertiesService } from "../services/comment-properties.service"; 4 | 5 | @Controller() 6 | export class CommentPropertiesController { 7 | constructor( 8 | @Inject(CommentPropertiesService) private readonly cpService: CommentPropertiesService, 9 | ) { } 10 | 11 | @GrpcMethod('CommentPropertiesService') 12 | async getAllCommentProperties() { 13 | const data = await this.cpService.getAllCommentProperties(); 14 | return { code: 200, message: '查询成功!', data }; 15 | } 16 | 17 | @GrpcMethod('CommentPropertiesService') 18 | async updateCommentProperties(body: { id: number, name: string }) { 19 | await this.cpService.updateCommentProperties(body.id, body.name); 20 | return { code: 200, message: '修改成功!' }; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/plugin/comment/controllers/discuss.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { DiscussService } from "../services/discuss.service"; 4 | import { Discuss } from "../entities/discuss.entity"; 5 | import { CreateDiscuss } from "../interfaces/discuss.interface"; 6 | 7 | @Controller() 8 | export class DiscussController { 9 | constructor( 10 | @Inject(DiscussService) private readonly discussService: DiscussService 11 | ) { } 12 | 13 | @GrpcMethod('DiscussService') 14 | async createDiscuss(body: { discuss: CreateDiscuss }) { 15 | await this.discussService.createDiscuss(body.discuss); 16 | return { code: 200, message: '评论成功!' }; 17 | } 18 | 19 | @GrpcMethod('DiscussService') 20 | async deleteDiscuss(body: { id: number }) { 21 | await this.discussService.deleteDiscuss(body.id); 22 | return { code: 200, message: '删除成功!' }; 23 | } 24 | 25 | @GrpcMethod('DiscussService') 26 | async auditDiscuss(body: { id: number, op: number }) { 27 | await this.discussService.auditDiscuss(body.id, body.op); 28 | return { code: 200, message: '审核成功!' }; 29 | } 30 | 31 | @GrpcMethod('DiscussService') 32 | async getAllDiscusss(body: { pageNUmber: number, pageSize: number, content: string, artTitle: string, artId: number, username: string, startTime: string, endTime: string }) { 33 | const result = await this.discussService.getAllDiscusss(body.pageNUmber, body.pageSize, body.content, body.artTitle, body.artId, body.username, body.startTime, body.endTime); 34 | return { code: 200, message: '查询成功!', total: result.total, data: result.data }; 35 | } 36 | 37 | @GrpcMethod('DiscussService') 38 | async updateDiscuss(body: { discuss: Discuss }) { 39 | await this.discussService.updateDiscuss(body.discuss); 40 | return { code: 200, message: '修改成功!' }; 41 | } 42 | 43 | @GrpcMethod('DiscussService') 44 | async getOneDiscuss(body: { id: number }) { 45 | const data = await this.discussService.getOneDiscuss(body.id); 46 | return { code: 200, message: '查询成功!', data }; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/plugin/comment/entities/comment-properties.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; 2 | 3 | @Entity('comment_properties') 4 | export class CommentProperties { 5 | 6 | @PrimaryGeneratedColumn() 7 | id: number; 8 | 9 | @Column({ 10 | comment: '开启评论', 11 | default: true 12 | }) 13 | enableComment: boolean; 14 | 15 | @Column({ 16 | comment: '开启审核', 17 | default: false 18 | }) 19 | needAudit: boolean; 20 | 21 | @Column({ 22 | comment: '开启star', 23 | default: false 24 | }) 25 | enableStar: boolean; 26 | 27 | 28 | } -------------------------------------------------------------------------------- /src/plugin/comment/entities/discuss.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; 2 | import * as moment from 'moment'; 3 | import { Article } from "src/articles/entities/article.entity"; 4 | 5 | @Entity('discuss') 6 | export class Discuss { 7 | @PrimaryGeneratedColumn() 8 | id: number; 9 | 10 | @Column({ 11 | comment: '评论内容', 12 | type: 'text' 13 | }) 14 | content: string; 15 | 16 | @Column({ 17 | nullable: true, 18 | transformer: { 19 | from: (date) => { 20 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 21 | }, 22 | to: (date) => { 23 | date = date ? date : new Date(); 24 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 25 | } 26 | } 27 | }) 28 | time: string; 29 | 30 | // 0:待审核,1:审核通过,2: 拒绝 31 | @Column({ 32 | comment: '评论状态', 33 | default: 0 34 | }) 35 | status: number; 36 | 37 | @Column({ 38 | comment: '发布人', 39 | nullable: true 40 | }) 41 | userId: number; 42 | 43 | @ManyToOne(type => Article, article => article.discuss, { onDelete: 'CASCADE', cascade: true }) 44 | article: Article; 45 | 46 | } -------------------------------------------------------------------------------- /src/plugin/comment/interfaces/discuss.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreateDiscuss{ 2 | content: string; 3 | artId: number; 4 | userId: number; 5 | } -------------------------------------------------------------------------------- /src/plugin/comment/services/comment-properties.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from "@nestjs/common"; 2 | import { InjectRepository } from "@nestjs/typeorm"; 3 | import { CommentProperties } from "../entities/comment-properties.entity"; 4 | import { Repository } from "typeorm"; 5 | import { RpcException } from "@nestjs/microservices"; 6 | 7 | @Injectable() 8 | export class CommentPropertiesService { 9 | constructor( 10 | @InjectRepository(CommentProperties) private readonly cpRepo: Repository, 11 | ) { } 12 | 13 | async getAllCommentProperties() { 14 | const data = await this.cpRepo.find(); 15 | return data[0]; 16 | } 17 | 18 | async updateCommentProperties(id: number, name: string) { 19 | const exist = await this.cpRepo.findOne(id); 20 | if (!exist) { 21 | throw new RpcException({ code: 404, message: '该配置文件不存在!' }); 22 | } 23 | switch (name) { 24 | case 'enableComment': 25 | if (exist.enableComment === true) { 26 | await this.cpRepo.update(id, { enableComment: false }); 27 | } 28 | if (!exist.enableComment) { 29 | await this.cpRepo.update(id, { enableComment: true }); 30 | } 31 | break; 32 | case 'needAudit': 33 | if (exist.needAudit === true) { 34 | await this.cpRepo.update(id, { needAudit: false }); 35 | } 36 | if (!exist.needAudit) { 37 | await this.cpRepo.update(id, { needAudit: true }); 38 | }; 39 | break; 40 | case 'enableStar': 41 | if (exist.enableStar === true) { 42 | await this.cpRepo.update(id, { enableStar: false }); 43 | } 44 | if (!exist.enableStar) { 45 | await this.cpRepo.update(id, { enableStar: true }); 46 | } 47 | break; 48 | } 49 | 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/plugin/comment/services/discuss.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from "@nestjs/common"; 2 | import { InjectRepository } from "@nestjs/typeorm"; 3 | import { Discuss } from "../entities/discuss.entity"; 4 | import { Repository } from "typeorm"; 5 | import { nt_module_user } from "src/grpc/generated"; 6 | import { NotaddGrpcClientFactory } from "src/grpc.client-factory"; 7 | import * as moment from 'moment'; 8 | import { Article } from "src/articles/entities/article.entity"; 9 | import { RpcException } from "@nestjs/microservices"; 10 | import { UserInfoData } from "src/articles/interfaces/user.interface"; 11 | import { CreateDiscuss } from "../interfaces/discuss.interface"; 12 | 13 | @Injectable() 14 | export class DiscussService { 15 | 16 | onModuleInit() { 17 | this.userService = this.notaddGrpcClientFactory.userModuleClient.getService('UserService'); 18 | } 19 | 20 | constructor( 21 | @InjectRepository(Discuss) private readonly discussRepo: Repository, 22 | @InjectRepository(Article) private readonly artRepo: Repository
, 23 | @Inject(NotaddGrpcClientFactory) private readonly notaddGrpcClientFactory: NotaddGrpcClientFactory 24 | ) { } 25 | 26 | private userService: nt_module_user.UserService; 27 | 28 | /** 29 | * 发表评论 30 | * 31 | * @param comment 评论实体 32 | */ 33 | async createDiscuss(discuss: CreateDiscuss) { 34 | const time = moment().format('YYYY-MM-DD HH:mm:ss'); 35 | const art = await this.artRepo.findOne(discuss.artId); 36 | if (!art) { 37 | throw new RpcException({ code: 404, message: '该文章不存在!' }); 38 | } 39 | const user = (await this.userService.findUserInfoByIds({ userIds: [discuss.userId] }).toPromise()).data[0]; 40 | if(!user || user.recycle){ 41 | throw new RpcException({ code: 403, message: '该用户不存在!' }); 42 | } 43 | if (user.banned === true) { 44 | throw new RpcException({ code: 403, message: '您的账户已被封禁,请联系管理员!' }); 45 | } 46 | try { 47 | await this.discussRepo.save({ 48 | content: discuss.content, 49 | time, 50 | status: 0, 51 | userId: user.id, 52 | article: art 53 | }); 54 | } catch (error) { 55 | throw new RpcException({ code: 500, message: error }); 56 | } 57 | } 58 | 59 | /** 60 | * 删除评论 61 | * 62 | * @param id 评论id 63 | */ 64 | async deleteDiscuss(id: number) { 65 | const exist = await this.discussRepo.findOne(id); 66 | if (!exist) { 67 | throw new RpcException({ code: 404, message: '该评论不存在!' }); 68 | } 69 | await this.discussRepo.remove(exist); 70 | } 71 | 72 | /** 73 | * 审核评论,修改评论状态 74 | * 75 | * @param id 评论id 76 | * @param op 0:待审核,1:通过,2:拒绝 77 | */ 78 | async auditDiscuss(id: number, op: number) { 79 | const exist = await this.discussRepo.findOne(id); 80 | if (!exist) { 81 | throw new RpcException({ code: 404, message: '该评论不存在!' }); 82 | } 83 | await this.discussRepo.update(exist, { status: op }); 84 | } 85 | 86 | /** 87 | * 搜索评论 88 | * 89 | * @param pageNumber 当前页码 90 | * @param pageSize 每页显示数量 91 | * @param content 评论内容 92 | * @param artTitle 所属文章标题 93 | * @param artId 所属文章id 94 | * @param username 发布人用户名 95 | * @param startTime 起始时间 96 | * @param endTime 截止时间 97 | */ 98 | async getAllDiscusss(pageNumber: number, pageSize: number, content: string, artTitle: string, artId: number, username: string, startTime: string, endTime: string) { 99 | const sqb = this.discussRepo.createQueryBuilder('comment'); 100 | if (content) { 101 | sqb.andWhere('comment.content Like :content', { content: `%${content}%` }); 102 | } 103 | if (artTitle) { 104 | const arts = await this.artRepo.find({ where: { title: artTitle } }); 105 | if (!arts.length) { 106 | return { data: null, total: 0 }; 107 | } 108 | const ids = arts.map(item => item.id); 109 | sqb.andWhere('comment.article IN(:...ids)', ids); 110 | } 111 | if (artId) { 112 | sqb.andWhere('comment.article = :artId', { artId }); 113 | } 114 | if (username) { 115 | const user = await this.userService.findOneWithRolesAndPermissions({ username }).toPromise(); 116 | if (!user.data) { 117 | return { data: null, total: 0 }; 118 | } 119 | const id = user.data.id; 120 | sqb.andWhere('comment.userId = :id', { id }); 121 | } 122 | if (startTime) { 123 | sqb.andWhere('comment.time > :startTime', { startTime }); 124 | } 125 | if (endTime) { 126 | sqb.andWhere('comment.time < :endTime', { endTime }); 127 | } 128 | const result = await sqb.skip(pageSize * (pageNumber - 1)).take(pageSize).getManyAndCount(); 129 | return { data: result[0], total: result[1] }; 130 | } 131 | 132 | async updateDiscuss(comment: Discuss) { 133 | try { 134 | this.discussRepo.update(comment.id, this.discussRepo.create(comment)); 135 | } catch (error) { 136 | throw new RpcException(error); 137 | } 138 | } 139 | 140 | async getOneDiscuss(id:number){ 141 | return await this.discussRepo.findOne(id); 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /src/plugin/message-board/controllers/board-item.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { UpdateBoardItemInput } from "../interfaces/message-board.interface"; 4 | import { BoardItemService } from "../services/board-item.service"; 5 | 6 | @Controller() 7 | export class BoardItemController { 8 | constructor( 9 | @Inject(BoardItemService) private readonly biService: BoardItemService, 10 | ) { } 11 | 12 | @GrpcMethod('BoardItemService') 13 | async updateBoardItem(req, body: { updateBoardItem: UpdateBoardItemInput }) { 14 | await this.biService.updateBoardItem(body.updateBoardItem); 15 | return { code: 200, message: '修改留言板信息项成功!' }; 16 | } 17 | 18 | @GrpcMethod('BoardItemService') 19 | async deleteBoardItem(req, body: { id: number }) { 20 | await this.biService.deleteBoardItem(body.id); 21 | return { code: 200, message: '删除留言板信息项成功!' }; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/plugin/message-board/controllers/leaveword.conteroller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { CreateLeavewordInput } from "../interfaces/leaveword.interface"; 4 | import { LeavewordService } from "../services/leaveword.service"; 5 | 6 | @Controller() 7 | export class LeavewordController { 8 | constructor( 9 | @Inject(LeavewordService) private readonly leavewordService: LeavewordService, 10 | ) { } 11 | 12 | @GrpcMethod('LeavewordService') 13 | async createLeaveword(body: { createLeaveword: CreateLeavewordInput }) { 14 | await this.leavewordService.createLeaveword(body.createLeaveword); 15 | return { code: 200, message: '留言成功!' }; 16 | } 17 | 18 | @GrpcMethod('LeavewordService') 19 | async deleteLeaveword(req, body: { id: number }) { 20 | await this.leavewordService.deleteLeaveword(body.id); 21 | return { code: 200, message: '删除成功!' }; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/plugin/message-board/controllers/message-board.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { MessageBoardService } from "../services/message-board.service"; 4 | import { CreateBoardInput, UpdateBoardInput } from "../interfaces/message-board.interface"; 5 | 6 | @Controller() 7 | export class MessageBoardController { 8 | constructor( 9 | @Inject(MessageBoardService) private readonly mbService: MessageBoardService, 10 | ) { } 11 | 12 | @GrpcMethod('MessageBoardService') 13 | async createMessageBoard(body: { messageBoard: CreateBoardInput }) { 14 | await this.mbService.createMessageBoard(body.messageBoard); 15 | return { code: 200, message: '创建留言板成功!' }; 16 | } 17 | 18 | @GrpcMethod('MessageBoardService') 19 | async updateMessageBoard(body: { messageBoard: UpdateBoardInput }) { 20 | await this.mbService.updateMessageBoard(body.messageBoard); 21 | return { code: 200, message: '修改留言板成功!' }; 22 | } 23 | 24 | @GrpcMethod('MessageBoardService') 25 | async deleteMessageBoard(body: { id: number }) { 26 | await this.mbService.deleteMessageBoard(body.id); 27 | return { code: 200, message: '删除留言板成功!' }; 28 | } 29 | 30 | @GrpcMethod('MessageBoardService') 31 | async getAllMessageBoard(body: { pageNumber: number, pageSize: number }) { 32 | const result = await this.mbService.getAllMessageBoard(body.pageNumber, body.pageSize); 33 | return { code: 200, message: '查询成功!', total: result.total, data: result.data }; 34 | } 35 | 36 | @GrpcMethod('MessageBoardService') 37 | async getOneMessageBoard(body: { id: number }) { 38 | const data = await this.mbService.getOneMessageBoard(body.id); 39 | return { code: 200, message: '查询成功!', data }; 40 | } 41 | 42 | @GrpcMethod('MessageBoardService') 43 | async getMessageBoardContent(body: { id: number, pageNumber: number, pageSize: number }) { 44 | const data = await this.mbService.getMessageBoardContent(body.id, body.pageNumber, body.pageSize); 45 | return { code: 200, message: '查询成功!', data }; 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/plugin/message-board/entities/board-item.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; 2 | import { MessageBoard } from "./message-board.entity"; 3 | import { Item } from "src/articles/entities/item.entity"; 4 | 5 | @Entity('board_item') 6 | export class BoardItem { 7 | 8 | @PrimaryGeneratedColumn() 9 | id:number; 10 | 11 | @Column({ 12 | comment: '显示名称' 13 | }) 14 | name: string; 15 | 16 | @Column({ 17 | comment: '别名' 18 | }) 19 | alias: string; 20 | 21 | @Column({ 22 | comment: '是否必填', 23 | default: false 24 | }) 25 | required: boolean; 26 | 27 | @ManyToOne(type=>MessageBoard,messageBoard=>messageBoard.boardItems) 28 | messageBoard: MessageBoard; 29 | 30 | @ManyToOne(type=>Item,item=>item.boardItems) 31 | item: Item; 32 | 33 | } -------------------------------------------------------------------------------- /src/plugin/message-board/entities/leaveword-info.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; 2 | import { Leaveword } from "./leaveword.entity"; 3 | import { Item } from "src/articles/entities/item.entity"; 4 | 5 | @Entity('leaveword_info') 6 | export class LeavewordInfo { 7 | 8 | @PrimaryGeneratedColumn() 9 | id: number; 10 | 11 | @Column() 12 | value: string; 13 | 14 | @ManyToOne(type => Leaveword, leaveword => leaveword.leaveWordInfos, { 15 | onDelete: 'CASCADE' 16 | }) 17 | leaveword: Leaveword; 18 | 19 | @ManyToOne(type => Item, item => item.leavewordInfos, { 20 | onDelete: 'CASCADE' 21 | }) 22 | item: Item; 23 | 24 | } -------------------------------------------------------------------------------- /src/plugin/message-board/entities/leaveword.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm"; 2 | import { MessageBoard } from "./message-board.entity"; 3 | import { LeavewordInfo } from "./leaveword-info.entity"; 4 | 5 | @Entity('leaveword') 6 | export class Leaveword{ 7 | 8 | @PrimaryGeneratedColumn() 9 | id:number; 10 | 11 | @Column({ 12 | comment: '留言用户' 13 | }) 14 | userId: number; 15 | 16 | @ManyToOne(type=>MessageBoard,messageBoard=>messageBoard.leavewords) 17 | messageBoard: MessageBoard; 18 | 19 | @OneToMany(type=>LeavewordInfo,leavewordInfo=>leavewordInfo.leaveword) 20 | leaveWordInfos: LeavewordInfo[]; 21 | 22 | } -------------------------------------------------------------------------------- /src/plugin/message-board/entities/message-board.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; 2 | import { BoardItem } from "./board-item.entity"; 3 | import { Leaveword } from "./leaveword.entity"; 4 | import * as moment from 'moment'; 5 | 6 | @Entity('message-board') 7 | export class MessageBoard { 8 | 9 | @PrimaryGeneratedColumn() 10 | id: number; 11 | 12 | @Column({ 13 | comment: '留言板名称' 14 | }) 15 | name: string; 16 | 17 | @Column({ 18 | comment: '留言板别名', 19 | unique: true 20 | }) 21 | alias: string; 22 | 23 | @Column({ 24 | nullable: true, 25 | transformer: { 26 | from: (date) => { 27 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 28 | }, 29 | to: (date) => { 30 | date = date ? date : new Date(); 31 | return moment(date).format('YYYY-MM-DD HH:mm:ss'); 32 | } 33 | } 34 | }) 35 | time: string; 36 | 37 | @OneToMany(type => BoardItem, boardItem => boardItem.messageBoard) 38 | boardItems: []; 39 | 40 | @OneToMany(type => Leaveword, leaveword => leaveword.messageBoard) 41 | leavewords: Leaveword[]; 42 | 43 | } -------------------------------------------------------------------------------- /src/plugin/message-board/interfaces/leaveword.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreateLeavewordInput { 2 | userId: number; 3 | messageBoardId: number; 4 | infoKVs: { 5 | infoItemId: number; 6 | artInfoValue: string 7 | }[] 8 | } -------------------------------------------------------------------------------- /src/plugin/message-board/interfaces/message-board.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreateBoardInput { 2 | name: string; 3 | alias: string; 4 | boardItem: { 5 | name: string; 6 | alias: string; 7 | required: boolean; 8 | itemId: number; 9 | }[] 10 | } 11 | 12 | export class UpdateBoardInput { 13 | id: number; 14 | name: string; 15 | alias: string; 16 | } 17 | 18 | export class UpdateBoardItemInput { 19 | id: number; 20 | name: string; 21 | alias: string; 22 | item: number; 23 | required: boolean; 24 | } -------------------------------------------------------------------------------- /src/plugin/message-board/services/board-item.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { InjectRepository } from "@nestjs/typeorm"; 3 | import { BoardItem } from "../entities/board-item.entity"; 4 | import { Repository } from "typeorm"; 5 | import { Item } from "src/articles/entities/item.entity"; 6 | import { UpdateBoardItemInput } from "../interfaces/message-board.interface"; 7 | import { RpcException } from "@nestjs/microservices"; 8 | import { Leaveword } from "../entities/leaveword.entity"; 9 | 10 | @Injectable() 11 | export class BoardItemService { 12 | constructor( 13 | @InjectRepository(BoardItem) private readonly biRepo: Repository, 14 | @InjectRepository(Item) private readonly itemRepo: Repository, 15 | @InjectRepository(Leaveword) private readonly lwRepo: Repository, 16 | ) { } 17 | 18 | async updateBoardItem(updateBoardItem: UpdateBoardItemInput) { 19 | const exist = await this.biRepo.findOne(updateBoardItem.id); 20 | if (!exist) { 21 | throw new RpcException({ code: 404, message: '该留言板信息项不存在!' }); 22 | } 23 | if (updateBoardItem.alias && updateBoardItem.alias !== exist.alias) { 24 | if(await this.biRepo.findOne({where:{alias:updateBoardItem.alias}})){ 25 | throw new RpcException({code:406,message:'别名重复!'}); 26 | } 27 | } 28 | const item = await this.itemRepo.findOne(updateBoardItem.item); 29 | await this.biRepo.save(this.biRepo.create({ 30 | id:updateBoardItem.id, 31 | alias:updateBoardItem.alias, 32 | item, 33 | required: updateBoardItem.required 34 | })) 35 | } 36 | 37 | async deleteBoardItem(id:number){ 38 | const exist = await this.biRepo.findOne(id); 39 | if(!exist) { 40 | throw new RpcException({code:404,message:'该信息项不存在!'}); 41 | } 42 | await this.biRepo.remove(exist); 43 | const leavewords = await this.lwRepo.find({where:{messageBoard:exist.messageBoard}}); 44 | const ids = leavewords.map(item=>item.id); 45 | const infos = await this.biRepo.createQueryBuilder('boardItem') 46 | .leftJoinAndSelect('boardItem.leaveword','Leaveword') 47 | .where('Leaveword.id IN(:...ids)',{ids}) 48 | .getMany(); 49 | await this.biRepo.remove(infos); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/plugin/message-board/services/leaveword.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from "@nestjs/common"; 2 | import { InjectRepository } from "@nestjs/typeorm"; 3 | import { Leaveword } from "../entities/leaveword.entity"; 4 | import { Repository } from "typeorm"; 5 | import { LeavewordInfo } from "../entities/leaveword-info.entity"; 6 | import { CreateLeavewordInput } from "../interfaces/leaveword.interface"; 7 | import { MessageBoard } from "../entities/message-board.entity"; 8 | import { NotaddGrpcClientFactory } from "src/grpc.client-factory"; 9 | import { nt_module_user } from "src/grpc/generated"; 10 | import { RpcException } from "@nestjs/microservices"; 11 | 12 | @Injectable() 13 | export class LeavewordService { 14 | 15 | onModuleInit() { 16 | this.userService = this.notaddGrpcClientFactory.userModuleClient.getService('UserService'); 17 | } 18 | 19 | 20 | constructor( 21 | @InjectRepository(Leaveword) private readonly leavewordRepo: Repository, 22 | @InjectRepository(LeavewordInfo) private readonly lwInfoRepo: Repository, 23 | @InjectRepository(MessageBoard) private readonly mbRepo: Repository, 24 | @Inject(NotaddGrpcClientFactory) private readonly notaddGrpcClientFactory: NotaddGrpcClientFactory 25 | ) { } 26 | 27 | private userService: nt_module_user.UserService; 28 | 29 | async createLeaveword(createLeaveword: CreateLeavewordInput) { 30 | const user = (await this.userService.findUserInfoByIds({ userIds: [createLeaveword.userId] }).toPromise()).data[0]; 31 | if (!user) { 32 | throw new RpcException({ code: 404, message: '该用户不存在!' }); 33 | } 34 | if (user.banned === true) { 35 | throw new RpcException({ code: 403, message: '您的账户已被封禁,请联系管理员!' }); 36 | } 37 | const messageBoard = await this.mbRepo.findOne(createLeaveword.messageBoardId); 38 | if (!messageBoard) { 39 | throw new RpcException({ code: 404, message: '该留言板不存在!' }); 40 | } 41 | const leaveword = await this.leavewordRepo.save(this.leavewordRepo.create({ userId: createLeaveword.userId, messageBoard })); 42 | if (createLeaveword.infoKVs && createLeaveword.infoKVs.length) { 43 | for (let i = 0; i < createLeaveword.infoKVs.length; i++) { 44 | await this.lwInfoRepo.save(this.lwInfoRepo.create({ value: createLeaveword.infoKVs[i].artInfoValue, leaveword, item: { id: createLeaveword.infoKVs[i].infoItemId } })); 45 | } 46 | } 47 | } 48 | 49 | async deleteLeaveword(id: number) { 50 | const leaveword = await this.leavewordRepo.findOne(id, { relations: ['messageBoard'] }); 51 | if (!leaveword) { 52 | throw new RpcException({ code: 404, message: '该留言不存在!' }); 53 | } 54 | await this.leavewordRepo.createQueryBuilder('leaveword').relation(Leaveword, 'messageBoard').of(leaveword).remove(leaveword.messageBoard); 55 | await this.leavewordRepo.remove(leaveword); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/plugin/message-board/services/message-board.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from "@nestjs/common"; 2 | import { InjectRepository } from "@nestjs/typeorm"; 3 | import { MessageBoard } from "../entities/message-board.entity"; 4 | import { Repository } from "typeorm"; 5 | import { Leaveword } from "../entities/leaveword.entity"; 6 | import { Item } from "src/articles/entities/item.entity"; 7 | import { LeavewordInfo } from "../entities/leaveword-info.entity"; 8 | import { nt_module_user } from "src/grpc/generated"; 9 | import { NotaddGrpcClientFactory } from "src/grpc.client-factory"; 10 | import { CreateBoardInput, UpdateBoardInput } from "../interfaces/message-board.interface"; 11 | import { RpcException } from "@nestjs/microservices"; 12 | import { BoardItem } from "../entities/board-item.entity"; 13 | 14 | @Injectable() 15 | export class MessageBoardService { 16 | 17 | onModuleInit() { 18 | this.userService = this.notaddGrpcClientFactory.userModuleClient.getService('UserService'); 19 | } 20 | 21 | 22 | constructor( 23 | @InjectRepository(MessageBoard) private readonly mbRepo: Repository, 24 | @InjectRepository(Leaveword) private readonly lwRepo: Repository, 25 | @InjectRepository(Item) private readonly itemRepo: Repository, 26 | @InjectRepository(BoardItem) private readonly biRepo: Repository, 27 | @InjectRepository(LeavewordInfo) private readonly lwInfoRepo: Repository, 28 | @Inject(NotaddGrpcClientFactory) private readonly notaddGrpcClientFactory: NotaddGrpcClientFactory 29 | ) { } 30 | 31 | private userService: nt_module_user.UserService; 32 | 33 | async createMessageBoard(messageBoard: CreateBoardInput) { 34 | try { 35 | if (await this.mbRepo.findOne({ where: { alias: messageBoard.alias } })) { 36 | throw new RpcException({ code: 406, message: '留言板别名重复!' }); 37 | } 38 | const board = await this.mbRepo.save(this.mbRepo.create({ name: messageBoard.name, alias: messageBoard.alias })); 39 | if (messageBoard.boardItem && messageBoard.boardItem.length) { 40 | const array = messageBoard.boardItem.map(item => item.alias); 41 | for (let i = 0; i < array.length; i++) { 42 | const same = array.filter(item => item === array[i]); 43 | if (same.length > 1) { 44 | throw new RpcException({ code: 406, message: '信息项别名重复!' }); 45 | } 46 | } 47 | for (const i of messageBoard.boardItem) { 48 | const item = await this.itemRepo.findOne(i.itemId); 49 | await this.biRepo.save(this.biRepo.create({ 50 | name: i.name, 51 | alias: i.alias, 52 | required: i.required, 53 | item, 54 | messageBoard: board 55 | })) 56 | } 57 | } 58 | } catch (error) { 59 | throw new RpcException(error); 60 | } 61 | } 62 | 63 | async updateMessageBoard(messageBoard: UpdateBoardInput) { 64 | const board = await this.mbRepo.findOne(messageBoard.id); 65 | if (messageBoard.name && messageBoard.name !== board.name) { 66 | this.mbRepo.update(board.id, { name: messageBoard.name }); 67 | } 68 | if (messageBoard.alias && messageBoard.alias !== board.alias) { 69 | if (await this.mbRepo.findOne({ where: { alias: messageBoard.alias } })) { 70 | throw new RpcException({ code: 406, message: '留言板别名重复!' }); 71 | } 72 | this.mbRepo.update(board.id, { alias: messageBoard.alias }); 73 | } 74 | } 75 | 76 | async deleteMessageBoard(id: number) { 77 | const exist = await this.mbRepo.findOne(id); 78 | if (!exist) { 79 | throw new RpcException({ code: 404, message: '该留言板不存在!' }); 80 | } 81 | this.mbRepo.remove(exist); 82 | } 83 | 84 | async getAllMessageBoard(pageNumber: number, pageSize: number) { 85 | const data = await this.mbRepo.find({ 86 | skip: (pageNumber - 1) * pageSize, 87 | take: pageSize, 88 | relations: ['leavewords'] 89 | }); 90 | const result = []; 91 | for (const i of data) { 92 | const a = { 93 | name: i.name, 94 | alias: i.alias, 95 | time: i.time, 96 | amount: i.leavewords.length 97 | }; 98 | result.push(a); 99 | } 100 | const total = await this.mbRepo.count(); 101 | return { data: result, total }; 102 | } 103 | 104 | async getOneMessageBoard(id: number) { 105 | const data = await this.mbRepo.findOne(id, { 106 | relations: ['boardItems'] 107 | }); 108 | return data; 109 | } 110 | 111 | async getMessageBoardContent(id: number, pageNumber: number, pageSize: number) { 112 | const data = await this.lwRepo.findAndCount({ 113 | where: { messageBoard: id }, 114 | relations: ['leaveWordInfos'], 115 | skip: (pageNumber - 1) * pageSize, 116 | take: pageSize, 117 | }); 118 | return { total: data[1], data: data[0] }; 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /src/plugin/upyun/controllers/upyun.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from "@nestjs/common"; 2 | import { GrpcMethod } from "@nestjs/microservices"; 3 | import { BucketConfig } from "../interface/config/bucket.config"; 4 | import { UpYunService } from "../services/upyun.service"; 5 | import { UploadProcessBody } from "../interface/file/upload.process.body"; 6 | import { UploadProcessData } from "../interface/file/upload.process.data"; 7 | 8 | @Controller() 9 | export class UpYunController { 10 | constructor( 11 | @Inject(UpYunService) private readonly upyunService: UpYunService, 12 | ) { } 13 | 14 | @GrpcMethod('UpYunService') 15 | async bucket(body: { bucket: BucketConfig }) { 16 | return await this.upyunService.bucket(body.bucket); 17 | } 18 | 19 | @GrpcMethod('UpYunService') 20 | async uploadProcess(body: { upload: UploadProcessBody, protocol: string, host: string }) { 21 | return await this.upyunService.uploadProcess(body.upload, body.protocol, body.host); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/plugin/upyun/entities/abstract.file.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Column, CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm"; 3 | 4 | /* 文件抽象类,提取了五种文件类型公有属性 */ 5 | export class AbstractFile { 6 | 7 | @PrimaryGeneratedColumn() 8 | id: number; 9 | 10 | @Column({ length: 50 }) 11 | rawName: string; 12 | 13 | @Column({ length: 50, unique: true }) 14 | name: string; 15 | 16 | @Column({ nullable: true, type: "simple-array" }) 17 | tags: Array; 18 | 19 | @Column({ length: 50 }) 20 | md5: string; 21 | 22 | @Column({ length: 20, nullable: true }) 23 | type: string; 24 | 25 | @Column({ nullable: true }) 26 | size: number; 27 | 28 | // 访问密钥 29 | @Column({ length: "50", nullable: true }) 30 | contentSecret: string; 31 | 32 | @Column({ nullable: false }) 33 | status: string; 34 | 35 | @CreateDateColumn() 36 | createDate: Date; 37 | 38 | @UpdateDateColumn() 39 | updateDate: Date; 40 | } 41 | -------------------------------------------------------------------------------- /src/plugin/upyun/entities/audio.config.entity.ts: -------------------------------------------------------------------------------- 1 | import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from "typeorm"; 2 | import { Bucket } from "./bucket.entity"; 3 | 4 | /* 音频配置实体类 */ 5 | @Entity("audioConfig") 6 | export class AudioConfig { 7 | 8 | // 主键,需要设置插入,1默认为公有空间配置,2默认为私有空间配置 9 | @PrimaryColumn() 10 | id: number; 11 | 12 | // 保存格式,raw、mp3、aac 13 | @Column({ nullable: true }) 14 | format: string; 15 | 16 | @Column({ nullable: true, unique: true }) 17 | bucketId: number; 18 | 19 | @OneToOne(type => Bucket, bucket => bucket.audioConfig) 20 | @JoinColumn() 21 | bucket: Bucket; 22 | } 23 | -------------------------------------------------------------------------------- /src/plugin/upyun/entities/audio.entity.ts: -------------------------------------------------------------------------------- 1 | import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; 2 | import { AbstractFile } from "./abstract.file"; 3 | import { Bucket } from "./bucket.entity"; 4 | 5 | @Entity("audio") 6 | export class Audio extends AbstractFile { 7 | 8 | @Column({ nullable: true }) 9 | bucketId: number; 10 | 11 | @ManyToOne(type => Bucket, bucket => bucket.audios, { 12 | cascade: false, 13 | nullable: false, 14 | lazy: false, 15 | }) 16 | @JoinColumn() 17 | bucket: Bucket; 18 | } 19 | -------------------------------------------------------------------------------- /src/plugin/upyun/entities/bucket.entity.ts: -------------------------------------------------------------------------------- 1 | import { Column, Entity, OneToMany, OneToOne, PrimaryColumn } from "typeorm"; 2 | import { AudioConfig } from "./audio.config.entity"; 3 | import { Audio } from "./audio.entity"; 4 | import { Document } from "./document.entity"; 5 | import { File } from "./file.entity"; 6 | import { ImageConfig } from "./image.config.entity"; 7 | import { Image } from "./image.entity"; 8 | import { VideoConfig } from "./video.config.entity"; 9 | import { Video } from "./video.entity"; 10 | 11 | /* 后台配置实体类 */ 12 | @Entity("bucket") 13 | export class Bucket { 14 | 15 | // 主键,需要设置插入,1默认为公有空间配置,2默认为私有空间配置 16 | @PrimaryColumn() 17 | id: number; 18 | 19 | // 公有还是私有空间,值为public、private 20 | @Column({ length: 20, unique: true }) 21 | publicOrPrivate: string; 22 | 23 | // 空间名 24 | @Column({ length: 50, unique: true }) 25 | name: string; 26 | 27 | // 操作员名 28 | @Column({ length: 50 }) 29 | operator: string; 30 | 31 | // 操作员密码的md5 32 | @Column({ length: 50 }) 33 | password: string; 34 | 35 | // 此空间下所有文件都存储于这个目录里,与虚拟目录无关 36 | @Column({ length: 20 }) 37 | directory: string; 38 | 39 | // 请求过期时间,单位秒 40 | @Column() 41 | requestExpire: number; 42 | 43 | // 基本url 44 | @Column({ length: 50, unique: true }) 45 | baseUrl: string; 46 | 47 | // token密钥 48 | @Column({ length: 250, nullable: true }) 49 | tokenSecretKey: string; 50 | 51 | // token过期时间,单位秒 52 | @Column({ nullable: true }) 53 | tokenExpire: number; 54 | 55 | /* 56 | 这里lazy:false的意思不是每个Bucket查询出来的时候就会包含imageConfig 57 | 它的意思只是在于获取的属性是否是Promise,而要查询出来的Bucket包含imageConfig,必须使用find({relation:xxxx}) 58 | */ 59 | @OneToOne(type => ImageConfig, imageConfig => imageConfig.bucket, { 60 | cascade: ["insert"], 61 | lazy: false, 62 | eager: false 63 | }) 64 | imageConfig: ImageConfig; 65 | 66 | @OneToOne(type => AudioConfig, audioConfig => audioConfig.bucket, { 67 | cascade: ["insert"], 68 | lazy: false, 69 | eager: false 70 | }) 71 | audioConfig: AudioConfig; 72 | 73 | @OneToOne(type => VideoConfig, videoConfig => videoConfig.bucket, { 74 | cascade: ["insert"], 75 | lazy: false, 76 | eager: false 77 | }) 78 | videoConfig: VideoConfig; 79 | 80 | @OneToMany(type => File, file => file.bucket, { 81 | cascade: ["insert"], 82 | lazy: false, 83 | eager: false 84 | }) 85 | files: Array; 86 | 87 | @OneToMany(type => Image, image => image.bucket, { 88 | cascade: ["insert"], 89 | lazy: false, 90 | eager: false 91 | }) 92 | images: Array; 93 | 94 | @OneToMany(type => Audio, audio => audio.bucket, { 95 | cascade: ["insert"], 96 | lazy: false, 97 | eager: false 98 | }) 99 | audios: Array