Merge branch 'dev_20251030'

This commit is contained in:
2025-10-30 09:49:24 +08:00
30 changed files with 686 additions and 15 deletions

View File

@@ -41,6 +41,7 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j' // MySQL 驱动 runtimeOnly 'com.mysql:mysql-connector-j' // MySQL 驱动
annotationProcessor 'org.projectlombok:lombok' // Lombok 注解处理 annotationProcessor 'org.projectlombok:lombok' // Lombok 注解处理
testImplementation 'org.springframework.boot:spring-boot-starter-test' // 测试框架 testImplementation 'org.springframework.boot:spring-boot-starter-test' // 测试框架
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' // thymeleaf模版引擎
} }
tasks.named('test') { tasks.named('test') {

View File

@@ -27,10 +27,12 @@ public class SaTokenConfig implements WebMvcConfigurer {
.excludePathPatterns("/sys/user/login") .excludePathPatterns("/sys/user/login")
// 排除反馈接口(支持匿名提交) // 排除反馈接口(支持匿名提交)
.excludePathPatterns("/feedback", "/feedback/**") .excludePathPatterns("/feedback", "/feedback/**")
// 排除教程接口(支持匿名查询)
.excludePathPatterns("/tutorial", "/tutorial/**")
// 排除固件查询接口(不需要登录) // 排除固件查询接口(不需要登录)
.excludePathPatterns("/firmware/**") .excludePathPatterns("/firmware/**")
// 排除静态资源 // 排除静态资源
.excludePathPatterns("/", "/index.html", "/*.html", "/*.css", "/*.js", "/*.ico", "/static/**") .excludePathPatterns("/", "/loading.html", "/admin/login.html", "/*.css", "/*.js", "/*.ico", "/static/**")
// 排除后台管理静态资源 // 排除后台管理静态资源
.excludePathPatterns("/admin/**") .excludePathPatterns("/admin/**")
// 排除 Druid 监控 // 排除 Druid 监控

View File

@@ -0,0 +1,73 @@
package com.corewing.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
* 教程
*/
@Data
@TableName("app_tutorial")
public class Tutorial implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 教程id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 教程标题
*/
private String tutorialTitle;
/**
* 教程描述
*/
private String description;
/**
* 教程详情
*/
private String content;
/**
* 查看次数
*/
private Integer viewCount;
/**
* 推荐状态0不推荐1推荐
*/
private Integer recommendStatus;
/**
* 状态1正常2关闭
*/
private Integer status;
/**
* 语言中文zh英文en
*/
private String lang;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -0,0 +1,75 @@
package com.corewing.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
* 教程分类
*/
@Data
@TableName("app_tutorial_category")
public class TutorialCategory implements Serializable {
/**
* 分类id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 图标
*/
private String icon;
/**
* 颜色
*/
private String color;
/**
* 分类名称
*/
private String categoryTitle;
/**
* 分类描述
*/
private String description;
/**
* 类别category分类tag标签
*/
private String type;
/**
* 置首页0不置首1置首页
*/
private Integer firstStatus;
/**
* 状态1正常2关闭
*/
private Integer status;
/**
* 语言中文zh英文en
*/
private String lang;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -0,0 +1,35 @@
package com.corewing.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* 教程与分类关系实体
*/
@Data
@TableName("app_tutorial_category_relation")
public class TutorialCategoryRelation implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 教程id
*/
private Long TutorialId;
/**
* 教程分类id
*/
private Long CategoryId;
}

View File

@@ -0,0 +1,12 @@
package com.corewing.app.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.corewing.app.entity.TutorialCategory;
import org.apache.ibatis.annotations.Mapper;
/**
* 教程分类 Mapper接口
*/
@Mapper
public interface TutorialCategoryMapper extends BaseMapper<TutorialCategory> {
}

View File

@@ -0,0 +1,13 @@
package com.corewing.app.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.corewing.app.entity.TutorialCategoryRelation;
import org.apache.ibatis.annotations.Mapper;
/**
* 教程与教程分类关系Mapper接口
*/
@Mapper
public interface TutorialCategoryRelationMapper extends BaseMapper<TutorialCategoryRelation> {
}

View File

@@ -0,0 +1,17 @@
package com.corewing.app.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.corewing.app.entity.Tutorial;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 教程 Mapper 接口
*/
@Mapper
public interface TutorialMapper extends BaseMapper<Tutorial> {
Page<Tutorial> pageList(Page<Tutorial> page, @Param("categoryId") int categoryId, @Param("tutorialTitle") String tutorialTitle, @Param("lang") String lang);
}

View File

@@ -0,0 +1,24 @@
package com.corewing.app.modules.admin;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class SysMainController {
@GetMapping({"/", "/index.html"})
public String loading() {
return "/admin/loading";
}
@GetMapping("/admin/login.html")
public String login() {
return "/admin/login";
}
@GetMapping("/admin/index.html")
public String adminIndex() {
return "/admin/index";
}
}

View File

@@ -1,4 +1,4 @@
package com.corewing.app.controller; package com.corewing.app.modules.admin;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import com.corewing.app.common.Result; import com.corewing.app.common.Result;

View File

@@ -1,4 +1,4 @@
package com.corewing.app.controller; package com.corewing.app.modules.app;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;

View File

@@ -1,4 +1,4 @@
package com.corewing.app.controller; package com.corewing.app.modules.app;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;

View File

@@ -1,4 +1,4 @@
package com.corewing.app.controller; package com.corewing.app.modules.app;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;

View File

@@ -0,0 +1,111 @@
package com.corewing.app.modules.app;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.corewing.app.common.Result;
import com.corewing.app.entity.Tutorial;
import com.corewing.app.entity.TutorialCategory;
import com.corewing.app.service.TutorialCategoryService;
import com.corewing.app.service.TutorialService;
import com.corewing.app.util.I18nUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 教程接口
*/
@RequestMapping("/tutorial")
@Controller
@Slf4j
public class TutorialController {
private final TutorialService tutorialService;
private final TutorialCategoryService tutorialCategoryService;
public TutorialController(TutorialService tutorialService, TutorialCategoryService tutorialCategoryService) {
this.tutorialService = tutorialService;
this.tutorialCategoryService = tutorialCategoryService;
}
/**
* 跳转到界面查看教程详情
* @param tutorialId 教程id
* @param model 数据模型
* @return 详情页
*/
@GetMapping("/viewDetail/{tutorialId}")
public String viewDetail(@PathVariable Long tutorialId, ModelMap model) {
Tutorial tutorial = tutorialService.getById(tutorialId);
model.put("tutorial", tutorial);
return "/app/tutorial/viewDetail";
}
/**
* 添加查看次数
* @param tutorialId 教程id
* @return
*/
@GetMapping("/addViewCount")
@ResponseBody
public Result<String> addViewCount(@RequestParam Long tutorialId) {
Tutorial tutorial = tutorialService.getById(tutorialId);
if(tutorial == null) {
return Result.error();
}
tutorial.setViewCount(tutorial.getViewCount() + 1);
tutorialService.updateById(tutorial);
return Result.success();
}
/**
* 分类查询列表
*
* @param firstStatus 置首状态(选填)
*
*/
@GetMapping("/category")
@ResponseBody
public Result<List<TutorialCategory>> category(
@RequestParam(required = false, defaultValue = "0") Integer firstStatus
) {
log.info("当前语言环境:{}", I18nUtil.getCurrentLocale().getLanguage());
LambdaQueryWrapper<TutorialCategory> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(firstStatus != 0, TutorialCategory::getFirstStatus, firstStatus);
wrapper.eq(TutorialCategory::getLang, I18nUtil.getCurrentLocale().getLanguage());
List<TutorialCategory> list = tutorialCategoryService.list(wrapper);
return Result.success(list);
}
/**
* 分页查询教程列表
*
* @param current 当前页码
* @param size 每页数量
* @param categoryId 分类ID选填
* @param tutorialTitle 教程标题(选填)
*
*/
@GetMapping("/page")
@ResponseBody
public Result<IPage<Tutorial>> getPageList(
@RequestParam(defaultValue = "1") Long current,
@RequestParam(defaultValue = "10") Long size,
@RequestParam(required = false, defaultValue = "0") Integer categoryId,
@RequestParam(required = false) String tutorialTitle) {
try {
Page<Tutorial> page = new Page<>(current, size);
IPage<Tutorial> pageResult = tutorialService.pageList(page, categoryId, tutorialTitle, I18nUtil.getCurrentLocale().getLanguage());
return Result.success(pageResult);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
}

View File

@@ -1,4 +1,4 @@
package com.corewing.app.controller; package com.corewing.app.modules.app;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import com.corewing.app.common.Result; import com.corewing.app.common.Result;

View File

@@ -0,0 +1,7 @@
package com.corewing.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.corewing.app.entity.TutorialCategoryRelation;
public interface TutorialCategoryRelationService extends IService<TutorialCategoryRelation> {
}

View File

@@ -0,0 +1,7 @@
package com.corewing.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.corewing.app.entity.TutorialCategory;
public interface TutorialCategoryService extends IService<TutorialCategory> {
}

View File

@@ -0,0 +1,10 @@
package com.corewing.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.corewing.app.entity.Tutorial;
public interface TutorialService extends IService<Tutorial> {
IPage<Tutorial> pageList(Page<Tutorial> page, int categoryId, String tutorialTitle, String lang);
}

View File

@@ -0,0 +1,11 @@
package com.corewing.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.corewing.app.entity.TutorialCategoryRelation;
import com.corewing.app.mapper.TutorialCategoryRelationMapper;
import com.corewing.app.service.TutorialCategoryRelationService;
import org.springframework.stereotype.Service;
@Service
public class TutorialCategoryRelationServiceImpl extends ServiceImpl<TutorialCategoryRelationMapper, TutorialCategoryRelation> implements TutorialCategoryRelationService {
}

View File

@@ -0,0 +1,11 @@
package com.corewing.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.corewing.app.entity.TutorialCategory;
import com.corewing.app.mapper.TutorialCategoryMapper;
import com.corewing.app.service.TutorialCategoryService;
import org.springframework.stereotype.Service;
@Service
public class TutorialCategoryServiceImpl extends ServiceImpl<TutorialCategoryMapper, TutorialCategory> implements TutorialCategoryService {
}

View File

@@ -0,0 +1,24 @@
package com.corewing.app.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.corewing.app.entity.Tutorial;
import com.corewing.app.mapper.TutorialMapper;
import com.corewing.app.service.TutorialService;
import org.springframework.stereotype.Service;
@Service
public class TutorialServiceImpl extends ServiceImpl<TutorialMapper, Tutorial> implements TutorialService {
private final TutorialMapper tutorialMapper;
public TutorialServiceImpl(TutorialMapper tutorialMapper) {
this.tutorialMapper = tutorialMapper;
}
@Override
public IPage<Tutorial> pageList(Page<Tutorial> page, int categoryId, String tutorialTitle, String lang) {
return tutorialMapper.pageList(page, categoryId, tutorialTitle, lang);
}
}

View File

@@ -0,0 +1,58 @@
package com.corewing.app.vo;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
public class TutorialVO {
private Long id;
/**
* 教程标题
*/
private String tutorialTitle;
/**
* 教程描述
*/
private String description;
/**
* 教程详情
*/
private String content;
/**
* 查看次数
*/
private Integer viewCount;
/**
* 推荐状态0不推荐1推荐
*/
private Integer recommendStatus;
/**
* 状态1正常2关闭
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 教程分类名称
*/
private String categoryTitle;
}

View File

@@ -0,0 +1,44 @@
/*
Navicat Premium Dump SQL
Source Server : 120.24.204.180
Source Server Type : MySQL
Source Server Version : 80036 (8.0.36)
Source Host : 120.24.204.180:3306
Source Schema : app
Target Server Type : MySQL
Target Server Version : 80036 (8.0.36)
File Encoding : 65001
Date: 28/10/2025 13:46:26
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for app_tutorial
-- ----------------------------
DROP TABLE IF EXISTS `app_tutorial`;
CREATE TABLE `app_tutorial` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`tutorial_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '教程标题',
`description` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '教程描述',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '教程详情',
`view_count` int DEFAULT '0' COMMENT '查看次数',
`recommend_status` int DEFAULT '0' COMMENT '推荐状态0不推荐1推荐',
`status` tinyint(1) DEFAULT '1' COMMENT '状态1正常2关闭',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='使用教程表';
-- ----------------------------
-- Records of app_tutorial
-- ----------------------------
BEGIN;
INSERT INTO `app_tutorial` (`id`, `tutorial_title`, `description`, `content`, `view_count`, `recommend_status`, `status`, `create_time`, `update_time`) VALUES (1, '快速开始指南', '了解酷翼应用的基本功能与布局,快速上手使用各项功能。', NULL, 0, 0, 1, '2025-10-28 12:03:52', NULL);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,44 @@
/*
Navicat Premium Dump SQL
Source Server : 120.24.204.180
Source Server Type : MySQL
Source Server Version : 80036 (8.0.36)
Source Host : 120.24.204.180:3306
Source Schema : app
Target Server Type : MySQL
Target Server Version : 80036 (8.0.36)
File Encoding : 65001
Date: 28/10/2025 13:46:32
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for app_tutorial_category
-- ----------------------------
DROP TABLE IF EXISTS `app_tutorial_category`;
CREATE TABLE `app_tutorial_category` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`icon` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图标',
`category_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '分类名称',
`description` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分类描述',
`first_status` int NOT NULL DEFAULT '2' COMMENT '置首页1置首2不置首',
`type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '类别category分类tag标签',
`status` tinyint(1) DEFAULT '0' COMMENT '状态1正常2关闭',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='使用教程分类/标签表';
-- ----------------------------
-- Records of app_tutorial_category
-- ----------------------------
BEGIN;
INSERT INTO `app_tutorial_category` (`id`, `icon`, `category_title`, `description`, `first_status`, `type`, `status`, `create_time`, `update_time`) VALUES (1, NULL, '快速指南', NULL, 1, 'category', 1, '2025-10-28 12:06:03', NULL);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,38 @@
/*
Navicat Premium Dump SQL
Source Server : 120.24.204.180
Source Server Type : MySQL
Source Server Version : 80036 (8.0.36)
Source Host : 120.24.204.180:3306
Source Schema : app
Target Server Type : MySQL
Target Server Version : 80036 (8.0.36)
File Encoding : 65001
Date: 28/10/2025 13:46:38
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for app_tutorial_category_relation
-- ----------------------------
DROP TABLE IF EXISTS `app_tutorial_category_relation`;
CREATE TABLE `app_tutorial_category_relation` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`tutorial_id` bigint DEFAULT NULL COMMENT '教程id',
`category_id` bigint DEFAULT NULL COMMENT '教程分类id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='教程与教程分类关系表';
-- ----------------------------
-- Records of app_tutorial_category_relation
-- ----------------------------
BEGIN;
INSERT INTO `app_tutorial_category_relation` (`id`, `tutorial_id`, `category_id`) VALUES (1, 1, 1);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,44 @@
<?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.corewing.app.mapper.TutorialMapper">
<!-- 结果映射 -->
<resultMap id="VOResultMap" type="com.corewing.app.vo.TutorialVO">
<id column="id" property="id"/>
<result column="tutorial_title" property="tutorialTitle"/>
<result column="description" property="description"/>
<result column="content" property="content"/>
<result column="view_count" property="viewCount"/>
<result column="recommend_status" property="recommendStatus"/>
<result column="status" property="status"/>
<result column="category_title" property="categoryTitle"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<!-- 基础查询SQL片段 -->
<sql id="selectVOSql">
select c.*, cc.category_title
from app_tutorial c
left join app_tutorial_category_relation ccr on c.id = ccr.tutorial_id
left join app_tutorial_category cc on cc.id = ccr.category_id
</sql>
<!-- 分页查询 -->
<select id="pageList" resultMap="VOResultMap">
<include refid="selectVOSql"/>
<where>
c.status = 1
and c.lang = #{lang}
<if test="categoryId != null and categoryId != 0">
AND cc.id = #{categoryId}
</if>
<if test="tutorialTitle != null and tutorialTitle != ''">
AND c.tutorial_title like CONCAT('%', #{tutorialTitle}, '%')
</if>
</where>
ORDER BY c.recommend_status,c.create_time asc
</select>
</mapper>

View File

@@ -488,7 +488,7 @@
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
if (token) { if (token) {
// 如果已有 token尝试跳转到主页 // 如果已有 token尝试跳转到主页
// window.location.href = '/admin/index.html'; // window.location.href = '/admin/loading.html';
} }
} }
}).mount('#app'); }).mount('#app');

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${tutorial?.tutorialTitle}"></title>
</head>
<body>
</body>
</html>