├── wall-vue ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ ├── img.png │ │ ├── logo.png │ │ ├── video.png │ │ ├── pointer.cur │ │ └── background.svg │ ├── App.vue │ ├── utils │ │ ├── pub.js │ │ ├── cache.js │ │ └── request.js │ ├── views │ │ ├── other │ │ │ └── 404.vue │ │ ├── auth │ │ │ └── login.vue │ │ └── admin │ │ │ ├── setting.vue │ │ │ ├── tag.vue │ │ │ ├── index.vue │ │ │ ├── list.vue │ │ │ └── upload.vue │ ├── components │ │ ├── footer-index.vue │ │ ├── justifiedGallery.min.css │ │ └── jquery.justifiedGallery.min.js │ ├── main.js │ └── router │ │ └── index.js ├── babel.config.js ├── jsconfig.json ├── README.md ├── vue.config.js ├── package-copy.json └── package.json ├── wall-service ├── src │ └── main │ │ ├── resources │ │ ├── mapper │ │ │ ├── TTagMapper.xml │ │ │ ├── TOptionMapper.xml │ │ │ ├── TResourceMapper.xml │ │ │ └── TUserMapper.xml │ │ ├── banner.txt │ │ └── application.yml │ │ └── java │ │ └── cn │ │ └── ityao │ │ └── wall │ │ ├── mapper │ │ ├── TTagMapper.java │ │ ├── TOptionMapper.java │ │ ├── TResourceMapper.java │ │ └── TUserMapper.java │ │ ├── service │ │ ├── ITTagService.java │ │ ├── ITOptionService.java │ │ ├── ITUserService.java │ │ ├── impl │ │ │ ├── TTagServiceImpl.java │ │ │ ├── TUserServiceImpl.java │ │ │ ├── TOptionServiceImpl.java │ │ │ └── TResourceServiceImpl.java │ │ └── ITResourceService.java │ │ ├── vo │ │ ├── RepassVo.java │ │ └── TOptionVo.java │ │ ├── common │ │ └── SuperController.java │ │ ├── exception │ │ ├── Unauthorizedxception.java │ │ ├── CustomException.java │ │ ├── CustomErrorController.java │ │ └── ExceptionHandleController.java │ │ ├── config │ │ ├── MybatisPlusConfig.java │ │ ├── ErrorConfig.java │ │ ├── ImageConfig.java │ │ ├── JwtConfig.java │ │ └── JwtInterceptor.java │ │ ├── WallApplication.java │ │ ├── util │ │ ├── StringUtils.java │ │ ├── encryption │ │ │ ├── MD5Util.java │ │ │ └── DesUtil.java │ │ ├── TokenUtil.java │ │ ├── picture │ │ │ └── CompressUtils.java │ │ ├── CoverExtractor.java │ │ ├── DataResult.java │ │ └── FileUtils.java │ │ ├── controller │ │ ├── VideoHttpRequestHandler.java │ │ ├── AuthController.java │ │ ├── TUserController.java │ │ ├── TOptionController.java │ │ ├── TTagController.java │ │ └── TResourceController.java │ │ └── entity │ │ ├── TOption.java │ │ ├── TTag.java │ │ ├── TUser.java │ │ └── TResource.java ├── README.md ├── wall.sql └── pom.xml ├── 免责声明.md └── README.md /wall-vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/super-tongyao/wall/HEAD/wall-vue/public/favicon.ico -------------------------------------------------------------------------------- /wall-vue/src/assets/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/super-tongyao/wall/HEAD/wall-vue/src/assets/img.png -------------------------------------------------------------------------------- /wall-vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/super-tongyao/wall/HEAD/wall-vue/src/assets/logo.png -------------------------------------------------------------------------------- /wall-vue/src/assets/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/super-tongyao/wall/HEAD/wall-vue/src/assets/video.png -------------------------------------------------------------------------------- /wall-vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /wall-vue/src/assets/pointer.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/super-tongyao/wall/HEAD/wall-vue/src/assets/pointer.cur -------------------------------------------------------------------------------- /wall-service/src/main/resources/mapper/TTagMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /wall-service/src/main/resources/mapper/TOptionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /wall-service/src/main/resources/mapper/TResourceMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /wall-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /wall-vue/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/mapper/TTagMapper.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.mapper; 2 | 3 | import cn.ityao.wall.entity.TTag; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 标签表 Mapper 接口 9 | *

10 | * 11 | * @author tongyao 12 | * @since 2023-02-14 13 | */ 14 | public interface TTagMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/ITTagService.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service; 2 | 3 | import cn.ityao.wall.entity.TTag; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 标签表 服务类 9 | *

10 | * 11 | * @author tongyao 12 | * @since 2023-02-14 13 | */ 14 | public interface ITTagService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/vo/RepassVo.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.vo; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * wall-service >>> 【cn.ityao.wall.vo】 7 | * 修改密码vo 8 | * 9 | * @author: tongyao 10 | * @since: 2023-02-14 11 | */ 12 | @Data 13 | public class RepassVo { 14 | private String oldPass; 15 | private String newPass; 16 | private String reNewPass; 17 | } 18 | -------------------------------------------------------------------------------- /wall-service/README.md: -------------------------------------------------------------------------------- 1 | # wall-service 2 | 3 | ## 修改配置 4 | 5 | 修改```src/main/resources/application.yml```配置文件,及修改你的数据库地址及端口。 6 | ``` 7 | # project prot 8 | server: 9 | port: 9999 10 | 11 | # database config 12 | mysql: 13 | database: wall 14 | port: 3306 15 | ip: localhost 16 | username: root 17 | password: root 18 | ``` 19 | ## 启动项目 20 | 21 | 打开```WallApplication.java```类,右键Run运行此项目。 22 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/mapper/TOptionMapper.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.mapper; 2 | 3 | import cn.ityao.wall.entity.TOption; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 配置表 Mapper 接口 9 | *

10 | * 11 | * @author tongyao 12 | * @since 2023-02-14 13 | */ 14 | public interface TOptionMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/mapper/TResourceMapper.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.mapper; 2 | 3 | import cn.ityao.wall.entity.TResource; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 资源表 Mapper 接口 9 | *

10 | * 11 | * @author tongyao 12 | * @since 2023-02-14 13 | */ 14 | public interface TResourceMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /wall-service/src/main/resources/mapper/TUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /wall-vue/src/utils/pub.js: -------------------------------------------------------------------------------- 1 | import {message} from "ant-design-vue"; 2 | import {getLocal} from "@/utils/cache"; 3 | 4 | export function copyText(text,msg){ 5 | let save = function (e){ 6 | e.clipboardData.setData('text/plain',text); 7 | e.preventDefault(); 8 | } 9 | document.addEventListener('copy',save); 10 | document.execCommand("copy"); 11 | message.success(msg); 12 | } 13 | -------------------------------------------------------------------------------- /wall-service/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ____ __ ____ ___ __ __ 2 | \ \ / \ / / / \ | | | | 3 | \ \/ \/ / / ^ \ | | | | 4 | \ / / /_\ \ | | | | 5 | \ /\ / / _____ \ | `----.| `----. 6 | \__/ \__/ /__/ \__\ |_______||_______| v${project-info.version} 7 | 8 | GitHub开源地址: https://github.com/super-tongyao/wall 9 | 10 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/ITOptionService.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service; 2 | 3 | import cn.ityao.wall.entity.TOption; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 配置表 服务类 9 | *

10 | * 11 | * @author tongyao 12 | * @since 2023-02-14 13 | */ 14 | public interface ITOptionService extends IService { 15 | 16 | public String getOption(String optionKey); 17 | } 18 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/ITUserService.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service; 2 | 3 | import cn.ityao.wall.entity.TUser; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 服务类 9 | *

10 | * 11 | * @author tongyao 12 | * @since 2023-02-14 13 | */ 14 | public interface ITUserService extends IService { 15 | 16 | public TUser loadUserByUsername(String userName); 17 | } 18 | -------------------------------------------------------------------------------- /wall-vue/src/views/other/404.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/common/SuperController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.common; 2 | 3 | import cn.ityao.wall.service.ITOptionService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | 6 | /** 7 | * wall-service >>> 【cn.ityao.wall.common】 8 | * 通用Controller类 9 | * 10 | * @author: tongyao 11 | * @since: 2023-02-14 12 | */ 13 | public class SuperController { 14 | 15 | @Autowired 16 | public ITOptionService itOptionService; 17 | } 18 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/mapper/TUserMapper.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.mapper; 2 | 3 | import cn.ityao.wall.entity.TUser; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | /** 8 | *

9 | * Mapper 接口 10 | *

11 | * 12 | * @author tongyao 13 | * @since 2023-02-14 14 | */ 15 | public interface TUserMapper extends BaseMapper { 16 | 17 | public TUser loadUserByUsername(@Param("userName") String userName); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/exception/Unauthorizedxception.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.exception; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * byte-cabinet >>> 【com.cabinet.common.exception】 7 | * 自定义权限异常 8 | * 9 | * @author: tongyao 10 | * @since: 2023-02-14 11 | */ 12 | @Data 13 | public class Unauthorizedxception extends RuntimeException { 14 | Integer code; 15 | public Unauthorizedxception(Integer code, String message) { 16 | super(message); 17 | this.code = code; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wall-vue/src/utils/cache.js: -------------------------------------------------------------------------------- 1 | export function setLocal (key, value) { 2 | window.localStorage.setItem(key, JSON.stringify(value)) 3 | } 4 | export function getLocal (key) { 5 | const value = window.localStorage.getItem(key) 6 | try { 7 | return JSON.parse(window.localStorage.getItem(key)) 8 | } catch (error) { 9 | return value 10 | } 11 | } 12 | export function removeLocal (key) { 13 | window.localStorage.removeItem(key) 14 | } 15 | export function clearLocalAll () { 16 | window.localStorage.clear(); 17 | } -------------------------------------------------------------------------------- /wall-vue/README.md: -------------------------------------------------------------------------------- 1 | # wall-vue 2 | 3 | ## 安装 4 | ``` 5 | npm install 6 | ``` 7 | ## 修改地址 8 | 9 | 编辑根目录```vue.config.js```文件,修改你的后端请求地址和端口。 10 | ``` 11 | devServer: { 12 | proxy: { 13 | '/api': { 14 | target: 'http://localhost:9999/', 15 | ws: true, 16 | changeOrigin: true, 17 | pathRewrite: { 18 | '^/api': '' 19 | } 20 | } 21 | } 22 | } 23 | ``` 24 | 25 | ## 运行项目 26 | ``` 27 | npm run serve 28 | ``` 29 | 30 | ## 编译项目 31 | ``` 32 | npm run build 33 | ``` 34 | ## 更多配置 35 | 请看 [Configuration Reference](https://cli.vuejs.org/config/). 36 | -------------------------------------------------------------------------------- /wall-vue/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | module.exports = defineConfig({ 3 | transpileDependencies: true, 4 | 5 | // 关闭语法检查 6 | lintOnSave:false, 7 | 8 | devServer: { 9 | proxy: { 10 | '/api': { 11 | target: 'http://localhost:9999', 12 | ws: true, 13 | changeOrigin: true, 14 | pathRewrite: { 15 | '^/api': '' 16 | } 17 | } 18 | } 19 | }, 20 | pages: { 21 | index: { 22 | entry: 'src/main.js', // 入口文件 23 | title: '站点正在加载中...' 24 | } 25 | } 26 | }) 27 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/exception/CustomException.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.exception; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * byte-cabinet >>> 【com.cabinet.common.exception】 7 | * 自定义异常 8 | * 9 | * @author: tongyao 10 | * @since: 2023-02-14 11 | */ 12 | @Data 13 | public class CustomException extends RuntimeException { 14 | Integer httpCode; 15 | Integer JsonCode; 16 | public CustomException(Integer httpCode, Integer JsonCode, String message) { 17 | super(message); 18 | this.httpCode = httpCode; 19 | this.JsonCode = JsonCode; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/impl/TTagServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service.impl; 2 | 3 | import cn.ityao.wall.entity.TTag; 4 | import cn.ityao.wall.mapper.TTagMapper; 5 | import cn.ityao.wall.service.ITTagService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 标签表 服务实现类 12 | *

13 | * 14 | * @author tongyao 15 | * @since 2023-02-14 16 | */ 17 | @Service 18 | public class TTagServiceImpl extends ServiceImpl implements ITTagService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/ITResourceService.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service; 2 | 3 | import cn.ityao.wall.entity.TResource; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import org.springframework.web.multipart.MultipartFile; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | /** 10 | *

11 | * 资源表 服务类 12 | *

13 | * 14 | * @author tongyao 15 | * @since 2023-02-14 16 | */ 17 | public interface ITResourceService extends IService { 18 | 19 | public void uploadFileAndSave(TResource tResource, MultipartFile[] resource, HttpServletRequest request); 20 | } 21 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.config; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * wall-service >>> 【cn.ityao.wall.config】 9 | * MyBatisPlus分页配置 10 | * 11 | * @author: tongyao 12 | * @since: 2023-02-14 13 | */ 14 | @Configuration 15 | public class MybatisPlusConfig { 16 | 17 | @Bean 18 | public PaginationInterceptor paginationInterceptor() { 19 | PaginationInterceptor page = new PaginationInterceptor(); 20 | return page; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /免责声明.md: -------------------------------------------------------------------------------- 1 | ## 免责声明 2 | 3 | “主程序作者”字样包含主程序开发者GitHub账号ID:super-tongyao。“Wall团队”字样包含源主程序作者、所有Wall团队贡献者。全文均为双引号里的简称。 4 | 5 | (一)、Wall提供免费开源程序及代码,不提供网站相关的内容服务,该程序著作权归主程序作者所有。 6 | 7 | (二)、用户可自由选择是否使用我们的程序,Wall与用户使用我们的程序所建立的网站无任何关联,Wall对用户及其网站不承担任何责任(包括相关影像版权侵权等)。 8 | 9 | (三)、用户下载、安装、使用本程序,即表明用户信任Wall,Wall对任何原因在使用本程序时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。 10 | 11 | (四)、任何单位或个人认为使用本程序建立的网站可能涉嫌侵犯其合法权益的,应该及时向该网站的所有人反映、交涉、或诉诸法律手段。 12 | 13 | (五)、使用本程序的用户因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,Wall和Wall团队不承担任何责任。 14 | 15 | (六)、本声明未涉及的问题参见国家有关法律法规,当本声明与国家法律法规冲突时,以国家法律法规为准。 16 | 17 | 18 | ## 最后声明 19 | 20 | 本程序为开源项目,主要用于代码技术分享及技术业务讨论,请勿使用本程序上传违法国家法律等相关影音资源文件,如:成人黄色图片、成人黄色影音视频、血腥暴力、恐吓恐怖、等一切违法国家法律的和切勿用作商业用途。如不听劝阻,发生意外等相关事宜,与Wall团队、Wall开源项目无关。 -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/WallApplication.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 7 | 8 | /** 9 | * Wall启动类 10 | * 11 | * @author tongyao 12 | * @since 2023-02-14 13 | */ 14 | @SpringBootApplication 15 | @MapperScan(basePackages = { 16 | "cn.ityao.wall.mapper" 17 | }) 18 | public class WallApplication extends SpringBootServletInitializer { 19 | 20 | public static void main(String[] args){ 21 | SpringApplication.run(WallApplication.class, args); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/vo/TOptionVo.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | /** 9 | * wall-service >>> 【cn.ityao.wall.vo】 10 | * 配置项vo 11 | * 12 | * @author: tongyao 13 | * @since: 2023-02-21 14 | */ 15 | @Data 16 | public class TOptionVo { 17 | @NotBlank(message = "文件保存位置不能为空!") 18 | private String saveFilePath; 19 | private String beian; 20 | 21 | @NotBlank(message = "令牌过期时间不能为空!") 22 | private String expireDate; 23 | 24 | @NotBlank(message = "令牌密钥不能为空!") 25 | private String secret; 26 | 27 | @NotBlank(message = "首页标题不能为空!") 28 | private String homeTitle; 29 | private String initTagId; 30 | } 31 | -------------------------------------------------------------------------------- /wall-vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 当前程序使用Wall搭建,Github开源地址:https://github.com/super-tongyao/wall,尊重开源、保留注释,Wall团队鸣谢! 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/config/ErrorConfig.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.config; 2 | 3 | /** 4 | * wall-service >>> 【cn.ityao.wall.config】 5 | * 6 | * @author: tongyao 7 | * @since: 2023-08-15 15:15 8 | */ 9 | /*import org.springframework.boot.web.server.ErrorPageRegistrar; 10 | import org.springframework.boot.web.server.ErrorPage; 11 | import org.springframework.boot.web.server.ErrorPageRegistry; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.stereotype.Component; 14 | 15 | @Component 16 | public class ErrorConfig implements ErrorPageRegistrar { 17 | 18 | @Override 19 | public void registerErrorPages(ErrorPageRegistry registry) { 20 | ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html"); 21 | registry.addErrorPages(error404Page); 22 | } 23 | }*/ 24 | 25 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/impl/TUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service.impl; 2 | 3 | import cn.ityao.wall.entity.TUser; 4 | import cn.ityao.wall.mapper.TUserMapper; 5 | import cn.ityao.wall.service.ITUserService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | /** 11 | *

12 | * 服务实现类 13 | *

14 | * 15 | * @author tongyao 16 | * @since 2023-02-14 17 | */ 18 | @Service 19 | public class TUserServiceImpl extends ServiceImpl implements ITUserService { 20 | 21 | @Autowired 22 | private TUserMapper tUserMapper; 23 | 24 | @Override 25 | public TUser loadUserByUsername(String userName) { 26 | return tUserMapper.loadUserByUsername(userName); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.util; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * wall-service >>> 【cn.ityao.wall.util】 7 | * String工具类 8 | * 9 | * @author: tongyao 10 | * @since: 2023-02-14 11 | */ 12 | public class StringUtils { 13 | 14 | public static boolean isBlank(String str){ 15 | return str == null || "".equals(str.trim()) ?true:false; 16 | } 17 | 18 | public static boolean isNotBlank(String str){ 19 | return str!=null&&!"".equals(str.trim())&&!"null".equals(str)?true:false; 20 | } 21 | 22 | public static boolean isNull(Object str){ 23 | return str == null?true:false; 24 | } 25 | 26 | public static boolean isNotNull(Object str){ 27 | return str != null?true:false; 28 | } 29 | 30 | public static String getUUID(){ 31 | return UUID.randomUUID()+"".replaceAll("-",""); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /wall-vue/package-copy.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wall", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "ant-design-vue": "^3.2.10", 11 | "axios": "^1.3.2", 12 | "core-js": "^3.8.3", 13 | "js-md5": "^0.7.3", 14 | "nprogress": "^0.2.0", 15 | "photoswipe": "^5.3.5", 16 | "vue": "^3.2.13", 17 | "vue-axios": "^3.4.1", 18 | "vue-masonry": "^0.16.0", 19 | "vue-router": "^4.1.3" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.12.16", 23 | "@babel/eslint-parser": "^7.12.16", 24 | "@vue/cli-plugin-babel": "~5.0.0", 25 | "@vue/cli-plugin-eslint": "~5.0.0", 26 | "@vue/cli-service": "~5.0.0", 27 | "crypto-js": "^4.1.1" 28 | }, 29 | "browserslist": [ 30 | "> 1%", 31 | "last 2 versions", 32 | "not dead", 33 | "not ie 11" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /wall-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wall", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "ant-design-vue": "^3.2.10", 11 | "axios": "^1.3.2", 12 | "core-js": "^3.8.3", 13 | "jquery": "^3.7.1", 14 | "js-md5": "^0.7.3", 15 | "lightgallery": "^2.7.1", 16 | "nprogress": "^0.2.0", 17 | "photoswipe": "^5.3.5", 18 | "vue": "^3.2.13", 19 | "vue-axios": "^3.4.1", 20 | "vue-masonry": "^0.16.0", 21 | "vue-router": "^4.1.3" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "^7.12.16", 25 | "@babel/eslint-parser": "^7.12.16", 26 | "@vue/cli-plugin-babel": "~5.0.0", 27 | "@vue/cli-plugin-eslint": "~5.0.0", 28 | "@vue/cli-service": "~5.0.0", 29 | "crypto-js": "^4.1.1" 30 | }, 31 | "browserslist": [ 32 | "> 1%", 33 | "last 2 versions", 34 | "not dead", 35 | "not ie 11" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /wall-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # project prot 2 | server: 3 | port: 9999 4 | mvc: 5 | throw-exception-if-no-handler-found: true # 告诉 SpringBoot 当出现 404 错误时, 直接抛出异常 6 | resources: 7 | add-mappings: false # 告诉 SpringBoot 不要为我们工程中的资源文件建立映射 8 | 9 | # database config 10 | mysql: 11 | database: wall 12 | port: 3306 13 | ip: 82.157.190.245 14 | username: root 15 | password: Zty197368 16 | 17 | # spring config 18 | spring: 19 | datasource: 20 | driver-class-name: com.mysql.cj.jdbc.Driver 21 | url: jdbc:mysql://${mysql.ip}:${mysql.port}/${mysql.database}?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8 22 | username: ${mysql.username} 23 | password: ${mysql.password} 24 | 25 | # file upload size 26 | servlet: 27 | multipart: 28 | max-file-size: 1024MB 29 | max-request-size: 1024MB 30 | 31 | # MyBatis conf 32 | mybatis-plus: 33 | mapper-locations: classpath:mapper/**/*.xml 34 | type-aliases-package: cn.ityao.wall.entity 35 | 36 | project-info: 37 | version: @version@ 38 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/controller/VideoHttpRequestHandler.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.controller; 2 | 3 | /** 4 | * wall-service >>> 【cn.ityao.wall.controller】 5 | * 6 | * @author: tongyao 7 | * @since: 2023-03-06 17:16 8 | */ 9 | import org.springframework.core.io.FileSystemResource; 10 | import org.springframework.core.io.Resource; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.nio.file.Path; 15 | 16 | /** 17 | * @description: 视频流和音频流加载设置 18 | * @return: 19 | * @author: Ming 20 | * @time: 2022/6/24 21 | */ 22 | @Component 23 | public class VideoHttpRequestHandler extends ResourceHttpRequestHandler { 24 | public final static String ATTR_FILE = "NON-STATIC-FILE"; 25 | 26 | @Override 27 | protected Resource getResource(HttpServletRequest request) { 28 | final Path filePath = (Path) request.getAttribute(ATTR_FILE); 29 | return new FileSystemResource(filePath); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /wall-vue/src/components/footer-index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 29 | 30 | 41 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/config/ImageConfig.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.config; 2 | 3 | import cn.ityao.wall.service.ITOptionService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 9 | 10 | import java.io.File; 11 | 12 | /** 13 | * wall-service >>> 【cn.ityao.wall.config】 14 | * 本地图片资源映射 15 | * 16 | * @author: tongyao 17 | * @since: 2023-03-06 18 | */ 19 | @Configuration 20 | public class ImageConfig implements WebMvcConfigurer { 21 | 22 | @Autowired 23 | public ITOptionService itOptionService; 24 | 25 | @Override 26 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 27 | String rootPath = itOptionService.getOption("saveFilePath"); 28 | 29 | // 判断目录是否存在,不在就创建 30 | File file = new File(rootPath); 31 | if(!file.exists()){ 32 | file.mkdirs(); 33 | } 34 | registry.addResourceHandler("/static/**").addResourceLocations("file:"+rootPath); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/entity/TOption.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableId; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.io.Serializable; 11 | import java.util.Date; 12 | 13 | /** 14 | *

15 | * 配置表 16 | *

17 | * 18 | * @author tongyao 19 | * @since 2023-02-14 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = false) 23 | @Accessors(chain = true) 24 | public class TOption implements Serializable { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | /** 29 | * 配置键 30 | */ 31 | @TableId(value ="option_key") 32 | private String optionKey; 33 | 34 | /** 35 | * 配置内容 36 | */ 37 | private String optionValue; 38 | 39 | /** 40 | * 创建者 41 | */ 42 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 43 | private String createBy; 44 | 45 | /** 46 | * 创建时间 47 | */ 48 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 49 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 50 | private Date createTime; 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/impl/TOptionServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service.impl; 2 | 3 | import cn.ityao.wall.entity.TOption; 4 | import cn.ityao.wall.mapper.TOptionMapper; 5 | import cn.ityao.wall.service.ITOptionService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import javax.annotation.Resource; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | *

17 | * 配置表 服务实现类 18 | *

19 | * 20 | * @author tongyao 21 | * @since 2023-02-14 22 | */ 23 | @Service 24 | public class TOptionServiceImpl extends ServiceImpl implements ITOptionService { 25 | 26 | @Resource 27 | private TOptionMapper tOptionMapper; 28 | 29 | @Override 30 | public String getOption(String optionKey) { 31 | Map map = new HashMap(); 32 | map.put("option_key",optionKey); 33 | List tOptionList = tOptionMapper.selectByMap(map); 34 | if (tOptionList.size()==0){ 35 | throw new RuntimeException("配置项:"+optionKey+",不存在数据库中!"); 36 | } 37 | return tOptionList.get(0).getOptionValue(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/exception/CustomErrorController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.exception; 2 | 3 | import cn.ityao.wall.util.DataResult; 4 | import org.springframework.boot.web.servlet.error.ErrorController; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | 11 | /** 12 | * byte-cabinet >>> 【com.cabinet.auth.exception】 13 | * 由于@ControllerAdvice捕获不到404,用于只配置404错误,重写error页面 14 | * 15 | * @author: tongyao 16 | * @since: 2023-02-14 17 | */ 18 | @RestController 19 | public class CustomErrorController implements ErrorController { 20 | 21 | @RequestMapping(value = "/error") 22 | public ResponseEntity error(HttpServletRequest request) { 23 | Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); 24 | if(statusCode == 404){ 25 | return ResponseEntity.status(statusCode) 26 | .body(DataResult.setResult(statusCode,"接口没有找到")); 27 | } 28 | return ResponseEntity.status(500) 29 | .body(DataResult.setResult(500,"error pages 500")); 30 | } 31 | 32 | @Override 33 | public String getErrorPath() { 34 | return "/error"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/config/JwtConfig.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.InterceptorRegistration; 6 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 7 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 9 | 10 | /** 11 | * wall-service >>> 【cn.ityao.wall.config】 12 | * JWT 配置文件 13 | * 14 | * @author: tongyao 15 | * @since: 2023-02-21 16 | */ 17 | @Configuration 18 | public class JwtConfig implements WebMvcConfigurer{ 19 | 20 | @Override 21 | public void addInterceptors(InterceptorRegistry registry) { 22 | //注册TestInterceptor拦截器 23 | InterceptorRegistration registration = registry.addInterceptor(jwtInterceptor()); 24 | 25 | registration.excludePathPatterns( 26 | "/favicon.ico", 27 | "/login", 28 | "/t-tag/query", 29 | "/t-resource/query", 30 | "/t-option/target", 31 | "/static/**"); 32 | registration.addPathPatterns("/**"); 33 | } 34 | 35 | @Bean 36 | public JwtInterceptor jwtInterceptor() { 37 | return new JwtInterceptor(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/encryption/MD5Util.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.util.encryption; 2 | 3 | import java.security.MessageDigest; 4 | 5 | /** 6 | * wall-service >>> 【cn.ityao.wall.util.encryption】 7 | * md5加密 8 | * 9 | * @author: tongyao 10 | * @since: 2023-02-27 11 | */ 12 | public class MD5Util { 13 | 14 | /*** 15 | * MD5加码 生成32位md5码 16 | */ 17 | public static String string2MD5(String inStr){ 18 | MessageDigest md5 = null; 19 | try{ 20 | md5 = MessageDigest.getInstance("MD5"); 21 | }catch (Exception e){ 22 | System.out.println(e.toString()); 23 | e.printStackTrace(); 24 | return ""; 25 | } 26 | char[] charArray = inStr.toCharArray(); 27 | byte[] byteArray = new byte[charArray.length]; 28 | 29 | for (int i = 0; i < charArray.length; i++) 30 | byteArray[i] = (byte) charArray[i]; 31 | byte[] md5Bytes = md5.digest(byteArray); 32 | StringBuffer hexValue = new StringBuffer(); 33 | for (int i = 0; i < md5Bytes.length; i++){ 34 | int val = ((int) md5Bytes[i]) & 0xff; 35 | if (val < 16) 36 | hexValue.append("0"); 37 | hexValue.append(Integer.toHexString(val)); 38 | } 39 | return hexValue.toString(); 40 | 41 | } 42 | 43 | // 测试主函数 44 | public static void main(String args[]) { 45 | String s = new String("123456"); 46 | System.out.println("原始:" + s); 47 | System.out.println("MD5后:" + string2MD5(s)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/TokenUtil.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.util; 2 | 3 | import cn.ityao.wall.service.ITOptionService; 4 | import com.auth0.jwt.JWT; 5 | import com.auth0.jwt.algorithms.Algorithm; 6 | import com.auth0.jwt.interfaces.Claim; 7 | import com.auth0.jwt.interfaces.DecodedJWT; 8 | import com.auth0.jwt.interfaces.JWTVerifier; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.Date; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * wall-service >>> 【cn.ityao.wall.util】 18 | * token工具 19 | * 20 | * @author: tongyao 21 | * @since: 2023-02-16 22 | */ 23 | @Component 24 | public class TokenUtil { 25 | 26 | public static String createToken(String userId, String userName, int expireTime, String secret) { 27 | long EXPIRE_TIME = expireTime * 60 * 1000; 28 | Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME); 29 | 30 | Map map = new HashMap<>(); 31 | map.put("alg", "HS256"); 32 | map.put("typ", "JWT"); 33 | String token = JWT.create() 34 | .withHeader(map) 35 | 36 | .withClaim("userId", userId) 37 | .withClaim("userName", userName) 38 | .withExpiresAt(expireDate) 39 | .withIssuedAt(new Date()) 40 | .sign(Algorithm.HMAC256(secret)); 41 | return token; 42 | } 43 | 44 | public static Map parsingToken(String token,String secret) { 45 | JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build(); 46 | DecodedJWT jwt = verifier.verify(token); 47 | return jwt.getClaims(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/entity/TTag.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.experimental.Accessors; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import java.io.Serializable; 13 | import java.util.Date; 14 | 15 | /** 16 | *

17 | * 标签表 18 | *

19 | * 20 | * @author tongyao 21 | * @since 2023-02-14 22 | */ 23 | @Data 24 | @EqualsAndHashCode(callSuper = false) 25 | @Accessors(chain = true) 26 | public class TTag implements Serializable { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | /** 31 | * 标签编号 32 | */ 33 | @TableId(value ="tag_id", type = IdType.UUID) 34 | private String tagId; 35 | 36 | /** 37 | * 标签名称 38 | */ 39 | @NotBlank(message = "标签名称不能为空!") 40 | private String tagName; 41 | 42 | /** 43 | * 排序 44 | */ 45 | private Integer sort; 46 | 47 | /** 48 | * 创建者 49 | */ 50 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 51 | private String createBy; 52 | 53 | /** 54 | * 创建时间 55 | */ 56 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 57 | private Date createTime; 58 | 59 | /** 60 | * 修改者 61 | */ 62 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 63 | private String modifyBy; 64 | 65 | /** 66 | * 修改时间 67 | */ 68 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 69 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 70 | private Date modifyTime; 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/controller/AuthController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.controller; 2 | 3 | import cn.ityao.wall.entity.TUser; 4 | import cn.ityao.wall.service.ITOptionService; 5 | import cn.ityao.wall.service.ITUserService; 6 | import cn.ityao.wall.util.DataResult; 7 | import cn.ityao.wall.util.TokenUtil; 8 | import cn.ityao.wall.util.encryption.DesUtil; 9 | import lombok.extern.log4j.Log4j2; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import java.util.Date; 15 | 16 | /** 17 | *

18 | * 前端登录 19 | *

20 | * 21 | * @author tongyao 22 | * @since 2023-02-14 23 | */ 24 | @RestController 25 | @Log4j2 26 | public class AuthController { 27 | 28 | @Autowired 29 | private ITUserService itUserService; 30 | 31 | @Autowired 32 | private ITOptionService itOptionService; 33 | 34 | @RequestMapping("/login") 35 | public DataResult login(String username,String password){ 36 | TUser tUser = itUserService.loadUserByUsername(username); 37 | String pass = DesUtil.encryption(password); 38 | if(tUser == null){ 39 | throw new RuntimeException("当前用户不存在"); 40 | }else if(!tUser.getPassWord().equals(pass)){ 41 | throw new RuntimeException("账号或密码错误!"); 42 | } 43 | 44 | Integer expireDate = Integer.parseInt(itOptionService.getOption("expireDate")); 45 | String secret = itOptionService.getOption("secret"); 46 | 47 | // 创建token 48 | String accessToken = TokenUtil.createToken(tUser.getUserId(), tUser.getUserName(), expireDate, secret); 49 | 50 | tUser.setAccess_token(accessToken); 51 | tUser.setLastTime(new Date()); 52 | itUserService.updateById(tUser); 53 | return DataResult.setResult(tUser); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /wall-vue/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import {message} from "ant-design-vue"; 3 | import { clearLocalAll, getLocal, removeLocal, setLocal } from "@/utils/cache"; 4 | import NProgress from "nprogress"; 5 | 6 | const instance = axios.create({ 7 | baseURL: "/api", 8 | }) 9 | 10 | //请求前拦截器 11 | instance.interceptors.request.use( 12 | function(config) { 13 | config.headers.access_token = getLocal("access_token"); 14 | return config; 15 | }, 16 | function(err) { 17 | return Promise.reject(err); 18 | } 19 | ); 20 | 21 | const tishi = 0; 22 | var that = this; 23 | //全局请求后拦截器,没有做http状态码和返回状态码统一。 24 | instance.interceptors.response.use( 25 | function(response) { 26 | // 200 27 | // console.log("response:",response.data) 28 | return response; 29 | }, 30 | function(err) { 31 | if(err.response.data.code >= 500 || err.response.data.code == 404){ 32 | message.warning(err.response.data.message); 33 | }else if(err.response.data.code == 401){ 34 | if((getLocal("user") != null && getLocal("user") != "")){ 35 | removeLocal("user") 36 | removeLocal("access_token") 37 | message.warning("登录已过期,1秒后自动刷新页面。"); 38 | setTimeout(function () { 39 | location.reload(); 40 | },1000) 41 | }else{ 42 | message.warning(err.response.data.message); 43 | } 44 | } 45 | NProgress.done() 46 | return Promise.reject(err); 47 | } 48 | ); 49 | 50 | export function get(url) { 51 | return instance.get(url).then((res)=>{ 52 | return res.data; 53 | }); 54 | } 55 | export function post(url,data) { 56 | return instance.post(url, data).then((res)=>{ 57 | return res.data; 58 | }); 59 | } 60 | export function remove(url,data) { 61 | return instance.delete(url, data).then((res)=>{ 62 | return res.data; 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /wall-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from '@/router' 4 | import Antd from 'ant-design-vue'; 5 | import 'ant-design-vue/dist/antd.css' 6 | import {VueMasonryPlugin} from "vue-masonry"; 7 | import NProgress from 'nprogress' 8 | import 'nprogress/nprogress.css' 9 | NProgress.configure({ 10 | showSpinner: false, 11 | }) 12 | 13 | const app = createApp(App) 14 | app.use(router).use(Antd).use(VueMasonryPlugin).use(NProgress).mount('#app') 15 | 16 | app.config.globalProperties.NProgress = NProgress; 17 | 18 | import * as Icons from "@ant-design/icons-vue"; 19 | const icons = Icons; 20 | for(const i in icons){ 21 | app.component(i,icons[i]); 22 | } 23 | 24 | import * as pub from "@/utils/pub.js"; 25 | app.config.globalProperties.$pub = pub; 26 | 27 | var deviceName = navigator.userAgent.toLowerCase(); 28 | if (/android|iphone|webos|ipod|blackberry/.test(deviceName)) { 29 | app.config.globalProperties.maxTagCount = 2; 30 | app.config.globalProperties.waterfallWidth = 175; 31 | app.config.globalProperties.waterfallStyle = { 32 | overflow:'hidden',margin: '0px auto',width: '376px' 33 | }; 34 | app.config.globalProperties.width = { 35 | width: '100%' 36 | }; 37 | app.config.globalProperties.videoWidthHeighut = { 38 | width: '300px', 39 | height:'200px' 40 | } 41 | }else{ 42 | app.config.globalProperties.maxTagCount = 5; 43 | app.config.globalProperties.waterfallWidth = 372; 44 | app.config.globalProperties.waterfallStyle = { 45 | overflow:'hidden',margin: '0px auto',width: '1537px' 46 | }; 47 | app.config.globalProperties.width = {}; 48 | app.config.globalProperties.videoWidthHeighut = { 49 | width: '880px', 50 | height:'500px' 51 | } 52 | } 53 | 54 | 55 | if (window.console && window.console.log) { 56 | const e = (...e) => setTimeout(console.log.bind(console, ...e)); 57 | e("\n %c Wall %c https://github.com/super-tongyao/wall \n", "color:#FFFFFB;background:#ffa628;padding:5px 0;border-radius:.5rem 0 0 .5rem;", "background: #efefef;padding:5px 0 5px;border-radius:0 .5rem .5rem 0;") 58 | e(` 当前程序使用Wall搭建,作者:https://ityao.cn`); 59 | e(`\n\n\n\n\n`); 60 | } 61 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/exception/ExceptionHandleController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.exception; 2 | 3 | import cn.hutool.core.exceptions.ExceptionUtil; 4 | import cn.ityao.wall.util.DataResult; 5 | import lombok.extern.log4j.Log4j2; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.validation.BindingResult; 9 | import org.springframework.validation.FieldError; 10 | import org.springframework.web.bind.MethodArgumentNotValidException; 11 | import org.springframework.web.bind.annotation.ControllerAdvice; 12 | import org.springframework.web.bind.annotation.ExceptionHandler; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | /** 16 | * 统一异常处理类 17 | * 捕获程序所有异常,针对不同异常,采取不同的处理方式 18 | * 19 | * @version 2.0 20 | * @author tongyao 21 | * @since 2023-02-14 22 | */ 23 | @ControllerAdvice 24 | @RestController 25 | @Log4j2 26 | public class ExceptionHandleController { 27 | 28 | @ExceptionHandler(value = Exception.class) 29 | public ResponseEntity handle(Exception e) { 30 | if(e instanceof CustomException){ 31 | CustomException customException = (CustomException) e; 32 | return ResponseEntity.status(customException.getHttpCode()) 33 | .body(DataResult.setResult(customException.getJsonCode(),customException.getMessage())); 34 | }else if(e instanceof MethodArgumentNotValidException){ 35 | //500 字段为空异常 36 | BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult(); 37 | FieldError fieldError = bindingResult.getFieldError(); 38 | return ResponseEntity.status(500) 39 | .body(DataResult.setResult(500,fieldError.getDefaultMessage())); 40 | }else if(e instanceof Unauthorizedxception){ 41 | Unauthorizedxception unauthorizedxception = (Unauthorizedxception) e; 42 | return ResponseEntity.status(unauthorizedxception.getCode()) 43 | .body(DataResult.setResult(unauthorizedxception.getCode(),unauthorizedxception.getMessage())); 44 | } 45 | log.error(ExceptionUtil.stacktraceToString(e)); 46 | return ResponseEntity.status(500) 47 | .body(DataResult.setResult(500,e.getMessage())); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/config/JwtInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.config; 2 | 3 | import cn.ityao.wall.exception.Unauthorizedxception; 4 | import cn.ityao.wall.service.ITOptionService; 5 | import cn.ityao.wall.util.TokenUtil; 6 | import com.auth0.jwt.interfaces.Claim; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.servlet.HandlerInterceptor; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.util.Map; 14 | 15 | /** 16 | * wall-service >>> 【cn.ityao.wall.config】 17 | * JWT 拦截器 18 | * 19 | * @author: tongyao 20 | * @since: 2023-02-21 21 | */ 22 | public class JwtInterceptor implements HandlerInterceptor{ 23 | 24 | @Autowired 25 | private ITOptionService itOptionService; 26 | 27 | @Override 28 | public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { 29 | // 从 http 请求头中取出 token 30 | String token = httpServletRequest.getHeader("access_token"); 31 | 32 | //tonken验证 33 | if (token == null || "null".equals(token)) { 34 | throw new Unauthorizedxception(401,"请先登录!"); 35 | } 36 | 37 | String secret = itOptionService.getOption("secret"); 38 | // 验证令牌正确性 39 | Map userData = null; 40 | try { 41 | userData = TokenUtil.parsingToken(token,secret); 42 | } catch (Exception e) { 43 | throw new Unauthorizedxception(401,"Token令牌不正确,请先登录。"); 44 | } 45 | String userId = userData.get("userId").asString(); 46 | String userName = userData.get("userName").asString(); 47 | 48 | httpServletRequest.setAttribute("userId",userId); 49 | httpServletRequest.setAttribute("userName",userName); 50 | return true; 51 | } 52 | 53 | @Override 54 | public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { 55 | 56 | } 57 | @Override 58 | public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { 59 | 60 | } 61 | 62 | 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/picture/CompressUtils.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.util.picture; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | import javax.imageio.ImageIO; 7 | import java.awt.*; 8 | import java.awt.image.BufferedImage; 9 | import java.io.IOException; 10 | 11 | /** 12 | * wall-service >>> 【cn.ityao.wall.util】 13 | * 图片压缩 14 | * 15 | * @author: tongyao 16 | * @since: 2023-02-16 17 | */ 18 | @Slf4j 19 | public class CompressUtils { 20 | 21 | public BufferedImage commpressPicture(MultipartFile multipartFile){ 22 | try { 23 | BufferedImage templateImage = ImageIO.read(multipartFile.getInputStream()); 24 | 25 | //原始图片的长度和宽度 26 | int height = templateImage.getHeight(); 27 | int width = templateImage.getWidth(); 28 | 29 | //通过比例压缩 30 | float scale = 0.5f; 31 | 32 | //通过固定长度压缩 33 | /*int doWithHeight = 100; 34 | int dowithWidth = 300;*/ 35 | 36 | //压缩之后的长度和宽度 37 | int doWithHeight = (int) (scale * height); 38 | int dowithWidth = (int) (scale * width); 39 | 40 | BufferedImage finalImage = new BufferedImage(dowithWidth, doWithHeight, BufferedImage.TYPE_INT_RGB); 41 | finalImage.getGraphics() 42 | .drawImage( 43 | templateImage.getScaledInstance(dowithWidth, doWithHeight, java.awt.Image.SCALE_SMOOTH) 44 | , 0, 0, null); 45 | 46 | return finalImage; 47 | } catch (IOException e) { 48 | log.error("图片压缩异常:"+e.getMessage()); 49 | } 50 | return null; 51 | } 52 | 53 | 54 | public BufferedImage commpressPicture(BufferedImage templateImage){ 55 | //原始图片的长度和宽度 56 | int height = templateImage.getHeight(); 57 | int width = templateImage.getWidth(); 58 | 59 | //通过比例压缩 60 | float scale = 1f; 61 | 62 | //通过固定长度压缩 63 | /*int doWithHeight = 100; 64 | int dowithWidth = 300;*/ 65 | 66 | //压缩之后的长度和宽度 67 | int doWithHeight = (int) (scale * height); 68 | int dowithWidth = (int) (scale * width); 69 | 70 | BufferedImage finalImage = new BufferedImage(dowithWidth, doWithHeight, BufferedImage.TYPE_INT_RGB); 71 | finalImage.getGraphics().drawImage(templateImage.getScaledInstance(dowithWidth, doWithHeight, java.awt.Image.SCALE_SMOOTH), 0, 0, null); 72 | 73 | return finalImage; 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/entity/TUser.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | 12 | import java.io.Serializable; 13 | import java.util.Date; 14 | 15 | /** 16 | *

17 | * 用户表 18 | *

19 | * 20 | * @author tongyao 21 | * @since 2023-02-14 22 | */ 23 | @Data 24 | @EqualsAndHashCode(callSuper = false) 25 | @Accessors(chain = true) 26 | public class TUser implements Serializable { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | /** 31 | * 用户编号 32 | */ 33 | @TableId(value ="user_id", type = IdType.UUID) 34 | private String userId; 35 | 36 | /** 37 | * 用户账号 38 | */ 39 | private String userName; 40 | 41 | /** 42 | * 用户密码 43 | */ 44 | private String passWord; 45 | 46 | /** 47 | * 性别(1为男,2为女) 48 | */ 49 | private Integer sex; 50 | 51 | /** 52 | * 电话手机 53 | */ 54 | private String phone; 55 | 56 | /** 57 | * 邮箱地址 58 | */ 59 | private String email; 60 | 61 | /** 62 | * 最后登录的时间 63 | */ 64 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 65 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 66 | private Date lastTime; 67 | 68 | /** 69 | * 排序 70 | */ 71 | private Integer sort; 72 | 73 | /** 74 | * 创建者 75 | */ 76 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 77 | private String createBy; 78 | 79 | /** 80 | * 创建时间 81 | */ 82 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 83 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 84 | private Date createTime; 85 | 86 | /** 87 | * 修改者 88 | */ 89 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 90 | private String modifyBy; 91 | 92 | /** 93 | * 修改时间 94 | */ 95 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 96 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 97 | private Date modifyTime; 98 | 99 | /** 100 | * access_token 101 | */ 102 | @TableField(exist = false) 103 | private String access_token; 104 | } 105 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/entity/TResource.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | import org.hibernate.validator.constraints.Length; 12 | import org.springframework.web.multipart.MultipartFile; 13 | 14 | import java.io.Serializable; 15 | import java.util.Date; 16 | 17 | /** 18 | *

19 | * 资源表 20 | *

21 | * 22 | * @author tongyao 23 | * @since 2023-02-14 24 | */ 25 | @Data 26 | @EqualsAndHashCode(callSuper = false) 27 | @Accessors(chain = true) 28 | public class TResource implements Serializable { 29 | 30 | private static final long serialVersionUID = 1L; 31 | 32 | /** 33 | * 资源编号 34 | */ 35 | @TableId(value ="resource_id", type = IdType.UUID) 36 | private String resourceId; 37 | 38 | /** 39 | * 标题标题描述 40 | */ 41 | @Length(min = 0, max = 300, message = "标题长度不能超过500个字符!") 42 | private String title; 43 | 44 | /** 45 | * 封面路径 46 | */ 47 | private String coverPath; 48 | 49 | /** 50 | * 资源物理存储路径 51 | */ 52 | private String resourcePath; 53 | 54 | /** 55 | * 资源类型(1、图片 2、视频) 56 | */ 57 | private String resourceType; 58 | 59 | /** 60 | * 标签编号(可多个,以英文逗号隔开) 61 | */ 62 | private String tagId; 63 | 64 | /** 65 | * 资源来源 66 | */ 67 | private String sourceType; 68 | 69 | /** 70 | * 资源格式 71 | */ 72 | private String resourceFormat; 73 | 74 | /** 75 | * 可见状态(1、为可见 0、为不可见) 76 | */ 77 | private boolean visibleFlag; 78 | 79 | /** 80 | * 创建者 81 | */ 82 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 83 | private String createBy; 84 | 85 | /** 86 | * 创建时间 87 | */ 88 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 89 | private Date createTime; 90 | 91 | /** 92 | * 修改者 93 | */ 94 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 95 | private String modifyBy; 96 | 97 | /** 98 | * 修改时间 99 | */ 100 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 101 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 102 | private Date modifyTime; 103 | 104 | @TableField(exist = false) 105 | private String bedUrl; 106 | } 107 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/CoverExtractor.java: -------------------------------------------------------------------------------- 1 | /* 2 | package cn.ityao.wall.util; 3 | 4 | */ 5 | /** 6 | * wall-service >>> 【cn.ityao.wall.util】 7 | * 8 | * @author: tongyao 9 | * @since: 2023-08-22 14:13 10 | *//* 11 | 12 | import net.coobird.thumbnailator.Thumbnails; 13 | 14 | import javax.imageio.ImageIO; 15 | import java.awt.*; 16 | import java.awt.image.BufferedImage; 17 | import java.io.*; 18 | 19 | public class CoverExtractor { 20 | public static InputStream extractCover(InputStream inputStream, String outputPath) throws IOException { 21 | // 将输入流保存为临时文件 22 | File tempFile = File.createTempFile("temp", null); 23 | FileOutputStream fileOutputStream = new FileOutputStream(tempFile); 24 | byte[] buffer = new byte[1024]; 25 | int bytesRead; 26 | while ((bytesRead = inputStream.read(buffer)) != -1) { 27 | fileOutputStream.write(buffer, 0, bytesRead); 28 | } 29 | fileOutputStream.close(); 30 | 31 | // 提取封面 32 | Image coverImage; 33 | if (outputPath.toLowerCase().endsWith(".mp4") || outputPath.toLowerCase().endsWith(".mov") || 34 | outputPath.toLowerCase().endsWith(".avi")) { 35 | coverImage = getVideoCover(tempFile.getAbsolutePath()); 36 | } else { 37 | coverImage = getImageCover(tempFile.getAbsolutePath()); 38 | } 39 | 40 | // 压缩封面图片 41 | BufferedImage resizedImage = Thumbnails.of(coverImage) 42 | .size(200, 200) // 设置封面图片的宽高 43 | .asBufferedImage(); 44 | 45 | // 将压缩后的图片保存到输出路径 46 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 47 | ImageIO.write(resizedImage, "jpeg", outputStream); 48 | 49 | // 删除临时文件 50 | tempFile.delete(); 51 | 52 | // 返回压缩后的图片作为InputStream流 53 | return new ByteArrayInputStream(outputStream.toByteArray()); 54 | } 55 | 56 | private static Image getVideoCover(String videoPath) { 57 | // 使用你喜欢的方法获取视频封面,这里使用的是某个假设的方法 58 | // 这里需要调用第三方库或自定义代码来提取视频封面 59 | // 可以参考FFmpeg、OpenCV等库来实现 60 | // 返回一个Image对象作为视频的封面图像 61 | return null; 62 | } 63 | 64 | private static Image getImageCover(String imagePath) throws IOException { 65 | // 从给定的路径读取图像文件 66 | BufferedImage image = ImageIO.read(new File(imagePath)); 67 | 68 | // 返回BufferedImage作为图片的封面 69 | return image; 70 | } 71 | 72 | public static void main(String[] args) { 73 | try { 74 | // 示例:从输入流中提取图片或视频封面并进行压缩 75 | InputStream inputStream = new FileInputStream("C:\\wall-test233\\70ba43d1-3b8c-4648-bcc4-9230d3c2586b.wmv"); 76 | InputStream coverInputStream = extractCover(inputStream, "D:\\"); 77 | 78 | // 在这里可以使用你的专门保存inputStream流的方法来保存coverInputStream 79 | 80 | // 关闭输入流 81 | inputStream.close(); 82 | coverInputStream.close(); 83 | } catch (IOException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | } 88 | 89 | */ 90 | -------------------------------------------------------------------------------- /wall-vue/src/router/index.js: -------------------------------------------------------------------------------- 1 | import {createRouter, createWebHashHistory, createWebHistory} from 'vue-router' 2 | import {getLocal, removeLocal} from "@/utils/cache"; 3 | 4 | const routes = [ 5 | { 6 | path: '/', 7 | name: 'index', 8 | meta:{ 9 | title: '首页' 10 | }, 11 | component: () => import('@/views/index') 12 | }, 13 | { 14 | path: '/:tagId', 15 | name: 'index-type', 16 | meta:{ 17 | title: '首页' 18 | }, 19 | component: () => import('@/views/index') 20 | }, 21 | { 22 | path: '/admin', 23 | name: 'admin', 24 | meta:{ 25 | title: '加载后台' 26 | }, 27 | component: () => import('@/views/admin/index'), 28 | children:[ 29 | { 30 | path: '/admin/upload', 31 | name: 'upload', 32 | meta:{ 33 | title: '上传资源' 34 | }, 35 | component: () => import('@/views/admin/upload'), 36 | }, 37 | { 38 | path: '/admin/list', 39 | name: 'list', 40 | meta:{ 41 | title: '资源列表' 42 | }, 43 | component: () => import('@/views/admin/list'), 44 | }, 45 | { 46 | path: '/admin/tag', 47 | name: 'tag', 48 | meta:{ 49 | title: '标签管理' 50 | }, 51 | component: () => import('@/views/admin/tag'), 52 | }, 53 | { 54 | path: '/admin/setting', 55 | name: 'setting', 56 | meta:{ 57 | title: '其他设置' 58 | }, 59 | component: () => import('@/views/admin/setting'), 60 | } 61 | ] 62 | }, 63 | { 64 | path: '/login', 65 | name: 'login', 66 | meta:{ 67 | title: '登录' 68 | }, 69 | component: () => import('@/views/auth/login') 70 | }, 71 | { 72 | path: '/:catchAll(.*)', 73 | meta:{ 74 | title: '404' 75 | }, 76 | component: () => import('@/views/other/404') 77 | } 78 | ] 79 | 80 | 81 | const router = createRouter({ 82 | history: createWebHistory(), //hash 83 | routes 84 | }) 85 | 86 | router.beforeEach(function (to,from,next){ 87 | if(to.meta.title == null){ 88 | document.title = to.params.key + ' - Wall'; 89 | }else{ 90 | document.title = to.meta.title + ' - Wall'; 91 | } 92 | 93 | // 先看访问是不是需要登陆页面 94 | if(to.path.indexOf("admin") != -1){ 95 | if(getLocal("user") != null && getLocal("user") != "") { 96 | // 判断默认是不是通过浏览器直接输入/admin,如果是自动重定向到上传资源页面 97 | if(to.path == '/admin'){ 98 | next(to.path+"/upload") 99 | } 100 | }else{ 101 | next("/"); 102 | } 103 | } 104 | next() 105 | 106 | }) 107 | 108 | router.afterEach(function() {}) 109 | 110 | export default router 111 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/DataResult.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.util; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 数据结果返回类 7 | * 8 | * @version 2.0 9 | * @author tongyao 10 | * @since 2021-08-01 11 | */ 12 | @Data 13 | public class DataResult { 14 | 15 | private int code = 200; 16 | private String message = "success"; 17 | private Object data = null; 18 | 19 | public DataResult() { 20 | 21 | } 22 | 23 | public DataResult(int code, String message) { 24 | this.code = code; 25 | this.message = message; 26 | } 27 | 28 | public DataResult(int code, String message, Object data) { 29 | this.code = code; 30 | this.message = message; 31 | this.data = data; 32 | } 33 | 34 | /** 35 | * 设置状态为200的返回结果数据 36 | * @param data 37 | * @return 38 | */ 39 | public static DataResult setResult(Object data) { 40 | DataResult dataResult = new DataResult(); 41 | dataResult.setData(data); 42 | return dataResult; 43 | } 44 | 45 | /** 46 | * 设置自定义状态的返回结果数据 47 | * @param data 48 | * @return 49 | */ 50 | public static DataResult setResult(int code, Object data) { 51 | DataResult dataResult = new DataResult(); 52 | dataResult.setCode(code); 53 | dataResult.setData(data); 54 | return dataResult; 55 | } 56 | 57 | /** 58 | * 设置自定义状态和返回信息返回结果数据 59 | * @param data 60 | * @return 61 | */ 62 | public static DataResult setResult(int code, String message, Object data) { 63 | DataResult dataResult = new DataResult(); 64 | dataResult.setCode(code); 65 | dataResult.setMessage(message); 66 | dataResult.setData(data); 67 | return dataResult; 68 | } 69 | 70 | /** 71 | * 设置自定义状态和返回信息内容,但无结果数据 72 | * @param code 73 | * @param message 74 | * @return 75 | */ 76 | public static DataResult setResult(int code, String message) { 77 | DataResult dataResult = new DataResult(); 78 | dataResult.setCode(code); 79 | dataResult.setMessage(message); 80 | return dataResult; 81 | } 82 | 83 | /** 84 | * 设置默认200状态,自定义返回信息和结果数据 85 | * @param data 86 | * @return 87 | */ 88 | public static DataResult setResult(String message, Object data) { 89 | DataResult dataResult = new DataResult(); 90 | dataResult.setMessage(message); 91 | dataResult.setData(data); 92 | return dataResult; 93 | } 94 | 95 | /** 96 | * 默认返回错误方法,自定义返回错误信息 97 | * @param message 98 | * @return 99 | */ 100 | public static DataResult setError(String message) { 101 | DataResult dataResult = new DataResult(); 102 | dataResult.setCode(500); 103 | dataResult.setMessage(message); 104 | return dataResult; 105 | } 106 | 107 | /** 108 | * 默认返回成功方法,自定义返回结果数据 109 | * @param data 110 | * @return 111 | */ 112 | public static DataResult setSuccess(Object data) { 113 | DataResult dataResult = new DataResult(); 114 | dataResult.setData(data); 115 | return dataResult; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/controller/TUserController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.controller; 2 | 3 | 4 | import cn.ityao.wall.entity.TUser; 5 | import cn.ityao.wall.service.ITUserService; 6 | import cn.ityao.wall.util.DataResult; 7 | import cn.ityao.wall.util.encryption.DesUtil; 8 | import cn.ityao.wall.util.encryption.MD5Util; 9 | import cn.ityao.wall.vo.RepassVo; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import javax.servlet.http.HttpServletRequest; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.regex.Pattern; 23 | 24 | /** 25 | *

26 | * 前端控制器 27 | *

28 | * 29 | * @author tongyao 30 | * @since 2023-02-14 31 | */ 32 | @RestController 33 | @RequestMapping("/t-user") 34 | public class TUserController { 35 | 36 | @Autowired 37 | private ITUserService itUserService; 38 | 39 | @RequestMapping("/query") 40 | public DataResult query(){ 41 | return DataResult.setSuccess(itUserService.list()); 42 | } 43 | 44 | @PostMapping(value = "/pass" , produces = {MediaType.APPLICATION_JSON_VALUE}) 45 | public DataResult pass(@RequestBody RepassVo repassVo, HttpServletRequest request){ 46 | if(repassVo.getNewPass() == null || "".equals(repassVo.getNewPass())){ 47 | throw new RuntimeException("请输入新密码"); 48 | }else if(repassVo.getReNewPass() == null || "".equals(repassVo.getReNewPass())){ 49 | throw new RuntimeException("请再次输入新密码"); 50 | }else if(!(repassVo.getNewPass().length() >= 8 && repassVo.getNewPass().length() <= 16)){ 51 | throw new RuntimeException("密码长度要大于等于8位或小于等于16位"); 52 | }else if(!Pattern.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).*", repassVo.getNewPass())){ 53 | throw new RuntimeException("密码必须包含大小写字符和数字"); 54 | }else if(!repassVo.getNewPass().equals(repassVo.getReNewPass())){ 55 | throw new RuntimeException("两次密码输入不一致"); 56 | } 57 | String userName = (String) request.getAttribute("userName"); 58 | 59 | try { 60 | Map tempMap = new HashMap<>(); 61 | tempMap.put("user_name",userName); 62 | TUser tUser = null; 63 | try { 64 | List list = (List) itUserService.listByMap(tempMap); 65 | tUser = list.get(0); 66 | 67 | } catch (Exception e) { 68 | throw new RuntimeException("修改失败,账户不存在"); 69 | } 70 | String oldPass = DesUtil.encryption(MD5Util.string2MD5(repassVo.getOldPass())); 71 | if(!tUser.getPassWord().equals(oldPass)){ 72 | throw new RuntimeException("旧密码与原密码不一致"); 73 | } 74 | tUser.setPassWord(DesUtil.encryption(MD5Util.string2MD5(repassVo.getNewPass()))); 75 | itUserService.updateById(tUser); 76 | } catch (Exception e) { 77 | throw new RuntimeException(e.getMessage()); 78 | } 79 | return DataResult.setResult(null); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /wall-vue/src/components/justifiedGallery.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * justifiedGallery - v3.8.0 3 | * http://miromannino.github.io/Justified-Gallery/ 4 | * Copyright (c) 2020 Miro Mannino 5 | * Licensed under the MIT license. 6 | */ 7 | .justified-gallery { 8 | width: 100%; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | .justified-gallery > a, 13 | .justified-gallery > div, 14 | .justified-gallery > figure { 15 | position: absolute; 16 | display: inline-block; 17 | overflow: hidden; 18 | /* background: #888888; To have gray placeholders while the gallery is loading with waitThumbnailsLoad = false */ 19 | filter: "alpha(opacity=10)"; 20 | opacity: 0.1; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | .justified-gallery > a > img, 25 | .justified-gallery > div > img, 26 | .justified-gallery > figure > img, 27 | .justified-gallery > a > a > img, 28 | .justified-gallery > div > a > img, 29 | .justified-gallery > figure > a > img, 30 | .justified-gallery > a > svg, 31 | .justified-gallery > div > svg, 32 | .justified-gallery > figure > svg, 33 | .justified-gallery > a > a > svg, 34 | .justified-gallery > div > a > svg, 35 | .justified-gallery > figure > a > svg { 36 | position: absolute; 37 | top: 50%; 38 | left: 50%; 39 | margin: 0; 40 | padding: 0; 41 | border: none; 42 | filter: "alpha(opacity=0)"; 43 | opacity: 0; 44 | } 45 | .justified-gallery > a > .jg-caption, 46 | .justified-gallery > div > .jg-caption, 47 | .justified-gallery > figure > .jg-caption { 48 | display: none; 49 | position: absolute; 50 | bottom: 0; 51 | padding: 5px; 52 | background-color: #000000; 53 | left: 0; 54 | right: 0; 55 | margin: 0; 56 | color: white; 57 | font-size: 12px; 58 | font-weight: 300; 59 | font-family: sans-serif; 60 | } 61 | .justified-gallery > a > .jg-caption.jg-caption-visible, 62 | .justified-gallery > div > .jg-caption.jg-caption-visible, 63 | .justified-gallery > figure > .jg-caption.jg-caption-visible { 64 | display: initial; 65 | filter: "alpha(opacity=70)"; 66 | opacity: 0.7; 67 | -webkit-transition: opacity 500ms ease-in; 68 | -moz-transition: opacity 500ms ease-in; 69 | -o-transition: opacity 500ms ease-in; 70 | transition: opacity 500ms ease-in; 71 | } 72 | .justified-gallery > .jg-entry-visible { 73 | filter: "alpha(opacity=100)"; 74 | opacity: 1; 75 | background: none; 76 | } 77 | .justified-gallery > .jg-entry-visible > img, 78 | .justified-gallery > .jg-entry-visible > a > img, 79 | .justified-gallery > .jg-entry-visible > svg, 80 | .justified-gallery > .jg-entry-visible > a > svg { 81 | filter: "alpha(opacity=100)"; 82 | opacity: 1; 83 | -webkit-transition: opacity 500ms ease-in; 84 | -moz-transition: opacity 500ms ease-in; 85 | -o-transition: opacity 500ms ease-in; 86 | transition: opacity 500ms ease-in; 87 | } 88 | .justified-gallery > .jg-filtered { 89 | display: none; 90 | } 91 | .justified-gallery > .jg-spinner { 92 | position: absolute; 93 | bottom: 0; 94 | margin-left: -24px; 95 | padding: 10px 0 10px 0; 96 | left: 50%; 97 | filter: "alpha(opacity=100)"; 98 | opacity: 1; 99 | overflow: initial; 100 | } 101 | .justified-gallery > .jg-spinner > span { 102 | display: inline-block; 103 | filter: "alpha(opacity=0)"; 104 | opacity: 0; 105 | width: 8px; 106 | height: 8px; 107 | margin: 0 4px 0 4px; 108 | background-color: #000; 109 | border-radius: 6px; 110 | } 111 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/encryption/DesUtil.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.util.encryption; 2 | 3 | import sun.misc.BASE64Decoder; 4 | import sun.misc.BASE64Encoder; 5 | 6 | import javax.crypto.*; 7 | import javax.crypto.spec.SecretKeySpec; 8 | import java.io.IOException; 9 | import java.io.UnsupportedEncodingException; 10 | import java.security.InvalidKeyException; 11 | import java.security.NoSuchAlgorithmException; 12 | import java.security.SecureRandom; 13 | import java.util.Scanner; 14 | 15 | /** 16 | * wall-service >>> 【cn.ityao.wall.util.encryption】 17 | * Des对称加密 18 | * 19 | * @author: tongyao 20 | * @since: 2023-02-27 21 | */ 22 | public class DesUtil { 23 | public static String salt = "09ec4bc7-b685-11ed-93d8-525400d73e0b"; 24 | 25 | /** 26 | * 加密 27 | * @param content 28 | * @return 29 | */ 30 | public static String encryption(String content) { 31 | try { 32 | KeyGenerator keygen = KeyGenerator.getInstance("AES"); 33 | SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); 34 | secureRandom.setSeed(salt.getBytes("UTF-8")); 35 | keygen.init(secureRandom); 36 | SecretKey original_key = keygen.generateKey(); 37 | byte[] raw = original_key.getEncoded(); 38 | SecretKey key = new SecretKeySpec(raw, "AES"); 39 | Cipher cipher = Cipher.getInstance("AES"); 40 | cipher.init(Cipher.ENCRYPT_MODE, key); 41 | byte[] byte_encode = content.getBytes("utf-8"); 42 | byte[] byte_AES = cipher.doFinal(byte_encode); 43 | String AES_encode = new String(new BASE64Encoder().encode(byte_AES)); 44 | return AES_encode; 45 | } catch (NoSuchAlgorithmException e) { 46 | e.printStackTrace(); 47 | } catch (NoSuchPaddingException e) { 48 | e.printStackTrace(); 49 | } catch (InvalidKeyException e) { 50 | e.printStackTrace(); 51 | } catch (IllegalBlockSizeException e) { 52 | e.printStackTrace(); 53 | } catch (BadPaddingException e) { 54 | e.printStackTrace(); 55 | } catch (UnsupportedEncodingException e) { 56 | e.printStackTrace(); 57 | } 58 | return null; 59 | } 60 | 61 | /** 62 | * 解密 63 | * @param content 64 | * @return 65 | */ 66 | public static String decryption(String content) { 67 | try { 68 | KeyGenerator keygen = KeyGenerator.getInstance("AES"); 69 | keygen.init(128, new SecureRandom(salt.getBytes())); 70 | SecretKey original_key = keygen.generateKey(); 71 | byte[] raw = original_key.getEncoded(); 72 | SecretKey key = new SecretKeySpec(raw, "AES"); 73 | Cipher cipher = Cipher.getInstance("AES"); 74 | cipher.init(Cipher.DECRYPT_MODE, key); 75 | byte[] byte_content = new BASE64Decoder().decodeBuffer(content); 76 | byte[] byte_decode = cipher.doFinal(byte_content); 77 | String AES_decode = new String(byte_decode, "utf-8"); 78 | return AES_decode; 79 | } catch (NoSuchAlgorithmException e) { 80 | e.printStackTrace(); 81 | } catch (NoSuchPaddingException e) { 82 | e.printStackTrace(); 83 | } catch (InvalidKeyException e) { 84 | e.printStackTrace(); 85 | } catch (IOException e) { 86 | e.printStackTrace(); 87 | } catch (IllegalBlockSizeException e) { 88 | e.printStackTrace(); 89 | } catch (BadPaddingException e) { 90 | e.printStackTrace(); 91 | } 92 | return null; 93 | } 94 | 95 | public static void main(String[] args) { 96 | DesUtil se = new DesUtil(); 97 | Scanner scanner = new Scanner(System.in); 98 | 99 | /* 100 | * 加密 101 | */ 102 | System.out.println("请输入要加密的内容:"); 103 | String content = scanner.next(); 104 | System.out.println("根据输入的规则" + salt + "加密后的密文是:" + se.encryption(content)); 105 | 106 | /* 107 | * 解密 108 | */ 109 | System.out.println("请输入要解密的内容(密文):"); 110 | content = scanner.next(); 111 | System.out.println("根据输入的规则" + salt + "解密后的明文是:" + se.decryption(content)); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/controller/TOptionController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.controller; 2 | 3 | 4 | import cn.ityao.wall.entity.TOption; 5 | import cn.ityao.wall.entity.TResource; 6 | import cn.ityao.wall.service.ITOptionService; 7 | import cn.ityao.wall.util.DataResult; 8 | import cn.ityao.wall.util.StringUtils; 9 | import cn.ityao.wall.vo.TOptionVo; 10 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.Valid; 15 | import java.io.File; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | *

21 | * 配置表 前端控制器 22 | *

23 | * 24 | * @author tongyao 25 | * @since 2023-02-14 26 | */ 27 | @RestController 28 | @RequestMapping("/t-option") 29 | public class TOptionController { 30 | 31 | @Autowired 32 | private ITOptionService itOptionService; 33 | 34 | @PostMapping("/save") 35 | public DataResult save(@Valid @RequestBody TOptionVo tOptionVo){ 36 | List tOptionList = new ArrayList<>(); 37 | 38 | 39 | String path = tOptionVo.getSaveFilePath(); 40 | if (!path.substring(path.length()-1).equals("\\") && !path.substring(path.length()-1).equals("/")){ 41 | path = tOptionVo.getSaveFilePath()+ File.separator; 42 | } 43 | TOption saveFilePath = new TOption(); 44 | saveFilePath.setOptionKey("saveFilePath"); 45 | saveFilePath.setOptionValue(path); 46 | tOptionList.add(saveFilePath); 47 | 48 | TOption beian = new TOption(); 49 | beian.setOptionKey("beian"); 50 | beian.setOptionValue(tOptionVo.getBeian()); 51 | tOptionList.add(beian); 52 | 53 | TOption expireDate = new TOption(); 54 | expireDate.setOptionKey("expireDate"); 55 | expireDate.setOptionValue(tOptionVo.getExpireDate()); 56 | tOptionList.add(expireDate); 57 | 58 | TOption secret = new TOption(); 59 | secret.setOptionKey("secret"); 60 | secret.setOptionValue(tOptionVo.getSecret()); 61 | tOptionList.add(secret); 62 | 63 | TOption homeTitle = new TOption(); 64 | homeTitle.setOptionKey("homeTitle"); 65 | homeTitle.setOptionValue(tOptionVo.getHomeTitle()); 66 | tOptionList.add(homeTitle); 67 | 68 | TOption initTagId = new TOption(); 69 | initTagId.setOptionKey("initTagId"); 70 | initTagId.setOptionValue(tOptionVo.getInitTagId()); 71 | tOptionList.add(initTagId); 72 | 73 | itOptionService.updateBatchById(tOptionList); 74 | return DataResult.setSuccess(null); 75 | } 76 | 77 | @GetMapping("/query") 78 | public DataResult query(){ 79 | TOptionVo tOptionVo = new TOptionVo(); 80 | List tOptionList = itOptionService.list(); 81 | for (TOption tOption : tOptionList){ 82 | if(tOption.getOptionKey().equals("saveFilePath")){ 83 | tOptionVo.setSaveFilePath(tOption.getOptionValue()); 84 | }else if(tOption.getOptionKey().equals("beian")){ 85 | tOptionVo.setBeian(tOption.getOptionValue()); 86 | }else if(tOption.getOptionKey().equals("expireDate")){ 87 | tOptionVo.setExpireDate(tOption.getOptionValue()); 88 | }else if(tOption.getOptionKey().equals("secret")){ 89 | tOptionVo.setSecret(tOption.getOptionValue()); 90 | }else if(tOption.getOptionKey().equals("homeTitle")){ 91 | tOptionVo.setHomeTitle(tOption.getOptionValue()); 92 | }else if(tOption.getOptionKey().equals("initTagId")){ 93 | tOptionVo.setInitTagId(tOption.getOptionValue()); 94 | } 95 | } 96 | return DataResult.setSuccess(tOptionVo); 97 | } 98 | 99 | @GetMapping("/target") 100 | public DataResult target(){ 101 | TOptionVo tOptionVo = new TOptionVo(); 102 | List tOptionList = itOptionService.list(); 103 | for (TOption tOption : tOptionList){ 104 | if(tOption.getOptionKey().equals("beian")){ 105 | tOptionVo.setBeian(tOption.getOptionValue()); 106 | }else if(tOption.getOptionKey().equals("homeTitle")){ 107 | tOptionVo.setHomeTitle(tOption.getOptionValue()); 108 | }else if(tOption.getOptionKey().equals("initTagId")){ 109 | tOptionVo.setInitTagId(tOption.getOptionValue()); 110 | } 111 | } 112 | return DataResult.setSuccess(tOptionVo); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/controller/TTagController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.controller; 2 | 3 | 4 | import cn.ityao.wall.entity.TOption; 5 | import cn.ityao.wall.entity.TResource; 6 | import cn.ityao.wall.entity.TTag; 7 | import cn.ityao.wall.service.ITOptionService; 8 | import cn.ityao.wall.service.ITResourceService; 9 | import cn.ityao.wall.service.ITTagService; 10 | import cn.ityao.wall.util.DataResult; 11 | import cn.ityao.wall.util.StringUtils; 12 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 13 | import com.baomidou.mybatisplus.core.metadata.IPage; 14 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import javax.servlet.http.HttpServletRequest; 19 | import javax.validation.Valid; 20 | import java.util.*; 21 | 22 | /** 23 | *

24 | * 标签表 前端控制器 25 | *

26 | * 27 | * @author tongyao 28 | * @since 2023-02-14 29 | */ 30 | @RestController 31 | @RequestMapping("/t-tag") 32 | public class TTagController { 33 | 34 | 35 | @Autowired 36 | private ITTagService itTagService; 37 | 38 | @Autowired 39 | private ITResourceService itResourceService; 40 | 41 | @Autowired 42 | private ITOptionService itOptionService; 43 | 44 | @PostMapping("/saveOrUpdate") 45 | public DataResult saveOrUpdate(@Valid @RequestBody TTag tTag, HttpServletRequest request){ 46 | String userName = (String) request.getAttribute("userName"); 47 | if (tTag.getSort() == null){ 48 | tTag.setSort(0); 49 | } 50 | if(StringUtils.isNotBlank(tTag.getTagId())){ 51 | tTag.setModifyBy(userName); 52 | tTag.setModifyTime(new Date()); 53 | itTagService.updateById(tTag); 54 | }else{ 55 | tTag.setCreateBy(userName); 56 | tTag.setCreateTime(new Date()); 57 | itTagService.save(tTag); 58 | } 59 | return DataResult.setSuccess(null); 60 | } 61 | 62 | @GetMapping("/list") 63 | public DataResult list( 64 | String tagName, 65 | @RequestParam(defaultValue = "1") int pageNo, 66 | @RequestParam(defaultValue = "10") int pageSize 67 | ){ 68 | LambdaQueryWrapper tTagLambdaQueryWrapper = new LambdaQueryWrapper<>(); 69 | if(StringUtils.isNotBlank(tagName)){ 70 | tTagLambdaQueryWrapper.like(TTag::getTagName,tagName); 71 | } 72 | tTagLambdaQueryWrapper.orderByAsc(TTag::getCreateBy,TTag::getSort); 73 | 74 | Page page = new Page<>(pageNo,pageSize); 75 | IPage iPage = itTagService.page(page,tTagLambdaQueryWrapper); 76 | return DataResult.setSuccess(iPage); 77 | } 78 | 79 | @DeleteMapping("/delete") 80 | public DataResult delete(String tagIds){ 81 | List tagId = Arrays.asList(tagIds.split(",")); 82 | 83 | LambdaQueryWrapper tResourceLambdaQueryWrapper = new LambdaQueryWrapper<>(); 84 | for (String str : tagId){ 85 | tResourceLambdaQueryWrapper.like(TResource::getTagId,str); 86 | } 87 | List tResourceList = itResourceService.list(tResourceLambdaQueryWrapper); 88 | for (int i = 0; i < tResourceList.size(); i++) { 89 | for (int j = 0; j < tagId.size(); j++) { 90 | tResourceList.get(i).setTagId(tResourceList.get(i).getTagId().replaceAll(tagId.get(j)+",","")); 91 | } 92 | } 93 | if (tResourceList.size() != 0){ 94 | itResourceService.updateBatchById(tResourceList); 95 | } 96 | 97 | Map map = new HashMap(); 98 | map.put("option_key","initTagId"); 99 | List tOptionList = (List) itOptionService.listByMap(map); 100 | TOption tOption = tOptionList.get(0); 101 | if (tagIds.indexOf(tOption.getOptionValue()) != -1){ 102 | tOption = new TOption(); 103 | tOption.setOptionKey("initTagId"); 104 | tOption.setOptionValue(""); 105 | itOptionService.updateById(tOption); 106 | } 107 | 108 | itTagService.removeByIds(tagId); 109 | return DataResult.setSuccess(null); 110 | } 111 | 112 | @GetMapping("/query") 113 | public DataResult query(){ 114 | LambdaQueryWrapper tTagLambdaQueryWrapper = new LambdaQueryWrapper<>(); 115 | tTagLambdaQueryWrapper.select(TTag::getTagId, TTag::getTagName); 116 | tTagLambdaQueryWrapper.orderByAsc(TTag::getCreateBy,TTag::getSort); 117 | return DataResult.setSuccess(itTagService.list(tTagLambdaQueryWrapper)); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/util/FileUtils.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.util; 2 | 3 | import cn.hutool.core.exceptions.ExceptionUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import net.coobird.thumbnailator.Thumbnails; 6 | import org.apache.tomcat.util.http.fileupload.IOUtils; 7 | import org.bytedeco.javacv.FFmpegFrameGrabber; 8 | import org.bytedeco.javacv.Frame; 9 | import org.bytedeco.javacv.Java2DFrameConverter; 10 | 11 | import javax.imageio.ImageIO; 12 | import java.awt.image.BufferedImage; 13 | import java.io.*; 14 | import java.net.URL; 15 | import java.net.URLConnection; 16 | import java.nio.file.Files; 17 | import java.nio.file.Paths; 18 | 19 | 20 | /** 21 | * wall-service >>> 【cn.ityao.wall.util】 22 | * 文件工具类 23 | * 24 | * 作者注:如果你看到本类代码,觉得写的很优雅的话,那么不用质疑,这是作者舍弃旧代码花了3天时间又重新全部重构了一遍代码! 25 | * 26 | * @author: tongyao 27 | * @since: 2023-02-16 28 | */ 29 | @Slf4j 30 | public class FileUtils { 31 | 32 | /** 33 | * 获取文件名称后缀 34 | * @param fileName 35 | * @return 36 | */ 37 | public String getFileSuffix(String fileName){ 38 | String suffix = fileName.substring(fileName.lastIndexOf(".")+1,fileName.length()); 39 | return suffix.toLowerCase(); 40 | } 41 | 42 | /** 43 | * 资源和封面的文件写入 44 | * @param inputStream 45 | * @param path 46 | */ 47 | public void writeFile(InputStream inputStream,String path){ 48 | try { 49 | FileOutputStream fileOutputStream = new FileOutputStream(path); 50 | IOUtils.copy(inputStream, fileOutputStream); 51 | IOUtils.closeQuietly(fileOutputStream); 52 | fileOutputStream.close(); 53 | } catch (IOException e) { 54 | throw new RuntimeException("存储文件异常,可能最近修改了文件存储路径,请重启Wall服务!详细错误:"+ExceptionUtil.stacktraceToString(e)); 55 | } 56 | } 57 | 58 | /** 59 | * 根据本地上传类型或者图床URL写入图片/视频封面资源 60 | * @param resourcePath 61 | * @param coverPath 62 | */ 63 | public String writeCover(String resourcePath,String coverPath){ 64 | InputStream inputStream = null; 65 | String contentType = null; 66 | String resourceType = "0"; 67 | try { 68 | // 看是本地上传还是图床URL 69 | if (resourcePath.indexOf("http") != -1){ 70 | URL url = new URL(resourcePath); 71 | URLConnection connection = url.openConnection(); 72 | inputStream = connection.getInputStream(); 73 | contentType = connection.getContentType(); 74 | }else{ 75 | inputStream = new FileInputStream(new File(resourcePath)); 76 | contentType = Files.probeContentType(Paths.get(resourcePath)); 77 | } 78 | // 看是视频还是图片 79 | if (contentType.startsWith("image/")) { 80 | // 这里不要问为什么不写,因为不想写 - - 81 | resourceType = "1"; 82 | } else if (contentType.startsWith("video/")) { 83 | inputStream = videoCover(inputStream,2); 84 | resourceType = "2"; 85 | }else{ 86 | throw new RuntimeException("未知的资源存储类型:" + contentType); 87 | } 88 | // 不管图床URL还是本地上传 统一 保存 inputStream 写入到封面路径 89 | writeFile(inputStream,coverPath); 90 | } catch (IOException e) { 91 | throw new RuntimeException(ExceptionUtil.stacktraceToString(e)); 92 | } finally { 93 | try { 94 | inputStream.close(); 95 | } catch (IOException e) { 96 | throw new RuntimeException(ExceptionUtil.stacktraceToString(e)); 97 | } 98 | return resourceType; 99 | } 100 | } 101 | 102 | /** 103 | * 获取视频帧数 104 | * @param inputStream 输入流 105 | * @param frameCount 帧数 106 | * @return 107 | */ 108 | public InputStream videoCover(InputStream inputStream,int frameCount) { 109 | int flag = 0; 110 | Frame frame = null; 111 | try { 112 | FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(inputStream); 113 | fFmpegFrameGrabber.start(); 114 | // 获取视频的总帧数 115 | int lengthInFrames = fFmpegFrameGrabber.getLengthInFrames(); 116 | double frameNumber = fFmpegFrameGrabber.getFrameRate(); 117 | while (flag <= lengthInFrames) { 118 | frame = fFmpegFrameGrabber.grabImage(); 119 | // 帧数截取 120 | if (frame != null && flag == frameCount) { 121 | Java2DFrameConverter converter = new Java2DFrameConverter(); 122 | BufferedImage bufferedImage = converter.getBufferedImage(frame); 123 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 124 | ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream); 125 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); 126 | inputStream = byteArrayInputStream; 127 | break; 128 | } 129 | flag++; 130 | } 131 | fFmpegFrameGrabber.stop(); 132 | } catch (Exception e) { 133 | throw new RuntimeException("在获取视频帧时失败:" + ExceptionUtil.stacktraceToString(e)); 134 | } 135 | return inputStream; 136 | } 137 | 138 | public static void main(String[] args) throws Exception { 139 | Thumbnails.of("C:\\wall-test233\\57eacccd-3610-4d68-a446-9893623d8dd2.jpg").scale(1).toFile("D:\\222_t2.jpg"); 140 | Thumbnails.of("C:\\wall-test233\\57eacccd-3610-4d68-a446-9893623d8dd2.jpg").size(376,600).toFile("D:\\222_t.jpg"); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /wall-vue/src/views/auth/login.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 98 | 99 | 169 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/controller/TResourceController.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.controller; 2 | 3 | 4 | import cn.ityao.wall.common.SuperController; 5 | import cn.ityao.wall.entity.TResource; 6 | import cn.ityao.wall.entity.TTag; 7 | import cn.ityao.wall.service.ITOptionService; 8 | import cn.ityao.wall.service.ITResourceService; 9 | import cn.ityao.wall.util.DataResult; 10 | import cn.ityao.wall.util.FileUtils; 11 | import cn.ityao.wall.util.StringUtils; 12 | import cn.ityao.wall.util.picture.CompressUtils; 13 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 14 | import com.baomidou.mybatisplus.core.metadata.IPage; 15 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.web.bind.annotation.*; 18 | 19 | import org.springframework.web.multipart.MultipartFile; 20 | 21 | import javax.imageio.ImageIO; 22 | import javax.servlet.http.HttpServletRequest; 23 | import java.awt.*; 24 | import java.awt.image.BufferedImage; 25 | import java.io.File; 26 | import java.io.FileInputStream; 27 | import java.io.IOException; 28 | import java.util.Arrays; 29 | import java.util.Date; 30 | import java.util.List; 31 | import java.util.UUID; 32 | 33 | /** 34 | *

35 | * 资源表 前端控制器 36 | *

37 | * 38 | * @author tongyao 39 | * @since 2023-02-14 40 | */ 41 | @RestController 42 | @RequestMapping("/t-resource") 43 | public class TResourceController extends SuperController { 44 | 45 | @Autowired 46 | private ITResourceService itResourceService; 47 | 48 | @PostMapping("/upload") 49 | public DataResult upload(TResource tResource, MultipartFile[] resource,String[] title,HttpServletRequest request){ 50 | if(resource == null){ 51 | throw new RuntimeException("请上传图片或视频!"); 52 | } 53 | itResourceService.uploadFileAndSave(tResource,resource,request); 54 | return DataResult.setSuccess(null); 55 | } 56 | 57 | @GetMapping("/query") 58 | public DataResult query(String tagId, 59 | @RequestParam(defaultValue = "1") int pageNo, 60 | @RequestParam(defaultValue = "10") int pageSize){ 61 | 62 | LambdaQueryWrapper tResourceLambdaQueryWrapper = new LambdaQueryWrapper<>(); 63 | if(StringUtils.isNotBlank(tagId)){ 64 | tResourceLambdaQueryWrapper.like(TResource::getTagId,tagId); 65 | } 66 | tResourceLambdaQueryWrapper.eq(TResource::isVisibleFlag,true); 67 | tResourceLambdaQueryWrapper.orderByDesc(TResource::getCreateTime); 68 | 69 | /*Page page = new Page<>(pageNo,pageSize); 70 | IPage iPage = itResourceService.page(page,tResourceLambdaQueryWrapper);*/ 71 | 72 | String rootPath = itOptionService.getOption("saveFilePath"); 73 | List list = itResourceService.list(tResourceLambdaQueryWrapper); 74 | return DataResult.setSuccess(list); 75 | } 76 | 77 | @GetMapping("/list") 78 | public DataResult list( 79 | String title, String tagId, 80 | @RequestParam(defaultValue = "1") int pageNo, 81 | @RequestParam(defaultValue = "10") int pageSize){ 82 | 83 | LambdaQueryWrapper tResourceLambdaQueryWrapper = new LambdaQueryWrapper<>(); 84 | if(StringUtils.isNotBlank(title)){ 85 | tResourceLambdaQueryWrapper.like(TResource::getTitle,title); 86 | } 87 | if(StringUtils.isNotBlank(tagId)){ 88 | tResourceLambdaQueryWrapper.like(TResource::getTagId,tagId); 89 | } 90 | tResourceLambdaQueryWrapper.orderByDesc(TResource::getCreateTime); 91 | 92 | Page page = new Page<>(pageNo,pageSize); 93 | IPage iPage = itResourceService.page(page,tResourceLambdaQueryWrapper); 94 | return DataResult.setSuccess(iPage); 95 | } 96 | 97 | @DeleteMapping("/delete") 98 | public DataResult delete(String resourceIds){ 99 | List resourceId = Arrays.asList(resourceIds.split(",")); 100 | List tResourceList = (List) itResourceService.listByIds(resourceId); 101 | 102 | String saveFilePath = itOptionService.getOption("saveFilePath"); 103 | File deleteFile = null; 104 | for (TResource tResource : tResourceList){ 105 | deleteFile = new File(saveFilePath+tResource.getCoverPath()); 106 | deleteFile.delete(); 107 | 108 | deleteFile = new File(saveFilePath+tResource.getResourcePath()); 109 | deleteFile.delete(); 110 | } 111 | itResourceService.removeByIds(resourceId); 112 | return DataResult.setSuccess(null); 113 | } 114 | 115 | @PostMapping("/updateState") 116 | public DataResult updateState(String resourceId, boolean visibleFlag, HttpServletRequest request){ 117 | String userName = (String) request.getAttribute("userName"); 118 | 119 | TResource tResource = new TResource(); 120 | tResource.setResourceId(resourceId); 121 | tResource.setVisibleFlag(visibleFlag); 122 | tResource.setModifyBy(userName); 123 | tResource.setModifyTime(new Date()); 124 | itResourceService.updateById(tResource); 125 | return DataResult.setSuccess(null); 126 | } 127 | 128 | 129 | @PostMapping("/update") 130 | public DataResult update(String resourceId, String title,String tagIds, HttpServletRequest request){ 131 | String userName = (String) request.getAttribute("userName"); 132 | 133 | TResource tResource = new TResource(); 134 | tResource.setResourceId(resourceId); 135 | tResource.setTitle(title); 136 | tResource.setTagId(tagIds); 137 | tResource.setModifyBy(userName); 138 | tResource.setModifyTime(new Date()); 139 | itResourceService.updateById(tResource); 140 | return DataResult.setSuccess(null); 141 | } 142 | 143 | 144 | 145 | } 146 | -------------------------------------------------------------------------------- /wall-service/wall.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : localhost 5 | Source Server Type : MySQL 6 | Source Server Version : 50730 7 | Source Host : localhost:3306 8 | Source Schema : wall 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50730 12 | File Encoding : 65001 13 | 14 | Date: 02/03/2023 12:24:35 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | create database wall; 21 | use wall; 22 | 23 | -- ---------------------------- 24 | -- Table structure for t_option 25 | -- ---------------------------- 26 | DROP TABLE IF EXISTS `t_option`; 27 | CREATE TABLE `t_option` ( 28 | `option_key` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '配置键', 29 | `option_value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '配置内容', 30 | `create_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建者', 31 | `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', 32 | PRIMARY KEY (`option_key`) USING BTREE 33 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '配置表' ROW_FORMAT = Dynamic; 34 | 35 | -- ---------------------------- 36 | -- Records of t_option 37 | -- ---------------------------- 38 | INSERT INTO `t_option` VALUES ('beian', '', 'system', now()); 39 | INSERT INTO `t_option` VALUES ('expireDate', '120', 'system', now()); 40 | INSERT INTO `t_option` VALUES ('homeTitle', '🏞️ 照片墙', 'system', now()); 41 | INSERT INTO `t_option` VALUES ('initTagId', '', 'system', now()); 42 | INSERT INTO `t_option` VALUES ('saveFilePath', 'C:\\wall\\', 'system', now()); 43 | INSERT INTO `t_option` VALUES ('secret', uuid(), 'system', now()); 44 | 45 | -- ---------------------------- 46 | -- Table structure for t_resource 47 | -- ---------------------------- 48 | DROP TABLE IF EXISTS `t_resource`; 49 | CREATE TABLE `t_resource` ( 50 | `resource_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '资源编号', 51 | `title` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标题标题描述', 52 | `cover_type` tinyint(2) NULL DEFAULT NULL COMMENT '封面类型(1、自动截取封面 2、上传封面)', 53 | `cover_path` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '封面路径', 54 | `resource_path` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '资源物理存储路径', 55 | `resource_width` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源图片宽', 56 | `resource_height` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源图片高', 57 | `resource_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源后缀类型', 58 | `tag_id` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '标签编号(可多个,以英文逗号隔开)', 59 | `visible_flag` tinyint(2) NULL DEFAULT NULL COMMENT '可见状态(1、为可见 0、为不可见)', 60 | `create_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建者', 61 | `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', 62 | `modify_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改者', 63 | `modify_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间', 64 | PRIMARY KEY (`resource_id`) USING BTREE 65 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '资源表' ROW_FORMAT = Dynamic; 66 | 67 | -- ---------------------------- 68 | -- Records of t_resource 69 | -- ---------------------------- 70 | 71 | -- ---------------------------- 72 | -- Table structure for t_tag 73 | -- ---------------------------- 74 | DROP TABLE IF EXISTS `t_tag`; 75 | CREATE TABLE `t_tag` ( 76 | `tag_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '标签编号', 77 | `tag_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标签名称', 78 | `sort` tinyint(100) NULL DEFAULT NULL COMMENT '排序', 79 | `create_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建者', 80 | `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', 81 | `modify_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改者', 82 | `modify_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间', 83 | PRIMARY KEY (`tag_id`) USING BTREE 84 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '标签表' ROW_FORMAT = Dynamic; 85 | 86 | -- ---------------------------- 87 | -- Records of t_tag 88 | -- ---------------------------- 89 | 90 | -- ---------------------------- 91 | -- Table structure for t_user 92 | -- ---------------------------- 93 | DROP TABLE IF EXISTS `t_user`; 94 | CREATE TABLE `t_user` ( 95 | `user_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户编号', 96 | `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户账号', 97 | `pass_word` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户密码', 98 | `sex` tinyint(4) NULL DEFAULT NULL COMMENT '性别(1为男,2为女)', 99 | `phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电话手机', 100 | `email` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱地址', 101 | `last_time` datetime(0) NULL DEFAULT NULL COMMENT '最后登录的时间', 102 | `sort` tinyint(255) NULL DEFAULT NULL COMMENT '排序', 103 | `create_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建者', 104 | `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', 105 | `modify_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改者', 106 | `modify_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间', 107 | PRIMARY KEY (`user_id`) USING BTREE, 108 | UNIQUE INDEX `email`(`email`) USING BTREE 109 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic; 110 | 111 | -- ---------------------------- 112 | -- Records of t_user 113 | -- ---------------------------- 114 | INSERT INTO `t_user` VALUES ('0e191174de65f483d360f0d01e37aa65', 'admin', 'aFJjSQfE+pjqklRTjChVzrK5Ku1uLxkUWP0bwLXn5f5s4AdR1VgcRKwpAVCcGWpT', 1, NULL, NULL, NULL, 1, 'system', now(), NULL, NULL); 115 | 116 | SET FOREIGN_KEY_CHECKS = 1; 117 | -------------------------------------------------------------------------------- /wall-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.2.6.RELEASE 10 | 11 | 12 | 13 | cn.ityao 14 | wall-service 15 | 2.0.3 16 | 17 | wall-service 18 | 19 | wall-service 20 | 21 | 22 | 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-test 35 | test 36 | 37 | 38 | org.junit.vintage 39 | junit-vintage-engine 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-web 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | 56 | 57 | mysql 58 | mysql-connector-java 59 | 8.0.13 60 | 61 | 62 | 63 | 64 | com.alibaba 65 | druid 66 | 1.1.10 67 | 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-starter-jdbc 73 | 74 | 75 | 76 | 77 | org.projectlombok 78 | lombok 79 | 1.18.8 80 | provided 81 | 82 | 83 | 84 | 85 | com.baomidou 86 | mybatis-plus-boot-starter 87 | 3.1.1 88 | 89 | 90 | 91 | 92 | com.alibaba 93 | fastjson 94 | 1.2.41 95 | 96 | 97 | 98 | 99 | org.apache.logging.log4j 100 | log4j-core 101 | 2.15.0 102 | compile 103 | 104 | 105 | 106 | 107 | com.auth0 108 | java-jwt 109 | 3.8.2 110 | 111 | 112 | 113 | 114 | cn.hutool 115 | hutool-all 116 | 5.7.12 117 | 118 | 119 | 120 | 121 | org.bytedeco 122 | javacv-platform 123 | 1.5.6 124 | 125 | 126 | 127 | net.coobird 128 | thumbnailator 129 | 0.4.8 130 | 131 | 132 | 133 | 134 | com.drewnoakes 135 | metadata-extractor 136 | 2.7.0 137 | 138 | 139 | 140 | 141 | 142 | wall 143 | 144 | 145 | org.springframework.boot 146 | spring-boot-maven-plugin 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-surefire-plugin 151 | 2.18.1 152 | 153 | true 154 | 155 | 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-compiler-plugin 160 | 161 | ${java.version} 162 | ${java.version} 163 | ${project.build.sourceEncoding} 164 | 165 | 166 | ${java.home}/lib/rt.jar;${java.home}/lib/jce.jar 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /wall-service/src/main/java/cn/ityao/wall/service/impl/TResourceServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.ityao.wall.service.impl; 2 | 3 | import cn.ityao.wall.entity.TResource; 4 | import cn.ityao.wall.mapper.TResourceMapper; 5 | import cn.ityao.wall.service.ITOptionService; 6 | import cn.ityao.wall.service.ITResourceService; 7 | import cn.ityao.wall.util.FileUtils; 8 | import cn.ityao.wall.util.StringUtils; 9 | import com.alibaba.fastjson.JSONObject; 10 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 11 | import net.coobird.thumbnailator.Thumbnails; 12 | import net.coobird.thumbnailator.tasks.UnsupportedFormatException; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.web.multipart.MultipartFile; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | import java.io.IOException; 19 | import java.util.*; 20 | 21 | /** 22 | *

23 | * 资源表 服务实现类 24 | *

25 | * 26 | * @author tongyao 27 | * @since 2023-02-14 28 | */ 29 | @Service 30 | public class TResourceServiceImpl extends ServiceImpl implements ITResourceService { 31 | 32 | @Autowired 33 | private ITOptionService itOptionService; 34 | 35 | private FileUtils fileUtils = new FileUtils(); 36 | 37 | private String resourceSuffix = ""; 38 | private String resourceFileName = ""; 39 | private String resourcePath = ""; 40 | 41 | private List titles = new ArrayList<>(); 42 | private List> bedUrl = null; 43 | 44 | private String prefixUrl = "api/static/"; 45 | 46 | @Override 47 | public void uploadFileAndSave(TResource tResource, MultipartFile[] resource, HttpServletRequest request){ 48 | String saveFilePath = itOptionService.getOption("saveFilePath"); 49 | 50 | // 加载处理标题 51 | lodingTitle(tResource,resource); 52 | 53 | // 开始保存处理数据 54 | try { 55 | for (int i = 0; i < titles.size(); i++) { 56 | // 统一声明好封面和资源的文件名 57 | String uuid = StringUtils.getUUID(); 58 | 59 | // 封面文件名 60 | String coverFileName = uuid + "_cover.jpg"; 61 | if (tResource.getSourceType().equals("1")){ 62 | // 获取资源后缀 63 | resourceSuffix = fileUtils.getFileSuffix(resource[i].getOriginalFilename()); 64 | 65 | // 资源文件名 66 | resourceFileName = uuid + "." + resourceSuffix; 67 | 68 | // 资源完整的存储路径 69 | resourcePath = saveFilePath + resourceFileName; 70 | 71 | // 保存视频 或者 图片资源 72 | fileUtils.writeFile(resource[i].getInputStream(),resourcePath); 73 | 74 | tResource.setResourcePath(prefixUrl + resourceFileName); 75 | }else{ 76 | // - - 77 | resourcePath = bedUrl.get(i).get("url"); 78 | tResource.setResourcePath(resourcePath); 79 | } 80 | 81 | // 封面完整的存储路径 82 | String coverPath = saveFilePath + coverFileName; 83 | 84 | // 获取视频 或者 图片资源 封面 85 | String resourceType = fileUtils.writeCover(resourcePath,coverPath); 86 | 87 | // 压缩封面 88 | Thumbnails.of(coverPath).scale(1).toFile(coverPath); 89 | 90 | // 保存 91 | String userName = (String) request.getAttribute("userName"); 92 | tResource.setResourceId(null); 93 | tResource.setTitle(titles.get(i)); 94 | tResource.setCoverPath(prefixUrl + coverFileName); 95 | tResource.setVisibleFlag(true); 96 | tResource.setCreateBy(userName); 97 | tResource.setCreateTime(new Date()); 98 | tResource.setResourceType(resourceType); 99 | this.save(tResource); 100 | } 101 | } catch (IOException e) { 102 | if (e instanceof UnsupportedFormatException){ 103 | throw new RuntimeException("图床URL上传有不支持的格式!"); 104 | }else{ 105 | throw new RuntimeException("未知异常:"+e.getMessage()); 106 | } 107 | } 108 | } 109 | 110 | /** 111 | * 验证本地上传图片或视频后缀 112 | * @param resource 113 | */ 114 | public void localUploadSubffix(MultipartFile[] resource){ 115 | boolean subffix = false; 116 | for (int i = 0; i < resource.length; i++) { 117 | // 资源后缀 118 | String resourceSuffix = fileUtils.getFileSuffix(resource[i].getOriginalFilename()); 119 | if (resourceSuffix.equals("jpeg")){ 120 | resourceSuffix = "jpg"; 121 | } 122 | if (!resourceSuffix.equals("jpg") && !resourceSuffix.equals("png") && 123 | !resourceSuffix.equals("gif") && !resourceSuffix.equals("mp4") && 124 | !resourceSuffix.equals("mov") && !resourceSuffix.equals("wmv")){ 125 | subffix = true; 126 | break; 127 | } 128 | } 129 | if (subffix){ 130 | throw new RuntimeException("图片或视频只支持jpg、png、gif、mp4、mov、wmv格式!"); 131 | } 132 | } 133 | 134 | /** 135 | * 验证加载处理标题 136 | * @param tResource 137 | * @param resource 138 | * @return 139 | */ 140 | public List lodingTitle(TResource tResource,MultipartFile[] resource){ 141 | // 只验证类型为1的本地上传资源类型包括视频类型 142 | titles = new ArrayList(); 143 | bedUrl = new ArrayList(); 144 | if (tResource.getSourceType().equals("1")){ 145 | localUploadSubffix(resource); 146 | // 处理资源标题 147 | if (!tResource.getTitle().equals("")){ 148 | String[] title = tResource.getTitle().split(","); 149 | titles = Arrays.asList(title); 150 | } 151 | }else if(tResource.getSourceType().equals("2")){ 152 | // 此处不会验证图床URL 153 | bedUrl = (List>) JSONObject.parse(tResource.getBedUrl()); 154 | // 处理图床标题 155 | for (int i = 0; i < bedUrl.size(); i++) { 156 | titles.add(bedUrl.get(i).get("title")); 157 | } 158 | }else{ 159 | throw new RuntimeException("未知来源格式!"); 160 | } 161 | return titles; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /wall-vue/src/views/admin/setting.vue: -------------------------------------------------------------------------------- 1 | 52 | 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![](https://img-blog.csdnimg.cn/1842462da13147fea1c48f8c38fc6125.png) 4 |

Wall

5 | 6 | ![Vue](https://img.shields.io/badge/Vue-3.2.13-brightgreen.svg) 7 | ![Spring Boot 2.2.6](https://img.shields.io/badge/Spring%20Boot-2.2.6-brightgreen.svg) 8 | 9 |
10 | 11 | Wall是一款快速分享资源应用程序。俗称“照片墙、视频墙”,基于Vue3 + Spring Boot开发的云共享资源应用系统。快速分享发布照片和视频平台。兼容PC端和移动端,支持端对端跨平台上传资源文件。 12 | 13 | - 演示地址:[https://demo-wall.ityao.cn](https://demo-wall.ityao.cn) 14 | 15 | - 后台管理:[https://demo-wall.ityao.cn/login](https://demo-wall.ityao.cn/login),账号密码:admin/123456 16 | 17 | 🌟如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 18 | 19 | ## 演示截图 20 | 21 | ![](https://img-blog.csdnimg.cn/d90a3605852049e59e6129b6ea356d21.png) 22 | 23 | ![](https://img-blog.csdnimg.cn/b09c6dd1af074f77ba17575a32a49f6e.png) 24 | 25 | ![](https://img-blog.csdnimg.cn/42a33e6001104fda84fd2a66955cad90.png) 26 | 27 | ![](https://img-blog.csdnimg.cn/f170a54d25f54aeaacf75aa62177f207.png) 28 | 29 | ![](https://img-blog.csdnimg.cn/da2f0ec64c97424cae4e4c57e71d4488.png) 30 | 31 | ![](https://img-blog.csdnimg.cn/f8e02fe368c04000b82f875af872681b.jpeg) 32 | 33 | ## 开发者名单 34 | 35 | Wall还有很多不足之处,比如部分移动端机型兼容等相关问题。 36 | 37 | 或许你可以加入Wall团队,我们一起贡献代码。[申请加入](#共同协作) 38 | 39 | 40 | 41 | 下面表格中出现你的头像及GitHub账号地址,视为Wall团队成员。 42 | 43 | | 名称 | Github | 44 | | ------------- | --------------------------------- | 45 | | Tongyao | https://github.com/super-tongyao | 46 | | Wang Tuanjie | https://github.com/KingBoyAndGirl | 47 | | 期待你的入... | 期待你的入... | 48 | 49 | ## 安装教程 50 | 51 | 1、下载地址:[https://github.com/super-tongyao/wall/releases](https://github.com/super-tongyao/wall/releases),以最新版为准。 52 | 53 | 2、解压缩包,里面有两个文件夹,两个执行脚本,一个SQL脚本。 54 | 55 | ![](https://img-blog.csdnimg.cn/d41c213041d543ddacdc3b71f7a3a3ec.png) 56 | 57 | - wall:编译好的前端页面。 58 | - wall-service:编译好的后端jar程序包。 59 | - wall.sql:后端数据库。 60 | - startup.bat:Wall启动脚本。 61 | - stop.bat:Wall停止脚本。 62 | 63 | 3、如果你是开发者(程序员),如只需要前端编译程序页面和后端服务jar包,那么只从如下路径中抽取文件,到你的服务器手动命令启动Wall服务。 64 | 65 | - Wall前端页面编译文件:wall/html/wall 66 | 67 | - Wall后端服务Jar包:wall-service/jar/wall.jar 68 | 69 | ### 程序安装 70 | 71 | 72 | 自2.0.0版本以上起,所有Wall程序采用一键启动,内置Nginx、JDK,无需再配置Nginx等相关文件操作。 73 | 74 | 1、导入```Wall.sql```文件到MySQL数据库,`注:MySQL为5.7版本`。 75 | 76 | ``` 77 | mysql> source wall.sql 78 | ``` 79 | 80 | 2、修改wall-service/config/application.yml配置文件,并修改你本地的MySQL数据库连接端口及账号密码。 81 | 82 | ``` 83 | # project prot 84 | server: 85 | port: 9999 86 | 87 | # database config 88 | mysql: 89 | database: wall 90 | port: 3306 91 | ip: 127.0.0.1 92 | username: root 93 | password: root 94 | ``` 95 | 96 | 3、后端服务支持两种环境下快捷启动。 97 | 98 | - Windows:双击```startup.bat```文件启动。 99 | 100 | - Linux:执行根目录```./startup.sh```文件启动。 101 | 102 | 103 | 在Linux下,如果你想关闭会话后继续运行Wall,那么可以搭配nohup使用。 104 | 105 | ``` 106 | nohup ./startup.sh r> /dev/null 2> /dev/null & 107 | ``` 108 | 109 | 4、浏览器访问Wall程序。 110 | 111 | 浏览器输入:```http://localhost:80```,正常显示页面及操作数据,至此完成安装。 112 | 113 | 后台管理:```http://localhost:80/login```,默认账号密码:admin/123456,如有问题,请提交Issues。 114 | 115 | ### 拓展配置(可选) 116 | 117 | 主要简化Wall安装程序后的一些基本配置,如暂不需要,无需配置,其他更多配置,请参考[Nginx官网](http://nginx.org/en/docs/)文档。 118 | 119 | 1、前端页面端口修改 120 | 121 | 默认端口为80,修改wall/config/nginx.conf配置文件。并修改你的前端端口,本文设置为8888。 122 | 123 | ``` 124 | server { 125 | listen 8888; 126 | server_name localhost; 127 | 128 | underscores_in_headers on; 129 | 130 | location / { 131 | # 映射对应nginx/html下的wall前端程序 132 | root html/wall; 133 | try_files $uri $uri/ /index.html; 134 | } 135 | ....... 136 | } 137 | ``` 138 | 139 | 2、修改后端服务端口 140 | 141 | 默认端口为9999,修改wall-service/config/application.yml配置文件。并修改你的后端端口,本文设置为8080。 142 | 143 | ``` 144 | # project prot 145 | server: 146 | port: 8080 147 | 148 | ...... 149 | ``` 150 | 151 | 注意:如果你修改了后端服务端口,那么前端映射配置中,也需要修改映射后端服务端口,否则无法找到后端接口等问题。 152 | 153 | 修改wall/config/nginx.conf配置文件。找到映射后端服务的/api,改为对应8080端口后,重启Wall即可。 154 | 155 | ``` 156 | server { 157 | listen 80; 158 | server_name localhost; 159 | ...... 160 | 161 | # 后端服务地址 162 | location /api{ 163 | rewrite ^/api/(.*)$ /$1 break; 164 | proxy_set_header Host $host; 165 | proxy_set_header X-Real-IP $remote_addr; 166 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 167 | 168 | # 转发Wall的后台服务地址 169 | proxy_pass http://localhost:8080; 170 | } 171 | ...... 172 | } 173 | ``` 174 | 175 | 3、绑定域名 176 | 177 | 首先云服务商解析域名到本服务器IP,修改wall/config/nginx.conf配置文件,改成你的域名。更多内容请查看[Nginx官网](http://nginx.org/en/docs/)。 178 | 179 | ``` 180 | server { 181 | listen 80; 182 | server_name 你的域名地址; 183 | 184 | underscores_in_headers on; 185 | 186 | location / { 187 | # 映射对应nginx/html下的wall前端程序 188 | root html/wall; 189 | try_files $uri $uri/ /index.html; 190 | } 191 | ....... 192 | } 193 | ``` 194 | 195 | 4、使用https 196 | 197 | 确保你有可用的SSL证书,修改wall/config/nginx.conf配置文件,增加如下配置。更多内容请查看[SSL配置](http://nginx.org/en/docs/http/configuring_https_servers.html)。 198 | 199 | ``` 200 | server { 201 | listen 80; 202 | server_name 你的域名地址; 203 | 204 | ssl_certificate 你的SSL域名pem证书.pem; 205 | ssl_certificate_key 你的SSL域名key证书.key; 206 | 207 | underscores_in_headers on; 208 | 209 | location / { 210 | # 映射对应nginx/html下的wall前端程序 211 | root html/wall; 212 | try_files $uri $uri/ /index.html; 213 | } 214 | ....... 215 | } 216 | ``` 217 | 218 | 5、修改Wall上传文件大小限制 219 | 220 | Wall默认上传大小为1024MB(1GB),修改wall/config/nginx.conf配置文件,暂改为2048(2GB)。 221 | 222 | ``` 223 | http { 224 | ...... 225 | 226 | client_max_body_size 2048m; 227 | ``` 228 | 229 | 修改wall-service/config/application.yml配置文件,暂改为2048(2GB)。 230 | 231 | ``` 232 | # spring config 233 | spring: 234 | ...... 235 | 236 | # file upload size 237 | servlet: 238 | multipart: 239 | max-file-size: 2048MB 240 | max-request-size: 2048MB 241 | ``` 242 | 243 | ## 更新日志 244 | #### 2023-04 - 03(v2.0.3) 245 | 246 | > 1. 修复后端默认配置文件信息 247 | > 2. 优化前端相应提示文字 248 | 249 | #### 2023-03-22(v2.0.2) 250 | 251 | > 1. 修复封面图片压缩算法。 252 | 253 | #### 2023-03-15(v2.0.1) 254 | 255 | > 1. 修复Linux系统下启动时相关小问题。 256 | 257 | #### 2023-03-14(v2.0.0) 258 | 259 | > 1. 修复并兼容Linux操作系统。 260 | > 1. 修复图片视频资源上传路径相关小问题。 261 | > 1. 简化安装配置及实现一键启动Wall程序。 262 | 263 | #### 2023-03-10(v1.0.1) 264 | 265 | > 1. 修复上传点击标签归类时会唤起软键盘问题。 266 | > 2. 修复上传选择图后又删除或重新选择时选不了相册问题。 267 | > 3. 优化设置页面相应提示语。 268 | > 4. 优化部分页面样式效果。 269 | > 5. 优化上传选中标签时可见1个问题。 270 | 271 | #### 2023-03-07(v1.0.0) 272 | 273 | > 1. 初始版本发布 274 | 275 | ## 共同协作 276 | 277 | 参与共同协作会得到什么? 278 | 279 | - 你会出现在Wall开源人员贡献列表。 280 | 281 | 根据下列模板修改并回答问题: 282 | 283 | - 擅长技术:你熟悉和擅长的技术。 284 | 285 | - GitHub主页地址:你的GitHub主页地址(用于展示跳转你的主页)。 286 | 287 | - 如何理解开源?:你如何理解开源思想和想法。 288 | 289 | 290 | 并发送至邮箱:super_tongyao@163.com,留意邮箱回信,让我们一起把Wall变的更好。 291 | 292 | ## 技术讨论 293 | 294 | Wall微信讨论群欢迎扫码(如图损坏,手动添加微信号:Byte880),欢迎各位添加: 295 | 296 | 297 | 298 | 在这里插入图片描述 299 | 300 | ## 免责声明 301 | 302 | [查看免责声明](/免责声明.md) 303 | -------------------------------------------------------------------------------- /wall-vue/src/assets/background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 21 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /wall-vue/src/views/admin/tag.vue: -------------------------------------------------------------------------------- 1 | 74 | 241 | 244 | -------------------------------------------------------------------------------- /wall-vue/src/views/admin/index.vue: -------------------------------------------------------------------------------- 1 | 119 | 218 | 249 | -------------------------------------------------------------------------------- /wall-vue/src/views/admin/list.vue: -------------------------------------------------------------------------------- 1 | 99 | 289 | 292 | -------------------------------------------------------------------------------- /wall-vue/src/views/admin/upload.vue: -------------------------------------------------------------------------------- 1 | 120 | 290 | 296 | -------------------------------------------------------------------------------- /wall-vue/src/components/jquery.justifiedGallery.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * justifiedGallery - v3.8.0 3 | * http://miromannino.github.io/Justified-Gallery/ 4 | * Copyright (c) 2020 Miro Mannino 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | !function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof module&&module.exports?module.exports=function(t,i){return void 0===i&&(i="undefined"!=typeof window?require("jquery"):require("jquery")(t)),e(i),i}:e(jQuery)}(function(l){var r=function(t,i){this.settings=i,this.checkSettings(),this.imgAnalyzerTimeout=null,this.entries=null,this.buildingRow={entriesBuff:[],width:0,height:0,aspectRatio:0},this.lastFetchedEntry=null,this.lastAnalyzedIndex=-1,this.yield={every:2,flushed:0},this.border=0<=i.border?i.border:i.margins,this.maxRowHeight=this.retrieveMaxRowHeight(),this.suffixRanges=this.retrieveSuffixRanges(),this.offY=this.border,this.rows=0,this.spinner={phase:0,timeSlot:150,$el:l('
'),intervalId:null},this.scrollBarOn=!1,this.checkWidthIntervalId=null,this.galleryWidth=t.width(),this.$gallery=t};r.prototype.getSuffix=function(t,i){var e,s;for(e=i .jg-caption");return 0===i.length?null:i},r.prototype.displayEntry=function(t,i,e,s,n,r){t.width(s),t.height(r),t.css("top",e),t.css("left",i);var o=this.imgFromEntry(t);if(null!==o){o.css("width",s),o.css("height",n),o.css("margin-left",-Math.round(s/2)),o.css("margin-top",-Math.round(n/2));var a=o.data("jg.src");if(a){a=this.newSrc(a,s,n,o[0]),o.one("error",function(){this.resetImgSrc(o)});var h=function(){o.attr("src",a)};"skipped"===t.data("jg.loaded")&&a?this.onImageEvent(a,function(){this.showImg(t,h),t.data("jg.loaded",!0)}.bind(this)):this.showImg(t,h)}}else this.showImg(t);this.displayEntryCaption(t)},r.prototype.displayEntryCaption=function(t){var i=this.imgFromEntry(t);if(null!==i&&this.settings.captions){var e=this.captionFromEntry(t);if(null===e){var s=i.attr("alt");this.isValidCaption(s)||(s=t.attr("title")),this.isValidCaption(s)&&(e=l('
'+s+"
"),t.append(e),t.data("jg.createdCaption",!0))}null!==e&&(this.settings.cssAnimation||e.stop().fadeTo(0,this.settings.captionSettings.nonVisibleOpacity),this.addCaptionEventsHandlers(t))}else this.removeCaptionEventsHandlers(t)},r.prototype.isValidCaption=function(t){return void 0!==t&&0this.settings.justifyThreshold;if(i||t&&"hide"===this.settings.lastRow&&!d){for(e=0;e img, > a > img").fadeTo(0,0));return-1}for(t&&!d&&"justify"!==this.settings.lastRow&&"hide"!==this.settings.lastRow&&(a=!1,0this.settings.justifyThreshold)),e=0;ethis.settings.refreshSensitivity&&(this.galleryWidth=t,this.rewind(),this.rememberGalleryHeight(),this.startImgAnalyzer(!0))}},this),this.settings.refreshTime)},r.prototype.isSpinnerActive=function(){return null!==this.spinner.intervalId},r.prototype.getSpinnerHeight=function(){return this.spinner.$el.innerHeight()},r.prototype.stopLoadingSpinnerAnimation=function(){clearInterval(this.spinner.intervalId),this.spinner.intervalId=null,this.setGalleryTempHeight(this.$gallery.height()-this.getSpinnerHeight()),this.spinner.$el.detach()},r.prototype.startLoadingSpinnerAnimation=function(){var t=this.spinner,i=t.$el.find("span");clearInterval(t.intervalId),this.$gallery.append(t.$el),this.setGalleryTempHeight(this.offY+this.buildingRow.height+this.getSpinnerHeight()),t.intervalId=setInterval(function(){t.phase=this.yield.every))return void this.startImgAnalyzer(t)}else if("error"!==e.data("jg.loaded"))return}0 img, > a > img, > svg, > a > svg",triggerEvent:function(t){this.$gallery.trigger(t)}},l.fn.justifiedGallery=function(n){return this.each(function(t,i){var e=l(i);e.addClass("justified-gallery");var s=e.data("jg.controller");if(void 0===s){if(null!=n&&"object"!==l.type(n)){if("destroy"===n)return;throw"The argument must be an object"}s=new r(e,l.extend({},r.prototype.defaults,n)),e.data("jg.controller",s)}else if("norewind"===n);else{if("destroy"===n)return void s.destroy();s.updateSettings(n),s.rewind()}s.updateEntries("norewind"===n)&&s.init()})}}); --------------------------------------------------------------------------------