课程CRUD
课程表的设计
反三范式
垂直分表
课程管理代码生成
strategy.setInclude(new String[]{"t_course","t_course_detail","t_course_resource","t_course_market"});
前台集成支持vue富文本编辑器
npm install quill --save
npm install vue-quill-editor --save
添加“课程管理”
后台
CourseController
/**
* 分页查询数据
*
* @param query 查询对象
* @return PageList 分页对象
*/
@PostMapping("/list")
public PageList<Course> json(@RequestBody CourseQuery query)
{
return courseService.selectPageList(query);
}
ICourseService
public interface ICourseService extends IService<Course> {
/**
* 分页+高级查询+跨表查询
* @param query
* @return
*/
PageList<Course> selectPageList(CourseQuery query);
}
CourseServiceImpl
@Autowired
private CourseMapper courseMapper;
@Override
public PageList<Course> selectPageList(CourseQuery query) {
Page<Course> page = new Page<Course>(query.getPage(),query.getRows());
List<Course> courses = courseMapper.loadPageList(page,query);
return new PageList<>(page.getTotal(),courses);
}
@Override
public boolean insert(Course entity) {
//要添加三张表 课程表 详情表 市场信心表
//tenantId tenantName userId userName
System.out.println(entity);
entity.setStatus(0);
courseMapper.insert(entity);
Long courseId = entity.getId();
//同时保存详情和市场信心
CourseDetail courseDetail = entity.getCourseDetail();
courseDetail.setCourseId(courseId);
courseDetailMapper.insert(courseDetail);
CourseMarket courseMarket = entity.getCourseMarket();
courseMarket.setCourseId(courseId);
courseMarketMapper.insert(courseMarket);
return true;
}
CourseMapper
public interface CourseMapper extends BaseMapper<Course> {
List<Course> loadPageList(Page<Course> page, CourseQuery query);
}
CourseMapper.xml
<!-- 要对课程类型进行跨表查询-->
<!-- List<Course> loadPageList(Page<Course> page, CourseQuery query);-->
<select id="loadPageList" resultMap="CourseMap">
SELECT
c.*, ct.id tid,
ct. NAME tname
FROM
t_course c
LEFT JOIN t_course_type ct ON c.course_type_id = ct.id
</select>
<resultMap id="CourseMap" type="Course">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="users" property="users" />
<result column="course_type_id" property="courseTypeId" />
<result column="gradeName" property="gradeName" />
<result column="grade" property="grade" />
<result column="status" property="status" />
<result column="tenant_id" property="tenantId" />
<result column="tenantName" property="tenantName" />
<result column="user_id" property="userId" />
<result column="userName" property="userName" />
<result column="start_time" property="startTime" />
<result column="end_time" property="endTime" />
<result column="pic" property="pic" />
<association property="courseType" javaType="CourseType">
<id column="tid" property="id" />
<result column="tname" property="name" />
</association>
</resultMap>
课程上下线
上线:
现实生活中:
当培训机构研发出新的课程后,准备招生时,要先把它添加到数据中。但是期望暂时不需要用户能够搜索到。所以需要上线才能操作
系统中:
在系统中,我们添加了一个课程,用户不立即解就搜索到,需要上线以后才行.
下线:
当某个课程不想卖的时候,就要下线. 当课程下线后,用户不能搜索到,但是数据库是还有的.
ES
原生ESTransport Client方式
就相当于写jdbc代码,非常麻烦.
spring data es
spring data
是spring对数据访问抽象.这些数据可以放入db,index,nosql等包含以下:
spring data jpa spring对关系型数据库访问支持
spring data ES spring对es数据访问支持
spring data redis等 spring对redis数据访问支持
spring data …
springboot springdata xxxx
spring data es
是spring对Es数据访问.和原来spring data jpa对db访问.
springboot spring data es
对 spring data es简化了配置.
环境搭建
*创建maven项目,导入springboot spring data es的jar*
pom
<!--springboot仲裁中心-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencies>
<!--springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--springboot 对spring data es支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
</dependencies>
application.yml
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 127.0.0.1:9300 #9200是图形界面端,9300代码端
入口类
@SpringBootApplication
public class ESApplication {
public static void main(String[] args) {
SpringApplication.run(ESApplication.class);
}
}
doc
/**
* 原来jpa Domain @Entity @Column @ID 建立的是对象与表之间的映射
* 现在ES Doc @Document(index,type) @Id @Feild 建立的是对象与文档直接映射
*/
@Document(indexName = "test",type = "person") //操作的是test索引库下面的person类型的文档
public class PersonDoc {
@Id //文档的id就是对象的id
private Long id;
//没有特殊要求不用@Field
private Integer age;
@Field(type = FieldType.Keyword)
private String name; //zs
@Field(type = FieldType.Text,analyzer = "ik_max_word"
,searchAnalyzer = "ik_max_word")
private String intro; //zs is
//关键字搜索,作用要name和intro字段
//方案1:做组合查询 or 如果字段过多
//方案2:虚拟字段all(所有需要做关键字搜索的值,中间通过空格分隔) zs +" "+ zs is .相当于一个字段代替了很多字段
//模糊查询所有字段
@Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String all; //name intro
//进行拼接
public String getAll() {
return this.name+" "+this.intro;
}
repository
public interface PersonDocRepository extends
ElasticsearchRepository<PersonDoc,Long> {
}
测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ESApplication.class)
public class EsTest {
//以后所有的操作都是要通过ElasticSearchTemplate
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Autowired
private PersonDocRepository repository;
//创建索引库(待会看是否会自动创建)全局映射(暂时) 类型映射 crud(Repository)
@Test
public void init() {
System.out.println(elasticsearchTemplate);
//创建索引
elasticsearchTemplate.createIndex(PersonDoc.class);
//类型映射
elasticsearchTemplate.putMapping(PersonDoc.class);
}
//dsl分页+高级查询+排序
@Test
public void testDsl (){
//1 创建一个构建器 NativeSearchQueryBuilder
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//2 设置条件 BoolQuery query bool
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//2.1 must
boolQuery.must(QueryBuilders.matchQuery("all","zs"));
//2.2 filter
boolQuery.filter(QueryBuilders.rangeQuery("age").gte(18).lte(40));
builder.withQuery(boolQuery);
//3 排序
builder.withSort(SortBuilders.fieldSort("id"));
//4 分页 页数从0开始
builder.withPageable(PageRequest.of(0,10));
//5 截取字段
builder.withSourceFilter(new FetchSourceFilter(new String[]{"id","name","age"},
null));
//6 查询并封装结果 - 总数,当前页数据
NativeSearchQuery build = builder.build();
Page<PersonDoc> page = repository.search(build);
System.out.println(page.getTotalElements());
System.out.println(page.getContent());
}
}
课程上下线实现
管理员:(千级)
添加:直接操作数据库
删除和修改: 同步操作方案
分页查询:直接操作数据.并发量小.
上线:同步信息到ES库.
下线:删除ES库数据
用户:(千万级)
搜索课程:直接从ES
实现
课程服务调用搜索服务-服务内部调用feign
搭建搜索服务
1)导入jar包
<!--springboot 对spring data es支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2)做配置和入口(不用改)配置中心
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 127.0.0.1:9300 #9200是图形界面端,9300代码端
3)写服务-接口层
doc
EsCourse.java
package com.zhanglin.index.doc;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.math.BigDecimal;
import java.util.Date;
//包含了前台展示的字段和要添加到索引库都要写到这儿
@Document(indexName = "hrm",type = "course")
public class EsCourse {
@Id
private Long id;
private String name;
private String users;
private Long courseTypeId;
private String courseTypeName;
private Long gradeId;
private String gradeName;
private Integer status;
private Long tenantId;
private String tenantName;
private Long userId;
private String userName;
private Date startTime;
private Date endTime;
private String intro;
private String resources; //图片
private Date expires; //过期时间
private BigDecimal priceOld; //原价
private BigDecimal price; //原价
private String qq; //原价
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String all;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsers() {
return users;
}
public void setUsers(String users) {
this.users = users;
}
public Long getCourseTypeId() {
return courseTypeId;
}
public void setCourseTypeId(Long courseTypeId) {
this.courseTypeId = courseTypeId;
}
public String getCourseTypeName() {
return courseTypeName;
}
public void setCourseTypeName(String courseTypeName) {
this.courseTypeName = courseTypeName;
}
public Long getGradeId() {
return gradeId;
}
public void setGradeId(Long gradeId) {
this.gradeId = gradeId;
}
public String getGradeName() {
return gradeName;
}
public void setGradeName(String gradeName) {
this.gradeName = gradeName;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Long getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
this.tenantId = tenantId;
}
public String getTenantName() {
return tenantName;
}
public void setTenantName(String tenantName) {
this.tenantName = tenantName;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
public String getResources() {
return resources;
}
public void setResources(String resources) {
this.resources = resources;
}
public Date getExpires() {
return expires;
}
public void setExpires(Date expires) {
this.expires = expires;
}
public BigDecimal getPriceOld() {
return priceOld;
}
public void setPriceOld(BigDecimal priceOld) {
this.priceOld = priceOld;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getQq() {
return qq;
}
public void setQq(String qq) {
this.qq = qq;
}
@Override
public String toString() {
return "EsCourse{" +
"id=" + id +
", name='" + name + '\'' +
", users='" + users + '\'' +
", courseTypeId=" + courseTypeId +
", courseTypeName='" + courseTypeName + '\'' +
", gradeId=" + gradeId +
", gradeName='" + gradeName + '\'' +
", status=" + status +
", tenantId=" + tenantId +
", tenantName='" + tenantName + '\'' +
", userId=" + userId +
", userName='" + userName + '\'' +
", startTime=" + startTime +
", endTime=" + endTime +
", intro='" + intro + '\'' +
", resources='" + resources + '\'' +
", expires=" + expires +
", priceOld=" + priceOld +
", price=" + price +
", qq='" + qq + '\'' +
'}';
}
public String getAll() {
String tmp = name
+" "+ users
+" "+ courseTypeName
+" "+ gradeName
+" "+ tenantName
+" "+ userName
+" "+ intro;
return tmp;
}
public void setAll(String all) {
this.all = all;
}
}
EsCourseRepository.java 接口
import com.zhanglin.index.doc.EsCourse;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface EsCourseRepository extends ElasticsearchRepository<EsCourse,Long>{
}
测试 (初始化 创建索引库并建立类型映射)
EscourseInitTest.java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CourseService2020Application.class)
public class EscourseInitTest {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Autowired
private EsCourseRepository repository;
@Test
public void init(){
elasticsearchTemplate.createIndex(EsCourse.class);
elasticsearchTemplate.putMapping(EsCourse.class);
System.out.println(repository);
}
}
CourseController.java
//上线
//参数,long[] 支持批量上线
//返回值:Ajaxresult只关心成功与否
@PostMapping("/online")
public AjaxResult onLine(Long[] ids){
return courseService.onLine(ids);
}
//下线
@PostMapping("/offline")
public AjaxResult offline(Long[] ids){
return courseService.offLine(ids);
}
ICourseService
/**
* 上线
* @param ids
* @return
*/
AjaxResult onLine(Long[] ids);
/**
* 下线
* @param ids
* @return
*/
AjaxResult offLine(Long[] ids);
CourseServiceImpl
@Override
public AjaxResult onLine(Long[] ids) {
// try
// {
// //1添加索引库
// // 通过id查询数据库里面课程
// List<Course> courses = courseMapper
// .selectBatchIds(Arrays.asList(ids));
// //转换为doc
// List<EsCourse> esCourses = courses2EsCourses(courses);
// //批量添加就ok
// repository.saveAll(esCourses);
// //2 修改数据库状态和上线时间 - ids,time
// Map<String,Object> params = new HashMap<>();
// params.put("ids",ids);
// params.put("offLineTime",new Date());
// courseMapper.onLine(params);
// return AjaxResult.me();
// }catch (Exception e){
// e.printStackTrace();
// return AjaxResult.me().setSuccess(false).setMessage("上线失败!"+e.getMessage());
// }
return null;
}
@Override
public AjaxResult offLine(Long[] ids) {
return null;
}