1.项目结构
2.pom文件介绍
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>GrainOnlineEducation_Parent</artifactId>
<groupId>com.grain</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>GrainOnlineEducation_Statistics</artifactId>
<dependencies>
<dependency>
<groupId>com.grain</groupId>
<artifactId>GrainOnlineEducation_Common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--服务注册-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--服务调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--开发者工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
3.代码分析
package com.grain.statistics.client;
import com.grain.common.result.Result;
import io.swagger.annotations.ApiParam;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author:Dragon Wen
* @email:18475536452@163.com
* @date:Created in 2020/3/10 10:25
* @description:
* @modified By:
* @version: $
*/
@Component
@FeignClient("grain-ucenter")
public interface UcenterClient {
@GetMapping(value = "/ucenter/member/count-register/{day}")
public Result registerCount(@PathVariable String day);
}
package com.grain.statistics.config;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
@MapperScan("com.grain.statistics.mapper")
public class StatisticsConfig {
/**
* SQL 执行性能分析插件
* 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
*/
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1000);//ms,超过此处设置的ms则sql不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
}
package com.grain.statistics.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author:Dragon Wen
* @email:18475536452@163.com
* @date:Created in 2020/2/27 15:42
* @description:Swagger2配置文件
* @modified By:
* @version: $
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("谷粒学院-统计分析服务API文档")
.description("本文档描述了统计分析服务服务接口定义")
.version("1.0")
.contact(new Contact("Dragon Wen", "www.dragonwen.cn", "18475536452@163.com"))
.build();
}
}
package com.grain.statistics.controller;
import com.grain.common.result.Result;
import com.grain.statistics.service.DailyService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* <p>
* 网站统计日数据 前端控制器
* </p>
*
* @author Dragon Wen
* @since 2020-03-09
*/
@CrossOrigin
@RestController
@RequestMapping("/statistics/daily")
public class DailyController {
@Autowired
private DailyService dailyService;
/**
* 创建统计数据
* @param day
* @return
*/
@ApiOperation(value = "创建统计数据")
@PostMapping("{day}")
public Result createStatisticsByDate(
@ApiParam(name = "day", value = "统计日期", required = true)
@PathVariable String day){
dailyService.createStatisticsByDay(day);
return Result.ok();
}
/**
* 获取统计数据
* @param begin
* @param end
* @param type
* @return
*/
@ApiOperation(value = "获取统计数据")
@GetMapping("show-chart/{begin}/{end}/{type}")
public Result showChart(
@ApiParam(name = "begin", value = "开始时间")
@PathVariable String begin,
@ApiParam(name = "end", value = "结束时间")
@PathVariable String end,
@ApiParam(name = "type", value = "统计类型")
@PathVariable String type){
Map<String, Object> map = dailyService.getChartData(begin, end, type);
return Result.ok().data(map);
}
}
package com.grain.statistics.entity;
import com.baomidou.mybatisplus.annotation.*;
import java.util.Date;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 网站统计日数据
* </p>
*
* @author Dragon Wen
* @since 2020-03-09
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("statistics_daily")
@ApiModel(value="Daily对象", description="网站统计日数据")
public class Daily implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.ID_WORKER_STR)
private String id;
@ApiModelProperty(value = "统计日期")
private String dateCalculated;
@ApiModelProperty(value = "注册人数")
private Integer registerNum;
@ApiModelProperty(value = "登录人数")
private Integer loginNum;
@ApiModelProperty(value = "每日播放视频数")
private Integer videoViewNum;
@ApiModelProperty(value = "每日新增课程数")
private Integer courseNum;
@ApiModelProperty(value = "创建时间", example = "2020-01-01 8:00:00")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间", example = "2020-01-01 8:00:00")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
}
package com.grain.statistics.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @author:Dragon Wen
* @email:18475536452@163.com
* @date:Created in 2020/2/27 15:40
* @description:在操作数据库的时候根据语句拦截,帮我们自动补充被拦截的语句的属性
* @modified By:
* @version: $
*/
@Component
public class DataMetaObjectHandler implements MetaObjectHandler {
//在执行insert语句时被拦截操作的
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
//在执行update语句时被拦截操作的
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
/**
* 逻辑删除插件
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
//逻辑删除和分页插件都是package com.baomidou.mybatisplus.extension.plugins;此包下面的
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
package com.grain.statistics.service;
import com.grain.statistics.entity.Daily;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Map;
/**
* <p>
* 网站统计日数据 服务类
* </p>
*
* @author Dragon Wen
* @since 2020-03-09
*/
public interface DailyService extends IService<Daily> {
/**
* 根据日期创建统计信息
* @param day
*/
void createStatisticsByDay(String day);
/**
* 获取统计数据
* @param begin
* @param end
* @param type
* @return
*/
Map<String, Object> getChartData(String begin, String end, String type);
}
package com.grain.statistics.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.grain.statistics.client.UcenterClient;
import com.grain.statistics.entity.Daily;
import com.grain.statistics.mapper.DailyMapper;
import com.grain.statistics.service.DailyService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 网站统计日数据 服务实现类
* </p>
*
* @author Dragon Wen
* @since 2020-03-09
*/
@Service
public class DailyServiceImpl extends ServiceImpl<DailyMapper, Daily> implements DailyService {
@Autowired
private UcenterClient ucenterClient;
@Override
public void createStatisticsByDay(String day) {
// 查询统计表中是否存在当前日期的统计数据,如果存在则删除数据
QueryWrapper<Daily> dailyQueryWrapper = new QueryWrapper<>();
dailyQueryWrapper.eq("date_calculated", day);
baseMapper.delete(dailyQueryWrapper);
// 获取统计信息
Integer registerNum = (Integer) ucenterClient.registerCount(day).getData().get("countRegister");
Integer loginNum = RandomUtils.nextInt(100, 200);//TODO
Integer videoViewNum = RandomUtils.nextInt(200, 300);//TODO
Integer courseNum = RandomUtils.nextInt(100, 200);//TODO
// 创建统计对象
Daily daily = new Daily();
daily.setDateCalculated(day);
daily.setRegisterNum(registerNum);
daily.setLoginNum(loginNum);
daily.setVideoViewNum(videoViewNum);
daily.setCourseNum(courseNum);
baseMapper.insert(daily);
}
@Override
public Map<String, Object> getChartData(String begin, String end, String type) {
// 根据时间范围进行数据查询
QueryWrapper<Daily> dailyQueryWrapper = new QueryWrapper<>();
dailyQueryWrapper.between("date_calculated", begin, end);
// 指定查询的字段
dailyQueryWrapper.select("date_calculated", type);
List<Daily> statisticsList = baseMapper.selectList(dailyQueryWrapper);
//把数据构建成想要的结构,最终变成两个json数组形式
//创建两个list集合
//日期集合
List<String> calculatedList = new ArrayList<>();
//数据集合
List<Integer> dataList = new ArrayList<>();
//向两个list集合中封装数据
//遍历查询集合
for (int i = 0; i < statisticsList.size(); i++) {
//集合每个对象
Daily sta = statisticsList.get(i);
//封装日期集合数据
String dateCalculated = sta.getDateCalculated();
calculatedList.add(dateCalculated);
//封装数据部分
//判断获取哪个统计因子
switch (type) {
case "register_num":
Integer registerNum = sta.getRegisterNum();
dataList.add(registerNum);
break;
case "login_num":
Integer loginNum = sta.getLoginNum();
dataList.add(loginNum);
break;
case "video_view_num":
Integer videoViewNum = sta.getVideoViewNum();
dataList.add(videoViewNum);
break;
case "course_num":
Integer courseNum = sta.getCourseNum();
dataList.add(courseNum);
break;
default:
break;
}
}
//创建map集合,把封装之后两个list集合放到map集合,返回
Map<String,Object> map = new HashMap<>();
map.put("calculatedList",calculatedList);
map.put("dataList",dataList);
return map;
}
}
package com.grain.statistics;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author:Dragon Wen
* @email:18475536452@163.com
* @date:Created in 2020/3/9 17:43
* @description:
* @modified By:
* @version: $
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class StatisticsApplication {
public static void main(String[] args) {
SpringApplication.run(StatisticsApplication.class, args);
}
}
test包下的代码生成器:
package com.grain.statistics;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author:Dragon Wen
* @email:18475536452@163.com
* @date:Created in 2020/2/27 12:10
* @description:
* @modified By:
* @version: $
*/
@RunWith(SpringRunner.class)//运行在什么环境下:加载spring容器
@SpringBootTest(classes = CodeGenerator.class)
public class CodeGenerator {
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("Dragon Wen");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/grain?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("statistics"); //模块名
pc.setParent("com.grain");
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("statistics_daily");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
4.配置文件
# 服务端口
server.port=8006
# 服务名
spring.application.name=grain-statistics
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/grain?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/grain/statistics/mapper/xml/*.xml
# 设置日志级别
# logging.level.root=WARN
#指定注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8004/eureka/
#eureka服务器上获取的是服务器的ip地址,否则是主机名
eureka.instance.prefer-ip-address=true