数据库 表结构分析
字段类型与长度:
数字类型:长度最大20,数字类型
短整型:int
长整型:bigint
小数:decimal
金额类型的数组:看需求,需要小数点,使用 decimal,否则使用 int 单位为分
字符类型:统一使用varchart,字符长度通常设置为8的倍数
时间类型:不同公司规范不同,DATE/DATETIME/TIME,其他一般设置为 bigint 时间戳
攻略表
CREATE TABLE `strategy` ( `id` bigint NOT NULL AUTO_INCREMENT, `dest_id` bigint DEFAULT NULL , `dest_name` varchar (255 ) DEFAULT NULL , `theme_id` bigint DEFAULT NULL , `theme_name` varchar (255 ) DEFAULT NULL , `catalog_id` bigint DEFAULT NULL , `catalog_name` varchar (255 ) DEFAULT NULL , `title` varchar (255 ) DEFAULT NULL , `sub_title` varchar (255 ) DEFAULT NULL , `summary` varchar (255 ) DEFAULT NULL , `cover_url` varchar (255 ) DEFAULT NULL , `create_time` datetime DEFAULT NULL , `isabroad` int DEFAULT NULL , `viewnum` int DEFAULT NULL , `replynum` int DEFAULT NULL , `favornum` int DEFAULT NULL , `sharenum` int DEFAULT NULL , `thumbsupnum` int DEFAULT NULL , `state` int DEFAULT NULL , PRIMARY KEY (`id`) USING BTREE ) ENGINE= InnoDB AUTO_INCREMENT= 20 DEFAULT CHARSET= utf8mb3 ROW_FORMAT= DYNAMIC ;
攻略内容表
CREATE TABLE `strategy_content` ( `id` bigint NOT NULL , `content` text, PRIMARY KEY (`id`) USING BTREE ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb3 ROW_FORMAT= DYNAMIC ;
攻略排行表
CREATE TABLE `strategy_rank` ( `id` bigint NOT NULL AUTO_INCREMENT, `dest_id` bigint DEFAULT NULL , `dest_name` varchar (255 ) DEFAULT NULL , `strategy_id` bigint DEFAULT NULL , `strategy_title` varchar (255 ) DEFAULT NULL , `type` int DEFAULT NULL , `statis_time` datetime DEFAULT NULL , `statisnum` bigint DEFAULT NULL , PRIMARY KEY (`id`) USING BTREE ) ENGINE= InnoDB AUTO_INCREMENT= 11321 DEFAULT CHARSET= utf8mb3 ROW_FORMAT= DYNAMIC ;
实体类 找到 trip-article-api 模块,创建包:com.swx.article.domain
,在该包下创建如下的实体类。
攻略表实体类
Strategy @Setter @Getter @TableName("strategy") public class Strategy implements Serializable { public static final int ABROAD_NO = 0 ; public static final int ABROAD_YES = 1 ; public static final int STATE_NORMAL = 0 ; public static final int STATE_PUBLISH = 1 ; @TableId(type = IdType.AUTO) private Long id; private Long destId; private String destName; private Long themeId; private String themeName; private Long catalogId; private String catalogName; private String title; private String subTitle; private String summary; private String coverUrl; private Date createTime; private Integer isabroad; private Integer viewnum; private Integer replynum; private Integer favornum; private Integer sharenum; private Integer thumbsupnum; private Integer state; @TableField(exist = false) private boolean favorite; @TableField(exist = false) private StrategyContent content; }
攻略内容实体类
StrategyContent @Setter @Getter @TableName("strategy_content") public class StrategyContent implements Serializable { private Long id; private String content; }
攻略排行实体类
StrategyRank @Setter @Getter @TableName("strategy_rank") public class StrategyRank { public static final int TYPE_ABROAD = 1 ; public static final int TYPE_CHINA = 2 ; public static final int TYPE_HOT = 3 ; private Long id; private Long destId; private String destName; private Long strategyId; private String strategyTitle; private int type; private Date statisTime; private Long statisnum; }
文件上传 阿里云OSS
免费使用地址:https://free.aliyun.com/?crowd=personal
对象存储控制台:https://oss.console.aliyun.com/
创建资源 1、创建Bucket
控制台右侧找到 Bucket 列表,新建Bucket,读写权限选择公共读
,其他默认即可
2、创建虚拟账号
阿里云的服务都需要AccessKey和AccessSecret才能访问,主账号拥有所有的权限,所以我们需要创建子账号,给子账号授予访问OSS的权限即可
RAM访问控制:https://ram.console.aliyun.com/overview
在RAM访问控制中右侧找到用户
,创建用户,访问方式只勾选OpenAPI 调用访问
注意!创建成功后请立即保存 key 和 secret,不要离开页面,否则需要重新创建key和secret
3、给账号授权OSS
点击用户登录名称/显示名称
,即用户表格的第一列,选中刚创建的新用户 在下面的Tab栏中点击权限管理
,新增授权
给整个云账号授权,权限搜索OSS,授予:AliyunOSSFullAccess
,即所有OSS操作权限。
工具类 1、阿里云OSS工具类
基础的配置信息从配置文件中获取,为此需要从 Spring 容器中拿到其他 Environment 对象
OssUtil public class OssUtil { private final static String ossAccessDomainUrl; private final static String ossBucketName; private final static OSSClient client; static { Environment environment = SpringContextUtil.getBean(Environment.class); String endpointUrl = environment.getProperty("aliyun.oss.endpoint" ); ossBucketName = environment.getProperty("aliyun.oss.bucket-name" ); ossAccessDomainUrl = environment.getProperty("aliyun.oss.domain" ); String keyId = environment.getProperty("aliyun.accessKey.id" ); String keySecret = environment.getProperty("aliyun.accessKey.secret" ); client = new OSSClient (endpointUrl, CredentialsProviderFactory.newDefaultCredentialProvider(keyId, keySecret), new ClientConfiguration ()); } private static String getRealFileName (String saveFolder, String fileName) { return StringUtils.isNotEmpty(saveFolder) ? saveFolder + "/" + fileName : fileName; } public static String upload (String saveFolder, String contentType, String fileName, InputStream input) { if (StringUtils.isEmpty(fileName) || StringUtils.isEmpty(contentType) || null == input) { return null ; } ObjectMetadata objectMeta = new ObjectMetadata (); objectMeta.setContentType(contentType); String filePath = getRealFileName(saveFolder, fileName); try { client.putObject(ossBucketName, filePath, input, objectMeta); return ossAccessDomainUrl + filePath; } catch (Exception e) { e.printStackTrace(); return null ; } finally { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } } public static String upload (String saveFolder, String contentType, String fileName, long contentLength, InputStream input) { if (StringUtils.isEmpty(fileName) || StringUtils.isEmpty(contentType) || contentLength <= 0 || null == input) { return null ; } ObjectMetadata objectMeta = new ObjectMetadata (); objectMeta.setContentLength(contentLength); objectMeta.setContentType(contentType); String filePath = getRealFileName(saveFolder, fileName); try { client.putObject(ossBucketName, filePath, input, objectMeta); return ossAccessDomainUrl + filePath; } catch (Exception e) { e.printStackTrace(); return null ; } finally { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } } public static String upload (String saveFolder, String fileMainName, MultipartFile multipartFile) { if (null != multipartFile && !multipartFile.isEmpty()) { try { String filename = multipartFile.getOriginalFilename(); String extFileName; if (StringUtils.isNotEmpty(filename)) { extFileName = filename.substring(filename.lastIndexOf("." )); } else { extFileName = ".jpg" ; } return upload(saveFolder, multipartFile.getContentType(), fileMainName + extFileName, multipartFile.getSize(), multipartFile.getInputStream()); } catch (IOException e) { e.printStackTrace(); } } return null ; } public static String upload4SpecialName (String saveFolder, String fileName, MultipartFile multipartFile) { if (null != multipartFile && !multipartFile.isEmpty()) { try { return upload(saveFolder, multipartFile.getContentType(), fileName, multipartFile.getSize(), multipartFile.getInputStream()); } catch (IOException e) { e.printStackTrace(); } } return null ; } public static String uploadImgByBase64 (String saveFolder, String fileName, String base64ImgContent) { if (StringUtils.isEmpty(fileName) || StringUtils.isEmpty(base64ImgContent)) { return null ; } try { Matcher matcher = Pattern.compile("^data.(.*?);base64," ).matcher(base64ImgContent); if (matcher.find()) { base64ImgContent = base64ImgContent.replace(matcher.group(), "" ); } byte [] bytes = Base64Utils.decodeFromString(base64ImgContent); return upload(saveFolder, "image/jpg" , fileName, bytes.length, new ByteArrayInputStream (bytes)); } catch (Exception e) { e.printStackTrace(); return null ; } } public static void delete (String fileUrl) { if (StringUtils.isEmpty(fileUrl)) { return ; } try { fileUrl = fileUrl.replaceFirst(ossAccessDomainUrl, "" ); client.deleteObject(ossBucketName, fileUrl); } catch (OSSException | ClientException e) { e.printStackTrace(); } } public static String getByFileName (String pathFile) { return ossAccessDomainUrl + client.getObject(ossBucketName, pathFile).getKey(); } }
2、获取Spring上下文对象工具
SpringContextUtil @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext = null ; @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { if (SpringContextUtil.applicationContext == null ) { SpringContextUtil.applicationContext = applicationContext; System.out.println("----------" + applicationContext); } } public static ApplicationContext getApplicationContext () { return applicationContext; } public static Object getBean (String name) { return getApplicationContext().getBean(name); } public static <T> T getBean (Class<T> clazz) { return getApplicationContext().getBean(clazz); } public static <T> T getBean (String name, Class<T> clazz) { return getApplicationContext().getBean(name, clazz); } }
3、完善配置文件
进入桶的概览页面,找到 endpoint 和 domain
进入Nacos 后台,编辑article-service-dev.yaml
配置文件,添加如下的配置信息
aliyun: oss: endpoint: bucket-name: domain: accessKey: id: secret:
接口信息
上传Controller 找到 StrategyController,定义上传方法:
StrategyController @PostMapping("/uploadImg") public JSONObject uploadImg (MultipartFile upload) { JSONObject result = new JSONObject (); if (upload == null ) { result.put("uploaded" , 0 ); JSONObject error = new JSONObject (); error.put("message" , "请选择要上传的文件!" ); result.put("error" , error); return result; } String filename = upload.getOriginalFilename(); String mainFilename = "" ; if (StringUtils.hasText(filename)) { mainFilename = filename.substring(0 , filename.lastIndexOf("." )) + "_" + System.currentTimeMillis(); } else { mainFilename = System.currentTimeMillis() + "" ; } String url = OssUtil.upload("images" , mainFilename, upload); result.put("uploaded" , 1 ); result.put("fileName" , upload.getOriginalFilename()); result.put("url" , url); return result; }
主键查询攻略
获取攻略时需要从 strategy_content 表中查询内容信息
接口信息
Service
找到:StrategyServiceImpl,重写 getById 方法:
@Service public class StrategyServiceImpl extends ServiceImpl <StrategyMapper, Strategy> implements StrategyService { private final StrategyContentMapper strategyContentMapper; public StrategyServiceImpl (StrategyContentMapper strategyContentMapper) { this .strategyContentMapper = strategyContentMapper; } @Override public Strategy getById (Serializable id) { Strategy strategy = super .getById(id); StrategyContent strategyContent = strategyContentMapper.selectById(id); strategy.setContent(strategyContent); return strategy; } }
Controller
找到:StrategyController,定义查询方法:
StrategyController @GetMapping("/detail") public R<Strategy> getById (Long id) { return R.ok(strategyService.getById(id)); }
筛选标签查询 对应的前端页面如下:
接口信息
Mapper
找到:StrategyMapper,定义查询筛选条件的方法:
StrategyMapper public interface StrategyMapper extends BaseMapper <Strategy> { List<StrategyCondition> selectDestCondition (@Param("abroad") int abroad) ; List<StrategyCondition> selectThemeCondition () ; }
找到:StrategyMapper.xml,实现查询筛选条件的方法:
StrategyMapper.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.swx.article.mapper.StrategyMapper" > <select id ="selectDestCondition" resultType ="com.swx.article.vo.StrategyCondition" > SELECT dest_id refid, dest_name name, count(*) total FROM strategy WHERE isabroad = #{abroad} GROUP BY dest_id, dest_name ORDER BY total DESC </select > <select id ="selectThemeCondition" resultType ="com.swx.article.vo.StrategyCondition" > SELECT theme_id refid, theme_name name, count(*) total FROM strategy GROUP BY theme_id, theme_name ORDER BY total DESC </select > </mapper >
Service
找到:StrategyService,定义查询筛选条件的方法
List<StrategyCondition> findDestCondition (int abroad) ; List<StrategyCondition> findThemeCondition () ;
找到:StrategyServiceImpl,实现查询筛选条件的方法
StrategyServiceImpl @Override public List<StrategyCondition> findDestCondition (int abroad) { return getBaseMapper().selectDestCondition(abroad); }@Override public List<StrategyCondition> findThemeCondition () { return getBaseMapper().selectThemeCondition(); }
Controller
@GetMapping("/conditions") public R<Map<String, List<StrategyCondition>>> getConditions () { Map<String, List<StrategyCondition>> result = new HashMap <>(); List<StrategyCondition> chinaCondition = strategyService.findDestCondition(Strategy.ABROAD_NO); List<StrategyCondition> abroadCondition = strategyService.findDestCondition(Strategy.ABROAD_YES); List<StrategyCondition> themeCondition = strategyService.findThemeCondition(); result.put("chinaCondition" , chinaCondition); result.put("abroadCondition" , abroadCondition); result.put("themeCondition" , themeCondition); return R.ok(result); }
分页查询攻略 对应的前端页面如下:
接口信息
请求参数
请求参数除了基本参数,还包括分页参数,将公共的参数放到QueryObject中:
在 core 模块中创建包:com.swx.common.core.qo
,该包下创建 QueryObject
QueryObject @Getter @Setter public class QueryObject {private String keyword;private Integer current = 1 ;private Integer size = 10 ; }
StrategyQuery @Getter @Setter public class StrategyQuery extends QueryObject { public static final int CONDITION_THEME = 3 ; private Long destId; private Long themeId; private Long refid; private Integer type; private String orderBy; }
Service
找到:DestinationService,添加分页查询方法
StrategyService Page<Strategy> pageStrategy (StrategyQuery query) ;
找到:StrategyServiceImpl,实现上述方法
StrategyServiceImpl @Override public Page<Strategy> pageStrategy (StrategyQuery query) { if ((query.getType() != null && query.getType() != -1 )&& (query.getRefid() != null && query.getRefid() != -1 )) { if (query.getType() == StrategyQuery.CONDITION_THEME) { query.setThemeId(query.getRefid()); } else { query.setDestId(query.getRefid()); } } return super .page( new Page <>(query.getCurrent(), query.getSize()), Wrappers.<Strategy>lambdaQuery() .eq(query.getDestId() != null , Strategy::getDestId, query.getDestId()) .eq(!Objects.isNull(query.getThemeId()), Strategy::getThemeId, query.getThemeId()) .orderByDesc(query.getOrderBy() != null && query.getOrderBy().equals("viewnum" ), Strategy::getViewnum) .orderByDesc(query.getOrderBy() != null && query.getOrderBy().equals("create_time" ), Strategy::getCreateTime) ); }
Controller
找到:StrategyController,添加分页查询方法
StrategyController @GetMapping("/query") public R<Page<Strategy>> pageList (StrategyQuery query) { return R.ok(strategyService.pageStrategy(query)); }
保存更新攻略 接口信息
保存攻略
更新攻略
Service
更新或者保存方法
@Service public class StrategyServiceImpl extends ServiceImpl <StrategyMapper, Strategy> implements StrategyService { private final StrategyCatalogService strategyCatalogService; private final DestinationService destinationService; private final StrategyThemeService strategyThemeService; private final StrategyContentMapper strategyContentMapper; public StrategyServiceImpl (StrategyCatalogService strategyCatalogService, DestinationService destinationService, StrategyThemeService strategyThemeService, StrategyContentMapper strategyContentMapper) { this .strategyCatalogService = strategyCatalogService; this .destinationService = destinationService; this .strategyThemeService = strategyThemeService; this .strategyContentMapper = strategyContentMapper; } private Boolean doSaveOrUpdate (Strategy strategy) { if (StringUtils.hasText(strategy.getCoverUrl()) && !strategy.getCoverUrl().startsWith("http" )) { String fiilename = UUID.randomUUID().toString(); String url = OssUtil.uploadImgByBase64("images/strategies" , fiilename + ".jpg" , strategy.getCoverUrl()); strategy.setCoverUrl(url); } StrategyCatalog catalog = strategyCatalogService.getById(strategy.getCatalogId()); if (catalog == null ) { throw new BizException (R.CODE_ERROR_PARAM, "分类参数异常" ); } strategy.setCatalogName(catalog.getName()); strategy.setDestId(catalog.getDestId()); strategy.setDestName(catalog.getDestName()); List<Destination> toasts = destinationService.toasts(catalog.getDestId()); if (toasts.get(0 ).getId() == 1 ) { strategy.setIsabroad(Strategy.ABROAD_NO); } else { strategy.setIsabroad(Strategy.ABROAD_YES); } StrategyTheme theme = strategyThemeService.getById(strategy.getThemeId()); if (theme == null ) { throw new BizException (R.CODE_ERROR_PARAM, "主题参数异常" ); } strategy.setThemeName(theme.getName()); if (strategy.getId() == null ) { strategy.setCreateTime(new Date ()); strategy.setViewnum(0 ); strategy.setSharenum(0 ); strategy.setThumbsupnum(0 ); strategy.setReplynum(0 ); strategy.setFavornum(0 ); strategy.setState(Strategy.STATE_NORMAL); boolean save = super .save(strategy); StrategyContent content = strategy.getContent(); content.setId(strategy.getId()); return save && strategyContentMapper.insert(content) > 0 ; } boolean ret = super .updateById(strategy); StrategyContent content = strategy.getContent(); content.setId(strategy.getId()); int row = strategyContentMapper.updateById(content); return ret && row > 0 ; } }
重写保存方法
@Transactional(rollbackFor = Exception.class) @Override public boolean save (Strategy strategy) { return doSaveOrUpdate(strategy); }
重写更新方法
@Transactional(rollbackFor = Exception.class) @Override public boolean updateById (Strategy strategy) { return doSaveOrUpdate(strategy); }
Controller
保存攻略
StrategyController @PostMapping("/save") public R<?> save(Strategy strategy) { return R.ok(strategyService.save(strategy)); }
更新攻略
@PostMapping("/update") public R<?> update(Strategy strategy) { strategyService.updateById(strategy); return R.ok(); }
查询攻略内容 接口信息
Service
找到:DestinationService,添加分页查询方法
StrategyService StrategyContent getContentById (Long id) ;
找到:StrategyServiceImpl,实现上述方法
StrategyServiceImpl @Override public StrategyContent getContentById (Long id) { return strategyContentMapper.selectById(id); }
Controller
找到:StrategyController,添加查询方法
StrategyController @GetMapping("/content") public R<StrategyContent> getContentById (Long id) { return R.ok(strategyService.getContentById(id)); }
分组查询攻略
根据攻略类别分组查询指定目的地下的攻略
对应的前端页面如下:
接口信息
Mapper
找到:StrategyMapper,定义分组查询的方法:
StrategyMapper public interface StrategyMapper extends BaseMapper <Strategy> { List<StrategyCatalog> selectGroupsByDestId (@Param("destId") Long destId) ; }
找到:StrategyMapper.xml,实现分组查询的方法:
StrategyMapper.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.swx.article.mapper.StrategyMapper" > <resultMap id ="BaseResultMap" type ="com.swx.article.domain.Strategy" > <id property ="id" column ="id" /> <result property ="title" column ="title" /> </resultMap > <resultMap id ="CatalogGroupMap" type ="com.swx.article.domain.StrategyCatalog" > <id property ="id" column ="catalog_id" /> <result property ="name" column ="catalog_name" /> <collection property ="strategies" resultMap ="BaseResultMap" columnPrefix ="s_" > </collection > </resultMap > <select id ="selectGroupsByDestId" resultMap ="CatalogGroupMap" > SELECT t.catalog_id, t.catalog_name, s.id s_id, s.title s_title FROM (SELECT catalog_id, catalog_name FROM strategy WHERE dest_id = #{destId} GROUP BY catalog_id, catalog_name) t JOIN strategy s ON t.catalog_id = s.catalog_id </select > </mapper >
Service
找到:StrategyService,定义分组查询的方法
StrategyService List<StrategyCatalog> findGroupsByDestId (Long destId) ;
找到:StrategyServiceImpl,实现查询筛选条件的方法
StrategyServiceImpl @Override public List<StrategyCatalog> findGroupsByDestId (Long destId) { return baseMapper.selectGroupsByDestId(destId); }
Controller
StrategyController @GetMapping("/groups") public R<List<StrategyCatalog>> groupByCatalog (Long destId) { return R.ok(strategyService.findGroupsByDestId(destId)); }
攻略排行榜
攻略排行榜采用收藏数+点赞数的方式,计算导致索引失效,查询效率变低。排行榜的实时性要求不高,因此设计strategy_rank表,用于保存排行榜信息,使用Spring Boot定时任务,每10分钟获取排行数据保存到strategy_rank表中,查询时从该表查询即可。
数据处理模块 新建 trip-data-server 模块,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" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > com.swx</groupId > <artifactId > trip-modules</artifactId > <version > 1.0.0</version > </parent > <artifactId > trip-data-server</artifactId > <dependencies > <dependency > <groupId > com.swx</groupId > <artifactId > trip-article-api</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > </dependency > </dependencies > </project >
开启任务调度
@EnableScheduling @MapperScan("com.swx.data.mapper") @SpringBootApplication public class DataApplication { public static void main (String[] args) { SpringApplication.run(DataApplication.class, args); } }
在 trip-data-server 模块下创建 StrategyMapper 和 StrategyRankMapper
查询排行数据 新建 StrategyMapper 文件
public interface StrategyMapper extends BaseMapper <Strategy> { List<StrategyRank> selectStrategyRankByAbroad (@Param("abroad") Integer abroad) ; List<StrategyRank> selectStrategyRankHotList () ; }
在 StrategyMapper.xml 中实现SQL
StrategyMapper.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.swx.data.mapper.StrategyMapper" > <select id ="selectStrategyRankByAbroad" resultMap ="com.swx.data.mapper.StrategyRankMapper.BaseResultMap" > SELECT id strategy_id, title strategy_title, dest_id, dest_name, thumbsupnum + favornum statisnum FROM strategy WHERE isabroad = #{abroad} ORDER BY statisnum desc limit 10 </select > <select id ="selectStrategyRankHotList" resultMap ="com.swx.data.mapper.StrategyRankMapper.BaseResultMap" > SELECT id strategy_id, title strategy_title, dest_id, dest_name, (viewnum * 0.2) + (replynum * 0.4) + (favornum * 0.4) statisnum FROM strategy ORDER BY statisnum desc limit 10 </select > </mapper >
批量插入排行数据 新建 StrategyRankMapper 文件
StrategyRankMapper public interface StrategyRankMapper extends BaseMapper <StrategyRank> { int batchInsert (@Param("strategyRanks") List<StrategyRank> strategyRanks) ; }
在 StrategyRankMapper.xml 中实现SQL
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.swx.data.mapper.StrategyRankMapper" > <resultMap id ="BaseResultMap" type ="com.swx.article.domain.StrategyRank" > <id property ="id" column ="id" /> <result property ="destId" column ="dest_id" /> <result property ="destName" column ="dest_name" /> <result property ="strategyId" column ="strategy_id" /> <result property ="strategyTitle" column ="strategy_title" /> <result property ="type" column ="type" /> <result property ="statisTime" column ="statis_time" /> <result property ="statisnum" column ="statisnum" /> </resultMap > <insert id ="batchInsert" > INSERT INTO strategy_rank(dest_id, dest_name, strategy_id, strategy_title, type, statis_time, statisnum) values <foreach collection ="strategyRanks" item ="item" separator ="," > (#{item.destId}, #{item.destName}, #{item.strategyId}, #{item.strategyTitle}, #{item.type}, #{item.statisTime}, #{item.statisnum}) </foreach > </insert > </mapper >
任务调度Service 创建 StrategyRankStatisticJob
@Slf4j @Service public class StrategyRankStatisticJob { private final StrategyMapper strategyMapper; private final StrategyRankMapper strategyRankMapper; public StrategyRankStatisticJob (StrategyMapper strategyMapper, StrategyRankMapper strategyRankMapper) { this .strategyMapper = strategyMapper; this .strategyRankMapper = strategyRankMapper; } @Transactional(rollbackFor = Exception.class) @Scheduled(cron = "0 * * * * *") public void statisticRank () { log.info("[攻略排行统计] 排行数据统计开始 >>>>>>>>>" ); Date now = new Date (); strategyRankMapper.delete(Wrappers.<StrategyRank>lambdaQuery().lt(StrategyRank::getStatisTime, now)); log.info("[攻略排行统计] 删除旧的排行数据 >>>>>>>>>" ); doStatistic(now, StrategyRank.TYPE_CHINA, () -> strategyMapper.selectStrategyRankByAbroad(Strategy.ABROAD_NO)); doStatistic(now, StrategyRank.TYPE_ABROAD, () -> strategyMapper.selectStrategyRankByAbroad(Strategy.ABROAD_YES)); doStatistic(now, StrategyRank.TYPE_HOT, strategyMapper::selectStrategyRankHotList); } public void doStatistic (Date now, Integer type, Supplier<List<StrategyRank>> rankSupplier) { List<StrategyRank> strategyRanks = rankSupplier.get(); log.info("[攻略排行统计] 排行数据统计:type={}, ranks={}" , type, strategyRanks.size()); for (StrategyRank strategyRank : strategyRanks) { strategyRank.setType(type); strategyRank.setStatisTime(now); } strategyRankMapper.batchInsert(strategyRanks); } }
查询排行榜 接口信息
Service
新建 StrategyRankService,定义获取排行方法
StrategyRankService public interface StrategyRankService extends IService <StrategyRank> { List<StrategyRank> selectLastRanksByType (int type) ; }
实现上述方法
StrategyRankServiceImpl @Service public class StrategyRankServiceImpl extends ServiceImpl <StrategyRankMapper, StrategyRank> implements StrategyRankService { @Override public List<StrategyRank> selectLastRanksByType (int type) { return list(Wrappers.<StrategyRank>lambdaQuery() .eq(StrategyRank::getType, type) .orderByDesc(StrategyRank::getStatisTime) .last("limit 10" ) ); } }
Controller
StrategyController @RestController @RequestMapping("/strategies") public class StrategyController { private final StrategyRankService strategyRankService; public StrategyController (StrategyRankService strategyRankService) { this .strategyRankService = strategyRankService; } @GetMapping("/ranks") public R<JSONObject> ranks () { List<StrategyRank> chinaRank = strategyRankService.selectLastRanksByType(StrategyRank.TYPE_CHINA); List<StrategyRank> abroadRank = strategyRankService.selectLastRanksByType(StrategyRank.TYPE_CHINA); List<StrategyRank> hotRank = strategyRankService.selectLastRanksByType(StrategyRank.TYPE_HOT); JSONObject jsonObject = new JSONObject (); jsonObject.put("abroadRank" , abroadRank); jsonObject.put("chinaRank" , chinaRank); jsonObject.put("hotRank" , hotRank); return R.ok(jsonObject); } }
删除攻略 接口信息
Controller
StrategyController @PostMapping("/delete/{id}") public R<?> delete(@PathVariable Long id) { strategyService.removeById(id); return R.ok(); }
查询浏览量前三的攻略 接口信息
Service
找到:StrategyService,定义查询的方法
StrategyService List<Strategy> findViewnumTop3 (Long destId) ;
找到:StrategyServiceImpl,实现查询的方法
StrategyServiceImpl @Override public List<Strategy> findViewnumTop3 (Long destId) { return super .list(Wrappers.<Strategy>lambdaQuery() .eq(Strategy::getDestId, destId) .orderByDesc(Strategy::getViewnum) .last("limit 3" ) ); }
Controller
StrategyController @GetMapping("/viewnumTop3") public R<List<Strategy>> viewnumTop3 (Long destId) { return R.ok(strategyService.findViewnumTop3(destId)); }