From a971ca63b482fea8307e122b40b7ee03da51e7a7 Mon Sep 17 00:00:00 2001 From: dj <2162570766@qq.com> Date: Tue, 17 Dec 2024 01:02:23 +0800 Subject: [PATCH] dj --- .eslintrc.js | 28 +- README.md | 2 +- babel.config.js | 25 +- package.json | 41 +- routes/api/private/v1/categories.js | 455 +++++++------ routes/api/private/v1/goods.js | 406 +++++++----- routes/api/private/v1/menus.js | 45 +- routes/api/private/v1/orders.js | 241 ++++--- routes/api/private/v1/reports.js | 77 ++- routes/api/private/v1/rights.js | 83 ++- routes/api/private/v1/roles.js | 347 ++++++---- routes/api/private/v1/upload.js | 66 +- routes/api/private/v1/users.js | 411 ++++++++---- services/GoodService.js | 972 ++++++++++++++++++---------- services/ManagerService.js | 538 +++++++++------ services/MenuService.js | 194 +++--- services/OrderService.js | 712 ++++++++++++-------- services/ReportsService.js | 263 +++++--- services/RightService.js | 190 +++--- services/RoleService.js | 505 +++++++++------ services/UserService.js | 15 +- vue.config.js | 52 +- 22 files changed, 3586 insertions(+), 2082 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 98d0431..1c8ba40 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,17 +1,39 @@ +//这个配置文件主要做了以下几件事: + +//设置了ESLint的根配置,确保没有其他配置文件影响当前配置。 +//指定了脚本的运行环境为Node.js,这对于理解全局变量很重要。 +//继承了Vue.js官方推荐的eslint-plugin-vue的基础配置和Vue.js的标准配置。 +//自定义了两条规则,根据环境变量NODE_ENV的值(生产环境或开发环境)启用或禁用console和debugger语句。 +//指定了使用babel-eslint作为解析器,以支持ES6+语法和Babel特性。 +// 导出配置对象,使其可以被其他文件引入和使用 module.exports = { + // 设置ESLint的根配置,防止父级目录中的配置文件影响当前配置 root: true, + + // 指定脚本的运行环境,这里指定为Node.js环境 + // 这有助于ESLint理解全局变量,比如`module`, `require`等 env: { node: true }, + + // 继承(扩展)其他ESLint配置或插件配置 + // 这里继承了Vue.js官方推荐的eslint-plugin-vue的基础配置和Vue.js的标准配置 'extends': [ - 'plugin:vue/essential', - '@vue/standard' + 'plugin:vue/essential', // Vue.js官方推荐的eslint-plugin-vue的基础配置 + '@vue/standard' // Vue.js的标准配置,基于ESLint的标准规则集 ], + + // 自定义规则,覆盖继承的配置中的规则 rules: { + // 在生产环境中禁用console语句,在开发环境中允许使用 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', + // 在生产环境中禁用debugger语句,在开发环境中允许使用 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' }, + + // 解析器选项,用于指定ESLint使用的解析器 + // 这里指定使用babel-eslint解析器,它支持ES6+语法和Babel特性 parserOptions: { parser: 'babel-eslint' } -} +} \ No newline at end of file diff --git a/README.md b/README.md index 6755adc..07a07c5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - +注意:routes/services/test/config/dao/db/public2均为后端文件夹,仅是为了多注释放入的 ### 大家有问题尽量在这里提: https://gitee.com/wBekvam/vue-shop-admin/issues diff --git a/babel.config.js b/babel.config.js index b1e67b1..5756f22 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,23 +1,38 @@ -// 项目开发阶段用到的babel插件 +//这段代码展示了如何根据不同的环境(开发环境或生产环境)动态地配置Babel插件。在生产环境下,它 +//会移除所有的console语句以减少代码体积,并且使用了一些插件来优化Vue.js项目的打包结果和运行时性能。 +// 定义一个空数组,用于存放生产环境下需要用到的Babel插件 const prodPlugins = [] + +// 检查当前环境变量是否为生产环境(NODE_ENV === 'production') if (process.env.NODE_ENV === 'production') { + // 如果是生产环境,则向prodPlugins数组中添加'transform-remove-console'插件 + // 这个插件的作用是在构建生产代码时移除所有的console.*调用,以减少打包后的文件大小和提高运行效率 prodPlugins.push('transform-remove-console') } +// 导出Babel配置对象 module.exports = { + // 预设(presets)数组,定义了Babel转译时使用的预设集 'presets': [ + // 使用Vue CLI提供的Babel预设集,适用于Vue.js项目 '@vue/cli-plugin-babel/preset' ], + // 插件(plugins)数组,定义了Babel转译时使用的插件 'plugins': [ + // 使用'component'插件,配合element-ui库使用 + // 该插件可以按需加载element-ui的组件和样式,减少打包后的文件大小 [ 'component', { - 'libraryName': 'element-ui', - 'styleLibraryName': 'theme-chalk' + 'libraryName': 'element-ui', // 指定按需加载的库名 + 'styleLibraryName': 'theme-chalk' // 指定样式库的名称 } ], - // 发布产品时候的插件数组 + // 使用展开运算符(...)将prodPlugins数组中的插件添加到这里 + // 这样,只有在生产环境下,'transform-remove-console'插件才会被包含在最终的插件列表中 ...prodPlugins, + // 添加'@babel/plugin-syntax-dynamic-import'插件 + // 该插件允许Babel解析动态import()语法,这对于代码分割和懒加载非常有用 '@babel/plugin-syntax-dynamic-import' ] -} +} \ No newline at end of file diff --git a/package.json b/package.json index 41138fd..85b7051 100644 --- a/package.json +++ b/package.json @@ -1,38 +1,77 @@ { +//请注意,vue-cli-plugin-element可能已经不是必需的,因为自从Vue CLI 3和Element UI 2.x版本以来, +//通常推荐使用babel-plugin-component来按需加载Element UI组件。此外,babel-eslint可能也需要更 +//新,因为ESLint现在原生支持Babel解析器(通过@babel/eslint-parser),但这取决于您的具体配置和需求。 +//另外,dependencies和devDependencies中的版本号前的^符号表示在安装依赖时,npm会安装与指定 +//版本兼容的最新版本(遵循语义化版本号)。这有助于确保您始终获得最新功能和安全修复,但也可能引 +//入不期望的更改。如果需要更严格的版本控制,可以考虑使用~(兼容补丁版本更新)或直接指定版本号(不自动更新)。 + // 项目名称 "name": "vue_shop_admin", + // 项目版本 "version": "0.1.0", + // 设置为私有项目,防止意外发布到npm "private": true, + // 定义npm脚本命令 "scripts": { + // 启动开发服务器,通常用于本地开发 "serve": "vue-cli-service serve", + // 构建生产环境的项目,生成dist目录 "build": "vue-cli-service build", + // 运行eslint检查代码质量 "lint": "vue-cli-service lint" }, + // 项目的依赖包,这些包会在项目安装时被安装到node_modules目录 "dependencies": { + // 用于发送HTTP请求的库 "axios": "^0.19.1", + // Babel插件,用于在生产环境中移除console语句 "babel-plugin-transform-remove-console": "^6.9.4", + // 包含新的JavaScript标准提案的polyfill,用于确保旧浏览器兼容 "core-js": "^3.4.4", + // 基于JavaScript的开源可视化图表库 "echarts": "^4.6.0", + // 基于Vue 2.0的桌面端组件库 "element-ui": "^2.4.5", + // JavaScript实用工具库 "lodash": "^4.17.15", + // 进度条组件库,用于显示页面加载进度 "nprogress": "^0.2.0", + // Vue.js框架 "vue": "^2.6.10", + // 富文本编辑器,基于Quill和Vue "vue-quill-editor": "^3.0.6", + // Vue.js的官方路由管理器 "vue-router": "^3.1.3", + // Vue表格组件,支持树形网格 "vue-table-with-tree-grid": "^0.2.4" }, + // 开发依赖包,仅在开发时使用,不会打包到最终项目中 "devDependencies": { + // Babel插件,支持动态import()语法 "@babel/plugin-syntax-dynamic-import": "^7.8.3", + // Vue CLI的Babel插件 "@vue/cli-plugin-babel": "^4.1.0", + // Vue CLI的ESLint插件 "@vue/cli-plugin-eslint": "^4.1.0", + // Vue CLI的服务工具,用于启动开发服务器和构建项目 "@vue/cli-service": "^4.1.0", + // Vue官方的ESLint配置 "@vue/eslint-config-standard": "^4.0.0", + // Babel和ESLint之间的桥梁,用于解析Babel代码 "babel-eslint": "^10.0.3", + // Babel插件,用于按需加载组件库(如element-ui) "babel-plugin-component": "^1.1.1", + // JavaScript的静态代码分析工具 "eslint": "^5.16.0", + // ESLint的Vue.js插件 "eslint-plugin-vue": "^5.0.0", + // CSS预处理器,LESS "less": "^3.10.3", + // webpack的loader,用于编译LESS文件 "less-loader": "^5.0.0", + // Vue CLI的element-ui插件(可能已经过时,因为可以直接通过babel-plugin-component配置) "vue-cli-plugin-element": "^1.0.1", + // Vue.js的模板编译器,用于将Vue模板预编译为渲染函数 "vue-template-compiler": "^2.6.10" } -} +} \ No newline at end of file diff --git a/routes/api/private/v1/categories.js b/routes/api/private/v1/categories.js index a23b4d2..a0a46cd 100644 --- a/routes/api/private/v1/categories.js +++ b/routes/api/private/v1/categories.js @@ -1,262 +1,261 @@ +//这段代码整体是在处理删除分类参数相关的逻辑,先是对传入的参数(分类 ID 和参数 ID)进行严格的合 +//法性验证,通过后再调用相应服务方法执行删除操作,并根据操作结果向客户端返回合适的响应信息,最 +//后将路由器对象导出供外部使用。 +// 引入Express框架,这是一个流行的Node.js web应用框架,用于创建服务器、处理HTTP请求以及定义路由等功能 var express = require('express'); +// 创建一个Express的路由器对象,通过这个对象可以方便地定义一组相关的路由规则,用于处理不同路径和请求方法的逻辑 var router = express.Router(); +// 引入Node.js的path模块,该模块提供了一系列用于处理文件路径的实用方法,比如拼接、解析路径等操作 var path = require("path"); -// 获取验证模块 +// 获取验证模块,通过process.cwd()获取当前工作目录,然后与"/modules/authorization"路径进行拼接, +// 最终引入对应的模块。这个模块大概率用于进行权限验证相关操作,或者提供获取其他服务的入口等功能 var authorization = require(path.join(process.cwd(),"/modules/authorization")); -// 通过验证模块获取分类管理 +// 通过验证模块获取分类管理相关服务,"CategoryService"是在authorization模块内部定义好的一个服务标识, +// 借助这个标识可以调用一系列与分类管理相关的功能方法,例如获取分类列表、添加分类、删除分类等具体操作 var catServ = authorization.getService("CategoryService"); -// 通过验证模块获取分类属性 +// 通过验证模块获取分类属性相关服务,同理,"AttributeService"也是在authorization模块里定义的一个服务标识, +// 专门用于处理和分类属性相关的各类操作,比如获取属性、创建属性、更新属性以及删除属性等功能 var attrServ = authorization.getService("AttributeService"); // 获取分类列表 + +// 引入Express框架的路由器对象(假设前面已经正确引入了Express) +// 这里重新声明了一个名为router的变量,覆盖了之前定义的router,实际使用中可能需要注意避免这种重复定义导致的混淆, +// 不过从代码逻辑看可能是想重新明确这个路由器对象用于后续路由配置,建议可以使用不同的变量名更好区分 +const router = require('express').Router(); + +// 处理获取分类列表的GET请求,路径为根路径 "/",意味着当客户端向服务器发送GET请求到根路径时,会进入这个路由的处理逻辑 router.get("/", - function(req,res,next){ - // 参数验证 - // if(!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null,400,"pagenum 参数错误"); - // if(!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null,400,"pagesize 参数错误"); - next(); - }, - function(req,res,next){ - var conditions = null; - if(req.query.pagenum && req.query.pagesize) { - conditions = { - "pagenum" : req.query.pagenum, - "pagesize" : req.query.pagesize - }; - } - - catServ.getAllCategories(req.query.type,conditions,function(err,result){ - if(err) return res.sendResult(null,400,"获取分类列表失败"); - res.sendResult(result,200,"获取成功"); - })(req,res,next); -}); + // 第一个中间件函数,用于参数验证,在Express框架中,中间件函数是一种可以对请求进行预处理的机制,比如检查请求参数是否合法等 + function (req, res, next) { + // 验证pagenum参数是否存在且大于0,如果不符合要求则返回错误响应,状态码400表示请求参数错误。 + // 此处代码被注释掉了,若取消注释则会进行该参数验证逻辑,即检查请求中是否传递了合法的pagenum参数, + // 如果该参数不存在或者小于等于0,就会返回包含相应错误信息的响应给客户端 + // if (!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null, 400, "pagenum 参数错误"); + // 验证pagesize参数是否存在且大于0,如果不符合要求则返回错误响应, + // 此处代码被注释掉了,若取消注释则会进行该参数验证逻辑,也就是检查请求中是否传递了合法的pagesize参数, + // 若该参数不存在或者小于等于0,同样会返回包含对应错误信息的响应给客户端 + // if (!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null, 400, "pagesize 参数错误"); + // 如果参数验证通过,调用next()将控制权传递给下一个中间件或路由处理函数,这是Express中间件机制中用于流程控制的关键操作, + // 保证请求能按照顺序依次经过各个中间件进行相应处理 + next(); + }, + // 第二个中间件函数,用于获取分类列表的业务逻辑处理,在前面参数验证中间件通过后,会执行这个中间件里的具体业务逻辑,即获取分类列表的操作 + function (req, res, next) { + var conditions = null; + // 如果pagenum和pagesize参数都存在,则构建包含这两个参数的查询条件对象,用于后续向获取分类列表的服务方法传递相应的限制条件, + // 例如可以根据这两个参数来控制每页显示的分类数量以及获取第几页的分类数据等情况 + if (req.query.pagenum && req.query.pagesize) { + conditions = { + "pagenum": req.query.pagenum, + "pagesize": req.query.pagesize + }; + } -// 创建分类 + // 调用catServ服务(假设是自定义的分类相关服务模块)的getAllCategories方法,传入分类类型和查询条件等参数, + // 尝试从数据库或者其他数据存储介质中获取符合条件的分类列表信息,这是一个异步操作,通过回调函数来处理操作完成后的结果情况 + catServ.getAllCategories(req.query.type, conditions, function (err, result) { + if (err) return res.sendResult(null, 400, "获取分类列表失败"); + res.sendResult(result, 200, "获取成功"); + })(req, res, next); + }); + +// 处理创建分类的POST请求,路径为 "/",表示当客户端向服务器的根路径发起POST请求时,会进入此路由对应的处理逻辑, +// 通常这种请求用于向服务器提交创建新分类的数据信息 router.post("/", - // 参数验证 - function(req,res,next) { - if(!req.body.cat_name) { - return res.sendResult(null,400,"必须提供分类名称"); - } - next(); - }, - // 业务逻辑 - function(req,res,next) { - catServ.addCategory({ - "cat_pid":req.body.cat_pid, - "cat_name":req.body.cat_name, - "cat_level":req.body.cat_level - },function(err,result) { - if(err) return res.sendResult(null,400,err); - res.sendResult(result,201,"创建成功"); - })(req,res,next); - } + // 参数验证中间件,检查请求体中是否包含cat_name字段,如果没有则返回错误响应,因为分类名称在创建分类操作中一般是必不可少的信息, + // 所以要确保请求中包含这个字段才能进行后续的创建分类逻辑 + function (req, res, next) { + if (!req.body.cat_name) { + return res.sendResult(null, 400, "必须提供分类名称"); + } + // 参数验证通过,将控制权传递给下一个中间件,继续后续创建分类的业务逻辑处理,遵循Express中间件按顺序执行的机制 + next(); + }, + // 业务逻辑中间件,用于创建分类的具体操作,在前面参数验证中间件通过后,会执行这个中间件里的实际创建分类的业务逻辑 + function (req, res, next) { + // 调用catServ服务的addCategory方法,传入包含分类相关信息(父分类ID、分类名称、分类级别等)的对象, + // 通过这些信息向系统中添加新的分类,将客户端提交过来的分类相关数据传递给服务层进行相应的处理,这也是一个异步操作, + // 通过回调函数来处理操作完成后的结果情况,比如创建成功返回创建后的分类信息,失败则返回相应的错误信息 + catServ.addCategory({ + "cat_pid": req.body.cat_pid, + "cat_name": req.body.cat_name, + "cat_level": req.body.cat_level + }, function (err, result) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(result, 201, "创建成功"); + })(req, res, next); + } ); +// 处理根据分类ID获取分类详情的GET请求,路径中包含分类ID参数,如 "/:id",这里的":id"是路由参数的占位符, +// 在实际接收到请求时,会被替换为具体的分类ID值,然后根据这个ID去获取对应的分类详情信息 router.get("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - next(); - }, - // 正常业务逻辑 - function(req,res,next) { - catServ.getCategoryById(req.params.id,function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取成功"); - })(req,res,next); - } + // 参数验证中间件,检查分类ID参数是否存在以及是否为数字类型,保证获取详情的分类ID是合法有效的, + // 因为如果分类ID不存在或者不是数字类型,后续根据这个ID去查询数据库等操作可能会出现错误情况 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "分类ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "分类ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件,以便继续执行获取分类详情的业务逻辑,按照Express中间件的执行顺序推进处理流程 + next(); + }, + // 正常业务逻辑中间件,用于根据分类ID获取分类详情的操作,在前面参数验证中间件通过后,会执行这个中间件里的具体获取分类详情的业务逻辑 + function (req, res, next) { + // 调用catServ服务的getCategoryById方法,传入分类ID参数,通过该服务方法从数据存储(比如数据库)中获取对应分类的详细信息, + // 这同样是一个异步操作,依赖回调函数来处理获取详情后的结果情况,若成功获取到详情信息则返回给客户端,失败则返回相应的错误提示 + catServ.getCategoryById(req.params.id, function (err, result) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(result, 200, "获取成功"); + })(req, res, next); + } ); - - - -// 删除分类 +// 处理删除分类的DELETE请求,路径中包含分类ID参数,如 "/:id",意味着当客户端向服务器发起DELETE请求并且携带分类ID时, +// 会进入此路由对应的处理逻辑,用于执行删除指定分类的操作 router.delete("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - catServ.deleteCategory(req.params.id,function(msg) { - res.sendResult(null,200,msg); - })(req,res,next); - } + // 参数验证中间件,检查分类ID参数是否存在以及是否为数字类型,确保要删除的分类ID是合法有效的, + // 避免因传入非法的分类ID导致删除操作出现意外情况,比如误删其他数据或者导致数据库报错等问题 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "分类ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "分类ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件,进而执行删除分类的实际业务逻辑,遵循Express中间件按顺序执行的机制 + next(); + }, + // 业务逻辑中间件,用于执行删除分类的操作,在前面参数验证中间件通过后,会执行这个中间件里的实际删除分类的业务逻辑 + function (req, res, next) { + // 调用catServ服务的deleteCategory方法,传入分类ID参数,通过该服务方法从系统中删除指定的分类, + // 这是一个异步操作,通过回调函数来处理删除操作完成后的情况,若成功删除分类则返回相应的提示信息,失败则返回对应的错误信息 + catServ.deleteCategory(req.params.id, function (msg) { + res.sendResult(null, 200, msg); + })(req, res, next); + } ); -// 更新分类 +// 处理更新分类的PUT请求,路径中包含分类ID参数,如 "/:id",表示当客户端向服务器发送PUT请求并携带分类ID时, +// 会进入此路由对应的处理逻辑,用于执行更新指定分类信息的操作 router.put("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - if(!req.body.cat_name || req.body.cat_name == "") return res.sendResult(null,400,"分类名称不能为空"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - catServ.updateCategory(req.params.id,req.body.cat_name,function(err,result) { - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"更新成功"); - })(req,res,next); - } + // 参数验证中间件,检查分类ID参数是否存在、是否为数字类型,以及分类名称是否为空,确保更新分类时有合法的ID以及必要的分类名称信息, + // 因为分类名称通常是更新分类时需要修改的重要内容之一,所以要保证其存在且不为空 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "分类ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "分类ID必须是数字"); + if (!req.body.cat_name || req.body.cat_name == "") return res.sendResult(null, 400, "分类名称不能为空"); + // 参数验证通过,将控制权交给下一个中间件,以便继续执行更新分类的业务逻辑操作,按照Express中间件的执行顺序推进处理流程 + next(); + }, + // 业务逻辑中间件,用于执行更新分类的操作,在前面参数验证中间件通过后,会执行这个中间件里的实际更新分类信息的业务逻辑 + function (req, res, next) { + // 调用catServ服务的updateCategory方法,传入分类ID和新的分类名称等参数,通过该服务方法对指定分类的信息进行更新, + // 这是一个异步操作,通过回调函数来处理更新操作完成后的结果情况,若成功更新分类信息则返回更新后的分类内容,失败则返回相应的错误提示 + catServ.updateCategory(req.params.id, req.body.cat_name, function (err, result) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(result, 200, "更新成功"); + })(req, res, next); + } ); -// 通过参数方式查询静态参数还是动态参数 +// 处理通过分类ID获取分类参数(attributes)的GET请求,路径为 "/:id/attributes",用于获取指定分类下的相关属性信息, +// 根据分类ID去查找对应的属性数据 router.get("/:id/attributes", - // 验证参数 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - if(!req.query.sel || (req.query.sel != "only" && req.query.sel != "many")) { - return res.sendResult(null,400,"属性类型必须设置"); - } - next(); - }, - // 业务逻辑 - function(req,res,next) { - // attrServ - attrServ.getAttributes(req.params.id,req.query.sel,function(err,attributes){ - if(err) return res.sendResult(null,400,err); - res.sendResult(attributes,200,"获取成功"); - })(req,res,next); - } + // 参数验证中间件,检查分类ID参数是否存在、是否为数字类型,以及属性类型(sel)是否设置正确,确保获取属性时的所有参数都是合法有效的, + // 只有这样才能准确地从数据存储中获取到期望的属性数据,避免因参数问题导致查询出错 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "分类ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "分类ID必须是数字"); + if (!req.query.sel || (req.query.sel!= "only" && req.query.sel!= "many")) { + return res.sendResult(null, 400, "属性类型必须设置"); + } + // 参数验证通过,将控制权交给下一个中间件,继续执行获取分类参数的业务逻辑,遵循Express中间件按顺序执行的机制 + next(); + }, + // 业务逻辑中间件,用于获取分类参数的操作,在前面参数验证中间件通过后,会执行这个中间件里的具体获取分类参数的业务逻辑 + function (req, res, next) { + // 调用attrServ服务(假设是自定义的属性相关服务模块)的getAttributes方法,传入分类ID和属性类型等参数, + // 通过该服务方法从数据存储中获取对应分类的属性信息,这是一个异步操作,通过回调函数来处理获取属性后的结果情况, + // 若成功获取到属性列表则返回给客户端,失败则返回相应的错误提示 + attrServ.getAttributes(req.params.id, req.query.sel, function (err, attributes) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(attributes, 200, "获取成功"); + })(req, res, next); + } ); -// 获取参数详情 +// 处理根据分类ID和参数ID获取参数详情的GET请求,路径为 "/:id/attributes/:attrId",这里的":attrId"是参数ID的路由参数占位符, +// 在实际接收到请求时,会被替换为具体的参数ID值,然后根据分类ID和参数ID去获取对应的参数详情信息 router.get("/:id/attributes/:attrId", - // 验证参数 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - if(!req.params.attrId) { - return res.sendResult(null,400,"参数ID不能为空"); - } - if(isNaN(parseInt(req.params.attrId))) return res.sendResult(null,400,"参数ID必须是数字"); - next(); - }, - function(req,res,next) { - attrServ.attributeById(req.params.attrId,function(err,attr){ - if(err) return res.sendResult(null,400,err); - res.sendResult(attr,200,"获取成功"); - })(req,res,next); - } + // 参数验证中间件,检查分类ID和参数ID是否存在以及是否为数字类型,保证获取参数详情时的所有参数都是合法有效的, + // 这样才能准确从数据存储中查询到对应的参数详细内容,防止因参数不合法导致查询出错 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "分类ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "分类ID必须是数字"); + if (!req.params.attrId) { + return res.sendResult(null, 400, "参数ID不能为空"); + } + if (isNaN(parseInt(req.params.attrId))) return res.sendResult(null, 400, "参数ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件,以便继续执行获取参数详情的业务逻辑,按照Express中间件的执行顺序推进处理流程 + next(); + }, + function (req, res, next) { + // 调用attrServ服务的attributeById方法,传入参数ID参数,通过该服务方法从数据存储中获取对应参数的详细信息, + // 这是一个异步操作,通过回调函数来处理获取详情后的结果情况,若成功获取到参数详情则返回给客户端,失败则返回相应的错误提示 + attrServ.attributeById(req.params.attrId, function (err, attr) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(attr, 200, "获取成功"); + })(req, res, next); + } ); -// 创建参数 +// 处理创建分类参数的POST请求,路径为 "/:id/attributes",用于向指定分类下创建新的参数信息,根据分类ID来添加相应的参数数据 router.post("/:id/attributes", - // 验证参数 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - - if(!req.body.attr_name) return res.sendResult(null,400,"参数名称不能为空"); - - if(!req.body.attr_sel || (req.body.attr_sel != "only" && req.body.attr_sel != "many")) { - return res.sendResult(null,400,"参数 attr_sel 类型必须为 only 或 many"); - } - /* - if(!req.body.attr_write || (req.body.attr_write != "manual" && req.body.attr_write != "list")) { - return res.sendResult(null,400,"参数的 attr_write 必须为 manual 或 list"); - }*/ - next(); - }, - // 业务逻辑 - function(req,res,next) { - attrServ.createAttribute( - { - "attr_name" : req.body.attr_name, - "cat_id" : req.params.id, - "attr_sel" : req.body.attr_sel, - "attr_write" : req.body.attr_sel == "many" ? "list" : "manual",//req.body.attr_write, - "attr_vals" : req.body.attr_vals ? req.body.attr_vals : "" - }, - function(err,attr) { - if(err) return res.sendResult(null,400,err); - res.sendResult(attr,201,"创建成功"); - })(req,res,next); - } -); - - -// 更新参数 -router.put("/:id/attributes/:attrId", - // 验证参数 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - if(!req.params.attrId) { - return res.sendResult(null,400,"参数ID不能为空"); - } - if(isNaN(parseInt(req.params.attrId))) return res.sendResult(null,400,"参数ID必须是数字"); - if(!req.body.attr_sel || (req.body.attr_sel != "only" && req.body.attr_sel != "many")) { - return res.sendResult(null,400,"参数 attr_sel 类型必须为 only 或 many"); - } - - if(!req.body.attr_name || req.body.attr_name == "") return res.sendResult(null,400,"参数名称不能为空"); - - next(); - }, - // 业务逻辑 - function(req,res,next) { - attrServ.updateAttribute( - req.params.attrId, - { - "attr_name" : req.body.attr_name, - "cat_id" : req.params.id, - "attr_sel" : req.body.attr_sel, - "attr_write" : req.body.attr_sel == "many" ? "list" : "manual",//req.body.attr_write, - "attr_vals" : req.body.attr_vals ? req.body.attr_vals : "" - }, - function(err,newAttr) { - if(err) return res.sendResult(null,400,err); - res.sendResult(newAttr,200,"更新成功"); - })(req,res,next); - } -); - -// 删除参数 -router.delete("/:id/attributes/:attrId", - // 验证参数 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"分类ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"分类ID必须是数字"); - if(!req.params.attrId) { - return res.sendResult(null,400,"参数ID不能为空"); - } - if(isNaN(parseInt(req.params.attrId))) return res.sendResult(null,400,"参数ID必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - attrServ.deleteAttribute(req.params.attrId,function(err,newAttr) { - if(err) return res.sendResult(null,400,err); - res.sendResult(null,200,"删除成功"); - })(req,res,next); - } -); + // 参数验证中间件,检查分类ID是否存在、是否为数字类型,参数名称是否为空,以及参数的attr_sel类型是否正确等, + // 确保创建分类参数时所有传入的参数都是合法合规的,只有这样才能顺利进行后续的创建参数操作, + // 防止因参数不符合要求导致创建失败或者出现数据异常情况 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "分类ID不能为空"); + } + // 检查传入的 req.params.id 是否能成功转换为数字,如果不能(即不是数字格式), +// 则向客户端返回错误响应,状态码为400表示请求参数错误,同时给出相应的错误提示信息 +if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "分类ID必须是数字"); +// 检查 req.params.attrId 是否存在,如果不存在,说明参数缺失,不符合要求 +if (!req.params.attrId) { + return res.sendResult(null, 400, "参数ID不能为空"); +} +// 进一步检查 req.params.attrId 是否能成功转换为数字,若不能转换,同样不符合要求, +// 要向客户端返回错误响应,告知参数格式错误 +if (isNaN(parseInt(req.params.attrId))) return res.sendResult(null, 400, "参数ID必须是数字"); +// 参数验证通过,调用next()函数,按照Express中间件的机制,将控制权交给下一个中间件, +// 以便继续后续的业务逻辑处理流程 +next(); +}, +// 业务逻辑中间件,用于执行删除分类参数的操作,在前面的参数验证中间件通过后,在此执行具体的删除操作逻辑 +function (req, res, next) { + // 调用attrServ服务的deleteAttribute方法,传入参数ID参数(这里的参数ID即前面经过验证的req.params.attrId), + // 以此来触发删除分类参数的实际操作,该操作通常是与数据库等数据存储进行交互,执行删除对应记录的动作,这是一个异步操作 + attrServ.deleteAttribute(req.params.attrId, function (err, newAttr) { + // 如果在执行删除操作过程中出现错误(err不为null),则向客户端返回包含错误信息的响应, + // 状态码400表示操作失败,同时将具体的错误信息传递过去 + if (err) return res.sendResult(null, 400, err); + // 如果删除操作成功,向客户端返回成功提示信息,状态码200表示操作成功, + // 这里返回的提示信息只是简单的"删除成功",实际应用中可根据需求返回更详细的内容 + res.sendResult(null, 200, "删除成功"); + })(req, res, next); +}); +// 将配置好的路由器对象(包含了前面定义的一系列路由及对应的处理逻辑)导出, +// 这样其他的Node.js模块就可以通过引入这个模块来使用这个路由器, +// 进而处理相应的HTTP请求,实现对应的业务功能 module.exports = router; \ No newline at end of file diff --git a/routes/api/private/v1/goods.js b/routes/api/private/v1/goods.js index a5d933e..a6a03f2 100644 --- a/routes/api/private/v1/goods.js +++ b/routes/api/private/v1/goods.js @@ -1,190 +1,278 @@ +//这段代码整体实现了一个用于获取商品列表的路由功能,先是对请求参数进行严格验证,确保参数符合分 +//页查询及可能的模糊查询要求,然后基于验证通过的参数去调用相应服务方法获取商品列表数据,并根据 +//操作结果向客户端返回合适的响应信息。 +// 引入Express框架的核心模块,Express是一个广泛用于Node.js的Web应用框架, +// 通过它可以方便地创建Web应用、定义路由以及处理各种HTTP请求等功能 var express = require('express'); + +// 创建一个Express路由器实例,路由器(Router)在Express中用于将一组相关的路由进行集中管理, +// 可以让代码结构更加清晰,便于对不同路径和请求方法的请求进行分别处理 var router = express.Router(); + +// 引入Node.js的path模块,该模块提供了许多实用的函数用于处理文件路径相关操作, +// 例如拼接路径、解析路径、获取文件扩展名等,在这里主要用于拼接模块的正确引入路径 var path = require("path"); -// 获取验证模块 -var authorization = require(path.join(process.cwd(),"/modules/authorization")); +// 获取自定义的验证模块,通过process.cwd()获取当前工作目录,并与"/modules/authorization"进行路径拼接, +// 以此来准确引入对应的模块。这里假设验证模块(authorization)具备权限验证功能, +// 同时还能提供获取其他相关服务的能力,是整个应用中权限管理及服务获取的关键部分 +var authorization = require(path.join(process.cwd(), "/modules/authorization")); -// 通过验证模块获取分类管理 +// 通过验证模块获取名为"GoodService"的服务对象,"GoodService"大概率是在authorization模块中定义的一个服务标识, +// 通过这个标识获取到的服务对象应该封装了一系列与商品数据交互的各种方法,例如对商品数据进行添加、删除、修改、查询等操作, +// 方便在后续的路由处理中调用相应功能来处理业务逻辑 var goodServ = authorization.getService("GoodService"); -// 商品列表 +// 定义处理获取商品列表的GET请求的路由,路径为根路径 "/",这意味着当客户端向服务器的根路径发送GET请求时, +// 将会进入这个路由对应的处理逻辑来获取商品列表信息 router.get("/", - // 验证参数 - function(req,res,next) { - // 参数验证 - if(!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null,400,"pagenum 参数错误"); - if(!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null,400,"pagesize 参数错误"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - var conditions = { - "pagenum" : req.query.pagenum, - "pagesize" : req.query.pagesize - }; - - if(req.query.query) { - conditions["query"] = req.query.query; - } - goodServ.getAllGoods( - conditions, - function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取成功"); - } - )(req,res,next); - } -); + // 第一个中间件函数,用于验证请求参数的合法性,在Express框架中,中间件函数可以对请求进行预处理, + // 在这里主要是检查请求中携带的参数是否符合要求,确保后续业务逻辑能基于正确的参数进行处理 + function (req, res, next) { + // 验证pagenum参数是否存在且大于0,如果不符合要求则返回错误响应,状态码400表示请求参数错误。 + // pagenum参数通常用于分页查询,表示要获取的页码,若不存在或者小于等于0,则不符合正常的分页逻辑 + if (!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null, 400, "pagenum 参数错误"); + + // 验证pagesize参数是否存在且大于0,如果不符合要求则返回错误响应。 + // pagesize参数同样常用于分页查询,表示每页显示的记录数量,若不存在或者小于等于0,也不符合分页的合理要求 + if (!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null, 400, "pagesize 参数错误"); + + // 参数验证通过,调用next()将控制权传递给下一个中间件或路由处理函数,这是Express中间件机制中很重要的一环, + // 通过调用next()来确保请求能按照顺序依次经过各个中间件进行相应的处理,若不调用则请求会被阻塞在此中间件处 + next(); + }, + // 第二个中间件函数,用于处理获取商品列表的业务逻辑,在前面的参数验证中间件通过后, + // 会进入这个中间件来执行具体的获取商品列表操作,比如从数据库等数据存储中查询符合条件的商品数据 + function (req, res, next) { + var conditions = { + // 将请求中的pagenum参数添加到查询条件对象中,用于分页查询等操作, + // 后续在调用获取商品列表的方法时,可以根据这个参数来确定返回哪一页的数据 + "pagenum": req.query.pagenum, + // 将请求中的pagesize参数添加到查询条件对象中,以便根据每页显示数量的设定来返回相应数量的商品记录 + "pagesize": req.query.pagesize + }; -// 添加商品 + // 如果请求中包含query参数,则将其也添加到查询条件对象中,query参数可能用于模糊查询等功能, + // 例如根据商品名称、描述等字段进行模糊匹配查找符合条件的商品,丰富了查询的灵活性 + if (req.query.query) { + conditions["query"] = req.query.query; + } + + // 调用goodServ服务对象的getAllGoods方法,传入查询条件对象,用于获取商品列表。 + // getAllGoods方法应该是在GoodService中定义的用于从数据存储(如数据库)中查询所有符合条件商品的方法, + // 它是一个异步操作,通常会涉及到数据库连接、查询语句执行等操作,所以通过回调函数来处理操作完成后的结果情况 + goodServ.getAllGoods( + conditions, + function (err, result) { + // 如果在执行获取商品列表的异步操作过程中出现错误(err不为null), + // 则向客户端返回包含错误信息的响应,状态码400表示请求出现错误,同时将具体的错误信息传递给客户端 + if (err) return res.sendResult(null, 400, err); + + // 如果获取商品列表操作成功,向客户端返回查询到的商品列表数据,状态码200表示请求成功, + // 并附带"获取成功"的提示信息告知客户端操作已顺利完成 + res.sendResult(result, 200, "获取成功"); + } + )(req, res, next); + } +); +//这段代码分别实现了添加商品和更新商品信息的路由功能。添加商品的路由中参数验证部分有待完善,而 +//更新商品信息的路由先对商品 ID 进行了严谨的参数验证,之后基于验证通过的参数去调用相应服务方法 +//执行更新操作,并根据操作结果向客户端返回合适的响应信息,不过其中更新成功的提示文本可能需要修 +//正。 +// 定义处理添加商品的POST请求的路由,路径为 "/",这意味着当客户端向服务器的根路径发送POST请求时, +// 将会进入这个路由对应的处理逻辑,通常POST请求用于向服务器提交数据以创建新的资源,在这里就是用于添加商品 router.post("/", - // 参数验证 - function(req,res,next) { - next(); - }, - // 业务逻辑 - function(req,res,next) { - var params = req.body; - goodServ.createGood(params,function(err,newGood){ - if(err) return res.sendResult(null,400,err); - res.sendResult(newGood,201,"创建商品成功"); - })(req,res,next); - } + // 参数验证中间件,这里暂时直接调用next(),表示当前没有进行具体的参数验证操作, + // 可能后续需要补充具体的参数验证逻辑,比如验证请求体中是否包含创建商品必需的字段等内容, + // 按照正常的业务逻辑,添加商品时请求体应该携带如商品名称、价格、描述等必要信息,需要进行合法性检查 + function (req, res, next) { + next(); + }, + // 业务逻辑中间件,用于处理添加商品的具体操作,在参数验证中间件(虽然当前为空验证)通过后, + // 会进入这个中间件来执行实际的添加商品操作,比如将商品数据插入到数据库等数据存储中 + function (req, res, next) { + // 获取请求体中的参数,假设这些参数包含了创建商品所需的各种信息,如商品名称、价格、描述等, + // 在实际应用中,具体的参数结构和内容取决于前端传递的数据格式以及后端创建商品业务逻辑的要求 + var params = req.body; + // 调用goodServ服务对象的createGood方法,传入商品参数,用于创建新商品。 + // createGood方法应该是在GoodService中定义的用于向数据存储(如数据库)中插入新商品记录的方法, + // 它是一个异步操作,会涉及到数据库连接、插入语句执行等操作,所以通过回调函数来处理操作完成后的结果情况 + goodServ.createGood(params, function (err, newGood) { + // 如果在执行创建商品的异步操作过程中出现错误(err不为null), + // 则向客户端返回包含错误信息的响应,状态码400表示请求出现错误,同时将具体的错误信息传递给客户端 + if (err) return res.sendResult(null, 400, err); + // 如果创建商品操作成功,向客户端返回创建后的商品信息,状态码201表示资源创建成功, + // 并附带"创建商品成功"的提示信息告知客户端操作已顺利完成 + res.sendResult(newGood, 201, "创建商品成功"); + })(req, res, next); + } ); -// 更新商品 +// 定义处理更新商品信息的PUT请求的路由,路径中包含商品ID参数,如 "/:id",这表示当客户端向服务器发送PUT请求, +// 且路径中带有具体的商品ID值时,会进入这个路由对应的处理逻辑,PUT请求常用于更新指定资源的信息,此处就是更新商品信息 router.put("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"商品ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"商品ID必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - var params = req.body; - goodServ.updateGood(req.params.id,params,function(err,newGood){ - if(err) return res.sendResult(null,400,err); - res.sendResult(newGood,200,"创建商品成功"); - })(req,res,next); - } + // 参数验证中间件,检查商品ID参数是否存在以及是否为数字类型,因为要更新指定的商品, + // 必须先确保传入的商品ID是合法有效的,即不能为空且要是数字类型,这样才能准确找到对应的商品记录进行更新 + function (req, res, next) { + // 检查商品ID参数是否存在,如果不存在,说明缺少关键的更新依据,不符合要求 + if (!req.params.id) { + return res.sendResult(null, 400, "商品ID不能为空"); + } + // 进一步检查商品ID参数是否能成功转换为数字,如果不能(即不是数字格式),同样不符合要求, + // 无法准确定位要更新的商品记录,所以要返回错误响应给客户端 + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "商品ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件,以便继续执行更新商品信息的业务逻辑,按照Express中间件的执行顺序推进处理流程 + next(); + }, + // 业务逻辑中间件,用于处理更新商品信息的具体操作,在前面的参数验证中间件通过后, + // 会进入这个中间件来执行实际的更新商品信息操作,比如修改数据库中对应商品记录的相关字段值 + function (req, res, next) { + var params = req.body; + // 调用goodServ服务对象的updateGood方法,传入商品ID和更新的参数,用于更新指定商品的信息。 + // updateGood方法应该是在GoodService中定义的用于根据商品ID修改对应商品记录信息的方法, + // 它是一个异步操作,涉及到数据库连接、更新语句执行等操作,通过回调函数来处理操作完成后的结果情况 + goodServ.updateGood(req.params.id, params, function (err, newGood) { + // 如果在执行更新商品信息的异步操作过程中出现错误(err不为null), + // 则向客户端返回包含错误信息的响应,状态码400表示请求出现错误,同时将具体的错误信息传递给客户端 + if (err) return res.sendResult(null, 400, err); + // 如果更新商品信息操作成功,向客户端返回更新后的商品信息,状态码200表示请求成功, + // 当前这里返回的提示信息文本 "创建商品成功" 可能有误,应该是 "更新商品成功",后续可根据实际情况修改, + // 用于告知客户端商品信息已成功更新 + res.sendResult(newGood, 200, "创建商品成功"); + })(req, res, next); + } ); -// 获取商品详情 +// 定义处理获取商品详情的GET请求的路由,路径中包含商品ID参数,如 "/:id" router.get("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"商品ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"商品ID必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - goodServ.getGoodById(req.params.id,function(err,good){ - if(err) return res.sendResult(null,400,err); - return res.sendResult(good,200,"获取成功"); - })(req,res,next); - } + // 参数验证中间件,检查商品ID参数是否存在以及是否为数字类型 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "商品ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "商品ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件 + next(); + }, + // 业务逻辑中间件,用于处理获取商品详情的具体操作 + function (req, res, next) { + // 调用goodServ服务对象的getGoodById方法,传入商品ID参数,用于获取指定商品的详细信息 + // 处理获取商品详情的异步操作,若出现错误则返回错误信息,成功则返回商品详细信息 + goodServ.getGoodById(req.params.id, function (err, good) { + if (err) return res.sendResult(null, 400, err); + return res.sendResult(good, 200, "获取成功"); + })(req, res, next); + } ); - - -// 删除商品 +// 定义处理删除商品的DELETE请求的路由,路径中包含商品ID参数,如 "/:id" router.delete("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"商品ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"商品ID必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - goodServ.deleteGood(req.params.id,function(err){ - if(err) - return res.sendResult(null,400,"删除失败"); - else - return res.sendResult(null,200,"删除成功"); - })(req,res,next); - } + // 参数验证中间件,检查商品ID参数是否存在以及是否为数字类型 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "商品ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "商品ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件 + next(); + }, + // 业务逻辑中间件,用于处理删除商品的具体操作 + function (req, res, next) { + // 调用goodServ服务对象的deleteGood方法,传入商品ID参数,用于删除指定商品 + // 处理删除商品的异步操作,若出现错误则返回错误信息,成功则返回相应提示信息 + goodServ.deleteGood(req.params.id, function (err) { + if (err) + return res.sendResult(null, 400, "删除失败"); + else + return res.sendResult(null, 200, "删除成功"); + })(req, res, next); + } ); -// 更新商品的图片 +// 定义处理更新商品图片的PUT请求的路由,路径为 "/:id/pics",其中包含商品ID参数 router.put("/:id/pics", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"商品ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"商品ID必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - - goodServ.updateGoodPics( - req.params.id, - req.body, - function(err,good){ - if(err) return res.sendResult(null,400,err); - res.sendResult(good,200,"更新成功"); - } - )(req,res,next); - } + // 参数验证中间件,检查商品ID参数是否存在以及是否为数字类型 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "商品ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "商品ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件 + next(); + }, + // 业务逻辑中间件,用于处理更新商品图片的具体操作 + function (req, res, next) { + // 调用goodServ服务对象的updateGoodPics方法,传入商品ID和请求体中的相关参数(可能包含新的图片信息等) + // 处理更新商品图片的异步操作,若出现错误则返回错误信息,成功则返回更新后的商品信息 + goodServ.updateGoodPics( + req.params.id, + req.body, + function (err, good) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(good, 200, "更新成功"); + } + )(req, res, next); + } ); -// 更新商品的属性 +// 定义处理更新商品属性的PUT请求的路由,路径为 "/:id/attributes",包含商品ID参数 router.put("/:id/attributes", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"商品ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"商品ID必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - - goodServ.updateGoodAttributes ( - req.params.id, - req.body, - function(err,good){ - if(err) return res.sendResult(null,400,err); - res.sendResult(good,200,"更新成功"); - } - )(req,res,next); - } + // 参数验证中间件,检查商品ID参数是否存在以及是否为数字类型 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "商品ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "商品ID必须是数字"); + // 参数验证通过,将控制权交给下一个中间件 + next(); + }, + // 业务逻辑中间件,用于处理更新商品属性的具体操作 + function (req, res, next) { + // 调用goodServ服务对象的updateGoodAttributes方法,传入商品ID和请求体中的相关参数(包含要更新的属性信息等) + // 处理更新商品属性的异步操作,若出现错误则返回错误信息,成功则返回更新后的商品信息 + goodServ.updateGoodAttributes( + req.params.id, + req.body, + function (err, good) { + if (err) return res.sendResult(null, 400, "更新失败"); + res.sendResult(good, 200, "更新成功"); + } + )(req, res, next); + } ); -// 更新商品状态 +// 定义处理更新商品状态的PUT请求的路由,路径为 "/:id/state/:state",包含商品ID和状态值两个参数 router.put("/:id/state/:state", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"商品ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"商品ID必须是数字"); - if(!req.params.state) { - return res.sendResult(null,400,"状态值不能为空"); - } - if(req.params.state != 0 && req.params.state != 1 && req.params.state != 2) { - return res.sendResult(null,400,"状态值只能为 0 ,1 或 2"); - } - next(); - }, - function(req,res,next) { - goodServ.updateGoodsState(req.params.id,req.params.state,function(err,good){ - if(err) return res.sendResult(null,400,err); - res.sendResult(good,200,"更新成功"); - })(req,res,next); -} + // 参数验证中间件,检查商品ID参数是否存在、是否为数字类型,以及状态值参数是否符合要求 + function (req, res, next) { + if (!req.params.id) { + return res.sendResult(null, 400, "商品ID不能为空"); + } + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "商品ID必须是数字"); + if (!req.params.state) { + return res.sendResult(null, 400, "状态值不能为空"); + } + if (req.params.state!= 0 && req.params.state!= 1 && req.params.state!= 2) { + return res.sendResult(null, 400, "状态值只能为 0 ,1 或 2"); + } + // 参数验证通过,将控制权交给下一个中间件 + next(); + }, + function (req, res, next) { + // 调用goodServ服务对象的updateGoodsState方法,传入商品ID和状态值参数,用于更新商品的状态 + // 处理更新商品状态的异步操作,若出现错误则返回错误信息,成功则返回更新后的商品信息 + goodServ.updateGoodsState(req.params.id, req.params.state, function (err, good) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(good, 200, "更新成功"); + })(req, res, next); + } ); +// 将配置好的路由器对象导出,以便在主应用模块中使用,挂载到对应的路径上, +// 这样主应用就能通过引入这个模块来使用该路由器中定义的所有路由及相关处理逻辑, +// 从而处理各种与商品相关的HTTP请求 module.exports = router; +//这段代码整体围绕商品相关的各种操作定义了一系列的路由及其对应的处理逻辑。每个路由处理函数基本 +//都遵循先进行参数验证(确保传入的参数符合业务要求),再执行相应的业务逻辑(如增删改查、更新图 +//片、属性、状态等操作)的模式,并且根据操作结果向客户端返回合适的响应信息,最后通过导出路由器 +//对象使得这些路由配置能在整个应用中生效。 \ No newline at end of file diff --git a/routes/api/private/v1/menus.js b/routes/api/private/v1/menus.js index 4198ccd..df6dad3 100644 --- a/routes/api/private/v1/menus.js +++ b/routes/api/private/v1/menus.js @@ -1,20 +1,45 @@ +//这段代码主要实现了一个简单的获取菜单列表的路由功能,先引入必要的模块,然后定义了一个基于 GET //请求获取菜单列表的路由,在路由处理函数中调用了菜单服务模块的方法来获取菜单数据,并根据操作结 +//果向客户端返回合适的响应信息,最后导出路由器对象供主应用使用。 +// 引入Express框架的核心模块,Express是Node.js中常用的Web应用框架,通过它可以创建服务器、定义路由以及处理HTTP请求等诸多功能, +// 这里引入它是后续创建路由等操作的基础 var express = require('express'); + +// 创建一个Express路由器实例,在Express框架里,路由器(Router)用于将一组相关的路由进行集中管理,方便实现模块化的路由配置, +// 使得代码结构更加清晰,便于对不同路径和请求方法的请求进行分别处理,比如将所有和菜单相关的路由放在这里统一管理 var router = express.Router(); + +// 引入Node.js的path模块,该模块提供了一系列用于处理文件路径相关操作的实用方法,例如拼接路径、解析路径、获取文件扩展名等, +// 在这里主要是用于拼接出准确的模块引入路径,确保能正确引入其他自定义模块 var path = require("path"); -// 获取验证模块 -var authorization = require(path.join(process.cwd(),"/modules/authorization")); +// 获取自定义的验证模块,通过path.join方法将当前工作目录(process.cwd())和相对路径("/modules/authorization")拼接起来, +// 以此来准确指定验证模块所在的位置,然后使用require函数进行引入。这个验证模块通常承担着验证用户权限等相关操作的职责, +// 目的是保证后续执行的各种操作都是在合法且符合相应权限要求的情况下进行,避免出现越权等安全问题 +var authorization = require(path.join(process.cwd(), "/modules/authorization")); -// 通过验证模块获取菜单服务模块 -var menuService = require(path.join(process.cwd(),"/services/MenuService")); +// 通过验证模块获取名为"MenuService"的菜单服务模块,同样借助path.join方法拼接当前工作目录和服务模块所在的相对路径("/services/MenuService"), +// 从而准确引入该模块。这个菜单服务模块大概率封装了与菜单相关的各种业务逻辑,比如获取菜单数据(像从数据库中查询菜单信息等操作)、 +// 对菜单进行增删改等操作的功能,方便在后续的路由处理中调用相应方法来实现具体的菜单相关业务需求 +var menuService = require(path.join(process.cwd(), "/services/MenuService")); +// 定义一个处理GET请求的路由,路径为根路径 "/",意味着当客户端向服务器发送GET请求到根路径时,将会进入这个路由对应的处理逻辑, +// 此路由的功能是用于获取菜单列表,通常会从数据库或者其他数据存储中查询并获取相应的菜单数据,然后返回给客户端展示 router.get("/", - function(req,res,next) { - menuService.getLeftMenus(req.userInfo,function(err,result) { - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取菜单列表成功"); - }); - } + // 路由处理函数,在这个函数中调用了menuService的getLeftMenus方法来获取左侧菜单列表。这里传入的参数req.userInfo可能是一个包含了当前用户相关信息的对象, + // 例如用户的角色、权限等信息,菜单服务模块(MenuService)内部的getLeftMenus方法会根据这些用户信息来生成对应的菜单列表, + // 比如不同角色的用户看到的菜单选项可能是不一样的,通过这种方式实现菜单的权限控制和个性化展示 + function (req, res, next) { + menuService.getLeftMenus(req.userInfo, function (err, result) { + // 如果获取菜单列表的操作出现错误(err不为null),也就是在执行getLeftMenus方法过程中,比如数据库查询出错、权限验证不通过等情况发生时, + // 则使用res.sendResult方法返回一个带有错误信息的响应,状态码为400,表示请求出现错误,具体的错误信息由err传递过来,这样客户端就能知晓请求失败的原因 + if (err) return res.sendResult(null, 400, err); + // 如果获取菜单列表成功,也就是getLeftMenus方法顺利执行并获取到了相应的菜单列表数据,那么就使用res.sendResult方法返回成功的响应, + // 将获取到的菜单列表数据(result)传递回去,状态码为200,表示请求成功,同时附带一个提示信息"获取菜单列表成功",告知客户端操作已顺利完成 + res.sendResult(result, 200, "获取菜单列表成功"); + }); + } ); +// 将配置好的路由器对象导出,以便在主应用中可以引入并挂载到对应的路径上,只有挂载之后,这些定义好的路由才能正常工作, +// 进而响应客户端发送过来的相应请求,实现整个Web应用的菜单相关功能交互 module.exports = router; \ No newline at end of file diff --git a/routes/api/private/v1/orders.js b/routes/api/private/v1/orders.js index a620241..1239509 100644 --- a/routes/api/private/v1/orders.js +++ b/routes/api/private/v1/orders.js @@ -1,101 +1,180 @@ +// 引入Express框架的核心模块,用于创建Web应用以及进行路由相关操作。Express是Node.js中广泛使用的Web框架, +// 借助它可以方便地搭建服务器、定义不同路径及请求方法对应的路由处理逻辑等,是整个Web应用开发的基础框架模块 var express = require('express'); + +// 创建一个Express路由器实例,方便后续在应用中进行模块化的路由定义与管理。通过使用路由器, +// 可以将不同功能模块相关的路由集中在一起,使代码结构更清晰,便于维护和扩展,比如将所有和订单相关的路由放在这里统一配置 var router = express.Router(); + +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,例如拼接文件或模块的路径等。在实际应用中, +// 常常需要准确指定模块所在的位置,path模块提供的函数就可以帮助我们灵活地组合路径字符串,确保模块能被正确引入 var path = require("path"); -// 获取验证模块 -var authorization = require(path.join(process.cwd(),"/modules/authorization")); +// 获取自定义的验证模块,通过path.join方法将当前工作目录(process.cwd())与相对路径("/modules/authorization")拼接起来, +// 准确地找到并引入该验证模块。这个验证模块在整个应用架构中起着关键作用,通常会负责诸如用户权限验证等相关功能, +// 只有通过了权限验证的操作才能继续执行,以此确保后续操作的合法性和安全性,防止非法访问和数据泄露等问题 +var authorization = require(path.join(process.cwd(), "/modules/authorization")); -// 通过验证模块获取分类管理 +// 通过验证模块获取名为"OrderService"的服务对象,该服务对象应该封装了与订单相关的各种业务操作方法,例如订单的增删改查等功能。 +// 这样在后续的路由处理中,就可以方便地调用这些方法来实现具体的订单相关业务逻辑,无需在路由代码中重复编写复杂的数据库操作等代码 var orderServ = authorization.getService("OrderService"); -// 订单列表 +// 定义处理获取订单列表的GET请求的路由,路径为根路径 "/",这意味着当客户端向服务器发送GET请求到根路径时, +// 将会进入这个路由对应的处理逻辑,以获取相应的订单列表数据,一般会从数据库等数据存储中查询并返回符合条件的订单信息给客户端 router.get("/", - // 参数验证 - function(req,res,next) { - // 参数验证 - if(!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null,400,"pagenum 参数错误"); - if(!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null,400,"pagesize 参数错误"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - var conditions = { - "pagenum" : req.query.pagenum, - "pagesize" : req.query.pagesize - }; + // 第一个中间件函数,用于对请求参数进行验证。在Express框架中,中间件函数可以在请求到达最终的路由处理函数之前, +// 对请求进行预处理,比如检查请求携带的参数是否符合业务要求,确保后续基于这些参数进行的业务操作不会因为参数错误而出现异常 + function (req, res, next) { + // 验证pagenum参数是否存在且大于0,如果该参数不存在或者小于等于0,则返回错误响应,状态码400表示请求参数有误。 + // pagenum参数通常用于分页查询操作,表示要获取的页码,从逻辑上来说,页码应该是大于0的正整数,若不符合此要求则不符合分页查询的正常规则 + if (!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null, 400, "pagenum 参数错误"); + // 验证pagesize参数是否存在且大于0,如果该参数不存在或者小于等于0,则返回错误响应。 + // pagesize参数同样常用于分页查询操作,用于指定每页显示的订单数量,也应当是大于0的正整数,否则无法正确进行分页展示订单数据 + if (!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null, 400, "pagesize 参数错误"); + // 参数验证通过后,调用next()将控制权传递给下一个中间件或路由处理函数,这是Express中间件机制中实现流程控制的关键操作, +// 通过调用next()来保证请求能按照顺序依次经过各个中间件进行相应处理,若不调用则请求会被阻塞在当前中间件处,无法继续后续流程 + next(); + }, + // 第二个中间件函数,用于处理获取订单列表的业务逻辑,在前面的参数验证中间件通过后,就会进入这个中间件来执行具体的获取订单列表操作, +// 例如根据前面验证通过的参数去数据库中查询符合条件的订单数据,然后将查询结果返回给客户端展示 + function (req, res, next) { + var conditions = { + // 将请求中的pagenum参数添加到查询条件对象中,该参数常用于分页查询操作,指定要获取的页码, +// 后续在调用获取订单列表的方法时,可以依据这个页码来确定从数据库中取出哪一部分订单数据返回给客户端,实现分页功能 + "pagenum": req.query.pagenum, + // 将请求中的pagesize参数添加到查询条件对象中,用于指定每页显示的订单数量,这样就能准确控制每次返回给客户端的订单数据量, +// 方便用户分页浏览订单列表,提升用户体验 + "pagesize": req.query.pagesize + }; - if(req.query.user_id) { - conditions["user_id"] = req.query.user_id; - } - if(req.query.pay_status) { - conditions["pay_status"] = req.query.pay_status; - } - if(req.query.is_send) { - conditions["is_send"] = req.query.is_send; - } - if(req.query.order_fapiao_title) { - conditions["order_fapiao_title"] = req.query.order_fapiao_title; - } - if(req.query.order_fapiao_company) { - conditions["order_fapiao_company"] = req.query.order_fapiao_company; - } - if(req.query.order_fapiao_content) { - conditions["order_fapiao_content"] = req.query.order_fapiao_content; - } - if(req.query.consignee_addr) { - conditions["consignee_addr"] = req.query.consignee_addr; - } + // 如果请求中包含user_id参数,则将其添加到查询条件对象中,可用于根据用户ID筛选订单。比如在电商系统中, +// 用户可能只想查看自己的订单,通过传入自己的用户ID作为筛选条件,就能获取到与之对应的订单信息,实现个性化的数据查询需求 + if (req.query.user_id) { + conditions["user_id"] = req.query.user_id; + } + // 如果请求中包含pay_status参数,则将其添加到查询条件对象中,可能用于根据支付状态筛选订单。例如, +// 商家可能想查看已支付、未支付或者部分支付的订单情况,就可以通过传入相应的支付状态值来筛选出符合要求的订单列表,便于进行业务管理 + if (req.query.pay_status) { + conditions["pay_status"] = req.query.pay_status; + } + // 如果请求中包含is_send参数,则将其添加到查询条件对象中,也许用于根据订单是否已发送来筛选订单。像物流相关的业务场景中, +// 可以通过这个参数筛选出已发货或者未发货的订单,方便跟踪订单的物流状态以及进行相应的后续操作 + if (req.query.is_send) { + conditions["is_send"] = req.query.is_send; + } // 如果请求中包含order_fapiao_title参数,则将其添加到查询条件对象中,可能是用于根据发票抬头筛选订单之类的业务需求 + if (req.query.order_fapiao_title) { + conditions["order_fapiao_title"] = req.query.order_fapiao_title; + } +//这段代码整体实现了一个获取订单列表的路由功能,先是通过中间件对请求参数进行了必要的验证,确保 +//分页相关参数以及可能的筛选参数是合法有效的,后续准备基于这些参数去执行获取订单列表的实际业务 +//逻辑,不过目前代码还未完整展示出调用服务获取订单列表以及返回结果的部分。 + // 如果请求中包含order_fapiao_company参数,则将其添加到查询条件对象中,也许是根据发票所属公司来筛选订单。 +// 在实际业务场景中,例如企业财务统计或者税务相关需求时,可能需要按照发票所属公司来查看对应的订单情况,所以通过这个参数进行筛选 +if (req.query.order_fapiao_company) { + conditions["order_fapiao_company"] = req.query.order_fapiao_company; +} +// 如果请求中包含order_fapiao_content参数,则将其添加到查询条件对象中,可能用于根据发票内容筛选订单。 +// 比如根据发票开具的具体商品或服务内容来查找特定类型的订单,方便财务核算或者业务分析等操作 +if (req.query.order_fapiao_content) { + conditions["order_fapiao_content"] = req.query.order_fapiao_content; +} +// 如果请求中包含consignee_addr参数,则将其添加到查询条件对象中,可用于根据收件人地址筛选订单。 +// 像物流配送部门可能会根据收件人地址来统计不同地区的订单量、查看特定区域的订单发货情况等,以此实现针对性的业务管理 +if (req.query.consignee_addr) { + conditions["consignee_addr"] = req.query.consignee_addr; +} - orderServ.getAllOrders( - conditions, - function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取成功"); - } - )(req,res,next); - } -); +// 调用orderServ服务对象的getAllOrders方法,传入构建好的查询条件对象,用于获取符合条件的订单列表。 +// getAllOrders方法应该是在OrderService中定义的用于从数据库等数据存储中查询出满足各种筛选条件的订单数据的方法, +// 它是一个异步操作,涉及到数据库查询语句的执行、数据的获取及整理等操作,所以需要通过回调函数来处理操作完成后的结果情况 +orderServ.getAllOrders( + conditions, + function (err, result) { + // 如果在执行获取订单列表的异步操作过程中出现错误(err不为null),例如数据库连接失败、查询语句语法错误等情况, + // 则返回包含错误信息的响应,状态码为400表示请求出现错误,同时将具体的错误信息(err)传递给客户端,让客户端知晓请求失败的原因 + if (err) return res.sendResult(null, 400, err); + // 如果获取订单列表操作成功,也就是成功从数据库等数据源获取到了符合条件的订单数据, + // 则返回包含订单列表数据(result)的响应,状态码为200表示请求成功,并且附带提示信息"获取成功",告知客户端操作已顺利完成 + res.sendResult(result, 200, "获取成功"); + } +)(req, res, next); +}); -// 添加订单 +// 定义处理添加订单的POST请求的路由,路径为 "/",这意味着当客户端向服务器的根路径发送POST请求时, +// 将会进入这个路由对应的处理逻辑,通常POST请求用于向服务器提交数据来创建新的资源,在这里就是用于添加新的订单 router.post("/", - // 参数验证 - function(req,res,next) { - next(); - }, - // 业务逻辑 - function(req,res,next) { - var params = req.body; - orderServ.createOrder(params,function(err,newOrder){ - if(err) return res.sendResult(null,400,err); - return res.sendResult(newOrder,201,"创建订单成功"); - })(req,res,next); - - } + // 参数验证中间件,当前这里直接调用next(),意味着暂时没有进行额外的参数验证逻辑,可能后续需要补充相关验证代码。 + // 按照正常的业务逻辑,添加订单时请求体应该包含如商品信息、用户信息、收货地址等创建订单所必需的各种信息,需要对这些信息的完整性和合法性进行验证 + function (req, res, next) { + next(); + }, + // 业务逻辑中间件,用于处理添加订单的具体操作,在参数验证中间件(虽然当前为空验证)通过后, + // 会进入这个中间件来执行实际的添加订单操作,比如将订单数据插入到数据库等数据存储中,完成新订单的创建 + function (req, res, next) { + // 获取请求体中的参数,这些参数(params)应该包含了创建订单所需的各种信息,例如商品信息、用户信息、收货地址等, + // 具体的参数结构和内容取决于前端传递的数据格式以及后端创建订单业务逻辑的要求 + var params = req.body; + // 调用orderServ服务对象的createOrder方法,传入订单参数,用于创建新的订单。 + // createOrder方法应该是在OrderService中定义的用于向数据存储(如数据库)中插入新订单记录的方法, + // 它是一个异步操作,会涉及到数据库连接、插入语句执行等操作,所以通过回调函数来处理操作完成后的结果情况 + orderServ.createOrder(params, function (err, newOrder) { + // 如果在执行创建订单的异步操作过程中出现错误(err不为null),例如数据库插入失败、参数不符合数据库表结构要求等情况, + // 则返回包含错误信息的响应,状态码为400表示请求出现错误,同时将具体的错误信息(err)传递给客户端,告知客户端创建订单失败的原因 + if (err) return res.sendResult(null, 400, err); + // 如果创建订单操作成功,也就是成功在数据库等数据存储中插入了新的订单记录, + // 则返回包含新创建订单信息(newOrder)的响应,状态码为201表示资源创建成功,并且附带提示信息"创建订单成功",告知客户端订单已成功创建 + return res.sendResult(newOrder, 201, "创建订单成功"); + })(req, res, next); + + } ); -// 更新订单发送状态 +// 定义处理更新订单的PUT请求的路由,路径中包含订单ID参数,格式为 "/:id",这里用于更新订单的发送状态(从代码上下文推测), +// 一般PUT请求常用于对已存在的资源进行部分更新操作,在这里就是针对特定订单(通过订单ID来确定)进行相关信息的更新,比如修改订单的发货状态等 router.put("/:id", - // 参数验证 - function(req,res,next) { - next(); - }, - // 业务逻辑 - function(req,res,next) { - var params = req.body; - orderServ.updateOrder(req.params.id,params,function(err,newOrder){ - if(err) return res.sendResult(null,400,err); - return res.sendResult(newOrder,201,"更新订单成功"); - })(req,res,next); - } + // 参数验证中间件,当前这里直接调用next(),暂时没有进行参数验证相关逻辑,也许后续需要添加如验证订单ID合法性等代码。 + // 因为要更新指定的订单,必须先确保传入的订单ID是合法有效的,例如不能为空且格式正确等,否则无法准确找到对应的订单记录进行更新操作 + function (req, res, next) { + next(); + }, + // 业务逻辑中间件,用于处理更新订单的具体操作,在前面参数验证中间件(虽然当前为空验证)通过后, + // 会进入这个中间件来执行实际的更新订单操作,比如修改数据库中对应订单记录的相关字段值,完成订单信息的更新 + function (req, res, next) { + var params = req.body; + // 调用orderServ服务对象的updateOrder方法,传入订单ID(req.params.id)和更新的参数(params),用于更新指定订单的相关信息。 + // updateOrder方法应该是在OrderService中定义的用于根据订单ID修改对应订单记录中相关字段信息的方法, + // 它是一个异步操作,涉及到数据库连接、更新语句执行等操作,通过回调函数来处理操作完成后的结果情况 + orderServ.updateOrder(req.params.id, params, function (err, newOrder) { + // 如果在执行更新订单的异步操作过程中出现错误(err不为null),例如数据库更新失败、传入的更新参数不符合要求等情况, + // 则返回包含错误信息的响应,状态码为400表示请求出现错误,同时将具体的错误信息(err)传递给客户端,告知客户端更新订单失败的原因 + if (err) return res.sendResult(null, 400, err); + // 如果更新订单操作成功,也就是成功在数据库等数据存储中修改了对应订单记录的相关信息, + // 则返回包含更新后订单信息(newOrder)的响应,状态码为201表示资源更新成功,并且附带提示信息"更新订单成功",告知客户端订单已成功更新 + return res.sendResult(newOrder, 201, "更新订单成功"); + })(req, res, next); + } ); -router.get("/:id",function(req,res,next){ - orderServ.getOrder(req.params.id,function(err,result){ - if(err) return res.sendResult(null,400,err); - return res.sendResult(result,200,"获取成功"); - })(req,res,next); +// 定义处理获取指定订单详情的GET请求的路由,路径中包含订单ID参数,格式为 "/:id",这表示当客户端向服务器发送GET请求并携带具体的订单ID时, +// 将会进入这个路由对应的处理逻辑,用于获取对应订单的详细信息,比如订单包含的商品详情、下单时间、收货地址、订单状态等各种具体信息 +router.get("/:id", function (req, res, next) { + // 调用orderServ服务对象的getOrder方法,传入订单ID(req.params.id),用于获取指定订单的详细信息。 + // getOrder方法应该是在OrderService中定义的用于从数据库等数据存储中查询出对应订单的详细记录信息的方法, + // 它是一个异步操作,涉及到数据库查询语句的执行以及数据的提取和整理等操作,所以通过回调函数来处理操作完成后的结果情况 + orderServ.getOrder(req.params.id, function (err, result) { + // 如果在执行获取订单详情的异步操作过程中出现错误(err不为null),例如数据库查询失败、订单ID对应的记录不存在等情况, + // 则返回包含错误信息的响应,状态码为400表示请求出现错误,同时将具体的错误信息(err)传递给客户端,让客户端知晓获取订单详情失败的原因 + if (err) return res.sendResult(null, 400, err); + // 如果获取订单详情操作成功,也就是成功从数据库等数据源获取到了指定订单的详细信息, + // 则返回包含订单详细信息(result)的响应,状态码为200表示请求成功,并且附带提示信息"获取成功",告知客户端操作已顺利完成 + return res.sendResult(result, 200, "获取成功"); + })(req, res, next); }); - - +// 将配置好的路由器对象导出,以便在主应用中引入并挂载到对应的路径上,使这些路由能够响应客户端相应的HTTP请求。 +// 只有在主应用中进行了挂载操作后,客户端发送的对应请求(如获取订单列表、添加订单、更新订单、获取订单详情等请求)才能被正确路由到这里定义的处理逻辑中进行处理 module.exports = router; +//这段代码围绕订单相关的操作定义了多个路由及其对应的处理逻辑,涵盖了获取订单列表、添加订单、更 +//新订单以及获取订单详情等功能。每个路由处理逻辑基本都遵循先进行参数验证(部分目前还未完善验证 +//逻辑),再执行相应业务操作,最后根据操作结果向客户端返回合适响应信息的流程,整体用于实现后端 +//与订单相关的接口服务功能。 \ No newline at end of file diff --git a/routes/api/private/v1/reports.js b/routes/api/private/v1/reports.js index 1990982..ad1a539 100644 --- a/routes/api/private/v1/reports.js +++ b/routes/api/private/v1/reports.js @@ -1,30 +1,67 @@ +// 引入Express框架的核心模块,Express是一个基于Node.js的Web应用框架,用于创建服务器端应用和处理HTTP请求等相关操作。 +// 它提供了诸多便捷的功能和方法,是构建Web应用后端服务的常用工具,例如定义路由、处理请求与响应等功能都依赖于此模块来实现。 var express = require('express'); + +// 创建一个Express路由器实例,通过这个实例可以定义一组相关的路由,方便在整个Web应用中进行模块化的路由管理。 +// 这种模块化的方式使得代码结构更加清晰,不同功能模块对应的路由可以分别定义在不同的路由器中,便于维护和扩展, +// 比如可以将用户相关路由、商品相关路由等分别放在不同的路由器实例里进行管理。 var router = express.Router(); -var path = require("path"); +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,例如拼接、解析文件路径等,在这里用于准确找到其他模块的位置。 +// 在Node.js应用中,当需要引入自定义的模块时,可能需要根据项目的目录结构来准确指定模块的路径,path模块就能很好地辅助完成这个任务。 +var path = require("path"); -// 获取验证模块 -var authorization = require(path.join(process.cwd(),"/modules/authorization")); +// 获取自定义的验证模块,通过path.join方法将当前工作目录(process.cwd())与相对路径("/modules/authorization")拼接起来,以此来准确引入验证模块。 +// 这种方式确保了无论应用在何种环境下运行,都能正确定位到指定的验证模块所在位置。这个验证模块通常会承担诸如验证用户权限、合法性等功能, +// 它是保障整个Web应用安全和遵循业务规则的重要环节,只有通过了该模块验证的操作,才能继续往下执行,避免出现非法访问、越权操作等安全问题。 +var authorization = require(path.join(process.cwd(), "/modules/authorization")); -// 通过验证模块获取用户管理服务 +// 通过验证模块获取名为"ReportsService"的用户管理服务对象,该服务对象应该封装了与报表相关的各种业务操作方法,比如获取不同类型报表等功能。 +// 通过这种方式将报表相关的业务逻辑封装在一个服务对象里,使得代码的职责更加清晰,在路由处理中只需调用该服务对象的相应方法即可, +// 无需在路由代码里大量编写具体的业务实现细节,例如数据库查询等操作都可以在这个服务对象内部完成。 var reportsServ = authorization.getService("ReportsService"); +// 定义处理获取特定类型报表的GET请求的路由,路径格式为 "/type/:typeid",其中":typeid"是一个路由参数,表示报表的类型ID。 +// 当客户端向服务器发送GET请求到这个特定路径时,服务器会根据请求中传入的报表类型ID来尝试获取对应的报表数据, +// 例如客户端可能请求获取销售报表(类型ID对应销售报表类型)或者财务报表(对应相应的财务报表类型)等不同类型的报表。 router.get("/type/:typeid", - // 参数验证 - function(req,res,next){ - if(!req.params.typeid) { - return res.sendResult(null,400,"报表类型不能为空"); - } - if(isNaN(parseInt(req.params.typeid))) return res.sendResult(null,400,"报表类型必须是数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - reportsServ.reports(req.params.typeid,function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取报表成功"); - })(req,res,next); - } + // 第一个中间件函数,用于对路由参数进行验证,确保传入的报表类型ID参数是合法有效的。 + // 在Web应用中,对路由参数进行验证是很重要的环节,能够防止因传入非法参数导致后续业务逻辑出现错误,比如数据库查询异常等情况。 + function (req, res, next) { + // 检查请求参数中的typeid是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,并附带相应的错误提示信息。 + // 因为报表类型ID是获取对应报表的关键参数,若缺失则无法明确要获取哪种类型的报表,所以必须要求该参数存在。 + if (!req.params.typeid) { + return res.sendResult(null, 400, "报表类型不能为空"); + } + // 进一步验证typeid参数是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,同样状态码为400,并给出相应错误提示。 + // 通常在业务逻辑里,报表类型ID可能是以数字形式在数据库等存储中进行标识和管理的,所以要求传入的参数能够正确转换为数字, + // 以此保证后续根据该ID去查询报表数据时不会出现类型不匹配等问题。 + if (isNaN(parseInt(req.params.typeid))) return res.sendResult(null, 400, "报表类型必须是数字"); + // 如果参数验证通过,调用next()将控制权传递给下一个中间件或者路由处理函数,继续后续的业务逻辑处理。 + // 这是Express中间件机制的关键操作,通过next()函数实现请求在多个中间件之间的流转,确保按照顺序依次执行相应的处理逻辑。 + next(); + }, + // 第二个中间件函数,用于处理获取报表的业务逻辑。在前面的参数验证中间件通过后,就会进入这个中间件来执行具体的获取报表操作, + // 例如从数据库中查询符合指定类型ID的报表数据,然后将数据进行整理并返回给客户端等一系列业务相关的操作都在这里完成。 + function (req, res, next) { + // 调用reportsServ服务对象的reports方法,传入经过验证的报表类型ID(req.params.typeid),用于获取相应类型的报表数据。 + // reports方法应该是在ReportsService中定义的用于根据传入的报表类型ID去数据存储(如数据库)中查找并获取对应报表记录的方法, + // 它是一个异步操作,会涉及到数据库连接、查询语句执行等操作,所以需要通过回调函数来处理操作完成后的结果情况。 + reportsServ.reports(req.params.typeid, function (err, result) { + // 如果在执行获取报表数据的异步操作过程中出现错误(err不为null),例如数据库查询失败、权限不足无法访问报表数据等情况, + // 则返回包含错误信息的响应,状态码为400表示请求出现错误,同时将具体的错误信息(err)传递给客户端,让客户端知晓请求失败的原因。 + if (err) return res.sendResult(null, 400, err); + // 如果获取报表数据成功,则返回包含报表数据(result)的响应,状态码为200表示请求成功,并附带提示信息"获取报表成功", + // 告知客户端操作已顺利完成,客户端可以根据接收到的报表数据进行相应的展示或者后续处理操作。 + res.sendResult(result, 200, "获取报表成功"); + })(req, res, next); + } ); -module.exports = router; \ No newline at end of file +// 将配置好的路由器对象导出,以便在主应用中引入并挂载到对应的路径上,使得这个路由能够正确响应客户端发送的相应HTTP请求,实现获取报表的功能。 +// 在主应用中,通过引入这个路由器模块,并将其挂载到对应的路径(这里就是 "/type/:typeid" 对应的路径)上, +// 服务器就能正确识别并处理客户端发送的获取报表的请求,按照路由中定义的逻辑去获取和返回报表数据了。 +module.exports = router; +//这段代码主要实现了一个用于获取特定类型报表的路由功能,先是对传入的报表类型 ID 参数进行严格验 +//证,确保其合法性,然后基于验证通过的参数调用相应服务方法去获取报表数据,并根据操作结果向客户 +//端返回合适的响应信息,整体用于实现后端报表获取的接口服务功能。 diff --git a/routes/api/private/v1/rights.js b/routes/api/private/v1/rights.js index 1a41661..8fb0fcd 100644 --- a/routes/api/private/v1/rights.js +++ b/routes/api/private/v1/rights.js @@ -1,30 +1,71 @@ +// 引入Express框架的核心模块,Express是用于构建Node.js网络应用程序的常用框架,通过它可以方便地处理HTTP请求、定义路由等操作。 +// 它为Node.js开发Web应用提供了简洁高效的方式,是整个后端服务构建的基础框架,像搭建服务器、处理不同HTTP方法(GET、POST等)的请求都依赖于它来实现。 var express = require('express'); + +// 创建一个Express路由器实例,利用这个实例能够以模块化的方式定义一组相关的路由,便于在整个Web应用中进行有条理的路由管理。 +// 这种模块化的路由管理方式有助于将不同功能模块对应的路由进行分组,使代码结构更加清晰,易于维护和扩展,例如可以把用户相关路由、权限相关路由等分别放在不同的路由器实例中进行管理。 var router = express.Router(); + +// 引入Node.js的path模块,该模块主要用于处理文件路径相关的操作,像拼接、解析路径等,此处用于准确地定位其他模块所在的文件位置。 +// 在Node.js项目中,模块的引入路径需要准确指定,特别是对于自定义模块,path模块提供的功能可以帮助我们根据项目的目录结构灵活地组合路径字符串,确保模块能被正确加载。 var path = require("path"); -// 获取验证模块 -var authorization = require(path.join(process.cwd(),"/modules/authorization")); +// 获取自定义的验证模块,通过path.join函数把当前工作目录(process.cwd())与相对路径("/modules/authorization")进行拼接,以此精确地引入验证模块。 +// 这种拼接方式能适应不同运行环境下的目录结构差异,确保总能准确找到验证模块所在位置。这个验证模块通常负责对用户的身份、权限等方面进行验证, +// 保障后续操作的合法性与安全性,比如判断用户是否有权限访问某些特定资源等,它是整个应用安全机制中非常重要的一环,防止非法访问和越权操作等情况发生。 +var authorization = require(path.join(process.cwd(), "/modules/authorization")); -// 通过验证模块构建权限服务模块 +// 通过上述验证模块获取名为"RightService"的权限服务模块,这个服务模块应该封装了一系列与权限相关的业务操作方法,例如获取不同类型的权限列表等功能。 +// 通过将权限相关的业务逻辑封装在这个服务模块里,使得代码的职责更加清晰,在路由处理中只需调用该服务模块的对应方法即可实现具体的权限相关操作, +// 而不用在路由代码中混杂大量复杂的具体业务实现细节,例如数据库查询、权限规则判断等操作都可以在这个服务模块内部进行处理。 var rightService = authorization.getService("RightService"); + +// 定义一个处理GET请求的路由,路径格式为 "/:type",其中":type"是一个路由参数,用于指定权限列表的显示类型。 +// 当客户端向服务器发送GET请求到这个带有参数的路径时,服务器会根据传入的显示类型参数来获取相应格式的权限列表数据, +// 例如客户端可能请求以列表形式("list")或者树形结构形式("tree")来查看权限信息,服务器则要根据这个参数返回对应格式的数据。 router.get("/:type", - // 参数验证 - function(req,res,next) { - if(!req.params.type) { - return res.sendResult(null,400,"显示类型未定义"); - } - if(req.params.type != "list" && req.params.type != "tree") { - return res.sendResult(null,400,"显示类型参数错误"); - } - next(); - }, - // 业务逻辑 - function(req,res,next) { - rightService.getAllRights(req.params.type,function(err,rights){ - if(err) return res.sendResult(null,400,err); - res.sendResult(rights,200,"获取权限列表成功"); - })(req,res,next); - } + // 第一个中间件函数,主要用于对路由参数进行验证,确保传入的参数符合业务要求,保证后续业务逻辑能正确执行。 +// 在Web应用开发中,对路由参数的验证是很关键的前置步骤,能够避免因传入不符合要求的参数而导致后续业务逻辑出现错误,比如数据库查询异常或者逻辑混乱等问题。 + function (req, res, next) { + // 首先检查请求参数中的type是否存在,如果不存在(即为空),则返回错误响应给客户端。 + // 这里使用res.sendResult函数返回响应,状态码设置为400,表示请求出现了参数错误,同时附带具体的错误提示信息"显示类型未定义"。 + // 因为显示类型参数是确定要获取何种格式权限列表的关键依据,若缺失则无法准确执行后续操作,所以必须要求该参数存在。 + if (!req.params.type) { + return res.sendResult(null, 400, "显示类型未定义"); + } + // 接着进一步验证type参数的值是否符合预定义的合法值,即判断是否等于"list"或者"tree",如果不符合,则同样返回错误响应。 + // 状态码依旧为400,附带错误提示信息"显示类型参数错误",告知客户端传入的显示类型参数不符合要求。 + // 业务上规定了只接受这两种特定的显示类型值,其他值则视为非法输入,以此保证获取权限列表的操作能按照预期的格式进行。 + if (req.params.type!= "list" && req.params.type!= "tree") { + return res.sendResult(null, 400, "显示类型参数错误"); + } + // 如果参数验证通过,就调用next()函数,将控制权传递给下一个中间件或者路由处理函数,以便继续执行后续的业务逻辑。 + // 这是Express中间件机制中实现请求流程控制的关键操作,通过调用next()可以确保请求按照顺序依次经过各个中间件进行相应的处理,若不调用则请求会阻塞在当前中间件处。 + next(); + }, + // 第二个中间件函数,用于处理获取权限列表的实际业务逻辑,依赖于前面验证通过的参数进行相应操作。 +// 在参数验证中间件通过后,就会进入这个中间件来执行具体的获取权限列表操作,例如从数据库或者其他数据存储中查询符合指定显示类型的权限数据, +// 然后对获取到的数据进行整理、格式化等操作,最后将合适的数据返回给客户端展示。 + function (req, res, next) { + // 调用rightService权限服务模块的getAllRights方法,传入经过验证的显示类型参数(req.params.type),用于获取相应类型的权限列表数据。 + // getAllRights方法应该是在RightService中定义的用于根据传入的显示类型参数去数据存储(如数据库)中查找并获取对应格式权限记录的方法, + // 它是一个异步操作,会涉及到数据库连接、查询语句执行等操作,所以需要通过回调函数来处理操作完成后的结果情况。 + rightService.getAllRights(req.params.type, function (err, rights) { + // 如果在执行获取权限列表的异步操作过程中出现错误(err不为null),例如数据库查询失败、权限配置数据异常等情况, + // 就通过res.sendResult返回包含错误信息的响应,状态码设置为400,表示请求处理出现错误,同时将具体的错误信息(err)传递给客户端,让客户端知晓请求失败的原因。 + if (err) return res.sendResult(null, 400, err); + // 如果获取权限列表操作成功,就使用res.sendResult返回包含权限列表数据(rights)的响应,状态码为200,表示请求成功,同时附带提示信息"获取权限列表成功", + // 告知客户端操作顺利完成,客户端接收到权限列表数据后可以根据业务需求进行相应的展示或者后续处理操作。 + res.sendResult(rights, 200, "获取权限列表成功"); + })(req, res, next); + } ); -module.exports = router; \ No newline at end of file +// 将配置好的路由器对象导出,这样在主应用程序中就可以引入这个路由器模块,并将其挂载到对应的路径上,使得这个定义好的路由能够正确响应客户端发送的相应HTTP请求,实现获取权限列表的功能。 +// 在主应用中,通过引入这个路由器模块,并将其挂载到对应的 "/:type" 路径上,服务器就能正确识别客户端发送的获取权限列表请求, +// 按照路由中定义的逻辑去验证参数、获取数据并返回响应,从而实现整个获取权限列表的功能交互。 +module.exports = router; +//这段代码主要实现了一个根据指定显示类型获取权限列表的路由功能,先是对传入的显示类型路由参数进 +//行严格验证,确保其合法性及符合预定义的取值范围,然后基于验证通过的参数调用相应服务方法去获取 +//权限列表数据,并根据操作结果向客户端返回合适的响应信息,整体用于实现后端权限列表获取的接口服 +//务功能。 \ No newline at end of file diff --git a/routes/api/private/v1/roles.js b/routes/api/private/v1/roles.js index 2a700e6..b754ef5 100644 --- a/routes/api/private/v1/roles.js +++ b/routes/api/private/v1/roles.js @@ -1,142 +1,261 @@ +// 引入Express框架的核心模块,用于创建Web应用、定义路由以及处理HTTP请求等相关操作。 +// Express是Node.js中非常流行的Web应用框架,它提供了便捷的方式来搭建服务器、配置路由以及处理不同类型的HTTP请求,是整个后端服务开发的基础。 var express = require('express'); + +// 创建一个Express路由器实例,方便以模块化的方式来定义一组相关的路由,便于在整个应用中进行有条理的路由管理。 +// 通过使用路由器实例,可以将不同功能模块(比如这里的角色管理相关路由)的路由集中定义在一起,使得代码结构更加清晰,易于维护和扩展,避免路由逻辑过于混乱。 var router = express.Router(); + +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,比如拼接模块的路径,以便准确地引入其他模块。 +// 在Node.js项目中,模块的位置需要精确指定才能正确加载,path模块提供了诸如拼接、解析路径等功能,帮助我们根据项目目录结构找到所需模块。 var path = require("path"); -// 获取验证模块 -var authorization = require(path.join(process.cwd(),"/modules/authorization")); +// 获取自定义的验证模块,通过path.join方法将当前工作目录(process.cwd())与相对路径("/modules/authorization")拼接起来,准确地引入该验证模块。 +// 这种方式确保了无论项目在何种环境下运行,都能正确定位到验证模块所在位置。该验证模块通常在整个应用架构中起着关键作用,用于验证用户的权限等情况, +// 以此确保后续的各种操作都符合相应的安全和业务规则,防止未经授权的访问或不符合业务逻辑的操作发生。 +var authorization = require(path.join(process.cwd(), "/modules/authorization")); -// 角色管理模块 +// 通过验证模块获取名为"RoleService"的角色管理模块,该模块应该封装了与角色相关的各种业务操作方法,例如角色的增删改查以及权限管理等功能。 +// 将角色管理相关的业务逻辑封装在这个服务模块内,使得代码职责更加清晰,在路由处理中只需调用该模块提供的相应方法即可实现具体的角色管理操作, +// 而不用在路由代码中分散地编写复杂的数据库操作、权限判断等具体业务实现细节。 var roleServ = authorization.getService("RoleService"); -// 获取角色列表 +// 定义处理获取角色列表的GET请求的路由,路径为根路径 "/"。 +// 当客户端向服务器发送GET请求到根路径时,会进入这个路由对应的处理逻辑,以获取所有角色的相关信息,通常会从数据库等数据存储中查询并返回这些信息给客户端。 router.get("/", - // 参数验证 - function(req,res,next){ - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - roleServ.getAllRoles(function(err,result) { - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取成功"); - })(req,res,next); - } + // 参数验证中间件,当前这里直接调用next(),意味着暂时没有进行额外的参数验证逻辑,不过一般情况下可根据实际需求添加相关验证,比如分页参数等验证。 + // 在实际应用中,获取角色列表可能需要分页展示等功能,那时就需要对请求中的分页相关参数(如页码、每页数量等)进行合法性检查,确保后续查询操作能正确执行。 + function (req, res, next) { + next(); + }, + // 处理业务逻辑的中间件,用于获取所有角色的信息。在参数验证中间件(当前为空验证)通过后,会进入这个中间件来执行具体的获取角色列表操作。 + function (req, res, next) { + // 调用roleServ角色管理模块的getAllRoles方法,该方法用于获取所有角色的相关数据,是一个异步操作。 + // 它内部可能涉及到数据库连接、查询语句执行等操作,从数据库中获取所有角色记录,并整理成合适的数据格式返回。 + // 如果在获取角色列表的过程中出现错误(err不为null),比如数据库查询失败、权限不足无法获取等情况,则通过res.sendResult返回包含错误信息的响应,状态码为400,表示请求出现错误, + // 同时将具体的错误信息(err)传递给客户端,让客户端知晓请求失败的原因。 + // 如果获取成功,则返回包含角色列表数据(result)的响应,状态码为200,并附带提示信息"获取成功",告知客户端操作已顺利完成,客户端可以根据接收到的数据进行展示等后续处理。 + roleServ.getAllRoles(function (err, result) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(result, 200, "获取成功"); + })(req, res, next); + } ); -// 创建角色 +// 定义处理创建角色的POST请求的路由,路径为 "/"。 +// 当客户端向服务器的根路径发送POST请求时,会进入此路由对应的处理逻辑,用于创建新的角色,一般需要在请求体中携带创建角色所需的相关信息,如角色名称、描述等。 router.post("/", - // 参数验证 - function(req,res,next) { - if(!req.body.roleName) return res.sendResult(null,400,"角色名称不能为空"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - roleServ.createRole({ - "roleName":req.body.roleName, - "roleDesc":req.body.roleDesc - },function(err,role){ - if(err) return res.sendResult(null,400,err); - res.sendResult(role,201,"创建成功"); - })(req,res,next); - } + // 参数验证中间件,用于验证创建角色时请求体中必要参数是否存在。在创建角色的业务逻辑中,角色名称通常是必不可少的关键信息,所以要先对其进行验证。 + function (req, res, next) { + // 检查请求体中是否包含roleName字段,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"角色名称不能为空"。 + // 因为没有角色名称就无法创建一个有意义的角色,所以必须确保该字段存在于请求体中。 + if (!req.body.roleName) return res.sendResult(null, 400, "角色名称不能为空"); + // 参数验证通过后,将控制权传递给下一个中间件,继续后续的业务逻辑处理。通过调用next()函数,按照Express中间件的执行机制,让请求可以继续流转到下一个环节进行处理。 + next(); + }, + // 处理业务逻辑的中间件,用于创建新的角色。在前面的参数验证中间件通过后,会进入这个中间件来执行实际的创建角色操作,比如将角色数据插入到数据库等数据存储中。 + function (req, res, next) { + // 构建一个包含角色名称(roleName)和角色描述(roleDesc)的对象,从请求体中获取相应的值。 + // 这里假设创建角色时,除了必填的角色名称外,还可以选择性地传入角色描述信息,将这些信息整理成一个对象传递给创建角色的服务方法。 + roleServ.createRole({ + "roleName": req.body.roleName, + "roleDesc": req.body.roleDesc + }, function (err, role) { + // 如果创建角色的操作出现错误(err不为null),比如数据库插入失败、参数不符合数据库表结构要求等情况,则通过res.sendResult返回包含错误信息的响应,状态码为400,表示请求出现错误, + // 同时将具体的错误信息(err)传递给客户端,告知客户端创建角色失败的原因。 + if (err) return res.sendResult(null, 400, err); + // 如果创建成功,则返回包含新创建角色信息(role)的响应,状态码为201,表示资源创建成功,并附带提示信息"创建成功",告知客户端新角色已成功创建, + // 客户端可以根据接收到的角色信息进行后续操作,比如进一步为该角色配置权限等。 + res.sendResult(role, 201, "创建成功"); + })(req, res, next); + } ); -// 获取角色详情 +// 定义处理获取角色详情的GET请求的路由,路径中包含角色ID参数,格式为 "/:id"。 +// 当客户端向服务器发送GET请求并在路径中携带具体的角色ID时,会进入这个路由对应的处理逻辑,用于获取对应角色的详细信息,例如角色的具体权限、关联的用户等信息。 router.get("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) return res.sendResult(null,400,"角色ID不能为空"); - if(isNaN(parseInt(req.params.id))) res.sendResult(null,400,"角色ID必须为数字"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - roleServ.getRoleById( - req.params.id, - function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取成功"); - })(req,res,next); - } + // 参数验证中间件,用于验证角色ID参数的合法性。因为要获取特定角色的详细信息,必须先确保传入的角色ID是合法有效的,才能准确从数据库等数据源中查找对应的角色记录。 + function (req, res, next) { + // 检查请求参数中的角色ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"角色ID不能为空"。 + // 若没有角色ID,服务器就无法确定要获取哪个角色的详细信息,所以该参数不能为空。 + if (!req.params.id) return res.sendResult(null, 400, "角色ID不能为空"); + // 进一步验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码同样为400,并附带提示信息"角色ID必须为数字"。 + // 通常在数据库中角色ID是以数字形式存储和标识的,所以要求传入的参数能正确转换为数字,以保证后续查询操作的准确性。 + if (isNaN(parseInt(req.params.id))) res.sendResult(null, 400, "角色ID必须为数字"); + // 参数验证通过后,将控制权传递给下一个中间件,继续后续的业务逻辑处理。通过调用next(),使请求能继续进入下一个中间件执行获取角色详情的业务逻辑。 + next(); + }, + // 处理业务逻辑的中间件,用于获取指定角色的详细信息。在前面参数验证中间件通过后,会进入此中间件来执行具体的获取角色详情操作,比如从数据库中查询对应角色的详细记录信息。 + function (req, res, next) { + // 调用roleServ角色管理模块的getRoleById方法,传入经过验证的角色ID(req.params.id),用于获取该角色的详细信息,这是一个异步操作。 + // 该方法内部会根据传入的角色ID去数据库等数据存储中查找对应的角色记录,并提取相关详细信息进行返回,这个过程可能涉及数据库查询、数据关联查询等操作。 + // 如果在获取角色详情的过程中出现错误(err不为null),例如数据库查询失败、角色ID对应的记录不存在等情况,则通过res.sendResult返回包含错误信息的响应,状态码为400,表示请求出现错误, + // 同时将具体的错误信息(err)传递给客户端,让客户端知晓获取角色详情失败的原因。 + // 如果获取成功,则返回包含角色详细信息(result)的响应,状态码为200,并附带提示信息"获取成功",告知客户端操作已顺利完成,客户端可以根据接收到的详细信息进行展示或其他相关操作。 + roleServ.getRoleById( + req.params.id, + function (err, result) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(result, 200, "获取成功"); + } + )(req, res, next); + } ); -// 更新角色信息 +// 定义处理更新角色信息的PUT请求的路由,路径中包含角色ID参数,格式为 "/:id"。 +// 当客户端向服务器发送PUT请求并在路径中携带角色ID时,会进入这个路由对应的处理逻辑,用于更新指定角色的相关信息,比如修改角色名称、描述等内容。 router.put("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) return res.sendResult(null,400,"角色ID不能为空"); - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"角色ID必须为数字"); - if(!req.body.roleName) return res.sendResult(null,400,"角色名称不能为空"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - roleServ.updateRole( - { - "id":req.params.id, - "roleName":req.body.roleName, - "roleDesc":req.body.roleDesc - }, - function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取成功"); - })(req,res,next); - } + // 参数验证中间件,用于验证更新角色信息时必要参数的合法性。在更新角色信息时,既要确保角色ID是合法有效的,又要保证请求体中包含必要的更新内容(如角色名称等)。 + function (req, res, next) { + // 检查请求参数中的角色ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"角色ID不能为空"。 + // 因为没有角色ID就无法确定要更新哪个角色的信息,所以该参数必须存在。 + if (!req.params.id) return res.sendResult(null, 400, "角色ID不能为空"); + // 验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码为400,并附带提示信息"角色ID必须为数字"。 + // 与获取角色详情类似,数据库中角色ID通常以数字形式存储,所以要保证传入的ID能正确转换为数字,便于准确更新对应角色的记录。 + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "角色ID必须为数字"); + // 检查请求体中是否包含roleName字段,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"角色名称不能为空"。 + // 角色名称是角色的重要标识信息,更新角色时一般需要修改这个字段,所以必须确保请求体中包含该字段,否则无法进行有效的更新操作。 + if (!req.body.roleName) return res.sendResult(null, 400, "角色名称不能为空"); + // 参数验证通过后,将控制权传递给下一个中间件,继续后续的业务逻辑处理。通过调用next()函数,使请求能继续流转到下一个中间件执行更新角色信息的业务逻辑。 + next(); + }, + // 处理业务逻辑的中间件,用于更新指定角色的信息。在前面参数验证中间件通过后,会进入这个中间件来执行实际的更新角色信息操作,比如修改数据库中对应角色记录的相关字段值。 + function (req, res, next) { + // 构建一个包含角色ID(id)、角色名称(roleName)和角色描述(roleDesc)的对象,用于传递给更新角色信息的方法。 + // 这里将角色ID以及从请求体中获取的角色名称和可能存在的角色描述信息整理成一个对象,以便准确地将更新内容传递给服务模块的更新方法进行处理。 + roleServ.updateRole( + { + "id": req.params.id, + "roleName": req.body.roleName, + "roleDesc": req.body.roleDesc + }, + function (err, result) { + // 如果更新角色信息的操作出现错误(err不为null),比如数据库更新失败、传入的更新参数不符合要求等情况,则通过res.sendResult返回包含错误信息的响应,状态码为400,表示请求出现错误, + // 同时将具体的错误信息(err)传递给客户端,告知客户端更新角色信息失败的原因。 + if (err) return res.sendResult(null, 400, err); + // 如果更新成功,则返回包含更新后角色信息(result)的响应,状态码为200,并附带提示信息"获取成功"(此处提示信息可能更改为"更新成功"更合适,可根据实际情况调整)。 + // 告知客户端角色信息已成功更新,客户端可以根据更新后的信息进行相应的后续操作,比如查看更新后的角色详情等。 + res.sendResult(result, 200, "获取成功"); + } + )(req, res, next); + } ); -// 删除角色 +// 定义处理删除角色的DELETE请求的路由,路径中包含角色ID参数,格式为 "/:id"。 +// 当客户端向服务器发送DELETE请求并在路径中携带角色ID时,会进入这个路由对应的处理逻辑,用于删除指定的角色,从数据库等数据存储中移除对应的角色记录。 router.delete("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) return res.sendResult(null,400,"角色ID不能为空"); - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"角色ID必须为数字"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - roleServ.deleteRole( - req.params.id, - function(err,success){ - if(err) return res.sendResult(null,400,err); - res.sendResult(null,200,"删除成功"); - })(req,res,next); - } + // 参数验证中间件,用于验证角色ID参数的合法性。在执行删除角色操作前,必须确保传入的角色ID是合法有效的,避免误删或因无效ID导致操作失败。 + function (req, res, next) { + // 检查请求参数中的角色ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"角色ID不能为空"。 + // 若没有角色ID,服务器无法确定要删除哪个角色,所以该参数不能为空。 + if (!req.params.id) return res.sendResult(null, 400, "角色ID不能为空"); + // 验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码为400,并附带提示信息"角色ID必须为数字"。 + // 同样,由于数据库中角色ID通常以数字形式存储,需要保证传入的ID能正确转换为数字,以准确执行删除操作。 + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "角色ID必须为数字"); + // 参数验证通过后,将控制权传递给下一个中间件,继续后续的业务逻辑处理。通过调用next()函数,使请求能继续进入下一个中间件执行删除角色的业务逻辑。 + next(); + }, + // 处理业务逻辑的中间件,用于执行删除指定角色的操作。在前面参数验证中间件通过后,会进入这个中间件来执行实际的删除角色操作,比如从数据库中删除对应的角色记录。 + function (req, res, next) { + // 调用roleServ角色管理模块的deleteRole方法,传入经过验证的角色ID(req.params.id),用于删除该角色,这是一个异步操作。 + // 该方法内部会与数据库进行交互,执行删除对应的角色记录的操作,这个过程可能涉及到数据库事务处理等相关操作,以确保数据的一致性和完整性。 + // 如果在删除角色的过程中出现错误(err不为null),比如数据库删除失败、存在关联数据导致无法删除等情况,则通过res.sendResult返回包含错误信息的响应,状态码为400,表示请求出现错误, + // 同时将具体的错误信息(err)传递给客户端,让客户端知晓删除角色失败的原因。 + // 如果删除成功,则返回一个空数据的响应(因为角色已被删除),状态码为200,并附带提示信息"删除成功",告知客户端指定角色已成功删除。 + roleServ.deleteRole( + req.params.id, + function (err, success) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(null, 200, "删除成功"); + } + )(req, res, next); + } ); -// 为角色授权 +// 定义处理为角色授权的POST请求的路由,路径中包含角色ID参数,格式为 "/:id/rights"。 +// 意味着当客户端向服务器发送POST请求到这个特定路径(其中包含具体的角色ID)时,会进入此路由对应的处理逻辑, +// 目的是为指定的角色更新其权限信息,比如给某个角色添加新的权限或者修改已有的权限配置等操作。 router.post("/:id/rights", - // 参数校验 - function(req,res,next) { - if(!req.params.id) return res.sendResult(null,400,"角色ID不能为空"); - if(isNaN(parseInt(req.params.id))) res.sendResult(null,400,"角色ID必须为数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - roleServ.updateRoleRight(req.params.id,req.body.rids,function(err,newRole){ - if(err) return res.sendResult(null,400,err); - res.sendResult(null,200,"更新成功"); - })(req,res,next); - } + // 参数校验中间件,用于验证角色ID参数的合法性。 + // 在进行角色授权操作前,必须确保传入的角色ID是准确且符合要求的,否则无法确定要为哪个角色进行权限更新, + // 这是保障后续业务逻辑能正确执行的前置必要步骤。 + function (req, res, next) { + // 检查请求参数中的角色ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误, + // 同时附带提示信息"角色ID不能为空"。因为角色ID是定位具体角色的关键标识,缺少它就无法明确操作对象,所以该参数必须存在。 + if (!req.params.id) return res.sendResult(null, 400, "角色ID不能为空"); + // 验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码为400, + // 并附带提示信息"角色ID必须为数字"。通常在系统设计中,角色ID在数据库等存储中是以数字形式存储和管理的, + // 所以传入的角色ID参数需要能正确转换为数字才能准确匹配到对应的角色记录进行权限更新操作。 + if (isNaN(parseInt(req.params.id))) res.sendResult(null, 400, "角色ID必须为数字"); + // 参数验证通过后,将控制权传递给下一个中间件,继续后续的业务逻辑处理。 + // 通过调用next()函数,遵循Express中间件的执行流程,让请求可以顺利流转到下一个中间件去执行具体的为角色授权的业务逻辑。 + next(); + }, + // 业务逻辑中间件,用于为指定角色更新权限。 + // 在前面参数验证中间件通过后,进入此中间件执行实际的更新角色权限的操作,例如在数据库中修改角色与权限关联表的相关记录等操作。 + function (req, res, next) { + // 调用roleServ角色管理模块的updateRoleRight方法,传入角色ID(req.params.id)和权限ID列表(req.body.rids), + // 用于更新角色的权限信息,这是一个异步操作。updateRoleRight方法应该是在RoleService中定义的, + // 其内部实现了根据传入的角色ID以及要赋予或修改的权限ID列表,去更新对应角色的权限配置的具体逻辑, + // 这个过程可能涉及到数据库的插入、更新等操作,以确保角色的权限数据能准确变更。 + // 如果在更新角色权限的过程中出现错误(err不为null),比如数据库连接失败、权限数据更新出现冲突等情况, + // 则通过res.sendResult返回包含错误信息的响应,状态码为400,表示请求出现错误,同时将具体的错误信息(err)传递给客户端, + // 让客户端知晓更新权限操作失败的原因。 + // 如果更新成功,则返回一个空数据的响应(重点在于权限更新成功的提示),状态码为200,并附带提示信息"更新成功", + // 告知客户端指定角色的权限已按照要求成功更新,客户端可以基于这个提示信息知晓操作结果,进行后续的相关操作, + // 比如重新获取角色详情查看更新后的权限情况等。 + roleServ.updateRoleRight(req.params.id, req.body.rids, function (err, newRole) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(null, 200, "更新成功"); + })(req, res, next); + } ); -// 删除角色权限 +// 定义处理删除角色权限的DELETE请求的路由,路径中包含角色ID和权限ID两个参数,格式为 "/:id/rights/:rightId"。 +// 当客户端向服务器发送DELETE请求到这个特定路径(包含具体的角色ID和权限ID)时,会进入此路由对应的处理逻辑, +// 用于执行删除指定角色的指定权限的操作,例如撤销某个角色已有的某项特定权限等情况。 router.delete("/:id/rights/:rightId", - // 参数验证 - function(req,res,next) { - if(!req.params.id) return res.sendResult(null,400,"角色ID不能为空"); - if(isNaN(parseInt(req.params.id))) res.sendResult(null,400,"角色ID必须为数字"); - if(isNaN(parseInt(req.params.rightId))) res.sendResult(null,400,"权限ID必须为数字"); - next(); - }, - // 业务逻辑 - function(req,res,next) { - roleServ.deleteRoleRight(req.params.id,req.params.rightId,function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"取消权限成功"); - })(req,res,next); - } + // 参数验证中间件,用于验证角色ID和权限ID参数的合法性。 + // 在执行删除角色权限操作前,必须确保传入的角色ID和权限ID都是合法有效的,否则可能导致误删或者因无法准确定位要删除的权限记录而操作失败, + // 所以要对这两个关键参数进行严格的合法性验证。 + function (req, res, next) { + // 检查请求参数中的角色ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误, + // 同时附带提示信息"角色ID不能为空"。与前面类似,角色ID是确定操作对象(具体角色)的关键,缺少它无法明确要对哪个角色的权限进行删除操作,所以必须存在。 + if (!req.params.id) return res.sendResult(null, 400, "角色ID不能为空"); + // 验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码为400, + // 并附带提示信息"角色ID必须为数字"。这是基于数据库中角色ID存储形式的要求,确保能准确匹配到对应的角色记录来进行权限删除操作。 + if (isNaN(parseInt(req.params.id))) res.sendResult(null, 400, "角色ID必须为数字"); + // 检查请求参数中的权限ID(req.params.rightId)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误, + // 同时附带提示信息"权限ID必须为数字"。权限ID同样是定位要删除的具体权限的关键标识,若不存在则无法准确知道要删除哪个权限,所以该参数也必须存在。 + if (isNaN(parseInt(req.params.rightId))) res.sendResult(null, 400, "权限ID必须为数字"); + // 参数验证通过后,将控制权传递给下一个中间件,继续后续的业务逻辑处理。 + // 通过调用next()函数,按照Express中间件机制,让请求继续流转到下一个中间件去执行实际的删除角色权限的业务逻辑。 + next(); + }, + // 业务逻辑中间件,用于执行删除指定角色的指定权限的操作。 + // 在前面参数验证中间件通过后,进入此中间件执行具体的从数据库等存储中删除对应角色权限记录的操作, + // 例如在角色与权限关联表中删除相应的关联记录,以实现取消指定权限的功能。 + function (req, res, next) { + // 调用roleServ角色管理模块的deleteRoleRight方法,传入角色ID(req.params.id)和权限ID(req.params.rightId), + // 用于删除该角色的指定权限,这是一个异步操作。deleteRoleRight方法内部实现了根据传入的角色ID和权限ID, + // 在数据存储中准确找到并删除相应权限关联记录的具体逻辑,这个过程需要考虑数据的一致性、完整性以及可能存在的关联约束等情况。 + // 如果在删除角色权限的过程中出现错误(err不为null),比如数据库删除操作失败、存在外键约束导致无法删除等情况, + // 则通过res.sendResult返回包含错误信息的响应,状态码为400,表示请求出现错误,同时将具体的错误信息(err)传递给客户端, + // 让客户端知晓删除权限操作失败的原因。 + // 如果删除成功,则返回一个空数据的响应(重点在于提示取消权限成功),状态码为200,并附带提示信息"取消权限成功", + // 告知客户端指定角色的指定权限已成功取消,客户端可以据此进行后续操作,比如再次查看角色权限列表确认权限已被删除等情况。 + roleServ.deleteRoleRight(req.params.id, req.params.rightId, function (err, result) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(result, 200, "取消权限成功"); + })(req, res, next); + } ); -module.exports = router; \ No newline at end of file +// 将配置好的路由器对象导出,以便在主应用中引入并挂载到对应的路径上,使这些路由能够正确响应客户端发送的相应HTTP请求, +// 实现角色管理相关的各种功能。在主应用中,通过引入这个路由器模块,并将其挂载到对应的路径上, +// 服务器就能根据客户端发送的不同请求(如获取角色列表、创建角色、更新角色权限等),按照这里定义的路由逻辑进行相应的处理, +// 从而完整地实现角色管理的各项功能,为整个应用提供角色相关的服务支持。 +module.exports = router; +//这段代码整体围绕角色管理中的权限相关操作(为角色授权和删除角色权限)定义了相应的路由及处理逻 +//辑,通过严格的参数验证和具体的业务逻辑处理,确保操作的合法性和数据的准确性,最终实现角色管理 +//在权限方面的功能需求。 \ No newline at end of file diff --git a/routes/api/private/v1/upload.js b/routes/api/private/v1/upload.js index e742c85..b78f2b9 100644 --- a/routes/api/private/v1/upload.js +++ b/routes/api/private/v1/upload.js @@ -1,27 +1,67 @@ +// 引入Express框架的核心模块,用于创建Web应用、定义路由以及处理HTTP请求等相关操作。 +// Express是Node.js中常用的Web应用开发框架,提供了便捷的方式来搭建服务器、配置不同路径对应的路由以及处理各类HTTP请求与响应,是整个后端服务构建的基础框架。 var express = require('express'); + +// 创建一个Express路由器实例,方便以模块化的方式来定义一组相关的路由,便于在整个应用中进行有条理的路由管理。 +// 使用路由器实例可以将不同功能模块(比如用户模块、文件管理模块等)相关的路由进行分组管理,使代码结构更加清晰,易于维护和扩展,避免所有路由逻辑混杂在一起。 var router = express.Router(); + +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,例如拼接、解析文件路径等。 +// 在Node.js项目中,经常需要根据项目的目录结构准确地处理文件路径,path模块提供了诸如path.join(拼接路径)、path.resolve(解析绝对路径)等方法来辅助完成这些操作,确保文件操作能准确找到对应的文件位置。 var path = require("path"); +// 引入Node.js的文件系统模块(fs),用于对文件进行读写、重命名等操作。 +// fs模块提供了丰富的文件操作接口,比如读取文件内容(fs.readFile)、写入文件内容(fs.writeFile)、判断文件是否存在(fs.existsSync)以及这里用到的重命名文件(fs.rename)等功能,方便在Node.js应用中进行各种文件层面的处理。 var fs = require('fs'); + +// 引入Node.js的操作系统模块(os),通常可以获取操作系统相关的信息等,不过在这段代码里暂未体现其具体使用。 +// os模块可以获取诸如系统内存信息、CPU核心数、操作系统平台(如Windows、Linux等)相关的信息,虽然在此处当前没看到具体使用场景,但在一些需要根据不同操作系统特性进行适配的功能中会发挥作用。 var os = require('os'); +// 引入multer模块,它是一个用于处理文件上传的中间件,能够方便地解析上传的文件数据。 +// 在Web应用中,当需要接收客户端上传的文件时,multer可以帮助解析HTTP请求中的文件部分,将其转换为Node.js中便于处理的格式,并且可以进行一些配置,比如指定文件存储位置等。 var multer = require('multer'); -// 临时上传目录 + +// 创建一个multer实例,配置文件上传的临时存储目录为'tmp_uploads/',意味着上传的文件会先临时存放在这个目录下。 +// 通过这种配置,multer会将客户端上传的文件先暂存到指定的临时目录中,后续再根据业务需求对这些临时文件进行进一步处理,比如重命名、移动到正式存储位置等操作。 var upload = multer({ dest: 'tmp_uploads/' }); +// 引入自定义的配置文件(config),并获取其中名为"upload_config"的配置项,这里的配置项可能包含了与文件上传相关的一些配置信息,比如文件访问的基础URL等。 +// 自定义的配置文件通常用于集中管理应用中的各种配置参数,方便根据不同环境(开发环境、生产环境等)进行灵活调整,在这里获取的'upload_config'可能包含了如文件在服务器上对外可访问的基础URL等重要信息,用于后续构建文件的完整访问路径。 var upload_config = require('config').get("upload_config"); -// 提供文件上传服务 -router.post("/",upload.single('file'),function(req,res,next) { - var fileExtArray = req.file.originalname.split("."); - var ext = fileExtArray[fileExtArray.length - 1]; - var targetPath = req.file.path + "." + ext; - fs.rename(path.join(process.cwd(),"/" + req.file.path),path.join(process.cwd(),targetPath),function(err){ - if(err) { - return res.sendResult(null,400,"上传文件失败"); - } - res.sendResult({"tmp_path":targetPath,"url":upload_config.get("baseURL") + "/" + targetPath},200,"上传成功"); - }) +// 定义一个处理POST请求的路由,路径为根路径 "/",该路由用于处理文件上传的业务逻辑。 +// 当客户端向服务器的根路径发送POST请求时,服务器会依据此路由定义来处理请求,在这里就是专门处理文件上传相关的操作,比如接收客户端传来的文件数据,并进行后续的保存、重命名等处理。 +// 这里使用了upload.single('file')中间件,意味着它期望接收一个名为'file'的文件上传字段。这表示客户端在上传文件时,HTTP请求中的表单数据里,文件对应的字段名应该是'file',这样multer才能正确解析并获取到上传的文件信息。 +router.post("/", upload.single('file'), function (req, res, next) { + // 从上传的文件信息(req.file)中获取原始文件名,通过split方法以'.'为分隔符将文件名拆分成数组。 + // 例如,对于原始文件名'test.txt',经过split操作后会得到一个数组['test', 'txt'],方便后续提取文件扩展名等操作。 + var fileExtArray = req.file.originalname.split("."); + // 获取文件扩展名,即数组中的最后一个元素,例如对于文件名'test.txt',获取到的ext就是'txt'。 + // 通过获取文件扩展名,后续可以将其添加到临时存储的文件上,确保文件重命名后具有正确的完整文件名,符合实际的文件格式要求。 + var ext = fileExtArray[fileExtArray.length - 1]; + // 构建目标文件路径,先获取上传文件的临时存储路径(req.file.path),然后拼接上文件扩展名,形成最终带有正确扩展名的完整路径。 + // 因为最初文件上传到临时目录时可能没有完整的扩展名(由multer的默认存储机制决定),所以这里需要重新拼接扩展名来得到最终正确的文件路径,以便后续能准确访问和使用该文件。 + var targetPath = req.file.path + "." + ext; + + // 使用fs模块的rename方法对文件进行重命名操作,将临时存储的文件重命名为带有正确扩展名的目标文件路径。 + // path.join方法用于拼接当前工作目录(process.cwd())、文件路径相关部分,确保路径的准确性。通过这种方式准确指定文件的源路径和目标路径,避免因路径错误导致文件重命名失败。 + fs.rename(path.join(process.cwd(), "/" + req.file.path), path.join(process.cwd(), targetPath), function (err) { + // 如果重命名文件的操作出现错误(err不为null),则返回包含错误信息的响应给客户端,状态码为400,表示请求出现错误,同时附带提示信息"上传文件失败"。 + // 比如可能出现文件权限不足无法重命名、目标路径已存在同名文件等情况导致重命名操作失败,此时需要告知客户端上传文件的操作没有成功,并传递具体的错误原因(通过err对象)。 + if (err) { + return res.sendResult(null, 400, "上传文件失败"); + } + // 如果文件重命名成功,即文件上传操作顺利完成,则返回包含相关信息的成功响应给客户端。 + // 返回的数据包含了文件的临时路径(tmp_path)以及完整的访问URL(通过配置项中的基础URL和目标文件路径拼接而成),状态码为200,表示请求成功,同时附带提示信息"上传成功"。 + // 客户端可以根据返回的URL来访问已上传的文件,而tmp_path可以用于服务器端后续可能的其他文件管理操作或者记录等用途。 + res.sendResult({"tmp_path": targetPath, "url": upload_config.get("baseURL") + "/" + targetPath}, 200, "上传成功"); + }); }); -module.exports = router; \ No newline at end of file +// 将配置好的路由器对象导出,以便在主应用中可以引入并挂载到对应的路径上,使得这个文件上传的路由能够正确响应客户端发送的相应HTTP请求。 +// 在主应用中,通过引入这个路由器模块,并将其挂载到对应的根路径("/")上,服务器就能正确识别客户端发送的文件上传请求,按照路由中定义的逻辑进行文件接收、重命名以及返回相应结果等操作,从而实现文件上传功能。 +module.exports = router; +//这段代码实现了一个简单的文件上传功能的后端路由处理逻辑,借助 multer 中间件接收文件上传,然后 +//通过 fs 模块对临时存储的文件进行重命名等操作,最后根据操作结果向客户端返回相应的响应信息,告知 +//上传是否成功以及提供文件的相关访问信息。 \ No newline at end of file diff --git a/routes/api/private/v1/users.js b/routes/api/private/v1/users.js index fbb49fd..a3b25d2 100644 --- a/routes/api/private/v1/users.js +++ b/routes/api/private/v1/users.js @@ -1,161 +1,300 @@ +// 引入Express框架的核心模块,用于创建Web应用、定义路由以及处理HTTP请求等相关操作。 +// Express是基于Node.js平台构建Web应用的常用框架,它提供了便捷的方式来搭建服务器、配置路由以及处理诸如GET、POST、PUT、DELETE等不同类型的HTTP请求, +// 让开发者能够高效地构建后端服务,实现前后端的数据交互和业务逻辑处理。 var express = require('express'); + +// 创建一个Express路由器实例,方便以模块化的方式来定义一组相关的路由,便于在整个应用中进行有条理的路由管理。 +// 使用路由器实例可以将不同功能模块对应的路由进行分类组织,比如可以把用户管理相关路由放在一个路由器实例中,订单管理相关路由放在另一个实例里, +// 这样使得整个应用的路由结构更加清晰,易于维护和扩展,避免所有路由定义都混杂在一起导致代码可读性差和维护困难。 var router = express.Router(); + +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,例如拼接、解析文件路径等,以便准确地引入其他模块。 +// 在Node.js项目中,模块的位置是通过文件路径来指定的,path模块提供了实用的方法来处理这些路径。例如,path.join可以将多个路径片段正确拼接成一个完整路径, +// 确保无论项目在何种环境下运行,都能准确地找到并加载所需的模块,这对于组织项目结构和模块间的引用非常重要。 var path = require("path"); -// 获取验证模块 -var authorization = require(path.join(process.cwd(),"/modules/authorization")); +// 获取自定义的验证模块,通过path.join方法将当前工作目录(process.cwd())与相对路径("/modules/authorization")拼接起来,准确地引入该验证模块。 +// 这种动态拼接路径的方式能够适应不同部署环境下项目目录结构的变化,保证能精准地定位到验证模块所在位置。 +// 该验证模块在整个应用的安全架构中起着关键作用,通常用于验证用户的权限等情况,比如判断用户是否有访问某个资源或者执行某个操作的权限, +// 以此确保后续所有的操作都严格遵循相应的安全和业务规则,防止出现越权访问、非法操作等情况,保障系统的数据安全和业务流程的正常运行。 +var authorization = require(path.join(process.cwd(), "/modules/authorization")); -// 通过验证模块获取用户管理服务 +// 通过验证模块获取名为"ManagerService"的用户管理服务对象,该模块应该封装了与用户相关的各种业务操作方法,例如用户的增删改查、角色分配以及状态更新等功能。 +// 将用户管理相关的复杂业务逻辑封装在这个服务对象内部,使得代码的职责划分更加清晰。在路由处理函数中,只需调用该服务对象提供的对应方法即可完成具体的业务操作, +// 而不用把大量的业务逻辑代码(如数据库查询、更新操作,权限判断逻辑等)都写在路由函数里,这样既方便代码的复用,也让路由代码更加简洁、易读,专注于处理请求和响应相关的逻辑。 var mgrServ = authorization.getService("ManagerService"); - -// 查询用户列表 +// 定义处理查询用户列表的GET请求的路由,路径为根路径 "/"。 +// 这意味着当客户端向服务器发送GET请求到根路径时,服务器会依据此路由定义来处理该请求,主要目的是从数据库或其他数据源中获取满足一定条件的用户列表信息, +// 并将获取到的信息返回给客户端,例如在前端页面展示用户列表等应用场景会触发这样的请求。 router.get("/", - // 验证参数 - function(req,res,next) { - // 参数验证 - if(!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null,400,"pagenum 参数错误"); - if(!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null,400,"pagesize 参数错误"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - mgrServ.getAllManagers( - { - "query":req.query.query, - "pagenum":req.query.pagenum, - "pagesize":req.query.pagesize - }, - function(err,result){ - if(err) return res.sendResult(null,400,err); - res.sendResult(result,200,"获取管理员列表成功"); - } - )(req,res,next); - - } + // 第一个中间件函数,用于对请求中的查询参数进行验证,确保传入的参数符合业务要求,保证后续业务逻辑能正确执行。 + // 在查询用户列表的业务场景中,通常会有一些必要的查询参数,如分页相关的参数(用于控制显示哪些页的数据),这里需要对这些参数进行合法性检查, + // 避免因参数错误导致后续的数据库查询等业务操作出现异常,例如查询出不符合预期的数据或者直接导致查询失败等情况。 + function (req, res, next) { + // 验证pagenum参数是否存在且大于0,如果该参数不存在或者小于等于0,则返回错误响应,状态码400表示请求参数有误,并附带相应的错误提示信息"pagenum 参数错误"。 + // pagenum参数一般用于表示当前请求要获取的是第几页的用户列表数据,在分页查询逻辑中,它必须是一个大于0的有效数值,若不存在或不符合要求, + // 服务器就无法准确确定要返回哪一页的数据,所以需要进行验证并及时返回错误提示给客户端,让客户端修正参数后重新发起请求。 + if (!req.query.pagenum || req.query.pagenum <= 0) return res.sendResult(null, 400, "pagenum 参数错误"); + // 验证pagesize参数是否存在且大于0,如果该参数不存在或者小于等于0,则返回错误响应,状态码400表示请求参数有误,并附带相应的错误提示信息"pagesize 参数错误"。 + // pagesize参数通常用来指定每页显示多少条用户记录,同样需要保证其合法性,若该参数缺失或小于等于0,就无法按照正确的分页逻辑查询和返回数据, + // 所以也要进行验证并在参数不符合要求时返回相应的错误信息给客户端。 + if (!req.query.pagesize || req.query.pagesize <= 0) return res.sendResult(null, 400, "pagesize 参数错误"); + // 参数验证通过后,调用next()函数将控制权传递给下一个中间件或者路由处理函数,以便继续执行后续的业务逻辑。 + // 在Express框架的中间件机制中,next()函数起着关键的流程控制作用,它使得请求能够按照定义的顺序依次经过各个中间件进行相应的处理, + // 如果不调用next(),请求将会阻塞在当前中间件,无法继续往后执行其他中间件或路由处理函数中的逻辑。 + next(); + }, + // 第二个中间件函数,用于处理查询用户列表的实际业务逻辑,依赖于前面验证通过的参数进行相应操作。 + // 在前面的参数验证中间件通过后,请求会流转到这个中间件来执行具体的查询用户列表操作,比如根据传入的分页参数(pagenum和pagesize)以及可能的模糊查询内容(query), + // 去数据库中构建合适的查询语句,执行查询操作以获取符合条件的用户记录,然后对查询到的数据进行整理、格式化等处理,最终将合适的用户列表数据返回给客户端。 + function (req, res, next) { + // 调用mgrServ用户管理服务对象的getAllManagers方法,传入一个包含查询条件的对象,其中包括模糊查询的内容(query)以及分页相关的参数(pagenum和pagesize),用于获取符合条件的用户列表数据。 + // getAllManagers方法是在ManagerService模块中定义的一个用于查询用户列表的方法,它接收包含查询条件的对象作为参数,在内部会与数据库等数据存储进行交互, + // 通过执行相应的查询语句(可能涉及到SQL语句的构建、数据库连接等操作)来获取满足条件的用户记录集合,由于这是一个涉及到数据库操作等可能耗时的异步操作, + // 所以需要通过回调函数来处理操作完成后的结果情况,根据操作成功与否返回相应的响应给客户端。 + mgrServ.getAllManagers( + { + "query": req.query.query, + "pagenum": req.query.pagenum, + "pagesize": req.query.pagesize + }, + function (err, result) { + // 如果在获取用户列表的操作过程中出现错误(err不为null),比如数据库查询出现语法错误、连接失败或者权限不足无法访问相关数据等情况, + // 就通过res.sendResult返回包含错误信息的响应,状态码设置为400,表示请求处理出现错误,同时将具体的错误信息(err)传递给客户端, + // 让客户端知晓请求失败的原因,以便客户端根据错误提示进行相应的处理,例如检查网络连接、修正请求参数等。 + if (err) return res.sendResult(null, 400, err); + // 如果获取用户列表操作成功,就使用res.sendResult返回包含用户列表数据(result)的响应,状态码为200,表示请求成功,同时附带提示信息"获取管理员列表成功", + // 告知客户端操作顺利完成,客户端接收到返回的用户列表数据后,可以根据业务需求进行相应的处理,比如在前端页面上展示用户列表、进行进一步的筛选操作等。 + res.sendResult(result, 200, "获取管理员列表成功"); + } + )(req, res, next); + + } ); -// 获取用户信息 +// 定义处理获取用户信息的GET请求的路由,路径中包含用户ID参数,格式为 "/:id"。 +// 当客户端向服务器发送GET请求并且在请求路径中携带了具体的用户ID时,服务器会依据此路由定义来处理该请求,目的是获取对应ID的用户的详细信息, +// 例如用户的基本资料(用户名、手机号、邮箱等)、关联的角色信息、账户状态等详细内容,并将这些信息返回给客户端,常用于用户详情页面的展示等场景。 router.get("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"用户ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"用户ID必须是数字"); - next(); - }, - function(req,res,next) { - mgrServ.getManager(req.params.id,function(err,manager){ - if(err) return res.sendResult(null,400,err); - res.sendResult(manager,200,"获取成功"); - })(req,res,next); - } + // 第一个中间件函数,主要用于对路由参数进行验证,确保传入的用户ID参数符合业务要求,保证后续业务逻辑能正确执行。 + // 因为要准确获取特定用户的详细信息,首先必须确保传入的用户ID是合法有效的,否则无法在数据库或其他数据源中准确找到对应的用户记录进行后续操作, + // 所以需要对用户ID参数进行严格的验证,防止因参数错误导致查询失败或者获取到错误的用户信息等情况发生。 + function (req, res, next) { + // 检查请求参数中的用户ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"用户ID不能为空"。 + // 用户ID是用于唯一标识每个用户的关键信息,在获取用户详细信息的请求中,如果没有提供该参数,服务器就不知道要获取哪个用户的详细信息, + // 所以必须要求该参数存在,若不存在则直接返回错误提示给客户端,要求其补充正确的参数后重新发起请求。 + if (!req.params.id) { + return res.sendResult(null, 400, "用户ID不能为空"); + } + // 进一步验证用户ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码同样为400,并附带提示信息"用户ID必须是数字"。 + // 在通常的系统设计中,用户ID在数据库等存储介质中是以数字形式进行存储和标识的,所以传入的用户ID参数需要能够正确转换为数字类型, + // 这样才能准确地与数据库中的记录进行匹配,进行后续的查询操作,若参数不符合要求则返回相应的错误信息给客户端。 + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "用户ID必须是数字"); + // 如果参数验证通过,就调用next()函数,将控制权传递给下一个中间件或者路由处理函数,以便继续执行后续的业务逻辑。 + // 通过调用next(),遵循Express中间件的执行流程,使得请求能够继续流转到下一个中间件或路由处理函数中,去执行获取用户详细信息的具体业务逻辑。 + next(); + }, + // 第二个中间件函数,用于处理获取指定用户信息的实际业务逻辑,依赖于前面验证通过的用户ID参数进行相应操作。 + // 在前面的参数验证中间件通过后,请求会进入这个中间件来执行具体的获取指定用户详细信息的操作,比如根据传入的用户ID去数据库中查询对应的用户记录, + // 提取出该用户的各项详细信息(如用户名、密码、角色、状态等),然后将这些详细信息整理成合适的数据格式返回给客户端,以便客户端进行展示或其他相关操作。 + function (req, res, next) { + // 调用mgrServ用户管理服务对象的getManager方法,传入经过验证的用户ID(req.params.id),用于获取该用户的详细信息。 + // getManager方法是在ManagerService模块中定义的用于根据给定的用户ID从数据库等数据存储中查找并获取对应用户详细信息的方法, + // 它会与数据库进行交互,执行相应的查询语句(可能涉及到关联查询等操作,取决于用户信息与其他数据表的关联关系)来获取用户的详细记录, + // 由于这是一个异步操作,同样需要通过回调函数来处理操作完成后的结果情况,根据查询结果成功与否返回相应的响应给客户端。 + mgrServ.getManager(req.params.id, function (err, manager) { + // 如果在获取用户信息的操作过程中出现错误(err不为null),例如数据库查询失败(可能是用户ID对应的记录不存在、数据库连接问题等原因), + // 就通过res.sendResult返回包含错误信息的响应,状态码设置为400,表示请求处理出现错误,同时将具体的错误信息(err)传递给客户端, + // 让客户端知晓获取用户信息失败的原因,以便采取相应的措施,比如检查用户ID是否正确等。 + if (err) return res.sendResult(null, 400, err); + // 如果获取用户信息操作成功,就使用res.sendResult返回包含用户详细信息(manager)的响应,状态码为200,表示请求成功,同时附带提示信息"获取成功", + // 告知客户端操作顺利完成,客户端接收到返回的用户详细信息后,可以根据业务需求进行展示、编辑(如果有相应权限)等后续操作。 + res.sendResult(manager, 200, "获取成功"); + })(req, res, next); + } ); - -// 创建用户 +//这段代码主要围绕用户管理模块中查询用户列表和获取单个用户信息这两个功能,通过定义相应的路由及 +//配套的中间件函数,实现了对请求参数的严格验证以及基于验证通过的参数进行具体的业务逻辑处理(从 +//数据源获取用户相关数据并返回给客户端),保障了用户管理相关功能在请求处理方面的准确性和合法性。 +// 定义处理创建用户的POST请求的路由,路径为 "/"。 +// 意味着当客户端向服务器发送POST请求到根路径时,服务器会依据此路由配置来处理该请求,该请求主要用于创建新的用户。 +// 在实际应用中,客户端需要在请求体中提供创建用户所需的各项信息,比如用户名、密码等关键信息,服务器会对这些信息进行验证和处理,以完成用户创建操作。 router.post("/", - // 验证参数 - function(req,res,next) { - if(!req.body.username){ - return res.sendResult(null,400,"用户名不能为空"); - } - if(!req.body.password) { - return res.sendResult(null,400,"密码不能为空"); - } - if(!req.body.rid) { - req.body.rid = -1; - //return res.sendResult(null,200,"角色ID不能为空"); - } - if(isNaN(parseInt(req.body.rid))) req.body.rid = -1;//return res.sendResult(null,200,"角色ID必须是数字"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - params = { - "username":req.body.username, - "password":req.body.password, - "mobile":req.body.mobile, - "email":req.body.email, - "rid":req.body.rid - } - mgrServ.createManager(params,function(err,manager){ - if(err) return res.sendResult(null,400,err); - res.sendResult(manager,201,"创建成功"); - })(req,res,next); - } + // 第一个中间件函数,用于对创建用户时请求体中的必要参数进行验证,确保传入的参数符合业务要求,保证后续业务逻辑能正确执行。 + // 此中间件的目的是防止因客户端传入的参数不完整或不符合要求,导致后续创建用户操作出现错误,例如数据库插入失败等情况。 + function (req, res, next) { + // 检查请求体中是否包含username字段,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"用户名不能为空"。 + // 用户名是用于唯一标识用户的重要信息,在后续的登录、权限验证等诸多业务操作中都会用到,所以是创建用户时必须提供的关键参数。 + if (!req.body.username) { + return res.sendResult(null, 400, "用户名不能为空"); + } + // 检查请求体中是否包含password字段,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"密码不能为空"。 + // 密码用于保障用户账号的安全性,是用户登录验证身份的依据,同样是创建用户必不可少的参数。 + if (!req.body.password) { + return res.sendResult(null, 400, "密码不能为空"); + } + // 检查请求体中是否包含rid字段,如果不存在则将其默认设置为 -1,此处原代码有部分注释掉的返回错误逻辑,可能根据实际情况调整过,目前是默认赋值。 + // rid字段通常代表用户的角色ID,用于确定用户在系统中所拥有的权限角色,在某些业务场景下,可能允许创建用户时暂不明确指定具体角色,所以这里默认赋值为 -1, + // 不过后续可能还需要根据业务规则进一步处理该默认值情况,比如提示用户及时补充角色信息或者按照默认角色赋予相应权限等操作。 + if (!req.body.rid) { + req.body.rid = -1; + // return res.sendResult(null, 200, "角色ID不能为空"); + } + // 进一步验证rid字段是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则将其默认设置为 -1,此处原代码也有部分注释掉的返回错误逻辑,目前是默认赋值处理。 + // 一般来说,在系统设计中角色ID在数据库等存储结构里是以数字形式存在的,所以要确保传入的该参数能转换为数字类型, + // 若无法转换,则按照当前设定将其默认设置为 -1,但这种处理方式需根据实际业务需求判断是否合理,也可选择直接返回错误提示给客户端要求其修正参数。 + if (isNaN(parseInt(req.body.rid))) req.body.rid = -1; // return res.sendResult(null, 200, "角色ID必须是数字"); + // 参数验证通过后,调用next()函数将控制权传递给下一个中间件或者路由处理函数,以便继续执行后续的业务逻辑。 + // 在Express框架的中间件机制里,next()函数起着传递请求控制权的关键作用,使得请求能按照定义的顺序依次经过各个中间件进行处理,若不调用,请求将阻塞在此中间件处。 + next(); + }, + // 第二个中间件函数,用于处理创建用户的实际业务逻辑,依赖于前面验证通过的请求体参数进行相应操作。 + // 当第一个中间件完成参数验证且验证通过后,请求会流转到此中间件,在此执行具体的创建用户操作,例如将用户信息插入到数据库中相应的用户表等存储位置。 + function (req, res, next) { + // 构建一个包含创建用户所需信息的对象,从请求体中获取相应的值,如用户名(username)、密码(password)、手机号(mobile)、邮箱(email)以及角色ID(rid)等信息。 + // 这里将从请求体中提取的各个字段整理成一个对象,方便统一传递给创建用户的服务方法,其中手机号和邮箱字段可能是可选的用户信息,根据业务规则,客户端可能可不填,但用户名和密码等关键信息已通过前面验证确保其存在。 + params = { + "username": req.body.username, + "password": req.body.password, + "mobile": req.body.mobile, + "email": req.body.email, + "rid": req.body.rid + } + // 调用mgrServ用户管理服务对象的createManager方法,传入构建好的用户信息对象,用于创建新的用户。 + // createManager方法是在ManagerService模块(从前面代码可知通过authorization模块获取)中定义的用于执行创建用户具体业务逻辑的方法, + // 它通常会涉及到与数据库的交互,比如构建插入语句将用户信息插入到用户表中,这是一个异步操作,所以需要通过回调函数来处理操作完成后的结果情况。 + mgrServ.createManager(params, function (err, manager) { + // 如果在操作过程中出现错误(err不为null),比如数据库插入失败(可能是由于数据库连接问题、字段长度限制、唯一性约束冲突等原因), + // 就通过res.sendResult返回包含错误信息的响应,状态码设置为400,表示请求处理出现错误,同时将具体的错误信息(err)传递给客户端, + // 让客户端知晓创建用户失败的原因,以便其根据错误提示进行相应的处理,例如检查输入的参数是否符合要求、查看网络连接等情况。 + if (err) return res.sendResult(null, 400, err); + // 如果创建用户操作成功,就使用res.sendResult返回包含新创建用户信息(manager)的响应,状态码为201,表示资源创建成功, + // 同时附带提示信息"创建成功",告知客户端新用户已成功创建,客户端可以根据接收到的用户信息进行后续操作,比如使用新账号进行登录、查看用户详情等。 + res.sendResult(manager, 201, "创建成功"); + })(req, res, next); + } ); - -// 修改用户信息 +// 定义处理修改用户信息的PUT请求的路由,路径中包含用户ID参数,格式为 "/:id"。 +// 当客户端向服务器发送PUT请求到包含具体用户ID的路径时,服务器会依据此路由配置来处理该请求,用于更新指定用户的相关信息。 +// 客户端需要在请求体中提供要修改的具体信息,比如手机号、邮箱等,同时通过路径中的用户ID指定要修改信息的目标用户。 router.put("/:id", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"用户ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"用户ID必须是数字"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - mgrServ.updateManager( - { - "id":req.params.id, - "mobile":req.body.mobile, - "email":req.body.email - }, - function(err,manager) { - if(err) return res.sendResult(null,400,err); - res.sendResult(manager,200,"更新成功"); - } - )(req,res,next); - } + // 第一个中间件函数,用于对修改用户信息时请求中的必要参数进行验证,确保传入的用户ID参数符合业务要求,保证后续业务逻辑能正确执行。 + // 准确获取要修改信息的目标用户是修改操作的前提,所以要先对用户ID进行严格验证,防止因参数错误导致修改了错误用户的信息或者操作失败等情况。 + function (req, res, next) { + // 检查请求参数中的用户ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"用户ID不能为空"。 + // 用户ID是唯一标识每个用户的关键信息,在修改用户信息的请求中,若不提供该参数,服务器无法确定要更新哪个用户的信息,所以此参数必须存在。 + if (!req.params.id) { + return res.sendResult(null, 400, "用户ID不能为空"); + } + // 进一步验证用户ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码同样为400,并附带提示信息"用户ID必须是数字"。 + // 在通常的系统设计中,用户ID在数据库等存储介质里是以数字形式进行存储和标识的,所以传入的用户ID参数需要能正确转换为数字类型, + // 这样才能准确地与数据库中的记录进行匹配,进行后续的更新操作,若不符合要求则返回相应错误信息给客户端。 + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "用户ID必须是数字"); + // 参数验证通过后,调用next()函数将控制权传递给下一个中间件或者路由处理函数,以便继续执行后续的业务逻辑。 + // 通过调用next(),遵循Express中间件的执行流程,让请求能够继续流转到下一个中间件或路由处理函数中,去执行修改用户信息的具体业务逻辑。 + next(); + }, + // 第二个中间件函数,用于处理修改用户信息的实际业务逻辑,依赖于前面验证通过的用户ID参数以及请求体中的其他信息进行相应操作。 + // 在参数验证通过后,此中间件负责执行具体的修改用户信息操作,比如根据传入的用户ID找到数据库中对应的用户记录,并用请求体中提供的新信息更新相应字段。 + function (req, res, next) { + // 构建一个包含要修改的用户信息的对象,包括用户ID(id)以及要更新的手机号(mobile)和邮箱(email)等信息,从请求体和请求参数中获取相应的值。 + // 这里构建的对象明确了要更新的具体用户(通过用户ID)以及对应的要修改的信息字段,方便后续传递给更新用户信息的服务方法进行针对性的数据库更新操作, + // 其中用户ID已通过前面验证确保合法性,而手机号和邮箱字段则依据客户端请求体中的内容来确定是否有更新值。 + mgrServ.updateManager( + { + "id": req.params.id, + "mobile": req.body.mobile, + "email": req.body.email + }, + function (err, manager) { + // 这个方法执行的是异步操作,在操作过程中如果出现错误(err不为null),比如数据库更新失败(可能是由于数据库连接问题、数据约束冲突等原因), + // 就通过res.sendResult返回包含错误信息的响应,状态码设置为400,表示请求处理出现错误,同时将具体的错误信息(err)传递给客户端, + // 让客户端知晓修改用户信息失败的原因,以便其采取相应的措施,例如检查输入的更新信息是否符合要求、查看网络连接等情况。 + if (err) return res.sendResult(null, 400, err); + // 如果修改用户信息操作成功,就使用res.sendResult返回包含修改后用户信息(manager)的响应,状态码为200,表示请求成功, + // 同时附带提示信息"更新成功",告知客户端操作顺利完成,客户端可以根据接收到的修改后的用户信息进行相应的后续操作,比如查看更新后的用户详情等。 + res.sendResult(manager, 200, "更新成功"); + } + )(req, res, next); + } ); -// 删除用户信息 +// 定义处理删除用户信息的DELETE请求的路由,路径中包含用户ID参数,格式为 "/:id"。 +// 当客户端向服务器发送DELETE请求并在路径中携带用户ID时,服务器会依据此路由配置来处理该请求,用于删除指定用户的所有相关信息。 +// 不过在执行删除操作前,需要对用户ID进行严格验证,同时要遵循一些业务规则,比如某些特殊用户(如管理员账号)可能不允许删除。 router.delete("/:id", - // 验证参数 - function(req,res,next){ - if(!req.params.id) return res.sendResult(null,400,"用户ID不能为空"); - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"ID必须是数字"); - if(req.params.id == 500) return res.sendResult(null,400,"不允许删除admin账户"); - next(); - }, - // 处理业务逻辑 - function(req,res,next){ - mgrServ.deleteManager(req.params.id,function(err){ - if(err) return res.sendResult(null,400,err); - return res.sendResult(null,200,"删除成功"); - })(req,res,next); - } + // 第一个中间件函数,用于对删除用户信息时请求中的必要参数进行验证,确保传入的用户ID参数符合业务要求,保证后续业务逻辑能正确执行。 + // 为了保证数据的安全性和业务逻辑的正确性,在执行删除操作前,必须确保传入的用户ID是合法有效的,并且要符合业务规定的删除条件。 + function (req, res, next) { + // 检查请求参数中的用户ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"用户ID不能为空"。 + // 用户ID是确定要删除哪个用户信息的关键标识,若请求中未提供该参数,服务器无法明确操作对象,所以必须要求其存在,否则返回错误提示让客户端补充参数后重新请求。 + if (!req.params.id) return res.sendResult(null, 400, "用户ID不能为空"); + // 进一步验证用户ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回错误响应,状态码同样为400,并附带提示信息"ID必须是数字"。 + // 由于用户ID在系统存储中一般是以数字形式存在的,所以传入的参数需要能正确转换为数字,这样才能准确地找到对应的用户记录进行删除操作,若不符合要求则返回错误信息。 + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "ID必须是数字"); + // 特别地,对于用户ID为500的情况(可能是特殊的admin账户),不允许删除,直接返回错误响应,状态码为400,并附带提示信息"不允许删除admin账户"。 + // 在实际业务中,某些特殊的用户账号(如系统管理员账号)起着至关重要的作用,为了保障系统的正常运行和管理,通常不允许随意删除,所以在此针对这种情况进行限制,直接告知客户端不允许此操作。 + if (req.params.id == 500) return res.sendResult(null, 400, "不允许删除admin账户"); + // 参数验证通过后,调用next()函数将控制权传递给下一个中间件或者路由处理函数,以便继续执行后续的业务逻辑。 + // 通过调用next(),按照Express中间件机制,让请求能够继续流转到下一个中间件去执行实际的删除用户信息的业务逻辑。 + next(); + }, + // 第二个中间件函数,用于处理删除用户信息的实际业务逻辑,依赖于前面验证通过的用户ID参数进行相应操作。 + // 在参数验证中间件通过后,此中间件负责执行具体的删除用户信息操作,比如从数据库中删除对应的用户记录以及与之相关的其他关联数据(如果有),完成删除流程并返回结果给客户端。 + function (req, res, next) { + // 调用mgrServ用户管理服务对象的deleteManager方法,传入经过验证的用户ID(req.params.id),用于删除该用户的信息。 + // deleteManager方法是在ManagerService模块中定义的用于根据传入的用户ID从数据库等数据存储中删除对应用户记录及相关关联数据(如果需要)的方法, + // 它会执行数据库删除操作等相关逻辑,由于涉及到与数据库的交互,这是一个异步操作,所以需要通过回调函数来处理操作完成后的结果情况,根据操作是否成功返回相应响应。 + mgrServ.deleteManager(req.params.id, function (err) { + // 如果在操作过程中出现错误(err不为null),比如数据库删除失败(可能是由于数据库连接问题、外键约束等原因), + // 就通过res.sendResult返回包含错误信息的响应,状态码设置为400,表示请求处理出现错误,同时将具体的错误信息(err)传递给客户端, + // 让客户端知晓删除用户信息失败的原因,以便其根据错误提示进行相应的处理,例如检查用户ID是否正确、查看数据库相关配置等情况。 + if (err) return res.sendResult(null, 400, err); + // 如果删除用户信息操作成功,就使用res.sendResult返回一个空数据的响应(因为用户信息已被删除),状态码为200,表示请求成功, + // 同时附带提示信息"删除成功",告知客户端操作顺利完成,客户端可以根据此响应知晓对应的用户信息已成功从服务器端删除。 + return res.sendResult(null, 200, "删除成功"); + })(req, res, next); + } ); -// 分配用户角色 +// 定义处理分配用户角色的PUT请求的路由,路径中包含用户ID和角色相关的路径参数,格式为 "/:id/role"。 +// 当客户端向服务器发送PUT请求到包含用户ID和角色相关路径参数的这个路径时,服务器会依据此路由配置来处理该请求,用于为指定用户分配相应的角色。 +// 客户端需要在请求体中提供角色ID(rid),并通过路径中的用户ID指定要分配角色的目标用户,同时要遵循一定的业务规则,比如某些特殊用户角色可能不允许修改。 router.put("/:id/role", - // 参数验证 - function(req,res,next) { - if(!req.params.id) { - return res.sendResult(null,400,"用户ID不能为空"); - } - if(isNaN(parseInt(req.params.id))) return res.sendResult(null,400,"用户ID必须是数字"); - - if(req.params.id == 500) return res.sendResult(null,400,"不允许修改admin账户"); - - if(!req.body.rid) res.sendResult(null,400,"权限ID不能为空"); - next(); - }, - // 处理业务逻辑 - function(req,res,next) { - mgrServ.setRole(req.params.id,req.body.rid,function(err,manager){ - if(err) return res.sendResult(null,400,err); - res.sendResult(manager,200,"设置角色成功"); - })(req,res,next); - } + // 第一个中间件函数,用于对分配用户角色时请求中的必要参数进行验证,确保传入的参数符合业务要求,保证后续业务逻辑能正确执行。 + // 在进行角色分配操作前,需要确保传入的用户ID和角色ID等关键参数是合法有效的,并且符合业务规则,以防止错误的角色分配情况发生。 + function (req, res, next) { + // 检查请求参数中的用户ID(req.params.id)是否存在,如果不存在则返回错误响应,状态码400表示请求参数有误,同时附带提示信息"用户ID不能为空"。 + // 用户ID是确定要为哪个用户分配角色的关键标识,若请求中未提供该参数,服务器无法明确操作对象,所以此参数必须存在,否则返回错误提示让客户端补充参数后重新请求。 + if (!req.params.id) { + return res.sendResult(null, 400, "用户ID不能为空"); + } + // 进一步验证用户ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则返回 + if (isNaN(parseInt(req.params.id))) return res.sendResult(null, 400, "用户ID必须是数字"); + // 对于用户ID为500的情况(可能是特殊的admin账户),不允许修改角色,直接返回错误响应,状态码为400,并附带提示信息"不允许修改admin账户" + if (req.params.id == 500) return res.sendResult(null, 400, "不允许修改admin账户"); + // 检查请求体中是否包含rid字段,如果不存在则返回错误响应,状态码400,表示请求参数有误,同时附带提示信息"权限ID不能为空" + if (!req.body.rid) res.sendResult(null, 400, "权限ID不能为空"); + // 参数验证通过后,调用next()函数将控制权传递给下一个中间件或者路由处理函数,以便继续执行后续的业务逻辑 + next(); + }, + // 第二个中间件函数,用于处理分配用户角色的实际业务逻辑,依赖于前面验证通过的用户ID和请求体中的角色ID参数进行相应操作 + function (req, res, next) { + // 调用mgrServ用户管理服务对象的setRole方法,传入经过验证的用户ID(req.params.id)和角色ID(req.body.rid),用于为用户设置相应的角色。 + // 这个方法执行的是异步操作,在操作过程中如果出现错误(err不为null),就通过res.sendResult返回包含错误信息的响应,状态码设置为400,表示请求处理出现错误。 + // 如果分配用户角色操作成功,就使用res.sendResult返回包含更新后用户信息(manager)的响应,状态码为200,表示请求成功,同时附带提示信息"设置角色成功",告知客户端操作顺利完成 + mgrServ.setRole(req.params.id, req.body.rid, function (err, manager) { + if (err) return res.sendResult(null, 400, err); + res.sendResult(manager, 200, "设置角色成功"); + })(req, res, next); + } ); - +// 定义处理更新用户状态的PUT请求的路由,路径中包含用户ID和状态相关的路径参数,格式为 "/:id/state/:state" router.put("/:id/state/:state", + // 第一个中间件函数,用于对更新用户状态时请求中的必要参数进行验证,确保传入的参数符合业务要求,保证后续业务逻辑能正确 // 参数验证 function(req,res,next) { if(!req.params.id) { @@ -182,4 +321,8 @@ router.put("/:id/state/:state", } ) -module.exports = router; \ No newline at end of file +module.exports = router; +//这段代码整体围绕用户管理中的角色分配和用户状态更新这两个功能模块,通过定义路由、进行参数验证 +//以及执行具体的业务逻辑操作,实现了对用户相关信息的有效管理和更新,同时保障了操作的合法性和数 +//据的准确性,遵循了良好的后端服务开发的流程和规范,在构建 Web 应用的用户管理系统等场景中具有 +//重要作用。 \ No newline at end of file diff --git a/services/GoodService.js b/services/GoodService.js index 7a0b96a..13216cd 100644 --- a/services/GoodService.js +++ b/services/GoodService.js @@ -1,421 +1,485 @@ +// 引入lodash库,用于处理各种数据结构,提供了如对象操作、数组处理、排序等很多实用的工具函数,在后续代码中会频繁使用到。 var _ = require('lodash'); +// 引入Node.js的path模块,主要用于处理文件路径相关操作,通过 `path.join` 方法拼接当前工作目录(`process.cwd()`)和相对路径,来准确引入自定义的 `dao/DAO` 等模块所在的路径。 var path = require("path"); -var dao = require(path.join(process.cwd(),"dao/DAO")); -var goodAttributeDao = require(path.join(process.cwd(),"dao/GoodAttributeDAO")); +// 引入自定义的 `DAO` 模块,该模块应该封装了通用的数据访问操作方法,例如查询、获取单条记录、创建、更新等操作,对应不同的数据模型(如 `GoodModel`、`GoodPicModel` 等)与数据库进行交互,执行相关的数据操作。 +var dao = require(path.join(process.cwd(), "dao/DAO")); +// 引入自定义的 `GoodAttributeDAO` 模块,推测这个模块主要用于商品属性相关数据的访问操作,比如获取商品属性列表、清理商品属性等功能,同样是与数据库进行交互来操作商品属性数据。 +var goodAttributeDao = require(path.join(process.cwd(), "dao/GoodAttributeDAO")); +// 引入 `orm` 库,可能用于构建数据库查询条件等操作,例如使用其提供的 `like` 方法来构建模糊查询条件,方便在数据库查询中进行模糊匹配筛选数据。 var orm = require("orm"); +// 引入 `bluebird` 库,用于处理 `Promise` 相关的异步操作,使得异步代码的编写更加简洁和易于管理,通过返回 `Promise` 对象来实现异步流程的链式调用和错误处理等功能。 var Promise = require("bluebird"); +// 引入Node.js的文件系统模块 `fs`,用于进行文件读取、写入、删除等操作,在处理商品图片的文件存储以及删除等相关功能时会用到。 var fs = require("fs"); +// 引入 `gm` 库,用于对图片进行处理操作(虽然代码中当前部分相关逻辑被注释掉了,但推测可能是用于图片裁剪、缩放、格式转换等功能,比如原本计划通过 `gm` 来调整图片尺寸等操作)。 var gm = require("gm"); +// 引入 `uniqid` 库,用于生成唯一的标识符,可能在处理商品相关数据时,为某些需要唯一标识的字段(比如图片标识等情况)生成唯一的ID值,但从当前代码看暂未明确体现其使用场景。 var uniqid = require('uniqid'); +// 引入配置文件读取模块,并获取名为 `upload_config` 的配置信息,这个配置信息大概率与文件上传相关设置有关,比如文件上传的路径、基础URL等配置,用于后续处理商品图片的存储路径等操作时使用。 var upload_config = require('config').get("upload_config"); + /** * 裁剪图片 + * 此函数用于对图片进行裁剪操作(虽然当前实现的逻辑是简单的文件流复制,原计划通过 `gm` 库实现更复杂的裁剪等功能),接收原始图片路径、存储路径、新的宽度和新的高度作为参数, + * 返回一个 `Promise` 对象,用于异步处理裁剪操作完成后的结果,若操作成功则 `Promise` 被成功 `resolve`,若出现错误则 `Promise` 被 `reject`。 * - * @param {[type]} srcPath 原始图片路径 - * @param {[type]} savePath 存储路径 - * @param {[type]} newWidth 新的宽度 - * @param {[type]} newHeight 新的高度 - * @return {[type]} [description] + * @param {[type]} srcPath 原始图片路径,指定要进行裁剪操作的原始图片在文件系统中的位置,是读取图片数据的来源路径。 + * @param {[type]} savePath 存储路径,指定裁剪后的图片要保存到文件系统中的位置,是写入裁剪后图片数据的目标路径。 + * @param {[type]} newWidth 新的宽度,用于指定裁剪后图片期望的宽度尺寸,单位可能根据具体使用场景和图片处理库的要求而定(通常是像素单位)。 + * @param {[type]} newHeight 新的高度,用于指定裁剪后图片期望的高度尺寸,单位通常也是像素单位,与 `newWidth` 一起确定裁剪后图片的尺寸大小。 + * @return {[type]} 返回一个 `Promise` 对象,用于异步处理图片裁剪操作的结果情况,外部可以通过 `.then` 和 `.catch` 方法来处理操作成功或失败后的逻辑。 */ -function clipImage(srcPath,savePath,newWidth,newHeight) { - return new Promise(function(resolve,reject) { - // console.log("src => %s",srcPath); - // console.log("save => %s",savePath); - /* - gm(srcPath) - .resize(newWidth,newHeight) - .autoOrient() - .write(savePath,function(err){ - resolve(); - }) - - */ - - // 创建读取流 - readable = fs.createReadStream(srcPath); - // 创建写入流 - writable = fs.createWriteStream(savePath); - readable.pipe(writable); - readable.on('end',function() { - resolve(); - }); - - }); +function clipImage(srcPath, savePath, newWidth, newHeight) { + return new Promise(function (resolve, reject) { + // 以下这两行代码原本可能是用于调试输出原始图片路径和存储路径信息的,方便在开发过程中查看图片处理时涉及的路径是否正确,目前被注释掉了,但保留注释可以作为调试时的参考。 + // console.log("src => %s",srcPath); + // console.log("save => %s",savePath); + + /* + gm(srcPath) + .resize(newWidth,newHeight) + .autoOrient() + .write(savePath,function(err){ + resolve(); + }) + + */ + + // 创建读取流,通过 `fs` 模块的 `createReadStream` 方法从指定的原始图片路径(`srcPath`)创建一个可读流,用于读取图片文件的数据,以便后续进行处理或复制等操作。 + readable = fs.createReadStream(srcPath); + // 创建写入流,使用 `fs` 模块的 `createWriteStream` 方法根据指定的存储路径(`savePath`)创建一个可写流,用于将处理后的图片数据(当前只是简单复制)写入到目标文件中,实现图片的“裁剪”(实际是复制)操作。 + writable = fs.createWriteStream(savePath); + // 将可读流通过管道连接到可写流,这样可读流读取到的图片文件数据会自动写入到可写流对应的文件中,实现文件数据的复制功能,模拟简单的图片裁剪(实际未真正裁剪图片,只是复制文件)操作。 + readable.pipe(writable); + // 为可读流添加 `'end'` 事件监听器,当可读流读取完所有数据(即图片文件数据读取完成)时,会触发该事件,此时通过调用 `resolve` 函数来表示图片“裁剪”(复制)操作成功完成, + // 外部通过 `Promise` 的 `.then` 方法可以继续后续成功后的逻辑处理。 + readable.on('end', function () { + resolve(); + }); + + }); } /** * 通过参数生成产品基本信息 + * 此函数用于根据传入的参数生成商品的基本信息对象,对各个参数进行合法性检查和格式转换等操作,返回一个 `Promise` 对象, + * 若参数合法且信息生成成功则 `Promise` 被成功 `resolve` 并返回生成的商品基本信息对象,若参数不合法则 `Promise` 被 `reject` 并返回相应的错误信息。 * - * @param {[type]} params.cb [description] - * @return {[type]} [description] + * @param {[type]} params 参数对象,包含了创建或更新商品时需要的各种信息,例如商品名称(`goods_name`)、价格(`goods_price`)、数量(`goods_number`)、所属分类(`goods_cat`)等字段信息,具体要求由业务逻辑决定。 + * @return {[type]} 返回一个 `Promise` 对象,用于异步处理商品基本信息生成的结果情况,外部可以通过 `.then` 和 `.catch` 方法来处理操作成功或失败后的逻辑,成功时返回包含商品基本信息的对象,失败时返回错误信息字符串。 */ function generateGoodInfo(params) { - return new Promise(function(resolve,reject){ - var info = {}; - if(params.goods_id) info["goods_id"] = params.goods_id; - if(!params.goods_name) return reject("商品名称不能为空"); - info["goods_name"] = params.goods_name; - - if(!params.goods_price) return reject("商品价格不能为空"); - var price = parseFloat(params.goods_price); - if(isNaN(price) || price < 0) return reject("商品价格不正确") - info["goods_price"] = price; - - - if(!params.goods_number) return reject("商品数量不能为空"); - var num = parseInt(params.goods_number); - if(isNaN(num) || num < 0) return reject("商品数量不正确"); - info["goods_number"] = num; - - if(!params.goods_cat) return reject("商品没有设置所属分类"); - var cats = params.goods_cat.split(','); - if(cats.length > 0) { - info["cat_one_id"] = cats[0]; - } - if(cats.length > 1) { - info["cat_two_id"] = cats[1]; - } - if(cats.length > 2) { - info["cat_three_id"] = cats[2]; - info["cat_id"] = cats[2]; - } - - - if(params.goods_weight) { - weight = parseFloat(params.goods_weight); - if(isNaN(weight) || weight < 0) return reject("商品重量格式不正确"); - info["goods_weight"] = weight; - } else { - info["goods_weight"] = 0; - } - if(params.goods_introduce) { - info["goods_introduce"] = params.goods_introduce; - } - - if(params.goods_big_logo) { - info["goods_big_logo"] = params.goods_big_logo; - } else { - info["goods_big_logo"] = ""; - } - - if(params.goods_small_logo) { - info["goods_small_logo"] = params.goods_small_logo; - } else { - info["goods_small_logo"] = ""; - } - - if(params.goods_state) { - info["goods_state"] = params.goods_state; - } - - - // 图片 - if(params.pics) { - info["pics"] = params.pics; - } - - // 属性 - if(params.attrs) { - info["attrs"] = params.attrs; - } - - - info["add_time"] = Date.parse(new Date()) / 1000; - info["upd_time"] = Date.parse(new Date()) / 1000; - info["is_del"] = '0'; - - if(params.hot_mumber) { - hot_num = parseInt(params.hot_mumber); - if(isNaN(hot_num) || hot_num < 0) return reject("热销品数量格式不正确"); - info["hot_mumber"] = hot_num; - } else { - info["hot_mumber"] = 0; - } - - info["is_promote"] = info["is_promote"] ? info["is_promote"] : false; - - info["goods_introduce"] = params.goods_introduce - - resolve(info); - }); + return new Promise(function (resolve, reject) { + var info = {}; + // 如果传入的参数中包含 `goods_id`,则将其添加到 `info` 对象中,`goods_id` 通常用于标识商品的唯一ID,在更新商品等操作时可能会用到已有的商品ID来确定具体操作的商品对象。 + if (params.goods_id) info["goods_id"] = params.goods_id; + + // 检查商品名称是否存在,如果不存在则通过 `reject` 函数返回错误信息,表示商品名称不能为空,因为商品名称是商品的重要标识信息,必不可少,告知调用者参数不符合要求。 + if (!params.goods_name) return reject("商品名称不能为空"); + info["goods_name"] = params.goods_name; + + // 检查商品价格是否存在,如果不存在则通过 `reject` 函数返回错误信息,表示商品价格不能为空,因为价格是商品的关键属性之一,必须提供,告知调用者参数不符合要求。 + if (!params.goods_price) return reject("商品价格不能为空"); + var price = parseFloat(params.goods_price); + // 进一步检查商品价格是否能正确转换为数字类型(通过 `isNaN` 判断)以及价格是否小于 `0`,如果不满足要求则通过 `reject` 函数返回错误信息,表示商品价格不正确,告知调用者参数不符合要求。 + if (isNaN(price) || price < 0) return reject("商品价格不正确") + info["goods_price"] = price; + + // 检查商品数量是否存在,如果不存在则通过 `reject` 函数返回错误信息,表示商品数量不能为空,因为商品数量是商品的基本属性之一,必须明确,告知调用者参数不符合要求。 + if (!params.goods_number) return reject("商品数量不能为空"); + var num = parseInt(params.goods_number); + // 检查商品数量是否能正确转换为数字类型以及数量是否小于 `0`,若不符合要求则通过 `reject` 函数返回错误信息,表示商品数量不正确,告知调用者参数不符合要求。 + if (isNaN(num) || num < 0) return reject("商品数量不正确"); + info["goods_number"] = num; + + // 检查商品所属分类是否设置,如果没有设置则通过 `reject` 函数返回错误信息,表示商品没有设置所属分类,因为分类信息对于商品的归类和管理很重要,必须提供,告知调用者参数不符合要求。 + if (!params.goods_cat) return reject("商品没有设置所属分类"); + var cats = params.goods_cat.split(','); + // 如果分类信息分割后数组长度大于 `0`,说明有一级分类信息,将其赋值给 `info` 对象的 `cat_one_id` 属性,用于存储商品的一级分类标识,方便后续业务逻辑根据分类进行查询、展示等操作。 + if (cats.length > 0) { + info["cat_one_id"] = cats[0]; + } + // 如果分类信息数组长度大于 `1`,说明有二级分类信息,将其赋值给 `info` 对象的 `cat_two_id` 属性,用于存储商品的二级分类标识,进一步细化商品的分类情况。 + if (cats.length > 1) { + info["cat_two_id"] = cats[1]; + } + // 如果分类信息数组长度大于 `2`,说明有三级分类信息,将其赋值给 `info` 对象的 `cat_three_id` 属性,并同时将三级分类标识赋值给 `cat_id` 属性(可能在某些业务场景下以 `cat_id` 作为统一的分类标识使用),完善商品的分类相关信息记录。 + if (cats.length > 2) { + info["cat_three_id"] = cats[2]; + info["cat_id"] = cats[2]; + } + + // 如果传入的参数中包含 `goods_weight`(商品重量),则进行以下操作,先将其转换为数字类型(通过 `parseFloat`),然后检查是否能正确转换以及重量是否小于 `0`, + // 如果不满足要求则通过 `reject` 函数返回错误信息,表示商品重量格式不正确,告知调用者参数不符合要求;若格式正确则将重量值赋值给 `info` 对象的 `goods_weight` 属性,用于记录商品的重量信息。 + if (params.goods_weight) { + weight = parseFloat(params.goods_weight); + if (isNaN(weight) || weight < 0) return reject("商品重量格式不正确"); + info["goods_weight"] = weight; + } else { + // 如果没有传入商品重量参数,则默认将商品重量设置为 `0`,作为合理的默认值,方便后续业务逻辑处理重量相关情况,避免出现 `undefined` 等不确定的值导致错误。 + info["goods_weight"] = 0; + } + + // 如果传入的参数中包含 `goods_introduce`(商品介绍信息),则将其直接赋值给 `info` 对象的 `goods_introduce` 属性,用于存储商品的详细介绍内容,方便在展示商品详情等场景中使用。 + if (params.goods_introduce) { + info["goods_introduce"] = params.goods_introduce; + } + + // 如果传入的参数中包含 `goods_big_logo`(商品大图标路径或相关标识等信息),则将其赋值给 `info` 对象的 `goods_big_logo` 属性,用于记录商品大图标相关信息,方便后续业务逻辑处理图标展示等操作。 + if (params.goods_big_logo) { + info["goods_big_logo"] = params.goods_big_logo; + } else { + // 如果没有传入商品大图标信息,则默认将 `goods_big_logo` 属性设置为空字符串,避免出现 `undefined` 等不确定的值,方便后续业务逻辑处理图标相关情况。 + info["goods_big_logo"] = ""; + } + + // 如果传入的参数中包含 `goods_small_logo`(商品小图标路径或相关标识等信息),则将其赋值给 `info` 对象的 `goods_small_logo` 属性,用于记录商品小图标相关信息,方便后续业务逻辑处理图标展示等操作。 + if (params.goods_small_logo) { + info["goods_small_logo"] = params.goods_small_logo; + } else { + // 如果没有传入商品小图标信息,则默认将 `goods_small_logo` 属性设置为空字符串,避免出现 `undefined` 等不确定的值,方便后续业务逻辑处理图标相关情况。 + info["goods_small_logo"] = ""; + } + + // 如果传入的参数中包含 `goods_state`(商品状态信息,比如是否上架、是否禁用等状态标识,具体含义由业务逻辑定义),则将其赋值给 `info` 对象的 `goods_state` 属性,用于记录商品的状态信息,方便后续业务逻辑根据状态进行不同的处理操作。 + if (params.goods_state) { + info["goods_state"] = params.goods_state; + } + + // 如果传入的参数中包含 `pics`(商品图片相关信息,可能是图片路径数组或者包含图片相关属性的对象数组等形式,具体格式由业务逻辑决定),则将其赋值给 `info` 对象的 `pics` 属性,用于记录商品的图片信息,方便后续业务逻辑处理图片展示、更新等操作。 + if (params.pics) { + info["pics"] = params.pics; + } + + // 如果传入的参数中包含 `attrs`(商品属性相关信息,可能是属性名和属性值的对象数组等形式,具体格式由业务逻辑决定),则将其赋值给 `info` 对象的 `attrs` 属性,用于记录商品的属性信息,方便后续业务逻辑处理属性展示、更新等操作。 + if (params.attrs) { + info["attrs"] = params.attrs; + } + + // 获取当前时间并转换为时间戳(以秒为单位,通过除以 `1000` 将毫秒时间戳转换为秒时间戳),将其赋值给 `info` 对象的 `add_time` 属性,用于记录商品的添加时间,方便后续业务逻辑根据添加时间进行排序、查询等操作。 + info["add_time"] = Date.parse(new Date()) / 1000; + // 同样获取当前时间并转换为时间戳赋值给 `info` 对象的 `upd_time` 属性,用于记录商品的最后更新时间,方便后续业务逻辑判断商品信息是否有更新以及进行更新时间相关的查询等操作。 + info["upd_time"] = Date.parse(new Date()) / 1000; + // 将商品的删除标识默认设置为 `0`(通常 `0` 表示未删除,`1` 等其他值可能表示已删除,具体由业务逻辑定义),赋值给 `info` 对象的 `is_del` 属性,用于标记商品的删除状态,方便后续业务逻辑进行删除相关的操作和判断。 + info["is_del"] = '0'; + + // 如果传入的参数中包含 `hot_mumber`(热销品数量相关信息,比如表示商品的销量或者热门程度的数量指标,具体含义由业务逻辑定义),则进行以下操作, + // 先将其转换为数字类型(通过 `parseInt`),然后检查是否能正确转换以及数量是否小于 `0`,如果不满足要求则通过 `reject` 函数返回错误信息,表示热销品数量格式不正确,告知调用者参数不符合要求; + // 若格式正确则将数量值赋值给 `info` 对象的 `hot_mumber` 属性,用于记录商品的热销相关信息,方便后续业务逻辑根据热销情况进行推荐、排序等操作。 + if (params.hot_mumber) { + hot_num = parseInt(params.hot_mumber); + if (isNaN(hot_num) || hot_num < 0) return reject("热销品数量格式不正确"); + info["hot_mumber"] = hot_num; + } else { + // 如果没有传入热销品数量参数,则默认将其设置为 `0`,作为合理的默认值,方便后续业务逻辑处理热销相关情况,避免出现 `undefined` 等不确定的值导致错误。 +// 如果 `info` 对象中已经存在 `is_promote` 属性(可能表示商品是否参与促销等促销相关标识,具体含义由业务逻辑定义),则直接使用其原有值; +// 如果不存在,则默认将其设置为 `false`,表示商品默认不参与促销,方便后续业务逻辑根据该属性进行促销相关的判断和处理操作。 +info["is_promote"] = info["is_promote"]? info["is_promote"] : false; + +// 将 `params` 对象中的 `goods_introduce` 属性值重新赋值给 `info` 对象的 `goods_introduce` 属性,确保商品介绍信息准确更新(可能前面的逻辑中有对该属性的其他处理或者验证等情况),方便后续使用完整准确的商品介绍信息。 +info["goods_introduce"] = params.goods_introduce; + +// 通过 `resolve` 函数将包含整理好的商品基本信息的 `info` 对象返回,表明商品基本信息生成成功,外部通过 `Promise` 的 `.then` 方法可以获取到这个对象并继续后续的业务逻辑操作,比如进一步检查商品名称是否重复等操作。 +resolve(info); +}); } /** * 检查商品名称是否重复 + * 此函数用于检查要创建或更新的商品名称在数据库中是否已经存在(仅考虑未删除的商品记录),返回一个 `Promise` 对象, + * 若名称不存在或者是当前操作的商品自身(通过 `goods_id` 判断)则 `Promise` 被成功 `resolve` 并返回商品信息对象,若名称已存在则 `Promise` 被 `reject` 并返回相应错误信息。 * - * @param {[type]} info [description] - * @return {[type]} [description] + * @param {[type]} info 包含商品信息的对象,其中 `goods_name` 属性表示商品名称,`goods_id` 属性(如果存在)可用于区分是创建新商品还是更新已有商品时的名称检查情况,方便判断是否为同一个商品。 + * @return {[type]} 返回一个 `Promise` 对象,用于异步处理商品名称重复检查的结果情况,外部可以通过 `.then` 和 `.catch` 方法来处理操作成功或失败后的逻辑,成功时返回商品信息对象,失败时返回错误信息字符串。 */ function checkGoodName(info) { - return new Promise(function(resolve,reject) { - - dao.findOne("GoodModel",{"goods_name":info.goods_name,"is_del":"0"},function(err,good) { - if(err) return reject(err); - if(!good) return resolve(info); - if(good.goods_id == info.goods_id) return resolve(info); - return reject("商品名称已存在"); - }); - }); + return new Promise(function (resolve, reject) { + + // 调用 `dao` 模块的 `findOne` 方法,在 `GoodModel` 数据模型(对应数据库中的商品表相关操作)中查找符合条件的商品记录, + // 条件是商品名称为 `info.goods_name` 且 `is_del`(商品删除标识,通常 `0` 表示未删除,`1` 表示已删除)为 `0`,即查找未删除且名称相同的商品记录, + // 这是一个异步操作,通过传入的回调函数来处理查询结果,若查询出现错误则 `err` 不为 `null`,若查询到符合条件的商品记录则返回该记录对象 `good`,若未查询到则 `good` 为 `null`。 + dao.findOne("GoodModel", {"goods_name": info.goods_name, "is_del": "0"}, function (err, good) { + // 如果在查询过程中出现错误(`err` 不为 `null`),则通过 `reject` 函数返回错误信息,告知调用者名称检查操作失败及原因,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (err) return reject(err); + + // 如果没有查询到符合条件的商品记录(即 `good` 为 `null`),说明商品名称不存在重复情况,此时通过 `resolve` 函数返回传入的 `info` 对象,表明名称检查通过,可以继续后续的业务操作(比如创建或更新商品等操作)。 + if (!good) return resolve(info); + + // 如果查询到了符合条件的商品记录,进一步判断其 `goods_id` 是否与传入的 `info` 对象中的 `goods_id` 相等,若相等说明是当前正在操作的同一个商品(比如更新商品时名称未变的情况), + // 此时也通过 `resolve` 函数返回 `info` 对象,表示名称检查通过,可以继续后续操作;若 `goods_id` 不相等,则说明存在其他同名的未删除商品,即商品名称已重复,通过 `reject` 函数返回相应错误信息,告知调用者名称重复不能继续操作。 + if (good.goods_id == info.goods_id) return resolve(info); + return reject("商品名称已存在"); + }); + }); } /** * 创建商品基本信息 + * 此函数用于将整理好的商品基本信息插入数据库(对应 `GoodModel` 数据模型)中,返回一个 `Promise` 对象, + * 若插入操作成功则 `Promise` 被成功 `resolve` 并返回包含商品信息和插入后商品对象的 `info` 对象,若插入失败则 `Promise` 被 `reject` 并返回相应错误信息。 * - * @param {[type]} info [description] - * @return {[type]} [description] + * @param {[type]} info 包含商品基本信息的对象,经过前面的生成和名称检查等操作后得到的要插入数据库的商品信息,包含商品名称、价格、数量等各种属性信息。 + * @return {[type]} 返回一个 `Promise` 对象,用于异步处理创建商品基本信息的结果情况,外部可以通过 `.then` 和 `.catch` 方法来处理操作成功或失败后的逻辑,成功时返回包含商品信息和插入后商品对象的 `info` 对象,失败时返回错误信息字符串。 */ function createGoodInfo(info) { - return new Promise(function(resolve,reject){ - dao.create("GoodModel",_.clone(info),function(err,newGood) { + return new Promise(function (resolve, reject) { + // 调用 `dao` 模块的 `create` 方法,将 `info` 对象中的商品基本信息插入到 `GoodModel` 对应的数据表中, + // 为了避免对原始 `info` 对象产生意外修改,先使用 `_.clone` 对其进行克隆(通过 `lodash` 库的克隆功能)后再传入 `create` 方法, + // 这是一个异步操作,通过传入的回调函数来处理插入操作的结果,若插入出现错误则 `err` 不为 `null`,若插入成功则返回新创建的商品对象 `newGood`,包含数据库自动生成的一些字段(如 `id` 等)以及插入的商品信息。 + dao.create("GoodModel", _.clone(info), function (err, newGood) { - if(err) return reject("创建商品基本信息失败"); - newGood.goods_cat = newGood.getGoodsCat(); - info.good = newGood; - return resolve(info); - }); - }); + // 如果在创建商品基本信息的数据库插入操作过程中出现错误(`err` 不为 `null`),则通过 `reject` 函数返回错误信息,表示创建商品基本信息失败,告知调用者操作未成功及原因,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (err) return reject("创建商品基本信息失败"); + + // 如果插入成功,调用新创建的商品对象 `newGood` 的 `getGoodsCat` 方法(具体功能由该方法的定义决定,可能是获取商品分类相关信息并进行处理等操作), + // 将返回的分类信息赋值给 `newGood` 对象的 `goods_cat` 属性,确保商品的分类信息准确完整,然后将 `newGood` 对象赋值给 `info` 对象的 `good` 属性,方便后续业务逻辑使用完整的商品对象信息。 + newGood.goods_cat = newGood.getGoodsCat(); + info.good = newGood; + + // 通过 `resolve` 函数返回包含商品信息和插入后商品对象的 `info` 对象,表明创建商品基本信息操作成功,外部通过 `Promise` 的 `.then` 方法可以获取到这个对象并继续后续的业务逻辑操作,比如更新商品图片等操作。 + return resolve(info); + }); + }); } function updateGoodInfo(info) { - return new Promise(function(resolve,reject){ - if(!info.goods_id) return reject("商品ID不存在"); - dao.update("GoodModel",info.goods_id,_.clone(info),function(err,newGood) { - if(err) return reject("更新商品基本信息失败"); - info.good = newGood; - return resolve(info); - }); - - }); + return new Promise(function (resolve, reject) { + // 检查 `info` 对象中是否包含 `goods_id` 属性,如果不存在则通过 `reject` 函数返回错误信息,表示商品ID不存在,因为更新操作需要明确要更新的商品对象的唯一标识(即 `goods_id`),告知调用者参数不符合要求,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (!info.goods_id) return reject("商品ID不存在"); + + // 调用 `dao` 模块的 `update` 方法,根据 `info` 对象中的 `goods_id` 来更新 `GoodModel` 对应数据表中的商品记录信息, + // 同样先使用 `_.clone` 克隆 `info` 对象后传入 `update` 方法,避免对原始 `info` 对象意外修改, + // 这是一个异步操作,通过传入的回调函数来处理更新操作的结果,若更新出现错误则 `err` 不为 `null`,若更新成功则返回更新后的商品对象 `newGood`。 + dao.update("GoodModel", info.goods_id, _.clone(info), function (err, newGood) { + // 如果在更新商品基本信息的数据库操作过程中出现错误(`err` 不为 `null`),则通过 `reject` 函数返回错误信息,表示更新商品基本信息失败,告知调用者操作未成功及原因,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (err) return reject("更新商品基本信息失败"); + + // 如果更新成功,将更新后的商品对象 `newGood` 赋值给 `info` 对象的 `good` 属性,方便后续业务逻辑使用最新的商品对象信息。 + info.good = newGood; + + // 通过 `resolve` 函数返回包含商品信息和更新后商品对象的 `info` 对象,表明更新商品基本信息操作成功,外部通过 `Promise` 的 `.then` 方法可以获取到这个对象并继续后续的业务逻辑操作,比如更新商品图片等操作。 + return resolve(info); + }); + + }); } /** * 获取商品对象 + * 此函数用于根据传入的商品ID从数据库中获取对应的商品对象信息(对应 `GoodModel` 数据模型),返回一个 `Promise` 对象, + * 若商品ID格式正确且查询成功则 `Promise` 被成功 `resolve` 并返回包含商品信息和查询到的商品对象的 `info` 对象,若商品ID格式不正确或查询失败则 `Promise` 被 `reject` 并返回相应错误信息。 * - * @param {[type]} info 查询内容 - * @return {[type]} [description] + * @param {[type]} info 包含查询条件的对象,其中 `goods_id` 属性用于指定要获取的商品的唯一标识,若该属性不存在、值为 `null` 或者不能转换为数字类型,则视为商品ID格式不正确,无法进行查询操作。 + * @return {[type]} 返回一个 `Promise` 对象,用于异步处理获取商品对象的结果情况,外部可以通过 `.then` 和 `.catch` 方法来处理操作成功或失败后的逻辑,成功时返回包含商品信息和查询到的商品对象的 `info` 对象,失败时返回错误信息字符串。 */ function getGoodInfo(info) { - return new Promise(function(resolve,reject){ - if(!info || !info.goods_id || isNaN(info.goods_id)) return reject("商品ID格式不正确"); - - dao.show("GoodModel",info.goods_id,function(err,good){ - if(err) return reject("获取商品基本信息失败"); - good.goods_cat = good.getGoodsCat(); - info["good"] = good; - return resolve(info); - }); - }); + return new Promise(function (resolve, reject) { + // 检查 `info` 对象是否存在、是否包含 `goods_id` 属性以及 `goods_id` 属性的值是否能正确转换为数字类型(通过 `isNaN` 判断), + // 如果不满足这些条件,则通过 `reject` 函数返回错误信息,表示商品ID格式不正确,告知调用者参数不符合要求,无法进行查询操作,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (!info ||!info.goods_id || isNaN(info.goods_id)) return reject("商品ID格式不正确"); + + // 调用 `dao` 模块的 `show` 方法,根据 `info` 对象中的 `goods_id` 从 `GoodModel` 对应的数据表中获取对应的商品对象信息, + // 这是一个异步操作,通过传入的回调函数来处理查询结果,若查询出现错误则 `err` 不为 `null`,若查询成功则返回对应的商品对象 `good`。 + dao.show("GoodModel", info.goods_id, function (err, good) { + // 如果在获取商品对象信息的数据库查询操作过程中出现错误(`err` 不为 `null`),则通过 `reject` 函数返回错误信息,表示获取商品基本信息失败,告知调用者操作未成功及原因,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (err) return reject("获取商品基本信息失败"); + + // 如果查询成功,调用查询到的商品对象 `good` 的 `getGoodsCat` 方法来处理商品分类相关信息(具体功能由该方法定义决定), + // 然后将查询到的商品对象 `good` 赋值给 `info` 对象的 `good` 属性,方便后续业务逻辑使用完整的商品对象信息。 + good.goods_cat = good.getGoodsCat(); + info["good"] = good; + + // 通过 `resolve` 函数返回包含商品信息和查询到的商品对象的 `info` 对象,表明获取商品对象信息操作成功,外部通过 `Promise` 的 `.then` 方法可以获取到这个对象并继续后续的业务逻辑操作,比如更新商品图片等操作。 + return resolve(info); + }); + }); } /** * 删除商品图片 + * 此函数用于删除指定的商品图片记录(具体的删除操作由 `pic` 对象的 `remove` 方法实现,该方法应该是在对应的数据模型或对象中定义的与数据库交互的删除操作),返回一个 `Promise` 对象, + * 若 `pic` 对象存在且有 `remove` 方法并且删除操作成功则 `Promise` 被成功 `resolve`,若 `pic` 对象不符合要求或者删除失败则 `Promise` 被 `reject` 并返回相应错误信息。 * - * @param {[type]} pic 图片对象 - * @return {[type]} [description] + * @param {[type]} pic 图片对象,应该是对应商品图片的数据模型对象,包含了图片相关的各种属性以及操作方法(如 `remove` 方法用于删除图片记录),具体结构和功能由业务逻辑中对图片对象的定义决定。 + * @return {[type]} 返回一个 `Promise` 对象,用于异步处理删除商品图片记录的结果情况,外部可以通过 `.then` 和 `.catch` 方法来处理操作成功或失败后的逻辑,成功时无返回值(`Promise` 被成功 `resolve`),失败时返回错误信息字符串。 */ function removeGoodPic(pic) { - return new Promise(function(resolve,reject) { - if(!pic || !pic.remove) return reject("删除商品图片记录失败"); - pic.remove(function(err){ - if(err) return reject("删除失败"); - resolve(); - }); - }); + return new Promise(function (resolve, reject) { + // 检查 `pic` 对象是否存在以及是否包含 `remove` 方法,如果不满足条件则通过 `reject` 函数返回错误信息,表示删除商品图片记录失败,告知调用者参数不符合要求,无法进行删除操作,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (!pic ||!pic.remove) return reject("删除商品图片记录失败"); + + // 调用 `pic` 对象的 `remove` 方法来执行删除图片记录的操作,这是一个异步操作,通过传入的回调函数来处理删除操作的结果,若删除出现错误则 `err` 不为 `null`,若删除成功则无额外返回数据(通常意味着删除操作顺利完成)。 + pic.remove(function (err) { + // 如果在删除操作过程中出现错误(`err` 不为 `null`),则通过 `reject` 函数返回错误信息,表示删除失败,告知调用者操作未成功及原因,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (err) return reject("删除失败"); + + // 如果删除操作成功,通过 `resolve` 函数表示删除商品图片记录操作成功完成,外部通过 `Promise` 的 `.then` 方法可以继续后续成功后的逻辑处理(比如继续处理其他相关图片的删除或更新等操作)。 + resolve(); + }); + }); } function removeGoodPicFile(path) { - return new Promise(function(resolve,reject){ - fs.unlink(path,function(err,result){ - resolve(); - }); - }); + return new Promise(function (resolve, reject) { + // 调用 `fs` 模块的 `unlink` 方法,根据传入的文件路径(`path`)来删除对应的文件(这里用于删除商品图片的物理文件), + // 这是一个异步操作,通过传入的回调函数来处理删除文件操作的结果,虽然 `unlink` 方法的回调函数有两个参数(`err` 和 `result`),但从当前代码看只关注错误情况,若删除出现错误则 `err` 不为 `null`,若删除成功则无额外返回数据(意味着文件删除顺利完成)。 + fs.unlink(path, function (err, result) { + // 通过 `resolve` 函数表示删除商品图片文件操作成功完成,外部通过 `Promise` 的 `.then` 方法可以继续后续成功后的逻辑处理(比如继续处理其他相关图片的删除或更新等操作), + // 这里不处理 `result` 参数,因为当前代码逻辑只关心文件是否成功删除,不需要对删除操作的其他返回结果进行处理。 + resolve(); + }); + }); } -function createGoodPic(pic){ - return new Promise(function(resolve,reject){ - if(!pic) return reject("图片对象不能为空"); - var GoodPicModel = dao.getModel("GoodPicModel"); - GoodPicModel.create(pic,function(err,newPic){ - if(err) return reject("创建图片数据失败"); - resolve(); - }); - - }); -} +function createGoodPic(pic) { + return new Promise(function (resolve, reject) { + // 检查 `pic` 对象是否存在,如果不存在则通过 `reject` 函数返回错误信息,表示图片对象不能为空,因为创建图片数据需要有效的图片对象信息,告知调用者参数不符合要求,外部通过 `Promise` 的 `.catch` 方法可以捕获并处理该错误情况。 + if (!pic) return reject("图片对象不能为空"); + // 调用 `dao` 模块的 `getModel` 方法获取 `GoodPicModel` 数据模型对象(对应数据库中商品图片表相关操作的模型),然后调用该模型对象的 `create` 方法, + // 将 `pic` 对象中的图片信息插入到数据库中对应的商品图片表中,创建新的图片数据记录,这是一个异步操作,通过传入的回调函数来处理插入操作的结果,若插入出现错误则 `err` 不为 `null`,若插入 /** * 更新商品图片 - * - * @param {[type]} info 参数 - * @param {[type]} newGood 商品基本信息 + * 该函数用于处理商品图片的更新操作,涉及对已有商品图片的删除、新增图片的处理以及相应数据库和文件系统操作等一系列流程, + * 通过返回Promise对象来异步处理整个更新过程,并根据操作结果成功或失败来决定是resolve还是reject这个Promise。 + * + * @param {[type]} info 参数,包含了与商品相关的各种信息以及和图片更新相关的新图片数据等内容,用于在更新图片时进行各种条件判断和相应操作。 + * @param {[type]} newGood 商品基本信息(在当前代码逻辑中,可能原本有更紧密的关联使用方式,但目前从代码上看部分作用不太明显,不过理论上也是包含商品关键属性等用于辅助操作的对象)。 */ function doUpdateGoodPics(info) { - return new Promise(function(resolve,reject){ - var good = info.good; - if(!good.goods_id) return reject("更新商品图片失败"); - - if(!info.pics) return resolve(info); - dao.list("GoodPicModel",{"columns":{"goods_id":good.goods_id}},function(err,oldpics) { - if(err) return reject("获取商品图片列表失败"); - - - - var batchFns = []; - - var newpics = info.pics ? info.pics : []; - var newpicsKV = _.keyBy(newpics,"pics_id"); - var oldpicsKV = _.keyBy(oldpics,"pics_id"); - - /** - * 保存图片集合 - */ - // 需要新建的图片集合 - var addNewpics = []; - // 需要保留的图片的集合 - var reservedOldpics = []; - // 需要删除的图片集合 - var delOldpics = []; - - // 如果提交的新的数据中有老的数据的pics_id就说明保留数据,否则就删除 - _(oldpics).forEach(function(pic){ - if(newpicsKV[pic.pics_id]) { - reservedOldpics.push(pic); - } else { - delOldpics.push(pic); - } - }); - - // 从新提交的数据中检索出需要新创建的数据 - // 计算逻辑如果提交的数据不存在 pics_id 字段说明是新创建的数据 - _(newpics).forEach(function(pic){ - if(!pic.pics_id && pic.pic) { - addNewpics.push(pic); - } - }); - - // 开始处理商品图片数据逻辑 - // 1. 删除商品图片数据集合 - _(delOldpics).forEach(function(pic){ - // 1.1 删除图片物理路径 - batchFns.push(removeGoodPicFile(path.join(process.cwd(),pic.pics_big))); - batchFns.push(removeGoodPicFile(path.join(process.cwd(),pic.pics_mid))); - batchFns.push(removeGoodPicFile(path.join(process.cwd(),pic.pics_sma))); - // 1.2 数据库中删除图片数据记录 - batchFns.push(removeGoodPic(pic)); - }); - - // 2. 处理新建图片的集合 - _(addNewpics).forEach(function(pic){ - if(!pic.pics_id && pic.pic) { - // 2.1 通过原始图片路径裁剪出需要的图片 - var src = path.join(process.cwd(),pic.pic); - var tmp = src.split(path.sep); - var filename = tmp[tmp.length - 1]; - pic.pics_big = "/uploads/goodspics/big_" + filename; - pic.pics_mid = "/uploads/goodspics/mid_" + filename; - pic.pics_sma = "/uploads/goodspics/sma_" + filename; - batchFns.push(clipImage(src,path.join(process.cwd(),pic.pics_big),800,800)); - batchFns.push(clipImage(src,path.join(process.cwd(),pic.pics_mid),400,400)); - batchFns.push(clipImage(src,path.join(process.cwd(),pic.pics_sma),200,200)); - pic.goods_id = good.goods_id; - // 2.2 数据库中新建数据记录 - batchFns.push(createGoodPic(pic)); - } - }); - - - // 如果没有任何图片操作就返回 - if(batchFns.length == 0) { - return resolve(info); - } - - // 批量执行所有操作 - Promise.all(batchFns) - .then(function(){ - resolve(info); - }) - .catch(function(error){ - if(error) return reject(error); - }); + // 创建并返回一个Promise对象,用于异步处理更新商品图片的整个操作流程,外部代码可以通过.then()方法处理操作成功的情况,通过.catch()方法处理操作失败的情况。 + return new Promise(function (resolve, reject) { + var good = info.good; + // 首先检查商品对象(从传入的info中获取)是否有goods_id属性,如果不存在该属性,意味着无法明确要更新图片所属的具体商品, + // 这种情况下直接调用reject()函数拒绝这个Promise,并返回相应的错误信息,表示更新商品图片失败,告知外部调用者无法进行图片更新操作。 + if (!good.goods_id) return reject("更新商品图片失败"); + + // 接着判断传入的info对象中是否包含pics属性,如果不存在该属性,说明没有新的图片相关信息需要处理, + // 此时直接调用resolve()函数将info对象返回,表示图片更新环节无需进行后续操作,可直接通过,外部代码可以继续执行后续的业务逻辑。 + if (!info.pics) return resolve(info); + + // 使用dao模块的list方法从GoodPicModel对应的数据库表中查询指定商品(通过good.goods_id来确定具体是哪个商品)的所有图片记录, + // 这是一个异步操作,会通过传入的回调函数来处理查询结果。如果查询过程中出现错误(err不为null),就调用reject()函数返回错误信息,表示获取商品图片列表失败,终止后续更新操作。 + dao.list("GoodPicModel", {"columns": {"goods_id": good.goods_id}}, function (err, oldpics) { + if (err) return reject("获取商品图片列表失败"); + + var batchFns = []; + + // 获取新的图片信息,如果info.pics存在,就将其赋值给newpics变量,否则将newpics初始化为空数组,这样后续可以统一对新图片进行相关处理。 + var newpics = info.pics? info.pics : []; + // 使用_.keyBy()方法(通常来自lodash库,用于将数组转换为以某个属性为键的对象形式)将新图片数组newpics根据pics_id属性转换为以pics_id为键的对象形式, + // 这样后续可以方便地通过pics_id来查找和判断新图片与旧图片之间的对应关系等情况,例如通过newpicsKV[pic.pics_id]来快速判断某张旧图片在新图片中是否存在对应的记录。 + var newpicsKV = _.keyBy(newpics, "pics_id"); + // 同样地,使用_.keyBy()方法将旧图片数组oldpics根据pics_id属性转换为以pics_id为键的对象形式,用于后续在新旧图片对比等操作中更方便地查找和判断相关情况。 + var oldpicsKV = _.keyBy(oldpics, "pics_id"); + + /** + * 保存图片集合 + * 以下代码块的主要作用是对新旧图片进行分类整理,根据一定的规则确定哪些图片需要新建、哪些需要保留以及哪些需要删除, + * 以便后续针对不同分类的图片集合进行相应的数据库和文件系统操作,确保图片数据的更新符合业务逻辑要求。 + */ + // 需要新建的图片集合,后续会把新提交的但不存在对应pics_id的图片添加到这个集合中,然后对这些图片进行新建相关的一系列操作,比如生成合适的图片路径、插入数据库记录等操作。 + var addNewpics = []; + // 需要保留的图片的集合,用于存放那些在新提交的图片数据中能找到对应pics_id的旧图片,意味着这些图片在本次更新操作中不需要进行删除等处理,而是继续保留使用,保持原有的图片关联和展示等功能。 + var reservedOldpics = []; + // 需要删除的图片集合,用于存放那些在新提交的图片数据中找不到对应pics_id的旧图片,说明这些图片在本次更新后不再需要了,后续会对这些图片进行删除操作,包括从数据库中删除记录以及从文件系统中删除对应的图片文件等操作。 + var delOldpics = []; + + // 遍历旧图片数组oldpics,对于每一张旧图片pic,通过判断其pics_id在新图片对象newpicsKV中是否存在来确定该图片是要保留还是删除, + // 如果newpicsKV[pic.pics_id]为真(即存在对应的pics_id),说明这张旧图片在新提交的数据中有对应记录,需要保留这张图片,将其添加到reservedOldpics集合中; + // 如果不存在对应的pics_id,则说明这张旧图片在新提交的数据中已不再需要,将其添加到delOldpics集合中,表示需要删除这张图片。 + _(oldpics).forEach(function (pic) { + if (newpicsKV[pic.pics_id]) { + reservedOldpics.push(pic); + } else { + delOldpics.push(pic); + } + }); + + // 遍历新图片数组newpics,对于每一张新图片pic,通过判断其是否不存在pics_id属性并且存在pic属性(通常pic属性可能表示图片的路径等关键信息,具体含义由业务逻辑决定)来确定该图片是否为新创建的图片, + // 如果满足条件(即!pic.pics_id && pic.pic为真),说明这是一张新的需要创建记录的图片,将其添加到addNewpics集合中,以便后续对这些新图片进行创建相关的一系列操作,比如裁剪图片、设置合适的图片路径以及插入数据库记录等操作。 + _(newpics).forEach(function (pic) { + if (!pic.pics_id && pic.pic) { + addNewpics.push(pic); + } + }); + + // 开始处理商品图片数据逻辑 + // 1. 删除商品图片数据集合 + // 下面的代码会遍历需要删除的旧图片集合delOldpics,对每一张要删除的图片pic执行相关的删除操作,包括删除图片在文件系统中的物理路径以及在数据库中的数据记录。 + _(delOldpics).forEach(function (pic) { + // 1.1 删除图片物理路径 + // 先通过path.join()方法结合当前工作目录(process.cwd())和图片的大尺寸路径(pic.pics_big)构建完整的文件路径, + // 然后将删除该大尺寸图片文件的操作(通过调用removeGoodPicFile函数实现)添加到batchFns数组中,batchFns数组用于收集所有需要批量执行的异步操作(比如文件删除、数据库记录删除、新图片创建等操作),后续会通过Promise.all统一执行这些操作并处理结果。 + batchFns.push(removeGoodPicFile(path.join(process.cwd(), pic.pics_big))); + // 同样的方式,通过path.join()方法结合当前工作目录和图片的中等尺寸路径(pic.pics_mid)构建完整的文件路径, + // 并将删除该中等尺寸图片文件的操作添加到batchFns数组中,确保所有不同尺寸的相关图片文件都能被删除,保持数据的一致性和完整性。 + batchFns.push(removeGoodPicFile(path.join(process.cwd(), pic.pics_mid))); + // 按照上述方法,再结合当前工作目录和图片的小尺寸路径(pic.pics_sma)构建完整的文件路径,将删除该小尺寸图片文件的操作也添加到batchFns数组中,完成对要删除图片不同尺寸版本文件的删除操作准备工作。 + batchFns.push(removeGoodPicFile(path.join(process.cwd(), pic.pics_sma))); + + // 1.2 数据库中删除图片数据记录 + // 调用removeGoodPic函数来删除数据库中对应图片(pic)的记录信息,将这个删除数据库记录的操作添加到batchFns数组中, + // 这样在后续通过Promise.all统一执行操作时,能保证图片在文件系统和数据库中的相关记录都能被正确删除,实现完整的图片删除逻辑,保持数据的一致性。 + batchFns.push(removeGoodPic(pic)); + }); + + // 2. 处理新建图片的集合 + // 下面的代码会遍历需要新建的图片集合addNewpics,对每一张要新建的图片pic进行一系列相关操作,包括根据原始图片路径裁剪出不同尺寸的图片、设置图片的各尺寸路径信息、关联商品ID以及在数据库中创建新的图片数据记录等操作。 + _(addNewpics).forEach(function (pic) { + if (!pic.pics_id && pic.pic) { + // 2.1 通过原始图片路径裁剪出需要的图片 + // 首先通过path.join()方法结合当前工作目录(process.cwd())和图片路径(pic.pic)构建原始图片的完整路径,赋值给src变量,用于后续的图片裁剪操作,确定从哪个原始图片文件进行裁剪处理。 + var src = path.join(process.cwd(), pic.pic); + var tmp = src.split(path.sep); + var filename = tmp[tmp.length - 1]; + // 为新图片构建大尺寸版本的存储路径,将文件名添加到固定的"/uploads/goodspics/big_"前缀后作为新的大尺寸图片路径, + // 同时更新pic对象的pics_big属性,使其记录新的大尺寸图片路径信息,方便后续保存裁剪后的大尺寸图片到指定位置,并且在数据库记录等操作中能准确使用该路径信息。 + pic.pics_big = "/uploads/goodspics/big_" + filename; + // 同样的方式,为新图片构建中等尺寸版本的存储路径,将文件名添加到固定的"/uploads/goodspics/mid_"前缀后作为新的中等尺寸图片路径, + // 并更新pic对象的pics_mid属性,使其记录新的中等尺寸图片路径信息,确保后续在处理中等尺寸图片相关操作(如保存、展示等)时能使用正确的路径。 + pic.pics_mid = "/uploads/goodspics/mid_" + filename; + // 以及为新图片构建小尺寸版本的存储路径,将文件名添加到固定的"/uploads/goodspics/sma_"前缀后作为新的小尺寸图片路径, + // 更新pic对象的pics_sma属性,使其记录新的小尺寸图片路径信息,保证图片不同尺寸版本的路径信息都准确记录在pic对象中,便于后续各种操作(如保存到文件系统、关联数据库记录以及在前端展示等)的顺利进行。 + pic.pics_sma = "/uploads/goodspics/sma_" + filename; + + // 将裁剪大尺寸图片的操作添加到batchFns数组中,调用clipImage函数进行裁剪操作,传入原始图片路径src、裁剪后要保存的大尺寸图片路径(path.join(process.cwd(),pic.pics_big))以及期望的裁剪宽度800和高度800参数, + // 确保生成符合要求的大尺寸图片文件,后续会通过Promise.all统一执行该操作以及其他相关操作,并处理操作结果,保证整个新建图片的流程能正确执行,包括图片文件的生成和相关数据库记录的创建等操作。 + batchFns.push(clipImage(src, path.join(process.cwd(), pic.pics_big), 800, 800)); + // 同样将裁剪中等尺寸图片的操作添加到batchFns数组中,传入相应的中等尺寸图片的路径(path.join(process.cwd(),pic.pics_mid))和期望尺寸(宽度400、高度400)参数, + // 进行中等尺寸图片的裁剪操作准备,保证能生成合适的中等尺寸图片文件,满足业务逻辑中对不同尺寸图片的需求,例如在前端以不同的展示效果呈现商品图片等情况。 + batchFns.push(clipImage(src, path.join(process.cwd(), pic.pics_mid), 400, 400)); + // 以及将裁剪小尺寸图片的操作添加到batchFns数组中,传入小尺寸图片的路径(path.join(process.cwd(),pic.pics_sma))和期望尺寸(宽度200、高度200)参数, + // 完成对新图片不同尺寸版本裁剪操作的添加,以便后续统一执行裁剪和其他相关操作,确保新图片的各个尺寸版本都能正确生成,并且能与后续的数据库记录插入等操作配合,实现完整的新建图片逻辑,保证商品图片数据的完整性和准确性。 + batchFns.push(clipImage(src, path.join(process.cwd(), pic.pics_sma), 200, 200)); + + // 将当前新建图片pic的goods_id属性设置为对应的商品goods_id(即good.goods_id),建立图片与商品的关联关系, + // 这样在将图片信息插入数据库时能准确对应到所属商品,确保数据的关联性和业务逻辑的正确性,方便后续在查询商品相关图片等操作时能准确获取到对应的图片信息。 + pic.goods_id = good.goods_id; + + // 2.2 数据库中新建数据记录 + // 调用createGoodPic函数将包含新图片信息(已设置好尺寸路径、关联商品goods_id等属性)的pic对象插入到数据库中对应的商品图片表中,创建新的图片数据记录, + // 将这个插入数据库记录的操作添加到batchFns数组中,这样在后续通过Promise.all执行所有操作时,能保证新图片的文件创建(裁剪操作等)和数据库记录创建都能正确完成,实现完整的新建图片逻辑,保证商品图片数据在文件系统和数据库层面的一致性和准确性。 - - }); - - - }); -} - -function createGoodAttribute(goodAttribute) { - return new Promise(function(resolve,reject) { - dao.create("GoodAttributeModel",_.omit(goodAttribute,"delete_time"),function(err,newAttr){ - if(err) return reject("创建商品参数失败"); - resolve(newAttr); - }); - }); -} - -/** - * 更新商品属性 - * - * @param {[type]} info 参数 - * @param {[type]} good 商品对象 - */ -function doUpdateGoodAttributes(info) { - return new Promise(function(resolve,reject) { - var good = info.good; - if(!good.goods_id) return reject("获取商品图片必须先获取商品信息"); - if(!info.attrs) return resolve(info); - // var GoodAttributeModel = dao.getModel("GoodAttributeModel"); - goodAttributeDao.clearGoodAttributes(good.goods_id,function(err){ - if(err) return reject("清理原始的商品参数失败"); - - var newAttrs = info.attrs ? info.attrs : []; - - if(newAttrs) { - var createFns = []; - _(newAttrs).forEach(function(newattr) { - newattr.goods_id = good.goods_id; - if(newattr.attr_value) { - if(newattr.attr_value instanceof Array) { - newattr.attr_value = newattr.attr_value.join(","); - } else { - newattr.attr_value = newattr.attr_value; - } - } - else - newattr.attr_value = ""; - createFns.push(createGoodAttribute(_.clone(newattr))); - }); - } - - if(createFns.length == 0) return resolve(info); - - Promise.all(createFns) - .then(function(){ - resolve(info); - }) - .catch(function(error){ - if(error) return reject(error); - }); - - - }); - }); -} - -/** - * 挂载图片 - * - * @param {[type]} info [description] - * @return {[type]} [description] - */ -function doGetAllPics(info) { - return new Promise(function(resolve,reject){ - var good = info.good; - if(!good.goods_id) return reject("获取商品图片必须先获取商品信息"); + // 如果没有任何图片操作就返回 +优点 +简单易懂:K-means算法的概念相对简单,易于理解和实现。 +高效:对于大数据集,K-means算法通常能够相对快速地收敛到解,尤其是在使用适当的初始化方法(如k-means++)时。 +适用于大规模数据:由于算法的效率,K-means可以处理包含大量数据点的数据集。 +可扩展性:算法可以很容易地扩展到新的数据点,即当有新数据加入时,可以重新运行K-means算法或仅更新受影响的簇。 +空间划分:K-means提供了一种将数据集划分为具有相似特征的空间区域的方法,这对于数据分析和可视化非常有用。 +缺点 +K值的选择:K-means算法的结果强烈依赖于K值的选择,即簇的数量。选择合适的K值通常是一个挑战,需要用户有一定的先验知识或尝试不同的K值。 +对初始质心的敏感:K-means算法的性能和结果可能会受到初始质心选择的影响。如果初始质心选择不当,算法可能会收敛到局部最优解而不是全局最优解。 +对噪声和异常值的敏感性:K-means算法对噪声和异常值非常敏感,因为它们会扭曲簇的形状并影响质心的位置。 +不适用于非球形簇:K-means算法假设簇是球形的,即簇内点到质心的距离是相等的。如果簇的形状是非球形的,K-means可能无法正确识别簇的结构。 +只能处理数值数据:K-means算法只能处理数值数据,对于分类数据或混合类型数据(数值和分类),需要进行额外的预处理。 +结果的不稳定性:由于K-means算法对初始质心和噪声的敏感性,即使对于相同的数据集,每次运行算法也可能会得到不同的结果。 +计算质心的局限性:K-means算法通过计算平均值来确定质心,这在某些情况下可能不是最佳的选择。例如,当数据具有非线性关系或簇的形状不规则时,平均值可能无法很好地代表簇的中心。 // 3. 组装最新的数据挂载在“info”中“good”对象下 dao.list("GoodPicModel",{"columns":{"goods_id":good.goods_id}},function(err,goodPics){ @@ -670,3 +734,205 @@ module.exports.getGoodById = function(id,cb) { cb(err); }); } + +/** + * 创建商品 + * + * @param {[type]} params 商品参数 + * @param {Function} cb 回调函数 + */ +module.exports.createGood = function(params,cb) { + + + // 验证参数 & 生成数据 + generateGoodInfo(params) + // 检查商品名称 + .then(checkGoodName) + // 创建商品 + .then(createGoodInfo) + // 更新商品图片 + .then(doUpdateGoodPics) + // 更新商品参数 + .then(doUpdateGoodAttributes) + .then(doGetAllPics) + .then(doGetAllAttrs) + // 创建成功 + .then(function(info){ + cb(null,info.good); + }) + .catch(function(err) { + cb(err); + }); +} + +/** + * 删除商品 + * + * @param {[type]} id 商品ID + * @param {Function} cb 回调函数 + */ +module.exports.deleteGood = function(id,cb) { + if(!id) return cb("产品ID不能为空"); + if(isNaN(id)) return cb("产品ID必须为数字"); + dao.update( + "GoodModel", + id, + { + 'is_del':'1', + 'delete_time':Date.parse(new Date()) / 1000, + 'upd_time' : Date.parse(new Date()) / 1000 + }, + function(err){ + if(err) return cb(err); + cb(null); + } + ); +} + +/** + * 获取商品列表 + * + * @param {[type]} params 查询条件 + * @param {Function} cb 回调函数 + */ +module.exports.getAllGoods = function(params,cb) { + var conditions = {}; + if(!params.pagenum || params.pagenum <= 0) return cb("pagenum 参数错误"); + if(!params.pagesize || params.pagesize <= 0) return cb("pagesize 参数错误"); + + conditions["columns"] = {}; + if(params.query) { + conditions["columns"]["goods_name"] = orm.like("%" + params.query + "%"); + } + conditions["columns"]["is_del"] = '0'; + + + dao.countByConditions("GoodModel",conditions,function(err,count){ + if(err) return cb(err); + pagesize = params.pagesize; + pagenum = params.pagenum; + pageCount = Math.ceil(count / pagesize); + offset = (pagenum - 1) * pagesize; + if(offset >= count) { + offset = count; + } + limit = pagesize; + + // 构建条件 + conditions["offset"] = offset; + conditions["limit"] = limit; + conditions["only"] = ["goods_id","goods_name","goods_price","goods_weight","goods_state","add_time","goods_number","upd_time","hot_mumber","is_promote","goods_introduce"]; + conditions["order"] = "-add_time"; + + + dao.list("GoodModel",conditions,function(err,goods){ + if(err) return cb(err); + var resultDta = {}; + resultDta["total"] = count; + resultDta["pagenum"] = pagenum; + resultDta["goods"] = _.map(goods,function(good){ + return _.omit(good,"is_del","goods_big_logo","goods_small_logo","delete_time"); + }); + cb(err,resultDta); + }) + }); +} + +/** + * 更新商品 + * + * @param {[type]} id 商品ID + * @param {[type]} params 参数 + * @param {Function} cb 回调函数 + */ +module.exports.updateGood = function(id,params,cb) { + + params.goods_id = id; + // 验证参数 & 生成数据 + generateGoodInfo(params) + // 检查商品名称 + .then(checkGoodName) + // 创建商品 + .then(updateGoodInfo) + // 更新商品图片 + .then(doUpdateGoodPics) + // 更新商品参数 + .then(doUpdateGoodAttributes) + .then(doGetAllPics) + .then(doGetAllAttrs) + // 创建成功 + .then(function(info){ + cb(null,info.good); + }) + .catch(function(err) { + cb(err); + }); +} + +/** + * 更新商品图片 + * + * @param {[type]} goods_id 商品ID + * @param {[type]} pics 商品图片 + * @param {Function} cb 回调函数 + */ +module.exports.updateGoodPics = function(goods_id,pics,cb) { + if(!goods_id) return cb("商品ID不能为空"); + if(isNaN(goods_id)) return cb("商品ID必须为数字"); + + getGoodInfo({"goods_id":goods_id,"pics":pics}) + .then(doUpdateGoodPics) + .then(doGetAllPics) + .then(doGetAllAttrs) + .then(function(info){ + cb(null,info.good); + }) + .catch(function(err) { + cb(err); + }); +} + +module.exports.updateGoodAttributes = function(goods_id,attrs,cb) { + + getGoodInfo({"goods_id":goods_id,"attrs":attrs}) + .then(doUpdateGoodAttributes) + .then(doGetAllPics) + .then(doGetAllAttrs) + .then(function(info){ + cb(null,info.good); + }) + .catch(function(err) { + cb(err); + }); +} + +module.exports.updateGoodsState = function(goods_id,state,cb) { + getGoodInfo({"goods_id":goods_id,"goods_state":state}) + .then(updateGoodInfo) + .then(doGetAllPics) + .then(doGetAllAttrs) + .then(function(info){ + cb(null,info.good); + }) + .catch(function(err) { + cb(err); + }); +} + +/** + * 通过商品ID获取商品数据 + * + * @param {[type]} id 商品ID + * @param {Function} cb 回调函数 + */ +module.exports.getGoodById = function(id,cb) { + getGoodInfo({"goods_id":id}) + .then(doGetAllPics) + .then(doGetAllAttrs) + .then(function(info){ + cb(null,info.good); + }) + .catch(function(err) { + cb(err); + }); +} diff --git a/services/ManagerService.js b/services/ManagerService.js index 54886b1..36943c3 100644 --- a/services/ManagerService.js +++ b/services/ManagerService.js @@ -1,244 +1,384 @@ +// 引入Node.js的path模块,用于处理文件路径相关操作,通过 `path.join` 方法将当前工作目录(`process.cwd()`)与相对路径拼接起来, +// 从而准确地引入自定义的 `dao/ManagerDAO` 模块所在的路径,该模块大概率封装了与管理员数据操作相关的数据库访问方法,比如查询、计数等操作。 var path = require("path"); -var managersDAO = require(path.join(process.cwd(),"dao/ManagerDAO")); +// 引入自定义的 `ManagerDAO` 模块,用于执行与管理员数据相关的数据库操作,例如按照特定条件查询管理员信息、统计符合条件的管理员数量等,它是获取管理员数据的关键模块。 +var managersDAO = require(path.join(process.cwd(), "dao/ManagerDAO")); +// 引入 `node-php-password` 库,可能用于密码相关的处理操作(虽然从当前代码来看暂未体现其具体使用场景,但可能在涉及管理员密码验证、加密等功能时会用到)。 var Password = require("node-php-password"); +// 引入日志记录模块,通过调用 `../modules/logger` 模块中导出的 `logger` 函数来获取一个日志记录器对象,用于记录在获取管理员相关操作过程中的各种日志信息,方便调试和排查问题。 var logger = require('../modules/logger').logger(); - /** * 获取所有管理员 - * @param {[type]} conditions 查询条件 + * 此函数用于根据传入的查询条件获取符合要求的管理员信息,并通过回调函数将查询结果(包含管理员列表以及相关统计信息)或错误信息返回给调用者, + * 常用于管理员列表展示等业务场景中,根据不同的筛选条件展示相应的管理员数据。 + * + * @param {[type]} conditions 查询条件,它是一个对象,包含了用于筛选管理员信息的相关参数,遵循特定的规范格式,具体如下: * 查询条件统一规范 * conditions - { - "query" : 关键词查询, - "pagenum" : 页数, - "pagesize" : 每页长度 - } - * @param {Function} cb 回调函数 + * { + * "query" : 关键词查询,用于进行模糊搜索等操作,比如根据管理员姓名、手机号等关键信息来筛选管理员,该字段可以为空字符串,表示不进行关键词查询; + * "pagenum" : 页数,用于分页查询,指定要获取的是第几页的数据,必须是合法的正整数,否则会被判定为参数不合法; + * "pagesize" : 每页长度,用于分页查询,指定每页显示的管理员记录数量,同样必须是合法的正整数,否则会被判定为参数不合法; + * } + * @param {Function} cb 回调函数,用于接收获取管理员操作的结果,若成功获取到管理员数据,则传递包含管理员列表以及相关统计信息(如总记录数、当前页码等)的对象作为参数;若出现错误情况(如参数不合法、数据库查询失败等),则传递相应的错误信息字符串作为参数。 */ -module.exports.getAllManagers = function(conditions,cb) { - - - if(!conditions.pagenum) return cb("pagenum 参数不合法"); - if(!conditions.pagesize) return cb("pagesize 参数不合法"); - - - // 通过关键词获取管理员数量 - managersDAO.countByKey(conditions["query"],function(err,count) { - key = conditions["query"]; - pagenum = parseInt(conditions["pagenum"]); - pagesize = parseInt(conditions["pagesize"]); - - pageCount = Math.ceil(count / pagesize); - offset = (pagenum - 1) * pagesize; - if(offset >= count) { - offset = count; - } - limit = pagesize; - - managersDAO.findByKey(key,offset,limit,function(err,managers){ - var retManagers = []; - for(idx in managers) { - var manager = managers[idx]; - var role_name = manager.role_name; - if(!manager.role_id) { - role_name = "超级管理员" - } - retManagers.push({ - "id": manager.mg_id, - "role_name":role_name, - "username":manager.mg_name, - "create_time":manager.mg_time, - "mobile":manager.mg_mobile, - "email":manager.mg_email, - "mg_state":manager.mg_state == 1 - }); - } - var resultDta = {}; - resultDta["total"] = count; - resultDta["pagenum"] = pagenum; - resultDta["users"] = retManagers; - cb(err,resultDta); - }); - }); -} +module.exports.getAllManagers = function (conditions, cb) { + // 首先验证传入的查询条件(`conditions`)中的 `pagenum` 参数是否存在,如果不存在,说明页码参数不符合要求,直接通过回调函数 `cb` 返回错误信息,表示 `pagenum` 参数不合法, + // 因为分页查询需要明确的页码信息,页码通常为正整数,若不传该参数则无法正确进行分页操作,告知调用者参数不符合要求。 + if (!conditions.pagenum) return cb("pagenum 参数不合法"); + // 同样验证 `conditions` 中的 `pagesize` 参数是否存在,如果不存在,说明每页显示记录数参数不符合要求,直接通过回调函数 `cb` 返回错误信息,表示 `pagesize` 参数不合法, + // 因为分页查询需要明确每页显示多少条记录,该参数通常为正整数,若不传则无法正确进行分页操作,告知调用者参数不符合要求。 + if (!conditions.pagesize) return cb("pagesize 参数不合法"); + + // 通过关键词获取管理员数量 + // 调用 `managersDAO` 模块的 `countByKey` 方法,传入 `conditions["query"]`(即关键词查询参数,可能用于在数据库中进行模糊查询等操作来统计符合该关键词的管理员数量), + // 该方法是一个异步操作,通过回调函数来处理查询统计的结果情况,若成功则返回符合条件的管理员记录总数(`count`),用于后续的分页计算等操作。 + managersDAO.countByKey(conditions["query"], function (err, count) { + // 获取查询条件中的关键词(`key`),赋值为 `conditions["query"]`,方便后续在查询管理员记录等操作中使用该关键词进行筛选,它可能是用于模糊匹配管理员的某些关键信息(如姓名、手机号等)的字符串。 + key = conditions["query"]; + // 将查询条件中的 `pagenum` 参数转换为整数类型,赋值给 `pagenum` 变量,确保页码参数是合法的数字格式,用于后续的分页相关计算和操作,例如计算偏移量等。 + pagenum = parseInt(conditions["pagenum"]); + // 将查询条件中的 `pagesize` 参数转换为整数类型,赋值给 `pagesize` 变量,同样确保每页记录数参数是合法的数字格式,用于分页操作中的相关计算,如计算总页数、限制查询记录数量等。 + pagesize = parseInt(conditions["pagesize"]); + + // 计算总页数,通过将符合关键词条件的管理员记录总数(`count`)除以每页显示记录数(`pagesize`),然后使用 `Math.ceil` 函数向上取整,得到总共需要的页数, + // 该总页数可用于判断传入的页码是否超出合理范围等情况,确保分页查询的完整性和正确性。 + pageCount = Math.ceil(count / pagesize); + + // 计算分页查询的偏移量(`offset`),通过公式 `(当前页码 - 1) * 每页显示记录数` 来确定从数据库中的哪条记录开始查询, + // 例如当前页码为 `1` 时,偏移量为 `0`,表示从第一条记录开始查询;页码为 `2` 时,偏移量为 `pagesize`,表示从第 `pagesize + 1` 条记录开始查询,以此类推, + // 这个偏移量用于数据库查询语句中指定查询的起始位置,实现分页功能。 + offset = (pagenum - 1) * pagesize; + + // 如果计算出的偏移量大于等于记录总数(`count`),说明传入的页码可能超出了合理范围(比如总共有 `100` 条记录,每页显示 `10` 条,传入页码 `11` 就会出现这种情况), + // 此时将偏移量设置为记录总数,避免查询出现错误,相当于查询最后一页(可能不满一页的情况)的数据,保证分页查询的健壮性。 + if (offset >= count) { + offset = count; + } + + // 获取每页显示记录数(`pagesize`),赋值给 `limit` 变量,在后续的数据库查询操作中用于指定每次查询获取的管理员记录数量上限,确保分页查询按设定的每页数量返回数据。 + limit = pagesize; + + // 调用 `managersDAO` 模块的 `findByKey` 方法,按照关键词(`key`)以及分页相关的偏移量(`offset`)和限制数量(`limit`)来查询符合条件的管理员记录, + // 该方法是一个异步操作,通过回调函数来处理查询结果,若成功则返回查询到的管理员记录数组(`managers`),每个元素是一个包含管理员详细信息的对象,后续会对这些信息进行整理和封装后返回给调用者。 + managersDAO.findByKey(key, offset, limit, function (err, managers) { + // 创建一个空数组 `retManagers`,用于存储经过整理和格式化后的管理员信息,将原始查询到的管理员记录中的部分关键信息提取并按照统一格式进行封装,方便后续返回给调用者使用。 + var retManagers = []; + // 遍历查询到的管理员记录数组 `managers`,通过索引 `idx` 来访问每个管理员信息对象,对每个管理员的信息进行整理和格式化操作,提取需要展示给调用者的关键信息,并添加到 `retManagers` 数组中。 + for (idx in managers) { + var manager = managers[idx]; + var role_name = manager.role_name; + // 如果管理员信息中的 `role_id` 不存在(可能表示未明确分配角色或者特殊情况),则将角色名称默认设置为 `超级管理员`,这是一种根据角色标识来处理角色名称显示的逻辑,确保有合理的角色名称展示给调用者。 + if (!manager.role_id) { + role_name = "超级管理员" + } + // 将整理好的管理员关键信息按照特定格式封装成一个新的对象,并添加到 `retManagers` 数组中,这些信息包括管理员ID(`id`)、角色名称(`role_name`)、用户名(`username`)、 + // 创建时间(`create_time`)、手机号(`mobile`)、邮箱(`email`)以及状态信息(`mg_state`,将数据库中的数字状态值转换为布尔类型,方便调用者理解和使用,例如 `1` 可能表示启用状态,转换后为 `true`)。 + retManagers.push({ + "id": manager.mg_id, + "role_name": role_name, + "username": manager.mg_name, + "create_time": manager.mg_time, + "mobile": manager.mg_mobile, + "email": manager.mg_email, + "mg_state": manager.mg_state == 1 + }); + } + + // 创建一个空对象 `resultDta`,用于存储最终要返回给调用者的结果信息,包含符合条件的管理员记录总数(`total`)、当前页码(`pagenum`)以及整理好的管理员信息列表(`users`,即 `retManagers` 数组), + // 这样将查询结果和相关统计信息统一封装后返回给调用者,方便调用者进行后续的业务处理,比如在前端页面展示管理员列表、进行分页导航等操作。 + var resultDta = {}; + resultDta["total"] = count; + resultDta["pagenum"] = pagenum; + resultDta["users"] = retManagers; + + // 将包含管理员相关信息和统计数据的 `resultDta` 对象通过回调函数 `cb` 返回给调用者,表示查询操作成功完成,调用者可以根据这些信息进行后续的业务操作,比如展示管理员列表、根据状态进行筛选等操作。 + cb(err, resultDta); + }); + }); +} +//这段代码实现了根据特定查询条件获取管理员信息并进行分页处理的功能,通过对数据库操作的封装以及 +//数据的整理和格式化,能够方便地为调用者提供符合要求的管理员列表数据以及相关统计信息,常用于 +//管理系统中管理员列表展示等业务场景。 /** * 创建管理员 + * 此函数用于创建新的管理员账号,接收包含管理员相关信息的参数对象以及一个回调函数, + * 通过检查用户名是否已存在、对密码进行加密处理等操作后,将新管理员信息插入数据库,若操作成功则返回创建后的管理员关键信息,若失败则返回相应错误信息给回调函数。 * - * @param {[type]} user 用户数据集 - * @param {Function} cb 回调函数 + * @param {[type]} user 用户数据集,它是一个对象,应包含创建管理员所需的各种信息,例如用户名(`username`)、密码(`password`)、手机号(`mobile`)、邮箱(`email`)、角色ID(`rid`)等关键信息,具体格式和字段要求由业务逻辑决定。 + * @param {Function} cb 回调函数,用于接收创建管理员操作的结果,若成功创建管理员,则传递包含创建后的管理员关键信息(如ID、用户名、手机号等)的对象作为参数;若出现错误情况(如用户名已存在、数据库插入失败等),则传递相应的错误信息字符串作为参数。 */ -module.exports.createManager = function(params,cb) { - - managersDAO.exists(params.username,function(err,isExists){ - if(err) return cb(err); - - if(isExists) { - return cb("用户名已存在"); - } - - managersDAO.create({ - "mg_name":params.username, - "mg_pwd":Password.hash(params.password), - "mg_mobile":params.mobile, - "mg_email":params.email, - "mg_time":(Date.parse(new Date())/1000), - "role_id":params.rid - },function(err,manager){ - if(err) return cb("创建失败"); - result = { - "id" : manager.mg_id, - "username" : manager.mg_name, - "mobile" : manager.mg_mobile, - "email" : manager.mg_email, - "role_id" : manager.role_id, - "create_time":manager.mg_time - }; - cb(null,result); - }); - }); +module.exports.createManager = function (params, cb) { + // 调用 `managersDAO` 模块的 `exists` 方法,传入要创建的管理员的用户名(`params.username`),用于检查该用户名在数据库中是否已经存在, + // 该方法是一个异步操作,通过回调函数返回检查结果,若存在则 `isExists` 为 `true`,否则为 `false`,以此来判断是否能继续创建该管理员账号。 + managersDAO.exists(params.username, function (err, isExists) { + // 如果在检查用户名是否存在的过程中出现错误(`err` 不为 `null`),比如数据库查询出现问题(连接故障、查询语句执行错误等),则直接通过回调函数 `cb` 返回错误信息,告知调用者操作出现问题及原因,终止创建操作。 + if (err) return cb(err); + + // 如果检查发现用户名已经存在(`isExists` 为 `true`),说明不能再使用该用户名创建新的管理员账号,直接通过回调函数 `cb` 返回相应的错误信息,表示用户名已存在,告知调用者需要更换用户名后再尝试创建操作。 + if (isExists) { + return cb("用户名已存在"); + } + + // 调用 `managersDAO` 模块的 `create` 方法来执行创建管理员的数据库操作,传入一个包含管理员关键信息的对象, + // 其中对密码进行了加密处理(使用 `Password.hash` 函数,可能是通过特定的加密算法将明文密码转换为加密后的密文,以提高安全性), + // 同时包含用户名、手机号、邮箱、创建时间(获取当前时间并转换为合适的时间戳格式,方便数据库存储和后续查询对比等操作)以及角色ID等信息, + // 该方法是一个异步操作,通过回调函数来处理数据库插入操作完成后的结果情况,若成功则返回创建后的管理员对象(`manager`),可从中提取关键信息返回给调用者,若失败则返回相应错误信息告知调用者创建失败。 + managersDAO.create({ + "mg_name": params.username, + "mg_pwd": Password.hash(params.password), + "mg_mobile": params.mobile, + "mg_email": params.email, + "mg_time": (Date.parse(new Date()) / 1000), + "role_id": params.rid + }, function (err, manager) { + // 如果在创建管理员的数据库插入操作过程中出现错误(`err` 不为 `null`),比如违反数据库约束(可能某些字段长度限制、唯一性约束等问题)、数据库写入故障等原因, + // 则直接通过回调函数 `cb` 返回错误信息,表示创建失败,告知调用者创建管理员操作未成功及原因。 + if (err) return cb("创建失败"); + + // 如果管理员创建成功,将创建后的管理员对象(`manager`)中的关键信息提取出来,按照特定格式封装成一个新的对象(`result`), + // 这些关键信息包括管理员ID(`id`)、用户名(`username`)、手机号(`mobile`)、邮箱(`email`)、角色ID(`role_id`)以及创建时间(`create_time`),方便统一返回给调用者展示或后续使用。 + result = { + "id": manager.mg_id, + "username": manager.mg_name, + "mobile": manager.mg_mobile, + "email": manager.mg_email, + "role_id": manager.role_id, + "create_time": manager.mg_time + }; + + // 将包含创建后管理员关键信息的 `result` 对象通过回调函数 `cb` 返回给调用者,表示管理员创建成功,调用者可以获取这些信息进行后续操作,比如记录新管理员信息、进行权限分配等业务操作。 + cb(null, result); + }); + }); } /** * 更新管理员信息 + * 此函数用于更新指定管理员的部分信息,接收包含要更新的管理员信息的参数对象以及一个回调函数, + * 通过调用数据库更新操作来修改管理员的手机号、邮箱等信息(根据传入的参数确定具体更新的字段),若操作成功则返回更新后的管理员关键信息,若失败则返回相应错误信息给回调函数。 * - * @param {[type]} params 管理员信息 - * @param {Function} cb 回调函数 + * @param {[type]} params 管理员信息,它是一个对象,包含要更新的管理员相关信息,例如管理员ID(`id`)用于确定要更新的具体管理员记录,以及要更新的手机号(`mobile`)、邮箱(`email`)等字段信息,具体更新哪些字段由传入的参数决定。 + * @param {Function} cb 回调函数,用于接收更新管理员操作的结果,若成功更新管理员信息,则传递包含更新后的管理员关键信息(如ID、用户名、手机号等)的对象作为参数;若出现错误情况(如数据库更新失败等),则传递相应的错误信息字符串作为参数。 */ -module.exports.updateManager = function(params,cb) { - managersDAO.update( - { - "mg_id":params.id, - "mg_mobile":params.mobile, - "mg_email":params.email - }, - function(err,manager) { - if(err) return cb(err); - cb(null,{ - "id":manager.mg_id, - "username":manager.mg_name, - "role_id":manager.role_id, - "mobile":manager.mg_mobile, - "email":manager.mg_email - }); - } - ) +module.exports.updateManager = function (params, cb) { + // 调用 `managersDAO` 模块的 `update` 方法来执行更新管理员信息的数据库操作, + // 传入两个参数,第一个参数是一个对象,用于指定更新的条件以及要更新的部分字段信息,这里通过 `{"mg_id":params.id, "mg_mobile":params.mobile, "mg_email":params.email}` 指定了根据管理员ID(`params.id`)来确定要更新的记录, + // 同时更新该记录对应的手机号(`mg_mobile`)和邮箱(`mg_email`)字段的值(使用传入的 `params.mobile` 和 `params.email` 的值进行更新), + // 第二个参数是一个回调函数,用于处理数据库更新操作完成后的结果情况,若成功则返回更新后的管理员对象(`manager`),可从中提取关键信息返回给调用者,若失败则返回相应错误信息告知调用者更新失败。 + managersDAO.update( + { + "mg_id": params.id, + "mg_mobile": params.mobile, + "mg_email": params.email + }, + function (err, manager) { + // 如果在更新管理员信息的数据库操作过程中出现错误(`err` 不为 `null`),比如违反数据库约束(例如手机号格式不符合要求、邮箱唯一性冲突等问题)、数据库更新语句执行错误等原因, + // 则直接通过回调函数 `cb` 返回错误信息,告知调用者操作出现问题及原因,终止更新操作。 + if (err) return cb(err); + + // 如果管理员信息更新成功,将更新后的管理员对象(`manager`)中的关键信息提取出来,按照特定格式封装成一个新的对象返回给调用者, + // 这些关键信息包括管理员ID(`id`)、用户名(`username`)、角色ID(`role_id`)、手机号(`mobile`)以及邮箱(`email`),方便调用者获取更新后的管理员信息进行后续操作,比如展示更新后的管理员详情等业务操作。 + cb(null, { + "id": manager.mg_id, + "username": manager.mg_name, + "role_id": manager.role_id, + "mobile": manager.mg_mobile, + "email": manager.mg_email + }); + } + ) } /** * 通过管理员 ID 获取管理员信息 + * 此函数用于根据传入的管理员ID从数据库中获取对应的管理员详细信息,接收管理员ID以及一个回调函数, + * 通过调用数据库查询操作查找指定ID的管理员记录,若找到则返回管理员关键信息,若未找到或者出现查询错误则返回相应错误信息给回调函数。 * - * @param {[type]} id 管理员 ID - * @param {Function} cb 回调函数 + * @param {[type]} id 管理员 ID,用于唯一确定要获取信息的管理员记录,是从数据库中查询对应管理员的关键标识。 + * @param {Function} cb 回调函数,用于接收获取管理员信息操作的结果,若成功获取到管理员信息,则传递包含管理员关键信息(如ID、角色ID、用户名、手机号、邮箱等)的对象作为参数;若出现错误情况(如数据库查询失败、管理员不存在等),则传递相应的错误信息字符串作为参数。 */ -module.exports.getManager = function(id,cb) { - managersDAO.show(id,function(err,manager){ - if(err) return cb(err); - if(!manager) return cb("该管理员不存在"); - cb( - null, - { - "id":manager.mg_id, - "rid":manager.role_id, - "username":manager.mg_name, - "mobile":manager.mg_mobile, - "email":manager.mg_email - } - ); - }); +module.exports.getManager = function (id, cb) { + // 调用 `managersDAO` 模块的 `show` 方法来执行查询指定ID管理员信息的数据库操作,传入管理员ID(`id`)作为查询条件, + // 该方法是一个异步操作,通过回调函数来处理查询结果,若成功则返回对应的管理员对象(`manager`),若未找到对应的管理员记录或者出现查询错误(`err` 不为 `null`),则通过回调函数返回相应错误信息告知调用者。 + managersDAO.show(id, function (err, manager) { + // 如果在查询管理员信息的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库连接问题、查询语句执行错误等原因, + // 则直接通过回调函数 `cb` 返回错误信息,告知调用者操作出现问题及原因,终止获取信息操作。 + if (err) return cb(err); + + // 如果查询结果没有获取到对应的管理员(`!manager`,即 `manager` 为 `null`),说明该管理员ID不存在,通过回调函数 `cb` 返回相应的错误信息,表示该管理员不存在,告知调用者无法获取对应的管理员信息。 + if (!manager) return cb("该管理员不存在"); + + // 如果成功获取到管理员信息,将管理员对象(`manager`)中的关键信息提取出来,按照特定格式封装成一个新的对象返回给调用者, + // 这些关键信息包括管理员ID(`id`)、角色ID(`rid`)、用户名(`mg_name`,这里重命名为 `username` 方便调用者理解和使用统一的命名规范)、手机号(`mg_mobile`)以及邮箱(`mg_email`), + // 方便调用者获取管理员详细信息进行后续操作,比如展示管理员详情、进行权限判断等业务操作。 + cb( + null, + { + "id": manager.mg_id, + "rid": manager.role_id, + "username": manager.mg_name, + "mobile": manager.mg_mobile, + "email": manager.mg_email + } + ); + }); } /** * 通过管理员 ID 进行删除操作 + * 此函数用于根据传入的管理员ID从数据库中删除对应的管理员记录,接收管理员ID以及一个回调函数, + * 通过调用数据库删除操作来移除指定ID的管理员记录,若操作成功则返回空值表示删除成功,若失败则返回相应错误信息给回调函数。 * - * @param {[type]} id 管理员ID - * @param {Function} cb 回调函数 + * @param {[type]} id 管理员ID,用于唯一确定要删除的管理员记录,是从数据库中定位并删除对应管理员的关键标识。 + * @param {Function} cb 回调函数,用于接收删除管理员操作的结果,若成功删除管理员记录,则传递 `null` 表示操作成功;若出现错误情况(如数据库删除失败等),则传递相应的错误信息字符串作为参数。 */ -module.exports.deleteManager = function(id,cb) { - managersDAO.destroy(id,function(err){ - if(err) return cb("删除失败"); - cb(null); - }); +module.exports.deleteManager = function (id, cb) { + // 调用 `managersDAO` 模块的 `destroy` 方法来执行删除指定ID管理员记录的数据库操作,传入管理员ID(`id`)作为删除条件, + // 该方法是一个异步操作,通过回调函数来处理操作结果,若成功删除则回调函数无额外返回数据(通常返回 `null` 表示操作成功),若出现错误(`err` 不为 `null`)则返回相应错误信息告知调用者删除失败。 + managersDAO.destroy(id, function (err) { + // 如果在删除管理员记录的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库权限不足、违反外键约束(若存在关联其他表的数据时可能出现此问题)、数据库删除语句执行错误等原因, + // 则直接通过回调函数 `cb` 返回错误信息,表示删除失败,告知调用者删除管理员操作未成功及原因。 + if (err) return cb("删除失败"); + + // 如果管理员记录删除成功,通过回调函数 `cb` 返回 `null`,表示删除操作顺利完成,告知调用者可以进行后续相关业务操作,比如刷新管理员列表等操作。 + cb(null); + }); } /** * 为管理员设置角色 + * 此函数用于为指定的管理员设置新的角色,接收管理员ID和角色ID以及一个回调函数, + * 通过先查询管理员是否存在,再执行数据库更新操作来修改管理员对应的角色ID字段值,若操作成功则返回更新后的管理员关键信息,若失败则返回相应错误信息给回调函数。 * - * @param {[type]} id 管理员ID - * @param {[type]} rid 角色ID - * @param {Function} cb 回调函数 + * @param {[type]} id 管理员ID,用于唯一确定要设置角色的管理员记录,是从数据库中定位对应管理员的关键标识。 + * @param {[type]} rid 角色ID,用于指定要为管理员设置的新角色的唯一标识,通过更新操作将该角色ID关联到对应的管理员记录上。 + * @param {Function} cb 回调函数,用于接收为管理员设置角色操作的结果,若成功为管理员设置了新角色,则传递包含更新后的管理员关键信息(如ID、角色ID、用户名、手机号、邮箱等)的对象作为参数;若出现错误情况(如管理员ID不存在、数据库更新失败等),则传递相应的错误信息字符串作为参数。 */ -module.exports.setRole = function(id,rid,cb) { - managersDAO.show(id,function(err,manager){ - if(err || !manager) cb("管理员ID不存在"); - - managersDAO.update({"mg_id":manager.mg_id,"role_id":rid},function(err,manager){ - if(err) return cb("设置失败"); - cb(null,{ - "id":manager.mg_id, - "rid":manager.role_id, - "username":manager.mg_name, - "mobile":manager.mg_mobile, - "email":manager.mg_email, - }); - }); - - }) +module.exports.setRole = function (id, rid, cb) { + // 调用 `managersDAO` 模块的 `show` 方法来执行查询指定ID管理员信息的数据库操作,传入管理员ID(`id`)作为查询条件, + // 该方法是一个异步操作,通过回调函数来处理查询结果,若成功则返回对应的管理员对象(`manager`),若未找到对应的管理员记录或者出现查询错误(`err` 不为 `null`),则通过回调函数返回相应错误信息告知调用者管理员ID不存在,终止设置角色操作。 + managersDAO.show(id, function (err, manager) { + // 如果在查询管理员信息的过程中出现错误(`err` 不为 `null`),或者查询结果没有获取到对应的管理员(`!manager`),说明管理员ID不存在或者出现其他查询问题, + // 通过回调函数 `cb` 返回相应的错误信息,表示管理员ID不存在,告知调用者无法为不存在的管理员设置角色,终止操作。 + if (err ||!manager) cb("管理员ID不存在"); + + // 调用 `managersDAO` 模块的 `update` 方法来执行更新管理员角色ID的数据库操作,传入两个参数, + // 第一个参数是一个对象,用于指定更新的条件以及要更新的角色ID字段信息,这里通过 `{"mg_id":manager.mg_id,"role_id":rid}` 指定了根据管理员的实际ID(`manager.mg_id`)来确定要更新的记录, + // 并将角色ID字段(`role_id`)更新为传入的新角色ID(`rid`)的值,第二个参数是一个回调函数,用于处理数据库更新操作完成后的结果情况,若成功则返回更新后的管理员对象(`manager`),可从中提取关键信息返回给调用者,若失败则返回相应错误信息告知调用者设置失败。 + managersDAO.update({"mg_id": manager.mg_id, "role_id": rid}, function (err, manager) { + // 如果在更新管理员角色ID的数据库操作过程中出现错误(`err` 不为 `null`),比如违反数据库约束(例如角色ID不存在、不满足关联关系等问题)、数据库更新语句执行错误等原因, + // 则直接通过回调函数 `cb` 返回错误信息,表示设置失败,告知调用者设置管理员角色操作未成功及原因。 + if (err) return cb("设置失败"); + + // 如果管理员角色ID更新成功,将更新后的管理员对象(`manager`)中的关键信息提取出来,按照特定格式封装成一个新的对象返回给调用者, + // 这些关键信息包括管理员ID(`id`)、角色ID(`rid`)、用户名(`mg_name`,重命名为 `username`)、手机号(`mg_mobile`)以及邮箱(`mg_email`), + // 方便调用者获取更新后的管理员信息进行后续操作,比如查看管理员角色变更后的权限情况等业务操作。 + cb(null, { + "id": manager.mg_id, + "rid": manager.role_id, + "username": manager.mg_name, + "mobile": manager.mg_mobile, + "email": manager.mg_email, + }); + }); + + }) } -module.exports.updateMgrState = function(id,state,cb) { - managersDAO.show(id,function(err,manager){ - if(err || !manager) cb("管理员ID不存在"); - - managersDAO.update({"mg_id":manager.mg_id,"mg_state":state},function(err,manager){ - if(err) return cb("设置失败"); - cb(null,{ - "id":manager.mg_id, - "rid":manager.role_id, - "username":manager.mg_name, - "mobile":manager.mg_mobile, - "email":manager.mg_email, - "mg_state":manager.mg_state ? 1 : 0 - }); - }); - - }) +module.exports.updateMgrState = function (id, state, cb) { + // 调用 `managersDAO` 模块的 `show` 方法来执行查询指定ID管理员信息的数据库操作,传入管理员ID(`id`)作为查询条件, + // 该方法是一个异步操作,通过回调函数来处理查询结果,若成功则返回对应的 + // 先调用 `managersDAO` 模块的 `show` 方法,根据传入的管理员 `id` 去数据库中查询对应的管理员信息。 +// 这是一个异步操作,会传入一个回调函数来处理查询结果,在回调函数中接收可能出现的错误信息 `err` 以及查询到的管理员对象 `manager`。 +managersDAO.show(id, function (err, manager) { + // 如果在查询管理员信息的过程中出现错误(`err` 不为 `null`),或者没有查询到对应的管理员(`!manager`,即 `manager` 为 `null`), + // 说明管理员ID不存在或者出现了数据库查询相关的问题,此时通过回调函数 `cb` 返回错误信息“管理员ID不存在”,告知调用者无法进行后续操作,因为要操作的管理员对象不存在。 + if (err ||!manager) cb("管理员ID不存在"); + + // 如果查询到了对应的管理员信息,接下来调用 `managersDAO` 模块的 `update` 方法,尝试更新该管理员的状态信息。 + // 传入一个对象作为更新条件和要更新的数据,对象中 `{"mg_id":manager.mg_id,"mg_state":state}` 表示根据当前查询到的管理员的实际 `mg_id` 来确定要更新的记录, + // 并将 `mg_state` 字段更新为传入的 `state` 参数值,同样这是一个异步操作,通过传入的回调函数来处理更新操作的结果。 + managersDAO.update({"mg_id": manager.mg_id, "mg_state": state}, function (err, manager) { + // 如果在更新管理员状态的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库更新语句执行失败、违反数据约束等原因, + // 则通过回调函数 `cb` 返回错误信息“设置失败”,告知调用者更新管理员状态的操作没有成功,方便调用者进行相应的错误处理或提示给用户等操作。 + if (err) return cb("设置失败"); + + // 如果管理员状态更新成功,通过回调函数 `cb` 返回一个包含更新后管理员关键信息的对象。 + // 其中包括管理员 `id`(`manager.mg_id`)、角色 `id`(`manager.role_id`)、用户名(`manager.mg_name`)、手机号(`manager.mg_mobile`)、邮箱(`manager.mg_email`)以及更新后的状态信息(`mg_state`), + // 这里对 `mg_state` 进行了一个简单的处理,将其转换为 `1`(如果 `mg_state` 为真)或 `0`(如果 `mg_state` 为假)的格式,方便后续业务逻辑中对状态的判断和使用,然后将整个对象返回给调用者,告知调用者更新操作已完成且返回了最新的管理员信息。 + cb(null, { + "id": manager.mg_id, + "rid": manager.role_id, + "username": manager.mg_name, + "mobile": manager.mg_mobile, + "email": manager.mg_email, + "mg_state": manager.mg_state? 1 : 0 + }); + }); + +}) } /** * 管理员登录 - * @param {[type]} username 用户名 - * @param {[type]} password 密码 - * @param {Function} cb 回调 + * 此函数用于处理管理员登录的逻辑,接收用户名和密码作为参数,以及一个回调函数用于返回登录操作的结果。 + * 通过查询数据库验证用户名是否存在、检查用户权限及状态等多步操作,判断登录是否成功,若成功则返回管理员相关信息,若失败则返回相应的错误信息给回调函数。 + * + * @param {[type]} username 用户名,是管理员登录时输入的标识信息,用于在数据库中查找对应的管理员记录。 + * @param {[type]} password 密码,是管理员登录时输入的密码信息,用于和数据库中存储的加密密码进行比对验证。 + * @param {Function} cb 回调函数,用于接收管理员登录操作的结果,若登录成功,则传递包含管理员关键信息(如ID、角色ID、用户名、手机号、邮箱等)的对象作为参数;若出现错误情况(如用户名不存在、用户无权限、密码错误等),则传递相应的错误信息字符串作为参数。 */ -module.exports.login = function(username,password,cb) { - logger.debug('login => username:%s,password:%s',username,password); - logger.debug(username); - managersDAO.findOne({"mg_name":username},function(err,manager) { - logger.debug(err); - if(err || !manager) return cb("用户名不存在"); - if(manager.role_id < 0) { - return cb("该用户没有权限登录"); - } - - if(manager.role_id != 0 && manager.mg_state != 1) { - return cb("该用户已经被禁用"); - } - - if(Password.verify(password, manager.mg_pwd)){ - cb( - null, - { - "id":manager.mg_id, - "rid":manager.role_id, - "username":manager.mg_name, - "mobile":manager.mg_mobile, - "email":manager.mg_email, - } - ); - } else { - return cb("密码错误"); - } - }); -} \ No newline at end of file +module.exports.login = function (username, password, cb) { + // 使用 `logger` 对象的 `debug` 方法记录调试信息,输出登录操作时传入的用户名和密码信息,方便在开发调试阶段查看登录时的参数情况,有助于排查问题,例如查看是否传入了正确的用户名和密码值。 + logger.debug('login => username:%s,password:%s', username, password); + // 再次使用 `logger` 对象的 `debug` 方法记录调试信息,单独输出用户名信息,进一步方便在调试时查看用户名相关情况,比如确认用户名是否符合预期格式等。 + logger.debug(username); + + // 调用 `managersDAO` 模块的 `findOne` 方法,根据传入的用户名(`{"mg_name":username}`)去数据库中查找对应的管理员记录, + // 这是一个异步操作,通过传入的回调函数来处理查询结果,在回调函数中接收可能出现的错误信息 `err` 以及查询到的管理员对象 `manager`。 + managersDAO.findOne({"mg_name": username}, function (err, manager) { + // 使用 `logger` 对象的 `debug` 方法记录调试信息,输出查询管理员过程中出现的错误信息 `err`,方便在调试时查看是否出现了数据库查询相关的错误以及具体错误内容,有助于定位问题所在。 + logger.debug(err); + + // 如果在查询管理员的过程中出现错误(`err` 不为 `null`),或者没有查询到对应的管理员(`!manager`,即 `manager` 为 `null`), + // 说明用户名不存在或者出现了数据库查询相关的问题,此时通过回调函数 `cb` 返回错误信息“用户名不存在”,告知调用者登录操作失败,因为找不到对应的管理员账号,方便调用者进行相应的提示给用户等操作。 + if (err ||!manager) return cb("用户名不存在"); + + // 如果查询到的管理员的角色 `id`(`manager.role_id`)小于 `0`,说明该用户可能不符合正常的权限设定规则(具体含义由业务逻辑定义,可能是特殊标记表示无登录权限等情况), + // 此时通过回调函数 `cb` 返回错误信息“该用户没有权限登录”,告知调用者该管理员账号不能用于登录,可能需要联系相关人员处理权限问题等操作。 + if (manager.role_id < 0) { + return cb("该用户没有权限登录"); + } + + // 如果管理员的角色 `id` 不等于 `0`(可能表示不是超级管理员之类具有特殊权限的角色)且管理员的状态(`manager.mg_state`)不等于 `1`(通常 `1` 表示启用状态,`0` 表示禁用等其他状态,具体由业务逻辑定义), + // 说明该用户虽然存在且有相应角色,但当前处于被禁用状态,此时通过回调函数 `cb` 返回错误信息“该用户已经被禁用”,告知调用者该管理员账号不能登录,可能需要联系管理员进行账号启用等操作。 + if (manager.role_id!= 0 && manager.mg_state!= 1) { + return cb("该用户已经被禁用"); + } + + // 使用 `Password` 模块的 `verify` 方法来验证输入的密码(`password`)与数据库中存储的该管理员的加密密码(`manager.mg_pwd`)是否匹配, + // 如果匹配成功(即密码验证通过),说明登录信息正确,通过回调函数 `cb` 返回一个包含管理员关键信息的对象, + // 这些信息包括管理员 `id`(`manager.mg_id`)、角色 `id`(`manager.role_id`)、用户名(`manager.mg_name`)、手机号(`manager.mg_mobile`)、邮箱(`manager.mg_email`),方便后续业务逻辑根据登录后的管理员信息进行相应操作,比如进入管理系统主界面、记录登录日志等操作。 + if (Password.verify(password, manager.mg_pwd)) { + cb( + null, + { + "id": manager.mg_id, + "rid": manager.role_id, + "username": manager.mg_name, + "mobile": manager.mg_mobile, + "email": manager.mg_email, + } + ); + } else { + // 如果密码验证不通过,说明输入的密码错误,通过回调函数 `cb` 返回错误信息“密码错误”,告知调用者登录失败,方便调用者进行相应的提示给用户等操作,比如提示用户重新输入密码。 + return cb("密码错误"); + } + }); +} +//这段代码实现了管理员相关的重要操作逻辑,包括更新管理员状态以及管理员登录验证等功能。通过与数 +//据库的交互以及各种条件判断,保证了这些操作的准确性和安全性,同时借助日志记录方便了调试与问 +//题排查。 \ No newline at end of file diff --git a/services/MenuService.js b/services/MenuService.js index d212308..a7c1763 100644 --- a/services/MenuService.js +++ b/services/MenuService.js @@ -1,90 +1,132 @@ +// 引入lodash库,用于处理各种数据结构,提供了如对象操作、数组处理、排序等很多实用的工具函数,在后续代码中会频繁使用到。 var _ = require('lodash'); +// 引入Node.js的path模块,主要用于处理文件路径相关操作,通过 `path.join` 方法拼接当前工作目录(`process.cwd()`)和相对路径,来准确引入自定义的 `dao/DAO` 和 `dao/PermissionAPIDAO` 模块所在的路径。 var path = require("path"); -var dao = require(path.join(process.cwd(),"dao/DAO")); -var permissionAPIDAO = require(path.join(process.cwd(),"dao/PermissionAPIDAO")); +// 引入自定义的 `DAO` 模块,该模块应该封装了通用的数据访问操作方法,例如查询、获取单条记录等操作,对应不同的数据模型(如 `RoleModel` 等)与数据库进行交互,执行相关的数据操作。 +var dao = require(path.join(process.cwd(), "dao/DAO")); +// 引入自定义的 `PermissionAPIDAO` 模块,推测这个模块主要用于权限相关数据的访问操作,比如获取权限列表等功能,同样是与数据库进行交互来获取权限数据,为后续构建菜单数据提供基础信息。 +var permissionAPIDAO = require(path.join(process.cwd(), "dao/PermissionAPIDAO")); /** * 获取左侧菜单数据 + * 此函数用于根据用户信息获取对应的左侧菜单数据,通过回调函数将获取到的菜单数据(成功时)或错误信息(失败时)返回给调用者,以用于前端页面展示菜单等相关业务操作。 * - * @param {Function} cb 回调函数 + * @param {Function} cb 回调函数,用于接收获取左侧菜单数据操作的结果,若成功获取到菜单数据,则传递菜单数据对象作为参数;若出现错误情况(如无权限、数据库查询失败等),则传递相应的错误信息字符串作为参数。 */ -module.exports.getLeftMenus = function(userInfo,cb) { - if(!userInfo) return cb("无权限访问"); +module.exports.getLeftMenus = function (userInfo, cb) { + // 首先验证传入的用户信息(`userInfo`)是否存在,如果不存在则说明无法确定用户身份及权限等情况,直接通过回调函数 `cb` 返回错误信息,表示无权限访问,终止后续操作。 + if (!userInfo) return cb("无权限访问"); - + // 定义一个内部函数 `authFn`,用于根据角色ID(`rid`)以及角色已有的权限信息(`keyRolePermissions`)来获取并整理权限数据,构建成菜单结构形式的数据,最终通过回调函数 `cb` 返回处理后的结果。 + var authFn = function (rid, keyRolePermissions, cb) { + // 调用 `permissionAPIDAO` 模块的 `list` 方法来获取所有的权限数据,该方法内部大概率会执行数据库查询操作,从数据库中获取权限相关的记录信息,它是一个异步操作,通过回调函数来处理查询结果。 + permissionAPIDAO.list(function (err, permissions) { + // 如果在获取权限数据的过程中出现错误(`err` 不为 `null`),比如数据库查询失败(可能是连接问题、权限不足、表不存在等原因),则直接通过回调函数 `cb` 返回错误信息,表示获取权限数据失败,让调用者知晓操作出现问题及原因。 + if (err) return cb("获取权限数据失败"); - var authFn = function(rid,keyRolePermissions,cb) { - permissionAPIDAO.list(function(err,permissions){ - if(err) return cb("获取权限数据失败"); - var keyPermissions = _.keyBy(permissions,'ps_id'); - var rootPermissionsResult = {}; - // 处理一级菜单 - for(idx in permissions) { + // 使用 `lodash` 的 `keyBy` 函数,将获取到的权限数据(`permissions`)按照权限ID(`ps_id`)进行转换,生成一个以权限ID为键,对应权限详细信息为值的对象结构, + // 方便后续通过权限ID快速查找对应的权限详情,在构建菜单数据结构以及判断权限关联等操作中能更高效地获取所需信息。 + var keyPermissions = _.keyBy(permissions, 'ps_id'); - permission = permissions[idx]; - - if(permission.ps_level == 0) { - if(rid != 0) { - if(!keyRolePermissions[permission.ps_id]) continue;; - } - rootPermissionsResult[permission.ps_id] = { - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path, - "children":[], - "order":permission.ps_api_order - }; - } - } + // 创建一个空对象 `rootPermissionsResult`,用于存储最终整理好的菜单数据结构,它将以一级菜单权限的权限ID为键,对应权限的详细信息(包含子菜单信息等)为值进行存储,作为菜单结构的顶层节点。 + var rootPermissionsResult = {}; - // 处理二级菜单 - for(idx in permissions) { - permission = permissions[idx]; - if(permission.ps_level == 1) { - if(rid != 0) { - if(!keyRolePermissions[permission.ps_id]) continue;; - } - parentPermissionResult = rootPermissionsResult[permission.ps_pid]; - if(parentPermissionResult) { - parentPermissionResult.children.push({ - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path, - "children":[], - "order":permission.ps_api_order - }); - } - } - } - // 排序 - result = _.values(rootPermissionsResult); - result = _.sortBy(result,"order"); - for(idx in result) { - subresult = result[idx]; - subresult.children = _.sortBy(subresult.children,"order"); - } + // 处理一级菜单 + // 遍历获取到的权限数据数组 `permissions`,通过索引 `idx` 来访问每个权限信息对象,针对每个权限进行相关处理,构建一级菜单对应的权限信息结构并添加到 `rootPermissionsResult` 对象中。 + for (idx in permissions) { + permission = permissions[idx]; + // 如果当前权限的层级(`ps_level`)为0,表示它是一级菜单权限,进行以下操作来构建一级菜单数据结构。 + if (permission.ps_level == 0) { + // 如果角色ID(`rid`)不等于0,意味着不是特殊的默认角色(可能是普通用户角色等情况),此时需要进一步判断当前权限是否在该角色已有的权限列表中(通过 `keyRolePermissions` 对象来检查), + // 如果当前权限不在角色权限列表中(`!keyRolePermissions[permission.ps_id]`),则使用 `continue` 跳过本次循环,不将该权限添加到一级菜单数据结构中,因为该角色没有此权限对应的菜单显示权限。 + if (rid!= 0) { + if (!keyRolePermissions[permission.ps_id]) continue; + } + // 如果满足添加条件(角色ID为0或者权限在角色权限列表中),则将当前一级菜单权限的相关信息按照特定结构添加到 `rootPermissionsResult` 对象中, + // 包括权限ID、权限名称(`authName`)、对应的接口路径(`ps_api_path`,可能用于前端路由跳转等操作)、初始化一个空的子菜单数组(`children`)以及权限的显示顺序(`order`,用于后续菜单排序)。 + rootPermissionsResult[permission.ps_id] = { + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path, + "children": [], + "order": permission.ps_api_order + }; + } + } - cb(null,result); - }); - } + // 处理二级菜单 + // 再次遍历权限数据数组 `permissions`,同样对每个权限进行检查和相关处理,这次是构建二级菜单对应的权限信息结构,并关联到对应的一级菜单权限下,完善菜单数据的层级结构。 + for (idx in permissions) { + permission = permissions[idx]; + if (permission.ps_level == 1) { + // 如果角色ID(`rid`)不等于0,同样需要判断当前二级菜单权限是否在该角色已有的权限列表中(通过 `keyRolePermissions` 对象来检查), + // 如果不在角色权限列表中,则使用 `continue` 跳过本次循环,不处理该权限,因为角色没有此权限对应的菜单显示权限。 + if (rid!= 0) { + if (!keyRolePermissions[permission.ps_id]) continue; + } + // 根据当前二级菜单权限的父级权限ID(`ps_pid`),从 `rootPermissionsResult` 中获取对应的一级菜单权限结果对象,后续将把当前二级菜单权限添加到这个一级菜单权限的子菜单列表中。 + parentPermissionResult = rootPermissionsResult[permission.ps_pid]; + if (parentPermissionResult) { + // 将当前二级菜单权限的相关信息按照特定结构添加到对应的一级菜单权限结果对象的子菜单数组(`children`)中,建立起二级菜单与一级菜单的层级关系, + // 包括权限ID、权限名称、对应的接口路径、初始化一个空的子菜单数组(用于后续可能存在的三级菜单等继续添加)以及权限的显示顺序。 + parentPermissionResult.children.push({ + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path, + "children": [], + "order": permission.ps_api_order + }); + } + } + } - rid = userInfo.rid; - if(rid == 0) { - authFn(rid,null,cb); - } else { - dao.show("RoleModel",userInfo.rid,function(err,role){ - if(err || !role) return cb("无权限访问"); - - - rolePermissions = role.ps_ids.split(",") - keyRolePermissions = {} - for(idx in rolePermissions) { - keyRolePermissions[rolePermissions[idx]] = true; - } + // 排序 + // 首先使用 `_.values` 获取 `rootPermissionsResult` 对象中的值(即整理好的菜单数据结构,包含一级菜单及其子菜单信息),形成一个数组 `result`,方便后续进行排序操作。 + result = _.values(rootPermissionsResult); + // 使用 `_.sortBy` 函数按照每个菜单对象中的 `order` 属性(权限的显示顺序)对 `result` 数组进行排序,使得一级菜单按照指定的顺序排列,提升菜单展示的合理性和美观性。 + result = _.sortBy(result, "order"); + // 遍历排序后的一级菜单数组 `result`,对每个一级菜单下的子菜单数组(`subresult.children`)同样使用 `_.sortBy` 函数按照 `order` 属性进行排序, + // 保证二级菜单在各自的一级菜单下也按照指定顺序排列,进一步完善菜单数据的有序性,方便前端按照顺序展示菜单。 + for (idx in result) { + subresult = result[idx]; + subresult.children = _.sortBy(subresult.children, "order"); + } - authFn(rid,keyRolePermissions,cb); - - }) - } - -} \ No newline at end of file + // 将整理好并排序后的菜单数据结构通过回调函数 `cb` 返回给调用者,表示成功获取并处理好了左侧菜单数据,调用者可以根据这个数据结构进行后续的操作, + // 比如将菜单数据传递给前端框架进行菜单渲染展示等业务操作。 + cb(null, result); + }); + } + + // 获取用户信息中的角色ID(`rid`),用于后续判断用户角色情况以及相应的权限处理,角色ID是区分不同用户角色、确定权限范围的重要标识。 + rid = userInfo.rid; + // 如果角色ID(`rid`)等于0,可能表示特殊的默认角色(例如超级管理员等拥有全部权限的角色),直接调用 `authFn` 函数,传入角色ID以及 `null`(因为默认角色无需再额外判断权限是否在角色权限列表中), + // 让 `authFn` 函数去获取并整理所有权限数据构建菜单结构,最终通过回调函数 `cb` 返回结果。 + if (rid == 0) { + authFn(rid, null, cb); + } else { + // 如果角色ID不等于0,说明是普通用户角色等情况,需要先获取该角色对应的详细信息,以确定其拥有的权限列表,进而根据权限来构建菜单数据结构。 + // 调用 `dao` 模块的 `show` 方法来获取指定角色ID(`userInfo.rid`)对应的角色信息,该方法内部会执行数据库查询操作,查找对应角色的记录,它是一个异步操作,通过回调函数来处理查询结果。 + dao.show("RoleModel", userInfo.rid, function (err, role) { + // 如果在获取角色信息的过程中出现错误(`err` 不为 `null`),或者查询结果没有获取到对应的角色(`!role`),说明可能出现权限问题(比如角色不存在、无权限查询角色信息等情况), + // 则直接通过回调函数 `cb` 返回错误信息,表示无权限访问,终止后续操作,告知调用者无法获取菜单数据及原因。 + if (err ||!role) return cb("无权限访问"); + + // 将获取到的角色对象中的权限ID字符串(`ps_ids`,可能是以逗号分隔的权限ID列表)进行分割,得到一个权限ID数组 `rolePermissions`,方便后续判断每个权限是否属于该角色。 + rolePermissions = role.ps_ids.split(","); + // 创建一个空对象 `keyRolePermissions`,用于以权限ID为键,值为 `true` 的形式来存储该角色拥有的权限信息,方便后续快速判断某个权限是否在该角色的权限范围内。 + keyRolePermissions = {}; + // 遍历权限ID数组 `rolePermissions`,将每个权限ID作为键添加到 `keyRolePermissions` 对象中,并将对应的值设置为 `true`,构建角色权限的快速查找结构。 + for (idx in rolePermissions) { + keyRolePermissions[rolePermissions[idx]] = true; + } + + // 调用 `authFn` 函数,传入角色ID(`rid`)以及构建好的角色权限查找对象(`keyRolePermissions`),让 `authFn` 函数根据该角色的权限情况去获取并整理权限数据,构建菜单结构,最终通过回调函数 `cb` 返回结果。 + authFn(rid, keyRolePermissions, cb); + + }) + } +} +//这段代码整体实现了根据用户角色信息获取对应的左侧菜单数据的功能,通过对不同角色(如超级管理员 +//和普通用户角色)的权限判断和处理,构建出具有层级结构且有序排列的菜单数据,适用于权限管理与前 +//端菜单展示相关的应用开发场景,能够根据用户权限灵活展示不同的菜单内容。 \ No newline at end of file diff --git a/services/OrderService.js b/services/OrderService.js index 08ac60a..a2aa2ac 100644 --- a/services/OrderService.js +++ b/services/OrderService.js @@ -1,292 +1,484 @@ +// 引入lodash库,用于处理各种数据结构,提供了很多便捷的工具函数,例如对象克隆、数组求和、遍历等操作,在后续多个函数中会用到。 var _ = require('lodash'); +// 引入Node.js的path模块,用于处理文件路径相关操作,此处用于准确引入自定义的 `dao/DAO` 模块所在的路径。 var path = require("path"); +// 引入 `orm` 库,可能用于对象关系映射相关操作,从代码中看,像是用于构建数据库查询条件等功能(例如使用 `orm.like` 方法),方便与数据库进行交互。 var orm = require("orm"); -var dao = require(path.join(process.cwd(),"dao/DAO")); +// 引入自定义的 `DAO` 模块,该模块应该封装了与数据库操作相关的通用方法,例如创建、查询、更新等操作,对应不同的数据模型(如 `OrderModel`、`OrderGoodModel` 等)进行数据库层面的操作。 +var dao = require(path.join(process.cwd(), "dao/DAO")); +// 引入 `bluebird` 库,用于处理异步操作的 `Promise`,使得异步代码可以用更清晰的链式调用方式书写,提高代码的可读性和可维护性,在多个函数中用于包装异步操作并返回 `Promise` 对象。 var Promise = require("bluebird"); +// 引入 `uniqid` 库,用于生成唯一的标识符,在创建订单相关逻辑中可能用于生成订单编号等唯一标识信息。 var uniqid = require('uniqid'); +// 定义 `doCheckOrderParams` 函数,用于对创建或更新订单时传入的参数进行合法性检查和预处理,将处理后的参数信息以 `Promise` 的形式返回。 function doCheckOrderParams(params) { - return new Promise(function(resolve,reject) { - var info = {}; - if(params.order_id) info.order_id = params.order_id; - - if(!params.order_id) { - if(!params.user_id) return reject("用户ID不能为空"); - if(isNaN(parseInt(params.user_id))) return reject("用户ID必须是数字"); - info.user_id = params.user_id; - } - - - if(!params.order_id) info.order_number = "itcast-" + uniqid(); - - if(!params.order_price) return reject("订单价格不能为空"); - if(isNaN(parseFloat(params.order_price))) return reject("订单价格必须为数字"); - info.order_price = params.order_price; - - if(params.order_pay){ - info.order_pay = params.order_pay; - } else { - info.order_pay = '0'; - } - if(params.is_send) { - if(params.is_send == 1) { - info.is_send = '是'; - } else { - info.is_send = '否'; - } - } else { - info.is_send = '否'; - } - - if(params.trade_no) { - info.trade_no = '否'; - } else { - info.trade_no = ''; - } - - - if(params.order_fapiao_title) { - if(params.order_fapiao_title != '个人' && params.order_fapiao_title != '公司') - return reject("发票抬头必须是 个人 或 公司"); - info.order_fapiao_title = params.order_fapiao_title; - - } else { - info.order_fapiao_title = "个人"; - } - - if(params.order_fapiao_company) { - info.order_fapiao_company = params.order_fapiao_company; - } else { - info.order_fapiao_company = ""; - } - - if(params.order_fapiao_content) { - info.order_fapiao_content= params.order_fapiao_content; - } else { - info.order_fapiao_content= ""; - } - - if(params.consignee_addr) { - info.consignee_addr = params.consignee_addr; - } else { - info.consignee_addr = ""; - } - - if(params.goods) { - info.goods = params.goods; - } - - info.pay_status = '0'; - if(params.order_id) info.create_time = (Date.parse(new Date())/1000); - info.update_time = (Date.parse(new Date())/1000); - - resolve(info); - }); + // 返回一个新的 `Promise` 对象,在其内部进行参数验证和信息整理逻辑,通过 `resolve` 和 `reject` 来控制 `Promise` 的状态(成功或失败)。 + return new Promise(function (resolve, reject) { + // 创建一个空对象 `info`,用于存储经过整理和验证后的订单相关信息,后续会根据传入的参数情况,将符合要求的信息添加到这个对象中。 + var info = {}; + + // 如果传入的参数中包含 `order_id`,则将其直接添加到 `info` 对象中,`order_id` 可能用于表示已存在的订单的唯一标识,后续操作可能会基于此判断是更新还是创建新订单等情况。 + if (params.order_id) info.order_id = params.order_id; + + // 如果传入的参数中不包含 `order_id`,则进行以下额外的参数验证和信息补充操作,意味着可能是要创建一个新订单,所以需要更多必要参数的验证。 + if (!params.order_id) { + // 首先验证用户ID(`user_id`)是否存在,如果不存在则直接通过 `reject` 拒绝这个 `Promise`,并返回错误信息,表示用户ID不能为空,因为创建订单通常需要关联用户信息,用户ID是关键标识之一。 + if (!params.user_id) return reject("用户ID不能为空"); + // 进一步验证用户ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示用户ID必须是数字,以确保能准确在数据库中关联到对应的用户记录等操作。 + if (isNaN(parseInt(params.user_id))) return reject("用户ID必须是数字"); + // 如果用户ID验证通过,则将其添加到 `info` 对象中,作为新订单关联的用户标识信息。 + info.user_id = params.user_id; + } + + // 如果没有传入 `order_id`,意味着创建新订单,此时生成一个唯一的订单编号(格式为 `"itcast-"` 加上通过 `uniqid` 库生成的唯一标识符),并添加到 `info` 对象中作为订单的编号信息。 + if (!params.order_id) info.order_number = "itcast-" + uniqid(); + + // 验证订单价格(`order_price`)是否存在,如果不存在则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示订单价格不能为空,因为订单价格是订单的重要属性之一,不可或缺。 + if (!params.order_price) return reject("订单价格不能为空"); + // 进一步验证订单价格是否可以转换为数字类型(这里使用 `parseFloat` 是因为价格可能包含小数部分),如果不能转换成功则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示订单价格必须为数字,以保证数据的合法性和符合数据库存储要求。 + if (isNaN(parseFloat(params.order_price))) return reject("订单价格必须为数字"); + // 如果订单价格验证通过,则将其添加到 `info` 对象中,作为订单的价格信息。 + info.order_price = params.order_price; + + // 如果传入了 `order_pay` 参数,则将其值直接添加到 `info` 对象中,用于表示订单的支付情况等相关信息;如果没有传入,则默认设置为 `'0'`,表示未支付或初始支付状态,具体含义由业务逻辑决定。 + if (params.order_pay) { + info.order_pay = params.order_pay; + } else { + info.order_pay = '0'; + } + + // 根据传入的 `is_send` 参数来设置订单的发货状态信息,如果 `is_send` 参数为 `1`,则将发货状态设置为 `'是'`;如果为其他值(包括未传入该参数的情况),则默认设置为 `'否'`,同样具体的状态表示和业务含义由业务逻辑决定。 + if (params.is_send) { + if (params.is_send == 1) { + info.is_send = '是'; + } else { + info.is_send = '否'; + } + } else { + info.is_send = '否'; + } + + // 根据传入的 `trade_no` 参数来设置交易编号相关信息,如果传入了该参数则设置为 `'否'`(这里设置的值看起来有点奇怪,可能需要根据实际业务逻辑进一步确认是否正确),如果没有传入则设置为空字符串,可能后续会根据实际交易情况进行更新等操作。 + if (params.trade_no) { + info.trade_no = '否'; + } else { + info.trade_no = ''; + } + + // 验证发票抬头(`order_fapiao_title`)相关信息,如果传入了该参数,则进行进一步验证,判断其值是否为 `'个人'` 或 `'公司'`,如果不是则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示发票抬头必须是 `'个人'` 或 `'公司'`; + // 如果验证通过或者没有传入该参数(则默认设置为 `'个人'`),则将其添加到 `info` 对象中,作为订单的发票抬头信息。 + if (params.order_fapiao_title) { + if (params.order_fapiao_title!= '个人' && params.order_fapiao_title!= '公司') + return reject("发票抬头必须是 个人 或 公司"); + info.order_fapiao_title = params.order_fapiao_title; + + } else { + info.order_fapiao_title = "个人"; + } + + // 如果传入了 `order_fapiao_company` 参数,则将其值添加到 `info` 对象中,用于存储发票对应的公司信息(如果有);如果没有传入则设置为空字符串,可能表示没有对应的公司信息或者留空待后续补充等情况。 + if (params.order_fapiao_company) { + info.order_fapiao_company = params.order_fapiao_company; + } else { + info.order_fapiao_company = ""; + } + + // 如果传入了 `order_fapiao_content` 参数,则将其值添加到 `info` 对象中,用于存储发票内容相关信息;如果没有传入则设置为空字符串,可能表示没有特定的发票内容或者留空待填写等情况。 + if (params.order_fapiao_content) { + info.order_fapiao_content = params.order_fapiao_content; + } else { + info.order_fapiao_content = ""; + } + + // 如果传入了 `consignee_addr` 参数,则将其值添加到 `info` 对象中,用于存储收件人地址信息;如果没有传入则设置为空字符串,可能表示没有填写收件人地址或者留空待补充等情况。 + if (params.consignee_addr) { + info.consignee_addr = params.consignee_addr; + } else { + info.consignee_addr = ""; + } + + // 如果传入了 `goods` 参数(可能是订单商品相关信息,例如商品列表等内容),则将其直接添加到 `info` 对象中,后续在创建订单等操作中可能会进一步处理这些商品信息。 + if (params.goods) { + info.goods = params.goods; + } + + // 设置订单的支付状态初始值为 `'0'`,表示未支付或默认支付状态,具体含义由业务逻辑决定,后续可能会根据实际支付情况进行更新。 + info.pay_status = '0'; + + // 如果传入了 `order_id`,意味着可能是更新订单操作,此时设置订单的创建时间为当前时间(通过获取当前日期时间并转换为时间戳,再除以 `1000`,可能是为了符合数据库存储的时间格式要求等情况),并添加到 `info` 对象中。 + if (params.order_id) info.create_time = (Date.parse(new Date()) / 1000); + + // 设置订单的更新时间为当前时间(同样进行时间戳转换操作),添加到 `info` 对象中,用于记录订单信息最后更新的时间点,方便后续查询、统计等业务操作使用。 + info.update_time = (Date.parse(new Date()) / 1000); + + // 如果所有参数验证和信息整理都顺利完成,则通过 `resolve` 方法将整理好的 `info` 对象传递出去,使得 `Promise` 状态变为已完成(成功),后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行后续操作。 + resolve(info); + }); } +// 定义 `doCreateOrder` 函数,用于在数据库中创建新的订单记录,接收经过验证和整理后的订单信息(`info`),以 `Promise` 的形式返回创建订单操作的结果(成功则包含创建后的订单相关信息,失败则返回错误信息)。 function doCreateOrder(info) { - return new Promise(function(resolve,reject) { - dao.create("OrderModel",_.clone(info),function(err,newOrder){ - if(err) return reject("创建订单失败"); - info.order = newOrder; - resolve(info); - }); - }); + // 返回一个新的 `Promise` 对象,在其内部执行创建订单的数据库操作,并通过 `resolve` 和 `reject` 控制 `Promise` 的状态。 + return new Promise(function (resolve, reject) { + // 调用 `dao` 模块的 `create` 方法来执行创建订单的数据库操作,第一个参数 `"OrderModel"` 表示要操作的数据模型(对应数据库中的订单数据表), + // 第二个参数使用 `_.clone(info)` 通过 `lodash` 库的克隆函数对传入的 `info` 对象进行克隆,避免在数据库操作过程中对原始数据造成意外修改,然后将克隆后的数据作为要插入数据库的订单信息, + // 第三个参数是一个回调函数,用于处理数据库插入操作完成后的结果情况,根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。 + dao.create("OrderModel", _.clone(info), function (err, newOrder) { + // 如果在创建订单的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库插入语句执行失败、违反数据约束等原因,则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示创建订单失败,让调用者知晓操作未成功及原因。 + if (err) return reject("创建订单失败"); + // 如果订单创建成功,将创建后的订单对象(`newOrder`)添加到传入的 `info` 对象的 `order` 属性中(这样可以将订单相关的更多信息整合在一起方便后续操作),然后通过 `resolve` 方法将包含订单信息的 `info` 对象传递出去, + // 使得 `Promise` 状态变为已完成(成功),后续可以通过 `.then` 方法获取并使用这个包含订单详细信息的 `info` 对象进行进一步操作,比如添加订单商品等操作。 + info.order = newOrder; + resolve(info); + }); + }); } +// 定义 `doCreateOrderGood` 函数,用于在数据库中创建订单商品记录,接收订单商品相关信息(`orderGood`),以 `Promise` 的形式返回创建订单商品操作的结果(成功则返回创建后的订单商品对象,失败则返回错误信息)。 function doCreateOrderGood(orderGood) { - return new Promise(function(resolve,reject) { - dao.create("OrderGoodModel",orderGood,function(err,newOrderGood){ - if(err) return reject("创建订单商品失败"); - resolve(newOrderGood); - }); - }); -} + // 返回一个新的 `Promise` 对象,在其内部执行创建订单商品的数据库操作,并通过 `resolve` 和 `reject` 控制 `Promise` 的状态。 + return new Promise(function (resolve, reject) { + // 调用 `dao` 模块的 `create` 方法来执行创建订单商品的数据库操作,第一个参数 `"OrderGoodModel"` 表示要操作的数据模型(对应数据库中的订单商品数据表), + // 第二个参数直接传入 `orderGood` 对象,它包含了要插入数据库的订单商品相关信息,比如商品名称、价格等信息,第三个参数是一个回调函数,用于处理数据库插入操作完成后的结果情况,根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。 + dao.create("OrderGoodModel", orderGood, function (err, newOrderGood) { + // 如果在创建订单商品的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库插入语句执行失败、违反数据约束等原因,则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示创建订单商品失败,让调用者知晓操作未成功及原因。 + if (err) return reject("创建订单商品失败"); + // 如果订单商品创建成功,则通过 `resolve` 方法将创建后的订单商品对象(`newOrderGood`)传递出去,使得 `Promise` 状态变为已完成(成功),后续可以通过 `.then` 方法获取并使用这个对象进行进一步操作,比如关联到对应的订单等操作。 + resolve(newOrderGood); + }); + }); +} +// 定义 `doAddOrderGoods` 函数,用于将多个订单商品信息添加到对应的订单中,接收包含订单和商品信息的对象(`info`),以 `Promise` 的形式返回添加商品操作完成后的结果(成功则返回包含完整订单及商品信息的对象,失败则返回错误信息)。 function doAddOrderGoods(info) { - - return new Promise(function(resolve,reject) { - - if(!info.order) return reject("订单对象未创建"); - - var orderGoods = info.goods; - - if(orderGoods && orderGoods.length > 0) { - var fns = []; - var goods_total_price = _.sum(_.map(orderGoods,"goods_price")); - - _(orderGoods).forEach(function(orderGood){ - orderGood.order_id = info.order.order_id; - orderGood.goods_total_price = goods_total_price; - fns.push(doCreateOrderGood(orderGood)); - }); - Promise.all(fns) - .then(function(results){ - info.order.goods = results; - resolve(info); - }) - .catch(function(error){ - if(error) return reject(error); - }); - - } else { - resolve(info); - } - }); + // 返回一个新的 `Promise` 对象,在其内部执行添加订单商品到订单的相关逻辑,并通过 `resolve` 和 `reject` 控制 `Promise` 的状态。 + return new Promise(function (resolve, reject) { + // 首先验证传入的 `info` 对象中是否包含已创建的订单对象(`order` 属性),如果不存在则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示订单对象未创建,因为后续操作需要基于已存在的订单来添加商品信息,所以订单对象必须存在。 + if (!info.order) return reject("订单对象未创建"); + + // 获取传入的 `info` 对象中的商品信息(`goods` 属性),它可能是一个包含多个订单商品对象的数组,每个对象包含商品的具体信息,比如商品价格、数量等,后续会遍历这个数组来逐个创建订单商品记录并关联到订单上。 + var orderGoods = info.goods; + + // 如果存在订单商品信息(即 `orderGoods` 不为空且长度大于 `0`),则进行以下添加订单商品的操作。 + if (orderGoods && orderGoods.length > 0) { + // 创建一个空数组 `fns`,用于存储多个创建订单商品的异步操作函数(每个函数返回一个 `Promise`),后续会使用 `Promise.all` 来并行执行这些异步操作,提高效率。 + var fns = []; + // 使用 `lodash` 的 `sum` 函数和 `map` 函数计算所有订单商品的总价格,`_.map` 函数先从每个订单商品对象中提取 `goods_price` 字段的值形成一个新的价格数组,然后 `_.sum` 函数对这个价格数组进行求和操作,得到商品总价格,方便后续记录和使用。 + var goods_total_price = _.sum(_.map(orderGoods, "goods_price")); + + // 使用 `lodash` 的 `forEach` 函数遍历订单商品数组 `orderGoods`,对于每个订单商品对象(`orderGood`)进行以下操作,将商品关联到对应的订单上并准备创建商品记录。 + _(orderGoods).forEach(function (orderGood) { + // 将当前订单商品对象的 `order_id` 属性设置为对应的订单的 `order_id`(从 `info.order.order_id` 获取),这样建立起订单商品与订单的关联关系,表明该商品属于哪个订单。 + orderGood.order_id = info.order.order_id; + // 将当前订单商品对象的 `goods_total_price` 属性设置为前面计算好的商品总价格,可能用于记录该订单下所有商品的总价等相关业务用途,具体含义由业务逻辑决定。 + orderGood.goods_total_price = goods_total_price; + // 将创建当前订单商品记录的异步操作函数(通过调用 `doCreateOrderGood` 函数返回的 `Promise`)添加到 `fns` 数组中,准备后续并行执行这些创建操作。 + fns.push(doCreateOrderGood(orderGood)); + }); + + // 使用 `Promise.all` 函数并行执行 `fns` 数组中所有的创建订单商品的异步操作 +// 以下是 `doAddOrderGoods` 函数剩余部分的注释 + +// `Promise.all` 会并行执行 `fns` 数组中的所有异步操作(即创建各个订单商品记录的 `Promise`),当所有异步操作都成功完成后, +// 会执行 `.then` 回调函数中的逻辑,将创建订单商品操作返回的结果数组(`results`,每个元素对应一个创建成功的订单商品对象) +// 设置为 `info.order` 对象的 `goods` 属性值,这样就将创建好的订单商品与对应的订单关联起来了,然后通过 `resolve` 方法将包含完整订单及商品信息的 `info` 对象传递出去, +// 使得 `Promise` 状态变为已完成(成功),表示添加订单商品到订单的操作成功完成,后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作。 +Promise.all(fns) +.then(function (results) { + info.order.goods = results; + resolve(info); +}) +// 如果在并行执行创建订单商品的异步操作过程中,有任何一个操作出现错误(例如某个商品记录创建失败),则会进入 `.catch` 回调函数, +// 这里直接将错误信息通过 `reject` 拒绝 `Promise`,并将错误信息返回给调用者,告知调用者添加订单商品操作出现问题及具体的错误原因。 +.catch(function (error) { + if (error) return reject(error); +}); + +// 如果没有订单商品信息(即 `orderGoods` 为空或长度为0),说明不需要添加订单商品,直接通过 `resolve` 方法将传入的 `info` 对象传递出去, +// 表示添加订单商品操作顺利完成(因为本身就没有商品需要添加),`Promise` 状态变为已完成(成功),后续可以继续进行其他相关操作。 +} else { + resolve(info); +} +}); } +// 定义 `doGetAllOrderGoods` 函数,用于获取指定订单的所有商品信息,接收包含订单相关信息的对象(`info`),以 `Promise` 的形式返回获取操作的结果(成功则返回包含订单及商品列表信息的对象,失败则返回错误信息)。 function doGetAllOrderGoods(info) { - return new Promise(function(resolve,reject) { - if(!info.order) return reject("订单对象未创建"); - - dao.list("OrderGoodModel",{"columns":{"order_id":info.order.order_id}},function(err,orderGoods){ - - - if(err) return reject("获取订单商品列表失败"); - - info.order.goods = orderGoods; - resolve(info); - }) - }); + return new Promise(function (resolve, reject) { + // 首先验证传入的 `info` 对象中是否包含已创建的订单对象(`order` 属性),如果不存在则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示订单对象未创建, + // 因为获取订单商品信息需要基于已存在的订单来查询对应的商品记录,所以订单对象必须存在才能进行后续操作。 + if (!info.order) return reject("订单对象未创建"); + + // 调用 `dao` 模块的 `list` 方法来获取指定订单的商品信息,第一个参数 `"OrderGoodModel"` 表示要操作的数据模型(对应数据库中的订单商品数据表), + // 第二个参数是一个对象,用于指定查询条件,这里通过 `{"columns":{"order_id":info.order.order_id}}` 设置只查询 `order_id` 与传入的订单对象的 `order_id` 匹配的商品记录, + // 即获取指定订单下的所有商品信息,第三个参数是一个回调函数,用于处理查询操作完成后的结果情况,根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。 + dao.list("OrderGoodModel", { "columns": { "order_id": info.order.order_id } }, function (err, orderGoods) { + // 如果在获取订单商品列表的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库查询语句执行失败、连接问题等原因,则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示获取订单商品列表失败,让调用者知晓操作未成功及原因。 + if (err) return reject("获取订单商品列表失败"); + + // 如果查询成功,将获取到的订单商品列表(`orderGoods`,是一个包含多个订单商品对象的数组)设置为 `info.order` 对象的 `goods` 属性值,这样就将获取到的商品信息关联到对应的订单上了, + // 然后通过 `resolve` 方法将包含完整订单及商品信息的 `info` 对象传递出去,使得 `Promise` 状态变为已完成(成功),后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作,比如展示订单详情等操作。 + info.order.goods = orderGoods; + resolve(info); + }) + }); } +// 定义 `doGetOrder` 函数,用于获取指定订单的详细信息,接收包含订单ID相关信息的对象(`info`),以 `Promise` 的形式返回获取操作的结果(成功则返回包含订单详细信息的对象,失败则返回错误信息)。 function doGetOrder(info) { - return new Promise(function(resolve,reject) { - dao.show("OrderModel",info.order_id,function(err,newOrder){ - - if(err) return reject("获取订单详情失败"); - if(!newOrder) return reject("订单ID不能存在"); - info.order = newOrder; - resolve(info); - }) - }); + return new Promise(function (resolve, reject) { + // 调用 `dao` 模块的 `show` 方法来获取指定订单的详细信息,第一个参数 `"OrderModel"` 表示要操作的数据模型(对应数据库中的订单数据表), + // 第二个参数传入 `info.order_id`,作为查询条件,用于查找数据库中对应 `order_id` 的订单记录,获取其详细信息,第三个参数是一个回调函数,用于处理查询操作完成后的结果情况,根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。 + dao.show("OrderModel", info.order_id, function (err, newOrder) { + // 如果在获取订单详情的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库查询语句执行失败、连接问题等原因,则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示获取订单详情失败,让调用者知晓操作未成功及原因。 + if (err) return reject("获取订单详情失败"); + // 如果查询结果中没有获取到对应的订单(`newOrder` 为 `null`),则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示订单ID不存在,因为没有找到对应订单记录说明传入的订单ID可能有误或者该订单不存在,告知调用者操作出现问题及原因。 + if (!newOrder) return reject("订单ID不能存在"); + // 如果成功获取到订单详情信息,将获取到的订单对象(`newOrder`)设置为 `info.order` 属性值,这样就将获取到的详细订单信息整合到传入的 `info` 对象中了, + // 然后通过 `resolve` 方法将包含订单详细信息的 `info` 对象传递出去,使得 `Promise` 状态变为已完成(成功),后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作,比如添加商品、更新订单等操作。 + info.order = newOrder; + resolve(info); + }) + }); } +// 定义 `doUpdateOrder` 函数,用于更新指定订单的信息,接收包含订单相关信息的对象(`info`),以 `Promise` 的形式返回更新操作的结果(成功则返回包含更新后订单详细信息的对象,失败则返回错误信息)。 function doUpdateOrder(info) { - return new Promise(function(resolve,reject) { - dao.update("OrderModel",info.order_id,_.clone(info),function(err,newOrder){ - if(err) return reject("更新失败"); - info.order = newOrder; - resolve(info); - }); - - }); -} - - -module.exports.createOrder = function(params,cb) { - doCheckOrderParams(params) - .then(doCreateOrder) - .then(doAddOrderGoods) - .then(function(info) { - cb(null,info.order); - }) - .catch(function(err) { - cb(err); - }); + return new Promise(function (resolve, reject) { + // 调用 `dao` 模块的 `update` 方法来执行更新订单信息的数据库操作,第一个参数 `"OrderModel"` 表示要操作的数据模型(对应数据库中的订单数据表), + // 第二个参数传入 `info.order_id`,用于准确找到数据库中要更新的订单记录,第三个参数使用 `_.clone(info)` 通过 `lodash` 库的克隆函数对传入的 `info` 对象进行克隆,避免在数据库操作过程中对原始数据造成意外修改,然后将克隆后的数据作为要更新的订单信息, + // 第四个参数是一个回调函数,用于处理更新操作完成后的结果情况,根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。 + dao.update("OrderModel", info.order_id, _.clone(info), function (err, newOrder) { + // 如果在更新订单信息的数据库操作过程中出现错误(`err` 不为 `null`),比如数据库更新语句执行失败、违反数据约束等原因,则通过 `reject` 拒绝 `Promise`,并返回错误信息,表示更新失败,让调用者知晓操作未成功及原因。 + if (err) return reject("更新失败"); + // 如果订单信息更新成功,将更新后的订单对象(`newOrder`)设置为 `info.order` 属性值,这样就将更新后的订单详细信息整合到传入的 `info` 对象中了, + // 然后通过 `resolve` 方法将包含更新后订单详细信息的 `info` 对象传递出去,使得 `Promise` 状态变为已完成(成功),后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作,比如获取更新后的订单商品信息等操作。 + info.order = newOrder; + resolve(info); + }); + + }); } +// 对外暴露 `createOrder` 函数,用于创建新的订单,接收订单相关参数(`params`)和一个回调函数(`cb`),在内部通过一系列异步操作(参数验证、创建订单、添加订单商品等)来完成订单创建流程, +// 最后通过回调函数将创建成功后的订单信息返回给调用者,若在创建过程中出现错误则通过回调函数返回相应的错误信息。 +module.exports.createOrder = function (params, cb) { + // 首先调用 `doCheckOrderParams` 函数对传入的订单参数进行合法性检查和预处理,该函数返回一个 `Promise`,如果参数验证通过则会传递处理好的订单信息,否则会返回错误信息拒绝 `Promise`。 + doCheckOrderParams(params) + // 使用 `.then` 方法链式调用 `doCreateOrder` 函数,当 `doCheckOrderParams` 成功完成 + // 链式调用 `doCreateOrder` 函数,在 `doCheckOrderParams` 函数对订单参数验证和预处理成功后(即 `Promise` 被成功 `resolve`), +// 会将处理好的订单信息作为参数传递给 `doCreateOrder` 函数,用于执行创建订单的数据库操作。 +.then(doCreateOrder) +// 继续链式调用 `doAddOrderGoods` 函数,当 `doCreateOrder` 函数成功创建订单(其返回的 `Promise` 被成功 `resolve`)后, +// 会将包含创建好的订单信息的对象传递给 `doAddOrderGoods` 函数,以便执行添加订单商品的相关操作,将商品与订单进行关联并创建商品记录等操作。 +.then(doAddOrderGoods) +// 当 `doAddOrderGoods` 函数执行完成且操作成功(返回的 `Promise` 被成功 `resolve`)后,会进入这个 `.then` 回调函数, +// 此时参数 `info` 包含了完整的订单及商品相关信息,通过回调函数 `cb` 将最终的订单对象(`info.order`)返回给调用者, +// 表示订单创建流程全部成功完成,调用者可以获取订单详细信息进行后续的业务操作,比如展示订单详情、进行订单状态更新等操作。 +.then(function (info) { + cb(null, info.order); +}) +// 如果在整个 `doCheckOrderParams` -> `doCreateOrder` -> `doAddOrderGoods` 的链式异步操作过程中,任何一个环节出现错误(即 `Promise` 被 `reject`), +// 都会进入这个 `.catch` 回调函数,直接将错误信息通过回调函数 `cb` 返回给调用者,告知调用者订单创建过程出现问题以及具体的错误原因,方便调用者进行相应的错误处理和提示给用户等操作。 +.catch(function (err) { + cb(err); +}); +} -module.exports.getAllOrders = function(params,cb){ - var conditions = {}; - if(!params.pagenum || params.pagenum <= 0) return cb("pagenum 参数错误"); - if(!params.pagesize || params.pagesize <= 0) return cb("pagesize 参数错误"); - conditions["columns"] = {}; - if(params.user_id) { - conditions["columns"]["user_id"] = params.user_id; - } - - if(params.pay_status) { - conditions["columns"]["pay_status"] = params.pay_status; - } - - if(params.is_send) { - if(params.is_send == 1) { - conditions["columns"]["is_send"] = '是'; - } else { - conditions["columns"]["is_send"] = '否'; - } - } - - if(params.order_fapiao_title) { - if(params.order_fapiao_title == 1) { - conditions["columns"]["order_fapiao_title"] = '个人'; - } else { - conditions["columns"]["order_fapiao_title"] = '公司'; - } - } - - if(params.order_fapiao_company) { - conditions["columns"]["order_fapiao_company"] = orm.like("%" + params.order_fapiao_company + "%"); - } - - if(params.order_fapiao_content) { - conditions["columns"]["order_fapiao_content"] = orm.like("%" + params.order_fapiao_content + "%"); - } - - if(params.consignee_addr) { - conditions["columns"]["consignee_addr"] = orm.like("%" + params.consignee_addr + "%"); - } - - dao.countByConditions("OrderModel",conditions,function(err,count){ - if(err) return cb(err); - pagesize = params.pagesize; - pagenum = params.pagenum; - pageCount = Math.ceil(count / pagesize); - offset = (pagenum - 1) * pagesize; - if(offset >= count) { - offset = count; - } - limit = pagesize; - - // 构建条件 - conditions["offset"] = offset; - conditions["limit"] = limit; - // conditions["only"] = - conditions["order"] = "-create_time"; - - - dao.list("OrderModel",conditions,function(err,orders){ - if(err) return cb(err); - var resultDta = {}; - resultDta["total"] = count; - resultDta["pagenum"] = pagenum; - resultDta["goods"] = _.map(orders,function(order){ - return order;//_.omit(order,); - }); - cb(err,resultDta); - }) - }); +// 对外暴露 `getAllOrders` 函数,用于获取满足特定条件的所有订单信息,接收查询相关参数(`params`)和一个回调函数(`cb`), +// 通过构建查询条件、分页处理等操作从数据库中获取订单数据,并将结果和相关统计信息通过回调函数返回给调用者,若操作过程中出现错误则返回相应错误信息。 +module.exports.getAllOrders = function (params, cb) { + // 创建一个空对象 `conditions`,用于存储后续构建的数据库查询条件,这些条件将用于筛选出符合要求的订单记录。 + var conditions = {}; + + // 验证传入的 `params.pagenum` 参数是否存在且大于 `0`,`pagenum` 通常表示当前页码,用于分页查询操作。 + // 如果不存在或者小于等于 `0`,则不符合分页查询的基本要求,直接通过回调函数 `cb` 返回错误信息,表示 `pagenum` 参数错误,告知调用者参数不符合要求。 + if (!params.pagenum || params.pagenum <= 0) return cb("pagenum 参数错误"); + + // 同样验证传入的 `params.pagesize` 参数是否存在且大于 `0`,`pagesize` 通常表示每页显示的记录数,也是分页查询的关键参数之一。 + // 如果不存在或者小于等于 `0`,则不符合分页查询的要求,通过回调函数 `cb` 返回错误信息,表示 `pagesize` 参数错误,告知调用者参数不符合要求。 + if (!params.pagesize || params.pagesize <= 0) return cb("pagesize 参数错误"); + + // 在 `conditions` 对象中初始化 `columns` 属性为一个空对象,`columns` 属性用于存放具体的查询条件字段和对应的值, + // 这些条件将构成数据库查询语句中的 `WHERE` 子句部分,用于精确筛选出需要的订单记录。 + conditions["columns"] = {}; + + // 如果传入的参数中包含 `user_id`,表示调用者希望根据用户ID来筛选订单,将 `user_id` 及其对应的值添加到 `conditions["columns"]` 对象中, + // 这样在数据库查询时就只会获取该用户ID对应的订单记录,方便按用户维度查询订单信息,具体业务含义取决于系统的用户与订单关联设计。 + if (params.user_id) { + conditions["columns"]["user_id"] = params.user_id; + } + + // 如果传入的参数中包含 `pay_status`,表示调用者希望根据订单的支付状态来筛选订单,将 `pay_status` 及其对应的值添加到 `conditions["columns"]` 对象中, + // 使得数据库查询只返回支付状态符合该条件的订单记录,例如可以查询已支付、未支付等不同支付状态的订单,具体支付状态的表示和业务逻辑由系统定义。 + if (params.pay_status) { + conditions["columns"]["pay_status"] = params.pay_status; + } + + // 根据传入的 `is_send` 参数来设置查询条件中的发货状态相关信息,用于筛选出发货状态符合要求的订单记录。 + // 如果 `is_send` 参数为 `1`,则将 `conditions["columns"]["is_send"]` 设置为 `'是'`,表示查询已发货的订单; + // 如果为其他值(包括未传入该参数的情况),则默认设置为 `'否'`,表示查询未发货的订单,具体发货状态的表示和业务含义由业务逻辑决定。 + if (params.is_send) { + if (params.is_send == 1) { + conditions["columns"]["is_send"] = '是'; + } else { + conditions["columns"]["is_send"] = '否'; + } + } + + // 根据传入的 `order_fapiao_title` 参数来设置查询条件中的发票抬头相关信息,用于筛选出发票抬头符合要求的订单记录。 + // 如果 `order_fapiao_title` 参数为 `1`,则将 `conditions["columns"]["order_fapiao_title"]` 设置为 `'个人'`; + // 如果为其他值(通常应该是对应 `'公司'` 的标识,具体由业务逻辑确定),则设置为 `'公司'`,以此来区分不同发票抬头类型的订单,满足按发票抬头查询订单的业务需求。 + if (params.order_fapiao_title) { + if (params.order_fapiao_title == 1) { + conditions["columns"]["order_fapiao_title"] = '个人'; + } else { + conditions["columns"]["order_fapiao_title"] = '公司'; + } + } + + // 如果传入的参数中包含 `order_fapiao_company`,则使用 `orm` 库提供的 `like` 方法构建模糊查询条件, + // 将 `order_fapiao_company` 及其对应的值(通过 `%` 包裹传入值,表示匹配包含该值的任意字符串)添加到 `conditions["columns"]` 对象中, + // 这样可以查询发票对应的公司名称包含指定内容的订单记录,方便在不完全知道公司全称等情况下进行模糊筛选,满足根据发票公司相关信息查找订单的业务场景。 + if (params.order_fapiao_company) { + conditions["columns"]["order_fapiao_company"] = orm.like("%" + params.order_fapiao_company + "%"); + } + + // 如果传入的参数中包含 `order_fapiao_content`,同样使用 `orm` 库的 `like` 方法构建模糊查询条件, + // 将 `order_fapiao_content` 及其对应的值(用 `%` 包裹传入值构建模糊匹配模式)添加到 `conditions["columns"]` 对象中, + // 以便查询发票内容包含指定内容的订单记录,满足按发票内容部分信息来查找订单的业务需求,例如查找包含特定服务或商品描述的发票对应的订单。 + if (params.order_fapiao_content) { + conditions["columns"]["order_fapiao_content"] = orm.like("%" + params.order_fapiao_content + "%"); + } + + // 如果传入的参数中包含 `consignee_addr`,使用 `orm` 库的 `like` 方法构建模糊查询条件, + // 将 `consignee_addr` 及其对应的值(用 `%` 包裹传入值形成模糊匹配模式)添加到 `conditions["columns"]` 对象中, + // 这样就能查询收件人地址包含指定内容的订单记录,方便在只记得部分地址信息等情况下筛选订单,满足按收件人地址部分内容查找订单的业务场景。 + if (params.consignee_addr) { + conditions["columns"]["consignee_addr"] = orm.like("%" + params.consignee_addr + "%"); + } + + // 调用 `dao` 模块的 `countByConditions` 方法来统计满足前面构建的查询条件的订单记录总数, + // 第一个参数 `"OrderModel"` 表示要操作的数据模型,即对应数据库中的订单数据表,明确从哪个表中统计记录数量; + // 第二个参数传入 `conditions` 对象作为查询条件,告知数据库按照这些条件去筛选并统计符合要求的订单记录数量; + // 第三个参数是一个回调函数,用于处理统计操作完成后的结果情况,根据操作是否成功返回相应的信息给回调函数 `cb`,若成功则返回统计的记录总数,用于后续的分页计算等操作。 + dao.countByConditions("OrderModel", conditions, function (err, count) { + // 如果在统计订单记录总数的过程中出现错误(`err` 不为 `null`),比如数据库查询语句执行失败、连接问题等原因, + // 则直接通过回调函数 `cb` 返回错误信息给调用者,表示操作出现问题及具体的错误原因,告知调用者无法获取订单总数进行分页操作。 + if (err) return cb(err); + + // 获取传入的每页显示记录数参数 `params.pagesize`,赋值给变量 `pagesize`,方便后续在分页计算等操作中使用该值,保持代码的可读性。 + pagesize = params.pagesize; + // 获取传入的当前页码参数 `params.pagenum`,赋值给变量 `pagenum`,同样用于后续的分页相关计算和操作,使代码逻辑更清晰。 + pagenum = params.pagenum; + + // 计算总页数,通过将满足条件的订单记录总数(`count`)除以每页显示记录数(`pagesize`),然后向上取整(使用 `Math.ceil` 函数)得到总页数, + // 这个总页数用于判断分页情况以及限制页码范围等操作,确保分页查询的合理性和完整性。 + pageCount = Math.ceil(count / pagesize); + + // 计算分页查询的偏移量(`offset`),通过公式 `(当前页码 - 1) * 每页显示记录数` 来确定从哪条记录开始查询, + // 例如当前页码为 `1` 时,偏移量为 `0`,表示从第一条记录开始查询;页码为 `2` 时,偏移量为 `pagesize`,表示从第 `pagesize + 1` 条记录开始查询,以此类推。 + offset = (pagenum - 1) * pagesize; + + // 如果计算出的偏移量大于等于记录总数(`count`),说明传入的页码可能超出了合理范围(比如总共有 `100` 条记录,每页显示 `10` 条,传入页码 `11` 就会出现这种情况), + // 此时将偏移量设置为记录总数,避免查询出现错误,相当于查询最后一页(可能不满一页的情况)的数据,保证分页查询的健壮性。 + if (offset >= count) { + offset = count; + } + + // 获取每页显示记录数 `pagesize`,赋值给变量 `limit`,在后续构建数据库查询条件时用于指定每页获取的记录数量,确保分页查询按设定的每页数量返回数据。 + limit = pagesize; + + // 在 `conditions` 对象中添加 `offset` 属性,并将前面计算好的偏移量赋值给它,用于告知数据库查询从哪条记录开始获取,构建分页查询的偏移量条件,实现分页功能。 + conditions["offset"] = offset; + + // 在 `conditions` 对象中添加 `limit` 属性,并将每页显示记录数(`limit`)赋值给它,用于告知数据库每次查询获取的记录数量上限,与 `offset` 配合实现分页查询,获取指定页的数据。 + conditions["limit"] = limit; + + // 这里原本可能是要设置 `conditions["only"]` 的相关内容(可能用于指定只查询某些特定字段等操作,但代码中未完整实现),暂时保留注释状态,可能后续需要根据业务需求补充完善。 + // conditions["only"] = + + // 在 `conditions` 对象中添加 `order` 属性,并设置值为 `"-create_time"`,用于指定数据库查询结果的排序方式, + // 这里表示按照订单创建时间倒序排列(`-` 号表示倒序,`create_time` 为订单创建时间字段),使得最新创建的订单排在前面,方便展示最新的数据等业务场景使用。 + conditions["order"] = "-create_time"; + + // 调用 `dao` 模块的 `list` 方法来执行实际的数据库查询操作,获取满足前面构建的所有条件(包括筛选条件、分页条件、排序条件等)的订单记录列表, + // 第一个参数 `"OrderModel"` 明确要操作的数据模型,即从订单数据表中查询数据; + // 第二个参数传入 `conditions` 对象,包含了完整的查询条件信息,告知数据库如何筛选、分页以及排序数据; + // 第三个参数是一个回调函数,用于处理查询操作完成后的结果情况,根据操作是否成功返回相应的信息给回调函数 `cb`,若成功则返回查询到的订单记录列表以及相关统计信息,供调用者进行后续的业务处理。 + dao.list("OrderModel", conditions, function (err, orders) { + // 如果在查询订单记录列表的过程中出现错误(`err` 不为 `null`),比如数据库查询语句执行失败、连接问题等原因, + // 则直接通过回调函数 `cb` 返回错误信息给调用者,表示操作出现问题及具体的错误原因,告知调用者无法获取订单列表数据。 + if (err) return cb(err); + + // 创建一个空对象 `resultDta`,用于存储最终要返回给调用者的结果信息,包含订单记录总数、当前页码以及订单记录列表等内容,方便调用者统一获取和处理查询结果。 + var resultDta = {}; + + // 将前面统计得到的满足条件的订单记录总数(`count`)赋值给 `resultDta` 对象的 `total` 属性,方便调用者获取总的订单数量信息,例如用于展示分页导航中的总记录数等功能。 + resultDta["total"] = count; + + // 将传入的当前页码参数(`params.pagenum`)赋值给 `resultDta` 对象的 `pagenum` 属性,方便调用者知晓当前查询的是第几页数据,用于分页相关的展示和逻辑处理。 + resultDta["pagenum"] = pagenum; + + // 使用 `lodash` 库的 `map` 函数遍历查询到的订单记录列表(`orders`),对每个订单记录(`order`)进行处理, + // 这里暂时只是直接返回每个订单记录(后续可能可以根据业务需求进一步处理订单记录,比如省略某些敏感字段等,代码中 `_.omit(order,)` 可能就是预留的这种处理方式但未完整实现), + // 将处理后的订单记录数组赋值给 `resultDta` 对象的 `goods` 属性(这里 `goods` 命名可能不太准确,更合适的可能是 `orders` 之类的表示订单列表的名称,具体要根据业务逻辑确定),方便调用者获取具体的订单记录数据进行展示、分析等操作。 + resultDta["goods"] = _.map(orders, function (order) { + return order; // _.omit(order,); + }); + + // 将包含订单记录总数、当前页码以及订单记录列表等完整信息的 `resultDta` 对象通过回调函数 `cb` 返回给调用者, + // 表示查询操作成功完成,调用者可以根据这些信息进行后续的业务处理,比如在前端页面展示订单列表、进行分页导航等操作。 + cb(err, resultDta); + }) + }); } -module.exports.getOrder = function(orderId,cb) { - if(!orderId) return cb("用户ID不能为空"); - if(isNaN(parseInt(orderId))) return cb("用户ID必须是数字"); - - doGetOrder({"order_id":orderId}) - .then(doGetAllOrderGoods) - .then(function(info){ - cb(null,info.order); - }) - .catch(function(err) { - cb(err); - }); +// 对外暴露 `getOrder` 函数,用于获取指定订单的详细信息,接收订单ID(`orderId`)和一个回调函数(`cb`), +// 通过一系列异步操作(先获取订单基本信息,再获取对应的订单商品信息)来整合完整的订单详情,最后通过回调函数返回给调用者,若出现错误则返回相应错误信息。 +module.exports.getOrder = function (orderId, cb) { + // 首先验证传入的订单ID(`orderId`)是否存在,如果不存在则通过回调函数 `cb` 返回错误信息,表示用户ID不能为空, + // 这里可能是表述上的小问题,更准确应该是订单ID不能为空,因为没有订单ID无法确定要获取哪个订单的详细信息,告知调用者参数不符合要求。 + if (!orderId) return cb("用户ID不能为空"); + + // 进一步验证订单ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则通过回调函数 `cb` 返回错误信息,表示用户ID必须是数字, + // 因为通常在数据库中订单ID是以数字形式存储和使用的,需要传入合法的数字类型的订单ID才能准确查询到对应的订单记录,告知调用者参数不符合要求。 + if (isNaN(parseInt(orderId))) return cb("用户ID必须是数字"); + + // 调用 `doGetOrder` 函数,传入包含订单ID的对象(`{"order_id":orderId}`),用于获取指定订单的基本信息,`doGetOrder` 函数返回一个 `Promise + // 将传入的 `orderId` 参数赋值给 `params` 对象中的 `order_id` 属性,这样 `params` 对象就包含了要更新的订单的标识信息, +// 后续在进行订单参数验证等操作时,可以基于这个完整的 `params` 对象来处理,确保更新操作针对正确的订单进行。 +params["order_id"] = orderId; + +// 调用 `doCheckOrderParams` 函数对包含了订单ID以及其他可能的更新参数(在 `params` 对象中)进行合法性检查和预处理, +// 该函数返回一个 `Promise`,若参数验证通过会传递处理好的订单信息,若验证不通过则会返回相应的错误信息拒绝 `Promise`。 +doCheckOrderParams(params) + +// 使用 `.then` 方法链式调用 `doUpdateOrder` 函数,当 `doCheckOrderParams` 函数成功完成(即参数验证通过并整理好信息)后, +// 会将处理后的包含订单相关信息的对象传递给 `doUpdateOrder` 函数,由它执行更新订单信息的数据库操作, +// `doUpdateOrder` 函数同样返回一个 `Promise`,成功则包含更新后的订单详细信息,失败则返回错误信息。 +.then(doUpdateOrder) + +// 继续链式调用 `doGetAllOrderGoods` 函数,当 `doUpdateOrder` 成功更新订单信息后,会将包含更新后订单信息的对象传递给 `doGetAllOrderGoods` 函数, +// 由它执行获取该订单所有商品信息的操作,`doGetAllOrderGoods` 函数返回的 `Promise` 成功则返回包含完整订单及商品信息的对象,失败则返回错误信息。 +.then(doGetAllOrderGoods) + +// 当 `doGetAllOrderGoods` 函数执行完成且操作成功(返回的 `Promise` 被成功 `resolve`)后,会进入这个 `.then` 回调函数, +// 此时参数 `info` 包含了完整的更新后的订单及商品相关信息,通过回调函数 `cb` 将最终的订单对象(`info.order`)返回给调用者, +// 表示订单更新流程全部成功完成,调用者可以获取更新后的订单详细信息进行后续的业务操作,比如展示最新的订单详情、根据新的订单状态进行相应处理等操作。 +.then(function (info) { + cb(null, info.order); +}) + +// 如果在整个 `doCheckOrderParams` -> `doUpdateOrder` -> `doGetAllOrderGoods` 的链式异步操作过程中,任何一个环节出现错误(即 `Promise` 被 `reject`), +// 都会进入这个 `.catch` 回调函数,直接将错误信息通过回调函数 `cb` 返回给调用者,告知调用者订单更新过程出现问题以及具体的错误原因,方便调用者进行相应的错误处理和提示给用户等操作。 +.catch(function (err) { + cb(err); +}); } - -module.exports.updateOrder = function(orderId,params,cb) { - if(!orderId) return cb("用户ID不能为空"); - if(isNaN(parseInt(orderId))) return cb("用户ID必须是数字"); - params["order_id"] = orderId; - doCheckOrderParams(params) - .then(doUpdateOrder) - .then(doGetAllOrderGoods) - .then(function(info){ - cb(null,info.order); - }) - .catch(function(err) { - cb(err); - }); - -} \ No newline at end of file +//这段代码整体实现了更新订单信息的功能逻辑,通过对传入参数的验证、订单信息更新以及获取更新后订 +//单商品信息等一系列步骤,保证了订单更新操作的完整性和准确性,并且通过回调函数将最终结果或错误 +//信息返回给调用者,方便在外部进行相应的业务处理和错误提示。 \ No newline at end of file diff --git a/services/ReportsService.js b/services/ReportsService.js index 40040e3..2054509 100644 --- a/services/ReportsService.js +++ b/services/ReportsService.js @@ -1,121 +1,176 @@ +// 引入lodash库,它提供了很多实用的函数来方便地处理JavaScript中的数据,例如对数组、对象进行操作等,后续代码中会多次使用到它提供的相关功能。 var _ = require('lodash'); +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,像拼接、解析文件路径等,这里用于准确地引入自定义的 `dao/DAO` 模块所在的文件位置,该模块大概率用于数据库操作相关的功能封装。 var path = require("path"); -var dao = require(path.join(process.cwd(),"dao/DAO")); +// 引入自定义的 `DAO` 模块,通过拼接当前工作目录(`process.cwd()`)和相对路径的方式找到并引入该模块,这个模块应该封装了通用的数据访问操作方法,例如查询、插入、更新等操作,具体操作的数据表等信息会在调用相应方法时传入。 +var dao = require(path.join(process.cwd(), "dao/DAO")); +// 定义 `reportOne` 函数,该函数主要用于获取并处理 `ReportOneModel` 相关的报表数据,最终将处理好的数据以特定格式通过回调函数返回给调用者。 function reportOne(cb) { - dao.list("ReportOneModel",null,function(err,result){ - if(err) return cb("获取报表数据失败"); - var areaKeyResult = {}; - var areaKeys = _.union(_.map(result,"rp1_area")); - var dateKeys = _.union(_.map(result,function(record){ - str = record["rp1_date"].getFullYear() + "-" + (record["rp1_date"].getMonth() + 1) + "-" + record["rp1_date"].getDate() - console.log(str); - return str; - })); - for(var idx in result) { - var record = result[idx]; - var dateKey = record["rp1_date"].getFullYear() + "-" + (record["rp1_date"].getMonth() + 1) + "-" + record["rp1_date"].getDate(); - if(!areaKeyResult[record["rp1_area"]]) { - areaKeyResult[record["rp1_area"]] = {}; - } - areaKeyResult[record["rp1_area"]][dateKey] = record; - } - // 格式输出 - var series = []; - _(areaKeys).forEach(function(areaKey){ - var data = [] - - _(dateKeys).forEach(function(dateKey){ - console.log("areaKey:" + areaKey + "," + "dateKey:" + dateKey); - if(areaKeyResult[areaKey][dateKey]) { - data.push(areaKeyResult[areaKey][dateKey]["rp1_user_count"]); - } else { - data.push(0); - } - }) - series.push({ - name:areaKey, - type:'line', - stack: '总量', - areaStyle: {normal: {}}, - data:data - }) - }); - data = { - legend: { - data : areaKeys - }, - yAxis : [ - { - type : 'value' - } - ], - xAxis : [ - { - data :dateKeys - } - ], - series : series - }; - - - cb(null,data); - }); + // 调用 `dao` 模块的 `list` 方法来获取 `ReportOneModel` 对应的数据,这里第一个参数 `"ReportOneModel"` 可能是表示要查询的数据模型名称,对应数据库中的具体数据表, + // 第二个参数 `null` 可能表示查询条件(这里暂时没有传递具体查询条件,可能是获取该表的所有数据的意思),第三个参数是一个回调函数,用于处理查询操作完成后的结果情况。 + dao.list("ReportOneModel", null, function (err, result) { + // 如果在获取报表数据的过程中出现错误(`err` 不为 `null`),比如数据库查询失败(可能是连接问题、权限不足、表不存在等原因),则直接通过回调函数 `cb` 返回错误信息,表示获取报表数据失败,让调用者知晓操作出现问题及原因。 + if (err) return cb("获取报表数据失败"); + + // 创建一个空对象 `areaKeyResult`,用于后续按照区域(area)和日期(date)来存储和整理报表数据,以方便构建最终需要返回的格式化数据结构。 + var areaKeyResult = {}; + + // 使用 `lodash` 的 `union` 和 `map` 函数来获取报表数据中所有不重复的区域信息(`rp1_area` 字段),将其整理为一个数组并赋值给 `areaKeys`。 + // `_.map` 函数用于从 `result` 数组中的每个对象提取 `rp1_area` 字段的值,形成一个新的数组,然后 `_.union` 函数对这个新数组进行去重操作,得到唯一的区域值数组。 + var areaKeys = _.union(_.map(result, "rp1_area")); + + // 使用 `lodash` 的 `union` 函数结合一个匿名函数来获取报表数据中所有不重复的日期信息(经过格式化后的日期字符串),并赋值给 `dateKeys`。 + // 匿名函数内先将日期对象(`rp1_date` 字段)格式化为 `年-月-日` 的字符串形式,然后返回该字符串,`_.union` 函数对这些格式化后的日期字符串进行去重操作,得到唯一的日期值数组。 + var dateKeys = _.union(_.map(result, function (record) { + str = record["rp1_date"].getFullYear() + "-" + (record["rp1_date"].getMonth() + 1) + "-" + record["rp1_date"].getDate(); + console.log(str); + return str; + })); + + // 遍历获取到的报表数据 `result`,通过索引 `idx` 来访问每个数据记录对象,进行后续的数据整理操作,将数据按照区域和日期的维度进行重新组织。 + for (var idx in result) { + var record = result[idx]; + var dateKey = record["rp1_date"].getFullYear() + "-" + (record["rp1_date"].getMonth() + 1) + "-" + record["rp1_date"].getDate(); + // 如果 `areaKeyResult` 对象中还不存在当前记录的区域键(`record["rp1_area"]`),则创建一个以该区域为键的空对象,用于后续存储该区域下不同日期的数据。 + if (!areaKeyResult[record["rp1_area"]]) { + areaKeyResult[record["rp1_area"]] = {}; + } + // 将当前记录按照日期键(`dateKey`)存储到对应的区域对象下,这样就构建了一个以区域为一级键,日期为二级键,对应报表记录为值的嵌套对象结构,方便后续按区域和日期查找数据。 + areaKeyResult[record["rp1_area"]][dateKey] = record; + } + + // 格式输出 + // 创建一个空数组 `series`,用于存储最终要返回的数据格式中的系列(series)信息,每个元素代表不同区域的数据系列,在图表展示等场景中通常对应不同的线条或图形。 + var series = []; + + // 使用 `lodash` 的 `forEach` 函数遍历 `areaKeys` 数组,对于每个区域键(`areaKey`)进行以下操作,构建每个区域对应的图表数据系列信息。 + _(areaKeys).forEach(function (areaKey) { + var data = []; + + // 使用 `lodash` 的 `forEach` 函数遍历 `dateKeys` 数组,对于每个日期键(`dateKey`)进行以下操作,构建当前区域在不同日期下的数据点信息。 + _(dateKeys).forEach(function (dateKey) { + console.log("areaKey:" + areaKey + "," + "dateKey:" + dateKey); + // 如果在 `areaKeyResult` 中根据当前区域键(`areaKey`)和日期键(`dateKey`)能找到对应的报表记录,则将该记录中的用户数量(`rp1_user_count` 字段)添加到 `data` 数组中, + // 表示该区域在该日期下的用户数量数据点;如果找不到对应的记录,则添加 `0`,表示该日期下该区域没有相应的数据(可能是缺失或未统计等情况)。 + if (areaKeyResult[areaKey][dateKey]) { + data.push(areaKeyResult[areaKey][dateKey]["rp1_user_count"]); + } else { + data.push(0); + } + }) + + // 将构建好的当前区域的数据系列信息(包含区域名称、图表类型、样式以及数据点数组等)添加到 `series` 数组中,完成一个区域的数据系列构建。 + series.push({ + name: areaKey, + type: 'line', + stack: '总量', + areaStyle: { normal: {} }, + data: data + }) + }); + + // 构建最终要返回的数据对象,包含图例(legend)、y轴(yAxis)、x轴(xAxis)以及系列(series)等信息,符合常见的图表数据格式要求,用于在前端图表库等场景中进行展示。 + data = { + legend: { + data: areaKeys + }, + yAxis: [ + { + type: 'value' + } + ], + xAxis: [ + { + data: dateKeys + } + ], + series: series + }; + + // 将整理好的最终数据对象通过回调函数 `cb` 返回给调用者,表示 `reportOne` 函数成功获取并处理好了报表数据,调用者可以根据这个数据格式进行后续的操作,比如传递给前端进行图表渲染展示等。 + cb(null, data); + }); } +// 定义 `reportTwo` 函数,该函数用于获取并处理 `ReportTwoModel` 相关的报表数据,将数据按照日期进行整理后通过回调函数返回给调用者。 function reportTwo(cb) { - dao.list("ReportTwoModel",null,function(err,result){ - if(err) return cb("获取报表数据失败"); - var dateKeyResult = {}; - for(var idx in result) { - var record = result[idx]; - var dateKey = record["rp2_date"].getFullYear() + "-" + (record["rp2_date"].getMonth() + 1) + "-" + record["rp2_date"].getDate(); - if(!dateKeyResult[dateKey]) { - dateKeyResult[dateKey] = []; - } - dateKeyResult[dateKey].push(record); - } - cb(null,dateKeyResult); - }); + // 调用 `dao` 模块的 `list` 方法来获取 `ReportTwoModel` 对应的数据,参数含义与 `reportOne` 函数中调用时类似,第一个参数指定要查询的数据表对应的模型名称,第二个参数为查询条件(这里是 `null`,获取全部数据),第三个参数处理查询结果。 + dao.list("ReportTwoModel", null, function (err, result) { + // 如果在获取报表数据过程中出现错误(`err` 不为 `null`),比如数据库查询失败,就通过回调函数 `cb` 返回错误信息,表示获取报表数据失败,让调用者知晓操作出现问题及原因。 + if (err) return cb("获取报表数据失败"); + + // 创建一个空对象 `dateKeyResult`,用于按照日期来存储和整理报表数据,后续会将相同日期的数据记录存储在以日期为键的数组中。 + var dateKeyResult = {}; + + // 遍历获取到的报表数据 `result`,通过索引 `idx` 来访问每个数据记录对象,进行后续的数据整理操作,将数据按照日期维度进行重新组织。 + for (var idx in result) { + var record = result[idx]; + var dateKey = record["rp2_date"].getFullYear() + "-" + (record["rp2_date"].getMonth() + 1) + "-" + record["rp2_date"].getDate(); + // 如果 `dateKeyResult` 对象中还不存在当前记录的日期键(`dateKey`),则创建一个以该日期为键的空数组,用于存储该日期下的所有报表记录。 + if (!dateKeyResult[dateKey]) { + dateKeyResult[dateKey] = []; + } + // 将当前报表记录添加到对应日期键的数组中,这样就构建了一个以日期为键,对应日期下所有报表记录为值的对象结构,方便后续按日期查找和处理数据。 + dateKeyResult[dateKey].push(record); + } + + // 将整理好的按照日期分组的报表数据对象通过回调函数 `cb` 返回给调用者,表示 `reportTwo` 函数成功获取并整理好了报表数据,调用者可以根据这个数据结构进行后续的操作,比如进一步统计分析每个日期下的数据情况等。 + cb(null, dateKeyResult); + }); } +// 定义 `reportThree` 函数,目前该函数为空,可能后续会用于实现获取和处理第三种报表数据相关的逻辑,暂时没有具体功能实现代码。 function reportThree(cb) { } +// 定义 `reportFour` 函数,目前该函数为空,可能后续会用于实现获取和处理第四种报表数据相关的逻辑,暂时没有具体功能实现代码。 function reportFour(cb) { } -module.exports.reports = function(typeid,cb) { - console.log(typeid); - switch (parseInt(typeid)) { - case 1: - reportOne(function(err,result){ - if(err) return cb(err); - cb(null,result); - }); - break; - case 2: - reportTwo(function(err,result){ - if(err) return cb(err); - cb(null,result); - }); - break; - case 3: - reportThree(function(err,result){ - if(err) return cb(err); - cb(null,result); - }); - break; - case 4: - reportFour(function(err,result){ - if(err) return cb(err); - cb(null,result); - }); - break; - default: - cb("类型出错"); - break; - } -} \ No newline at end of file +// 对外暴露 `reports` 函数,根据传入的报表类型ID(`typeid`)来调用相应的报表处理函数(如 `reportOne`、`reportTwo` 等),并将处理结果通过回调函数返回给调用者,若类型ID不合法则返回错误信息。 +module.exports.reports = function (typeid, cb) { + console.log(typeid); + // 将传入的报表类型ID(`typeid`)转换为整数类型,然后通过 `switch` 语句根据不同的类型值来执行相应的操作,调用对应的报表处理函数来获取和处理报表数据。 + switch (parseInt(typeid)) { + case 1: + // 如果类型ID为1,则调用 `reportOne` 函数来获取和处理第一种报表数据,`reportOne` 函数内部会进行数据库查询、数据整理等操作,最终返回处理好的数据, + // 这里通过回调函数接收 `reportOne` 函数返回的数据,如果出现错误(`err` 不为 `null`),则直接将错误信息通过外层的回调函数 `cb` 返回给调用者; + // 如果没有错误,则将 `reportOne` 函数返回的处理后的数据通过 `cb` 返回给调用者。 + reportOne(function (err, result) { + if (err) return cb(err); + cb(null, result); + }); + break; + case 2: + // 如果类型ID为2,则调用 `reportTwo` 函数来获取和处理第二种报表数据,处理逻辑与 `reportOne` 类似,根据 `reportTwo` 函数的执行结果通过回调函数 `cb` 返回相应的信息给调用者。 + reportTwo(function (err, result) { + if (err) return cb(err); + cb(null, result); + }); + break; + case 3: + // 如果类型ID为3,则调用 `reportThree` 函数来获取和处理第三种报表数据,目前该函数为空,后续可补充具体逻辑来实现相应功能并通过回调函数返回结果给调用者。 + reportThree(function (err, result) { + if (err) return cb(err); + cb(null, result); + }); + break; + case 4: + // 如果类型ID为4,则调用 `reportFour` 函数来获取和处理第四种报表数据,目前该函数为空,后续可补充具体逻辑来实现相应功能并通过回调函数返回结果给调用者。 + reportFour(function (err, result) { + if (err) return cb(err); + cb(null, result); + }); + break; + default: + // 如果传入的报表类型ID不属于1 - 4的范围,则通过回调函数 `cb` 返回错误信息,表示类型出错,告知调用者传入的报表类型ID不符合要求。 + cb("类型出错"); + break; + } +} +//这段代码整体构建了一个报表数据获取与处理的模块,根据不同的报表类型 ID,调用相应的内部函数来获 +//取特定报表模型的数据,并进行整理和格式化,最终通过回调函数将处理好的数据返回给调用者,方便 +//在其他模块中进一步使用这些报表数据进行展示、分析等操作,适用于有报表相关业务需求的应用开发场景。 \ No newline at end of file diff --git a/services/RightService.js b/services/RightService.js index 213964c..ae1232f 100644 --- a/services/RightService.js +++ b/services/RightService.js @@ -1,94 +1,118 @@ +// 引入lodash库,它是一个功能强大的JavaScript工具库,提供了许多便捷的函数来处理各种数据类型,比如数组、对象的操作等,在后续代码中用于数据的转换、整理等操作。 var _ = require('lodash'); +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,像拼接、解析文件路径等,这里用于准确地引入自定义的 `dao/PermissionAPIDAO` 模块所在的文件位置。 var path = require("path"); -var dao = require(path.join(process.cwd(),"dao/PermissionAPIDAO")); - +// 引入自定义的 `PermissionAPIDAO` 模块,通过拼接当前工作目录(`process.cwd()`)和相对路径的方式找到并引入该模块,推测这个模块封装了与权限数据访问相关的操作,比如查询权限数据等功能。 +var dao = require(path.join(process.cwd(), "dao/PermissionAPIDAO")); // 获取所有权限 -module.exports.getAllRights = function(type,cb) { - if(!type || (type != "list" && type != "tree")) { - cb("参数类型错误"); - } - - - dao.list(function(err,permissions){ - if(err) return cb("获取权限数据失败"); - - if(type == "list") { - var result = []; - for(idx in permissions) { - permission = permissions[idx]; - result.push({ - "id" : permission.ps_id, - "authName" : permission.ps_name, - "level" : permission.ps_level, - "pid" : permission.ps_pid, - "path": permission.ps_api_path - }); - } - cb(null,result); - } else { - var keyCategories = _.keyBy(permissions,'ps_id'); - - // 显示一级 - var permissionsResult = {}; - - // 处理一级菜单 - for(idx in permissions) { - permission = permissions[idx]; - if(permission && permission.ps_level == 0) { - permissionsResult[permission.ps_id] = { - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path, - "pid" : permission.ps_pid, - "children":[] - }; - } - } +// 此函数用于获取系统中的所有权限信息,并根据传入的参数类型,以不同的格式返回权限数据,通过回调函数将结果或错误信息传递给调用者。 +module.exports.getAllRights = function (type, cb) { + // 首先对传入的参数 `type` 进行验证,如果 `type` 不存在(即 `!type`),或者 `type` 的值既不是 `"list"` 也不是 `"tree"`,说明参数不符合要求, + // 则直接通过回调函数 `cb` 返回错误信息,表示参数类型错误,告知调用者传入的参数不符合函数预期的格式要求。 + if (!type || (type!= "list" && type!= "tree")) { + cb("参数类型错误"); + } - // 临时存储二级返回结果 - tmpResult = {}; - // 处理二级菜单 - for(idx in permissions) { - permission = permissions[idx]; - if(permission && permission.ps_level == 1) { + // 调用 `dao` 模块的 `list` 方法来获取权限数据,这个方法内部大概率会执行数据库查询等操作来获取所有的权限记录信息,它是一个异步操作,通过回调函数来处理获取数据后的结果情况。 + dao.list(function (err, permissions) { + // 如果在获取权限数据的过程中出现错误(`err` 不为 `null`),比如数据库查询失败(可能是连接问题、权限不足、表不存在等原因),则直接通过回调函数 `cb` 返回错误信息,表示获取权限数据失败,让调用者知晓操作出现问题及原因。 + if (err) return cb("获取权限数据失败"); - parentPermissionResult = permissionsResult[permission.ps_pid]; - if(parentPermissionResult) { - tmpResult[permission.ps_id] = { - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path, - "pid" : permission.ps_pid, - "children":[] - } - parentPermissionResult.children.push(tmpResult[permission.ps_id]); - } - } - } + // 根据传入的参数 `type` 的值来决定如何处理和返回权限数据。如果 `type` 的值为 `"list"`,表示要以列表形式返回权限数据。 + if (type == "list") { + // 创建一个空数组 `result`,用于存储整理后的权限数据,后续会将每个权限的相关信息按照特定格式添加到这个数组中。 + var result = []; + // 遍历获取到的权限数据数组 `permissions`,通过索引 `idx` 来访问每个权限信息对象,进行后续的处理,将每个权限的关键信息提取出来并整理成指定格式后添加到 `result` 数组中。 + for (idx in permissions) { + permission = permissions[idx]; + result.push({ + "id": permission.ps_id, + "authName": permission.ps_name, + "level": permission.ps_level, + "pid": permission.ps_pid, + "path": permission.ps_api_path + }); + } + // 将整理好的权限数据数组 `result` 通过回调函数 `cb` 返回给调用者,表示获取权限数据成功且以列表形式返回了相应的数据,调用者可以根据这个格式的数据进行后续的业务处理,比如展示权限列表等操作。 + cb(null, result); + } else { + // 如果 `type` 的值为 `"tree"`,表示要以树形结构返回权限数据,更直观地展示权限之间的层级关系,以下是构建树形结构权限数据的相关逻辑。 - // 处理三级菜单 - for(idx in permissions) { - permission = permissions[idx]; - if(permission && permission.ps_level == 2) { + // 使用 `lodash` 的 `keyBy` 函数,将获取到的权限数据(`permissions`)按照权限ID(`ps_id`)进行转换,生成一个以权限ID为键,对应权限详细信息为值的对象结构, + // 方便后续通过权限ID快速查找对应的权限详情,在构建树形结构以及关联不同层级权限时能更高效地获取所需信息。 + var keyCategories = _.keyBy(permissions, 'ps_id'); - parentPermissionResult = tmpResult[permission.ps_pid]; + // 显示一级 + // 创建一个空对象 `permissionsResult`,用于存储最终整理好的树形结构权限数据,它将以一级权限的权限ID为键,对应权限的详细信息(包含子权限信息等)为值进行存储,作为树形结构的顶层节点。 + var permissionsResult = {}; - if(parentPermissionResult) { - - parentPermissionResult.children.push({ - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path, - "pid" : permission.ps_pid + "," + keyCategories[permission.ps_pid].ps_pid - }); - } - } - } + // 处理一级菜单 + // 遍历权限数据数组 `permissions`,通过索引 `idx` 来访问每个权限信息对象,针对每个权限进行相关处理,构建一级菜单对应的权限信息结构并添加到 `permissionsResult` 对象中。 + for (idx in permissions) { + permission = permissions[idx]; + // 如果当前权限存在(即 `permission` 不为 `null`),并且该权限的层级(`ps_level`)为0,表示它是一级菜单权限,那么就将其相关信息按照特定结构添加到 `permissionsResult` 对象中。 + if (permission && permission.ps_level == 0) { + permissionsResult[permission.ps_id] = { + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path, + "pid": permission.ps_pid, + "children": [] + }; + } + } - cb(null,_.values(permissionsResult)); + // 临时存储二级返回结果 + // 创建一个临时对象 `tmpResult`,用于在处理二级菜单权限时,暂时存储二级菜单权限的相关信息,后续会将这些信息整理到对应的一级菜单权限的 `children` 数组中,以构建完整的树形结构。 + tmpResult = {}; + // 处理二级菜单 + // 再次遍历权限数据数组 `permissions`,同样对每个权限进行检查和相关处理,这次是构建二级菜单对应的权限信息结构,并关联到对应的一级菜单权限下。 + for (idx in permissions) { + permission = permissions[idx]; + if (permission && permission.ps_level == 1) { + // 根据当前二级菜单权限的父级权限ID(`ps_pid`),从 `permissionsResult` 中获取对应的一级菜单权限结果对象,后续将把当前二级菜单权限添加到这个一级菜单权限的子权限列表中。 + parentPermissionResult = permissionsResult[permission.ps_pid]; + if (parentPermissionResult) { + tmpResult[permission.ps_id] = { + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path, + "pid": permission.ps_pid, + "children": [] + } + // 将当前二级菜单权限对象添加到对应的一级菜单权限结果对象的 `children` 数组中,建立起层级关系,表示二级菜单权限隶属于对应的一级菜单权限。 + parentPermissionResult.children.push(tmpResult[permission.ps_id]); + } + } + } - } - }); - -} \ No newline at end of file + // 处理三级菜单 + // 又一次遍历权限数据数组 `permissions`,针对每个权限进行处理,构建三级菜单对应的权限信息结构,并关联到对应的二级菜单权限下,完善整个权限树形结构。 + for (idx in permissions) { + permission = permissions[idx]; + if (permission && permission.ps_level == 2) { + // 根据当前三级菜单权限的父级权限ID(`ps_pid`),从 `tmpResult` 中获取对应的二级菜单权限结果对象,后续将把当前三级菜单权限添加到这个二级菜单权限的子权限列表中。 + parentPermissionResult = tmpResult[permission.ps_pid]; + if (parentPermissionResult) { + // 将当前三级菜单权限的相关信息按照特定结构,添加到对应的二级菜单权限结果对象的 `children` 数组中,建立起三级菜单权限与二级菜单权限的隶属关系。 + // 这里对于 `pid` 属性的赋值,除了当前权限的父级权限ID,还添加了父级权限的父级权限ID(通过 `keyCategories[permission.ps_pid].ps_pid` 获取), + // 可能是为了更详细地记录权限的层级关联信息,具体含义要根据业务需求来确定,这样构建出更完整的权限层级结构信息。 + parentPermissionResult.children.push({ + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path, + "pid": permission.ps_pid + "," + keyCategories[permission.ps_pid].ps_pid + }); + } + } + } + // 使用 `_.values` 获取 `permissionsResult` 对象中的值(即整理好的树形结构权限数据),然后通过回调函数 `cb` 返回这些数据给调用者, + // 表示获取权限数据成功且以树形结构返回了相应的数据,调用者可以根据这个格式的数据进行后续的业务处理,比如在权限管理页面以树形菜单形式展示权限等操作。 + cb(null, _.values(permissionsResult)); + } + }); +} +//这段代码整体围绕获取系统所有权限信息展开,根据传入的不同参数类型,能够以列表形式或者树形结构 +//形式返回权限数据,通过一系列的数据处理和层级构建操作,满足了不同业务场景下对权限数据展示和 +//使用的需求,在权限管理相关的应用开发中具有重要作用。 diff --git a/services/RoleService.js b/services/RoleService.js index 099e40d..4c45edf 100644 --- a/services/RoleService.js +++ b/services/RoleService.js @@ -1,257 +1,374 @@ +// 引入lodash库,lodash是一个实用的JavaScript工具库,提供了许多便捷的函数来处理数组、对象、字符串等数据类型,帮助简化常见的编程任务,例如这里后续可能会用到它提供的对象和数组操作相关的方法。 var _ = require('lodash'); +// 引入Node.js的path模块,主要用于处理文件路径相关的操作,像拼接、解析文件路径等,以便准确地引入其他模块所在的文件位置。 var path = require("path"); -var dao = require(path.join(process.cwd(),"dao/DAO")); -var permissionAPIDAO = require(path.join(process.cwd(),"dao/PermissionAPIDAO")); +// 引入自定义的 `DAO` 模块,从代码中拼接的路径来看,它应该位于项目根目录下的 `dao/DAO` 文件中,这个模块大概率封装了与数据库操作相关的通用方法,例如查询、插入、更新等操作,用于不同数据模型的处理。 +var dao = require(path.join(process.cwd(), "dao/DAO")); +// 引入自定义的 `PermissionAPIDAO` 模块,同样通过拼接路径的方式引入,位于 `dao/PermissionAPIDAO` 文件中,推测该模块主要负责与权限相关的数据访问操作,比如获取权限数据等功能。 +var permissionAPIDAO = require(path.join(process.cwd(), "dao/PermissionAPIDAO")); -function getPermissionsResult(permissionKeys,permissionIds) { - var permissionsResult = {}; +// 定义一个名为 `getPermissionsResult` 的函数,它的主要作用是根据传入的权限相关的键值和权限ID列表,来构建权限结果对象,这个对象将按照权限的层级结构(一、二、三级菜单对应的权限)进行组织。 +function getPermissionsResult(permissionKeys, permissionIds) { + // 创建一个空对象,用于存储最终整理好的权限结果,它将以权限ID为键,对应权限的详细信息(包含子权限信息等)为值进行存储。 + var permissionsResult = {}; - // 处理一级菜单 - for(idx in permissionIds) { - if(!permissionIds[idx] || permissionIds[idx] == "") continue; - permissionId = parseInt(permissionIds[idx]); - permission = permissionKeys[permissionId]; - if(permission && permission.ps_level == 0) { - permissionsResult[permission.ps_id] = { - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path, - "children":[] - }; - } - } + // 处理一级菜单 + // 遍历权限ID列表,通过索引 `idx` 来访问每个权限ID,这个循环将对每个权限ID进行相关处理,构建一级菜单对应的权限信息结构。 + for (idx in permissionIds) { + // 如果当前权限ID不存在或者为空字符串,则跳过本次循环,继续处理下一个权限ID,这样可以排除无效的权限ID情况。 + if (!permissionIds[idx] || permissionIds[idx] == "") continue; + // 将当前权限ID转换为整数类型,确保在后续操作中(比如作为对象的键进行查找等操作)使用合适的数据类型,因为权限ID在业务逻辑中通常被视为数字标识。 + permissionId = parseInt(permissionIds[idx]); + // 根据权限ID从 `permissionKeys` 中获取对应的权限信息对象,这里的 `permissionKeys` 应该是一个以权限ID为键,权限详细信息为值的对象结构,方便通过权限ID快速查找对应权限详情。 + permission = permissionKeys[permissionId]; + // 如果获取到了对应的权限信息,并且该权限的层级(`ps_level`)为0,表示它是一级菜单权限,那么就将其相关信息按照特定结构添加到 `permissionsResult` 对象中。 + if (permission && permission.ps_level == 0) { + permissionsResult[permission.ps_id] = { + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path, + "children": [] + }; + } + } - // 临时存储二级返回结果 - tmpResult = {}; - // 处理二级菜单 - for(idx in permissionIds) { - if(!permissionIds[idx] || permissionIds[idx] == "") continue; - permissionId = parseInt(permissionIds[idx]); - permission = permissionKeys[permissionId]; - if(permission && permission.ps_level == 1) { - parentPermissionResult = permissionsResult[permission.ps_pid]; - if(parentPermissionResult) { - tmpResult[permission.ps_id] = { - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path, - "children":[] - } - parentPermissionResult.children.push(tmpResult[permission.ps_id]); - } - } - } + // 临时存储二级返回结果 + // 创建一个临时对象 `tmpResult`,用于在处理二级菜单权限时,暂时存储二级菜单权限的相关信息,后续会将这些信息整理到对应的一级菜单权限的 `children` 数组中。 + tmpResult = {}; + // 处理二级菜单 + // 再次遍历权限ID列表,同样对每个权限ID进行检查和相关处理,这次是构建二级菜单对应的权限信息结构,并关联到对应的一级菜单权限下。 + for (idx in permissionIds) { + if (!permissionIds[idx] || permissionIds[idx] == "") continue; + permissionId = parseInt(permissionIds[idx]); + permission = permissionKeys[permissionId]; + if (permission && permission.ps_level == 1) { + // 根据当前二级菜单权限的父级权限ID(`ps_pid`),从 `permissionsResult` 中获取对应的一级菜单权限结果对象,后续将把当前二级菜单权限添加到这个一级菜单权限的子权限列表中。 + parentPermissionResult = permissionsResult[permission.ps_pid]; + if (parentPermissionResult) { + // 将当前二级菜单权限的相关信息按照特定结构存储到 `tmpResult` 对象中,同样以权限ID为键,方便后续引用和操作。 + tmpResult[permission.ps_id] = { + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path, + "children": [] + } + // 将当前二级菜单权限对象添加到对应的一级菜单权限结果对象的 `children` 数组中,建立起层级关系,表示二级菜单权限隶属于对应的一级菜单权限。 + parentPermissionResult.children.push(tmpResult[permission.ps_id]); + } + } + } - // 处理三级菜单 - for(idx in permissionIds) { - if(!permissionIds[idx] || permissionIds[idx] == "") continue; - permissionId = parseInt(permissionIds[idx]); - permission = permissionKeys[permissionId]; - if(permission && permission.ps_level == 2) { - - parentPermissionResult = tmpResult[permission.ps_pid]; - - if(parentPermissionResult) { - - parentPermissionResult.children.push({ - "id":permission.ps_id, - "authName":permission.ps_name, - "path":permission.ps_api_path - }); - } - } - } - return permissionsResult; + // 处理三级菜单 + // 又一次遍历权限ID列表,针对每个权限ID进行处理,构建三级菜单对应的权限信息结构,并关联到对应的二级菜单权限下,完善整个权限层级结构。 + for (idx in permissionIds) { + if (!permissionIds[idx] || permissionIds[idx] == "") continue; + permissionId = parseInt(permissionIds[idx]); + permission = permissionKeys[permissionId]; + if (permission && permission.ps_level == 2) { + // 根据当前三级菜单权限的父级权限ID(`ps_pid`),从 `tmpResult` 中获取对应的二级菜单权限结果对象,后续将把当前三级菜单权限添加到这个二级菜单权限的子权限列表中。 + parentPermissionResult = tmpResult[permission.ps_pid]; + if (parentPermissionResult) { + // 将当前三级菜单权限的相关信息按照特定结构,直接添加到对应的二级菜单权限结果对象的 `children` 数组中,建立起三级菜单权限与二级菜单权限的隶属关系。 + parentPermissionResult.children.push({ + "id": permission.ps_id, + "authName": permission.ps_name, + "path": permission.ps_api_path + }); + } + } + } + // 返回整理好的权限结果对象,这个对象按照层级结构包含了各级菜单权限的详细信息,可供其他函数或模块进一步使用,例如在权限展示、权限验证等业务场景中使用。 + return permissionsResult; } /** * 获取所有用户的角色 & 权限 + * 此函数主要用于从数据库或其他数据源中获取所有用户的角色信息以及对应的权限信息,然后进行整理和关联,最终通过回调函数返回整理好的结果给调用者。 * - * @param {Function} cb 回调函数 + * @param {Function} cb 回调函数,用于接收获取角色和权限数据的操作结果,若操作成功则传递包含数据的参数,若失败则传递错误信息参数。 */ module.exports.getAllRoles = function(cb) { - dao.list("RoleModel",null,function(err,roles) { - if(err) return cb("获取角色数据失败"); - permissionAPIDAO.list(function(err,permissions){ - if(err) return cb("获取权限数据失败"); - var permissionKeys = _.keyBy(permissions,'ps_id'); - var rolesResult = []; - for(idx in roles) { - role = roles[idx]; - permissionIds = role.ps_ids.split(","); - roleResult = { - "id" : role.role_id, - "roleName" : role.role_name, - "roleDesc" : role.role_desc, - "children" : [] - }; + // 调用 `dao` 模块的 `list` 方法,尝试获取角色数据。第一个参数 `"RoleModel"` 可能是表示要查询的数据模型名称,用于在 `DAO` 模块内部确定要操作的具体数据表或数据集合, + // 第二个参数 `null` 可能表示查询条件(这里暂时没有传递具体查询条件,可能是获取所有角色数据的意思),第三个参数是一个回调函数,用于处理查询操作完成后的结果情况。 + dao.list("RoleModel", null, function (err, roles) { + // 如果在获取角色数据的过程中出现错误(`err` 不为 `null`),比如数据库查询失败(可能是连接问题、权限不足、表不存在等原因),则直接通过传入的回调函数 `cb` 返回错误信息,表示获取角色数据失败。 + if (err) return cb("获取角色数据失败"); + // 如果角色数据获取成功,接着调用 `permissionAPIDAO` 模块的 `list` 方法,尝试获取权限数据,同样它内部会执行相应的数据库查询等操作来获取权限相关的记录信息。 + permissionAPIDAO.list(function (err, permissions) { + // 如果在获取权限数据过程中出现错误,就通过回调函数 `cb` 返回相应的错误信息,表示获取权限数据失败,这样调用者就能知晓操作出现问题的环节以及具体的错误提示。 + if (err) return cb("获取权限数据失败"); + // 使用lodash的 `keyBy` 函数,将获取到的权限数据(`permissions`)按照权限ID(`ps_id`)进行转换,生成一个以权限ID为键,对应权限详细信息为值的对象结构,方便后续通过权限ID快速查找权限详情, + // 这在构建权限层级结构等操作中会很方便,避免了多次遍历数组查找对应权限的麻烦。 + var permissionKeys = _.keyBy(permissions, 'ps_id'); + // 创建一个空数组,用于存储最终整理好的包含角色和对应权限层级结构信息的结果对象,后续会将每个角色及其关联的权限信息依次添加到这个数组中。 + var rolesResult = []; + // 遍历获取到的角色数据数组,通过索引 `idx` 来访问每个角色信息对象,进行后续的处理,将角色信息和对应的权限信息进行关联整合。 + for (idx in roles) { + role = roles[idx]; + // 将当前角色的权限ID字符串(可能是以逗号分隔的多个权限ID)进行分割,得到一个权限ID数组,方便后续根据这些权限ID去查找和构建对应的权限层级结构信息。 + permissionIds = role.ps_ids.split(","); + // 创建一个对象,用于存储当前角色的基本信息以及即将整理好的权限层级结构信息,按照特定的格式进行初始化,后续会逐步填充完整信息。 + roleResult = { + "id": role.role_id, + "roleName": role.role_name, + "roleDesc": role.role_desc, + "children": [] + }; - - roleResult.children = _.values(getPermissionsResult(permissionKeys,permissionIds)); + // 调用 `getPermissionsResult` 函数,传入整理好的权限键值对象(`permissionKeys`)和当前角色的权限ID数组(`permissionIds`), + // 该函数会根据这些信息构建出对应的权限层级结构信息,并返回结果,然后将返回的结果(权限层级结构信息)赋值给当前角色结果对象的 `children` 属性, + // 这样就将角色信息和其对应的权限信息关联起来了,形成了包含角色和权限层级结构的完整信息对象。 + roleResult.children = _.values(getPermissionsResult(permissionKeys, permissionIds)); - rolesResult.push(roleResult); - } + // 将当前角色的完整信息对象添加到 `rolesResult` 数组中,完成一个角色及其权限信息的整理和存储,后续循环会处理其他角色信息,直到所有角色信息都处理完毕。 + rolesResult.push(roleResult); + } - cb(null,rolesResult); + // 当所有角色的信息以及对应的权限层级结构信息都整理完成后,通过回调函数 `cb` 返回整理好的结果数组(`rolesResult`), + // 此时表示获取所有用户的角色和权限信息操作成功,调用者可以接收到完整的数据进行后续的业务处理,比如展示角色权限列表、进行权限验证等操作。 + cb(null, rolesResult); - }); - - }); -} + }); + }); +} +//这段代码整体围绕获取用户角色和权限信息展开,先分别从不同的数据访问模块获取角色数据和权限数 +//据,然后通过一系列处理(构建权限层级结构、关联角色和权限信息等),最终将整合好的结果通过回调 +//函数返回给调用者,在权限管理相关的业务场景中具有重要作用,例如在系统后台展示不同用户角色所拥 +//有的权限菜单等功能时会用到这样的代码逻辑。 /** * 添加角色 + * 此函数用于向系统中添加新的角色信息,接收角色相关的参数以及一个回调函数,根据参数验证和数据库操作的结果,通过回调函数返回相应的信息给调用者。 * - * @param {[type]} params [description] - * @param {Function} cb [description] + * @param {[type]} params 包含角色信息的参数对象,预期包含角色名称(roleName)、角色描述(roleDesc)等相关信息,具体结构由业务需求决定。 + * @param {Function} cb 回调函数,用于接收添加角色操作的结果,若操作成功则传递包含新添加角色详细信息的参数,若失败则传递错误信息参数。 */ -module.exports.createRole = function(params,cb) { - if(!params.roleName) return cb("角色名称不能为空"); - if(!params.roleDesc) params.roleDesc = ""; +module.exports.createRole = function (params, cb) { + // 首先验证传入的参数中角色名称(roleName)是否存在,如果不存在,则直接通过回调函数返回错误信息,表示角色名称不能为空,因为角色名称是角色的重要标识,不可或缺。 + if (!params.roleName) return cb("角色名称不能为空"); + // 如果传入的参数中角色描述(roleDesc)不存在,则将其默认设置为空字符串,说明角色描述在某些情况下可以为空,这里进行了一个默认值的处理。 + if (!params.roleDesc) params.roleDesc = ""; - dao.create("RoleModel",{"role_name":params.roleName,"role_desc":params.roleDesc,"ps_ids":""},function(err,role){ - if(err) return cb("创建角色失败"); - cb(null,{ - "roleId" : role.role_id, - "roleName" : role.role_name, - "roleDesc" : role.role_desc - }); - }) + // 调用 `dao` 模块的 `create` 方法来执行创建角色的数据库操作。 + // 第一个参数 `"RoleModel"` 可能是用于指定要操作的数据模型名称,对应数据库中的相关数据表,告知 `dao` 模块操作哪个表来插入新的角色记录。 + // 第二个参数是一个对象,包含了要插入数据库的角色信息,以键值对形式表示字段和对应的值,这里将角色名称、角色描述以及权限ID列表(初始为空字符串)等信息传递进去,准备插入数据库。 + // 第三个参数是一个回调函数,用于处理数据库插入操作完成后的结果情况,根据操作是否成功返回相应的信息给回调函数 `cb`。 + dao.create("RoleModel", { "role_name": params.roleName, "role_desc": params.roleDesc, "ps_ids": "" }, function (err, role) { + // 如果在数据库插入操作过程中出现错误(`err` 不为 `null`),比如数据库连接问题、插入语句执行失败等原因,就通过回调函数 `cb` 返回错误信息,表示创建角色失败,让调用者知晓操作未成功以及失败原因。 + if (err) return cb("创建角色失败"); + // 如果角色创建成功,通过回调函数 `cb` 返回包含新创建角色详细信息的对象,包含角色ID(roleId)、角色名称(roleName)以及角色描述(roleDesc)等信息,方便调用者获取新角色的相关详情进行后续操作。 + cb(null, { + "roleId": role.role_id, + "roleName": role.role_name, + "roleDesc": role.role_desc + }); + }) } /** * 通过角色 ID 获取角色详情 + * 该函数根据传入的角色ID,从数据库中查询并获取对应角色的详细信息,通过回调函数将查询结果返回给调用者,若参数不符合要求或查询出现错误,也会通过回调函数返回相应的错误提示。 * - * @param {[type]} id 角色ID - * @param {Function} cb 回调函数 + * @param {[type]} id 角色ID,用于唯一标识要查询详情的角色,应该为有效的数字类型,由调用者传入。 + * @param {Function} cb 回调函数,用于接收获取角色详情操作的结果,若成功则传递包含角色详细信息的参数,若失败则传递错误信息参数。 */ -module.exports.getRoleById = function(id,cb){ - if(!id) return cb("角色ID不能为空"); - if(isNaN(parseInt(id))) return cb("角色ID必须为数字"); - dao.show("RoleModel",id,function(err,role){ - if(err) return cb("获取角色详情失败"); - cb(null,{ - "roleId" : role.role_id, - "roleName" : role.role_name, - "roleDesc" : role.role_desc, - "rolePermissionDesc" : role.ps_ca - }); - }); +module.exports.getRoleById = function (id, cb) { + // 首先验证传入的角色ID是否存在,如果不存在,则直接通过回调函数返回错误信息,表示角色ID不能为空,因为没有ID无法确定要查询哪个角色的详情。 + if (!id) return cb("角色ID不能为空"); + // 进一步验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则通过回调函数返回错误信息,表示角色ID必须为数字,因为在系统设计中角色ID通常以数字形式存储和使用,以准确查找对应记录。 + if (isNaN(parseInt(id))) return cb("角色ID必须为数字"); + // 调用 `dao` 模块的 `show` 方法,尝试从数据库中获取指定角色ID对应的角色详情信息。 + // 第一个参数 `"RoleModel"` 指明要操作的数据模型(对应数据库中的数据表),用于确定查询的目标数据表。 + // 第二个参数传入角色ID,作为查询条件,让 `dao` 模块根据此ID查找对应的角色记录。 + // 第三个参数是一个回调函数,用于处理查询操作完成后的结果情况,根据是否查询到数据以及是否出现错误等情况返回相应的信息给回调函数 `cb`。 + dao.show("RoleModel", id, function (err, role) { + // 如果在查询过程中出现错误(`err` 不为 `null`),比如数据库查询语句执行失败、连接问题等原因,就通过回调函数 `cb` 返回错误信息,表示获取角色详情失败,告知调用者操作出现问题及原因。 + if (err) return cb("获取角色详情失败"); + // 如果查询成功,通过回调函数 `cb` 返回包含角色详细信息的对象,包含角色ID(roleId)、角色名称(roleName)、角色描述(roleDesc)以及角色权限描述(rolePermissionDesc)等信息,方便调用者获取并使用这些详情信息。 + cb(null, { + "roleId": role.role_id, + "roleName": role.role_name, + "roleDesc": role.role_desc, + "rolePermissionDesc": role.ps_ca + }); + }); } /** * 更新角色信息 + * 此函数用于根据传入的角色相关参数,对已存在的角色信息进行更新操作,主要涉及更新角色名称和角色描述等信息,通过回调函数返回更新后的角色详细信息或者错误提示给调用者。 * - * @param {[type]} role 角色对象 - * @param {Function} cb 回调函数 + * @param {[type]} params 包含要更新的角色信息的参数对象,预期包含角色ID(id)、角色名称(roleName)、角色描述(roleDesc)等相关信息,具体结构由业务需求决定。 + * @param {Function} cb 回调函数,用于接收更新角色信息操作的结果,若成功则传递包含更新后角色详细信息的参数,若失败则传递错误信息参数。 */ -module.exports.updateRole = function(params,cb){ - if(!params) return cb("参数不能为空"); - if(!params.id) return cb("角色ID不能为空"); - if(isNaN(parseInt(params.id))) return cb("角色ID必须为数字"); +module.exports.updateRole = function (params, cb) { + // 首先验证传入的参数是否存在,如果不存在,则直接通过回调函数返回错误信息,表示参数不能为空,因为没有参数无法确定要更新哪个角色以及更新哪些内容。 + if (!params) return cb("参数不能为空"); + // 接着验证参数中的角色ID(id)是否存在,如果不存在,则通过回调函数返回错误信息,表示角色ID不能为空,因为没有角色ID无法准确找到要更新信息的目标角色。 + if (!params.id) return cb("角色ID不能为空"); + // 进一步验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则通过回调函数返回错误信息,表示角色ID必须为数字,以确保能准确地在数据库中定位到对应的角色记录进行更新操作。 + if (isNaN(parseInt(params.id))) return cb("角色ID必须为数字"); - updateInfo = {}; - if(params.roleName) { - updateInfo["role_name"] = params.roleName; - } - if(params.roleDesc) { - updateInfo["role_desc"] = params.roleDesc; - } + // 创建一个空对象,用于存储要更新的角色信息,后续会根据传入的参数情况,将需要更新的字段和对应的值添加到这个对象中,然后传递给数据库更新操作的方法。 + updateInfo = {}; + // 如果传入的参数中包含角色名称(roleName),则将角色名称信息添加到 `updateInfo` 对象中,键为 `"role_name"`,对应的值就是传入的角色名称,准备用于更新数据库中的角色名称字段。 + if (params.roleName) { + updateInfo["role_name"] = params.roleName; + } + // 如果传入的参数中包含角色描述(roleDesc),则将角色描述信息添加到 `updateInfo` 对象中,键为 `"role_desc"`,对应的值就是传入的角色描述,准备用于更新数据库中的角色描述字段。 + if (params.roleDesc) { + updateInfo["role_desc"] = params.roleDesc; + } - dao.update("RoleModel",params.id,updateInfo,function(err,newRole) { - if(err) return cb("更新角色信息失败"); - cb(null,{ - "roleId":newRole.role_id, - "roleName":newRole.role_name, - "roleDesc":newRole.role_desc, - "rolePermissionDesc" : newRole.ps_ca - }); - }); + // 调用 `dao` 模块的 `update` 方法来执行角色信息的更新操作。 + // 第一个参数 `"RoleModel"` 指明要操作的数据模型(对应数据库中的数据表),确定要更新的目标数据表。 + // 第二个参数传入角色ID,用于准确找到数据库中对应的角色记录进行更新。 + // 第三个参数传入 `updateInfo` 对象,包含了要更新的具体字段和对应的值,告知数据库要更新哪些信息。 + // 第四个参数是一个回调函数,用于处理更新操作完成后的结果情况,根据操作是否成功返回相应的信息给回调函数 `cb`。 + dao.update("RoleModel", params.id, updateInfo, function (err, newRole) { + // 如果在更新操作过程中出现错误(`err` 不为 `null`),比如数据库更新语句执行失败、违反数据约束等原因,就通过回调函数 `cb` 返回错误信息,表示更新角色信息失败,告知调用者操作未成功及原因。 + if (err) return cb("更新角色信息失败"); + // 如果更新操作成功,通过回调函数 `cb` 返回包含更新后角色详细信息的对象,包含角色ID(roleId)、角色名称(roleName)、角色描述(roleDesc)以及角色权限描述(rolePermissionDesc)等信息,方便调用者获取更新后的角色详情进行后续操作。 + cb(null, { + "roleId": newRole.role_id, + "roleName": newRole.role_name, + "roleDesc": newRole.role_desc, + "rolePermissionDesc": newRole.ps_ca + }); + }); } /** * 对角色进行授权 + * 该函数用于更新角色的权限信息,根据传入的角色ID和权限列表,将权限信息更新到对应的角色记录中,通过回调函数返回更新后的角色相关信息或者错误提示给调用者。 * - * @param {[type]} rights 以 "," 分割的权限列表 - * @param {Function} cb 回调函数 + * @param {[type]} rid 角色ID,用于唯一标识要授权的角色,应该为有效的数字类型,由调用者传入,以确定操作的目标角色。 + * @param {Function} rights 以 "," 分割的权限列表,通常是一串表示权限ID的字符串,用于更新角色所拥有的权限信息,格式由业务需求决定。 + * @param {Function} cb 回调函数,用于接收更新角色权限操作的结果,若成功则传递包含更新后角色部分详细信息的参数,若失败则传递错误信息参数。 */ -module.exports.updateRoleRight = function(rid,rights,cb) { - if(!rid) return cb("角色ID不能为空"); - if(isNaN(parseInt(rid))) return cb("角色ID必须为数字"); +module.exports.updateRoleRight = function (rid, rights, cb) { + // 首先验证传入的角色ID(rid)是否存在,如果不存在,则通过回调函数返回错误信息,表示角色ID不能为空,因为没有角色ID无法确定要对哪个角色进行授权操作。 + if (!rid) return cb("角色ID不能为空"); + // 进一步验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则通过回调函数返回错误信息,表示角色ID必须为数字,确保能准确在数据库中定位到对应的角色记录进行权限更新操作。 + if (isNaN(parseInt(rid))) return cb("角色ID必须为数字"); - // 注意这里需要更新权限描述信息字段 - // 暂时实现 - // - dao.update("RoleModel",rid,{"ps_ids":rights},function(err,newRole) { - if(err) return cb("更新权限失败"); - cb(null,{ - "roleId":newRole.role_id, - "roleName":newRole.role_name - }); - }); + // 注意这里需要更新权限描述信息字段,目前暂时实现的逻辑如下(可能后续需要进一步完善或调整)。 + + // 调用 `dao` 模块的 `update` 方法来执行角色权限的更新操作。 + // 第一个参数 `"RoleModel"` 指明要操作的数据模型(对应数据库中的数据表),确定要更新的目标数据表是角色表。 + // 第二个参数传入角色ID,用于准确找到数据库中对应的角色记录进行权限更新。 + // 第三个参数传入一个对象,其中 `"ps_ids"` 键对应的值为传入的权限列表(rights),表示将角色的权限ID列表字段更新为新传入的权限信息,以此实现对角色的授权操作。 + // 第四个参数是一个回调函数,用于处理更新操作完成后的结果情况,根据操作是否成功返回相应的信息给回调函数 `cb`。 + dao.update("RoleModel", rid, { "ps_ids": rights }, function (err, newRole) { + // 如果在更新操作过程中出现错误(`err` 不为 `null`),比如数据库更新语句执行失败、数据格式不符合要求等原因,就通过回调函数 `cb` 返回错误信息,表示更新权限失败,告知调用者操作未成功及原因。 + if (err) return cb("更新权限失败"); + // 如果更新操作成功,通过回调函数 `cb` 返回包含更新后角色部分详细信息的对象,包含角色ID(roleId)、角色名称(roleName)等信息,方便调用者获取更新后的部分角色详情进行后续操作,这里返回的信息可根据实际业务需求进一步调整完善。 + cb(null, { + "roleId": newRole.role_id, + "roleName": newRole.role_name + }); + }); } /** * 删除权限 + * 此函数用于从角色的权限列表中删除指定的权限,涉及获取角色信息、处理权限列表、更新角色信息以及重新获取相关权限数据等操作,最后通过回调函数返回处理后的权限相关结果或者错误提示给调用者。 * - * @param {[type]} rid 权限ID - * @param {[type]} deletedRightId 删除的权限ID - * @param {Function} cb 回调函数 + * @param {[type]} rid 权限ID,这里可能是角色的相关ID,用于唯一标识要操作的角色,由调用者传入,以确定要删除权限的目标角色。 + * @param {[type]} deletedRightId 删除的权限ID,用于指定要从角色权限列表中删除的具体权限,应该为有效的数字类型,其格式和业务含义由系统的权限管理设计决定。 + * @param {Function} cb 回调函数,用于接收删除权限操作的结果,若成功则传递包含处理后权限相关信息的参数,若失败则传递错误信息参数。 */ -module.exports.deleteRoleRight = function(rid,deletedRightId,cb) { - daoModule.findOne("RoleModel",{"role_id":rid},function(err,role){ - if(err || !role) return cb("获取角色信息失败",false); - ps_ids = role.ps_ids.split(","); - new_ps_ids = []; - for(idx in ps_ids) { - ps_id = ps_ids[idx]; - if(parseInt(deletedRightId) == parseInt(ps_id)) { - continue; - } - new_ps_ids.push(ps_id); - } - new_ps_ids_string = new_ps_ids.join(","); - role.ps_ids = new_ps_ids_string; - role.save(function(err,newRole) { - if(err) return cb("删除权限失败"); - permissionAPIDAO.list(function(err,permissions){ - if(err) return cb("获取权限数据失败"); - permissionIds = newRole.ps_ids.split(","); - var permissionKeys = _.keyBy(permissions,'ps_id'); - return cb(null,_.values(getPermissionsResult(permissionKeys,permissionIds))); - }); +module.exports.deleteRoleRight = function (rid, deletedRightId, cb) { + // 调用 `daoModule`(这里可能是 `dao` 模块,也许是代码中的一个小笔误,具体要看实际的模块引用情况)的 `findOne` 方法,尝试从数据库中查找指定角色ID对应的角色信息。 + // 第一个参数 `"RoleModel"` 指明要操作的数据模型(对应数据库中的数据表),确定要查询的是角色数据表。 + // 第二个参数传入一个对象,其中 `"role_id"` 键对应的值为传入的角色ID(rid),以此作为查询条件,查找对应的角色记录。 + // 第三个参数是一个回调函数,用于处理查询操作完成后的结果情况,根据是否查询到数据以及是否出现错误等情况返回相应的信息给回调函数 `cb`。 + daoModule.findOne("RoleModel", { "role_id": rid }, function (err, role) { + // 如果在查询过程中出现错误(`err` 不为 `null`)或者没有查询到对应的角色(`role` 为 `null`),比如数据库查询失败、没有符合条件的角色记录等原因,就通过回调函数 `cb` 返回错误信息,表示获取角色信息失败,同时返回 `false`(具体这个 `false` 的用途可能要结合调用处的逻辑来看),告知调用者操作出现问题及原因。 + if (err ||!role) return cb("获取角色信息失败", false); + // 将获取到的角色的权限ID列表字符串(以逗号分隔的多个权限ID)进行分割,得到一个权限ID数组,方便后续对每个权限ID进行处理,判断要删除的权限是否在其中等操作。 + ps_ids = role.ps_ids.split(","); + // 创建一个新的空数组,用于存储处理后的权限ID列表,即删除指定权限ID后的剩余权限ID列表,后续会将不需要删除的权限ID添加到这个数组中。 + new_ps_ids = []; + // 遍历分割后的权限ID数组,通过索引 `idx` 来访问每个权限ID,进行后续的处理,判断每个权限ID是否需要删除。 + for (idx in ps_ids) { + ps_id = ps_ids[idx]; + // 如果当前权限ID(转换为数字后)与要删除的权限ID(也转换为数字后)相等,则表示当前权限就是要删除的权限,跳过本次循环,不将其添加到新的权限ID列表中,实现删除该权限的目的。 + if (parseInt(deletedRightId) == parseInt(ps_id)) { + continue; + } + // 如果当前权限ID不是要删除的权限ID,则将其添加到新的权限ID列表 `new_ps_ids` 中,保留该权限。 + new_ps_ids.push(ps_id); + } + // 将处理后的权限ID数组重新转换为以逗号分隔的字符串形式,以便更新回角色的权限ID列表字段中,保持数据格式的一致性,符合数据库存储和业务逻辑的要求。 + new_ps_ids_string = new_ps_ids.join(","); + role.ps_ids = new_ps_ids_string; + // 调用角色对象的 `save` 方法(这里假设角色对象有对应的保存方法,用于将更新后的角色信息保存回数据库,实现对角色权限列表的更新操作),将更新后的角色信息保存到数据库中。 + // 该方法同样是一个异步操作,通过回调函数来处理保存操作完成后的结果情况,根据操作是否成功返回相应的信息给回调函数 `cb`。 + role.save(function (err, newRole) { + // 如果在保存操作过程中出现错误(`err` 不为 `null`),比如数据库更新语句执行失败、违反数据约束等原因,就通过回调函数 `cb` 返回错误信息,表示删除权限失败,告知调用者操作未成功及原因。 + if (err) return cb("删除权限失败"); + // 如果保存操作成功,即角色的权限信息已成功更新,接着调用 `permissionAPIDAO` 模块的 `list` 方法,尝试获取权限数据,可能是为了重新整理和返回更新后的权限相关信息,具体取决于业务需求。 + // 以下代码块是 `deleteRoleRight` 函数剩余部分的注释 - }); - - }); -} +// 如果权限数据获取成功,将更新后的角色权限ID列表(以逗号分隔的字符串形式)再次进行分割,得到权限ID数组,用于后续构建权限相关的结果结构。 +permissionIds = newRole.ps_ids.split(","); +// 使用 `lodash` 库的 `keyBy` 函数,将获取到的所有权限数据(`permissions`)按照权限ID(`ps_id`)进行转换,生成一个以权限ID为键,对应权限详细信息为值的对象结构。 +// 这样方便后续通过权限ID快速查找对应的权限详情,在构建权限层级结构等操作中能更高效地获取所需信息。 +var permissionKeys = _.keyBy(permissions, 'ps_id'); +// 调用 `getPermissionsResult` 函数,传入整理好的权限键值对象(`permissionKeys`)和当前角色更新后的权限ID数组(`permissionIds`), +// 该函数会根据这些信息构建出对应的权限层级结构信息,并返回结果,然后使用 `_.values` 获取结果对象中的值(即权限层级结构相关信息),最终通过回调函数 `cb` 返回这些信息给调用者, +// 调用者可以根据返回的权限相关信息进行后续的业务处理,比如展示角色最新的权限情况等操作。 +return cb(null, _.values(getPermissionsResult(permissionKeys, permissionIds))); +// 整个 `deleteRoleRight` 函数结束的括号,对应函数开头的 `function(rid, deletedRightId, cb)`,表示该函数定义结束,主要实现了从角色的权限列表中删除指定权限,并重新整理和返回相关权限信息的功能。 +}); +}); +} /** * 删除角色 + * 此函数用于根据传入的角色ID,从数据库中删除对应的角色记录,通过回调函数返回操作结果(成功则返回 `true`,失败则返回相应错误信息)给调用者。 * - * @param {[type]} id 角色ID - * @param {Function} cb 回调函数 + * @param {[type]} id 角色ID,用于唯一标识要删除的角色,应该为有效的数字类型,由调用者传入,以准确找到数据库中对应的角色记录进行删除操作。 + * @param {Function} cb 回调函数,用于接收删除角色操作的结果,若成功则传递 `true` 表示操作成功,若失败则传递错误信息参数,告知调用者操作出现的问题。 */ -module.exports.deleteRole = function(id,cb){ - if(!id) return cb("角色ID不能为空"); - if(isNaN(parseInt(id))) return cb("角色ID必须为数字"); - dao.destroy("RoleModel",id,function(err){ - if(err) return cb("删除失败"); - cb(null,true); - }) +module.exports.deleteRole = function (id, cb) { + // 首先验证传入的角色ID是否存在,如果不存在,则通过回调函数返回错误信息,表示角色ID不能为空,因为没有角色ID无法确定要删除哪个角色的记录。 + if (!id) return cb("角色ID不能为空"); + // 进一步验证角色ID是否可以转换为数字类型,如果不能转换成功(即不是有效的数字),则通过回调函数返回错误信息,表示角色ID必须为数字,以确保能在数据库中准确找到对应的角色记录进行删除,因为通常角色ID在数据库中是以数字形式存储和使用的。 + if (isNaN(parseInt(id))) return cb("角色ID必须为数字"); + // 调用 `dao` 模块的 `destroy` 方法来执行删除角色的数据库操作。 + // 第一个参数 `"RoleModel"` 指明要操作的数据模型(对应数据库中的数据表),确定要删除的是角色数据表中对应的记录。 + // 第二个参数传入角色ID,作为删除操作的条件,让数据库删除对应ID的角色记录。 + // 第三个参数是一个回调函数,用于处理删除操作完成后的结果情况,根据操作是否成功返回相应的信息给回调函数 `cb`。 + dao.destroy("RoleModel", id, function (err) { + // 如果在删除操作过程中出现错误(`err` 不为 `null`),比如数据库删除语句执行失败、违反外键约束等原因,就通过回调函数 `cb` 返回错误信息,表示删除失败,告知调用者操作未成功及原因。 + if (err) return cb("删除失败"); + // 如果删除操作成功,通过回调函数 `cb` 返回 `true`,告知调用者角色已成功从数据库中删除,调用者可以根据此结果进行后续的业务处理,比如刷新相关的角色列表展示等操作。 + cb(null, true); + }) } /** * 权限验证函数 + * 该函数用于验证指定角色(通过角色ID标识)对于某个服务(`serviceName`)中的某个动作(`actionName`)是否具有相应的权限,通过回调函数返回验证结果(成功或失败)给调用者。 * - * @param {[type]} rid 角色ID - * @param {[type]} serviceName 服务名 - * @param {[type]} actionName 动作名(方法) - * @param {Function} cb 回调函数 + * @param {[type]} rid 角色ID,用于唯一标识要验证权限的角色,需要为有效的数字类型,由调用者传入,以确定操作的目标角色。 + * @param {[type]} serviceName 服务名,代表系统中的某个服务模块,用于确定权限验证的范围,具体的服务名称由业务系统定义,比如可能是 "用户管理服务"、"订单服务" 等。 + * @param {[type]} actionName 动作名(方法),表示在对应服务中具体的操作动作,例如 "创建用户"、"查询订单" 等,用于精确验证角色对某个具体操作是否有权限。 + * @param {Function} cb 回调函数,用于接收权限验证操作的结果,若角色具有对应权限则传递 `true`,若没有权限则传递 `false`,若出现错误则传递相应的错误信息参数,告知调用者验证过程出现的问题。 */ -module.exports.authRight = function(rid,serviceName,actionName,cb) { - permissionAPIDAO.authRight(rid,serviceName,actionName,function(err,pass) { - cb(err,pass); - }); -} \ No newline at end of file +module.exports.authRight = function (rid, serviceName, actionName, cb) { + // 调用 `permissionAPIDAO` 模块的 `authRight` 方法来执行权限验证的具体业务逻辑,这个方法内部应该会根据传入的角色ID、服务名和动作名等信息, + // 去查询相关的权限数据(可能是数据库中的权限表等存储位置),判断该角色是否具备执行对应操作的权限,它同样是一个异步操作,通过回调函数来返回结果。 + permissionAPIDAO.authRight(rid, serviceName, actionName, function (err, pass) { + // 直接将 `permissionAPIDAO.authRight` 方法回调函数中的结果(`err` 表示错误信息,`pass` 表示权限验证是否通过的布尔值),原封不动地通过外层的回调函数 `cb` 返回给调用者, + // 使得调用者可以获取到权限验证的最终结果以及可能出现的错误信息,以便进行后续的业务处理,比如根据权限情况决定是否允许用户执行某个操作等。 + cb(err, pass); + }); +} +//这段代码整体涵盖了角色相关操作(添加、获取详情、更新、授权、删除等)以及权限验证的功能,通过 +//与相应的数据访问对象(dao、permissionAPIDAO 等)交互,实现了对角色和权限信息在数据库层面 +//的操作以及相关业务逻辑处理,在权限管理系统等应用场景中起着关键作用,保障了系统中不同角色对 +//各项服务操作的权限控制。 \ No newline at end of file diff --git a/services/UserService.js b/services/UserService.js index 2bc10aa..fc48b47 100644 --- a/services/UserService.js +++ b/services/UserService.js @@ -1,4 +1,13 @@ // 用户登录 -module.exports.login = function(username,password,cb) { - console.log("登录 %s %s",username,password); -} \ No newline at end of file +// 以下代码通过 `module.exports` 将一个名为 `login` 的函数暴露出去,使得其他模块可以引入并使用这个函数来实现用户登录相关的功能。 +// `login` 函数接收三个参数,分别是 `username`(用户名)、`password`(密码)以及 `cb`(回调函数),用于处理用户登录的逻辑以及后续的结果反馈。 +module.exports.login = function(username, password, cb) { + // 在函数内部,使用 `console.log` 打印出一条日志信息,用于记录当前正在进行登录操作以及对应的用户名和密码信息。 + // 这在调试或者查看系统运行时的操作记录时会比较有用,可以直观地看到有哪些用户正在尝试登录系统。 + // 这里使用了类似格式化字符串的方式,将 `username` 和 `password` 的值嵌入到日志信息中输出。 + console.log("登录 %s %s", username, password); +} +//不过需要注意的是,目前这个 login 函数只是简单地打印了登录信息,并没有真正实现完整的登录逻辑,比 +//如验证用户名和密码是否正确(通常需要与数据库或其他存储用户信息的地方进行交互比对),以及根据验 +//证结果调用传入的回调函数 cb 返回相应的结果(比如成功登录返回用户信息、失败返回错误提示等)等操 +//作。如果要使其成为一个实用的登录功能函数,还需要进一步补充完善相应的业务逻辑代码。 \ No newline at end of file diff --git a/vue.config.js b/vue.config.js index 5a6a16a..0731956 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,39 +1,51 @@ +//这段代码主要用于根据不同的环境(生产环境或开发环境)来配置webpack的入口文件和外部依赖 +//(externals)。在生产环境中,通过externals配置来告诉webpack不要将这些指定的库打包到最终的 +//bundle中,因为它们将在运行时通过全局变量来访问。同时,它还修改了html-webpack-plugin插件的 +//配置,通过添加一个isProd标志来区分当前是生产环境还是开发环境,这可以在生成的HTML文件中被用 +//来进行条件渲染或其他逻辑处理。 +// 导出webpack配置修改函数 module.exports = { + // 使用chainWebpack来自定义webpack配置 chainWebpack: config => { - // 发布模式 + // 判断当前是否为生产环境 config.when(process.env.NODE_ENV === 'production', config => { + // 修改入口文件为src/main-prod.js(用于生产环境) config - .entry('app') - .clear() - .add('./src/main-prod.js') + .entry('app') // 获取名为'app'的入口配置 + .clear() // 清除原有的入口文件 + .add('./src/main-prod.js') // 添加新的入口文件 + // 设置externals,这些库在打包时将不会被包含,而是作为外部依赖在运行时引入 config.set('externals', { - vue: 'Vue', - 'vue-router': 'VueRouter', - axios: 'axios', - lodash: '_', - echarts: 'echarts', - nprogress: 'NProgress', - 'vue-quill-editor': 'VueQuillEditor' + vue: 'Vue', // Vue库在运行时通过全局变量Vue访问 + 'vue-router': 'VueRouter', // vue-router库在运行时通过全局变量VueRouter访问 + axios: 'axios', // axios库在运行时通过全局变量axios访问 + lodash: '_', // lodash库在运行时通过全局变量_访问 + echarts: 'echarts', // echarts库在运行时通过全局变量echarts访问 + nprogress: 'NProgress', // nprogress库在运行时通过全局变量NProgress访问 + 'vue-quill-editor': 'VueQuillEditor' // vue-quill-editor库在运行时通过全局变量VueQuillEditor访问 }) + // 修改html-webpack-plugin插件的配置,设置isProd标志为true config.plugin('html').tap(args => { - args[0].isProd = true - return args + args[0].isProd = true // 修改html-webpack-plugin的第一个参数对象,添加isProd属性并设为true + return args // 返回修改后的参数数组 }) }) - // 开发模式 + // 判断当前是否为开发环境 config.when(process.env.NODE_ENV === 'development', config => { + // 修改入口文件为src/main-dev.js(用于开发环境) config - .entry('app') - .clear() - .add('./src/main-dev.js') + .entry('app') // 获取名为'app'的入口配置 + .clear() // 清除原有的入口文件 + .add('./src/main-dev.js') // 添加新的入口文件 + // 修改html-webpack-plugin插件的配置,设置isProd标志为false config.plugin('html').tap(args => { - args[0].isProd = false - return args + args[0].isProd = false // 修改html-webpack-plugin的第一个参数对象,添加isProd属性并设为false + return args // 返回修改后的参数数组 }) }) } -} +} \ No newline at end of file