当前位置: 首页 > news >正文

做新闻类网站专业企专业企业网站设计

做新闻类网站,专业企专业企业网站设计,个人设计作品集,专业的做网站的《学成在线》项目实操笔记系列【上】#xff0c;跟视频的每一P对应#xff0c;全系列12万字#xff0c;涵盖详细步骤与问题的解决方案。如果你操作到某一步卡壳#xff0c;参考这篇#xff0c;相信会带给你极大启发。同时也欢迎大家提问与讨论#xff0c;我会尽力帮大家解…《学成在线》项目实操笔记系列【上】跟视频的每一P对应全系列12万字涵盖详细步骤与问题的解决方案。如果你操作到某一步卡壳参考这篇相信会带给你极大启发。同时也欢迎大家提问与讨论我会尽力帮大家解答。 一、前期准备 1.1 项目介绍 P2 To C面向个人B2B2C第1个B是指商品或服务的供应商第2个B是指从事电子商务的企业C是消费者。例子腾讯课堂第1个B是腾讯公司第2个B是入驻授课的企业C是用户学习课程 本项目含有3个端用户端机构端运营端 1.2 说自己项目 P3 从以下几个方面进行项目介绍 1.项目的背景包括是自研还是外包什么业务服务的客户群是谁谁去运营等问题。 2.项目的业务流程核心的业务流程 3.项目的功能模块核心模块一定要说 4.项目的技术架构 5.个人工作职责说得详细一些 6.个人负责模块的详细说明包括模块设计用到的技术技术的实现方案等找最熟悉的模块进行说明。 项目基本介绍是公司自研的专门针对成人职业技能教育的网络课堂系统网站提供了成人职业技能培训的相关课程如软件开发培训。基于B2B2C的业务模式。培训机构可以在平台入驻、发布课程我们公司作为运营方由专门的人员对发布的课程进行审核审核通过后课程才可以发布成功课程包括免费和收费两种形式对于免费课程用户可以直接选课学习对于收费课程要在选课后支付成功才可以继续学习。 本项目包括3个端用户端、机构端、运营端。 核心模块内容管理、媒资管理、课程搜索、订单支付、选课管理、认证授权等。 本项目采用前后端分离架构后端采用SpringBoot、SpringCloud技术栈开发数据库使用了Mysql还使用了Redis、消息队列、分布式文件系统、Elasticsearch等中间件系统要清楚这些中间件在系统中是如何使用的在哪里使用的。 划分的微服务包括内容管理服务、媒资管理服务、搜索服务、订单支付服务、学习中心服务、系统管理服务、认证授权服务、网关服务、注册中心服务、配置中心服务等。 我在这个项目中负责了内容管理、媒资管理、订单支付模块的设计与开发。 个人负责模块的详细说明内容管理模块是对平台上的课程进行管理。设计了课程基本信息表、课程营销表、课程计划、课程师资表。培训机构要发布一门课程需要填写课程基本信息、课程营销信息、课程计划信息、课程师资信息填写完毕后要提交审核由运营人员进行课程信息的审核整个审核过程是程序自动审核加入人工确认的方式通常24小时完成审核。课程审核通过即可发布课程课程的相关信息会聚合到课程发布表中这里不仅要将课程信息写到课程发布表还要将课程信息写到索引库、分布式文件系统中所以这里存在分布式事务的问题项目使用本地消息表加任务调度的方式去解决这里的分布式事务保证数据的最终一致性。 1.3 技术架构 P5 业务解决了什么问题为用户提供了什么样的服务。 技术栈 1.4 环境配置 P6 配置环境版本如下 IDEA基本配置如下 Maven配置 先把maven解压然后把maven仓库解压。 在maven的setting.xml文件中进行配置 mirroridalimaven/idnamealiyun maven/nameurlhttp://maven.aliyun.com/nexus/content/groups/public/urlblockedcentral/blocked /mirror 在IDEA中进行配置 虚拟机配置 解压虚拟机 双击虚拟机 点击虚拟网络编辑器 把VMnet8的子网地址改为192.168.101.0 虚拟机的用户名root密码centos 虚拟机ip192.168.101.65 启动docker systemctl start docker 运行所有的软件  sh /data/soft/restart.sh 查询docker容器docker ps docker ps 数据库配置 我是先用Navicat连接上虚拟机中的mysql Git配置 下载完Git然后配置到IDEA上 搭建Gogs Gogs是一个轻量级的远程仓库是在虚拟机里面的本项目使用Gogs作为Git远程仓库。进入Gogs http://192.168.101.65:10880 账号gogs密码gogs 关联远程仓库 我这里项目已经创建好了我想要关联远程仓库就不按照视频的方法进行。 点击VCS-Create Git Repositoty选择当前项目的文件夹作为仓库全选当前项目中所有文件输入文字然后点击commit。就可以把项目上传到本地仓库。 然后点击向上的按钮点击Define remote会跳出一个弹窗弹窗的地址填写下面网页中的地址 我直接使用现成的仓库复制下面的HTTP地址粘贴到上面弹窗中然后会有输入账号密码的弹窗填gogs的账号密码 然后就会出现项目内容 如果忘记gogs密码 可以在用户设置处更改密码也可以在管理面板用户管理编辑处让管理员指定密码 如何还是提示密码错误可以在IDEA中把文件名改掉改成不存在的会提示输入密码也可以选不保存忘记密码 配置.gitignore文件 把下面文件夹中的内容复制到IDEA的.gitignore文件中。 选中.gitignore先commit然后push到远程仓库。 关于分支 老师每一天的授课内容都会创建一个分支。如果想看看第1天的代码就可以切换到第1天的分支然后该分支只会显示第1天的代码。 分支在Git中相当于一个独立的工作流每个分支都可以有不同的提交历史和代码改动。 可以通过在Git界面右键分支点击Checkout来切换分支。 1.5 创建工程 P7 父工程职责把所有依赖的版本确定下来模块的聚合作用。 基础工程基础的代码。所有的微服务依赖于基础工程。 首先创建名为xuecheng-plus-project的工程把src文件删掉然后创建xuecheng-plus-parent的模块。 项目结构大概如下然后把第1章里的pom.xml代码复制到父工程的pom.xml文件中 首先用properties标签把所有依赖的版本确定然后dependencyManagement标签来引入对应的依赖。 下面创建xuecheng-plus-base模块这个模块和parent模块是并列关系。base模块里除了src和pom.xml文件外其它东西删掉包括一些启动类和配置项都要删除只留下基本结构。 所有模块都是直接或间接继承父模块所以要把父工程的坐标下面红框里复制然后粘贴到base模块的parent标签下 然后把下面文件中base模块pom.xml中的dependencies标签下的内容单独复制粘贴替换base模块的pom.xml中的dependencies下的内容。 1.6 Git面试 P8 可以在Git面板看到所有commit提交到本地仓库的版本。小铅笔所在的就是当前的分支。 面试题1Git代码冲突怎么处理 冲突的原因本地文件的版本浴目标分支中文件的版本不一致时当存在同一行的内容不同时在进行合并时会出现冲突。 场景多个分支向主分支合并时A同事和B同事开发过程中对同一文件的同一行内容进行修改。同一个分支下pull或push操作时。 在IDEA里commit是提交到本地仓库是本地机上的一个目录。push是把本地仓库提交到远程仓库。 可以通过图形界面修改 通过代码行修改方式如下 然后要add将文件添加到暂存区commit将文件提交最后push提交到远程仓库。 面试题2你是在哪个分支开发 我们不是直接在主分支开发由技术经理创建独立的开发分支我们是在独立的开发分支中进行开发最后由技术经理将开发分支合并到主分支技术经理对代码进行审查最终合并到主分支。 1.7 Maven面试 P9 面试题1Maven指令的作用 mvn clean 清除target目录中的生成结果 mvn compile 编译源代码生成target目录然后会有classes文件 mvn test 执行单元测试 mvn package 打包打成的jar包会放在target目录 mvn install 打包并把打好的包上传到本地仓库 mvn deploy 打包并把打好的包上传到远程仓库 面试题2Maven依赖版本冲突怎么处理 maven的依赖版本冲突一般是由于间接依赖导致一个jar包有多个不同的版本。比如A依赖了B的1.0版本C依赖了B的2.0版本项目依赖A和C从而间接依赖了B的1.0和2.0版本此时B有两个版本引入到了项目中可能会出现ClassNotFoundException和NoSuchMethodError等错误。 处理版本冲突可以使用以下方法 1.使用exclusions排除依赖 比如我们只依赖B的1.0版本此时可以在依赖C时排除对B的依赖。 2.使用dependencyManagement锁定版本号 通常在父工程对依赖的版本同一管理 比如我们只依赖B的1.0版本此时可以在父工程中限定B的版本为1.0 1.8 数据库环境 P10 数据库用的是虚拟docker容器里的数据库。 数据库用户名root密码mysql。 输入下面启动运行 systemctl start docker sh /data/soft/restart.sh我是在Navicat中对虚拟机中MySQL的连接下新建了一个数据库  右键表然后点击运行sql文件选择下面的xcplus_content.sql这个文件打开最后可以看到表都加载到了数据库中 1.9 存储引擎及区别 P11 1.InnoDBInnoDB用于事务处理具有ACID事务支持等特性如果要执行大量insert和update操作应该选择这个支持事务。使用的锁颗粒度默认为行级锁可以支持更高的并发也可以支持表锁。支持外键约束外键约束降低了表的查询速度增加了表之间的耦合度。 2.MyISAM管理非事务表提供高速存储和检索以及全文搜索能力不提供事务支持。只支持表级锁。不支持外键。 3.memory数据存储在内存中。 1.10 MySQL建表注意 P12 注意选择存储引擎如果要支持事务需要选择InnoDB。 日期类型如果要记录时分秒选择datetime只记录年月日使用date固定长度字符选择char不固定长度字符varcharvarchar比char节省空间但速度没有char快对于内容介绍类的长广文本字段使用text或longtext类型如果存储图片等二进制数据使用blob或longblob类型对金额字段使用DECIMAL。 如果要存储text、blob字段建议单独建一张表使用外键关联。 尽量不要定义外键保证表的独立性可以存在外键意义的字段。 注意字段的约束如非空、唯一、主键等。 1.11 (内容管理)创建工程 P15 这一节具体可以参考day01/资料中的第2章讲义。 在xuecheng-plus-content下面创建xuecheng-plus-content-api、xuecheng-plus-content-model和xuecheng-plus-content-service这三个模块。 新建的目录结构如下 模块之间依赖关系如下 xuecheng-plus-content-model依赖xuecheng-plus-base。 xuecheng-plus-content-service依赖xuecheng-plus-model。 xuecheng-plus-content-api依赖xuecheng-plus-content-service。因为看图api依赖service也依赖model但因为service依赖model所以api如果依赖了service变相也依赖了model。 xuecheng-plus-content-model的pom.xml文件 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentartifactIdxuecheng-plus-content/artifactIdgroupIdcom.xuecheng/groupIdversion0.0.1-SNAPSHOT/version/parentartifactIdxuecheng-plus-content-model/artifactIddependenciesdependencygroupIdcom.xuecheng/groupIdartifactIdxuecheng-plus-base/artifactIdversion0.0.1-SNAPSHOT/version/dependency/dependencies/projectxuecheng-plus-content-service的pom.xml文件 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentartifactIdxuecheng-plus-content/artifactIdgroupIdcom.xuecheng/groupIdversion0.0.1-SNAPSHOT/version/parentartifactIdxuecheng-plus-content-service/artifactIddependenciesdependencygroupIdcom.xuecheng/groupIdartifactIdxuecheng-plus-content-model/artifactIdversion0.0.1-SNAPSHOT/version/dependency/dependencies /projectxuecheng-plus-content-api的pom.xml文件 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentartifactIdxuecheng-plus-content/artifactIdgroupIdcom.xuecheng/groupIdversion0.0.1-SNAPSHOT/version/parentartifactIdxuecheng-plus-content-api/artifactIddependenciesdependencygroupIdcom.xuecheng/groupIdartifactIdxuecheng-plus-content-service/artifactIdversion0.0.1-SNAPSHOT/version/dependency/dependencies /project二、课程模块开发 2.1 课程查询 需求分析 P16 操作流程一步一步要怎么操作就是业务流程然后弄清在操作流程中需要哪些数据。 2.2 课程查询 生成PO类 P17 具体的搭建步骤可以看资料中下面这个文件里的内容 course_base文件是课程基本信息文件。 表结构如下。 首先把xuecheng-plus-generator这个代码生成器解压出来然后放到xuecheng-plus-project这个总工程下面。 此时该模块还是灰色的右键pom.xml然后点击Add as Maven Project可以使其变成maven工程。 修改generator下的ContentCodeGenerator类 修改下面数据的连接配置像我的表叫content可以去掉前面xc402 先把java下面的content删掉然后运行ContentCodeGenerator的main方法 main方法执行完后可以看到生成了content包model下面有po类。把po下面的所有po类复制粘贴到xuecheng-plus-content-model下的po包中 发现有一些报错主要的原因是缺乏了依赖。 可以加入如下的依赖到model模块的pom.xml文件中 !--存在mybatisplus注解添加相关注解保证不报错--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-annotation/artifactIdversion${mybatis-plus-boot-starter.version}/version/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-core/artifactIdversion${mybatis-plus-boot-starter.version}/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId /dependency现在就不会报错了  2.3 课程查询 设计分析 P18 2.4 课程查询 接口定义 P19 该节内容可以参考下面的文档进行配置所以我只罗列关键步骤。 第1步定义分页查询模型类。在xuecheng-plus-base下面的src\main\java\com\xuecheng\base\model下面创建PageParams类写入代码。 第2步定义条件模型类。在xuecheng-plus-content/xuecheng-plus-content-model下面src/main/java/com/xuecheng/content/model下创建dto包创建QueryCourseParamsDto类 第3步定义响应模型类。 PageResult分为2部分1部分是数据另1部分是分页信息。 第4步把依赖放到xuecheng-plus-content-api的pom.xml中 详细请见文档 我个人导入的时候cloud的基础环境包有点问题然后我修改了parent里面的代码 第5步在xuecheng-plus-content-api的com/xuecheng下面创建content/api然后创建一个CourseBaseInfoController类 RestController public class CourseBaseInfoController {RequestMapping(/course/list)public PageResultCourseBase list(PageParams pageParams,RequestBody QueryCourseParamsDto queryCourseParamsDto){return null;} } 第6步在xuecheng-plus-content-api的com/xuecheng下面创建ContentApplication启动类注意这个类一定是要在xucheng下面不要放到content下。 SpringBootApplication public class ContentApplication {public static void main(String[] args){SpringApplication.run(ContentApplication.class,args);} } log4j2-dev.xml在下面的位置粘贴到下面位置 在xuecheng-plus-content的xuecheng-plus-content-api下面的resources下创建bootstrap.yml文件然后写入如下代码注意把url中端口后的数据库名称改成自己的 点击启动类中下面按钮启动项目 重点请求localhost:63040/content/course/list出现的是下面的情况政策现象 请求的数据和接口不匹配加上pageNo和pageSize仍旧不行。 localhost:63040/content/course/list?pageNo1pageSize30 是因为第二个参数要接受json数据转化为Java对象而现在没有这个json数据。 RequestBody这个注解对参数的要求为true因此不行。修改方式如下给RequestBody加一个requiredfalse。 现在再请求就没有任何问题了 控制台也显示成功 前端和controller层间用vo传递数据controller和service层间用DTO传输数据service和dao层间用po传输数据。 如果有多个前端比如手机、PC传入的参数个数不同就需要有VO避免让负责手机前端的工程师误认为有5个参数比如VO1对应手机是3个参数VO2对应PC是5个参数。如果没有多个前端就只有1个前端那就只需要用DTO即可不需要VO。 2.5 课程查询 swagger P20 swagger可以在线生成接口文档。 首先加入swagger依赖之前已加完然后要在配置文件bootstrap.yml中进行配置 swagger:title: 学成在线内容管理系统description: 内容系统管理系统对课程相关信息进行管理base-package: com.xuecheng.contentenabled: trueversion: 1.0.0 在启动类上加EnableSwagger2Doc注解 然后重启项目 在浏览器中输入localhost:63040/content/swagger-ui.html然后可以看到如下的界面 在类上加Api注解在方法上加ApiOperation接口把RequestMapping改成PostMapping Api(value课程信息管理接口,tags课程信息管理接口) RestController public class CourseBaseInfoController {ApiOperation(课程查询接口)PostMapping(/course/list)public PageResultCourseBase list(PageParams pageParams,RequestBody(requiredfalse) QueryCourseParamsDto queryCourseParamsDto){return null;} } 效果如下  ApiModelProperty可以加在属性上用来给属性备注名称。 swagger里还可以进行接口测试 但发现日期不太好看 可以直接从下面这个文件中取出LocalDateTimeConfig这个工具类 然后放到如下的位置 重启项目重新测试 2.6 SpringBoot常用注解 P21 ResponseBody 将数据以json的格式响应给前端侧重返回定义在类上 RequestBody 将json数据转化为java对象侧重接收定义在方法上 PathVariable 接收请求路径中占位符的值 Autowired是基于类型的注入。 Resource基于名称注入。 2.7 项目开发流程 P22 1.产品人员设计产品原型。 2.讨论需求。 3.分模块设计接口。 4.出接口文档。 5.将接口文档给到前端人员前后端分离开发。 6.开发完毕进行测试。 7.测试完毕发布项目由运维人员进行部署安装。 2.8 课程查询 DAO接口 P23 前后端分离开发先定义controller层的接口生成接口文档然后再前后端一起开发。 要从底层开始写从持久层开始写。 在xuecheng-plus-content-service下面的com/xuecheng下面创建content包在content下创建mapper包。 然后把xuecheng-plus-generator下的com/xuecheng/content/mapper下的所有文件拷贝到上面service的mapper包下。 然后要进行单元测试首先把xuecheng-plus-content-service的依赖补全把如下这些依赖复制粘贴到service 在service的test下创建resources 把api模块定义的2个配置文件拷贝到service模块 配置文件只需要像下面这样 在service的test/java下创建com/xuecheng包然后把api的启动类拷贝到com/xuecheng下删掉生成接口文档的注解 分页插件会自动加limit语句 分页插件的原理分页参数会放到ThreadLocal中mybatis plus有一个拦截器可以拦截执行的sql根据数据库类型添加对应的分页语句重写sql在末尾拼加limit。 在xuecheng-plus-content-service的java/com/xuecheng/content下创建一个config包然后创建MybatisPlusConfig类把代码都写入进去 在service模块的test/java/com/xuecheng/content下创建CourseBaseMapperTests写入如下内容 如果左侧为绿√代表测试通过也可以打个断点看看 CourseBaseMapperTests代码如下  SpringBootTest public class CourseBaseMapperTests {AutowiredCourseBaseMapper courseBaseMapper;Testpublic void testCourseBaseMapper(){CourseBase courseBase courseBaseMapper.selectById(18);Assertions.assertNotNull(courseBase);//详细进行分页查询的单元测试//查询条件QueryCourseParamsDto courseParamsDto new QueryCourseParamsDto();courseParamsDto.setCourseName(java); //课程名称查询条件//拼装查询条件LambdaQueryWrapperCourseBase queryWrapper new LambdaQueryWrapper();//根据名称模糊查询.在sql中拼接course_base.name like %值%queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName()),CourseBase::getName,courseParamsDto.getCourseName());//根据课程审核状态查询 course_base.audit_status ?queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus()),CourseBase::getAuditStatus,courseParamsDto.getAuditStatus());//分页参数对象PageParams pageParams new PageParams();pageParams.setPageNo(1L);pageParams.setPageSize(2L);//创建page分页参数对象参数当前页码每页记录数。PageCourseBase page new Page(pageParams.getPageNo(), pageParams.getPageSize());//开始进行分页查询PageCourseBase pageResult courseBaseMapper.selectPage(page, queryWrapper);//数据列表ListCourseBase items pageResult.getRecords();//总记录数long total pageResult.getTotal();//ListT items,long counts,long page,long pageSizePageResultCourseBase courseBasePageResult new PageResultCourseBase(items,total,pageParams.getPageNo(),pageParams.getPageSize());System.out.println(courseBasePageResult);} } 在末尾打上断点进行断点调试  看一下是否有LIMIT语句  看一下结果数据是否完整 如果觉得数据有问题可以把SQL语句复制到Navicat中然后把参数逐一替换问号进行执行、 2.9 数据字典表 P24 下拉框的文字来源于数据字典表方便前端进行修改替换展示。 数据字典由code编码和文字组成通过编码来指代文字。 在Navicat中创建一个新的数据库xc_systemuff8mb4_general_ci。然后执行.sql文件全称是xcplus_system.sql。 虚拟机里面的Mysql已经有了这个数据库名字叫作xcplus-system所以我们不用创建只需要知道流程。 2.10 (课程查询)service P25 在xuecheng-plus-base的xuecheng-plus-content-service下的src/main/java/com/xuecheng/content下创建service包在service包下创建CourseBaseInfoService接口写入如下代码 //课程信息管理接口 public interface CourseBaseInfoService {/*** 课程分页查询* param pageParams 分页查询参数* param queryCourseParamsDto 查询条件* return 查询结果*/public PageResultCourseBase queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto queryCourseParamsDto); } 再在service包下创建impl包在impl包下创建实现类CourseBaseInfoServiceImpl写入如下代码注意一定要把return null改过了 Slf4j Service public class CourseBaseInfoServiceImpl implements CourseBaseInfoService {AutowiredCourseBaseMapper courseBaseMapper;Overridepublic PageResultCourseBase queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto courseParamsDto) {//拼装查询条件LambdaQueryWrapperCourseBase queryWrapper new LambdaQueryWrapper();//根据名称模糊查询.在sql中拼接course_base.name like %值%queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName()),CourseBase::getName,courseParamsDto.getCourseName());//根据课程审核状态查询 course_base.audit_status ?queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus()),CourseBase::getAuditStatus,courseParamsDto.getAuditStatus());//创建page分页参数对象参数当前页码每页记录数。PageCourseBase page new Page(pageParams.getPageNo(), pageParams.getPageSize());//开始进行分页查询PageCourseBase pageResult courseBaseMapper.selectPage(page, queryWrapper);//数据列表ListCourseBase items pageResult.getRecords();//总记录数long total pageResult.getTotal();//ListT items,long counts,long page,long pageSizePageResultCourseBase courseBasePageResult new PageResultCourseBase(items,total,pageParams.getPageNo(),pageParams.getPageSize());return courseBasePageResult;} } 然后要进行单元测试复制service模块test下原有的CourseBaseMapperTests粘贴到自己的路径下然后改名为CourseBaseInfoServiceTests写入如下代码 SpringBootTest public class CourseBaseInfoServiceTests {AutowiredCourseBaseInfoService courseBaseInfoService;Testpublic void testCourseBaseInfoService(){//查询条件QueryCourseParamsDto courseParamsDto new QueryCourseParamsDto();courseParamsDto.setCourseName(java); //课程名称查询条件courseParamsDto.setAuditStatus(202004);//分页参数对象PageParams pageParams new PageParams();pageParams.setPageNo(1L);pageParams.setPageSize(2L);PageResultCourseBase courseBasePageResult courseBaseInfoService.queryCourseBaseList(pageParams, courseParamsDto);System.out.println(courseBasePageResult);} }看一下控制台输出没太大问题  2.11 (课程查询)接口测试 P26 完善xuecheng-plus-content-api下的CourseBaseInfoController的代码 Api(value课程信息管理接口,tags课程信息管理接口) RestController public class CourseBaseInfoController {AutowiredCourseBaseInfoService courseBaseInfoService;ApiOperation(课程查询接口)PostMapping(/course/list)public PageResultCourseBase list(PageParams pageParams,RequestBody(requiredfalse) QueryCourseParamsDto queryCourseParamsDto){PageResultCourseBase courseBasePageResult courseBaseInfoService.queryCourseBaseList(pageParams, queryCourseParamsDto);return courseBasePageResult;} } 用swagger来测试没有问题  点击请求这里的地球然后点击如下生成一个Http的请求。  把swagger上面的参数转移到下面 ### POST http://localhost:63040/content/course/list?pageNo1pageSize2 Content-Type: application/json{auditStatus: 202004,courseName: java,publishStatus: } 同样可以查询出结果  现在可以把这些查询的文件统一放到一个包下方便管理。 在xuecheng-plus-project下面创建一个api-test包在包下创建一个文件名为xc-content-api.http用来存放请求测试的语句然后再创建一个http-client.env.json文件用来配置相应的环境变量。 效果如下 2.12 部署前端和管理服务 P27 安装完node和npm看看版本我用的是《苍穹外卖》配置的版本经测试可以表明项目可以向下兼容 用IDEA打开project-xczx2-portal-vue-ts右键package.json点击show npm scripts 右键serve然后点击Edit serve Settings。然后配置好Node和nmp的版本。最后Run serve 4 成功后点击链接访问即可 打开开发者工具all报错这是系统管理服务请求的是63110端口。 打开第2天的资料把xuecheng-plus-system放到project里把pom.xml转化为maven工程把api模块的配置文件中的数据库修改为我们自己的数据库。 运行Api模块下的SystemApplication启动类。 出现的是跨域问题 2.13 跨域三种方案 P28 判断是否跨域请求是基于浏览器的同源策略同源策略是浏览器的一种安全机制从一个地址请求另一个地址如果协议、主机、端口三者全部一致则不属于跨域否则有一个不一致就是跨域。 从http://localhost:8601到http://localhost:8602因为端口不同所以是跨域 从http://192.168.101.10:8601到http://192.168.101.11:8601由于主机不同是跨域 从http://192.168.101.10:8601到https://192.168.101.10:8601由于协议不用是跨域 方法1JSONP 方法2添加响应头 Access-Control-Allow-Origin: http://localhost:8601 服务器收到请求判断这个Origin是否允许跨域如果允许则在响应头中说明允许该来源的跨域请求。 Access-Control-Allow-Origin: * 方法3通过nginx代理跨域 由于服务器之间没有跨域浏览器可以通过nginx去访问。  Nginx和浏览器之间不是跨域的协议、服务器、端口都一样通过Nginx代理去访问服务器。 2.14 定义cors过滤器 P29 在xuecheng-plus-system的xuecheng-plus-content-api下的config包下创建一个GlobalCorsConfig类写入如下代码 Configurationpublic class GlobalCorsConfig {Beanpublic CorsFilter corsFilter() {CorsConfiguration config new CorsConfiguration();//允许白名单域名进行跨域调用config.addAllowedOrigin(*);//允许跨越发送cookieconfig.setAllowCredentials(true);//放行全部原始头信息config.addAllowedHeader(*);//允许所有请求方法跨域调用config.addAllowedMethod(*);UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**, config);return new CorsFilter(source);}}现在解决了跨域问题前端页面能正常显示。  2.15 课程查询前后端联调 P30 前端.env是重要文件修改了要重新启动项目。 当前内容管理模块打开VUE_APP_SERVER_API_URLhttp://localhost:63040 引入网关后会打开VUE_APP_SERVER_API_URLhttp://localhost:63010 前后端联调的过程。 测试输入java看是否出现课程。点击下拉框看是否有文字。 2.16 (分类查询)接口定义 P31 下表是树形结构是三级结构。 在xuecheng-plus-content的xuecheng-plus-content-model下的dto中定义一个新类CourseCategoryTreeDto写入如下代码 Data public class CourseCategoryTreeDto extends CourseCategory implements java.io.Serializable{ListCourseCategoryTreeDto childrenTreeNodes; } 在xuecheng-plus-content的xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api下新增一个类CourseCategoryController写入如下代码 RestController public class CourseCategoryController {GetMapping(/course-category/tree-nodes)public ListCourseCategoryTreeDto queryTreeNodes(){return null;} }2.17 (分类查询)树型查询 P32 方法1表的自连接。 select one.id one_id, one.label one_label, two.id two_id, two.label two_label from course_category one inner join course_category two on two.parentid one.id where one.parentid 1 and one.is_show 1 and two.is_show 1 order by one.orderby,two.orderby 方法2递归 向下递归由根节点找子节点。  with recursive t1 as (select * from course_category where id1union all select t2.* from course_category t2 inner join t1 on t1.idt2.parentid ) select * from t1 order by t1.id 向上递归由子节点找根节点。 with recursive t1 as (select * from course_category where id1-1-1union all select t2.* from course_category t2 inner join t1 on t1.parentid t2.id ) select * from t1 order by t1.id2.18 (分类查询)开发测试 P33 在xuecheng-plus-content的xuecheng-plus-content-service下的mapper中增加CourseCategoryMapper中的代码 public interface CourseCategoryMapper extends BaseMapperCourseCategory {//使用递归查询分类public ListCourseCategoryTreeDto selectTreeNodes(String id); } 在xuecheng-plus-content的xuecheng-plus-content-service下的mapper中增加CourseCategoryMapper.xml中的代码 select idselectTreeNodes parameterTypestring resultTypecom.xuecheng.content.model.dto.CourseCategoryTreeDtowith recursive t1 as (select * from course_category where id#{id}union allselect t2.* from course_category t2 inner join t1 on t1.id t2.parentid)select * from t1order by t1.id /select 在xuecheng-plus-content的xuecheng-plus-content-service的test下的java/com/xuecheng/content下把CourseBaseMapperTests拷贝一份起名CourseCategoryMapperTests 写入如下代码 SpringBootTest public class CourseCategoryMapperTests {AutowiredCourseCategoryMapper courseCategoryMapper;Testpublic void testCourseBaseMapper(){ListCourseCategoryTreeDto courseCategoryTreeDtos courseCategoryMapper.selectTreeNodes(1);System.out.println(courseCategoryTreeDtos);} }在xuecheng-plus-content的xuecheng-plus-content-service下的service下创建CourseCategoryService接口写入如下代码 public interface CourseCategoryService {public ListCourseCategoryTreeDto queryTreeNodes(String id); } 在xuecheng-plus-content的xuecheng-plus-content-service下的service下创建CourseCategoryServiceImpl类写入如下代码 Slf4j Service public class CourseCategoryServiceImpl implements CourseCategoryService {AutowiredCourseCategoryMapper courseCategoryMapper;Overridepublic ListCourseCategoryTreeDto queryTreeNodes(String id) {//调用mapper递归查询出分类信息ListCourseCategoryTreeDto courseCategoryTreeDtos courseCategoryMapper.selectTreeNodes(id);//找到每个节点的子节点最终封装成ListCourseCategoryTreeDto//先将list转成map,key就是结点的id,value就是CourseCategoryTreeDto对象目的是为了方便从map获取结点。filter(item-!id.equals(item.getId()))把根节点拍出MapString, CourseCategoryTreeDto mapTemp courseCategoryTreeDtos.stream().filter(item-!id.equals(item.getId())).collect(Collectors.toMap(key - key.getId(), value - value));//定义一个list作为最终返回的listListCourseCategoryTreeDto courseCategoryList new ArrayList();//从头遍历ListCourseCategoryTreeDto一边遍历一边找子节点放在父节点的childrenTreeNodescourseCategoryTreeDtos.stream().filter(item-!id.equals(item.getId())).forEach(item-{if(item.getParentid().equals(id)){courseCategoryList.add(item);}//找到节点的父节点CourseCategoryTreeDto courseCategoryTreeDto mapTemp.get(item.getParentid());if(courseCategoryTreeDto!null) {if (courseCategoryTreeDto.getChildrenTreeNodes() null) {//如果该父节点的ChildrenTreeNodes属性为空要new一个集合因为要向该集合中放它的子节点courseCategoryTreeDto.setChildrenTreeNodes(new ArrayListCourseCategoryTreeDto());}//到每个节点的子节点放在父节点的childrenTreeNodes属性中courseCategoryTreeDto.getChildrenTreeNodes().add(item);}});return courseCategoryList;} } 在xuecheng-plus-content的xuecheng-plus-content-service下的test下创建CourseCategoryServiceTests测试类写入如下代码 SpringBootTest public class CourseCategoryServiceTests {AutowiredCourseCategoryService courseCategoryService;Testpublic void testCourseBaseInfoService(){ListCourseCategoryTreeDto courseCategoryTreeDtos courseCategoryService.queryTreeNodes(1);System.out.println(courseCategoryTreeDtos);} } 单元测试的效果如下  在xuecheng-plus-content-api的CourseCategoryController下完善代码如下 RestController public class CourseCategoryController {AutowiredCourseCategoryService courseCategoryService;GetMapping(/course-category/tree-nodes)public ListCourseCategoryTreeDto queryTreeNodes(){return courseCategoryService.queryTreeNodes(1);} } 先把xuecheng-plus-content的xuecheng-plus-content-api下的ContentApplication启动。 然后在api-test包下的xc-content-api.http中添加如下代码 ### 查询课程分类 GET {{content_host}}/content/course-category/tree-nodes 可以看到正常输出结果  在前后端联调的时候可以看到课程分类有了结果 2.19 (新增课程)接口定义 P34 2.20 (新增课程)接口开发 P35 把下面2个Dto类复制到xuecheng-plus-content的xuecheng-plus-content-api的dto下面 在xuecheng-plus-content的xuecheng-plus-content-api下的CourseBaseInfoController中写入如下代码 ApiOperation(新增课程)PostMapping(/content/course)public CourseBaseInfoDto createCourseBase(RequestBody AddCourseDto addCourseDto){return null;} 在xuecheng-plus-content的xuecheng-plus-content-service的service下的CourseBaseInfoService中添加接口 public CourseBaseInfoDto createCourseBase(Long companyId,AddCourseDto addCourseDto); 在xuecheng-plus-content的xuecheng-plus-content-service的service/impl下的CourseBaseInfoServiceImpl中添加代码 Autowiredprivate CourseMarketMapper courseMarketMapper;Autowiredprivate CourseCategoryMapper courseCategoryMapper;TransactionalOverridepublic CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto dto) {//参数的合法性校验if (StringUtils.isBlank(dto.getName())) {throw new RuntimeException(课程名称为空);}if (StringUtils.isBlank(dto.getMt())) {throw new RuntimeException(课程分类为空);}if (StringUtils.isBlank(dto.getSt())) {throw new RuntimeException(课程分类为空);}if (StringUtils.isBlank(dto.getGrade())) {throw new RuntimeException(课程等级为空);}if (StringUtils.isBlank(dto.getTeachmode())) {throw new RuntimeException(教育模式为空);}if (StringUtils.isBlank(dto.getUsers())) {throw new RuntimeException(适应人群为空);}if (StringUtils.isBlank(dto.getCharge())) {throw new RuntimeException(收费规则为空);}//向课程基本信息表course_base写入数据CourseBase courseBaseNew new CourseBase();//将传入的页面的参数放到courseBase对象中BeanUtils.copyProperties(dto,courseBaseNew); //只要属性名相同就可以拷贝courseBaseNew.setCompanyId(companyId);courseBaseNew.setCreateDate(LocalDateTime.now());//审核状态默认为未提交courseBaseNew.setAuditStatus(202002);//发布状态为未发布courseBaseNew.setStatus(203001);//插入数据库int insert courseBaseMapper.insert(courseBaseNew);if(insert0){throw new RuntimeException(添加课程失败);}//向课程营销表course_market写入数据CourseMarket courseMarketNew new CourseMarket();//将页面输入的数据拷贝到courseMarketNewBeanUtils.copyProperties(dto,courseMarketNew);//课程的idLong courseId courseBaseNew.getId();courseMarketNew.setId(courseId);//保存营销信息saveCourseMarket(courseMarketNew);//从数据库查询课程的详细信息包括两部分CourseBaseInfoDto courseBaseInfo getCourseBaseInfo(courseId);return courseBaseInfo;}//查询课程信息public CourseBaseInfoDto getCourseBaseInfo(long courseId){//从课程基本信息表查询CourseBase courseBase courseBaseMapper.selectById(courseId);if(courseBasenull){return null;}//从课程营销表查询CourseMarket courseMarket courseMarketMapper.selectById(courseId);//组装在一起CourseBaseInfoDto courseBaseInfoDto new CourseBaseInfoDto();BeanUtils.copyProperties(courseBase,courseBaseInfoDto);BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);//通过courseCategoryMapper查询分类信息将分类名称放在courseBaseInfoDto对象return courseBaseInfoDto;}//单独写一个方法保存营销信息逻辑存在则更新不存在则添加private int saveCourseMarket(CourseMarket courseMarketNew){//参数的合法性校验String charge courseMarketNew.getCharge();if(StringUtils.isEmpty(charge)){throw new RuntimeException(收费规则为空);}//如果课程收费价格没有填写也需要抛出异常if(charge.equals(201001)){if(courseMarketNew.getPrice()null || courseMarketNew.getPrice().floatValue()0){throw new RuntimeException(课程的价格不能为空并且必须大于0);}}//从数据库查询营销信息存在则更新不存在则添加Long id courseMarketNew.getId();CourseMarket courseMarket courseMarketMapper.selectById(id);if(courseMarketnull){//插入数据库int insert courseMarketMapper.insert(courseMarketNew);return insert;}else{//将courseMarketNew拷贝到courseMarketBeanUtils.copyProperties(courseMarketNew,courseMarket);courseMarket.setId(courseMarket.getId());//更新int i courseMarketMapper.updateById(courseMarket);return i;}}2.21 (新增课程)接口测试 P36 首先是完善controller的接口。在xuecheng-plus-content的xuecheng-plus-content-api下的CourseBaseInfoController中完善createCourseBase方法 ApiOperation(新增课程) PostMapping(/course) public CourseBaseInfoDto createCourseBase(RequestBody AddCourseDto addCourseDto){//获取到用户所述机构的idLong companyId 1232141425L;CourseBaseInfoDto courseBase courseBaseInfoService.createCourseBase(companyId, addCourseDto);return courseBase; } 然后节约时间跳过单元测试直接倒api-test下的xc-content-api.http中进行接口测试 ### 新增课程 POST {{content_host}}/content/course Content-Type: application/json {charge: 201001,description: adsd,grade: 204001,mt: 1-1,name: java网络编程高级,originalPrice: 100,phone: 13333333,pic: fdsf,price: 10,qq: 22333,st: 1-1-1,tags: sdsdwe,teachmode: 20002,users: 初级人员,validDays: 365,wechat: 223344 } 在controller和service的实现类上打上断点主要是看看断点调试时接受的数据是否完整。 放行之后数据效果如下 前端可以直接看到之前新增的课程 数据库中也有如下数据没问题 前端还暂时无法提交。 2.22 Mybatis相关问题 P37 1.Mybatis分页插件的实现原理 首先分页参数会被放到ThreadLocal中拦截执行的sql语句根据数据库的类型比如是Mysql就会在末尾添加LIMIT添加对应的分页语句重写sql。 计算出total总记录数pageNum当前是第几页pageSize每页的大小。是否为首页是否为尾页总页数等。 2.树型表的标记字段是什么如何查询MySQL树型表 树型表的标记字段是parentid即父结点的id。 3.查询一个树型表的方法 当层级固定时可以用表的自连接进行查询。 如果想灵活查询每个层级可以使用mysql递归方法使用with RECURSIVE实现。 4.Result Type和Result Map的区别 Result Type当查询到的SQL字段的名字和Result Type中模型类型的属性名字对应上的可以由MyBatis自动完成映射。 Result Map当查询到的SQL字段的名字和Result Type中模型类型的属性名字对应不上时需要通过Result Map手动完成映射。 5. #{}和${}的区别 #{}是标记一个占位符可以防止sql注入。 ${}用于在动态sql中拼接字符串可能导致sql注入。 2.23 自定义异常类型 P38 如果前端名称为空会报错500。但仅在控制台输出前端没展示。 如果写很多try-catch代码会造成代码冗余。 由增强类来捕获异常原理是AOP面向切面编程。 用ControllerAdvice注解来控制器增强用异常处理注解ExceptionHandler。 在xuecheng-plus-base的base包下创建exception包。 在exception包下创建RestErrorResponse类和前端约定返回的异常信息模型 /*** 错误响应参数包装*/ public class RestErrorResponse implements Serializable {private String errMessage;public RestErrorResponse(String errMessage){this.errMessage errMessage;}public String getErrMessage() {return errMessage;}public void setErrMessage(String errMessage) {this.errMessage errMessage;} }在exception包下创建XueChengPlusException类 /*** description 学成在线项目异常类* author Mr.M* date 2022/9/6 11:29* version 1.0*/ public class XueChengPlusException extends RuntimeException {private String errMessage;public XueChengPlusException() {super();}public XueChengPlusException(String errMessage) {super(errMessage);this.errMessage errMessage;}public String getErrMessage() {return errMessage;}public static void cast(CommonError commonError){throw new XueChengPlusException(commonError.getErrMessage());}public static void cast(String errMessage){throw new XueChengPlusException(errMessage);}}在exception包下创建CommonError类 /*** description 通用错误信息* author Mr.M* date 2022/9/6 11:29* version 1.0*/ public enum CommonError {UNKOWN_ERROR(执行过程异常请重试。),PARAMS_ERROR(非法参数),OBJECT_NULL(对象为空),QUERY_NULL(查询结果为空),REQUEST_NULL(请求参数为空);private String errMessage;public String getErrMessage() {return errMessage;}private CommonError( String errMessage) {this.errMessage errMessage;}}2.24 异常处理开发测试 P39 现在想测试课程名称为空是否会触发XueChengPlusException异常处理。 首先在service模块的service包的impl包下的CourseBaseInfoServiceImpl类中修改下面的代码 //参数的合法性校验 if (StringUtils.isBlank(dto.getName())) {//throw new RuntimeException(课程名称为空);XueChengPlusException.cast(课程名称为空); } 在xuecheng-plus-base的exception下的GlobalExceptionHandler类下的如下位置打上断点 最后在api-test包下的xc-content-api.http下面让name参数为空。 在如下位置添加自定义的异常处理语句。 把现价改为负数会触发异常。 2.25 系统异常处理 P40 处理自定义异常程序在编写代码时根据校验结果主动抛出自定义异常类对象抛出异常时指定详细的异常信息异常处理器捕获异常信息记录异常日志并相应给客户。 2.26 JSR303校验 P41 JSR303是一个校验框架。 使用方式只需要在模型类中通过注解指定校验规则比如NOTEmpty来指定不能为空Size来指定长度的下限和上限对不同的应用场景可以通过分组来切换校验规则使用groups在controller方法上开启校验Validated。 前端请求后端接口传输参数在controller和service中都需要校验。 controller校验请求参数的合法性包括必填项校验、数据格式校验是否符合一定的日期格式。 service校验业务规则的相关内容。 因为service是根据业务规则去校验所以不方便写成通用代码controller中则可以将校验的代码写成通用代码。 引入如下依赖 合法性校验的注解如下 如果想要使用需要激活 首先要把service层校验屏蔽掉我是直接删掉 然后记得要把启动类重启一下代码才能生效。 在xc-content-api.http中把name改为空走的是系统异常处理所以一律输出为位置错误执行过程异常请重试。 现在出现问题如果多个接口使用同一个模型类时对校验的需求不一样时会出现问题。 解决方法分组校验。 在xuecheng-plus-base的exception下面的ValidationGroups类中写入如下代码 //用于分组校验定义一些常用的组 public class ValidationGroups {public interface Insert{};public interface Update{};public interface Delete{}; } 在xuecheng-plus-content的xuecheng-plus-content-model的dto下面的类AddCourseDto替换如下代码 NotEmpty(message 新增课程名称不能为空,groups{ValidationGroups.Insert.class}) NotEmpty(message 修改课程名称不能为空,groups{ValidationGroups.Update.class}) ApiModelProperty(value 课程名称, required true) private String name; Validated注解里面写上所分的组类 2.27 系统参数合法性校验 P42 提问对表单的数据是怎么校验的 回答JSR303校验规则。 如果javax.validation.constraints包下的校验规则满足不了需求怎么办 1.手写校验代码 2.自定义校验规则注解 2.28 (修改课程)接口开发 P44 在xuecheng-plus-content的xuecheng-plus-content-api的api包下的CourseBaseInfoController中写入如下代码 ApiOperation(根据课程id查询接口) GetMapping(/course/{courseId}) public CourseBaseInfoDto getCourseBaseById(PathVariable Long courseId){CourseBaseInfoDto courseBaseInfo courseBaseInfoService.getCourseBaseInfo(courseId);return courseBaseInfo; } 在xuecheng-plus-content的xuecheng-plus-content-service的service包下的CourseBaseInfoService中写入如下代码 //根据课程id查询课程信息 public CourseBaseInfoDto getCourseBaseInfo(Long courseId); 在xuecheng-plus-content的xuecheng-plus-content-service的service/impl包下的CourseBaseInfoServiceImpl中写入如下代码 //查询课程信息 public CourseBaseInfoDto getCourseBaseInfo(Long courseId){//从课程基本信息表查询CourseBase courseBase courseBaseMapper.selectById(courseId);if(courseBasenull){return null;}//从课程营销表查询CourseMarket courseMarket courseMarketMapper.selectById(courseId);//组装在一起CourseBaseInfoDto courseBaseInfoDto new CourseBaseInfoDto();BeanUtils.copyProperties(courseBase,courseBaseInfoDto);BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);//通过courseCategoryMapper查询分类信息将分类名称放在courseBaseInfoDto对象return courseBaseInfoDto; } 在api-test的xc-content-api.http中写入如下代码 ### 课程查询 GET {{content_host}}/content/course/40 Content-Type: application/json 返回的结果如下 在xuecheng-plus-content的xuecheng-plus-content-model下面的dto包下面重新定义一个类EditCourseDto代码如下 因为修改比新增就多了一个id可以继承新增。 Data ApiModel(valueEditCourseDto,description修改课程基本信息) public class EditCourseDto extends AddCourseDto{ApiModelProperty(value课程id,requiredtrue)private Long Id; } 在xuecheng-plus-content的xuecheng-plus-content-api的api包下的CourseBaseInfoController中写入如下代码 ApiOperation(修改课程) PutMapping(/course) public CourseBaseInfoDto modifyCourseBase(RequestBody Validated EditCourseDto editCourseDto){//获取到用户所属机构的idLong companyId 1232141425L;CourseBaseInfoDto courseBaseInfoDto courseBaseInfoService.updateCourseBase(companyId, editCourseDto);return courseBaseInfoDto; } 在xuecheng-plus-content的xuecheng-plus-content-service的service包下的CourseBaseInfoService中写入如下代码 public CourseBaseInfoDto updateCourseBase(Long companyId,EditCourseDto editCourseDto); 在xuecheng-plus-content的xuecheng-plus-content-service的service/impl包下的CourseBaseInfoServiceImpl中写入如下代码缺少更新营销信息 Override public CourseBaseInfoDto updateCourseBase(Long companyId, EditCourseDto editCourseDto) {//拿到课程idLong courseId editCourseDto.getId();//查询课程信息CourseBase courseBase courseBaseMapper.selectById(courseId);if(courseBase null){XueChengPlusException.cast(课程不存在);}//数据合法性校验//根据具体的业务逻辑去校验//本机构只能修改本机构的课程if(!companyId.equals(courseBase.getCompanyId())){XueChengPlusException.cast(本机构只能膝盖本机构的课程);}//封装数据BeanUtils.copyProperties(editCourseDto,courseBase);//修改时间courseBase.setChangeDate(LocalDateTime.now());//更新数据库int i courseBaseMapper.updateById(courseBase);if(i0){XueChengPlusException.cast(修改课程失败);}//查询课程信息CourseBaseInfoDto courseBaseInfo getCourseBaseInfo(courseId);return courseBaseInfo; } 在api-test的xc-content-api.http中写入如下代码 ### 课程查询 GET {{content_host}}/content/course/40 Content-Type: application/json 2.29 (修改课程)接口测试 P45 输入下面的网址打开前端界面 http://localhost:8601/#/ 点击编辑按钮尝试修改基本信息 修改完毕之后原先的信息会被更改。 返回的结果如下属于正常跳转到另外一个还没写的界面 2.30 (计划查询)接口定义 P46 在xuecheng-plus-content的xuecheng-plus-content-model下面的dto包下面定义一个新类TeachplanDto代码如下 Data ToString public class TeachplanDto extends Teachplan {//课程计划关联的媒资信息private TeachplanMedia teachplanMedia;//子结点private ListTeachplanDto teachPlanTreeNodes; } 在xuecheng-plus-content的xuecheng-plus-content-api的api包下的TeachplanController中写入如下代码初步 //课程计划管理相关的接口 Api(value课程计划编辑接口,tags课程计划编辑接口) RestController public class TeachplanController {ApiOperation(查询课程计划树形结构)GetMapping(/teachplan/{courseId}/tree-nodes)public ListTeachplanDto getTreeNodes(PathVariable Long courseId){return null;} } 2.31 (计划查询)sql语句 P47 在xuecheng-plus-content的xuecheng-plus-content-service的mapper下的TeachplanMapper中写入如下代码 public interface TeachplanMapper extends BaseMapperTeachplan {//课程计划查询public ListTeachplanDto selectTreeNodes(Long courseId); } 在xuecheng-plus-content的xuecheng-plus-content-service的mapper下的TeachplanMapper.xml中写入如下代码 select idselectTreeNodes parameterTypelong resultMaptreeNodeResultMapselectone.id one_id,one.pname one_pname,one.parentid one_parentid,one.grade one_grade,one.media_type one_mediaType,one.start_time one_stratTime,one.end_time one_endTime,one.orderby one_orderby,one.course_id one_courseId,one.course_pub_id one_coursePubId,two.id two_id,two.pname two_pname,two.parentid two_parentid,two.grade two_grade,two.media_type two_mediaType,two.start_time two_stratTime,two.end_time two_endTime,two.orderby two_orderby,two.course_id two_courseId,two.course_pub_id two_coursePubId,m1.media_fileName mediaFilename,m1.id teachplanMeidaId,m1.media_id mediaIdfrom teachplan oneINNER JOIN teachplan two on one.id two.parentidLEFT JOIN teachplan_media m1 on m1.teachplan_id two.idwhere one.parentid 0 and one.course_id#{value}order by one.orderby,two.orderby /select 2.32 (计划查询)接口开发 P48 在xuecheng-plus-content的xuecheng-plus-content-service的java的mapper下的TeachplanMapper下写入如下代码 public interface TeachplanMapper extends BaseMapperTeachplan {//课程计划查询public ListTeachplanDto selectTreeNodes(Long courseId); } 在xuecheng-plus-content的xuecheng-plus-content-service的java的mapper下的TeachplanMapper.xml下写入如下代码 column是从数据库查到的字段property是目标类的属性也就是将从数据库查到的字段column映射到目标类的属性property上。 除了id字段用id标签其它都用result标签。 resultMap idtreeNodeResultMap typecom.xuecheng.content.model.dto.TeachplanDtoid columnone_id propertyid/result columnone_pname propertypname/result columnone_pname propertypname /result columnone_parentid propertyparentid /result columnone_grade propertygrade /result columnone_mediaType propertymediaType /result columnone_stratTime propertystratTime /result columnone_endTime propertyendTime /result columnone_orderby propertyorderby /result columnone_courseId propertycourseId /result columnone_coursePubId propertycoursePubId /!--映射子节点一对多映射ofType填的是list中对象类型--collection propertyteachPlanTreeNodes ofTypecom.xuecheng.content.model.dto.TeachplanDto!-- 二级数据映射 --id columntwo_id propertyid /result columntwo_pname propertypname /result columntwo_parentid propertyparentid /result columntwo_grade propertygrade /result columntwo_mediaType propertymediaType /result columntwo_stratTime propertystratTime /result columntwo_endTime propertyendTime /result columntwo_orderby propertyorderby /result columntwo_courseId propertycourseId /result columntwo_coursePubId propertycoursePubId /!--一对一映射--association propertyteachplanMedia javaTypecom.xuecheng.content.model.po.TeachplanMediaresult columnteachplanMeidaId propertyid /result columnmediaFilename propertymediaFilename /result columnmediaId propertymediaId /result columntwo_id propertyteachplanId /result columntwo_courseId propertycourseId /result columntwo_coursePubId propertycoursePubId //association/collection /resultMap 在xuecheng-plus-content的xuecheng-plus-content-service的test的content下创建一个TeachplanMapperTests类写入如下代码 SpringBootTest public class TeachplanMapperTests {AutowiredTeachplanMapper teachplanMapper;Testpublic void testSelectTreeNodes(){ListTeachplanDto teachplanDtos teachplanMapper.selectTreeNodes(117L);System.out.println(teachplanDtos);} } 当测试数据为117L的时候会有2个大章节。 在xuecheng-plus-content的xuecheng-plus-content-service的service包下的TeachplanService 中写入如下代码 //课程计划管理相关接口 public interface TeachplanService {//根据课程id查询课程计划public ListTeachplanDto findTeachplanTree(Long courseId); } 在xuecheng-plus-content的xuecheng-plus-content-service的service/impl包下的TeachplanService 中写入如下代码 Service public class TeachplanServiceImpl implements TeachplanService {AutowiredTeachplanMapper teachplanMapper;Overridepublic ListTeachplanDto findTeachplanTree(Long courseId) {ListTeachplanDto teachplanTree teachplanMapper.selectTreeNodes(courseId);return teachplanTree;} } 在xuecheng-plus-content的xuecheng-plus-content-api的api包下的TeachplanController中写入如下代码完善  //课程计划管理相关的接口 Api(value课程计划编辑接口,tags课程计划编辑接口) RestController public class TeachplanController {Autowiredprivate TeachplanService teachplanService;ApiOperation(查询课程计划树形结构)GetMapping(/teachplan/{courseId}/tree-nodes)public ListTeachplanDto getTreeNodes(PathVariable Long courseId){ListTeachplanDto teachplanTree teachplanService.findTeachplanTree(courseId);return teachplanTree;} } 在api-test的xc-content-api.http中写入如下代码 ### 课程计划查询 GET {{content_host}}/content/teachplan/117/tree-nodes 控制台输出的效果如下测试成功前后端联调测试没问题  2.33 (新增修改计划)接口定义 P49 点击“添加章”新增第一级课程计划。 点击“添加小节”向某个第一级课程计划下添加小节。 点击“章”、“节”的名称可以修改名称、选择是否免费。 同一个接口接收新增和修改两个业务请求以是否传递课程计划id 来判断是新增还是修改传递了课程计划id说明当前是要修改该课程计划否则是新增一个课程计划。 在xuecheng-plus-content的xuecheng-plus-content-model下的dto中新增一个类写入如下代码 Data ToString public class SaveTeachplanDto {private Long id;private String pname;private Long parentid;private Integer grade;private String mediaType;private Long courseId;private Long coursePubId;private String isPreview; }在xuecheng-plus-conten的xuecheng-plus-content-api下的content/api下的TeachplanController中写入如下代码 ApiOperation(课程计划创建或修改) PostMapping(/teachplan) public void saveTeachplan(RequestBody SaveTeachplanDto teachplan){ }
http://www.pierceye.com/news/629749/

相关文章:

  • 手机网站做落地页石家庄网络营销
  • 如何把网站主关键词做到百度首页网站页面设计优化方案
  • 做门户网站多少钱做视频解析网站播放器和接口
  • 打开一个网站网站被挂马无法访问
  • 大连网站建设公司排名装饰设计公司哪个好
  • 苏州企业建设网站公司400电话网站源码
  • 贵州住房和城乡建设厅官网泰安千橙网站建设优化熊掌号
  • metro网站模板平面设计师网站
  • 怎样通过阿里云建设网站国内免费crm
  • 网站开发都需要学什么iis7网站建设
  • 网站 关键字it网站建设资讯网
  • 白银网站建设公司石家庄建行网站
  • 做网站全部乱码怎么办教学资源网站建设方案
  • 自己做的网站怎么添加文档做淘宝详情的网站
  • 安全认证的机票网站成就怎么做山东省住房和城乡建设厅政务服务
  • 海口网站建设方案咨询信息流优化师面试常见问题
  • 网上课程网站wordpress自定义页面分页
  • 自建站成本深圳网站建设营销服务平台
  • 模仿网站怎么做网站编辑建设
  • 湖南做网站 磐石网络引领免费网上开店
  • wordpress内容做成目录seo排名分析
  • 大型网站 网站建设做网站赔了8万
  • python官方网站建设网站要什么
  • 青海 网站开发 图灵如何创建自己的网页
  • 建设银行网站怎么登陆不做网站首页的尺寸
  • 谁能给我一个网站谢谢dedecms收费怎么办
  • dede 网站地图 模块青岛做网站服务商
  • 征信网站开发扬州市建设局网站
  • 教育网站建设 飞沐软件定制公司值得去吗
  • 金耀网站建设网站制作景观建筑人才网