[
  {
    "path": ".gitignore",
    "content": "/.idea\ntarget\n\n/temp\n\napplication-dev?.yml\napplication-prod.yml\napplication-prod?.yml\napplication-server.yml\napplication-server?.yml\napplication-common-secret.yml\napplication-qiniu.yml\n"
  },
  {
    "path": "README.md",
    "content": "# 次元印象\n\n<p align=\"center\">\n<img widht=\"240px\" height=\"240px\" src=\"./doc/images/website-icon.jpg\" ></img>\n</p>\n<p align=\"center\">\n次元印象，一个SpringCloud构建的动漫交流论坛\n</p>\n\n<p align=\"center\">\n<img src=\"https://img.shields.io/badge/JDK-1.8-green.svg\" ></img>\n<img src=\"https://img.shields.io/badge/springboot-2.6.11-green\" ></img>\n<img src=\"https://img.shields.io/badge/SpringCloud-2021.0.3-brightgreen\" ></img>\n<img src=\"https://img.shields.io/badge/mybatis--plus-3.4.1-green\" ></img>\n<img src=\"https://img.shields.io/badge/vue-2.6.14-green\" ></img>\n<img src=\"https://img.shields.io/badge/license-Apache2.0-blue\" ></img>\n</p>\n\n## 前言\n\n传说Github上的中国程序员有一半是二次元，于是~~为了吸引更多star~~我边学习SpringCloud技术边开发了这个动漫交流论坛。\n\n<p align=\"center\">\n<img width=\"240px\" src=\"./doc/images/二次元段子.jpg\" ></img>\n</p>\n\n## 项目介绍\n\n**次元印象** ( **acimage**) 是一个**基于SpringCloud**构建的**前后端分离**的动漫交流论坛。后端使用 **SpringCloud**+**Mybatis-Plus**+**Reids**+**Rabbitmq**+**Elasticsearch**。前端使用 **Vue** + **ElementUi** + **Vite** 。项目已经上线。各位Github的二次元们，还不来点个star(๑•̀ㅂ•́)و✧。\n\n## 网址\n\n**次元印象o(*≧▽≦)ツ~动漫交流论坛** www.acimage.top （只适配网页端，如果画面显小，可以适当放大浏览器，视觉效果更佳）。\n\n**首页**\n\n<p align=\"center\">\n<img src=\"./doc/images/home1.webp\" ></img>\n</p>\n\n<p align=\"center\">\n<img src=\"./doc/images/home2.webp\" ></img>\n</p>\n\n**论坛页**\n\n<p align=\"center\">\n<img src=\"./doc/images/forum1.webp\" ></img>\n</p>\n\n<p align=\"center\">\n<img src=\"./doc/images/forum2.webp\" ></img>\n</p>\n\n<p align=\"center\">\n<img src=\"./doc/images/forum3.webp\" ></img>\n</p>\n\n## 系统架构\n\n<p align=\"center\">\n<img src=\"./doc/images/architecture.jpg\" ></img>\n</p>\n\n## 项目功能\n\n#### 用户前台\n\n- **以图识图**，即上传图片，识别出论坛内相似图片及其所在话题。\n- **多样化搜索**，可以同时根据关键词、分类、标签以及排序字段等搜索，并根据关键词高亮匹配文字。\n- **相关话题推荐**，**随机推荐**。\n- **话题排行榜**（按star、浏览量、评论数等、活跃时间排行）。\n- **用户排行榜**（按话题数、star等排行）。\n- 分类、标签，并且点击可筛选出符合相关分类或标签的话题。\n- **实时展示最新活跃话题**。\n- **敏感词过滤**，**图片压缩**。\n- **消息通知**\n- 查看个人动态、修改个人信息。\n- 登录、注册、登出，**验证码防刷**，**邮箱验证**。\n- 发表话题，评论，star等。\n\n#### 后台管理\n\n- 较为完善的RBAC权限控制，可动态调整用户角色、角色权限、接口权限等。\n- 网站基本数据展示，访客量，接口调用数等。\n- 首页轮播图片管理。\n- 话题、评论、用户管理。\n- \n\n## 项目特点\n\n- **JWT**实现单点登录，网关统一身份认证和鉴权，完善的**RBAC**权限控制。\n- 利用**Redis+Lua脚本+定时任务**对变动比较频繁的数据（浏览量等）定时持久化。\n- 利用**Redis+Lua脚本+自定义注解**，只需一个注解就可以实现针对ip、用户、和总体请求量的限流。\n- 利用**Dhash**算法实现以图识图功能，效果还可以。\n- 上传的图片均用webp压缩，极大地减少带宽压力。\n\n## 项目目录\n\n**后端服务**\n\n- **acimage_gateway**：网关，统一身份认证、鉴权，分别针对用户和总体请求量的限流\n- **acimage_user**：用户中心，用户注册、登录，邮箱服务等\n- **acimage_community**：社区服务，负责论坛话题相关的核心功能\n- **acimage_image**：图片服务，负责话题图片上传、头像上传、以图识图等功能\n- **acimage_admin**: 管理服务\n\n**后端模块**\n\n- **acimage_common**：公共模块，存放实体类、公共的拦截器、公共配置以及公共工具类等\n- **acimage_feign**：放置各个模块的feign客户端\n\n**前端**\n\n- **vue_manage_system**：后台管理页面\n- **vue_acimage_web**：门户网站页面\n\n**其它**\n\n- **doc**: 一些文档、图片和数据库文件\n\n## 运行与部署\n\n**目前项目仍在不断完善，运行与部署流程以后再更新**。\n\n- ~~将**doc/sql** 下的三个数据库分别导入到**mysql**中，四个数据库分别是四个前台服务对应的数据库~~\n\n- ~~在每个服务的**application-dev.yml**文件中配置**mysql、redis、rabbitmq**、**nacos**相应的地址或账号密码~~\n\n- ~~填写**acimage_common**中的**application-qiniu-template.yml**中的七牛云账号信息，包括**access-key**、**secret-key**、**domian**、**bucket**，或者给这四个属性随便赋值（不能为空，否则**NPE**），但是这样无法使用上传图片。并将**application-qiniu-template.yml**重命名为**application-qiniu.yml**~~\n\n- ~~在**acimage_common**模块的下的**application.yml**配置 **nacos** 地址、**sentinel**地址（**sentinel**不配也不影响运行）~~\n\n- ~~启动**nacos、redis、rabbitmq、mysql**~~\n\n- ~~依次启动**acimage_user**、**acimage_community**、**acimage_image**、**acimage_gateway**，不这样启动的话可能会由于**rabbitmq**队列创建和绑定顺序的问题报错，如果遇到了，则全部服务再重新启动一遍。~~\n\n- ~~运行前端（具体看**vue_acimage_web**的**README**）后点击默认弹出的链接即可访问首页~~\n\n- ~~前台登录**：用户：wk，密码：wk123456 \n  （还有几个用户可以从数据库sql文件看到，密码均为  用户名123456）~~\n\n- 端口：\n  \n  - **acimage_user**: 8100  \n  \n  - **acimage_image** : 8090 \n  \n  - **acimage_community**: 8080\n  \n  - **acimage_gateway**: 8070\n\n## 技术选型\n\n#### 后端技术栈\n\n**SpringBoot**、**SpringCloud**、**MyBatis-plus**、**Druid**（数据库连接池）、**Redis**（分布式缓存）、**Rabbitmq**（消息队列）、**Elasticsearch**（分布式搜索引擎）、**Minio**（对象存储服务）、**Nginx**（反向代理服务器）、**Docker**（应用容器引擎）\n\n#### 用户界面前端\n\n**Vue2**、**Vue Router**（路由）、**ElementUi**（Vue基础组件库）、**axios**（http客户端）、**jsencrypt**（基于RSA加解密的js库）、**vue-dompurify-html**（防xss攻击）、**tinymce-vue**（富文本编辑器）\n\n#### 后台管理界面前端\n\n**Vite**、**TypeSript**\n\n## Todo\n\n- [x] 分类、标签\n- [x] 话题、用户排行榜\n- [x] 随机推荐\n- [x] 相似话题推荐\n- [x] 敏感词过滤\n- [x] 接口幂等\n- [x] 细化限流\n- [x] 完善RABC权限控制，网关统一身份认证及鉴权\n- [x] 增加邮件服务，邮箱验证，验证码等\n- [x] 将图片存储在minio（原来存储在七牛云）\n- [x] elasticsearch多样化搜索，关键词高亮\n- [x] 增加用户消息通知功能\n- [ ] 利用redis+lua批量提取缓存中话题和浏览量等，减少通信次数，实时展示数据。\n- [ ] 增加评论表情\n- [ ] 增加关注功能\n- [ ] 完善sentinel和nacos等配置和使用\n- [ ] 增加爬虫模块\n- [ ] 完善单元测试\n- [ ] 等等等\n\n## 交流\n\n项目起初是为了学习技术搭建的，由于能力有限，还有很多不完善的地方，欢迎各位能够指正。如果有人感兴趣（多么希望真的有人感兴趣 手动捂脸）或者该项目遇到什么问题或有什么建议提issue，可联系邮箱1179836161@qq.com或进群692992463交流。喜欢的话记得点个star。\n\n## 网站截图\n\n **Web端** \n![image text](./doc/images/home1.webp)             \n![image text](./doc/images/home2.webp)  \n![image text](./doc/images/forum1.webp)             \n![image text](./doc/images/forum2.webp)  \n![image text](./doc/images/forum3.webp) \n![image text](./doc/images/topic-info1.webp) \n![image text](./doc/images/topic-info2.webp) \n![image text](./doc/images/search.webp) \n![image text](./doc/images/search-image1.webp) \n![image text](./doc/images/search-image2.webp)  \n![image text](./doc/images/login.webp)  \n![image text](./doc/images/activity.webp) \n![image text](./doc/images/profile.webp)              \n\n **Admin端**  \n 管理系统随便放几张吧，反正管理系统都长一个样。\n ![image text](./doc/images/admin-home.webp) \n![image text](./doc/images/admin-authorize.webp)  \n\n## 开源协议\n\n[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html)\n"
  },
  {
    "path": "acimage_admin/.gitignore",
    "content": "/target/\n"
  },
  {
    "path": "acimage_admin/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>acimage</artifactId>\n        <groupId>com.acimage</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>acimage_admin</artifactId>\n    <packaging>jar</packaging>\n\n    <name>acimage_admin</name>\n    <url>http://maven.apache.org</url>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_common</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_feign</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!--druid依赖-->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid</artifactId>\n        </dependency>\n\n        <!--mysql依赖-->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n        <!--动态多数据源-->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!--minio-->\n        <dependency>\n            <groupId>io.minio</groupId>\n            <artifactId>minio</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>3.8.1</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <includeSystemScope>true</includeSystemScope>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <skipTests>true</skipTests><!--跳过测试包-->\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/AdminApplication.java",
    "content": "package com.acimage.admin;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n@Slf4j\n@SpringBootApplication\n@EnableDiscoveryClient\n@EnableScheduling\n@EnableFeignClients(basePackages=\"com.acimage.feign\")\n@ComponentScan(value={\"com.acimage\"})\n@MapperScan(basePackages = {\"com.acimage.admin.dao\"})\npublic class AdminApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(AdminApplication.class, args);\n\t\tlog.info(\"------------->>>Admin启动<<<-------------\");\n\t}\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/config/WebMvcConfig.java",
    "content": "package com.acimage.admin.config;\n\nimport com.acimage.common.web.interceptor.AccessInterceptor;\nimport com.acimage.common.web.interceptor.JwtInterceptor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Slf4j\n@Configuration\npublic class WebMvcConfig implements WebMvcConfigurer {\n\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n\n\n        registry.addInterceptor(new AccessInterceptor()).addPathPatterns(\"/**\").order(30);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/config/mybatis/CommunityDataSourceConfig.java",
    "content": "package com.acimage.admin.config.mybatis;\n\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\n\nimport javax.sql.DataSource;\n\n@Deprecated\n//@Configuration\n//@MapperScan(basePackages = \"com.acimage.admin.dao.community\", sqlSessionFactoryRef = \"communitySqlSessionFactory\")\npublic class CommunityDataSourceConfig {\n\n    @Bean(name = \"communityDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.community\")\n    public DataSource communityDataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n\n    @Bean(name = \"communitySqlSessionFactory\")\n    public SqlSessionFactory orderSqlSessionFactory(@Qualifier(\"communityDataSource\")DataSource dataSource,@Autowired GlobalConfig globalConfig) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactoryBean=new MybatisSqlSessionFactoryBean();\n\n        sqlSessionFactoryBean.setDataSource(dataSource);\n\n        sqlSessionFactoryBean.setGlobalConfig(globalConfig);\n\n        sqlSessionFactoryBean.setTypeAliasesPackage(\"com.acimage.common.model.domain\");\n        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(\"classpath:mapper/community/*.xml\"));\n        sqlSessionFactoryBean.setTypeHandlersPackage(\"com.acimage.admin.config.mybatis.typehandler\");\n        return sqlSessionFactoryBean.getObject();\n    }\n\n\n    @Bean(name = \"communityTransactionManager\")\n    public DataSourceTransactionManager orderTransactionManager(@Qualifier(\"communityDataSource\") DataSource dataSource) {\n        return new DataSourceTransactionManager(dataSource);\n    }\n\n\n    @Bean(name = \"communitySqlSessionTemplate\")\n    public SqlSessionTemplate memberSqlSessionTemplate(\n            @Qualifier(\"communitySqlSessionFactory\") SqlSessionFactory sqlSessionFactory) throws Exception {\n        return new SqlSessionTemplate(sqlSessionFactory);\n    }\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/config/mybatis/GlobalConfigBean.java",
    "content": "package com.acimage.admin.config.mybatis;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n//@Configuration\n@Deprecated\npublic class GlobalConfigBean {\n\n    @Bean\n    @ConfigurationProperties(prefix = \"mybatis-plus.global-config\")\n    GlobalConfig globalConfig(){\n        return new GlobalConfig();\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/config/mybatis/ImageDataSourceConfig.java",
    "content": "package com.acimage.admin.config.mybatis;\n\n\nimport com.alibaba.druid.pool.DruidDataSource;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\n\nimport javax.sql.DataSource;\n\n@Deprecated\n//@Configuration\n//@MapperScan(basePackages = \"com.acimage.admin.dao.image\", sqlSessionFactoryRef = \"imageSqlSessionFactory\",sqlSessionTemplateRef =\"imageSqlSessionTemplate\")\npublic class ImageDataSourceConfig {\n\n\n    @Bean(name = \"imageDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.image\")\n    public DataSource imageDataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n//    @Bean\n//    @ConfigurationProperties(prefix = \"mybatis-plus.global-config\")\n//    GlobalConfig globalConfig(){\n//        return new GlobalConfig();\n//    }\n\n\n\n\n    @Bean(name = \"imageSqlSessionFactory\")\n    public SqlSessionFactory imageSqlSessionFactory(@Qualifier(\"imageDataSource\") DataSource dataSource, @Autowired GlobalConfig globalConfig) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactoryBean=new MybatisSqlSessionFactoryBean();\n\n        sqlSessionFactoryBean.setDataSource(dataSource);\n\n        sqlSessionFactoryBean.setGlobalConfig(globalConfig);\n\n        sqlSessionFactoryBean.setTypeAliasesPackage(\"com.acimage.common.model.domain\");\n        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(\"classpath:mapper/image/*.xml\"));\n        sqlSessionFactoryBean.setTypeHandlersPackage(\"com.acimage.admin.config.mybatis.typehandler\");\n        return sqlSessionFactoryBean.getObject();\n\n    }\n\n\n    @Bean(name = \"imageTransactionManager\")\n    public DataSourceTransactionManager imageTransactionManager(@Qualifier(\"imageDataSource\") DataSource dataSource) {\n        return new DataSourceTransactionManager(dataSource);\n    }\n\n\n    @Bean(name = \"imageSqlSessionTemplate\")\n    public SqlSessionTemplate memberSqlSessionTemplate(\n            @Qualifier(\"imageSqlSessionFactory\") SqlSessionFactory sqlSessionFactory) throws Exception {\n        return new SqlSessionTemplate(sqlSessionFactory);\n    }\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/community/CategoryDao.java",
    "content": "package com.acimage.admin.dao.community;\n\nimport com.acimage.common.model.domain.community.Category;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface CategoryDao extends BaseMapper<Category> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/community/CommentDao.java",
    "content": "package com.acimage.admin.dao.community;\n\nimport com.acimage.common.model.domain.community.Comment;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface CommentDao extends BaseMapper<Comment> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/community/HomeCarouselDao.java",
    "content": "package com.acimage.admin.dao.community;\n\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\n\npublic interface HomeCarouselDao extends BaseMapper<HomeCarousel> {\n    @Select(\"select coalesce(max(location),0) from tb_home_carousel\")\n    Integer getMaxLocation();\n\n    List<HomeCarousel> count();\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/community/TopicDao.java",
    "content": "package com.acimage.admin.dao.community;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface TopicDao extends BaseMapper<Topic> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/sys/ApiDao.java",
    "content": "package com.acimage.admin.dao.sys;\n\nimport com.acimage.common.model.domain.sys.Api;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface ApiDao extends BaseMapper<Api> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/sys/AuthorizeDao.java",
    "content": "package com.acimage.admin.dao.sys;\n\nimport com.acimage.common.model.domain.sys.Authorize;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface AuthorizeDao extends BaseMapper<Authorize> {\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/sys/PermissionDao.java",
    "content": "package com.acimage.admin.dao.sys;\n\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\npublic interface PermissionDao extends BaseMapper<Permission> {\n\n    List<Permission> selectTreeByParentId(@Nullable @Param(\"parentId\") Integer parentId);\n\n    List<Permission> selectPermissionsWithParent(@Param(\"startIndex\") int startIndex,@Param(\"recordNumber\") int recordNumber);\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/sys/RoleDao.java",
    "content": "package com.acimage.admin.dao.sys;\n\nimport com.acimage.common.model.domain.sys.Role;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface RoleDao extends BaseMapper<Role> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/sys/UserRoleDao.java",
    "content": "package com.acimage.admin.dao.sys;\n\n\nimport com.acimage.common.model.domain.sys.UserRole;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UserRoleDao extends BaseMapper<UserRole> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/user/UserDao.java",
    "content": "package com.acimage.admin.dao.user;\n\nimport com.acimage.common.model.domain.user.User;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UserDao extends BaseMapper<User> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/dao/user/UserPrivacyDao.java",
    "content": "package com.acimage.admin.dao.user;\n\nimport com.acimage.common.model.domain.user.UserPrivacy;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UserPrivacyDao extends BaseMapper<UserPrivacy> {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/global/consts/ModuleConstants.java",
    "content": "package com.acimage.admin.global.consts;\n\npublic class ModuleConstants {\n    public static final String COMMUNITY=\"community\";\n    public static final String SYS=\"sys\";\n\n    public static final String IMAGE=\"image\";\n\n    public static final String USER=\"user\";\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/AdminLoginReq.java",
    "content": "package com.acimage.admin.model.request;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.Size;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class AdminLoginReq {\n    @Email(message = \"邮箱格式错误\")\n    @Size(min=6,max=32,message = \"邮箱长度在6到32之间\")\n    private String email;\n\n    @Size(min = 100, max = 2000, message = \"非法密码\")\n    String password;\n\n    @Size(min=4,max=6,message = \"验证码长度不符\")\n    String verifyCode;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/ApiAddReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport com.acimage.common.global.enums.MatchRule;\nimport com.acimage.common.global.enums.MyHttpMethod;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.http.HttpMethod;\n\nimport javax.annotation.Nullable;\nimport javax.validation.constraints.*;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ApiAddReq {\n    public static final int PATH_MAX = 200;\n    public static final int PATH_MIN = 2;\n    public static final int NOTE_MAX = 100;\n\n\n    @NotBlank\n    @Pattern(regexp = Api.PATH_PATTERN)\n    @Size(min= Api.PATH_MIN,max=Api.PATH_MAX)\n    String path;\n    @NotNull\n    MyHttpMethod method;\n    @Positive\n    @NotNull\n    Integer permissionId;\n    @Size(max=Api.NOTE_MAX)\n    String note;\n    boolean enable;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/ApiModifyReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport com.acimage.common.global.enums.MatchRule;\nimport com.acimage.common.global.enums.MyHttpMethod;\nimport com.acimage.common.model.domain.sys.Api;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.http.HttpMethod;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Pattern;\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ApiModifyReq {\n    public static final int PATH_MAX = 200;\n    public static final int PATH_MIN = 2;\n    public static final int NOTE_MAX = 100;\n    @Positive\n    Integer id;\n    @Size(min = Api.PATH_MIN, max = Api.PATH_MAX)\n    @Pattern(regexp = Api.PATH_PATTERN)\n    String path;\n    @NotNull\n    MyHttpMethod method;\n    @Positive\n    @NotNull\n    Integer permissionId;\n    @Size(max = Api.NOTE_MAX)\n    String note;\n    boolean enable;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/ApiQueryReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Min;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ApiQueryReq {\n    String keyword;\n    @Min(1)\n    Integer pageNo;\n    @Min(2)\n    @Max(20)\n    Integer pageSize;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/CarouselAddReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class CarouselAddReq {\n    @Size(min = HomeCarousel.DESC_MIN, max = HomeCarousel.DESC_MAX, message = HomeCarousel.DESC_INVALID_MSG)\n    String description;\n    @NotNull\n    @Size(min = 0, max = HomeCarousel.LINK_MAX, message = HomeCarousel.LINK_INVALID_MSG)\n    String link;\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/CarouselModifyReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class CarouselModifyReq {\n    @NotNull\n    @Positive\n    private Integer id;\n    @Size(min = HomeCarousel.DESC_MIN, max = HomeCarousel.DESC_MAX, message = HomeCarousel.DESC_INVALID_MSG)\n    String description;\n    @NotNull\n    @Size(min = 0, max = HomeCarousel.LINK_MAX, message = HomeCarousel.LINK_INVALID_MSG)\n    String link;\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/CommentQueryReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.hibernate.validator.constraints.Range;\n\nimport javax.validation.constraints.*;\n\n@Data\n@NoArgsConstructor\npublic class CommentQueryReq {\n    Long topicId;\n    String keyword;\n    @Range(min=1)\n    Integer pageNo;\n    @Range(min=5,max=20)\n    Integer pageSize;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/PermissionAddReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport com.acimage.common.model.domain.sys.Permission;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class PermissionAddReq {\n\n    Integer parentId;\n    @Size(max = Permission.CODE_MAX,message =Permission.CODE_VALIDATION_MSG)\n    String code;\n    @Size(max = Permission.NOTE_MAX,message =Permission.NOTE_VALIDATION_MSG)\n    String note;\n    @NotNull\n    @Size(max = Permission.LABEL_MAX,message =Permission.LABEL_VALIDATION_MSG)\n    String label;\n    @NotNull\n    Boolean module;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/PermissionModifyReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport com.acimage.common.model.domain.sys.Permission;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class PermissionModifyReq {\n    @Positive\n    Integer id;\n    Integer parentId;\n    @Size(max = Permission.CODE_MAX,message =Permission.CODE_VALIDATION_MSG)\n    String code;\n    @Size(max = Permission.NOTE_MAX,message =Permission.NOTE_VALIDATION_MSG)\n    String note;\n    @NotNull\n    @Size(max = Permission.LABEL_MAX,message =Permission.LABEL_VALIDATION_MSG)\n    String label;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/RoleAddReq.java",
    "content": "package com.acimage.admin.model.request;\n\n\nimport com.acimage.common.model.domain.sys.Role;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class RoleAddReq {\n    @Size(max= Role.ROLE_NAME_MAX,min=Role.ROLE_NAME_MIN,message = Role.ROLE_NAME_VALIDATION_MSG)\n    String roleName;\n    @Size(max=Role.NOTE_MAX,message = Role.NOTE_VALIDATION_MSG)\n    String note;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/RoleModifyReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport com.acimage.common.model.domain.sys.Role;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class RoleModifyReq {\n\n\n    @Positive\n    Integer id;\n    @Size(max= Role.ROLE_NAME_MAX,min=Role.ROLE_NAME_MIN,message = Role.ROLE_NAME_VALIDATION_MSG)\n    String roleName;\n    @Size(max=Role.NOTE_MAX,message = Role.NOTE_VALIDATION_MSG)\n    String note;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/TopicQueryReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.hibernate.validator.constraints.Range;\n\nimport javax.validation.constraints.*;\n\n@Data\n@NoArgsConstructor\npublic class TopicQueryReq {\n    @NotBlank\n    String column;\n    @Positive\n    @NotNull\n    Integer pageNo;\n    @Range(min=5,max=20)\n    Integer pageSize;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/request/UserQueryReq.java",
    "content": "package com.acimage.admin.model.request;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.*;\n\n@Data\npublic class UserQueryReq {\n\n    String keyword;\n    @NotNull\n    @Positive\n    Integer pageNo;\n    @Min(2)\n    @Max(20)\n    Integer pageSize;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/model/vo/WebsiteDataVo.java",
    "content": "package com.acimage.admin.model.vo;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\npublic class WebsiteDataVo {\n    private Integer pageView;\n    private Integer apiAccessCount;\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/api/ApiQueryService.java",
    "content": "package com.acimage.admin.service.api;\n\nimport com.acimage.admin.model.request.ApiQueryReq;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.common.model.page.MyPage;\n\npublic interface ApiQueryService {\n    MyPage<Api> pageBy(ApiQueryReq apiQueryReq);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/api/ApiWriteService.java",
    "content": "package com.acimage.admin.service.api;\n\nimport com.acimage.admin.model.request.ApiAddReq;\nimport com.acimage.admin.model.request.ApiModifyReq;\n\npublic interface ApiWriteService {\n    void save(ApiAddReq apiAddReq);\n\n    void update(ApiModifyReq apiModifyReq);\n\n    void delete(int id);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/api/impl/ApiQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.api.impl;\n\nimport com.acimage.admin.dao.sys.ApiDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.ApiQueryReq;\nimport com.acimage.admin.service.api.ApiQueryService;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.common.model.page.MyPage;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\n@DS(ModuleConstants.SYS)\npublic class ApiQueryServiceImpl implements ApiQueryService {\n    @Autowired\n    ApiDao apiDao;\n\n    @Override\n    public MyPage<Api> pageBy(ApiQueryReq apiQueryReq) {\n        IPage<Api> page = new Page<>(apiQueryReq.getPageNo(), apiQueryReq.getPageSize());\n        LambdaQueryWrapper<Api> qw = new LambdaQueryWrapper<>();\n        qw.orderByAsc(Api::getPath);\n        String keyword = apiQueryReq.getKeyword();\n        if (keyword != null) {\n            qw.like(Api::getPath, keyword);\n        }\n        IPage<Api> resultPage=apiDao.selectPage(page, qw);\n        return MyPage.from(resultPage);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/api/impl/ApiWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.api.impl;\n\n\nimport com.acimage.admin.dao.sys.ApiDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.ApiAddReq;\nimport com.acimage.admin.model.request.ApiModifyReq;\nimport com.acimage.admin.service.api.ApiWriteService;\nimport com.acimage.admin.service.permission.PermissionQueryService;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.acimage.common.utils.common.BeanUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Date;\nimport java.util.List;\n\n@Service\n@DS(ModuleConstants.SYS)\npublic class ApiWriteServiceImpl implements ApiWriteService {\n    @Autowired\n    ApiDao apiDao;\n    @Autowired\n    PermissionQueryService permissionQueryService;\n\n    @Override\n    public void save(ApiAddReq apiAddReq){\n        List<Permission> permissionList=permissionQueryService.listByModule(false);\n        List<Integer> permissionIds= ListUtils.extract(Permission::getId,permissionList);\n        if(!permissionIds.contains(apiAddReq.getPermissionId())){\n            throw new BusinessException(\"权限不存在或非可用权限\");\n        }\n        Api api= BeanUtils.copyPropertiesTo(apiAddReq,Api.class);\n        api.setCreateTime(new Date());\n        api.setUpdateTime(new Date());\n        apiDao.insert(api);\n    }\n\n    @Override\n    public void update(ApiModifyReq apiModifyReq){\n        List<Permission> permissionList=permissionQueryService.listByModule(false);\n        List<Integer> permissionIds= ListUtils.extract(Permission::getId,permissionList);\n        if(!permissionIds.contains(apiModifyReq.getPermissionId())){\n            throw new BusinessException(\"权限不存在或非可用权限\");\n        }\n        Api api= BeanUtils.copyPropertiesTo(apiModifyReq,Api.class);\n        api.setCreateTime(new Date());\n        api.setUpdateTime(new Date());\n        apiDao.updateById(api);\n    }\n\n    @Override\n    public void delete(int id){\n        apiDao.deleteById(id);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/authorize/AuthorizeQueryService.java",
    "content": "package com.acimage.admin.service.authorize;\n\nimport com.acimage.common.model.domain.sys.Authorize;\n\nimport java.util.List;\n\npublic interface AuthorizeQueryService {\n    List<Authorize> listAuthorizeByRoleId(int roleId);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/authorize/AuthorizeWriteService.java",
    "content": "package com.acimage.admin.service.authorize;\n\npublic interface AuthorizeWriteService {\n\n    void save(int roleId, int permissionId);\n\n    void remove(int roleId, int permissionId);\n\n    void remove(int permissionId);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/authorize/impl/AuthorizeQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.authorize.impl;\n\nimport com.acimage.admin.dao.sys.AuthorizeDao;\nimport com.acimage.admin.service.authorize.AuthorizeQueryService;\nimport com.acimage.common.model.domain.sys.Authorize;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\n@DS(\"sys\")\npublic class AuthorizeQueryServiceImpl implements AuthorizeQueryService {\n    @Autowired\n    AuthorizeDao authorizeDao;\n\n    @Override\n    public List<Authorize> listAuthorizeByRoleId(int roleId){\n        LambdaQueryWrapper<Authorize> qw=new LambdaQueryWrapper<>();\n        qw.eq(Authorize::getRoleId,roleId);\n        return authorizeDao.selectList(qw);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/authorize/impl/AuthorizeWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.authorize.impl;\n\nimport com.acimage.admin.dao.sys.AuthorizeDao;\nimport com.acimage.admin.service.authorize.AuthorizeWriteService;\nimport com.acimage.admin.service.permission.PermissionQueryService;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.sys.Authorize;\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\n@DS(\"sys\")\npublic class AuthorizeWriteServiceImpl implements AuthorizeWriteService {\n    @Autowired\n    AuthorizeDao authorizeDao;\n    @Autowired\n    PermissionQueryService permissionQueryService;\n\n    @Override\n    public void save(int roleId, int permissionId) {\n        Permission permission = permissionQueryService.getPermission(permissionId);\n        if (permission.isModule()) {\n            throw new BusinessException(\"该结点为模块，不可授权\");\n        }\n        Authorize authorize = new Authorize(roleId, permissionId);\n        authorizeDao.insert(authorize);\n    }\n\n    @Override\n    public void remove(int roleId, int permissionId) {\n        LambdaQueryWrapper<Authorize> uw = new LambdaQueryWrapper<>();\n        uw.eq(Authorize::getRoleId, roleId)\n                .eq(Authorize::getPermissionId, permissionId);\n        authorizeDao.delete(uw);\n    }\n\n    @Override\n    public void remove(int permissionId) {\n        LambdaQueryWrapper<Authorize> uw = new LambdaQueryWrapper<>();\n        uw.eq(Authorize::getPermissionId, permissionId);\n        authorizeDao.delete(uw);\n    }\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/category/CategoryQueryService.java",
    "content": "package com.acimage.admin.service.category;\n\nimport com.acimage.common.model.domain.community.Category;\n\nimport java.util.List;\n\npublic interface CategoryQueryService {\n    List<Category> listAll();\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/category/impl/CategoryQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.category.impl;\n\nimport com.acimage.admin.dao.community.CategoryDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.service.category.CategoryQueryService;\nimport com.acimage.common.model.domain.community.Category;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@DS(ModuleConstants.COMMUNITY)\n@Service\npublic class CategoryQueryServiceImpl implements CategoryQueryService {\n    @Autowired\n    CategoryDao categoryDao;\n\n    @Override\n    public List<Category> listAll(){\n        return categoryDao.selectList(null);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/comment/CommentQueryService.java",
    "content": "package com.acimage.admin.service.comment;\n\nimport com.acimage.admin.model.request.CommentQueryReq;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.model.page.MyPage;\n\npublic interface CommentQueryService {\n    MyPage<Comment> pageCommentsBy(CommentQueryReq commentQueryReq);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/comment/CommentWriteService.java",
    "content": "package com.acimage.admin.service.comment;\n\npublic interface CommentWriteService {\n    void delete(long id);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/comment/impl/CommentQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.comment.impl;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.admin.dao.community.CommentDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.CommentQueryReq;\nimport com.acimage.admin.service.comment.CommentQueryService;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.model.page.MyPage;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\n@DS(ModuleConstants.COMMUNITY)\npublic class CommentQueryServiceImpl implements CommentQueryService {\n    @Autowired\n    CommentDao commentDao;\n\n    @Override\n    public MyPage<Comment> pageCommentsBy(CommentQueryReq commentQueryReq) {\n        String keyword=commentQueryReq.getKeyword();\n        Long topicId=commentQueryReq.getTopicId();\n\n        int pageNo = commentQueryReq.getPageNo();\n        int pageSize = commentQueryReq.getPageSize();\n        IPage<Comment> page=new Page<>(pageNo,pageSize);\n\n        LambdaQueryWrapper<Comment> qw = new LambdaQueryWrapper<>();\n        qw.orderByDesc(Comment::getCreateTime);\n        if(!StrUtil.isBlank(keyword)){\n            qw.like(Comment::getContent,keyword);\n        }\n        if(topicId!=null&&topicId>0){\n            qw.eq(Comment::getTopicId,topicId);\n        }\n\n        IPage<Comment> resultPage = commentDao.selectPage(page,qw);\n\n        return MyPage.from(resultPage);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/comment/impl/CommentWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.comment.impl;\n\n\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.service.comment.CommentWriteService;\nimport com.acimage.feign.client.CommentClient;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\n@DS(ModuleConstants.COMMUNITY)\npublic class CommentWriteServiceImpl implements CommentWriteService {\n    @Autowired\n    private CommentClient client;\n\n    @Override\n    public void delete(long id){\n        client.delete(id);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/homecarousel/HomeCarouselQueryService.java",
    "content": "package com.acimage.admin.service.homecarousel;\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\n\nimport java.util.List;\n\npublic interface HomeCarouselQueryService {\n    List<HomeCarousel> listCurrent();\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/homecarousel/HomeCarouselWriteService.java",
    "content": "package com.acimage.admin.service.homecarousel;\n\nimport com.acimage.admin.model.request.CarouselAddReq;\nimport com.acimage.admin.model.request.CarouselModifyReq;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.multipart.MultipartFile;\n\npublic interface HomeCarouselWriteService {\n    @Transactional(rollbackFor = Exception.class)\n    void saveHomeCarouselImage(MultipartFile multipartFile, CarouselAddReq carouselAddReq);\n    void deleteHomeCarouselImage(long id);\n    void updateHomeCarouselImage(CarouselModifyReq modifyReq);\n    @Transactional(rollbackFor = Exception.class)\n    void coverHomeCarouselImage(long id, MultipartFile multipartFile);\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/homecarousel/impl/HomeCarouselQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.homecarousel.impl;\n\nimport com.acimage.admin.dao.community.HomeCarouselDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.service.homecarousel.HomeCarouselQueryService;\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\n@DS(ModuleConstants.COMMUNITY)\npublic class HomeCarouselQueryServiceImpl implements HomeCarouselQueryService {\n\n    @Autowired\n    HomeCarouselDao homeCarouselDao;\n    @Override\n    public List<HomeCarousel> listCurrent() {\n        LambdaQueryWrapper<HomeCarousel> qw = new LambdaQueryWrapper<>();\n        qw.orderByAsc(HomeCarousel::getLocation);\n        return homeCarouselDao.selectList(qw);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/homecarousel/impl/HomeCarouselWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.homecarousel.impl;\n\nimport cn.hutool.core.util.RandomUtil;\nimport com.acimage.admin.dao.community.HomeCarouselDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.CarouselAddReq;\nimport com.acimage.admin.model.request.CarouselModifyReq;\nimport com.acimage.admin.service.homecarousel.HomeCarouselWriteService;\nimport com.acimage.common.global.consts.StorePrefixConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport com.acimage.common.utils.common.FileUtils;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.common.utils.minio.MinioUtils;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.util.Date;\n\n@Slf4j\n@Service\n@DS(ModuleConstants.COMMUNITY)\npublic class HomeCarouselWriteServiceImpl implements HomeCarouselWriteService {\n    @Autowired\n    HomeCarouselDao homeCarouselDao;\n    @Autowired\n    MinioUtils minioUtils;\n\n    @Override\n    public void saveHomeCarouselImage(MultipartFile multipartFile, CarouselAddReq carouselAddReq) {\n        String description = carouselAddReq.getDescription();\n        String link = carouselAddReq.getLink().trim();\n        int length = 5;\n        String idString = RandomUtil.randomString(length);\n        int size = (int) multipartFile.getSize();\n        int location = homeCarouselDao.getMaxLocation() + 1;\n        Date now = new Date();\n        String fileName = String.format(\"%s.%s\", idString, FileUtils.formatOf(multipartFile));\n        String url = minioUtils.generateBaseUrl(StorePrefixConstants.HOME_CAROUSEL, now, fileName);\n        String newUrl = minioUtils.upload(multipartFile, url);\n\n        HomeCarousel homeCarousel = new HomeCarousel();\n        homeCarousel.setSize(size);\n        homeCarousel.setDescription(description);\n        homeCarousel.setCreateTime(now);\n        homeCarousel.setUpdateTime(now);\n        homeCarousel.setUrl(newUrl);\n        homeCarousel.setLocation(location);\n        homeCarousel.setLink(link);\n\n\n        homeCarouselDao.insert(homeCarousel);\n    }\n\n    @Override\n    public void deleteHomeCarouselImage(long id) {\n        HomeCarousel homeCarousel = homeCarouselDao.selectById(id);\n        if (homeCarousel == null) {\n            log.error(\"用户：{} 操作：删除主页走马灯 特殊图片id:{} 错误：图片不存在\", UserContext.getUsername(), id);\n            throw new BusinessException(\"该图片不存在\");\n        }\n\n        LambdaUpdateWrapper<HomeCarousel> qw = new LambdaUpdateWrapper<>();\n        qw.eq(HomeCarousel::getId, id);\n        int col = homeCarouselDao.delete(qw);\n        minioUtils.deleteFile(homeCarousel.getUrl());\n\n    }\n\n    @Override\n    public void updateHomeCarouselImage(CarouselModifyReq modifyReq) {\n        Integer id = modifyReq.getId();\n        String description = modifyReq.getDescription();\n        String link = modifyReq.getLink();\n        LambdaUpdateWrapper<HomeCarousel> uw = new LambdaUpdateWrapper<>();\n        uw.set(HomeCarousel::getDescription, description)\n                .set(HomeCarousel::getLink, link)\n                .set(HomeCarousel::getUpdateTime, new Date())\n                .eq(HomeCarousel::getId, id);\n\n\n        int col = homeCarouselDao.update(null, uw);\n        if (col == 0) {\n            log.error(\"用户：{} 操作：修改主页走马灯描述 特殊图片id:{} 错误：图片非主页走马灯图片\", UserContext.getUsername(), id);\n            throw new BusinessException(\"该主页走马灯图片不存在\");\n        }\n    }\n\n    @Override\n    public void coverHomeCarouselImage(long id, MultipartFile multipartFile) {\n        HomeCarousel homeCarousel = homeCarouselDao.selectById(id);\n        if (homeCarouselDao == null) {\n            log.error(\"用户：{} 操作：覆盖主页走马灯 特殊图片id:{} 错误：图片不存在\", UserContext.getUsername(), id);\n            throw new BusinessException(\"图片不存在\");\n        }\n\n        int size = (int) multipartFile.getSize();\n        Date now = new Date();\n        String oldUrl = homeCarousel.getUrl();\n        String suffix = String.format(\"%s.%s\", IdGenerator.getSnowflakeNextId(), FileUtils.formatOf(multipartFile));\n        String newUrl = minioUtils.generateBaseUrl(StorePrefixConstants.HOME_CAROUSEL, now, suffix);\n        newUrl = minioUtils.upload(multipartFile, newUrl);\n        LambdaUpdateWrapper<HomeCarousel> uw = new LambdaUpdateWrapper<>();\n        uw.set(HomeCarousel::getSize, size)\n                .set(HomeCarousel::getUpdateTime, now)\n                .set(HomeCarousel::getUrl, newUrl)\n                .eq(HomeCarousel::getId, id);\n        homeCarouselDao.update(null, uw);\n\n        //删除文件\n        minioUtils.deleteFile(oldUrl);\n\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/login/LoginService.java",
    "content": "package com.acimage.admin.service.login;\n\nimport com.acimage.admin.model.request.AdminLoginReq;\n\npublic interface LoginService {\n    String login(AdminLoginReq adminLoginReq);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/login/VerifyCodeService.java",
    "content": "package com.acimage.admin.service.login;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic interface VerifyCodeService {\n    void writeCodeImageToResponseAndRecord(HttpServletRequest request, HttpServletResponse response);\n\n    boolean verifyAndRemoveIfSuccess(HttpServletRequest request, String code);\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/login/impl/LoginServiceImpl.java",
    "content": "package com.acimage.admin.service.login.impl;\n\nimport cn.hutool.crypto.digest.DigestUtil;\nimport com.acimage.admin.dao.user.UserDao;\nimport com.acimage.admin.dao.user.UserPrivacyDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.AdminLoginReq;\nimport com.acimage.admin.service.login.LoginService;\nimport com.acimage.common.global.consts.JwtConstants;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.model.domain.user.UserPrivacy;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.RsaUtils;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Slf4j\n@Service\n@DS(ModuleConstants.USER)\npublic class LoginServiceImpl implements LoginService {\n    @Autowired\n    UserPrivacyDao userPrivacyDao;\n    @Autowired\n    UserDao userDao;\n    @Autowired\n    TokenService tokenService;\n\n    @Override\n    public String login(AdminLoginReq adminLoginReq) {\n        String email = adminLoginReq.getEmail();\n        String password = adminLoginReq.getPassword();\n\n        LambdaQueryWrapper<UserPrivacy> qw = new LambdaQueryWrapper<>();\n        qw.eq(UserPrivacy::getEmail, email);\n        UserPrivacy userPrivacy = userPrivacyDao.selectOne(qw);\n        if (userPrivacy == null) {\n            throw new BusinessException(\"邮箱不存在\");\n        }\n\n        //找到密码\n        long userId=userPrivacy.getUserId();\n        String salt = userPrivacy.getSalt();\n        String passwordDigest = userPrivacy.getPwd();\n\n        //获取私钥\n        String privateKey = RsaUtils.getPrivateKey();\n        //解密密码\n        String passwordDecrypt = RsaUtils.decrypt(privateKey, password);\n//        log.debug(\" decrypt as：{}\", passwordDecrypt);\n        //判断密码正确性\n        if (!DigestUtil.md5Hex(salt + passwordDecrypt).equals(passwordDigest)) {\n            log.warn(\"登录 错误：邮箱{} 或密码错误\", email);\n            throw new BusinessException(\"用户名或密码错误\");\n        }\n\n        //返回token\n        User user=userDao.selectById(userId);\n        return tokenService.createAndRecordToken(userId,\n                user.getUsername(),\n                user.getPhotoUrl(),\n                JwtConstants.ADMIN_EXPIRE_DAYS);\n\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/login/impl/VerifyCodeServiceImpl.java",
    "content": "package com.acimage.admin.service.login.impl;\n\nimport cn.hutool.captcha.CaptchaUtil;\nimport cn.hutool.captcha.ShearCaptcha;\nimport com.acimage.admin.service.login.VerifyCodeService;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Service\npublic class VerifyCodeServiceImpl implements VerifyCodeService {\n    @Autowired\n    RedisUtils redisUtils;\n    public static final String STRINGKP_VERIFY_CODE=\"acimage:admin:verifyCode:sessionId:\";\n\n    @Override\n    public void writeCodeImageToResponseAndRecord(HttpServletRequest request, HttpServletResponse response){\n        int width=100;\n        int height=40;\n        int codeCount=4;\n        int thickness=4;\n        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(width, height, codeCount, thickness);\n        //图形验证码写出，可以写出到文件，也可以写出到流\n        try {\n            captcha.write(response.getOutputStream());\n        } catch (IOException e) {\n            log.error(\"response.getOutputStream()错误 {}\",e.getMessage());\n            throw new RuntimeException(e);\n        }\n        //获取验证码中的文字内容\n        String verifyCode = captcha.getCode();\n        //记录到redis\n        String sessionId=request.getSession().getId();\n        long timeout=30L;\n        redisUtils.setAsString(STRINGKP_VERIFY_CODE+sessionId,verifyCode,timeout, TimeUnit.SECONDS);\n    }\n\n    @Override\n    public boolean verifyAndRemoveIfSuccess(HttpServletRequest request, String code){\n        String key=STRINGKP_VERIFY_CODE+request.getSession().getId();\n\n        String trueCode=redisUtils.getForString(key);\n        if(trueCode==null){\n            return false;\n        }\n\n        if(trueCode.equals(code)){\n            redisUtils.delete(key);\n            return true;\n        }else{\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/permission/PermissionQueryService.java",
    "content": "package com.acimage.admin.service.permission;\n\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.acimage.common.model.page.MyPage;\n\nimport java.util.List;\n\npublic interface PermissionQueryService {\n    Permission getPermission(int id);\n\n    List<Permission> getPermissionTree();\n\n    MyPage<Permission> pagePermissionsWithParent(int pageNo, int pageSize);\n\n\n    List<Permission> listByModule(boolean isModule);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/permission/PermissionWriteSercice.java",
    "content": "package com.acimage.admin.service.permission;\n\nimport com.acimage.admin.model.request.PermissionAddReq;\nimport com.acimage.admin.model.request.PermissionModifyReq;\nimport org.springframework.transaction.annotation.Transactional;\n\npublic interface PermissionWriteSercice {\n    void save(PermissionAddReq permissionAddReq);\n\n    @Transactional\n    void remove(int id);\n\n    @Transactional\n    void update(PermissionModifyReq permissionModifyReq);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/permission/impl/PermissionQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.permission.impl;\n\nimport com.acimage.admin.dao.sys.PermissionDao;\nimport com.acimage.admin.service.permission.PermissionQueryService;\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\n@DS(\"sys\")\npublic class PermissionQueryServiceImpl implements PermissionQueryService {\n    @Autowired\n    PermissionDao permissionDao;\n\n    @Override\n    public Permission getPermission(int id){\n        return permissionDao.selectById(id);\n    }\n    @Override\n    public List<Permission> getPermissionTree(){\n        return permissionDao.selectTreeByParentId(null);\n    }\n    @Override\n    public MyPage<Permission> pagePermissionsWithParent(int pageNo, int pageSize){\n        int startIndex= PageUtils.startIndexOf(pageNo,pageSize);\n        List<Permission> permissionList= permissionDao.selectPermissionsWithParent(startIndex,pageSize);\n        int count=permissionDao.selectCount(null);\n        return new MyPage<>(count,permissionList);\n    }\n\n    @Override\n    public List<Permission> listByModule(boolean isModule){\n        LambdaQueryWrapper<Permission> qw=new LambdaQueryWrapper<>();\n        qw.eq(Permission::isModule,isModule).orderByDesc(Permission::getCode);\n        return permissionDao.selectList(qw);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/permission/impl/PermissionWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.permission.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.acimage.admin.dao.sys.PermissionDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.PermissionAddReq;\nimport com.acimage.admin.model.request.PermissionModifyReq;\nimport com.acimage.admin.service.authorize.AuthorizeWriteService;\nimport com.acimage.admin.service.permission.PermissionWriteSercice;\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Date;\n\n@Service\n@DS(ModuleConstants.SYS)\npublic class PermissionWriteServiceImpl implements PermissionWriteSercice {\n\n    @Autowired\n    PermissionDao permissionDao;\n    @Autowired\n    AuthorizeWriteService authorizeWriteService;\n\n    @Override\n    public void save(PermissionAddReq permissionAddReq){\n        Permission permission=new Permission();\n        BeanUtil.copyProperties(permissionAddReq,permission);\n        permissionDao.insert(permission);\n    }\n\n    @Override\n    public void remove(int id){\n        authorizeWriteService.remove(id);\n        permissionDao.deleteById(id);\n    }\n\n    @Override\n    public void update(PermissionModifyReq permissionModifyReq){\n        Permission permission=new Permission();\n        BeanUtil.copyProperties(permissionModifyReq,permission);\n        permission.setUpdateTime(new Date());\n        permissionDao.updateById(permission);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/role/RoleQueryService.java",
    "content": "package com.acimage.admin.service.role;\n\nimport com.acimage.common.model.domain.sys.Role;\n\nimport java.util.List;\n\npublic interface RoleQueryService {\n    List<Role> listAll();\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/role/RoleWriteService.java",
    "content": "package com.acimage.admin.service.role;\n\nimport com.acimage.admin.model.request.RoleAddReq;\nimport com.acimage.admin.model.request.RoleModifyReq;\n\npublic interface RoleWriteService {\n\n\n    void save(RoleAddReq roleAddReq);\n\n    void remove(long id);\n\n    void update(RoleModifyReq roleModifyReq);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/role/impl/RoleQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.role.impl;\n\nimport com.acimage.admin.dao.sys.RoleDao;\nimport com.acimage.admin.service.role.RoleQueryService;\nimport com.acimage.common.model.domain.sys.Role;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\n@DS(\"sys\")\npublic class RoleQueryServiceImpl implements RoleQueryService {\n    @Autowired\n    RoleDao roleDao;\n\n    @Override\n    public List<Role> listAll(){\n        return roleDao.selectList(null);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/role/impl/RoleWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.role.impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.acimage.admin.dao.sys.RoleDao;\nimport com.acimage.admin.model.request.RoleAddReq;\nimport com.acimage.admin.model.request.RoleModifyReq;\nimport com.acimage.admin.service.role.RoleWriteService;\nimport com.acimage.common.model.domain.sys.Role;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Date;\n\n@Service\n@DS(\"sys\")\npublic class RoleWriteServiceImpl implements RoleWriteService {\n    @Autowired\n    RoleDao roleDao;\n\n    @Override\n    public void save(RoleAddReq roleAddReq) {\n        Role role = new Role();\n        BeanUtil.copyProperties(roleAddReq,role,false);\n        roleDao.insert(role);\n    }\n\n    @Override\n    public void remove(long id) {\n        roleDao.deleteById(id);\n    }\n\n    @Override\n    public void update(RoleModifyReq roleModifyReq) {\n        LambdaUpdateWrapper<Role> uw = new LambdaUpdateWrapper<>();\n        uw.eq(Role::getId, roleModifyReq.getId())\n                .set(Role::getRoleName, roleModifyReq.getRoleName())\n                .set(Role::getNote, roleModifyReq.getNote())\n                .set(Role::getUpdateTime, new Date());\n        roleDao.update(null, uw);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/topic/TopicQueryService.java",
    "content": "package com.acimage.admin.service.topic;\n\nimport com.acimage.admin.model.request.TopicQueryReq;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\n\npublic interface TopicQueryService {\n    MyPage<Topic> listOrderByColumn(TopicQueryReq topicQueryReq);\n\n    Integer getTopicCount();\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/topic/TopicWriteService.java",
    "content": "package com.acimage.admin.service.topic;\n\npublic interface TopicWriteService {\n    void remove(long topicId);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/topic/impl/TopicQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.topic.impl;\n\nimport com.acimage.admin.dao.community.TopicDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.TopicQueryReq;\nimport com.acimage.admin.service.topic.TopicQueryService;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n@DS(ModuleConstants.COMMUNITY)\n@Service\npublic class TopicQueryServiceImpl implements TopicQueryService {\n    public static final String STRINGKP_TOPIC_COUNT = \"acimage:admin:topic:totalCount:\";\n    @Autowired\n    TopicDao topicDao;\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public MyPage<Topic> listOrderByColumn(TopicQueryReq topicQueryReq) {\n        int pageNo = topicQueryReq.getPageNo();\n        int pageSize = topicQueryReq.getPageSize();\n        String column = StringUtils.camelToUnderline(topicQueryReq.getColumn()) ;\n        int startIndex = PageUtils.startIndexOf(pageNo, pageSize);\n\n        QueryWrapper<Topic> qw = new QueryWrapper<>();\n        qw.orderByDesc(column).last(String.format(\"limit %d,%d\", startIndex, pageSize));\n        List<Topic> topicList = topicDao.selectList(qw);\n        int count = this.getTopicCount();\n\n        return new MyPage<>(count, topicList);\n    }\n\n    @Override\n    public Integer getTopicCount() {\n        Integer totalCount = redisUtils.getForString(STRINGKP_TOPIC_COUNT, Integer.class);\n        if (totalCount != null) {\n            return totalCount;\n        }\n        LambdaQueryWrapper<Topic> qw = new LambdaQueryWrapper<>();\n        qw.select(Topic::getId);\n        totalCount = topicDao.selectCount(qw);\n        long timeout = 2L;\n        redisUtils.setAsString(STRINGKP_TOPIC_COUNT, totalCount.toString(), timeout, TimeUnit.MINUTES);\n        return totalCount;\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/topic/impl/TopicWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.topic.impl;\n\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.service.topic.TopicWriteService;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.result.Result;\nimport com.acimage.feign.client.TopicClient;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Slf4j\n@Service\n@DS(ModuleConstants.COMMUNITY)\npublic class TopicWriteServiceImpl implements TopicWriteService {\n\n    @Autowired\n    TopicClient topicClient;\n\n    @Override\n    public void remove(long topicId){\n        Result<?> result=topicClient.delete(topicId);\n        if(!result.isOk()){\n            log.error(\"话题删除失败 id:{}\",topicId);\n            throw new BusinessException(\"话题删除失败\");\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/user/UserQueryService.java",
    "content": "package com.acimage.admin.service.user;\n\nimport com.acimage.admin.model.request.UserQueryReq;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.model.page.MyPage;\n\npublic interface UserQueryService {\n    MyPage<User> listBy(UserQueryReq userQueryReq);\n\n    User getUser(long userId);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/user/UserWriteService.java",
    "content": "package com.acimage.admin.service.user;\n\npublic interface UserWriteService {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/user/impl/UserQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.user.impl;\n\n\nimport com.acimage.admin.dao.user.UserDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.model.request.UserQueryReq;\nimport com.acimage.admin.service.user.UserQueryService;\nimport com.acimage.admin.service.userrole.UserRoleQueryService;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n\n@Service\n@DS(ModuleConstants.USER)\npublic class UserQueryServiceImpl implements UserQueryService {\n    @Autowired\n    UserDao userDao;\n    @Autowired\n    UserRoleQueryService userRoleQueryService;\n\n    @Override\n    public MyPage<User> listBy(UserQueryReq userQueryReq) {\n        IPage<User> page = new Page<>(userQueryReq.getPageNo(), userQueryReq.getPageSize());\n        String like = userQueryReq.getKeyword();\n\n\n        IPage<User> resultPage;\n        if (like != null && like.trim().length() > 0) {\n            LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();\n            qw.like(User::getUsername, userQueryReq.getKeyword());\n            page = userDao.selectPage(page, qw);\n            resultPage = userDao.selectPage(page, qw);\n        } else {\n            resultPage = userDao.selectPage(page, null);\n        }\n\n        List<User> userList = resultPage.getRecords();\n        int totalCount = (int) resultPage.getTotal();\n        Map<Long, List<Integer>> map = userRoleQueryService.listUserIdWithRoleIds(ListUtils.extract(User::getId, userList));\n        for (User user : userList) {\n            List<Integer> roleIds = map.get(user.getId());\n            if (roleIds == null) {\n                user.setRoleIds(new ArrayList<>());\n            } else {\n                user.setRoleIds(roleIds);\n            }\n        }\n\n        return new MyPage<>(totalCount, userList);\n    }\n\n    @Override\n    public User getUser(long userId){\n        return userDao.selectById(userId);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/user/impl/UserWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.user.impl;\n\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.stereotype.Service;\n\n@Service\n@DS(ModuleConstants.USER)\npublic class UserWriteServiceImpl {\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/userrole/UserRoleQueryService.java",
    "content": "package com.acimage.admin.service.userrole;\n\nimport com.acimage.common.model.domain.sys.UserRole;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface UserRoleQueryService {\n    Map<Long, List<Integer>> listUserIdWithRoleIds(List<Long> userIds);\n\n    UserRole getUserRole(long userId, int roleId);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/userrole/UserRoleWriteService.java",
    "content": "package com.acimage.admin.service.userrole;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface UserRoleWriteService {\n    void save(long userId, int roleId);\n\n    void remove(long userId, int roleId);\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/userrole/impl/UserRoleQueryServiceImpl.java",
    "content": "package com.acimage.admin.service.userrole.impl;\n\nimport com.acimage.admin.dao.sys.UserRoleDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.service.userrole.UserRoleQueryService;\nimport com.acimage.common.model.domain.sys.UserRole;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@Service\n@DS(ModuleConstants.SYS)\npublic class UserRoleQueryServiceImpl implements UserRoleQueryService {\n\n    @Autowired\n    UserRoleDao userRoleDao;\n\n    @Override\n    public Map<Long, List<Integer>> listUserIdWithRoleIds(List<Long> userIds) {\n        LambdaQueryWrapper<UserRole> qw = new LambdaQueryWrapper<>();\n        qw.in(UserRole::getUserId, userIds);\n        List<UserRole> userRoles = userRoleDao.selectList(qw);\n\n        //组装\n        Map<Long, List<Integer>> map = new HashMap<>();\n        for (UserRole userRole : userRoles) {\n            long userId = userRole.getUserId();\n            List<Integer> roleIds = map.get(userId);\n            if (roleIds == null) {\n                roleIds = new ArrayList<>();\n                roleIds.add(userRole.getRoleId());\n                map.put(userId,roleIds);\n            }else{\n                roleIds.add(userRole.getRoleId());\n            }\n        }\n        return map;\n    }\n\n    @Override\n    public UserRole getUserRole(long userId, int roleId){\n        LambdaQueryWrapper<UserRole> qw=new LambdaQueryWrapper<>();\n        qw.eq(UserRole::getUserId,userId)\n                .eq(UserRole::getRoleId,roleId);\n        return userRoleDao.selectOne(qw);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/userrole/impl/UserRoleWriteServiceImpl.java",
    "content": "package com.acimage.admin.service.userrole.impl;\n\nimport com.acimage.admin.dao.sys.UserRoleDao;\nimport com.acimage.admin.global.consts.ModuleConstants;\nimport com.acimage.admin.service.user.UserQueryService;\nimport com.acimage.admin.service.userrole.UserRoleQueryService;\nimport com.acimage.admin.service.userrole.UserRoleWriteService;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.sys.UserRole;\nimport com.acimage.common.utils.IdGenerator;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Date;\n\n@DS(ModuleConstants.SYS)\n@Service\npublic class UserRoleWriteServiceImpl implements UserRoleWriteService {\n\n    @Autowired\n    UserRoleDao userRoleDao;\n    @Autowired\n    UserRoleQueryService userRoleQueryService;\n    @Autowired\n    UserQueryService userQueryService;\n\n    @Override\n    public void save(long userId, int roleId) {\n        if (userRoleQueryService.getUserRole(userId, roleId) != null) {\n            throw new BusinessException(\"该用户已有该角色\");\n        }\n\n        if (userQueryService.getUser(userId) == null) {\n            throw new BusinessException(\"该用户不存在\");\n        }\n\n        long id = IdGenerator.getSnowflakeNextId();\n        UserRole userRole = new UserRole(id, userId, roleId, new Date());\n        userRoleDao.insert(userRole);\n    }\n\n    @Override\n    public void remove(long userId, int roleId) {\n        LambdaQueryWrapper<UserRole> qw=new LambdaQueryWrapper<>();\n        qw.eq(UserRole::getUserId,userId)\n                .eq(UserRole::getRoleId,roleId);\n        userRoleDao.delete(qw);\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/websitedata/WebsiteDataService.java",
    "content": "package com.acimage.admin.service.websitedata;\n\nimport com.acimage.admin.model.vo.WebsiteDataVo;\n\npublic interface WebsiteDataService {\n    WebsiteDataVo getWebsiteData();\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/service/websitedata/impl/WebsiteDataServiceImpl.java",
    "content": "package com.acimage.admin.service.websitedata.impl;\n\nimport com.acimage.admin.model.vo.WebsiteDataVo;\nimport com.acimage.admin.service.websitedata.WebsiteDataService;\nimport com.acimage.common.global.consts.SysKeyConstants;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class WebsiteDataServiceImpl implements WebsiteDataService {\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public WebsiteDataVo getWebsiteData() {\n        WebsiteDataVo websiteDataVo = new WebsiteDataVo();\n        Long pageViewLong = redisUtils.sizeForHyperLogLog(SysKeyConstants.LOGK_PAGE_VIEW);\n        Integer apiAccessCount = redisUtils.getForString(SysKeyConstants.STRINGK_INTERFACE_TOTAL, Integer.class);\n        if (pageViewLong != null) {\n            websiteDataVo.setPageView(pageViewLong.intValue());\n        }\n\n        websiteDataVo.setApiAccessCount(apiAccessCount);\n        return websiteDataVo;\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/AdminLoginController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.AdminLoginReq;\nimport com.acimage.admin.service.login.LoginService;\nimport com.acimage.admin.service.login.VerifyCodeService;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.RsaUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/logins\")\n@Validated\npublic class AdminLoginController {\n    @Autowired\n    LoginService loginService;\n    @Autowired\n    VerifyCodeService verifyCodeService;\n\n    @RequestLimit(limitTimes = {1}, durations = {2}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @PostMapping(\"/doLogin\")\n    public Result<String> doLogin(@Validated @RequestBody AdminLoginReq adminLoginReq, HttpServletRequest request) {\n        String code = adminLoginReq.getVerifyCode();\n        boolean verifyCorrect = verifyCodeService.verifyAndRemoveIfSuccess(request, code);\n        if (!verifyCorrect) {\n            return Result.fail(\"验证码错误，请重新验证\");\n        }\n        return Result.ok(loginService.login(adminLoginReq));\n    }\n\n    @GetMapping(\"/publicKey\")\n    public Result<?> getPublicKey() {\n        return Result.ok(RsaUtils.getPublicKey());\n    }\n\n\n    @GetMapping(\"/commonCode\")\n    public Result<?> getCommonVerifyCode(HttpServletRequest request, HttpServletResponse response) {\n        verifyCodeService.writeCodeImageToResponseAndRecord(request,response);\n        return Result.ok();\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/ApiOperateController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.ApiAddReq;\nimport com.acimage.admin.model.request.ApiModifyReq;\nimport com.acimage.admin.service.api.ApiWriteService;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/apis/operate\")\n@Validated\npublic class ApiOperateController {\n    @Autowired\n    ApiWriteService apiWriteService;\n\n    @PostMapping\n    public Result<?> addApi(@Validated @RequestBody ApiAddReq apiAddreq){\n        String path=apiAddreq.getPath().trim();\n        if(path.length()<2){\n            return Result.fail(\"路径有效长度不少于2\");\n        }\n        apiAddreq.setPath(path);\n        apiWriteService.save(apiAddreq);\n        return Result.ok();\n    }\n\n    @PutMapping\n    public Result<?> modifyApi(@Validated @RequestBody ApiModifyReq apiModifyReq){\n        String path=apiModifyReq.getPath().trim();\n        if(path.length()<2){\n            return Result.fail(\"路径有效长度不少于2\");\n        }\n        apiModifyReq.setPath(path);\n        apiWriteService.update(apiModifyReq);\n        return Result.ok();\n    }\n\n    @DeleteMapping(\"/{id}\")\n    public Result<?> deleteApi(@Positive @NotNull @PathVariable int id){\n        apiWriteService.delete(id);\n        return Result.ok();\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/ApiQueryController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.ApiQueryReq;\nimport com.acimage.admin.service.api.ApiQueryService;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/apis/query\")\n@Validated\npublic class ApiQueryController {\n    @Autowired\n    ApiQueryService apiQueryService;\n\n    @GetMapping(\"/search\")\n    public Result<MyPage<Api>> queryApisBy(@Validated @ModelAttribute ApiQueryReq apiQueryReq){\n        return Result.ok(apiQueryService.pageBy(apiQueryReq));\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/AuthorizeOperateController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.service.authorize.AuthorizeWriteService;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/authorizes\")\n@Validated\npublic class AuthorizeOperateController {\n    @Autowired\n    AuthorizeWriteService authorizeWriteService;\n\n    @PostMapping\n    public Result<?> addAuthorize(@RequestParam @Positive Integer roleId,\n                                  @RequestParam @Positive Integer permissionId){\n        authorizeWriteService.save(roleId,permissionId);\n        return Result.ok();\n    }\n\n    @DeleteMapping(\"/{roleId}/{permissionId}\")\n    public Result<?> deleteAuthorize(@PathVariable @Positive Integer roleId,\n                                  @PathVariable @Positive Integer permissionId){\n        authorizeWriteService.remove(roleId,permissionId);\n        return Result.ok();\n    }\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/AuthorizeQueryController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.service.authorize.AuthorizeQueryService;\nimport com.acimage.common.model.domain.sys.Authorize;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/authorizes\")\n@Validated\npublic class AuthorizeQueryController {\n    @Autowired\n    AuthorizeQueryService authorizeQueryService;\n\n    @GetMapping(\"/roleId/{roleId}\")\n    public Result<List<Authorize>> queryRoleAuthorize(@PathVariable @Positive Integer roleId){\n        return Result.ok(authorizeQueryService.listAuthorizeByRoleId(roleId));\n    }\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/CategoryController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.service.category.CategoryQueryService;\nimport com.acimage.common.model.domain.community.Category;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/categories\")\n@Validated\npublic class CategoryController {\n    @Autowired\n    CategoryQueryService categoryQueryService;\n\n    @GetMapping(\"/all\")\n    public Result<List<Category>> queryAll(){\n        return Result.ok(categoryQueryService.listAll()) ;\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/CommentOperateController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.CommentQueryReq;\nimport com.acimage.admin.service.comment.CommentQueryService;\nimport com.acimage.admin.service.comment.CommentWriteService;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/comments/operate\")\n@Validated\npublic class CommentOperateController {\n    @Autowired\n    CommentWriteService commentWriteService;\n\n    @DeleteMapping(\"/{id}\")\n    public Result<?> pageCommentsBy(@PathVariable @Positive Long id) {\n        commentWriteService.delete(id);\n        return Result.ok();\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/CommentQueryController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.CommentQueryReq;\nimport com.acimage.admin.model.request.TopicQueryReq;\nimport com.acimage.admin.service.comment.CommentQueryService;\nimport com.acimage.admin.service.topic.TopicQueryService;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.LambdaUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ModelAttribute;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/comments/query\")\n@Validated\npublic class CommentQueryController {\n    @Autowired\n    CommentQueryService commentQueryService;\n\n    @GetMapping(\"/by\")\n    public Result<MyPage<Comment>> pageCommentsBy(@Validated @ModelAttribute CommentQueryReq queryReq) {\n\n        return Result.ok(commentQueryService.pageCommentsBy(queryReq));\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/HomeCarouselController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.admin.model.request.CarouselAddReq;\nimport com.acimage.admin.model.request.CarouselModifyReq;\nimport com.acimage.admin.service.homecarousel.HomeCarouselWriteService;\nimport com.acimage.admin.service.homecarousel.HomeCarouselQueryService;\nimport com.acimage.common.global.consts.FileFormatConstants;\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/homeCarousels\")\n@Validated\npublic class HomeCarouselController {\n    @Autowired\n    HomeCarouselWriteService homeCarouselWriteService;\n    @Autowired\n    HomeCarouselQueryService homeCarouselQueryService;\n\n    @PostMapping\n    public Result<?> addImage(@RequestParam(\"image\") MultipartFile imageFile,\n                              @Validated @ModelAttribute CarouselAddReq carouselAddReq) {\n        String originName = imageFile.getOriginalFilename();\n        String format = StrUtil.subAfter(originName, '.', true);\n        if (!FileFormatConstants.ALLOWED_CAROUSEL_FORMAT.contains(format)) {\n            return Result.fail(\"图片格式需为\"+FileFormatConstants.ALLOWED_CAROUSEL_FORMAT);\n        }\n        homeCarouselWriteService.saveHomeCarouselImage(imageFile, carouselAddReq);\n        return Result.ok();\n    }\n\n\n    @DeleteMapping(\"/{id}\")\n    public Result<?> deleteImage(@PathVariable @Positive Long id) {\n        homeCarouselWriteService.deleteHomeCarouselImage(id);\n        return Result.ok();\n    }\n\n    @PutMapping(\"/descriptionAndLink\")\n    public Result<?> modifyDescription(@Validated @ModelAttribute CarouselModifyReq carouselModifyReq) {\n        homeCarouselWriteService.updateHomeCarouselImage(carouselModifyReq);\n        return Result.ok();\n    }\n\n    @PostMapping(\"/cover\")\n    public Result<?> coverImageFile(@RequestParam(\"id\") @Positive Long id,\n                                    @RequestParam(\"image\") MultipartFile imageFile) {\n        String originName = imageFile.getOriginalFilename();\n        String format = StrUtil.subAfter(originName, '.', true);\n        if (!FileFormatConstants.ALLOWED_CAROUSEL_FORMAT.contains(format)) {\n            return Result.fail(\"图片格式需为\"+FileFormatConstants.ALLOWED_CAROUSEL_FORMAT);\n        }\n        homeCarouselWriteService.coverHomeCarouselImage(id, imageFile);\n        return Result.ok();\n    }\n\n    @GetMapping(\"/current\")\n    public Result<List<HomeCarousel>> queryCurrentHomeCarousel() {\n        return Result.ok(homeCarouselQueryService.listCurrent());\n\n    }\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/PermissionOperateController.java",
    "content": "package com.acimage.admin.web.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.admin.model.request.PermissionAddReq;\nimport com.acimage.admin.model.request.PermissionModifyReq;\nimport com.acimage.admin.service.permission.PermissionWriteSercice;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@Validated\n@RequestMapping(\"/api/admin/permissions\")\npublic class PermissionOperateController {\n    @Autowired\n    PermissionWriteSercice permissionWriteSercice;\n\n    @PostMapping\n    public Result<?> add(@Validated @RequestBody PermissionAddReq permissionAddReq){\n\n        if(permissionAddReq.getCode()==null|| StrUtil.isBlank(permissionAddReq.getCode())){\n            permissionAddReq.setCode(null);\n        }\n        if(!permissionAddReq.getModule() &&permissionAddReq.getCode()==null){\n            return Result.fail(\"非模块的权限码不能为空\");\n        }\n        permissionWriteSercice.save(permissionAddReq);\n        return Result.ok();\n    }\n\n    @PutMapping\n    public Result<?> modify(@Validated @RequestBody PermissionModifyReq permissionModifyReq){\n        permissionWriteSercice.update(permissionModifyReq);\n        return Result.ok();\n    }\n\n    @DeleteMapping(\"/{id}\")\n    public Result<?> modify(@PathVariable @Positive Integer id){\n        permissionWriteSercice.remove(id);\n        return Result.ok();\n    }\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/PermissionQueryController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.service.permission.PermissionQueryService;\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Positive;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@Validated\n@RequestMapping(\"/api/admin/permissions\")\npublic class PermissionQueryController {\n    @Autowired\n    PermissionQueryService permissionQueryService;\n\n    @GetMapping(\"/tree\")\n    public Result<List<Permission>> queryPermissionTree(){\n        return Result.ok(permissionQueryService.getPermissionTree());\n    }\n\n    @GetMapping(\"/page/{pageNo}/{pageSize}\")\n    public Result<MyPage<Permission>> pagePermissionsWithParent(@PathVariable @Positive Integer pageNo,\n                                                                @PathVariable @Max(20) Integer pageSize){\n        return Result.ok(permissionQueryService.pagePermissionsWithParent(pageNo,pageSize));\n    }\n\n    @GetMapping(\"/modules\")\n    public Result<List<Permission>> queryModules(){\n        return Result.ok(permissionQueryService.listByModule(true));\n    }\n\n    @GetMapping(\"/nonModules\")\n    public Result<List<Permission>> queryNonModules(){\n        return Result.ok(permissionQueryService.listByModule(false));\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/RoleController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.RoleAddReq;\nimport com.acimage.admin.model.request.RoleModifyReq;\nimport com.acimage.admin.service.role.RoleQueryService;\nimport com.acimage.admin.service.role.RoleWriteService;\nimport com.acimage.common.model.domain.sys.Role;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/roles\")\n@Validated\npublic class RoleController {\n    @Autowired\n    RoleQueryService roleQueryService;\n    @Autowired\n    RoleWriteService roleWriteService;\n\n    @GetMapping(\"/all\")\n    public Result<List<Role>> queryAllRoles(){\n        return Result.ok(roleQueryService.listAll());\n    }\n\n    @PostMapping()\n    public Result<?> add(@Validated @RequestBody RoleAddReq roleAddReq){\n        roleWriteService.save(roleAddReq);\n        return Result.ok();\n    }\n\n    @DeleteMapping(\"/{id}\")\n    public Result<?> delete(@PathVariable @Positive Integer id){\n        roleWriteService.remove(id);\n        return Result.ok();\n    }\n\n    @PutMapping()\n    public Result<?> modify(@Validated @RequestBody RoleModifyReq roleModifyReq){\n        roleWriteService.update(roleModifyReq);\n        return Result.ok();\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/TopicOperateController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.service.topic.TopicWriteService;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/topics/operate\")\n@Validated\npublic class TopicOperateController {\n    @Autowired\n    TopicWriteService topicWriteService;\n\n    @DeleteMapping(\"/{topicId}\")\n    public Result<?> delete(@PathVariable @Positive Long topicId){\n        topicWriteService.remove(topicId);\n        return Result.ok();\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/TopicQueryController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.TopicQueryReq;\nimport com.acimage.admin.service.topic.TopicQueryService;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.LambdaUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/topics/query\")\n@Validated\npublic class TopicQueryController {\n    @Autowired\n    TopicQueryService topicQueryService;\n\n    @GetMapping(\"/orderBy\")\n    public Result listOrderBy(@Validated @ModelAttribute TopicQueryReq topicQueryReq) {\n        List<String> allowedColumns= LambdaUtils.columnsFrom(Topic::getUpdateTime,Topic::getCreateTime,Topic::getPageView,Topic::getStarCount,Topic::getCommentCount);\n        if(!allowedColumns.contains(topicQueryReq.getColumn())){\n            return Result.fail(\"非法查询字段\");\n        }\n        return Result.ok(topicQueryService.listOrderByColumn(topicQueryReq));\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/UserQueryController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.UserQueryReq;\nimport com.acimage.admin.service.user.UserQueryService;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.result.Result;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ModelAttribute;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/users/query\")\n@Validated\npublic class UserQueryController {\n    @Autowired\n    UserQueryService userQueryService;\n\n    @GetMapping(\"/search\")\n    public Result<MyPage<User>> queryUsersBy(@Validated @ModelAttribute UserQueryReq userQueryReq){\n        String keyword=userQueryReq.getKeyword();\n        if(keyword!=null){\n            if(keyword.trim().length()==0){\n                userQueryReq.setKeyword(null);\n            }else{\n                userQueryReq.setKeyword(keyword.trim());\n            }\n        }\n        return Result.ok(userQueryService.listBy(userQueryReq));\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/UserRoleOperateController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.request.UserQueryReq;\nimport com.acimage.admin.service.userrole.UserRoleWriteService;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/userRoles/operate\")\n@Validated\npublic class UserRoleOperateController {\n    @Autowired\n    UserRoleWriteService userRoleWriteService;\n\n    @PostMapping\n    public Result<?> addRoleForUser(@Positive @NotNull Long userId, @Positive @NotNull Integer roleId) {\n        userRoleWriteService.save(userId, roleId);\n        return Result.ok();\n    }\n\n    @DeleteMapping(\"/{userId}/{roleId}\")\n    public Result<?> deleteRoleForUser(@PathVariable @Positive @NotNull Long userId,@PathVariable @Positive @NotNull Integer roleId) {\n        userRoleWriteService.remove(userId, roleId);\n        return Result.ok();\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/java/com/acimage/admin/web/controller/WebsiteDataController.java",
    "content": "package com.acimage.admin.web.controller;\n\n\nimport com.acimage.admin.model.vo.WebsiteDataVo;\nimport com.acimage.admin.service.websitedata.WebsiteDataService;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/admin/websites\")\n@Validated\npublic class WebsiteDataController {\n    @Autowired\n    WebsiteDataService websiteDataService;\n\n    @GetMapping(\"/accessData\")\n    public Result<WebsiteDataVo> queryWebsiteData() {\n        return Result.ok(websiteDataService.getWebsiteData());\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/main/resources/application-dev.yml",
    "content": "server:\n  port: 8060\n\nspring:\n  config:\n    activate:\n      on-profile:\n        - dev\n#  datasource:\n#    community:\n#      type: com.alibaba.druid.pool.DruidDataSource\n#      driver-class-name: com.mysql.cj.jdbc.Driver\n#      jdbc-url: jdbc:mysql://localhost:3306/acimage_community?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n#      username: root\n#      password: mysql\n#    image:\n#      type: com.alibaba.druid.pool.DruidDataSource\n#      driver-class-name: com.mysql.cj.jdbc.Driver\n#      jdbc-url: jdbc:mysql://localhost:3306/acimage_image?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n#      username: root\n#      password: mysql\n  datasource:\n    dynamic:\n      primary: sys #设置默认的数据源或者数据源组,默认值即为master\n      strict: true #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源\n      datasource:\n        sys:\n          url: jdbc:mysql://localhost:3306/acimage_sys?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n          username: root\n          password: mysql\n          driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置\n        community:\n          url: jdbc:mysql://localhost:3306/acimage_community?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n          username: root\n          password: mysql\n          driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置\n        image:\n          url: jdbc:mysql://localhost:3306/acimage_image?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n          username: root\n          password: mysql\n          driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置\n        user:\n          url: jdbc:mysql://localhost:3306/acimage_user?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n          username: root\n          password: mysql\n          driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: redis\n    lettuce:\n      pool:\n        max-active: 8\n        max-idle: 8 #最大空闲连接\n        min-idle: 0 #最小空闲连接\n        max-wait: 100ms #连接等待时间\n  cloud:\n    nacos:\n      server-addr: 43.136.68.91:8848 #nacos 服务地址\n"
  },
  {
    "path": "acimage_admin/src/main/resources/application.yml",
    "content": "\nspring:\n  profiles:\n    include: common,common-secret\n    active: dev2\n  application:\n    name: admin-service #服务名称\n  servlet:\n    multipart:\n      max-file-size: 10MB\n      max-request-size: 55MB\n#  rabbitmq:\n#    host: 192.168.130.128\n#    port: 5672\n#    username: acimage\n#    password: acimage\n#    virtual-host: /acimage\n#    listener:\n#      simple:\n#        auto-startup: false #消费者是否启动\n#      direct:\n#        auto-startup: false #生产者是否启动\n\n\nmybatis-plus:\n  mapper-locations: classpath*:mapper/**/*.xml\n  type-aliases-package: com.acimage.common.model.domain\n  type-handlers-package: com.acimage.common.config.typehandler\n  global-config:\n    db-config:\n      table-prefix: tb_\n\n\nfeign:\n  okhttp:\n    enabled: true\n  httpclient:\n    max-connections: 20 # 最大的连接数\n    max-connections-per-route: 5 # 每个路径的最大连接数"
  },
  {
    "path": "acimage_admin/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- 配置文件修改时重新加载，默认true -->\n<configuration scan=\"true\">\n\n    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->\n    <springProperty scope=\"context\" name=\"spring.application.name\" source=\"spring.application.name\"/>\n    <springProperty scope=\"context\" name=\"LOG_HOME\" source=\"logback.base\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%20.20thread{20}] %40logger{40} : %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"info\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>INFO</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-info.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>8</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"warn\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>WARN</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-warn.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"error\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>ERROR</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-error.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n\n    <springProfile name=\"dev2\">\n        <logger name=\"com.acimage.admin.dao\" level=\"INFO\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <logger name=\"com.acimage\" level=\"DEBUG\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n    <springProfile name=\"prod,server\">\n        <logger name=\"com.acimage\" level=\"info\" additivity=\"false\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n\n</configuration>"
  },
  {
    "path": "acimage_admin/src/main/resources/mapper/community/TopicMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.admin.dao.community.TopicDao\">\n    <resultMap id=\"topicWithUser\" type=\"Topic\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"activityTime\" column=\"activity_time\"/>\n        <result property=\"pageView\" column=\"page_view\"/>\n        <result property=\"starCount\" column=\"star_count\"/>\n        <result property=\"commentCount\" column=\"comment_count\"/>\n        <result property=\"coverImageUrl\" column=\"cover_image_url\"/>\n        <result property=\"categoryId\" column=\"category_id\"/>\n    </resultMap>\n\n\n</mapper>"
  },
  {
    "path": "acimage_admin/src/main/resources/mapper/image/HomeCarouselMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.admin.dao.community.HomeCarouselDao\" >\n\n<select id=\"count\" resultType=\"HomeCarousel\">\n    select * from tb_home_carousel\n</select>\n\n</mapper>"
  },
  {
    "path": "acimage_admin/src/main/resources/mapper/sys/ApiMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.admin.dao.sys.ApiDao\">\n\n\n\n<!--    <resultMap id=\"apiWithPermission\" type=\"Api\">-->\n<!--        <id property=\"id\" column=\"id\"/>-->\n<!--        <result property=\"path\" column=\"path\"/>-->\n<!--        <result property=\"matchRule\" column=\"match_rule\"/>-->\n<!--        <result property=\"method\" column=\"method\"/>-->\n<!--        <result property=\"note\" column=\"note\"/>-->\n<!--        <result property=\"permissionId\" column=\"permission_id\"/>-->\n<!--        <result property=\"createTime\" column=\"create_time\"/>-->\n<!--        <result property=\"updateTime\" column=\"update_time\"/>-->\n<!--        <association property=\"permission\" javaType=\"Permission\" column=\"permission_id\"-->\n<!--                     select=\"com.acimage.admin.dao.sys.ApiDao\"/>-->\n<!--    </resultMap>-->\n\n\n<!--    <select id=\"selectApisWithPermission\" resultMap=\"permissionWithParent\">-->\n<!--        select * from tb_permission order by code limit #{startIndex},#{recordNumber}-->\n<!--    </select>-->\n\n</mapper>"
  },
  {
    "path": "acimage_admin/src/main/resources/mapper/sys/PermissionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.admin.dao.sys.PermissionDao\">\n\n    <resultMap id=\"permissionTree\" type=\"Permission\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"code\" column=\"code\"/>\n        <result property=\"parentId\" column=\"parent_id\"/>\n        <result property=\"label\" column=\"label\"/>\n        <result property=\"note\" column=\"note\"/>\n        <result property=\"module\" column=\"module\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <collection property=\"children\" ofType=\"Permission\" column=\"id\"\n                    select=\"com.acimage.admin.dao.sys.PermissionDao.selectTreeByParentId\"/>\n    </resultMap>\n\n    <resultMap id=\"permissionWithParent\" type=\"Permission\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"code\" column=\"code\"/>\n        <result property=\"parentId\" column=\"parent_id\"/>\n        <result property=\"label\" column=\"label\"/>\n        <result property=\"note\" column=\"note\"/>\n        <result property=\"module\" column=\"module\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <association property=\"parent\" javaType=\"Permission\" column=\"parent_id\"\n                     select=\"com.acimage.admin.dao.sys.PermissionDao.selectById\"/>\n    </resultMap>\n\n    <select id=\"selectTreeByParentId\" resultMap=\"permissionTree\">\n        select * from tb_permission where\n        <choose>\n            <when test=\"parentId == null\">\n                parent_id is null\n            </when>\n            <otherwise>\n                parent_id=#{parentId}\n            </otherwise>\n        </choose>\n    </select>\n\n    <select id=\"selectPermissionsWithParent\" resultMap=\"permissionWithParent\">\n        select * from tb_permission order by code desc limit #{startIndex},#{recordNumber}\n    </select>\n\n</mapper>"
  },
  {
    "path": "acimage_admin/src/main/resources/mapper/user/TopicMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.admin.dao.sys.UserRoleDao\">\n<!--    <resultMap id=\"userWithRoleIds\" type=\"User\">-->\n<!--        <result property=\"id\" column=\"user_id\"/>-->\n<!--        <result property=\"createTime\" column=\"create_time\"/>-->\n<!--        <result property=\"updateTime\" column=\"update_time\"/>-->\n<!--        <result property=\"activityTime\" column=\"activity_time\"/>-->\n<!--        <result property=\"pageView\" column=\"page_view\"/>-->\n<!--        <result property=\"starCount\" column=\"star_count\"/>-->\n<!--        <result property=\"commentCount\" column=\"comment_count\"/>-->\n<!--        <result property=\"coverImageUrl\" column=\"cover_image_url\"/>-->\n<!--        <result property=\"roleIds\" column=\"category_id\"/>-->\n<!--        <collection property=\"roleIds\" ofType=\"Integer\" column=\"id\"-->\n<!--                    select=\"com.acimage.admin.dao.sys.tagTopicDao.selectTagIds\"/>-->\n<!--    </resultMap>-->\n\n\n</mapper>"
  },
  {
    "path": "acimage_admin/src/test/java/com/acimage/admin/AdminApplicationTest.java",
    "content": "package com.acimage.admin;\n\n\nimport com.acimage.common.deprecated.QiniuUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.io.File;\n\n@SpringBootTest\nclass AdminApplicationTest {\n\t@Autowired\n\tQiniuUtils qiniuUtils;\n\n\t@Test\n\tvoid qiniuUtilsTest() {\n\t\tFile file = new File(\"F:\\\\MyImage\\\\素材\\\\bg2.png\");\n\t\tqiniuUtils.upload(file,\"test/xlg.png\");\n\t}\n\n\n\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_admin/src/test/java/com/acimage/admin/dao/community/SpDaoTest.java",
    "content": "package com.acimage.admin.dao.community;\n\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.Date;\n\n@SpringBootTest\npublic class SpDaoTest {\n    @Autowired\n    HomeCarouselDao homeCarouselDao;\n\n    @Test\n    public void deleteTest(){\n        long id=10086L;\n        homeCarouselDao.deleteById(id);\n    }\n\n    @Test\n    public void getMaxLocationOfHomeCarousel(){\n        System.out.println(homeCarouselDao.getMaxLocation());\n    }\n\n    @Test\n    public void testSelectAll(){\n        System.out.println(homeCarouselDao.selectList(null));\n\n        System.out.println(homeCarouselDao.count());\n    }\n\n}\n"
  },
  {
    "path": "acimage_admin/src/test/java/com/acimage/admin/dao/community/TopicDaoTest.java",
    "content": "package com.acimage.admin.dao.community;\n\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\npublic class TopicDaoTest {\n\n    @Autowired\n    TopicDao topicDao;\n\n    @Test\n    void testSelectAll(){\n        System.out.println(topicDao.selectList(null));\n\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/test/java/com/acimage/admin/dao/sys/ApiDaoTest.java",
    "content": "package com.acimage.admin.dao.sys;\n\n\nimport com.acimage.common.global.enums.MatchRule;\nimport com.acimage.common.model.domain.sys.Api;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.HttpMethod;\n\n@SpringBootTest\npublic class ApiDaoTest {\n\n    @Autowired\n    ApiDao apiDao;\n\n\n    @Test\n    void testSelect(){\n        System.out.println(apiDao.selectList(null));\n    }\n}\n"
  },
  {
    "path": "acimage_admin/src/test/java/com/acimage/admin/service/HomeCarouselWriteServiceTest.java",
    "content": "package com.acimage.admin.service;\n\nimport com.acimage.admin.service.homecarousel.HomeCarouselWriteService;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\npublic class HomeCarouselWriteServiceTest {\n\n    @Autowired\n    HomeCarouselWriteService homeCarouselWriteService;\n\n\n}\n"
  },
  {
    "path": "acimage_common/.gitignore",
    "content": "/.idea\n/src/main/resources/application-qiniu.yml\n"
  },
  {
    "path": "acimage_common/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>acimage</artifactId>\n        <groupId>com.acimage</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>acimage_common</artifactId>\n    <packaging>jar</packaging>\n\n    <name>acimage_common</name>\n    <url>http://maven.apache.org</url>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n\n\n        <!--mybatis-plus-->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n        </dependency>\n\n        <!--Hutool-->\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-all</artifactId>\n        </dependency>\n\n        <!--redis依赖及其连接池依赖-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-pool2</artifactId>\n        </dependency>\n\n        <!--rabbitmq-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!--nacos-->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.cloud</groupId>\n                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!--spring cloud集成sentinel  有内存泄漏的bug-->\n<!--        <dependency>-->\n<!--            <groupId>com.alibaba.cloud</groupId>-->\n<!--            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>-->\n<!--        </dependency>-->\n\n        <!--JWT-->\n        <dependency>\n            <groupId>com.auth0</groupId>\n            <artifactId>java-jwt</artifactId>\n        </dependency>\n\n        <!--七牛云存储-->\n        <dependency>\n            <groupId>com.qiniu</groupId>\n            <artifactId>qiniu-java-sdk</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n            <version>3.14.2</version>\n            <scope>compile</scope>\n            <optional>true</optional>\n        </dependency>\n\n        <!--minio-->\n        <dependency>\n            <groupId>io.minio</groupId>\n            <artifactId>minio</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!--敏感词过滤-->\n<!--        <dependency>-->\n<!--            <groupId>com.github.houbb</groupId>-->\n<!--            <artifactId>sensitive-word</artifactId>-->\n<!--            <optional>true</optional>-->\n<!--        </dependency>-->\n\n        <!--敏感词过滤2-->\n        <dependency>\n            <groupId>io.github.toolgood</groupId>\n            <artifactId>toolgood-words</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <!--es-->\n        <dependency>\n            <groupId>org.springframework.data</groupId>\n            <artifactId>spring-data-elasticsearch</artifactId>\n            <optional>true</optional>\n        </dependency>\n\n        <!--validation依赖，用于数据校验-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>net.coobird</groupId>\n            <artifactId>thumbnailator</artifactId>\n        </dependency>\n\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>3.8.1</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <includeSystemScope>true</includeSystemScope>\n                    <classifier>exec</classifier>\n                    <skip>true</skip><!--不需要main类打包-->\n                </configuration>\n            </plugin>\n\n            <!-- maven打包 跳过测试包 -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <skipTests>true</skipTests><!--跳过测试包-->\n                </configuration>\n            </plugin>\n\n<!--            <plugin>-->\n<!--                <groupId>org.apache.maven.plugins</groupId>-->\n<!--                <artifactId>maven-compiler-plugin</artifactId>-->\n<!--                <configuration>-->\n<!--                    <compilerArguments>-->\n<!--                        <extdirs>${pom.basedir}/lib</extdirs>-->\n<!--                    </compilerArguments>-->\n<!--                </configuration>-->\n<!--            </plugin>-->\n\n<!--            <plugin>-->\n<!--                <groupId>org.apache.maven.plugins</groupId>-->\n<!--                <artifactId>maven-dependency-plugin</artifactId>-->\n<!--                <version>3.1.1</version>-->\n<!--                <executions>-->\n<!--                    <execution>-->\n<!--                        <id>copy</id>-->\n<!--                        <phase>package</phase>-->\n<!--                        <goals>-->\n<!--                            <goal>copy-dependencies</goal>-->\n<!--                        </goals>-->\n<!--                        <configuration>-->\n<!--                            <outputDirectory>${pom.basedir}/lib</outputDirectory>-->\n<!--                        </configuration>-->\n\n<!--                    </execution>-->\n<!--                </executions>-->\n<!--            </plugin>-->\n        </plugins>\n\n<!--        <resources>-->\n<!--            <resource>-->\n<!--                <directory>libs</directory>-->\n<!--                <targetPath>/WEB-INF/lib/</targetPath>-->\n<!--                <includes>-->\n<!--                    <include>**/*.jar</include>-->\n<!--                </includes>-->\n<!--            </resource>-->\n<!--        </resources>-->\n    </build>\n\n</project>\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/CommonMain.java",
    "content": "package com.acimage.common;\n\n\npublic class CommonMain {\n\tpublic static void main(String[] args) {\n\t}\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/config/EsConfig.java",
    "content": "package com.acimage.common.config;\n\nimport org.apache.http.auth.AuthScope;\nimport org.apache.http.auth.UsernamePasswordCredentials;\nimport org.apache.http.client.CredentialsProvider;\nimport org.apache.http.impl.client.BasicCredentialsProvider;\nimport org.elasticsearch.client.RestClientBuilder;\nimport org.elasticsearch.client.RestHighLevelClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Profile;\n\nimport java.util.concurrent.TimeUnit;\n\n\n@Profile(value = {\"test\",\"dev\",\"dev2\",\"server\"})\n@Configuration\n@ConditionalOnClass(RestHighLevelClient.class)\npublic class EsConfig {\n\n    @Bean\n    public RestHighLevelClient devRestHighLevelClient(@Autowired RestClientBuilder restClientBuilder) {\n\n        return new RestHighLevelClient(restClientBuilder.setHttpClientConfigCallback(requestConfig -> {\n            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n            return requestConfig.setDefaultCredentialsProvider(credentialsProvider);\n        }));\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/config/EsProdConfig.java",
    "content": "package com.acimage.common.config;\n\nimport org.apache.http.auth.AuthScope;\nimport org.apache.http.auth.UsernamePasswordCredentials;\nimport org.apache.http.client.CredentialsProvider;\nimport org.apache.http.impl.client.BasicCredentialsProvider;\nimport org.elasticsearch.client.RestClientBuilder;\nimport org.elasticsearch.client.RestHighLevelClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Profile;\n\nimport java.util.concurrent.TimeUnit;\n\n@Profile(\"prod\")\n@Configuration\n@ConditionalOnClass(RestHighLevelClient.class)\npublic class EsProdConfig {\n    @Value(\"${spring.elasticsearch.username}\")\n    private String username;\n    @Value(\"${spring.elasticsearch.password}\")\n    private String password;\n    @Bean\n    public RestHighLevelClient prodRestHighLevelClient(@Autowired RestClientBuilder restClientBuilder) {\n\n\n        return new RestHighLevelClient(restClientBuilder.setHttpClientConfigCallback(requestConfig -> {\n            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));\n            requestConfig.setKeepAliveStrategy((response, context) -> TimeUnit.MINUTES.toMillis(3));\n            return requestConfig.setDefaultCredentialsProvider(credentialsProvider);\n        }));\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/config/FilterConfig.java",
    "content": "package com.acimage.common.config;\n\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.filter.CharacterEncodingFilter;\nimport org.springframework.web.filter.HiddenHttpMethodFilter;\n\n@Configuration\npublic class FilterConfig {\n\n    /**\n     * 设置编码\n     * @return\n     */\n    @Bean\n    public FilterRegistrationBean<CharacterEncodingFilter> registerFilter1(){\n        FilterRegistrationBean<CharacterEncodingFilter> registerFilter=new FilterRegistrationBean<>();\n        CharacterEncodingFilter encodeFilter=new CharacterEncodingFilter();\n        encodeFilter.setEncoding(\"UTF-8\");\n        registerFilter.setFilter(encodeFilter);\n        registerFilter.addUrlPatterns(\"/*\");\n        return registerFilter;\n    }\n\n    /**\n     * 设置HiddenHttpMethod，支持put、get、delete等请求\n     * @return\n     */\n    @Bean\n    public FilterRegistrationBean<HiddenHttpMethodFilter> registerFilter2(){\n        FilterRegistrationBean<HiddenHttpMethodFilter> registerFilter=new FilterRegistrationBean<>();\n        HiddenHttpMethodFilter hiddenHttpMethodFilter=new HiddenHttpMethodFilter();\n        registerFilter.setFilter(hiddenHttpMethodFilter);\n        registerFilter.addUrlPatterns(\"/*\");\n        return registerFilter;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/config/JacksonConfig.java",
    "content": "package com.acimage.common.config;\n\n\nimport cn.hutool.core.date.DatePattern;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class JacksonConfig {\n\n    /**\n     * Jackson全局转化long类型为String，解决前端接受long类型缺失精度问题\n     * @return Jackson2ObjectMapperBuilderCustomizer 注入的对象\n     */\n    @Bean\n    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {\n        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder\n                .serializerByType(Long.class, ToStringSerializer.instance)\n                .serializerByType(Long.TYPE, ToStringSerializer.instance)\n                .simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);\n    }\n}"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/config/PaginationConfig.java",
    "content": "package com.acimage.common.config;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class PaginationConfig {\n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();\n        paginationInnerInterceptor.setDbType(DbType.MYSQL);\n        //页数溢出时跳到第一页\n        paginationInnerInterceptor.setOverflow(true);\n        interceptor.addInnerInterceptor(paginationInnerInterceptor);\n\n        return interceptor;\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/config/RabbitmqConvertConfig.java",
    "content": "package com.acimage.common.config;\n\n\nimport org.springframework.amqp.core.AcknowledgeMode;\nimport org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;\nimport org.springframework.amqp.rabbit.connection.ConnectionFactory;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;\nimport org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@ConditionalOnClass(RabbitTemplate.class)\n@Configuration\npublic class RabbitmqConvertConfig {\n    //发送方序列化\n    @Bean\n    public RabbitTemplate jacksonRabbitTemplate(ConnectionFactory connectionFactory) {\n        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);\n        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());\n        return rabbitTemplate;\n    }\n\n    //消费者序列化\n    @Bean\n    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){\n        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();\n        factory.setConnectionFactory(connectionFactory);\n        factory.setMessageConverter(new Jackson2JsonMessageConverter());\n        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);\n        return factory;\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/IpInterceptorBak.java",
    "content": "package com.acimage.common.deprecated;\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.utils.IpUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 获取请求的token状态\n */\n@Slf4j\n@Component\npublic class IpInterceptorBak implements HandlerInterceptor {\n\n    @Autowired\n    StringRedisTemplate stringRedisTemplate;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n        String KEY_IP=\"acimage:ip:\";\n        String KEY_BLACK_IP=\"acimage:ip:black\";\n        long EXPIRE_SECONDS=10L;\n        long limitTimes=300L;\n\n        String ip = IpUtils.getIp(request);\n        UserContext.setIp(ip);\n        String ipKey=KEY_IP+ip;\n        String blackIpKey=KEY_BLACK_IP+ip;\n\n        Boolean isBlackIp=stringRedisTemplate.opsForSet().isMember(blackIpKey,ip);\n\n        if(Boolean.TRUE.equals(isBlackIp)){\n            return false;\n        }\n\n        if (handler instanceof HandlerMethod) {\n            Long visitNum=stringRedisTemplate.opsForValue().increment(ipKey);\n            if(visitNum==null){\n                log.error(\"ip:{} 操作：ip访问计数 错误：redis中ip计数返回值为空\",ip);\n            } else if (visitNum==1){\n                stringRedisTemplate.expire(ipKey,EXPIRE_SECONDS, TimeUnit.SECONDS);\n            } else if (visitNum>limitTimes) {\n                //加入黑名单\n                stringRedisTemplate.opsForSet().add(blackIpKey,ip);\n                response.setStatus(HttpStatus.FORBIDDEN.value());\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        UserContext.remove();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/JwtInterceptorBak.java",
    "content": "package com.acimage.common.deprecated;\n\nimport com.acimage.common.global.exception.NullTokenException;\nimport com.acimage.common.deprecated.annotation.utils.AuthenticationUtils;\nimport com.acimage.common.global.consts.HeaderKeyConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.enums.AuthenticationType;\nimport com.acimage.common.global.enums.TokenStatus;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.IpUtils;\nimport com.acimage.common.utils.JwtUtils;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.auth0.jwt.exceptions.TokenExpiredException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * 获取请求的token状态\n */\npublic class JwtInterceptorBak implements HandlerInterceptor {\n    @Autowired\n    TokenService tokenService;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n        //设置ip\n        String ip = IpUtils.getIp(request);\n        UserContext.setIp(ip);\n        \n        if (handler instanceof HandlerMethod) {\n            HandlerMethod handlerMethod=(HandlerMethod) handler;\n            //获取该方法权限类型，优先注解顺序：方法、类\n            AuthenticationType authenticationType = AuthenticationUtils.getAuthenticationType(handlerMethod);\n            //无登录权限要求则放行\n            if (authenticationType == null || authenticationType == AuthenticationType.NONE) {\n                UserContext.setTokenStatus(TokenStatus.PASS_TOKEN_VERIFY);\n                return true;\n            }\n        }\n\n        //获取token\n        String token = request.getHeader(HeaderKeyConstants.AUTHORIZATION);\n\n        //验证token，验证不通过抛出异常\n        try {\n            JwtUtils.verifyToken(token);\n        } catch (NullTokenException e1) {\n            UserContext.setTokenStatus(TokenStatus.NULL);\n            return true;\n        } catch (TokenExpiredException e2) {\n            UserContext.setTokenStatus(TokenStatus.EXPIRE);\n            return true;\n        } catch (JWTVerificationException e3) {\n            UserContext.setTokenStatus(TokenStatus.INVALID);\n            return true;\n        }\n\n        if (!tokenService.hasRecorded(token)) {\n            UserContext.setTokenStatus(TokenStatus.MISMATCH_IP);\n        } else {\n            UserContext.setTokenStatus(TokenStatus.VALID);\n        }\n\n        UserContext.saveCurrentUserInfo(JwtUtils.getUserId(token), JwtUtils.getUsername(token), JwtUtils.getPhotoUrl(token));\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        UserContext.remove();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/PermissionInterceptorBak.java",
    "content": "package com.acimage.common.deprecated;\n\nimport com.acimage.common.deprecated.annotation.utils.AuthenticationUtils;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.enums.AuthenticationType;\nimport com.acimage.common.global.enums.TokenStatus;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.ParameterizableViewController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * 验证token状态和请求所需权限是否匹配\n */\n\n@Slf4j\npublic class PermissionInterceptorBak implements HandlerInterceptor {\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n        String ip = UserContext.getIp();\n\n        log.info(\"access 用户:{} token状态:{} 访问:{} {} ip:{}\",\n                UserContext.getUsername(), UserContext.getTokenStatus(), request.getRequestURI(), request.getMethod(), ip);\n\n        TokenStatus tokenStatus = UserContext.getTokenStatus();\n        if (tokenStatus == TokenStatus.PASS_TOKEN_VERIFY) {\n            return true;\n        }\n        //如果通过viewController访问页面\n        if (handler instanceof ParameterizableViewController) {\n            if (!(tokenStatus == TokenStatus.VALID||tokenStatus == TokenStatus.MISMATCH_IP)) {\n\n                response.sendRedirect(\"/\");\n                return false;\n            }\n        }\n\n        if (handler instanceof HandlerMethod) {\n            HandlerMethod handlerMethod = (HandlerMethod) handler;\n            //获取该方法权限类型，优先注解顺序：方法、类\n            AuthenticationType authenticationType = AuthenticationUtils.getAuthenticationType(handlerMethod);\n            //无登录权限要求则放行\n            if (authenticationType == null || authenticationType == AuthenticationType.NONE) {\n                return true;\n            } else if (authenticationType == AuthenticationType.TOKEN_REQUIRED) {\n                if (!(tokenStatus == TokenStatus.VALID||tokenStatus == TokenStatus.MISMATCH_IP)) {\n                    response.setStatus(HttpStatus.UNAUTHORIZED.value());\n                    return false;\n                }\n            }\n        }\n//        ParameterizableViewController viewHandler=(ParameterizableViewController) handler;\n//        viewHandler.getViewName();\n//        String token= CookieUtils.getValueByName(request.getCookies(), JwtUtils.KEY_TOKEN);\n\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView\n            modelAndView) throws Exception {\n        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception\n            ex) throws Exception {\n        //移除用户信息，防止之后用该线程的信息误判（因为线程池）\n        UserContext.remove();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/QiniuUtils.java",
    "content": "package com.acimage.common.deprecated;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.http.HttpUtil;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.qiniu.cdn.CdnManager;\nimport com.qiniu.cdn.CdnResult;\nimport com.qiniu.common.QiniuException;\nimport com.qiniu.http.Client;\nimport com.qiniu.http.Response;\nimport com.qiniu.storage.BucketManager;\nimport com.qiniu.storage.Configuration;\nimport com.qiniu.storage.Region;\nimport com.qiniu.storage.UploadManager;\nimport com.qiniu.storage.model.BatchStatus;\nimport com.qiniu.storage.model.DefaultPutRet;\nimport com.qiniu.util.Auth;\nimport com.qiniu.util.StringMap;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.PostConstruct;\nimport javax.validation.constraints.NotNull;\nimport java.io.*;\nimport java.net.URLEncoder;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\n@Deprecated\n@Slf4j\n@ConditionalOnClass(Auth.class)\n@ConfigurationProperties(prefix = \"qiniu\")\npublic class QiniuUtils {\n\n    private String accessKey;\n    private String secretKey;\n    private String bucket;\n    private String domain;\n\n    private String uploadToken;\n    UploadManager uploadManager;\n    Auth auth;\n    Configuration cfg;\n    private static final ObjectMapper mapper = new ObjectMapper();\n\n    @PostConstruct\n    private void init() {\n        cfg = new Configuration(Region.huanan());\n        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本\n        uploadManager = new UploadManager(cfg);\n        auth = Auth.create(accessKey, secretKey);\n        uploadToken = auth.uploadToken(bucket);\n//        System.out.println(accessKey);\n//        System.out.println(secretKey);\n//        System.out.println(bucket);\n//        System.out.println(domain);\n    }\n\n    public void setAccessKey(String accessKey) {\n        this.accessKey = accessKey;\n    }\n\n    public void setSecretKey(String secretKey) {\n        this.secretKey = secretKey;\n    }\n\n    public void setBucket(String bucket) {\n        this.bucket = bucket;\n    }\n\n    public void setDomain(String domain) {\n        this.domain = domain;\n    }\n\n    /**\n     * @param multipartFile 上传的图片\n     */\n    public void upload(@NotNull MultipartFile multipartFile, String urlWithoutDomain) {\n        InputStream is;\n        try {\n            is = multipartFile.getInputStream();\n        } catch (IOException e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"error: multipartFile.getInputStream()异常:{}\", e.getMessage());\n            throw new RuntimeException(e);\n        }\n\n        putAndLog(is, urlWithoutDomain, uploadToken);\n    }\n\n    public void upload(@NotNull File file, String urlWithoutDomain) {\n        putAndLog(file, urlWithoutDomain, uploadToken);\n    }\n\n    public void cover(@NotNull MultipartFile multipartFile, String urlWithoutDomain) {\n        String coverToken = auth.uploadToken(bucket, urlWithoutDomain);\n        InputStream is;\n        try {\n            is = multipartFile.getInputStream();\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        putAndLog(is, urlWithoutDomain, coverToken);\n        new Thread(() -> {\n            refreshFile(urlWithoutDomain);\n            refreshQuery(urlWithoutDomain);\n        }).start();\n\n    }\n\n\n    public void refreshFile(String... urls) {\n        int urlAmountLimit = 100;\n        CdnManager c = new CdnManager(auth);\n        if (urls.length > urlAmountLimit) {\n            log.error(\"调用刷新的链接超过100个\");\n            return;\n        }\n\n        //得到真实链接\n        for (int i = 0; i < urls.length; i++) {\n            urls[i] = getTrueUrl(urls[i]);\n        }\n\n        try {\n            //单次方法调用刷新的链接不可以超过100个\n            CdnResult.RefreshResult result = c.refreshUrls(urls);\n            //获取其他的回复内容\n        } catch (QiniuException e) {\n            log.error(\"刷新七牛云url失败 urls:{}\", Arrays.asList(urls));\n        }\n    }\n\n\n    public void refreshQuery(String url) {\n        String trueUrl = getTrueUrl(url);\n        StringMap str = auth.authorization(trueUrl);\n        Client client = new Client();\n        try {\n            client.post(trueUrl, \"\", str);\n        } catch (QiniuException e) {\n            log.error(\"刷新查询oss url失败 url:{}\", url);\n            ExceptionUtils.printIfDev(e);\n        }\n    }\n\n    public void deleteFile(String url) {\n        BucketManager bucketManager = new BucketManager(auth, cfg);\n        try {\n            bucketManager.delete(bucket, url);\n        } catch (QiniuException ex) {\n            //如果遇到异常，说明删除失败\n            log.error(\"云文件删除失败 url：{} 返回代码：{} 返回错误信息：{}\", url, ex.code(), ex.response.toString());\n        }\n    }\n\n    public void batchDelete(List<String> urlList) {\n        if (CollectionUtil.isEmpty(urlList)) {\n            return;\n        }\n        BucketManager bucketManager = new BucketManager(auth, cfg);\n        try {\n            //单次批量请求的文件数量不得超过1000\n\n            String[] urls = urlList.toArray(new String[0]);\n\n            BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();\n            batchOperations.addDeleteOp(bucket, urls);\n            Response response = bucketManager.batch(batchOperations);\n            BatchStatus[] batchStatusList = response.jsonToObject(BatchStatus[].class);\n            for (int i = 0; i < urls.length; i++) {\n                BatchStatus status = batchStatusList[i];\n                String key = urls[i];\n                System.out.print(key + \"\\t\");\n                if (status.code == 200) {\n                    System.out.println(\"delete success\");\n                } else {\n                    System.out.println(status.data.error);\n                }\n            }\n        } catch (QiniuException ex) {\n            System.err.println(ex.response.toString());\n        }\n    }\n\n    public String generateUrl(String suffix, Date uploadTime, @Nullable String prefix) {\n        String formatPattern = \"yyyy/MM/dd\";\n        String newPrefix = prefix == null ? \"\" : prefix + \"/\";\n        SimpleDateFormat formatter = new SimpleDateFormat(formatPattern);\n        return newPrefix + formatter.format(uploadTime) + \"/\" + suffix;\n    }\n\n    public String getTrueUrl(String urlWithoutDomain) {\n        return domain + \"/\" + urlWithoutDomain;\n    }\n\n    public void download(String url, String toPath) {\n        String encodedUrl = null;\n        try {\n            encodedUrl = URLEncoder.encode(url, \"utf-8\").replace(\"+\", \"%20\");\n        } catch (UnsupportedEncodingException e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"url编码失败 error：{}\", e.getLocalizedMessage());\n        }\n        String publicUrl = domain + \"/\" + encodedUrl;\n        HttpUtil.downloadFile(publicUrl, new File(toPath));//下载\n    }\n\n    private void putAndLog(Object inputStreamOrFile, String urlWithoutDomain, String token) {\n        Response response = null;\n        try {\n            if (inputStreamOrFile instanceof InputStream ) {\n                InputStream is=(InputStream) inputStreamOrFile;\n                response = uploadManager.put(is, urlWithoutDomain, token, null, null);\n            } else if (inputStreamOrFile instanceof File) {\n                File file=(File) inputStreamOrFile;\n                response = uploadManager.put(file, urlWithoutDomain, token);\n            } else {\n                throw new IllegalArgumentException(String.format(\"参数inputStreamOrFile类型错误:%s\", inputStreamOrFile.getClass()));\n            }\n\n            if (response == null) {\n                return;\n            }\n\n            //解析上传成功的结果\n            DefaultPutRet putRet = mapper.readValue(response.bodyString(), DefaultPutRet.class);\n            log.info(\"文件上传七牛云成功 返回结果key：{} hash：{}\", putRet.key, putRet.hash);\n        } catch (QiniuException ex) {\n            Response r = ex.response;\n            System.err.println(r.toString());\n            try {\n                String responseMsg = r.bodyString();\n                log.error(responseMsg);\n                throw new BusinessException(\"服务器上传文件失败\");\n            } catch (QiniuException ex2) {\n                log.error(\"文件上传后返回信息读取失败 url:{}\", urlWithoutDomain);\n                throw new BusinessException(\"上传文件出错了\");\n                //ignore\n            }\n        } catch (JsonProcessingException e) {\n            log.error(\"json解析七牛云返回结果异常\");\n            throw new BusinessException(\"上传文件出错了，服务器解析结果异常\");\n        }\n\n    }\n}\n\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/QiniuUtilsBak.java",
    "content": "package com.acimage.common.deprecated;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.http.HttpUtil;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.qiniu.cdn.CdnManager;\nimport com.qiniu.cdn.CdnResult;\nimport com.qiniu.common.QiniuException;\nimport com.qiniu.http.Client;\nimport com.qiniu.http.Response;\nimport com.qiniu.storage.BucketManager;\nimport com.qiniu.storage.Configuration;\nimport com.qiniu.storage.Region;\nimport com.qiniu.storage.UploadManager;\nimport com.qiniu.storage.model.BatchStatus;\nimport com.qiniu.storage.model.DefaultPutRet;\nimport com.qiniu.util.Auth;\nimport com.qiniu.util.StringMap;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.PostConstruct;\nimport javax.validation.constraints.NotNull;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\n@Slf4j\n@Component\n@ConditionalOnClass(Auth.class)\n@ConfigurationProperties(prefix = \"qiniu\")\n@Deprecated\npublic class QiniuUtilsBak {\n\n    private String accessKey;\n    private String secretKey;\n    private String bucket;\n    private String domain;\n\n    private String uploadToken;\n    UploadManager uploadManager;\n    Auth auth;\n    Configuration cfg;\n    private static final ObjectMapper mapper = new ObjectMapper();\n\n    @PostConstruct\n    private void init() {\n        cfg = new Configuration(Region.huanan());\n        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本\n        uploadManager = new UploadManager(cfg);\n        auth = Auth.create(accessKey, secretKey);\n        uploadToken = auth.uploadToken(bucket);\n    }\n\n    public void setAccessKey(String accessKey) {\n        this.accessKey = accessKey;\n    }\n\n    public void setSecretKey(String secretKey) {\n        this.secretKey = secretKey;\n    }\n\n    public void setBucket(String bucket) {\n        this.bucket = bucket;\n    }\n\n    public void setDomain(String domain) {\n        this.domain = domain;\n    }\n\n    /**\n     * @param multipartFile 上传的图片\n     */\n    public void upload(@NotNull MultipartFile multipartFile, String urlWithoutDomain) {\n        InputStream is;\n        try {\n            is = multipartFile.getInputStream();\n        } catch (IOException e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"error: multipartFile.getInputStream()异常:{}\", e.getMessage());\n            throw new RuntimeException(e);\n        }\n\n        putAndLog(is, urlWithoutDomain, uploadToken);\n    }\n\n    public void upload(@NotNull File file, String urlWithoutDomain) {\n        putAndLog(file, urlWithoutDomain, uploadToken);\n    }\n\n    public void cover(@NotNull MultipartFile multipartFile, String urlWithoutDomain) {\n        String coverToken = auth.uploadToken(bucket, urlWithoutDomain);\n        InputStream is;\n        try {\n            is = multipartFile.getInputStream();\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        putAndLog(is, urlWithoutDomain, coverToken);\n        new Thread(() -> {\n            refreshFile(urlWithoutDomain);\n            refreshQuery(urlWithoutDomain);\n        }).start();\n\n    }\n\n\n    public void refreshFile(String... urls) {\n        int urlAmountLimit = 100;\n        CdnManager c = new CdnManager(auth);\n        if (urls.length > urlAmountLimit) {\n            log.error(\"调用刷新的链接超过100个\");\n            return;\n        }\n\n        //得到真实链接\n        for (int i = 0; i < urls.length; i++) {\n            urls[i] = getTrueUrl(urls[i]);\n        }\n\n        try {\n            //单次方法调用刷新的链接不可以超过100个\n            CdnResult.RefreshResult result = c.refreshUrls(urls);\n            //获取其他的回复内容\n        } catch (QiniuException e) {\n            log.error(\"刷新七牛云url失败 urls:{}\", Arrays.asList(urls));\n        }\n    }\n\n\n    public void refreshQuery(String url) {\n        String trueUrl = getTrueUrl(url);\n        StringMap str = auth.authorization(trueUrl);\n        Client client = new Client();\n        try {\n            client.post(trueUrl, \"\", str);\n        } catch (QiniuException e) {\n            log.error(\"刷新查询oss url失败 url:{} error:{}\", url,e.getMessage());\n            ExceptionUtils.printIfDev(e);\n        }\n    }\n\n    public void deleteFile(String url) {\n        BucketManager bucketManager = new BucketManager(auth, cfg);\n        try {\n            bucketManager.delete(bucket, url);\n        } catch (QiniuException ex) {\n            //如果遇到异常，说明删除失败\n            log.error(\"云文件删除失败 url：{} 返回代码：{} 返回错误信息：{}\", url, ex.code(), ex.response.toString());\n        }\n    }\n\n    public void batchDelete(List<String> urlList) {\n        if (CollectionUtil.isEmpty(urlList)) {\n            return;\n        }\n        BucketManager bucketManager = new BucketManager(auth, cfg);\n        try {\n            //单次批量请求的文件数量不得超过1000\n\n            String[] urls = urlList.toArray(new String[0]);\n\n            BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();\n            batchOperations.addDeleteOp(bucket, urls);\n            Response response = bucketManager.batch(batchOperations);\n            BatchStatus[] batchStatusList = response.jsonToObject(BatchStatus[].class);\n            for (int i = 0; i < urls.length; i++) {\n                BatchStatus status = batchStatusList[i];\n                String key = urls[i];\n                System.out.print(key + \"\\t\");\n                if (status.code == 200) {\n                    System.out.println(\"delete success\");\n                } else {\n                    System.out.println(status.data.error);\n                }\n            }\n        } catch (QiniuException ex) {\n            System.err.println(ex.response.toString());\n        }\n    }\n\n    public String generateUrl(String suffix, Date uploadTime, @Nullable String prefix) {\n        String formatPattern = \"yyyy/MM/dd\";\n        String newPrefix = prefix == null ? \"\" : prefix + \"/\";\n        SimpleDateFormat formatter = new SimpleDateFormat(formatPattern);\n        return newPrefix + formatter.format(uploadTime) + \"/\" + suffix;\n    }\n\n    public String getTrueUrl(String urlWithoutDomain) {\n        return domain + \"/\" + urlWithoutDomain;\n    }\n\n    public void download(String url, String toPath) {\n        String encodedUrl = null;\n        try {\n            encodedUrl = URLEncoder.encode(url, \"utf-8\").replace(\"+\", \"%20\");\n        } catch (UnsupportedEncodingException e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"url编码失败 error：{}\", e.getMessage());\n        }\n        String publicUrl = domain + \"/\" + encodedUrl;\n        long expireInSeconds = 3600;//1小时，可以自定义链接过期时间\n\n        HttpUtil.downloadFile(publicUrl, new File(toPath));//下载\n    }\n\n    private void putAndLog(Object inputStreamOrFile, String urlWithoutDomain, String token) {\n        Response response = null;\n        try {\n            if (inputStreamOrFile instanceof InputStream) {\n                InputStream is = (InputStream) inputStreamOrFile;\n                response = uploadManager.put(is, urlWithoutDomain, token, null, null);\n            } else if (inputStreamOrFile instanceof File) {\n                File file = (File) inputStreamOrFile;\n                response = uploadManager.put(file, urlWithoutDomain, token);\n            } else {\n                throw new IllegalArgumentException(String.format(\"参数inputStreamOrFile类型错误:%s\", inputStreamOrFile.getClass()));\n            }\n\n            if (response == null) {\n                return;\n            }\n\n            //解析上传成功的结果\n            DefaultPutRet putRet = mapper.readValue(response.bodyString(), DefaultPutRet.class);\n            log.info(\"文件上传七牛云成功 返回结果key：{} hash：{}\", putRet.key, putRet.hash);\n        } catch (QiniuException ex) {\n            Response r = ex.response;\n            System.err.println(r.toString());\n            try {\n                String responseMsg = r.bodyString();\n                log.error(responseMsg);\n                throw new BusinessException(\"服务器上传文件失败\");\n            } catch (QiniuException ex2) {\n                log.error(\"文件上传后返回信息读取失败 url:{}\", urlWithoutDomain);\n                throw new BusinessException(\"上传文件出错了\");\n                //ignore\n            }\n        } catch (JsonProcessingException e) {\n            log.error(\"json解析七牛云返回结果异常\");\n            throw new BusinessException(\"上传文件出错了，服务器解析结果异常\");\n        }\n\n    }\n}\n\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/UserCommunityStatistic.java",
    "content": "package com.acimage.common.deprecated;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Deprecated\npublic class UserCommunityStatistic {\n    @TableId\n    Long userId;\n    Integer topicCount;\n    Integer starCount;\n    @TableField(exist = false)\n    CmtyUser cmtyUser;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/annotation/Authentication.java",
    "content": "package com.acimage.common.deprecated.annotation;\n\nimport com.acimage.common.global.enums.AuthenticationType;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 注解上的类或方法表示不需要登录后才能访问\n */\n@Target({ElementType.METHOD,ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Authentication {\n    AuthenticationType type() default AuthenticationType.TOKEN_REQUIRED;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/annotation/utils/AuthenticationUtils.java",
    "content": "package com.acimage.common.deprecated.annotation.utils;\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.enums.AuthenticationType;\nimport org.springframework.web.method.HandlerMethod;\n\n\npublic class AuthenticationUtils {\n    public static AuthenticationType getAuthenticationType(HandlerMethod handlerMethod){\n        if(handlerMethod==null){\n            return null;\n        }\n        //判断方法上是否有Authorization注解\n        Authentication methodAuthentication =handlerMethod.getMethod().getAnnotation(Authentication.class);\n        if(methodAuthentication !=null){\n            return methodAuthentication.type();\n        }\n        //获取类上的Authorization注解\n        Authentication classAuthentication =handlerMethod.getBeanType().getAnnotation(Authentication.class);\n        if(classAuthentication !=null){\n            return classAuthentication.type();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/deprecated/typehandler/MatchRuleTypeHandler.java",
    "content": "package com.acimage.common.deprecated.typehandler;\n\n\nimport com.acimage.common.global.enums.MatchRule;\nimport org.apache.ibatis.type.BaseTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n\n//@MappedTypes(MatchRule.class)\npublic class MatchRuleTypeHandler extends BaseTypeHandler<MatchRule> {\n\n    @Override\n    public void setNonNullParameter(PreparedStatement preparedStatement, int i, MatchRule matchRule, JdbcType jdbcType) throws SQLException {\n        preparedStatement.setInt(i, matchRule.getKey());\n    }\n\n    @Override\n    public MatchRule getNullableResult(ResultSet resultSet, String column) throws SQLException {\n        return MatchRule.from(resultSet.getInt(column));\n    }\n\n    @Override\n    public MatchRule getNullableResult(ResultSet resultSet, int i) throws SQLException {\n       return MatchRule.from(Integer.parseInt(resultSet.getString(i)));\n    }\n\n    @Override\n    public MatchRule getNullableResult(CallableStatement callableStatement, int i) throws SQLException {\n        return MatchRule.from(Integer.parseInt(callableStatement.getString(i)));\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/aop/LogAdvice.java",
    "content": "package com.acimage.common.global.aop;\n\n\nimport com.acimage.common.utils.common.AopUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.GetMapping;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\n\n@Aspect\n@Component\n@Slf4j\n@Order(200)\npublic class LogAdvice {\n\n    private static final int MAX_RETURN_VALUE_LENGTH = 100;\n\n    //    @Pointcut(\"execution(public * com.acimage..*.controller.*Controller.*(..))\")\n    @Pointcut(\"execution(public * com.acimage..*.*Controller.*(..))\")\n    public void controllerPointCut() {\n    }\n\n    @Pointcut(\"execution(public * com.acimage..*.*Provider.*(..))\")\n    public void providerPointCut() {\n    }\n\n    /**\n     * 记录每个接口出异常时时的入参\n     */\n    @Around(\"controllerPointCut() || providerPointCut()\")\n    private Object logParametersAndReturnValue(ProceedingJoinPoint joinPoint) throws Throwable {\n\n        Method method = AopUtils.methodOf(joinPoint);\n        Object[] args = joinPoint.getArgs();\n        Parameter[] parameters = method.getParameters();\n\n        long startTime = System.currentTimeMillis();\n\n        //记录入参\n        StringBuilder argsString = new StringBuilder();\n        argsString.append(method.getName());\n        argsString.append(\" 入参-->\");\n        if (args != null) {\n            for (int i = 0; i < args.length; i++) {\n                if (i < parameters.length) {\n                    argsString.append(parameters[i].getName());\n                    argsString.append(\": \");\n                }\n\n                argsString.append(args[i]);\n                argsString.append(\" \");\n            }\n        }\n\n        Object obj;\n        try {\n            obj = joinPoint.proceed();\n        } catch (Throwable e) {\n            log.error(\"出异常: \" + argsString);\n            throw e;\n        }\n\n        if (!method.isAnnotationPresent(GetMapping.class)) {\n            String returnValue = obj == null ? \"\" : obj.toString();\n            log.info(method.getName() + \" 返回值-->\" + returnValue);\n        }\n\n        String classMethod = method.getName() + \" \" + method.getDeclaringClass().getSimpleName();\n        long cost = System.currentTimeMillis() - startTime;\n        if (cost > 500) {\n            log.warn(\"{}耗时 {}ms\", classMethod, cost);\n        } else {\n            log.debug(\"{}耗时 {}ms\", classMethod, cost);\n        }\n\n        return obj;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/EsConstants.java",
    "content": "package com.acimage.common.global.consts;\n\npublic class EsConstants {\n    public static final String IK_SMART=\"ik_smart\";\n    public static final String IK_MAX_WORD=\"ik_max_word\";\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/FileFormatConstants.java",
    "content": "package com.acimage.common.global.consts;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class FileFormatConstants {\n    public static List<String> ALLOWED_IMAGE_FORMAT = Arrays.asList(\"jpeg\", \"jpg\", \"png\");\n    public static List<String> ALLOWED_CAROUSEL_FORMAT = Arrays.asList(\"jpeg\", \"jpg\", \"png\",\"webp\");\n    public static List<String> ALLOWED_COVER_IMAGE_FORMAT = Arrays.asList(\"jpeg\", \"jpg\", \"png\",\"webp\");\n    public static final String WEBP=\"webp\";\n    public static final String WEBP_CONTENT_TYPE=\"image/webp\";\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/HeaderKeyConstants.java",
    "content": "package com.acimage.common.global.consts;\n\npublic class HeaderKeyConstants {\n    public static final String AUTHORIZATION=\"authorization\";\n    public static final String FEIGN_X_USER_IP =\"x-origin-user-ip\";\n\n    public static final String SEC_WEBSOCKET_PROTOCOL=\"Sec-WebSocket-Protocol\";\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/JwtConstants.java",
    "content": "package com.acimage.common.global.consts;\n\npublic class JwtConstants {\n\n    public static final int USER_EXPIRE_DAYS = 2;\n    public static final int ADMIN_EXPIRE_DAYS = 1;\n\n    public static final String KEY_USERNAME=\"username\";\n    public static final String KEY_USER_ID=\"userId\";\n    public static final String KEY_PHOTO_URL=\"photoUrl\";\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/MqConstants.java",
    "content": "package com.acimage.common.global.consts;\n\npublic class MqConstants {\n    public static final String HASH_IMAGE_QUEUE = \"hash-image-queue\";\n    public static final String HASH_IMAGE_ROUTE = \"hash-image-route\";\n\n\n    public static final String REMOVE_TOPIC_IMAGES_QUEUE = \"remove-topic-images-queue\";\n    public static final String REMOVE_TOPIC_IMAGE_ROUTE = \"remove-topic-images-route\";\n\n    public static final String SYNC_IMAGES_QUEUE=\"sync-images-queue\";\n    public static final String SYNC_IMAGES_ROUTE=\"sync-images-route\";\n\n    public static final String SYNC_ES_QUEUE = \"sync-es-queue\";\n    public static final String SYNC_ES_ROUTE = \"sync-es-route\";\n\n    public static final String SYNC_USER_QUEUE = \"sync-user-queue\";\n    public static final String SYNC_USER_ROUTE = \"sync-user-route\";\n\n    public static final String USER_MSG_QUEUE=\"user-msg-queue\";\n    public static final String USER_MSG_ROUTE=\"user-msg-route\";\n\n\n    public static final String TOPIC_IMAGES_EXCHANGE = \"topic-images-exchange\";\n    public static final String SYNC_ES_EXCHANGE = \"sync-es-exchange\";\n    public static final String COMMUNITY_USER_EXCHANGE=\"community-user-exchange\";\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/StorePrefixConstants.java",
    "content": "package com.acimage.common.global.consts;\n\npublic class StorePrefixConstants {\n    public final static String TOPIC_IMAGE=\"topicImage\";\n    public final static String USER_PHOTO=\"userPhoto\";\n    public final static String COVER_IMAGE=\"coverImage\";\n    public final static String HOME_CAROUSEL=\"homeCarousel\";\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/SysKeyConstants.java",
    "content": "package com.acimage.common.global.consts;\n\npublic class SysKeyConstants {\n\n    /**\n     * 记录网站浏览量\n     */\n    public static final String LOGK_PAGE_VIEW=\"acimage:admin:websiteData:pageView\";\n    /**\n     * 记录接口访问次数\n     */\n    public static final String STRINGK_INTERFACE_TOTAL=\"acimage:admin:websiteData:interface:total\";\n\n    public static final String STRINGK_INTERFACE_SUCCESS=\"acimage:admin:websiteData:interface:success\";\n\n    public static final String STRINGK_INTERFACE_FAILURE=\"acimage:admin:websiteData:interface:failure\";\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/consts/TimeConstants.java",
    "content": "package com.acimage.common.global.consts;\n\npublic class TimeConstants {\n\n    public static final long DAY_SECONDS=24*60*60;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/context/UserContext.java",
    "content": "package com.acimage.common.global.context;\n\nimport com.acimage.common.global.enums.TokenStatus;\n\n\npublic class UserContext {\n    private static final ThreadLocal<Long> userId = new ThreadLocal<>();\n    private static final ThreadLocal<String> username = new ThreadLocal<>();\n    private static final ThreadLocal<String> photoUrl = new ThreadLocal<>();\n    private static final ThreadLocal<TokenStatus> tokenStatus = new ThreadLocal<>();\n    private static final ThreadLocal<String> ip = new ThreadLocal<>();\n\n    public static void saveCurrentUserInfo(Long userId, String username, String photoUrl) {\n        UserContext.userId.set(userId);\n        UserContext.username.set(username);\n        UserContext.photoUrl.set(photoUrl);\n    }\n\n    /**\n     * 移除登录用户信息，在拦截器方法afterCompletion中，应移除当前用户对象\n     */\n    public static void remove() {\n        userId.remove();\n        username.remove();\n        photoUrl.remove();\n        tokenStatus.remove();\n        ip.remove();\n    }\n\n    public static void setTokenStatus(TokenStatus tokenStatus) {\n        UserContext.tokenStatus.set(tokenStatus);\n    }\n    public static void setUserId(Long userId){\n        UserContext.userId.set(userId);\n    }\n    public static void setUsername(String username){\n        UserContext.username.set(username);\n    }\n\n    public static void setIp(String ip) {\n        UserContext.ip.set(ip);\n    }\n\n    public static Long getUserId() {\n        return userId.get();\n    }\n\n    public static String getUsername() {\n        return username.get();\n    }\n\n    public static String getPhotoUrl() {\n        return photoUrl.get();\n    }\n\n    public static TokenStatus getTokenStatus() {\n        return tokenStatus.get();\n    }\n\n    public static String getIp() {\n        return ip.get();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/enums/AuthenticationType.java",
    "content": "package com.acimage.common.global.enums;\n\npublic enum AuthenticationType {\n    NONE(0),\n    TOKEN_REQUIRED(1),\n    ADMIN(2);\n\n    private final int key;\n    AuthenticationType(int key) {\n        this.key = key;\n    }\n\n    public int getKey() {\n        return this.key;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/enums/MatchRule.java",
    "content": "package com.acimage.common.global.enums;\n\n/**\n * interface表中匹配规则\n */\npublic enum MatchRule {\n    /**\n     * 精确匹配\n     */\n    EXACT(1),\n    /**\n     * 匹配前缀\n     */\n    PREFIX(2);\n\n    private final int key;\n\n    MatchRule(int key) {\n        this.key = key;\n    }\n\n    public int getKey() {\n        return this.key;\n    }\n\n    public static MatchRule from(int key){\n        for(MatchRule matchRule:MatchRule.values()){\n            if(matchRule.getKey()==key){\n                return matchRule;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/enums/MyHttpMethod.java",
    "content": "package com.acimage.common.global.enums;\n\nimport org.springframework.http.HttpMethod;\n\npublic enum MyHttpMethod {\n    GET,\n    POST,\n    PUT,\n    DELETE,\n    ALL;\n\n    public static MyHttpMethod from(HttpMethod method) {\n        try {\n            return MyHttpMethod.valueOf(method.toString());\n        } catch (IllegalArgumentException e) {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/enums/ServiceType.java",
    "content": "package com.acimage.common.global.enums;\n\npublic enum ServiceType {\n    ADD,\n    DELETE,\n    UPDATE\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/enums/TokenStatus.java",
    "content": "package com.acimage.common.global.enums;\n\npublic enum TokenStatus {\n    PASS_TOKEN_VERIFY(0),\n    NULL(1),\n    EXPIRE(2),\n    INVALID(3),\n    MISMATCH_IP(4),\n    VALID(5);\n    private final int key;\n\n    TokenStatus(int key) {\n        this.key = key;\n    }\n\n    public int getKey() {\n        return this.key;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/exception/BaseException.java",
    "content": "package com.acimage.common.global.exception;\n\nimport com.acimage.common.result.Result;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\n\n\n@Getter\n@Setter\n@NoArgsConstructor\n@AllArgsConstructor\npublic abstract class BaseException extends RuntimeException{\n    Integer code;\n    String msg;\n\n    public Result asResult(){\n        return new Result(code,null,msg);\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/exception/BusinessException.java",
    "content": "package com.acimage.common.global.exception;\n\nimport com.acimage.common.result.Code;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Getter\n@Setter\n@AllArgsConstructor\npublic class BusinessException extends BaseException{\n\n    public BusinessException(String msg){\n        this.msg=msg;\n        this.code= Code.ERR;\n    }\n\n    public BusinessException(Integer code, String msg) {\n        super(code, msg);\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/exception/NullTokenException.java",
    "content": "package com.acimage.common.global.exception;\n\nimport com.auth0.jwt.exceptions.JWTVerificationException;\n\npublic class NullTokenException extends JWTVerificationException {\n\n    public NullTokenException(String message){super(message);}\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/global/exception/SystemException.java",
    "content": "package com.acimage.common.global.exception;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Getter\n@Setter\n@AllArgsConstructor\npublic class SystemException extends BaseException{\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/Index/TopicIndex.java",
    "content": "package com.acimage.common.model.Index;\n\n\nimport cn.hutool.core.date.DatePattern;\nimport com.acimage.common.global.consts.EsConstants;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.domain.community.Tag;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.utils.common.BeanUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.*;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Data\n@Document(indexName = \"topic\")\n@Setting(replicas = 0)\npublic class TopicIndex {\n\n    @Id\n    private Long id;\n\n    @Field(type = FieldType.Text)\n    private String all;\n\n    @Field(type = FieldType.Keyword, store = true)\n    private Long userId;\n    @Field(type = FieldType.Text, analyzer = EsConstants.IK_MAX_WORD, store = true)\n    private String username;\n    @Field(type = FieldType.Keyword, index = false, store = true)\n    private String photoUrl;\n\n    @Field(type = FieldType.Text, analyzer = EsConstants.IK_MAX_WORD, store = true, copyTo = \"all\")\n    private String content;\n\n    @Field(type = FieldType.Text, analyzer = EsConstants.IK_MAX_WORD, store = true, copyTo = \"all\")\n    private String title;\n\n    @Field(type = FieldType.Integer, store = true)\n    private Integer starCount;\n    @Field(type = FieldType.Integer, store = true)\n    private Integer pageView;\n    @Field(type = FieldType.Integer, store = true)\n    private Integer commentCount;\n\n    @Field(store = true, index = false, type = FieldType.Keyword)\n    private String coverImageUrl;\n\n    @Field(type = FieldType.Date, pattern = DatePattern.NORM_DATETIME_PATTERN, store = true)\n    private Date createTime;\n    @Field(type = FieldType.Date, pattern = DatePattern.NORM_DATETIME_PATTERN, store = true)\n    private Date updateTime;\n    @Field(type = FieldType.Date, pattern = DatePattern.NORM_DATETIME_PATTERN, store = true)\n    private Date activityTime;\n\n    @Field(type = FieldType.Keyword, store = true)\n    List<Integer> tagIds;\n    @Field(type = FieldType.Keyword, store = true)\n    Integer categoryId;\n\n    public static TopicIndex from(Topic topic) {\n        TopicIndex topicIndex = BeanUtils.copyPropertiesTo(topic, TopicIndex.class);\n        CmtyUser user = topic.getUser();\n        if (user != null) {\n            topicIndex.setUsername(user.getUsername());\n            topicIndex.setPhotoUrl(user.getPhotoUrl());\n        }\n\n        return topicIndex;\n    }\n\n    public static Topic toTopic(TopicIndex topicIndex) {\n        Topic topic = BeanUtils.copyPropertiesTo(topicIndex, Topic.class);\n        CmtyUser user = new CmtyUser();\n        user.setUsername(topicIndex.getUsername());\n        user.setPhotoUrl(topicIndex.getPhotoUrl());\n        topic.setUser(user);\n        return topic;\n    }\n\n    public static List<Topic> toTopicList(List<TopicIndex> topicIndexList) {\n        return topicIndexList.stream()\n                .map(TopicIndex::toTopic)\n                .collect(Collectors.toList());\n    }\n\n    public static MyPage<Topic> toTopicPage(MyPage<TopicIndex> topicPage) {\n        List<Topic> topicList = toTopicList(topicPage.getDataList());\n        return new MyPage<>(topicPage.getTotalCount(),topicList);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/Category.java",
    "content": "package com.acimage.common.model.domain.community;\n\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Category {\n    public static final int LABEL_MAX=50;\n    public static final int LABEL_MIN =2;\n    public static final String LABEL_VALIDATION_MSG =\"角色名在\"+ LABEL_MIN +\"-\"+LABEL_MAX+\"之间\";\n\n    @TableId(type = IdType.AUTO)\n    Integer id;\n    String label;\n    private Date createTime;\n    private Date updateTime;\n    @TableLogic(delval = \"1\")\n    boolean deleted;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/CmtyUser.java",
    "content": "package com.acimage.common.model.domain.community;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class CmtyUser {\n    @TableId\n    Long id;\n    String username;\n    String photoUrl;\n    Integer topicCount;\n    Integer starCount;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/Comment.java",
    "content": "package com.acimage.common.model.domain.community;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\n@Data\n@NoArgsConstructor\npublic class Comment {\n    public static final int CONTENT_MIN=2;\n    public static final int CONTENT_MAX=200;\n    public static final String CONTENT_VALIDATION_MSG=\"评论字数在\"+CONTENT_MIN+\"-\"+CONTENT_MAX+\"之间\";\n\n    @TableId(type= IdType.INPUT)\n    private Long id;\n    private Long topicId;\n    private Long userId;\n    private String content;\n    @TableLogic\n    private boolean deleted;\n    private Date createTime;\n    private Date updateTime;\n\n    @TableField(exist = false)\n    Topic topic;\n    @TableField(exist = false)\n    CmtyUser user;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/HomeCarousel.java",
    "content": "package com.acimage.common.model.domain.community;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class HomeCarousel {\n    public static final int DESC_MIN = 2;\n    public static final int DESC_MAX = 30;\n    public static final String DESC_INVALID_MSG =\"图片描述字数在\"+ DESC_MIN +\"-\"+ DESC_MAX +\"之间\";\n\n    public static final int LINK_MAX = 100;\n    public static final String LINK_INVALID_MSG =\"图片描述字数在\"+ 0 +\"-\"+ LINK_MAX +\"之间\";\n\n    @TableId(type= IdType.AUTO)\n    private Integer id;\n    private String description;\n    private String url;\n    private String link;\n    private Integer location;\n    private Integer size;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date createTime;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date updateTime;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/Star.java",
    "content": "package com.acimage.common.model.domain.community;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\npublic class Star {\n    private Long userId;\n    private Long topicId;\n\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    private Date createTime;\n\n    @TableField(exist = false)\n    Topic topic;\n\n    public Star(Long userId, Long topicId) {\n        this.userId = userId;\n        this.topicId = topicId;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/Tag.java",
    "content": "package com.acimage.common.model.domain.community;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Tag {\n    @TableId(type = IdType.AUTO)\n    Integer id;\n    String label;\n    private Date createTime;\n    private Date updateTime;\n    @TableLogic(delval = \"1\")\n    boolean deleted;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/TagTopic.java",
    "content": "package com.acimage.common.model.domain.community;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\npublic class TagTopic {\n\n    @TableId(type = IdType.AUTO)\n    Long id;\n    Long topicId;\n    Integer tagId;\n    private Date createTime;\n    @TableLogic(delval = \"1\")\n    boolean deleted;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/Topic.java",
    "content": "package com.acimage.common.model.domain.community;\n\n\nimport com.acimage.common.model.domain.user.User;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\nimport java.util.List;\n\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Data\npublic class Topic {\n    public static final int CONTENT_MIN = 4;\n    public static final int CONTENT_MAX = 500;\n    public static final String CONTENT_VALIDATION_MSG= \"内容长度在\" + CONTENT_MIN + \"-\" + CONTENT_MAX + \"之间\";\n    public static final int TITLE_MIN = 4;\n    public static final int TITLE_MAX = 30;\n    public static final String TITLE_VALIDATION_MSG= \"标题长度在\"+TITLE_MIN+\"-\"+TITLE_MAX+\"之间\";\n    public static int IMAGES_AMOUNT_MAX=5;\n    public static int IMAGES_AMOUNT_MIN=1;\n    public static final String IMAGE_VALIDATION_MSG= \"图片数量在\"+IMAGES_AMOUNT_MIN+\"-\"+IMAGES_AMOUNT_MAX+\"之间\";\n\n    public static int TAG_MAX=3;\n    public static int TAG_MIN=1;\n    public static final String TAG_VALIDATION_MSG= \"标签数量在\"+TAG_MIN+\"-\"+TAG_MAX+\"之间\";\n\n    @TableId(type= IdType.INPUT)\n    private Long id;\n    private Long userId;\n    private String content;\n    private String title;\n    private Integer starCount;\n    private Integer pageView;\n    private Integer commentCount;\n    private String coverImageUrl;\n    private Integer categoryId;\n\n    private Date activityTime;\n    private Date createTime;\n    private Date updateTime;\n\n    @TableLogic(delval = \"1\")\n    boolean deleted;\n\n    @TableField(exist = false)\n    CmtyUser user;\n    @TableField(exist = false)\n    Category category;\n    @TableField(exist = false)\n    List<Integer> tagIds;\n//    @TableField(exist = false)\n//    List<Image> images;\n    @TableField(exist = false)\n    List<Comment> comments;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/community/TopicHtml.java",
    "content": "package com.acimage.common.model.domain.community;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@NoArgsConstructor\n@Data\npublic class TopicHtml {\n    public static final int HTML_MIN = 4;\n    public static final int HTML_MAX = 4500;\n    public static final String HTML_VALIDATION_MSG = \"html源码长度在\" + HTML_MIN + \"-\" + HTML_MAX + \"之间\";\n\n    @TableId(type= IdType.INPUT)\n    private Long topicId;\n    String html;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date createTime;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date updateTime;\n\n    @TableLogic(delval = \"1\")\n    boolean deleted;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/image/Image.java",
    "content": "package com.acimage.common.model.domain.image;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Data\npublic class Image {\n    public static final int DESCRIPTION_MIN = 2;\n    public static final int DESCRIPTION_MAX = 30;\n    public static final String DESCRIPTION_VALIDATION_MSG =\"图片描述字数在\"+DESCRIPTION_MIN+\"-\"+DESCRIPTION_MAX+\"之间\";\n\n    public static final int FILE_NAME_MIN = 3;\n    public static final int FILE_NAME_MAX = 80;\n    public static final String FILE_NAME_VALIDATION_MSG =\"文件名在\"+FILE_NAME_MIN+\"-\"+FILE_NAME_MAX+\"之间\";\n\n    @TableId(type= IdType.INPUT)\n    private Long id;\n    private Long topicId;\n    private Integer size;\n    private String fileName;\n    private String description;\n    private String url;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date createTime;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date updateTime;\n    @TableLogic\n    private boolean deleted;\n    @TableField(exist = false)\n    Topic topic;\n\n    public Image(Long id, Long topicId, Integer size, String description) {\n        this.id = id;\n        this.topicId = topicId;\n        this.size = size;\n        this.description = description;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/image/ImageHash.java",
    "content": "package com.acimage.common.model.domain.image;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ImageHash {\n    @TableId(type= IdType.INPUT)\n    Long imageId;\n    Long hashValue;\n    Integer hashSum;\n    Date createTime;\n    @TableField(exist = false)\n    Integer distance;\n\n    public ImageHash(Long imageId, Long hashValue, Integer hashSum) {\n        this.imageId = imageId;\n        this.hashValue = hashValue;\n        this.hashSum = hashSum;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/sys/Api.java",
    "content": "package com.acimage.common.model.domain.sys;\n\nimport com.acimage.common.global.enums.MatchRule;\nimport com.acimage.common.global.enums.MyHttpMethod;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.http.HttpMethod;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Api {\n    public static final int PATH_MIN = 2;\n    public static final int PATH_MAX = 200;\n    public static final int NOTE_MAX = 100;\n    public static final String PATH_PATTERN=\"(/([a-zA-Z0-9]+|(\\\\*){1,2}))+\";\n\n    @TableId(type = IdType.AUTO)\n    Integer id;\n    String path;\n\n    MyHttpMethod method;\n\n    Integer permissionId;\n    String note;\n    boolean enable;\n    Date createTime;\n    Date updateTime;\n    @TableLogic(delval = \"1\")\n    boolean deleted;\n    @TableField(exist = false)\n    Permission permission;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/sys/Authorize.java",
    "content": "package com.acimage.common.model.domain.sys;\n\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Authorize {\n    @TableId(type = IdType.AUTO)\n    Integer id;\n    Integer roleId;\n    Integer permissionId;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date createTime;\n\n    public Authorize(Integer roleId, Integer permissionId) {\n        this.roleId = roleId;\n        this.permissionId = permissionId;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/sys/Permission.java",
    "content": "package com.acimage.common.model.domain.sys;\n\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\nimport java.util.List;\n\n@Data\n@NoArgsConstructor\npublic class Permission {\n    public static final int CODE_MAX=50;\n    public static final int NOTE_MAX=20;\n    public static final int LABEL_MAX=20;\n    public static final String CODE_VALIDATION_MSG=\"权限码字数小于\"+CODE_MAX;\n    public static final String NOTE_VALIDATION_MSG=\"备注字数小于\"+NOTE_MAX;\n    public static final String LABEL_VALIDATION_MSG=\"标识字数小于\"+LABEL_MAX;\n\n    @TableId(type = IdType.AUTO)\n    Integer id;\n    Integer parentId;\n    String code;\n    String note;\n    String label;\n    boolean module;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date createTime;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date updateTime;\n\n    @TableField(exist = false)\n    List<Permission> children;\n    @TableField(exist = false)\n    Permission parent;\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/sys/Role.java",
    "content": "package com.acimage.common.model.domain.sys;\n\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\npublic class Role {\n    public static final int ROLE_NAME_MAX=20;\n    public static final int ROLE_NAME_MIN =2;\n    public static final String ROLE_NAME_VALIDATION_MSG =\"角色名在\"+ ROLE_NAME_MIN +\"-\"+ROLE_NAME_MAX+\"之间\";\n    public static final int NOTE_MAX=20;\n    public static final String NOTE_VALIDATION_MSG =\"备注小于\"+ROLE_NAME_MAX+\"字\";\n\n    @TableId(type = IdType.AUTO)\n    Integer id;\n    String roleName;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date createTime;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\",timezone = \"GMT+8\")\n    private Date updateTime;\n    String note;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/sys/UserRole.java",
    "content": "package com.acimage.common.model.domain.sys;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserRole {\n    @TableId(type = IdType.INPUT)\n    private Long id;\n    private Long userId;\n    private Integer roleId;\n    private Date createTime;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/user/CommentMsg.java",
    "content": "package com.acimage.common.model.domain.user;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\npublic class CommentMsg {\n    @TableId(type = IdType.INPUT)\n    Long commentId;\n    String content;\n    Long fromUserId;\n    Long toUserId;\n    Long topicId;\n    String topicTitle;\n    Date createTime;\n    @TableField(exist = false)\n    User user;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/user/User.java",
    "content": "package com.acimage.common.model.domain.user;\n\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class User {\n    @TableId(type= IdType.INPUT)\n    private Long id;\n    private String username;\n    private String photoUrl;\n\n    @TableField(exist = false)\n    private Integer starCount;\n    @TableField(exist = false)\n    private Integer topicCount;\n    @TableField(exist = false)\n    List<Integer> roleIds;\n\n    public User(Long id, String username) {\n        this.id = id;\n        this.username = username;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/user/UserMsg.java",
    "content": "package com.acimage.common.model.domain.user;\n\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\nimport java.util.List;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserMsg {\n    @TableId(type= IdType.INPUT)\n    private Long userId;\n    private Integer commentMsgCount;\n    private Integer starMsgCount;\n\n    private Date readCommentTime;\n    private Date readStarTime;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/domain/user/UserPrivacy.java",
    "content": "package com.acimage.common.model.domain.user;\n\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n@Data\n@NoArgsConstructor\npublic class UserPrivacy {\n\n    @TableId(type = IdType.INPUT)\n    private Long userId;\n    private String pwd;\n    private String salt;\n    private String email;\n\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n    private Date registerTime;\n\n    public UserPrivacy(Long userId, String pwd, String salt, String email) {\n        this.userId = userId;\n        this.pwd = pwd;\n        this.salt = salt;\n        this.email = email;\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/EsAddDto.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport lombok.*;\n\n\n@Getter\n@Setter\n@NoArgsConstructor\npublic class EsAddDto extends ObjectWithClass {\n    public EsAddDto(Object obj){\n        this.with(obj);\n    }\n\n    @Override\n    public String toString() {\n        return super.toString();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/EsDeleteDto.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class EsDeleteDto {\n    String id;\n    Class<?> type;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/EsUpdateByIdDto.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport lombok.*;\n\nimport java.util.List;\n\n@Getter\n@Setter\n@NoArgsConstructor\npublic class EsUpdateByIdDto extends ObjectWithClass {\n    List<String> columns;\n\n    @Override\n    public String toString() {\n        return columns+super.toString();\n    }\n    //    List<SFunction<Object, Object>> columns;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/EsUpdateByTermDto.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\n\nimport java.util.List;\n\n@Getter\n@Setter\n@NoArgsConstructor\npublic class EsUpdateByTermDto extends ObjectWithClass{\n    String termColumn;\n    List<String> columns;\n\n    @Override\n    public String toString() {\n        return \"EsUpdateByTermDto{\" +\n                \"termColumn='\" + termColumn + '\\'' +\n                \", columns=\" + columns +\n                '}'+super.toString();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/ImageIdWithUrl.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ImageIdWithUrl {\n    private Long imageId;\n    private String url;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/ObjectWithClass.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport com.acimage.common.utils.common.JacksonUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * 用于序列化时候传输\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ObjectWithClass {\n    private String json;\n    private Class<?> type;\n\n    public void with(Object obj){\n        this.setJson(JacksonUtils.writeValueAsString(obj));\n        this.setType(obj.getClass());\n    }\n\n    /**\n     * 千万别写成getObject,否则会导致序列化出错\n     */\n    public Object object(){\n        return JacksonUtils.convert(json,type);\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/SyncImagesUpdateDto.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport com.acimage.common.global.enums.ServiceType;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Builder\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SyncImagesUpdateDto {\n    private Long topicId;\n    /**\n     * 话题内新增的图片链接\n     */\n    private List<String> addImageUrls;\n    /**\n     * 话题内移除的图片链接\n     */\n    private List<String> removeImageUrls;\n\n    private ServiceType serviceType;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/UserIdWithPhotoUrl.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class UserIdWithPhotoUrl {\n    private Long userId;\n    private String photoUrl;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/mq/dto/UserIdWithUsername.java",
    "content": "package com.acimage.common.model.mq.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class UserIdWithUsername {\n    private Long userId;\n    private String username;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/model/page/MyPage.java",
    "content": "package com.acimage.common.model.page;\n\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class MyPage<T> {\n    int totalCount;\n    List<T> dataList;\n\n    public static<T> MyPage<T> from(IPage<T> page){\n        return new MyPage<>((int)page.getTotal(),page.getRecords());\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/QueryRedisAdvice.java",
    "content": "package com.acimage.common.redis;\n\n\nimport com.acimage.common.redis.annotation.KeyParam;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.redis.enums.DataType;\nimport com.acimage.common.utils.common.*;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Nullable;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n\n@Aspect\n@Component\n@Order(100)\n@Slf4j\npublic class QueryRedisAdvice {\n    private static final String POINT_CUT_PATTERN = \"@annotation(com.acimage.common.redis.annotation.QueryRedis)\";\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Pointcut(POINT_CUT_PATTERN)\n    public void pointCut() {\n    }\n\n    @Around(\"pointCut()\")\n    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {\n\n        Method method = AopUtils.methodOf(joinPoint);\n        QueryRedis queryRedis = AopUtils.annotationFrom(joinPoint, QueryRedis.class);\n        Class<?> returnType = method.getReturnType();\n        Class<?>[] generics = ReflectUtils.genericsOfReturnType(method);\n\n        //获取@QueryRedis的属性\n        String prefix = queryRedis.keyPrefix();\n        TimeUnit timeUnit = queryRedis.unit();\n        DataType dataType = queryRedis.dataType();\n\n        //获取被注解的形参和实参\n        List<KeyParam> annotatedParams = AopUtils.paramAnnotationsFrom(joinPoint, KeyParam.class);\n        List<String> annotatedArgs = AopUtils.annotatedArgsFrom(joinPoint, KeyParam.class, String.class);\n        //获取所有实参\n        List<String> args = Arrays.stream(joinPoint.getArgs()).map(Object::toString).collect(Collectors.toList());\n        //合并出整体key\n        String suffix = StringUtils.concatForNotNull(\":\", args);\n        String overallKey = prefix + suffix;\n\n        //根据注解和实参信息得到起作用的expire\n        long expire = getActiveExpire(queryRedis.expire(), annotatedParams, annotatedArgs);\n        if (expire <= 0) {\n            return joinPoint.proceed();\n        }\n\n        if (dataType == DataType.STRING) {\n            return queryOrProceed(joinPoint, overallKey, expire, timeUnit, returnType, generics);\n        } else if (dataType == DataType.HASH) {\n            return queryOrProceedForHash(joinPoint, overallKey, expire, timeUnit, returnType);\n        } else {\n            throw new RuntimeException(this.getClass()+\"到达不存在的分支\");\n        }\n    }\n\n    /**\n     * 查询redis，有则返回，无则调用原方法被设置到redis并返回，针对返回值是list的类型\n     */\n    private Object queryOrProceed(ProceedingJoinPoint joinPoint, String redisKey, long expire, TimeUnit timeUnit, Class<?> returnType, @Nullable Class<?>... generics) throws Throwable {\n        //从redis获取\n        Object obj;\n        Class<?>[] newGenerics = (generics == null) ? new Class<?>[]{} : generics;\n\n        obj = redisUtils.getFromString(redisKey, returnType, newGenerics);\n\n        if (obj != null) {\n            return obj;\n        }\n\n        Object result = joinPoint.proceed();\n\n        //设置到redis\n        if (result != null) {\n            redisUtils.setObjectJson(redisKey, result, expire, timeUnit);\n        }\n\n        return result;\n    }\n\n    private Object queryOrProceedForHash(ProceedingJoinPoint joinPoint, String redisKey, long expire, TimeUnit timeUnit, Class<?> returnType) throws Throwable {\n        //从redis获取\n        Object obj;\n\n        obj = redisUtils.getObjectForHash(redisKey, returnType);\n\n        if (obj != null) {\n            return obj;\n        }\n\n        Object result = joinPoint.proceed();\n        //设置到redis\n        if (result != null) {\n            redisUtils.setObjectForHash(redisKey, result,expire,timeUnit);\n        }\n\n        return result;\n    }\n\n    /**\n     * 若当前实参值和@KeyParam中的specials匹配，则返回该参数注解的expire，否则返回default expire\n     * @param annotatedArgs 被注解的实参值\n     */\n    private long getActiveExpire(long defaultExpire, List<KeyParam> keyParams, List<String> annotatedArgs) {\n        assert keyParams.size() == annotatedArgs.size();\n\n        //先判断当前被注解实参值和它们的@KeyParam注解中的spValues参数是否符合\n        boolean isExpireChange = false;\n        long expire = defaultExpire;\n\n        for (int i = 0; i < keyParams.size(); i++) {\n\n            String[] specials = keyParams.get(i).specials();\n            long[] expires = keyParams.get(i).expires();\n            //未设置specials则不用考虑\n            if (specials.length == 0) {\n                continue;\n            }\n\n            Integer index = ArrayUtils.firstIndexOf(specials, annotatedArgs.get(i));\n            if (index == null) {\n                //当前实参和@KeyParam注解中specials不匹配\n                return defaultExpire;\n            } else {\n\n                if (expires.length <= index) {\n                    log.error(\"@KeyParam注解使用错误：specials和expire长度不一致\");\n                } else {\n                    if (!isExpireChange) {\n                        expire = expires[index];\n                        isExpireChange = true;\n                    }\n\n                }\n            }\n        }\n\n        return expire;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/RequestLimitAdvice.java",
    "content": "package com.acimage.common.redis;\n\n\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.common.*;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n\n@Aspect\n@Component\n@Slf4j\n@Order(1)\npublic class RequestLimitAdvice {\n    private static final String POINT_CUT_PATTERN = \"@annotation(com.acimage.common.redis.annotation.RequestLimit)\";\n    private static final String STRINGKP_REQUEST_LIMIT = \"acimage:request:limit:\";\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Pointcut(POINT_CUT_PATTERN)\n    public void pointCut() {\n    }\n\n    @Around(\"pointCut()\")\n    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {\n        Method method = AopUtils.methodOf(joinPoint);\n        RequestLimit requestLimit = AopUtils.annotationFrom(joinPoint, RequestLimit.class);\n        String methodName = method.getName();\n        String className = method.getDeclaringClass().getName();\n        String predix = STRINGKP_REQUEST_LIMIT + String.format(\"%s:%s:\", methodName, className);\n        LimitTarget[] targets = requestLimit.targets();\n\n        long[] limitTimes = requestLimit.limitTimes();\n        long[] durations = requestLimit.durations();\n        long[] penaltyTimes = requestLimit.penaltyTimes();\n        TimeUnit timeUnit = requestLimit.unit();\n\n        int len = targets.length;\n        if (limitTimes.length != len || durations.length != len || penaltyTimes.length != len) {\n            throw new IllegalArgumentException(\"注解参数长度不一致\");\n        }\n        List<String> keys = new ArrayList<>();\n        List<Long> limitList=new ArrayList<>();\n        List<Long> durationList=new ArrayList<>();\n        List<Long> penaltyList=new ArrayList<>();\n        List<LimitTarget> validTargetList=new ArrayList<>();\n\n        for (int i=0;i<targets.length;i++) {\n            switch (targets[i]){\n                case IP:\n                    if(UserContext.getIp()!=null){\n                        validTargetList.add(targets[i]);\n                        keys.add(predix + String.format(\"%s:%s\",targets[i],UserContext.getIp()));\n                        limitList.add(toSeconds(limitTimes[i],timeUnit));\n                        durationList.add(toSeconds(durations[i],timeUnit));\n                        penaltyList.add(toSeconds(penaltyTimes[i],timeUnit));\n                    }\n                    break;\n                case USER:\n                    if(UserContext.getUserId()!=null){\n                        validTargetList.add(targets[i]);\n                        keys.add(predix + String.format(\"%s:%s\",targets[i],UserContext.getUserId()));\n                        limitList.add(toSeconds(limitTimes[i],timeUnit));\n                        durationList.add(toSeconds(durations[i],timeUnit));\n                        penaltyList.add(toSeconds(penaltyTimes[i],timeUnit));\n                    }\n                    break;\n                case ALL:\n                    validTargetList.add(targets[i]);\n                    keys.add(predix +targets[i]);\n                    limitList.add(toSeconds(limitTimes[i],timeUnit));\n                    durationList.add(toSeconds(durations[i],timeUnit));\n                    penaltyList.add(toSeconds(penaltyTimes[i],timeUnit));\n                    break;\n            }\n        }\n        List<Long> incrementResults = redisUtils.requestLimitScript(keys, limitList, durationList, penaltyList);\n        for (int i = 0; i < incrementResults.size(); i++) {\n            if (incrementResults.get(i) > limitList.get(i)) {\n                switch (validTargetList.get(i)) {\n                    case IP:\n                    case USER:\n                        return Result.fail(\"请勿频繁操作\");\n                    case ALL:\n                        return Result.fail(\"系统繁忙，请稍后重试\");\n                }\n            }\n        }\n\n        return joinPoint.proceed();\n    }\n\n    private List<Long> toSecondsList(long[] times, TimeUnit unit) {\n        Long[] ts = new Long[times.length];\n        for (int i = 0; i < times.length; i++) {\n            ts[i] = times[i];\n        }\n        return Arrays.stream(ts).map(obj -> toSeconds(obj, unit)).collect(Collectors.toList());\n    }\n\n    private long toSeconds(long time, TimeUnit timeUnit) {\n        long res = time;\n        switch (timeUnit) {\n            case DAYS:\n                res = res * 24;\n            case HOURS:\n                res = res * 60;\n            case MINUTES:\n                res = res * 60;\n            case SECONDS:\n                return res;\n        }\n        throw new IllegalArgumentException(\"只支持timeUnit为day,hour,minute,second\");\n    }\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/annotation/KeyParam.java",
    "content": "package com.acimage.common.redis.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 和@QueryRedis配合使用，被注解的参数作为redis键的后缀，可以有多个\n * 但只能有一个KeyParam设置spValues和expire\n */\n@Target({ElementType.FIELD,ElementType.PARAMETER})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface KeyParam {\n    /**\n     * 后缀取这些值时，过期时间根据本身的expire设置\n     */\n    String[] specials() default {};\n\n    /**\n     * 对应的过期时间\n     */\n    long[] expires() default {};\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/annotation/QueryRedis.java",
    "content": "package com.acimage.common.redis.annotation;\n\nimport com.acimage.common.redis.enums.DataType;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.concurrent.TimeUnit;\n\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface QueryRedis {\n\n    String keyPrefix() ;\n    /**\n     * expire<=0并且参数中没有@KeyParam设置了expire时，不会设置到redis，\n     */\n    long expire() default 1L;\n    TimeUnit unit() default TimeUnit.MINUTES;\n    DataType dataType() default DataType.STRING;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/annotation/RequestLimit.java",
    "content": "package com.acimage.common.redis.annotation;\n\n\nimport com.acimage.common.redis.enums.LimitTarget;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.concurrent.TimeUnit;\n\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface RequestLimit {\n\n    long[] limitTimes();\n    long[] durations();\n    long[] penaltyTimes();\n    LimitTarget[] targets();\n    TimeUnit unit() default TimeUnit.SECONDS;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/enums/DataType.java",
    "content": "package com.acimage.common.redis.enums;\n\npublic enum DataType {\n    STRING,\n    HASH;\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/enums/LimitTarget.java",
    "content": "package com.acimage.common.redis.enums;\n\npublic enum LimitTarget {\n    USER,\n    IP,\n    ALL,\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/redis/utils/RedisAnnotationUtils.java",
    "content": "package com.acimage.common.redis.utils;\n\nimport com.acimage.common.redis.annotation.KeyParam;\nimport com.acimage.common.utils.common.AopUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.common.utils.common.StringUtils;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n@Component\npublic class RedisAnnotationUtils {\n\n    @Autowired\n    RedisUtils redisUtils;\n    /**\n     * 查询redis，有则返回，无则调用原方法被设置到redis并返回\n     */\n    private  Object queryOrProceed(ProceedingJoinPoint joinPoint, String redisKey, Class<?> resultType, long expire, TimeUnit unit) throws Throwable {\n        //查redis\n        Object objectResult = redisUtils.getObjectFromString(redisKey, resultType);\n        if (objectResult != null) {\n            return objectResult;\n        }\n\n        Object result = joinPoint.proceed();\n\n        //设置到redis\n        redisUtils.setObjectJson(redisKey, result, expire, unit);\n        return result;\n    }\n\n    /**\n     * 查询redis，有则返回，无则调用原方法被设置到redis并返回，针对返回值是list的类型\n     */\n    private Object queryOrProceedForList(ProceedingJoinPoint joinPoint, String redisKey, Class<?> resultType, long expire, TimeUnit timeUnit) throws Throwable {\n        //从redis获取\n        List<?> objectList = redisUtils.getListFromString(redisKey, resultType);\n        if (objectList != null) {\n            return objectList;\n        }\n\n        Object result = joinPoint.proceed();\n\n        //设置到redis\n        redisUtils.setObjectJson(redisKey,result,expire,timeUnit);\n        return result;\n    }\n\n    public static String overallKey(String prefix, ProceedingJoinPoint joinPoint){\n        //获取所有KeyParam注解\n        List<KeyParam> keyParams = AopUtils.paramAnnotationsFrom(joinPoint, KeyParam.class);\n        //对应实参转String\n        List<String> args = AopUtils.annotatedArgsFrom(joinPoint, KeyParam.class, String.class);\n        //合并出整体key\n        String suffix = StringUtils.concatForNotNull(\":\", args);\n        return (suffix == null) ? prefix : prefix + suffix;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/result/Code.java",
    "content": "package com.acimage.common.result;\n\npublic class Code {\n    public static final Integer SAVE_ERR=20010;\n\n    public static final Integer OK=20000;\n    public static final Integer ERR=20001;\n\n    public static final Integer SYSTEM_ERR=50001;\n    public static final Integer BUSINESS_ERR=50002;\n\n    public static final Integer DATA_NOT_EXIST=20011;\n    public static final Integer PARAM_INVALID =40001;\n    public static final Integer TOKEN_ERR=40011;\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/result/Result.java",
    "content": "package com.acimage.common.result;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Result <T> {\n    private Integer code;\n    private T data;\n    private String msg;\n    public static Result fail(String message){\n        return new Result(Code.ERR,null,message);\n    }\n    public static Result ok(){\n        return new Result(Code.OK,null,\"\");\n    }\n    public static <T> Result<T> ok(T data){\n        return new Result(Code.OK,data,\"\");\n    }\n    public static boolean isOk(Result<?> result){\n        return Code.OK.equals(result.getCode());\n    }\n\n    public Boolean isOk(){\n        return Code.OK.equals(code);\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/service/TokenService.java",
    "content": "package com.acimage.common.service;\n\npublic interface TokenService {\n\n\n    Boolean hasRecorded(String token);\n\n    String createAndRecordToken(long userId, String username, String photoUrl,int expireDays);\n\n    void record(String token, int expireDays);\n\n    void invalidate(String token);\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/service/impl/TokenServiceImpl.java",
    "content": "package com.acimage.common.service.impl;\n\nimport com.acimage.common.global.consts.JwtConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.JwtUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class TokenServiceImpl implements TokenService {\n    public static final String STRINGKP_TOKEN =\"acimage:tokens:token:\";\n\n    @Autowired\n    RedisUtils redisUtils;\n\n\n    @Override\n    public String createAndRecordToken(long userId, String username, String photoUrl,int expireDays){\n        //生成token\n        String token = JwtUtils.createToken(userId, username,photoUrl,expireDays);\n        //记录token和ip的对应\n        record(token, expireDays);\n        return token;\n    }\n\n    @Override\n    public void record(String token,int expireDays){\n        redisUtils.setAsString(STRINGKP_TOKEN +token,Boolean.TRUE.toString(), expireDays, TimeUnit.DAYS);\n    }\n\n    @Override\n    public Boolean hasRecorded(String token){\n\n        return redisUtils.getForString(STRINGKP_TOKEN +token)!=null;\n    }\n\n    @Override\n    public void invalidate(String token) {\n        redisUtils.delete(STRINGKP_TOKEN +token);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/CookieUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class CookieUtils {\n\n    public static String getValueByName(Cookie[] cookies,String name){\n        if(cookies==null){\n            return null;\n        }\n        for(int i=0;i< cookies.length;i++){\n            if(cookies[i].getName().equals(name)){\n                return cookies[i].getValue();\n            }\n        }\n        return null;\n    }\n    public static Cookie createCookie(String key,String value){\n        Cookie cookie = new Cookie(key,value);\n        cookie.setPath(\"/\");\n        cookie.setMaxAge(-1);\n        return cookie;\n    }\n    public static Cookie createCookie(String key,String value,boolean httpOnly){\n            Cookie cookie = new Cookie(key,value);\n            cookie.setPath(\"/\");\n            cookie.setMaxAge(-1);\n            cookie.setHttpOnly(httpOnly);\n            return cookie;\n    }\n    public static Cookie createCookie(String key,String value,String path,int expire){\n        Cookie cookie = new Cookie(key,value);\n        cookie.setPath(path);\n        cookie.setMaxAge(expire);\n        return cookie;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/EsUtils.java",
    "content": "package com.acimage.common.utils;\n\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.date.DatePattern;\nimport cn.hutool.core.date.DateUtil;\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.mq.dto.EsAddDto;\nimport com.acimage.common.model.mq.dto.EsDeleteDto;\nimport com.acimage.common.model.mq.dto.EsUpdateByIdDto;\nimport com.acimage.common.model.mq.dto.EsUpdateByTermDto;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.utils.common.BeanUtils;\nimport com.acimage.common.utils.common.ReflectUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.elasticsearch.index.query.MatchQueryBuilder;\nimport org.elasticsearch.index.query.QueryBuilder;\nimport org.elasticsearch.index.query.QueryBuilders;\nimport org.elasticsearch.search.sort.FieldSortBuilder;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.elasticsearch.core.*;\nimport org.springframework.data.elasticsearch.core.document.Document;\nimport org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;\nimport org.springframework.data.elasticsearch.core.query.*;\nimport org.springframework.stereotype.Component;\nimport reactor.util.annotation.Nullable;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n@Component\n@Slf4j\n@ConditionalOnClass(ElasticsearchRestTemplate.class)\npublic class EsUtils {\n    @Autowired\n    ElasticsearchRestTemplate esTemplate;\n\n    public void createIndexIfNotExist(Class<?> entityType) {\n        IndexCoordinates indexCoordinates = indexCoordinatesOf(entityType);\n        IndexOperations indexOperations = esTemplate.indexOps(indexCoordinates);\n        if (!indexOperations.exists()) {\n            // 创建索引和映射\n            indexOperations.create();\n            indexOperations.refresh();\n            Document mapping = indexOperations.createMapping(entityType);\n            indexOperations.refresh();\n            indexOperations.putMapping(mapping);\n            indexOperations.refresh();\n            log.info(\"创建索引和映射关系成功 {}\", entityType.getSimpleName());\n        }\n    }\n\n\n    public void updateById(EsUpdateByIdDto updateDto) {\n        Object entity = updateDto.object();\n        if (entity == null) {\n            return;\n        }\n        List<String> columns = updateDto.getColumns();\n        if (CollectionUtil.isEmpty(columns)) {\n            return;\n        }\n        //获取id\n        Class<?> indexClass = entity.getClass();\n        String id = Objects.requireNonNull(ReflectUtils.getAnnotatedFiled(entity, Id.class)).toString();\n\n        IndexCoordinates indexCoordinates = indexCoordinatesOf(indexClass);\n        Document document = this.createDocument(entity, columns);\n\n        UpdateQuery updateQuery = UpdateQuery.builder(id)\n                .withDocument(document)\n                .build();\n        esTemplate.update(updateQuery, indexCoordinates);\n    }\n\n    public void UpdateByTerm(EsUpdateByTermDto updateDto) {\n        Object entity = updateDto.object();\n        if (entity == null) {\n            return;\n        }\n        List<String> columns = updateDto.getColumns();\n        if (CollectionUtil.isEmpty(columns)) {\n            return;\n        }\n        //获取更新依据的term\n        Class<?> indexClass = entity.getClass();\n        String termColumn = updateDto.getTermColumn();\n        Object termValue = BeanUtil.getFieldValue(entity, termColumn);\n        QueryBuilder queryBuilder = QueryBuilders.termQuery(termColumn, termValue);\n\n        IndexCoordinates indexCoordinates = indexCoordinatesOf(indexClass);\n\n        //建立脚本和参数\n        Map<String, Object> params = new HashMap<>();\n        StringBuilder script = new StringBuilder();\n        for (String column : columns) {\n            Object value = BeanUtil.getFieldValue(entity, column);\n            script.append(String.format(\"ctx._source.%s=params.%s;\", column, column));\n            params.put(column, value);\n        }\n\n        Query query = new NativeSearchQueryBuilder()\n                .withQuery(queryBuilder)\n                .build();\n\n        UpdateQuery updateQuery = UpdateQuery.builder(query)\n                .withParams(params)\n                .withScript(script.toString())\n                .withScriptType(ScriptType.INLINE)\n                .withAbortOnVersionConflict(false)\n                .build();\n\n        ByQueryResponse byQueryResponse = esTemplate.updateByQuery(updateQuery, indexCoordinates);\n        log.debug(\"更新了{}条\", byQueryResponse.getUpdated());\n\n    }\n\n    public <T> void batchUpdateById(List<T> entityList, List<String> columns) {\n        if (CollectionUtil.isEmpty(entityList)) {\n            return;\n        }\n        Class<?> clz = entityList.get(0).getClass();\n        List<UpdateQuery> updateQueries = new ArrayList<>();\n\n        for (T entity : entityList) {\n            String id = Objects.requireNonNull(ReflectUtils.getAnnotatedFiled(entity, Id.class)).toString();\n            Document document = this.createDocument(entity, columns);\n            UpdateQuery updateQuery = UpdateQuery.builder(id)\n                    .withDocument(document)\n                    .build();\n            updateQueries.add(updateQuery);\n        }\n\n        esTemplate.bulkUpdate(updateQueries, clz);\n    }\n\n    public void save(EsAddDto esAddDto) {\n        Object obj = esAddDto.object();\n        esTemplate.save(obj);\n    }\n\n    public void remove(EsDeleteDto esDeleteDto) {\n        esTemplate.delete(esDeleteDto.getId(), esDeleteDto.getType());\n    }\n\n\n    public <T> MyPage<T> termQuery(String column, Object value, Class<T> indexClass, int pageNo, int pageSize, @Nullable FieldSortBuilder sortBuilder) {\n        QueryBuilder queryBuilder = QueryBuilders.termQuery(column, value);\n        Pageable pageable = PageRequest.of(pageNo - 1, pageSize);\n        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()\n                .withQuery(queryBuilder)\n                .withPageable(pageable);\n        if (sortBuilder != null) {\n            nativeSearchQueryBuilder.withSort(sortBuilder);\n        }\n        SearchHits<T> search = esTemplate.search(nativeSearchQueryBuilder.build(), indexClass);\n        int totalCount = (int) search.getTotalHits();\n        List<T> dateList = toList(search.getSearchHits());\n        return new MyPage<>(totalCount, dateList);\n    }\n\n    public <T> List<T> similarQuery(String id, Class<T> index, List<String> fields, int pageNo, int pageSize) {\n        MoreLikeThisQuery moreLikeThisQuery = new MoreLikeThisQuery();\n        moreLikeThisQuery.setId(id);\n        moreLikeThisQuery.addFields(fields.toArray(fields.toArray(new String[0])));\n        moreLikeThisQuery.setPageable(PageRequest.of(pageNo - 1, pageSize));\n        moreLikeThisQuery.setMinTermFreq(1);\n        moreLikeThisQuery.setMinDocFreq(2);\n        log.debug(\"{}\", esTemplate.search(moreLikeThisQuery, index).getSearchHits());\n        return toList(esTemplate.search(moreLikeThisQuery, index).getSearchHits());\n    }\n\n    public <T> List<T> matchQuery(Class<T> index, String field, Object value, int pageNo, int pageSize, float score) {\n        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery(field, value);\n        Pageable pageable = PageRequest.of(pageNo - 1, pageSize);\n        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()\n                .withQuery(matchQuery)\n                .withPageable(pageable)\n                .withMinScore(score)\n                .build();\n\n        SearchHits<T> search = esTemplate.search(nativeSearchQuery, index);\n        log.debug(\"{}\", search.getSearchHits());\n        return toList(search.getSearchHits());\n    }\n\n    public <T> MyPage<T> queryBySort(Class<T> indexClass, int pageNo, int pageSize, @Nullable FieldSortBuilder sortBuilder) {\n        Pageable pageable = PageRequest.of(pageNo - 1, pageSize);\n        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()\n                .withPageable(pageable);\n        if (sortBuilder != null) {\n            nativeSearchQueryBuilder.withSort(sortBuilder);\n        }\n        SearchHits<T> search = esTemplate.search(nativeSearchQueryBuilder.build(), indexClass);\n        int totalCount = (int) search.getTotalHits();\n        List<T> dateList = toList(search.getSearchHits());\n        return new MyPage<>(totalCount, dateList);\n    }\n\n    public IndexCoordinates indexCoordinatesOf(Class<?> clz) {\n        String indexName = clz.getAnnotation(org.springframework.data.elasticsearch.annotations.Document.class)\n                .indexName();\n        return IndexCoordinates.of(indexName);\n\n    }\n\n    private <T> List<T> toList(List<SearchHit<T>> searchHits) {\n        return searchHits.stream()\n                .map(SearchHit::getContent)\n                .collect(Collectors.toList());\n    }\n\n    private Document createDocument(Object entity, List<String> columns) {\n        Document document = Document.create();\n        //根据要更新的字段创建对应map\n        for (String column : columns) {\n            Object value = BeanUtil.getFieldValue(entity, column);\n            document.put(column, value);\n        }\n        return document;\n    }\n\n    private String buildScript(Object entity, List<String> columns) {\n        StringBuilder script = new StringBuilder();\n        for (String column : columns) {\n            Object value = BeanUtil.getFieldValue(entity, column);\n            script.append(String.format(\"ctx.source.%s=params.%s;\", column, column));\n        }\n        return script.toString();\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/ExceptionUtils.java",
    "content": "package com.acimage.common.utils;\n\npublic class ExceptionUtils {\n\n    public static void printIfDev(Throwable e){\n        if(SpringContextUtils.isDev()){\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/HtmlUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport com.acimage.common.utils.common.ListUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class HtmlUtils {\n\n    private static final Pattern imageSrcPattern;\n\n    static {\n        String regex = \"<img\\\\b[^>]*\\\\bsrc\\\\b\\\\s*=\\\\s*('|\\\")?(/[^'\\\"\\n\\r\\f>]+(\\\\.jpg|\\\\.png\\\\.jpe|\\\\.jpeg|\\\\.webp))\\\\b[^>]*>\";\n        imageSrcPattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);\n    }\n\n    public static String html2Text(String strHtml) {\n        String content = strHtml.replaceAll(\"</?[^>]+>\", \"\"); //剔出<html>的标签\n        content = content.replaceAll(\"(\\\\s|\\t|\\r|\\n)+\", \" \");//去除字符串中的空格,回车,换行符,制表符\n        content = content.replaceAll(\"(&[a-z]{2,6}+;)+\", \" \");//替换掉如&nbsp;之类的符号\n        return content;\n    }\n\n    /**\n     * 获取html中相对路径开头的图片，并对结果去重\n     */\n    public static List<String> getInnerImageUrls(String html) {\n        List<String> imageSrcList = new ArrayList<>();\n\n        Matcher m = imageSrcPattern.matcher(html);\n        String src;\n        while (m.find()) {\n            String quote = m.group(1);\n            src = (quote == null || quote.trim().length() == 0) ? m.group(2).split(\"\\\\s+\")[0] : m.group(2);\n            imageSrcList.add(src);\n        }\n        return ListUtils.removeRepeat(imageSrcList);\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/IdGenerator.java",
    "content": "package com.acimage.common.utils;\n\nimport cn.hutool.core.util.IdUtil;\n\npublic class IdGenerator {\n    public static long getSnowflakeNextId(){\n        return IdUtil.getSnowflakeNextId();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/ImageUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.global.consts.FileFormatConstants;\nimport com.acimage.common.utils.common.FileUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport net.coobird.thumbnailator.Thumbnails;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.imageio.ImageIO;\nimport java.awt.image.BufferedImage;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Collections;\n\n@Slf4j\npublic class ImageUtils {\n\n    public static InputStream compressAsFixedWebpImage(MultipartFile multipartFile, int width, int height, int limitSize) {\n        try {\n            float qualify = 0.76f;\n            BufferedImage bufferedImage = Thumbnails.fromInputStreams(Collections.singletonList(multipartFile.getInputStream()))\n                    .outputQuality(qualify)\n                    .forceSize(width, height)\n                    .outputFormat(FileFormatConstants.WEBP)\n                    .asBufferedImage();\n            try (InputStream is = ImageUtils.bufferedImage2InputStream(bufferedImage, FileFormatConstants.WEBP)) {\n                if (is != null && is.available() > limitSize) {\n                    log.warn(\"the image size after compressing exceed {} size:{}\", limitSize, is.available());\n                    throw new BusinessException(\"图片压缩后仍然较大，请尝试其它图片\");\n                }\n                return is;\n            }\n        } catch (IOException e) {\n            log.error(e.getMessage());\n            throw new BusinessException(\"图片数据错误\");\n        }\n    }\n\n    public static InputStream compressAsWebpImage(MultipartFile multipartFile, int limitSize, int limitLength) {\n        try {\n            float qualify = 0.76f;\n            BufferedImage image = ImageIO.read(multipartFile.getInputStream());\n            int width = image.getWidth();\n            int height = image.getHeight();\n            if (width > limitLength || height > limitLength) {\n                if (width > height) {\n                    width = limitLength;\n                    //等比例缩放\n                    height = (int) (1.0 * height * limitLength / width);\n                } else {\n                    height = limitLength;\n                    width = (int) (1.0 * width * limitLength / height);\n                }\n            }\n\n            BufferedImage bufferedImage = Thumbnails.fromInputStreams(Collections.singletonList(multipartFile.getInputStream()))\n                    .outputQuality(qualify)\n                    .size(width, height)\n                    .outputFormat(FileFormatConstants.WEBP)\n                    .asBufferedImage();\n\n            try (InputStream is = ImageUtils.bufferedImage2InputStream(bufferedImage, FileFormatConstants.WEBP)) {\n                if (is != null && is.available() > limitSize) {\n                    log.warn(\"the image size after compressing exceed {} size:{}\", limitSize, is.available());\n                    throw new BusinessException(\"图片压缩后仍然较大，请尝试其它图片\");\n                }\n                return is;\n            }\n        } catch (IOException e) {\n            log.error(e.getMessage());\n            throw new BusinessException(\"图片数据错误\");\n        }\n    }\n\n\n    public static InputStream compressBak(MultipartFile multipartFile, int limitSize) {\n\n        String format = FileUtils.formatOf(multipartFile);\n        try {\n            long size = multipartFile.getSize();\n            if (size < limitSize) {\n                return multipartFile.getInputStream();\n            }\n//            if (size < 240 * 1000) {\n//                return multipartFile.getInputStream();\n//            } else if (limitSize * 1.0 / size > 0.8f) {\n//                return multipartFile.getInputStream();\n//            }\n//            Thumbnails.fromInputStreams(Collections.singletonList(multipartFile.getInputStream()));\n\n            BufferedImage bufferedImage = ImageIO.read(multipartFile.getInputStream());\n            if (bufferedImage == null) {\n                log.error(\"bufferedImage为空\");\n                throw new BusinessException(\"图像为空\");\n            }\n            Thumbnails.Builder<BufferedImage> imageBuilder = Thumbnails.fromImages(Collections.singletonList(bufferedImage));\n//            BufferedImage bufferedImage = inputStreamBuilder.asBufferedImage();\n            int height = bufferedImage.getHeight();\n            int width = bufferedImage.getWidth();\n            //按边压缩比例，要开根号\n            double scale = Math.sqrt(limitSize * 1.0 / (width * height));\n            imageBuilder.scale(scale).outputQuality(1f);\n//            imageBuilder.size(270, 260).outputQuality(1f);\n            return ImageUtils.bufferedImage2InputStream(imageBuilder.asBufferedImage(), FileUtils.formatOf(multipartFile));\n        } catch (IOException e) {\n            log.error(e.getMessage());\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static InputStream bufferedImage2InputStream(BufferedImage image, String format) {\n        ByteArrayOutputStream os = new ByteArrayOutputStream();\n        try {\n            ImageIO.write(image, format, os);\n            return new ByteArrayInputStream(os.toByteArray());\n        } catch (IOException e) {\n            log.error(e.getMessage());\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/IpUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.common.global.consts.HeaderKeyConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\n\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.util.Objects;\n\n@Slf4j\npublic class IpUtils {\n\n    //获取请求客户端IP地址\n    public static String getClientIpAddr(HttpServletRequest request) {\n        String ip = request.getHeader(\"x-forwarded-for\");\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"WL-Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getRemoteAddr();\n            if (\"127.0.0.1\".equals(ip) || \"0:0:0:0:0:0:0:1\".equals(ip)) {\n                //根据网卡取本机配置的IP\n                InetAddress inet = null;\n                try {\n                    inet = InetAddress.getLocalHost();\n                } catch (Exception e) {\n                    ExceptionUtils.printIfDev(e);\n                }\n                assert inet != null;\n                ip = inet.getHostAddress();\n            }\n        }\n\n        // 多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割\n        if (ip != null && ip.length() > 15) {\n            if (ip.indexOf(\",\") > 0) {\n                ip = ip.substring(0, ip.indexOf(\",\"));\n            }\n        }\n        return ip;\n    }\n\n    private final static String IP_UTILS_FLAG = \",\";\n    // 未知IP\n    private final static String UNKNOWN = \"unknown\";\n    // 本地 IP\n    private final static String LOCALHOST_IP = \"0:0:0:0:0:0:0:1\";\n    private final static String LOCALHOST_IP1 = \"127.0.0.1\";\n\n\n    public static String getUserIp(ServerHttpRequest request) {\n        // 多次反向代理后会有多个ip值 的分割符\n        // 根据 HttpHeaders 获取 请求 IP地址\n        String ip = request.getHeaders().getFirst(\"X-Forwarded-For\");\n//        if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {\n//            ip = request.getHeaders().getFirst(\"x-forwarded-for\");\n//            if (ip != null && ip.length() != 0 && !UNKNOWN.equalsIgnoreCase(ip)) {\n//                // 多次反向代理后会有多个ip值，第一个ip才是真实ip\n//                if (ip.contains(IP_UTILS_FLAG)) {\n//                    ip = ip.split(IP_UTILS_FLAG)[0];\n//                }\n//            }\n//        }\n        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {\n            ip = request.getHeaders().getFirst(\"Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {\n            ip = request.getHeaders().getFirst(\"WL-Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {\n            ip = request.getHeaders().getFirst(\"HTTP_CLIENT_IP\");\n        }\n        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {\n            ip = request.getHeaders().getFirst(\"HTTP_X_FORWARDED_FOR\");\n        }\n        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {\n            ip = request.getHeaders().getFirst(\"X-Real-IP\");\n        }\n        //兼容k8s集群获取ip\n        if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {\n            ip = Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress();\n            if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {\n                //根据网卡取本机配置的IP\n                InetAddress iNet = null;\n                try {\n                    iNet = InetAddress.getLocalHost();\n                    ip = iNet.getHostAddress();\n                } catch (UnknownHostException e) {\n                    log.error(\"getClientIp error:{}\", e.getMessage());\n                }\n            }\n        }\n\n        return ip;\n    }\n\n\n    public static String getIp(HttpServletRequest request) {\n        String originUserIp = request.getHeader(HeaderKeyConstants.FEIGN_X_USER_IP);\n        if (!StrUtil.isEmpty(originUserIp)) {\n            return originUserIp;\n        } else {\n            return getClientIpAddr(request);\n        }\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/JwtUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.common.global.consts.JwtConstants;\nimport com.auth0.jwt.JWT;\nimport com.auth0.jwt.JWTVerifier;\nimport com.auth0.jwt.algorithms.Algorithm;\nimport com.auth0.jwt.exceptions.JWTDecodeException;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.acimage.common.global.exception.NullTokenException;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n\nimport javax.annotation.PostConstruct;\nimport java.util.Date;\n\n@Component\npublic class JwtUtils {\n    @Value(\"${jwt.secret-key}\")\n    public String secretKey;\n\n    private static String SECRET_KEY;\n\n    @PostConstruct\n    private void init(){\n        SECRET_KEY=this.secretKey;\n    }\n\n\n    public static String createToken(long userId, String username, String photoUrl, int expireDays) {\n\n        return JWT.create()\n                .withIssuedAt(new Date())    //发行时间\n                .withExpiresAt(DateUtil.offsetDay(new Date(), expireDays)) //有效截止时间\n                .withClaim(JwtConstants.KEY_USER_ID, userId)    //载荷，存储不敏感的用户信息\n                .withClaim(JwtConstants.KEY_USERNAME, username)\n                .withClaim(JwtConstants.KEY_PHOTO_URL, photoUrl)\n                .sign(Algorithm.HMAC256(SECRET_KEY));   //加密(摘要)\n    }\n\n    public static void verifyToken(String token) throws JWTVerificationException {\n        if (StrUtil.isBlank(token)) {\n            throw new NullTokenException(\"token is null！\");\n        }\n        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET_KEY)).build();\n\n        verifier.verify(token);\n\n    }\n\n    public static Long getUserId(String token) throws JWTDecodeException {\n        return JWT.decode(token).getClaim(JwtConstants.KEY_USER_ID).asLong();\n    }\n\n    public static String getUsername(String token) throws JWTDecodeException {\n        return JWT.decode(token).getClaim(JwtConstants.KEY_USERNAME).asString();\n    }\n\n    public static String getPhotoUrl(String token) throws JWTDecodeException {\n        return JWT.decode(token).getClaim(JwtConstants.KEY_PHOTO_URL).asString();\n    }\n\n    public static Date getExpire(String token) throws JWTDecodeException {\n        return JWT.decode(token).getExpiresAt();\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/LambdaUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport cn.hutool.core.util.ReflectUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.invoke.SerializedLambda;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\npublic class LambdaUtils {\n    public static final String TOKEN_GET=\"get\";\n    public static final String TOKEN_IS=\"is\";\n    public static final String WRITE_REPLACE=\"writeReplace\";\n\n    public static <T> String underlineColumnNameOf(SFunction<T, ?> getOrIs) {\n        return StringUtils.camelToUnderline(columnOf(getOrIs));\n\n    }\n\n    public static <T> String columnOf(SFunction<T, ?> getOrIs) {\n\n        // 从function取出序列化方法\n        Method writeReplaceMethod = ReflectUtil.getMethodByName(getOrIs.getClass(), WRITE_REPLACE);\n        // 从序列化方法取出序列化的lambda信息\n//        boolean isAccessible = writeReplaceMethod.isAccessible();\n        /**\n         * 如果要通过isAccessible设置回去，一定要考虑并发问题\n         */\n        writeReplaceMethod.setAccessible(true);\n        SerializedLambda serializedLambda;\n        try {\n            serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(getOrIs);\n        } catch (IllegalAccessException | InvocationTargetException e) {\n            log.error(e.getMessage());\n            throw new RuntimeException(e);\n        }\n        //如果将访问属性设置回去，这里可能会出错\n        //writeReplaceMethod.setAccessible(isAccessible);\n\n        // 从lambda信息取出method等\n        String methodName = serializedLambda.getImplMethodName();\n        String capitalizeFieldName;\n        if (methodName.startsWith(TOKEN_IS)) {\n            capitalizeFieldName = methodName.substring(TOKEN_IS.length());\n        } else if (methodName.startsWith(TOKEN_GET)) {\n            capitalizeFieldName = methodName.substring(TOKEN_GET.length());\n        } else {\n            throw new IllegalArgumentException(methodName + \"前缀非get或is\");\n        }\n        return StringUtils.firstToLowerCase(capitalizeFieldName);\n    }\n\n    @SafeVarargs\n    public static <T> List<String> columnsFrom(SFunction<T, ?>... getOrIsFunctions) {\n        List<String> columns=new ArrayList<>();\n        for(SFunction<T,?> function:getOrIsFunctions){\n            columns.add(columnOf(function));\n        }\n        return columns;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/RsaUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.asymmetric.RSA;\n\npublic class RsaUtils {\n    private static final String privateKey;\n    private static final String publicKey;\n\n    static {\n        RSA rsa = new RSA();\n        privateKey=rsa.getPrivateKeyBase64();\n        publicKey =rsa.getPublicKeyBase64();\n    }\n    public static String decrypt(String privateKeyBase64,String encryptBase64){\n        RSA rsa=new RSA(privateKeyBase64,null);\n        return rsa.decryptStr(encryptBase64, KeyType.PrivateKey);\n    }\n\n    public static String getPrivateKey(){\n        return privateKey;\n    }\n    public static String getPublicKey(){\n        return publicKey;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/SensitiveWordUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport cn.hutool.core.io.file.FileReader;\nimport cn.hutool.core.io.resource.ClassPathResource;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.stereotype.Component;\nimport toolgood.words.StringSearch;\n\nimport javax.annotation.PostConstruct;\nimport java.io.*;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Component\n@ConditionalOnClass(StringSearch.class)\npublic class SensitiveWordUtils {\n\n    private static StringSearch search;\n    String sensitiveWordFileName=\"sensitive_word.txt\";\n\n    @PostConstruct\n    private void init(){\n//\n        ClassPathResource classPathResource = new ClassPathResource(sensitiveWordFileName);\n        InputStream inputStream = classPathResource.getStream();\n        List<String> words = this.readLines(inputStream);\n        search = new StringSearch();\n        search.SetKeywords(words);\n    }\n\n    private List<String> readLines(InputStream inputStream){\n        InputStreamReader isr = new InputStreamReader(inputStream);//传入InputStream in　键盘录入对象 in\n        List<String> words=new ArrayList<>();\n        //为了提高效率,将字符串进行缓冲区技术高效操作.使用BufferedReader\n        BufferedReader bufr = new BufferedReader(isr);\n        String line = null;\n        try {\n            while ((line = bufr.readLine())!=null) {\n                words.add(line);\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }finally {\n            try {\n                bufr.close();\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return words;\n\n    }\n\n    public static String filter(String str){\n        return search.Replace(str,'*');\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/SpringContextUtils.java",
    "content": "package com.acimage.common.utils;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SpringContextUtils implements ApplicationContextAware {\n    /**\n     * spring的应用上下文\n     */\n    private static ApplicationContext applicationContext;\n\n    /**\n     * 初始化时将应用上下文设置进applicationContext\n     */\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        SpringContextUtils.applicationContext=applicationContext;\n    }\n\n    public static ApplicationContext getApplicationContext(){\n        return applicationContext;\n    }\n\n\n    public static Object getBean(String name) throws BeansException {\n        return applicationContext.getBean(name);\n    }\n\n\n    public static <T> T getBean(Class<T> beanClass) throws BeansException {\n        return applicationContext.getBean(beanClass);\n    }\n\n    /**\n     * 获取spring.profiles.active\n     * @return\n     */\n    public static String getProfile(){\n        return getApplicationContext().getEnvironment().getActiveProfiles()[0];\n    }\n\n    public static boolean isDev(){\n        String[] profiles=getApplicationContext().getEnvironment().getActiveProfiles();\n        for(String profile:profiles){\n            if(profile.startsWith(\"dev\")){\n                return true;\n            }\n        }\n        return false;\n    }\n}"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/AopUtils.java",
    "content": "package com.acimage.common.utils.common;\n\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.convert.Convert;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.reflect.MethodSignature;\n\nimport javax.validation.constraints.NotNull;\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\npublic class AopUtils {\n    public static Method methodOf(@NotNull JoinPoint joinPoint) {\n        MethodSignature signature = (MethodSignature) joinPoint.getSignature();\n        Method method;\n        try {\n            method = joinPoint.getTarget()\n                    .getClass()\n                    .getMethod(signature.getMethod().getName(),\n                            signature.getMethod().getParameterTypes());\n        } catch (NoSuchMethodException e) {\n            ExceptionUtils.printIfDev(e);\n            throw new RuntimeException(e);\n        }\n\n        return method;\n    }\n\n    public static boolean hasParameters(@NotNull JoinPoint joinPoint) {\n        Parameter[] parameters = methodOf(joinPoint).getParameters();\n        return parameters != null && parameters.length != 0;\n    }\n\n    public static <T extends Annotation> T annotationFrom(JoinPoint joinPoint, Class<T> annotation) {\n        Method method = methodOf(joinPoint);\n        return method.getAnnotation(annotation);\n    }\n\n    public static <T extends Annotation> List<T> paramAnnotationsFrom(JoinPoint joinPoint, Class<T> annotation) {\n        Parameter[] parameters = methodOf(joinPoint).getParameters();\n        List<T> annotationList=new ArrayList<>();\n        for(int i=0;i<parameters.length;i++){\n            if(parameters[i].isAnnotationPresent(annotation)){\n                annotationList.add(parameters[i].getAnnotation(annotation));\n            }\n        }\n        return annotationList;\n    }\n\n    public static <T extends Annotation, V> V annotatedArgOrArgFieldOf(@NotNull JoinPoint joinPoint, Class<T> annotation, Class<V> targetType) {\n\n        Parameter[] parameters = methodOf(joinPoint).getParameters();\n        Object[] args = joinPoint.getArgs();\n\n        Integer firstIndex = indexOfFirstAnnotatedParameter(parameters, annotation);\n        if (firstIndex != null) {\n            return Convert.convert(targetType, args[firstIndex]);\n        }\n\n        for(int i=0;i<parameters.length;i++){\n\n            Parameter parameter=parameters[i];\n            Field field = ReflectUtils.firstAnnotatedField(parameter.getType(), annotation);\n            if (field == null) {\n                continue;\n            }\n\n            String fieldName = field.getName();\n            Object result= BeanUtil.getFieldValue(args[i],fieldName);\n            return Convert.convert(targetType, result);\n\n        }\n\n        return null;\n    }\n\n    /**\n     * 返回首个被annotation注解的实参或实参成员变量\n     */\n    public static <T extends Annotation, V> List<V> annotatedArgsFrom(@NotNull JoinPoint joinPoint, Class<T> annotation, Class<V> targetType) {\n\n        Parameter[] parameters = methodOf(joinPoint).getParameters();\n        Object[] args = joinPoint.getArgs();\n        List<V> annotatedObjectList=new ArrayList<>();\n\n        for (int i = 0; i < parameters.length; i++) {\n            if (parameters[i].isAnnotationPresent(annotation)) {\n                annotatedObjectList.add(Convert.convert(targetType, args[i]));\n            }\n        }\n\n        return annotatedObjectList;\n    }\n\n    private static <T extends Annotation> Integer indexOfFirstAnnotatedParameter(Parameter[] parameters, Class<T> annotation) {\n        for (int i = 0; i < parameters.length; i++) {\n            if (parameters[i].isAnnotationPresent(annotation)) {\n                return i;\n            }\n        }\n        return null;\n    }\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/ArrayUtils.java",
    "content": "package com.acimage.common.utils.common;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class ArrayUtils {\n\n    public static Integer firstIndexOf(String[] strs, String targetStr){\n        for(int i=0;i<strs.length;i++){\n            if(strs[i].equals(targetStr)){\n                return i;\n            }\n        }\n        return null;\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/BeanUtils.java",
    "content": "package com.acimage.common.utils.common;\n\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.util.ReflectUtil;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\nimport java.lang.reflect.Method;\nimport java.util.*;\n\n\npublic class BeanUtils {\n    public static final String SET=\"set\";\n\n    public static Map<String, String> beanToFieldJsonMap(Object javaBean) {\n\n        Map<String, Object> map = BeanUtil.beanToMap(javaBean, false, true);\n        Map<String, String> fieldJsonMap = new HashMap<>();\n        Set<String> keys = map.keySet();\n        for (String key : keys) {\n            Object obj = map.get(key);\n            String json = JacksonUtils.writeValueAsString(obj);\n            fieldJsonMap.put(key, json);\n        }\n        return fieldJsonMap;\n    }\n\n    public static <T> T fieldJsonMapToBean(Map<String, String> fieldJsonMap, Class<T> clz) {\n        T instance=ReflectUtil.newInstance(clz);\n        List<String> fieldName = ReflectUtils.getFieldNames(clz);\n        Set<String> keys = fieldJsonMap.keySet();\n\n        for (String key : keys) {\n            if (fieldName.contains(key)) {\n                String json = fieldJsonMap.get(key);\n                //json转对象\n                Object obj = JacksonUtils.convert(json, ReflectUtil.getField(clz, key).getType());\n                //获取方法\n                String setMethodName = StringUtils.concatCapitalize(SET, key);\n                Method setMethod = ReflectUtil.getMethodByName(clz, setMethodName);\n                //调用方法，将属性set进去\n                ReflectUtil.invoke(instance, setMethod, obj);\n            }\n        }\n\n        return instance;\n    }\n\n    public static <T> T copyPropertiesTo(Object source,Class<T> targetType){\n        T target=ReflectUtil.newInstance(targetType);\n        BeanUtil.copyProperties(source,target,false);\n        return target;\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/FileUtils.java",
    "content": "package com.acimage.common.utils.common;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.http.HttpUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.*;\nimport java.net.URLEncoder;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\n@Slf4j\npublic class FileUtils {\n    public static void packageFiles(File[] files, File zipFile) throws IOException {\n        byte[] buffer = new byte[4096];\n        ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()));\n        for (int i = 0; i < files.length; i++) {\n            FileInputStream fis = new FileInputStream(files[i]);\n//            FileInputStream fis = new FileInputStream(files[i].getPath());\n            out.putNextEntry(new ZipEntry(files[i].getName()));\n            int len;\n            // 读入需要下载的文件的内容，打包到zip文件\n            while ((len = fis.read(buffer)) > 0) {\n                out.write(buffer, 0, len);\n            }\n            out.closeEntry();\n            fis.close();\n        }\n        out.close();\n\n    }\n\n    public static void downloadFileForClient(File file, HttpServletResponse response) throws IOException{\n        response.setCharacterEncoding(\"utf-8\");\n        // response.setContentType(\"application/octet-stream\");\n\n        // 以流的形式下载文件。\n        BufferedInputStream fis = new BufferedInputStream(Files.newInputStream(Paths.get(file.getPath())));\n        byte[] buffer = new byte[fis.available()];\n        fis.read(buffer);\n        fis.close();\n        // 清空response\n        response.reset();\n\n        OutputStream toClient = new BufferedOutputStream(response.getOutputStream());\n        response.setContentType(\"application/octet-stream\");\n        response.setHeader(\"Content-Disposition\", \"attachment;filename=\" + file.getName());\n        toClient.write(buffer);\n        toClient.flush();\n        toClient.close();\n    }\n\n    public static String formatOf(String fileName){\n        return StrUtil.subAfter(fileName,\".\",true);\n    }\n\n    public static String formatOf(MultipartFile multipartFile){\n        String fileName=multipartFile.getOriginalFilename();\n        return StrUtil.subAfter(fileName,\".\",true);\n    }\n\n    public static List<String> formatsOf(MultipartFile[] multipartFiles){\n        if(multipartFiles==null){\n            return null;\n        }else if(multipartFiles.length==0){\n            return new ArrayList<>();\n        }else{\n            List<String> formatList=new ArrayList<>();\n            for (MultipartFile multipartFile : multipartFiles) {\n                formatList.add(formatOf(multipartFile.getOriginalFilename()));\n            }\n            return formatList;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/JacksonUtils.java",
    "content": "package com.acimage.common.utils.common;\n\n\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.extern.slf4j.Slf4j;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n@Slf4j\npublic class JacksonUtils {\n    private static final ObjectMapper mapper = new ObjectMapper();\n    static {\n        //设置忽略空字段\n        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);\n    }\n\n    public static String writeValueAsString(Object object) {\n        String json = null;\n        try {\n            json = mapper.writeValueAsString(object);\n        } catch (JsonProcessingException e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"对象序列化成json错误 对象[{}] error:{}\", object,e.getMessage());\n            throw new RuntimeException(e);\n        }\n        return json;\n    }\n\n    public static <T> List<T> getList(@Nullable String json, Class<T> ofType) {\n        if (json == null) {\n            return null;\n        }\n        JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, ofType);\n        try {\n            return mapper.readValue(json, javaType);\n        } catch (JsonProcessingException e) {\n            log.error(\"数据反序列化异常 json：{} type:{} error:{}\", json,javaType,e.getMessage());\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T> T convert(@Nullable String json, Class<T> targetType) {\n        if (json == null) {\n            return null;\n        }\n\n        try {\n            return mapper.readValue(json, targetType);\n        } catch (JsonProcessingException e) {\n            log.error(\"数据反序列化异常 json：{} type:{} error:{}\", json,targetType,e.getMessage());\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/ListUtils.java",
    "content": "package com.acimage.common.utils.common;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Pair;\nimport org.springframework.data.redis.core.ZSetOperations;\n\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\npublic class ListUtils {\n    public static <T, V> List<V> extract(Function<T, V> attribute, List<T> list) {\n        if (list == null) {\n            return null;\n        }\n        return list.stream().map(attribute).collect(Collectors.toList());\n    }\n\n    public static <T, V> List<T> extractKeyFrom(List<Pair<T, V>> pairList) {\n        List<T> keyList = new ArrayList<>();\n        for (Pair<T, V> item : pairList) {\n            keyList.add(item.getKey());\n        }\n        return keyList;\n    }\n\n    public static <T> List<T> getListFrom(Set<T> set) {\n        if (set == null) {\n            return null;\n        }\n        List<T> list = new ArrayList<>(set.size());\n        return null;\n    }\n\n    public static List<Long> differenceSetOf(List<Long> list1, List<Long> list2) {\n        if (list2 == null || list2.size() == 0) {\n            return list1;\n        } else if (list1 == null || list1.size() == 0) {\n            return list1;\n        }\n\n        List<Long> list1Copy = new ArrayList<>(list1);\n        List<Long> list2Copy = new ArrayList<>(list2);\n\n        Comparator<Long> longComparator = Comparator.naturalOrder();\n\n        list1Copy.sort(longComparator);\n        list2Copy.sort(longComparator);\n        int ptr1 = 0, ptr2 = 0;\n        List<Long> differenceList = new ArrayList<>();\n        while (ptr1 < list1Copy.size() && ptr2 < list2Copy.size()) {\n            if (list1Copy.get(ptr1) < list2Copy.get(ptr2)) {\n                differenceList.add(list1Copy.get(ptr1));\n                ptr1++;\n            } else if (list1Copy.get(ptr1) > list2Copy.get(ptr2)) {\n                ptr2++;\n            } else if (list1Copy.get(ptr1).equals(list2Copy.get(ptr2))) {\n                ptr1++;\n                ptr2++;\n            }\n        }\n        if (ptr2 == list2Copy.size()) {\n            while (ptr1 < list1Copy.size()) {\n                differenceList.add(list1Copy.get(ptr1));\n                ptr1++;\n            }\n        }\n        return differenceList;\n    }\n\n    public static List<String> differenceSetOfV2(List<String> list1, List<String> list2) {\n        if (list2 == null || list2.size() == 0) {\n            return list1;\n        } else if (list1 == null || list1.size() == 0) {\n            return list1;\n        }\n\n        List<String> list1Copy = new ArrayList<>(list1);\n        List<String> list2Copy = new ArrayList<>(list2);\n\n        Comparator<String> stringComparator = Comparator.naturalOrder();\n\n        list1Copy.sort(stringComparator);\n        list2Copy.sort(stringComparator);\n        int ptr1 = 0, ptr2 = 0;\n        List<String> differenceList = new ArrayList<>();\n        while (ptr1 < list1Copy.size() && ptr2 < list2Copy.size()) {\n            if (list1Copy.get(ptr1).compareTo(list2Copy.get(ptr2))<0 ) {\n                differenceList.add(list1Copy.get(ptr1));\n                ptr1++;\n            } else if (list1Copy.get(ptr1).compareTo(list2Copy.get(ptr2))>0 ) {\n                ptr2++;\n            } else if (list1Copy.get(ptr1).equals(list2Copy.get(ptr2))) {\n                ptr1++;\n                ptr2++;\n            }\n        }\n        if (ptr2 == list2Copy.size()) {\n            while (ptr1 < list1Copy.size()) {\n                differenceList.add(list1Copy.get(ptr1));\n                ptr1++;\n            }\n        }\n        return differenceList;\n    }\n\n    public static <T> boolean isEmpty(List<T> list) {\n        return list == null || list.size() == 0;\n    }\n\n    public static List<Long> convertToLongList(List<String> list) {\n        List<Long> longList = new ArrayList<>();\n        for (String item : list) {\n            longList.add(Long.parseLong(item));\n        }\n        return longList;\n    }\n\n    public static List<Pair<Long, Integer>> getListInDescOrderFrom(Set<ZSetOperations.TypedTuple<String>> valueAnsScoreSet) {\n        List<Pair<Long, Integer>> valueAnsScoreList = new ArrayList<>();\n        for (ZSetOperations.TypedTuple<String> item : valueAnsScoreSet) {\n            Pair<Long, Integer> pair = new Pair<>(Long.parseLong(item.getValue()), item.getScore().intValue());\n            valueAnsScoreList.add(pair);\n        }\n\n        return valueAnsScoreList;\n    }\n\n    public static List<Pair<Long, Double>> convertToLongDoublePairFrom(List<Pair<String, Double>> pairList) {\n\n        List<Pair<Long, Double>> newList = new ArrayList<>();\n        for (Pair<String, Double> item : pairList) {\n            newList.add(new Pair<>(Long.parseLong(item.getKey()), item.getValue()));\n        }\n\n        return newList;\n    }\n\n    public static <T, V> List<V> convert(List<T> sourceList, Class<V> targetType) {\n        List<V> resultList = new ArrayList<>();\n        if (sourceList == null) {\n            return null;\n        } else if (sourceList.size() == 0) {\n            return new ArrayList<>();\n        }\n\n        for (T item : sourceList) {\n            resultList.add(Convert.convert(targetType, item));\n        }\n        return resultList;\n    }\n\n    public static <T> List<T> removeRepeat(List<T> list){\n        Set<T> set=new HashSet<>(list);\n        return new ArrayList<>(set);\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/PageUtils.java",
    "content": "package com.acimage.common.utils.common;\n\npublic class PageUtils {\n    public static int startIndexOf(int pageNo, int pageSize) {\n        return (pageNo - 1) * pageSize;\n    }\n\n    public static int endIndexOf(int pageNo, int pageSize) {\n        return pageNo * pageSize - 1;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/PairUtils.java",
    "content": "package com.acimage.common.utils.common;\n\nimport cn.hutool.core.lang.Pair;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class PairUtils {\n\n    public static <T, R> List<Pair<T, R>> combine(@NotNull List<T> list1, @NotNull List<R> list2) {\n        if (list1 == null) {\n            throw new IllegalArgumentException(\"参数1为空\");\n        }else if (list2 == null) {\n            throw new IllegalArgumentException(\"参数2为空\");\n        }else if (list1.size() != list2.size()) {\n            throw new IllegalArgumentException(String.format(\"参数1长度[%s] 参数2长度[%s] 参数长度不一致！\", list1.size(), list2.size()));\n        }else if (list1.size() == 0) {\n            return new ArrayList<>();\n        }\n\n        int len = list1.size();\n        List<Pair<T, R>> pairList = new ArrayList<>(len);\n        for (int i = 0; i < len; i++) {\n            pairList.add(new Pair<>(list1.get(i), list2.get(i)));\n        }\n        return pairList;\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/ReflectUtils.java",
    "content": "package com.acimage.common.utils.common;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport cn.hutool.core.convert.Convert;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.*;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\npublic class ReflectUtils {\n\n    public static Class<?>[] genericsOfReturnType(Method method) throws ClassNotFoundException {\n        Class<?>[] generics = new Class<?>[]{};\n\n        Type genericReturnType = method.getGenericReturnType();\n        if (genericReturnType instanceof ParameterizedType ) {\n            ParameterizedType parameterizedType=(ParameterizedType) genericReturnType;\n            Type[] types = parameterizedType.getActualTypeArguments();\n            generics = new Class<?>[types.length];\n            for (int i = 0; i < types.length; i++) {\n\n                if (types[i] instanceof WildcardType) {\n                    generics[i] = Object.class;\n                } else {\n                    generics[i] = Class.forName(types[i].getTypeName());\n                }\n\n            }\n        }\n        return generics;\n    }\n\n    public static List<String> getFieldNames(Class<?> clz){\n        Field[] fields=clz.getDeclaredFields();\n        List<String> fileNameList=new ArrayList<>();\n        for(Field field:fields){\n            fileNameList.add(field.getName());\n        }\n        return fileNameList;\n    }\n\n    public static Object getAnnotatedFiled(Object obj,Class<? extends Annotation> annotation){\n        Field field = ReflectUtils.firstAnnotatedField(obj.getClass(), annotation);\n        if (field == null) {\n            return null;\n        }\n\n        String fieldName = field.getName();\n        return BeanUtil.getFieldValue(obj,fieldName);\n    }\n\n    public static <T extends Annotation> Field firstAnnotatedField(Class<?> objectClass, Class<T> annotation) {\n        Field[] fields = objectClass.getDeclaredFields();\n        for (Field field : fields) {\n            boolean isAnnotated = field.isAnnotationPresent(annotation);\n            if (isAnnotated) {\n                return field;\n            }\n        }\n        return null;\n    }\n\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/common/StringUtils.java",
    "content": "package com.acimage.common.utils.common;\n\nimport cn.hutool.core.collection.CollectionUtil;\n\nimport javax.annotation.Nullable;\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\n\npublic class StringUtils {\n\n    public static String concatForNotNull(@Nullable String separator, @Nullable List<String> stringList) {\n        if (CollectionUtil.isEmpty(stringList)) {\n            return \"\";\n        }\n\n        String newSeparator = separator == null ? \"\" : separator;\n        //拼接\n        StringBuilder stringBuilder = new StringBuilder();\n        for (int i = 0; i < stringList.size(); i++) {\n            stringBuilder.append(stringList.get(i));\n            if (i != stringList.size() - 1) {\n                stringBuilder.append(newSeparator);\n            }\n        }\n\n        return stringBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/minio/MinioProperties.java",
    "content": "package com.acimage.common.utils.minio;\n\nimport io.minio.MinioClient;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@ConditionalOnClass(MinioClient.class)\n@ConfigurationProperties(prefix = \"minio\")\npublic class MinioProperties {\n    private String accessKey;\n    private String secretKey;\n    private String bucket;\n    private String endpoint;\n\n    public void setAccessKey(String accessKey) {\n        this.accessKey = accessKey;\n    }\n\n    public String getBucket() {\n        return bucket;\n    }\n\n    public String getEndpoint() {\n        return endpoint;\n    }\n\n    public void setSecretKey(String secretKey) {\n        this.secretKey = secretKey;\n    }\n\n    public void setBucket(String bucket) {\n        this.bucket = bucket;\n    }\n\n    public void setEndpoint(String endpoint) {\n        this.endpoint = endpoint;\n    }\n\n    @Bean\n    public MinioClient minioClient(){\n        return MinioClient.builder()\n                .endpoint(endpoint)\n                .credentials(accessKey, secretKey)\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/minio/MinioUtils.java",
    "content": "package com.acimage.common.utils.minio;\n\nimport cn.hutool.http.HttpUtil;\nimport com.acimage.common.utils.ExceptionUtils;\nimport io.minio.*;\nimport io.minio.messages.DeleteObject;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.annotation.Nullable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n@Slf4j\n@Component\n@ConditionalOnClass(MinioClient.class)\npublic class MinioUtils {\n    @Autowired\n    MinioClient minioClient;\n    @Autowired\n    MinioProperties minioProperties;\n\n    public String upload(MultipartFile multipartFile, String url) {\n        InputStream in = null;\n        try {\n            in = multipartFile.getInputStream();\n            minioClient.putObject(PutObjectArgs.builder()\n                    .bucket(minioProperties.getBucket())\n                    .object(url)\n                    .stream(in, in.available(), -1)\n                    .contentType(multipartFile.getContentType())\n                    .build()\n            );\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(e.getMessage());\n        } finally {\n            if (in != null) {\n                try {\n                    in.close();\n                } catch (IOException e) {\n                    log.error(e.getMessage());\n                    ExceptionUtils.printIfDev(e);\n                }\n            }\n        }\n        return String.format(\"/%s/%s\", minioProperties.getBucket(), url);\n    }\n\n    public String upload(InputStream is, String url, String contentType) {\n\n        try {\n            minioClient.putObject(PutObjectArgs.builder()\n                    .bucket(minioProperties.getBucket())\n                    .object(url)\n                    .contentType(contentType)\n                    .stream(is, is.available(), -1)\n                    .build()\n            );\n        } catch (Exception e) {\n            log.error(e.getMessage());\n        } finally {\n            if (is != null) {\n                try {\n                    is.close();\n                } catch (IOException e) {\n                    log.error(e.getMessage());\n                    ExceptionUtils.printIfDev(e);\n                }\n            }\n        }\n        return String.format(\"/%s/%s\", minioProperties.getBucket(), url);\n    }\n\n    public void downloadTo(String url, String destSrc) {\n        try {\n            String slashBucket = String.format(\"/%s\", minioProperties.getBucket());\n            boolean isInnerUrl = url.startsWith(slashBucket);\n            //不是站内图片则不下载\n            if (!isInnerUrl) {\n                return;\n            }\n            String innerUrl = url.substring(slashBucket.length());\n            minioClient.downloadObject(\n                    DownloadObjectArgs.builder()\n                            .bucket(minioProperties.getBucket())\n                            .object(innerUrl)\n                            .filename(destSrc)\n                            .build());\n        } catch (Exception e) {\n            log.error(e.getMessage());\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void download(String url, String toPath) {\n//        String encodedUrl = null;\n//        try {\n//            encodedUrl = URLEncoder.encode(url, \"utf-8\").replace(\"+\", \"%20\");\n//        } catch (UnsupportedEncodingException e) {\n//            e.printStackTrace();\n//            log.error(\"url编码失败 error：{}\", e.getLocalizedMessage());\n//        }\n        String publicUrl;\n        if (url.startsWith(\"/\")) {\n            publicUrl = minioProperties.getEndpoint() + \"/\" + url;\n        } else {\n            publicUrl = minioProperties.getEndpoint() + url;\n        }\n        HttpUtil.downloadFile(publicUrl, new File(toPath));//下载\n    }\n\n\n    public void deleteFile(String totalUrl) {\n        String slashBucket = String.format(\"/%s\", minioProperties.getBucket());\n        boolean isInnerUrl = totalUrl.startsWith(slashBucket);\n        if (!isInnerUrl) {\n            return;\n        }\n        String innerUrl = totalUrl.substring(slashBucket.length());\n        try {\n            minioClient.removeObject(RemoveObjectArgs.builder()\n                    .bucket(minioProperties.getBucket())\n                    .object(innerUrl)\n                    .build()\n            );\n        } catch (Exception e) {\n            log.error(e.getMessage());\n            ExceptionUtils.printIfDev(e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void deleteFiles(List<String> urls) {\n        try {\n            List<DeleteObject> deleteObjectList = new ArrayList<>();\n            for (String url : urls) {\n                deleteObjectList.add(new DeleteObject(url));\n            }\n            minioClient.removeObjects(RemoveObjectsArgs.builder()\n                    .bucket(minioProperties.getBucket())\n                    .objects(deleteObjectList)\n                    .build());\n        } catch (Exception e) {\n            log.error(e.getMessage());\n            ExceptionUtils.printIfDev(e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public String generateBaseUrl(@Nullable String prefix, Date uploadTime, String suffix) {\n        String formatPattern = \"yyyy/MM/dd\";\n\n        String newPrefix = prefix == null ? \"\" : prefix + \"/\";\n        SimpleDateFormat formatter = new SimpleDateFormat(formatPattern);\n        return newPrefix + formatter.format(uploadTime) + \"/\" + suffix;\n    }\n\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/redis/RedisLuaUtils.java",
    "content": "package com.acimage.common.utils.redis;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.data.redis.core.script.DefaultRedisScript;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n@Component\n@Slf4j\npublic class RedisLuaUtils {\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    private static final ObjectMapper mapper = new ObjectMapper();\n\n    private final static DefaultRedisScript<Long> incrementIfPresent = new DefaultRedisScript<>();\n    private final static DefaultRedisScript<Long> ifpForZSet = new DefaultRedisScript<>();\n    private final static DefaultRedisScript<String> getAndCombineAndDelete = new DefaultRedisScript<>();\n\n    private final static DefaultRedisScript<Long> ifpForFieldKey = new DefaultRedisScript<>();\n\n    private final static DefaultRedisScript<List> requestLimit = new DefaultRedisScript<>();\n\n    private final static DefaultRedisScript<Boolean> sfpForFieldKey = new DefaultRedisScript<>();\n\n\n    @PostConstruct\n    public void init() {\n        //设置忽略空字段\n        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);\n\n        // 指定脚本文件\n        incrementIfPresent.setLocation(new ClassPathResource(\"lua/incrementIfPresent.lua\"));\n        // 指定返回值\n        incrementIfPresent.setResultType(Long.class);\n\n        getAndCombineAndDelete.setLocation(new ClassPathResource(\"lua/getAndCombineAndDelete.lua\"));\n        getAndCombineAndDelete.setResultType(String.class);\n\n        ifpForZSet.setLocation(new ClassPathResource(\"lua/incrementIfPresentForZSet.lua\"));\n        ifpForZSet.setResultType(Long.class);\n\n        ifpForFieldKey.setLocation(new ClassPathResource(\"lua/incrementIfPresentForFieldKey.lua\"));\n        ifpForFieldKey.setResultType(Long.class);\n\n        requestLimit.setLocation(new ClassPathResource(\"lua/requestLimit.lua\"));\n        requestLimit.setResultType(List.class);\n\n        sfpForFieldKey.setLocation(new ClassPathResource(\"lua/setIfPresentForFieldKey.lua\"));\n        sfpForFieldKey.setResultType(Boolean.class);\n    }\n\n\n    public Long incrementIfPresent(String key, long increment) {\n        return stringRedisTemplate.opsForValue().getOperations()\n                .execute(incrementIfPresent, Collections.singletonList(key), Long.toString(increment));\n    }\n\n    /**\n     * 如果keyForBase存在，则将redis中keyForIncrement的值增加到keyForBase中\n     * 否则获取并删除keyForIncrement\n     * @return keyForIncrement对应的值\n     */\n    public Long getAndCombineAndDelete(String keyForIncrement, String fieldKeyForBase, String fieldKey) {\n        String result = stringRedisTemplate.opsForValue().getOperations()\n                .execute(getAndCombineAndDelete, Arrays.asList(keyForIncrement, fieldKeyForBase), fieldKey);\n        if (result == null) {\n            return null;\n        }\n        return Long.parseLong(result);\n    }\n\n\n    public Long incrementIfPresentForZSet(String key, String value, long increment) {\n        return stringRedisTemplate.opsForValue().getOperations()\n                .execute(ifpForZSet, Collections.singletonList(key), value, Long.toString(increment));\n    }\n\n    public Long incrementIfPresentForFieldKey(String key, String filedKey, long increment) {\n        return stringRedisTemplate.opsForValue().getOperations()\n                .execute(ifpForFieldKey, Collections.singletonList(key), filedKey, Long.toString(increment));\n    }\n\n    public List<Long> requestLimitScript(List<String> keys, List<Long> limitTimes, List<Long> expireSeconds, List<Long> penaltyTimes) {\n        int len = 3 * keys.size();\n        Object[] args = new Object[len];\n\n        int i = 0;\n        for (Long limitTime : limitTimes) {\n            args[i] = limitTime.toString();\n            i++;\n        }\n        for (Long expireSecond : expireSeconds) {\n            args[i] = expireSecond.toString();\n            i++;\n        }\n        for (Long penaltyTime : penaltyTimes) {\n            args[i] = penaltyTime.toString();\n            i++;\n        }\n\n        return stringRedisTemplate.execute(requestLimit, keys, args);\n    }\n\n    public Boolean setIfPresentForFieldKey(String key, String filedKey, String value) {\n        return stringRedisTemplate.execute(sfpForFieldKey, Collections.singletonList(key), filedKey, value);\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/utils/redis/RedisUtils.java",
    "content": "package com.acimage.common.utils.redis;\n\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.common.utils.SpringContextUtils;\nimport com.acimage.common.utils.common.BeanUtils;\nimport com.acimage.common.utils.common.JacksonUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.acimage.common.global.exception.BusinessException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.data.redis.core.ZSetOperations;\nimport org.springframework.stereotype.Component;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\n\n@Component\n@Slf4j\npublic class RedisUtils {\n    private static final ObjectMapper mapper = new ObjectMapper();\n\n    static {\n        //设置忽略空字段\n        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);\n    }\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n    @Autowired\n    private RedisLuaUtils redisLuaUtils;\n\n    @Deprecated\n    public <T> void setListAsString(String key, List<T> objectList, long timeout, TimeUnit unit) {\n        String json = JacksonUtils.writeValueAsString(objectList);\n        stringRedisTemplate.opsForValue().set(key, json, timeout, unit);\n\n    }\n\n    public <T> List<T> getListFromString(String key, Class<T> type) {\n        String json = stringRedisTemplate.opsForValue().get(key);\n        return JacksonUtils.getList(json, type);\n    }\n\n    public Object getFromString(String key, Class<?> parameterized, Class<?>... parametrizedTypes) {\n        JavaType javaType = mapper.getTypeFactory().constructParametricType(parameterized, parametrizedTypes);\n        String json = stringRedisTemplate.opsForValue().get(key);\n        if (json == null) {\n            return null;\n        }\n        Object result;\n        try {\n            //错误写法：result=mapper.readValue(json,new TypeReference<List<T>>() { });\n            result = mapper.readValue(json, javaType);\n        } catch (JsonProcessingException e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"redis数据反序列化异常,key：{} json：{}\", key, json);\n            throw new BusinessException(\"服务器出错了~~请稍后重试(>m<)\");\n        }\n        return result;\n    }\n\n    public void setObjectJson(String key, @NotNull Object obj, long timeout, TimeUnit unit) {\n        if (obj == null) {\n            return;\n        }\n        String json = JacksonUtils.writeValueAsString(obj);\n        stringRedisTemplate.opsForValue().set(key, json, timeout, unit);\n    }\n\n    public void setObjectJson(String key, @NotNull Object obj) {\n        if (obj == null) {\n            return;\n        }\n        String json = JacksonUtils.writeValueAsString(obj);\n        stringRedisTemplate.opsForValue().set(key, json);\n    }\n\n    public <T> T getObjectFromString(String key, Class<T> targetType) {\n        String json = stringRedisTemplate.opsForValue().get(key);\n        return JacksonUtils.convert(json, targetType);\n    }\n\n    /**\n     * @param targetType 仅支持Long、Double、Integer、String、Date，使用Date时set进去的数据需要是毫秒数\n     */\n    public <T> T getForString(String key, Class<T> targetType) {\n        String str = stringRedisTemplate.opsForValue().get(key);\n        if (str == null) {\n            return null;\n        }\n        if (targetType == Date.class) {\n            Long millis = Long.parseLong(str);\n            return Convert.convert(targetType, millis);\n        }\n        return Convert.convert(targetType, str);\n    }\n\n    public String getForString(String key) {\n        return stringRedisTemplate.opsForValue().get(key);\n    }\n\n    public List<String> multiGetForString(List<String> keys) {\n        return stringRedisTemplate.opsForValue().multiGet(keys);\n    }\n\n    public void setAsString(String key, Object value) {\n        stringRedisTemplate.opsForValue().set(key, value.toString());\n    }\n\n    public void setAsString(String key, String value, long timeout, TimeUnit timeUnit) {\n        stringRedisTemplate.opsForValue().set(key, value, timeout, timeUnit);\n    }\n\n    public Long increment(String key, long increment) {\n        return stringRedisTemplate.opsForValue().increment(key, increment);\n    }\n\n    /**\n     * redis6.2版本之后才支持\n     *\n     * @param key\n     */\n    public String getAndDeleteForString(String key) {\n        return stringRedisTemplate.opsForValue().getAndDelete(key);\n    }\n\n    public String getAndExpire(String key, long timeout, TimeUnit timeUnit) {\n        return stringRedisTemplate.opsForValue().getAndExpire(key, timeout, timeUnit);\n    }\n\n    public void setIfPresent(String key, String value) {\n        stringRedisTemplate.opsForValue().setIfPresent(key, value);\n    }\n\n    public void setIfPresent(String key, String value, long timeout, TimeUnit timeUnit) {\n        stringRedisTemplate.opsForValue().setIfPresent(key, value, timeout, timeUnit);\n    }\n\n    public Long incrementIfPresent(String key, long increment) {\n        return redisLuaUtils.incrementIfPresent(key, increment);\n    }\n\n    public Boolean setIfPresentForFieldKey(String key, String filedKey, String value) {\n        return redisLuaUtils.setIfPresentForFieldKey(key, filedKey, value);\n    }\n\n    public Long getAndCombineAndDelete(String keyForIncrement, String hashKeyForBase, String field) {\n        return redisLuaUtils.getAndCombineAndDelete(keyForIncrement, hashKeyForBase, field);\n    }\n\n    public void setObjectForHash(String key, Object javaBean, long timeOut, TimeUnit timeUnit) {\n        if (javaBean == null) {\n            return;\n        }\n        Map<String, String> fieldJsonMap = BeanUtils.beanToFieldJsonMap(javaBean);\n        stringRedisTemplate.opsForHash().putAll(key, fieldJsonMap);\n        stringRedisTemplate.expire(key, timeOut, timeUnit);\n    }\n\n    public <T> T getObjectForHash(String key, Class<T> targetType) {\n        Map<Object, Object> fieldJsonMap = stringRedisTemplate.opsForHash().entries(key);\n        if (fieldJsonMap.size() == 0) {\n            return null;\n        }\n        Map<String, String> fieldJsonStringMap = new HashMap<>();\n        for (Object hashKey : fieldJsonMap.keySet()) {\n            fieldJsonStringMap.put(hashKey.toString(), fieldJsonMap.get(hashKey).toString());\n        }\n        return BeanUtils.fieldJsonMapToBean(fieldJsonStringMap, targetType);\n    }\n\n    public Boolean delete(String key) {\n        return stringRedisTemplate.delete(key);\n    }\n\n    public Long delete(List<String> key) {\n        return stringRedisTemplate.delete(key);\n    }\n\n    public Boolean expire(String key, long timeout, TimeUnit timeUnit) {\n        return stringRedisTemplate.expire(key, timeout, timeUnit);\n    }\n\n    public Long getExpire(String key, TimeUnit timeUnit) {\n        return stringRedisTemplate.getExpire(key, timeUnit);\n    }\n\n    public Long addForHyperLogLog(String key, String... values) {\n        return stringRedisTemplate.opsForHyperLogLog().add(key, values);\n    }\n\n    public void deleteForHyperLogLog(String key) {\n        stringRedisTemplate.opsForHyperLogLog().delete(key);\n    }\n\n    public Long sizeForHyperLogLog(String key) {\n        return stringRedisTemplate.opsForHyperLogLog().size(key);\n    }\n\n\n    public Long addForSet(String key, Object value) {\n        return stringRedisTemplate.opsForSet().add(key, value.toString());\n    }\n\n    public Boolean isMemberForSet(String key, String value) {\n        return stringRedisTemplate.opsForSet().isMember(key, value);\n    }\n\n    public Long removeForSet(String key, String... values) {\n        return stringRedisTemplate.opsForSet().remove(key, values);\n    }\n\n    public <T> Long removeForSet(String key, List<T> valueList) {\n        if (CollectionUtil.isEmpty(valueList)) {\n            return 0L;\n        }\n        Object[] values = ListUtils.convert(valueList, String.class).toArray(new String[valueList.size()]);\n        return stringRedisTemplate.opsForSet().remove(key, values);\n    }\n\n    public <T> List<T> membersForSet(String key, Class<T> targetType) {\n        Set<String> members = stringRedisTemplate.opsForSet().members(key);\n        if (members == null) {\n            return null;\n        }\n\n        List<T> result = new ArrayList<>();\n        for (String member : members) {\n            result.add(Convert.convert(targetType, member));\n        }\n        return result;\n    }\n\n    public Boolean addForZSet(String key, String value, double score) {\n        return stringRedisTemplate.opsForZSet().add(key, value, score);\n    }\n\n    public Double scoreForZSet(String key, String value) {\n        return stringRedisTemplate.opsForZSet().score(key, value);\n    }\n\n    public Long sizeForZSet(String key) {\n        return stringRedisTemplate.opsForZSet().size(key);\n    }\n\n    public Long removeForZSet(String key, Object toStringValue) {\n        return stringRedisTemplate.opsForZSet().remove(key, toStringValue.toString());\n    }\n\n    public List<String> randomMembersForZSet(String key, int count) {\n        if (SpringContextUtils.isDev()) {\n            //randomMembers 在redis 6.2之后才有，我开发环境redis是windows 3.x版本，先自己实现一个\n            Long sizeLong = stringRedisTemplate.opsForZSet().size(key);\n            if (sizeLong == null) {\n                return new ArrayList<>();\n            } else if (sizeLong <= 0) {\n                return new ArrayList<>();\n            }\n            int size = sizeLong.intValue();\n            Random random = new Random(System.currentTimeMillis());\n            List<String> resultList = new ArrayList<>();\n            for (int i = 0; i < count; i++) {\n                int index = random.nextInt(size);\n                Set<String> set = stringRedisTemplate.opsForZSet().reverseRange(key, index, index);\n                String idString = null;\n                if (!CollectionUtil.isEmpty(set)) {\n                    for (String item : set) {\n                        idString = item;\n                    }\n                }\n                if (idString != null) {\n                    resultList.add(idString);\n                }\n            }\n            return resultList;\n        } else {\n            return stringRedisTemplate.opsForZSet().randomMembers(key, count);\n        }\n    }\n\n    public Long incrementIfPresentForZSet(String key, String value, long increment) {\n        return redisLuaUtils.incrementIfPresentForZSet(key, value, increment);\n    }\n\n    public Set<String> reverseRangeForZSet(String key, int start, int end) {\n        return stringRedisTemplate.opsForZSet().reverseRange(key, start, end);\n    }\n\n    public Long incrementIfPresentForFieldKey(String key, String hashKey, long increment) {\n        return redisLuaUtils.incrementIfPresentForFieldKey(key, hashKey, increment);\n    }\n\n\n    public List<Pair<String, Double>> reverseRangeWithScoreForZSet(String key, int start, int end) {\n        Set<ZSetOperations.TypedTuple<String>> valueAnsScoreSet = stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);\n        if (valueAnsScoreSet == null) {\n            return new ArrayList<>();\n        }\n\n        List<Pair<String, Double>> valueAndScoreList = new ArrayList<>();\n        for (ZSetOperations.TypedTuple<String> item : valueAnsScoreSet) {\n            Pair<String, Double> pair = new Pair<>(item.getValue(), item.getScore());\n            valueAndScoreList.add(pair);\n        }\n\n        valueAndScoreList.sort((next, current) -> current.getValue().compareTo(next.getValue()));\n        return valueAndScoreList;\n    }\n\n    public <T, V> List<Pair<T, V>> reverseRangeWithScoreForZSet(String key, int start, int end, Class<T> valueType, Class<V> scoreType) {\n        Set<ZSetOperations.TypedTuple<String>> valueAnsScoreSet = stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);\n        if (valueAnsScoreSet == null) {\n            return new ArrayList<>();\n        }\n\n        List<Pair<T, V>> valueAndScoreList = new ArrayList<>();\n        for (ZSetOperations.TypedTuple<String> item : valueAnsScoreSet) {\n            T value = Convert.convert(valueType, item.getValue());\n            Object newScore = item.getValue();\n            if (scoreType == Date.class) {\n                assert newScore != null;\n                newScore = Long.parseLong(newScore.toString());\n            }\n\n            V score = Convert.convert(scoreType, newScore);\n            Pair<T, V> pair = new Pair<>(value, score);\n            valueAndScoreList.add(pair);\n        }\n        return valueAndScoreList;\n    }\n\n    public List<Long> requestLimitScript(List<String> keys, List<Long> limitTimes, List<Long> expireSeconds, List<Long> penaltyTimes) {\n        return redisLuaUtils.requestLimitScript(keys, limitTimes, expireSeconds, penaltyTimes);\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/web/exceptionhandler/ArgumentValidateExceptionHandler.java",
    "content": "package com.acimage.common.web.exceptionhandler;\n\n\nimport com.acimage.common.result.Result;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.common.utils.SpringContextUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.validation.BindException;\nimport org.springframework.validation.FieldError;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport java.util.List;\nimport java.util.Set;\n\n\n@Slf4j\n@RestControllerAdvice\npublic class ArgumentValidateExceptionHandler {\n    /**\n     * 单参数校验失败\n     */\n    @ExceptionHandler(value = {ConstraintViolationException.class})\n    public Result doConstraintViolationException(ConstraintViolationException ex) {\n        ExceptionUtils.printIfDev(ex);\n        log.warn(ex.getMessage());\n        //获取报错信息\n        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();\n        StringBuilder message = new StringBuilder();\n\n        int i = 0;\n        for (ConstraintViolation<?> violation : violations) {\n            message.append(violation.getMessage());\n            i++;\n            if (i < violations.size() - 1) {\n                message.append(\";\");\n            }\n        }\n        //记录日志\n        logWarnMessage(ex, message.toString());\n        return Result.fail(message.toString());\n    }\n\n    @ExceptionHandler(value = {MethodArgumentNotValidException.class})\n    public Result doMethodArgumentNotValidException(MethodArgumentNotValidException ex) {\n        ExceptionUtils.printIfDev(ex);\n        //获取报错信息\n        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();\n        StringBuilder logMessage = new StringBuilder();\n        StringBuilder userMessage = new StringBuilder();\n\n        for (int i = 0; i < fieldErrors.size(); i++) {\n            FieldError fieldError = fieldErrors.get(i);\n            logMessage.append(String.format(\"参数：%s 值：%s  错误：%s\",\n                    fieldError.getField(),\n                    fieldError.getRejectedValue(),\n                    fieldError.getDefaultMessage()));\n            userMessage.append(fieldError.getDefaultMessage());\n            if (i < fieldErrors.size() - 1) {\n                logMessage.append(\";\");\n                userMessage.append(\";\");\n            }\n        }\n        //记录日志\n        logWarnMessage(ex, logMessage.toString());\n        return Result.fail(userMessage.toString());\n    }\n\n    @ExceptionHandler(value = {BindException.class})\n    public Result doBindException(BindException ex) {\n        ExceptionUtils.printIfDev(ex);\n        //获取报错信息\n        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();\n\n        StringBuilder logMessage = new StringBuilder();\n        StringBuilder userMessage = new StringBuilder();\n\n        for (int i = 0; i < fieldErrors.size(); i++) {\n            FieldError fieldError = fieldErrors.get(i);\n            logMessage.append(String.format(\"参数：%s 值：%s  错误：%s\",\n                    fieldError.getField(),\n                    fieldError.getRejectedValue(),\n                    fieldError.getDefaultMessage()));\n\n            userMessage.append(fieldError.getDefaultMessage());\n\n            if (i < fieldErrors.size() - 1) {\n                logMessage.append(\";\");\n                userMessage.append(\";\");\n            }\n        }\n        //记录日志\n        logWarnMessage(ex, logMessage.toString());\n        return Result.fail(userMessage.toString());\n    }\n\n    private void logWarnMessage(Exception ex, String message) {\n        String exceptionName = ex.getClass().getSimpleName();\n        log.warn(\"用户：{} {} from {}\", UserContext.getUsername(), message, exceptionName);\n    }\n}\n\n\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/web/exceptionhandler/GlobalExceptionHandler.java",
    "content": "package com.acimage.common.web.exceptionhandler;\n\n\n\nimport com.acimage.common.result.Result;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.utils.ExceptionUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\n\n\n@Slf4j\n@RestControllerAdvice\npublic class GlobalExceptionHandler {\n\n\n\n    @ExceptionHandler(value={BusinessException.class})\n    public Result<?> doBusinessException(BusinessException ex){\n        return Result.fail(ex.getMsg());\n    }\n\n    @ExceptionHandler(value={MaxUploadSizeExceededException.class})\n    public Result<?> doMaxUploadSizeExceededException(MaxUploadSizeExceededException ex){\n        ExceptionUtils.printIfDev(ex);\n        log.error(ex.getMessage());\n        return Result.fail(\"文件大小超出限制\");\n    }\n\n    @ExceptionHandler(value={RuntimeException.class})\n    public Result<?> doRuntimeException(RuntimeException ex){\n        ExceptionUtils.printIfDev(ex);\n        log.error(ex.getMessage());\n        return Result.fail(\"未知错误，请刷新重试\");\n    }\n\n    @ExceptionHandler(value={Exception.class})\n    public Result<?> doException(Exception ex){\n        ExceptionUtils.printIfDev(ex);\n        log.error(ex.getMessage());\n        return Result.fail(\"未知异常，请刷新重试\");\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/web/exceptionhandler/JwtExceptionHandler.java",
    "content": "package com.acimage.common.web.exceptionhandler;\n\n\nimport com.acimage.common.utils.SpringContextUtils;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.acimage.common.result.Code;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\n\n\n@Slf4j\n@RestControllerAdvice\npublic class JwtExceptionHandler {\n    @ExceptionHandler(value={JWTVerificationException.class})\n    public Result doTokenException(JWTVerificationException ex){\n        log.error(\"{}\",ex.getMessage());\n        return new Result(Code.TOKEN_ERR,null,\"登录失效，请重新登录\");\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/web/interceptor/AccessInterceptor.java",
    "content": "package com.acimage.common.web.interceptor;\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.utils.IpUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * 验证token状态和请求所需权限是否匹配\n */\n\n@Slf4j\npublic class AccessInterceptor implements HandlerInterceptor {\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n        String ip = IpUtils.getIp(request);\n        UserContext.setIp(ip);\n\n        log.debug(\"access 用户:{} 访问:{} {} ip:{}\",\n                UserContext.getUsername(), request.getRequestURI(), request.getMethod(), ip);\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView\n            modelAndView) throws Exception {\n        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception\n            ex) throws Exception {\n        //移除用户信息，防止之后用该线程的信息误判（因为线程池）\n        UserContext.remove();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/java/com/acimage/common/web/interceptor/JwtInterceptor.java",
    "content": "package com.acimage.common.web.interceptor;\n\n\nimport com.acimage.common.global.consts.HeaderKeyConstants;\nimport com.acimage.common.global.exception.NullTokenException;\nimport com.acimage.common.utils.IpUtils;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.JwtUtils;\nimport com.auth0.jwt.exceptions.TokenExpiredException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Date;\n\n/**\n * 获取请求的token状态\n */\n@Slf4j\n@Component\npublic class JwtInterceptor implements HandlerInterceptor {\n    @Autowired\n    TokenService tokenService;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n\n        String ip = IpUtils.getIp(request);\n        UserContext.setIp(ip);\n        String token = request.getHeader(HeaderKeyConstants.AUTHORIZATION);\n\n        try {\n            JwtUtils.verifyToken(token);\n        } catch (TokenExpiredException e1) {\n            Date date = JwtUtils.getExpire(token);\n            //过时毫秒数\n            long expireMillis = System.currentTimeMillis() - date.getTime();\n            //过时限制不超过10s,可以继续解析token\n            long boundMillis = 10 * 1000;\n            if (expireMillis > boundMillis) {\n                return true;\n            }\n        } catch (JWTVerificationException e2) {\n            if (!(e2 instanceof NullTokenException)) {\n                log.warn(\"ip:{}非法token\", ip);\n            }\n            return true;\n        }\n\n        if (!tokenService.hasRecorded(token)) {\n            return true;\n        }\n\n        //设置当前用户信息\n        UserContext.saveCurrentUserInfo(JwtUtils.getUserId(token), JwtUtils.getUsername(token), JwtUtils.getPhotoUrl(token));\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        UserContext.remove();\n    }\n}\n"
  },
  {
    "path": "acimage_common/src/main/resources/application-common.yml",
    "content": "\nspring:\n  mvc:\n    hiddenmethod:\n      filter:\n        enabled: true\n  cloud:\n    sentinel:\n      eager: true\n      transport:\n        dashboard: localhost:7010 #sentinel控制台\n\n\n#feign支持sentinel\nfeign:\n  sentinel:\n    enabled: true\n\n#自定义配置\nmy-config:\n  enable-mq: true\n\n"
  },
  {
    "path": "acimage_common/src/main/resources/application-qiniu-template.yml",
    "content": "#七牛云配置\nqiniu:\n  access-key:\n  secret-key:\n  bucket:\n  domain:\n"
  },
  {
    "path": "acimage_common/src/main/resources/lua/getAndCombineAndDelete.lua",
    "content": "--取出KEYS[1]并加到KEYS[2]的ARGV[1]上（如果KEYS[2]的AVG[1]存在），删除KEYS[1]\n--返回KEYS[1]对应的值\n--KEYS[1] string\n--KEYS[2] hash\n--ARGV[1] fieldKey\nlocal increment = redis.call('get', KEYS[1])\nif not increment then\n    return nil\nend\nlocal base = redis.call('hget',KEYS[2],ARGV[1])\nif base then\n    redis.call('hincrby',KEYS[2],ARGV[1],increment)\nend\nredis.call('del',KEYS[1])\nreturn increment\n"
  },
  {
    "path": "acimage_common/src/main/resources/lua/incrementIfPresent.lua",
    "content": "if not redis.call('get', KEYS[1]) then\n    return nil\nelse\n\treturn redis.call('incrby', KEYS[1], ARGV[1])\nend"
  },
  {
    "path": "acimage_common/src/main/resources/lua/incrementIfPresentForFieldKey.lua",
    "content": "--如果KEYS[1]的hash key ARGV[1]存在，则为它增加ARGV[2]，并返回增加后的值\n--否则返回nil\nlocal base = redis.call('hget',KEYS[1],ARGV[1])\nif not base then\n    return nil\nend\nreturn redis.call('hincrby',KEYS[1],ARGV[1],ARGV[2])\n"
  },
  {
    "path": "acimage_common/src/main/resources/lua/incrementIfPresentForZSet.lua",
    "content": "if not redis.call('zscore', KEYS[1], ARGV[1]) then\n    return nil\nelse\n-- zincrby key increment member\n\treturn redis.call('zincrby', KEYS[1], ARGV[2], ARGV[1])\nend"
  },
  {
    "path": "acimage_common/src/main/resources/lua/requestLimit.lua",
    "content": "--设n是KEYS长度\n--ARGV[1]到ARGV[n]次数限制\n--ARGV[n+1]到ARGV[2n]过期时间\n--ARGV[2n+1]到ARGV[3n]惩罚过期时间\nlocal result={}\nlocal len = #KEYS\nif (not len) or ( tonumber(len)==0 ) then\n    return result;\nend\nfor i=1,len do\n    local res=redis.call('incrby',KEYS[i],1)\n    if res==1 then\n        --如果是第一次访问该key则设置过期时间\n        redis.call('expire',KEYS[i],ARGV[len+i])\n    end\n    if res==tonumber(ARGV[i])+1 then\n        --超过限制则惩罚过期时间\n        if tonumber(ARGV[2*len+i])>=0 then\n            redis.call('expire',KEYS[i],ARGV[2*len+i])\n        end\n    end\n    result[i]=res\nend\nreturn result\n"
  },
  {
    "path": "acimage_common/src/main/resources/lua/setIfPresentForFieldKey.lua",
    "content": "--如果KEYS[1]的hash key ARGV[1]存在，则将其它设置为ARGV[2]，并返回增加后的值\n--否则返回nil\nlocal base = redis.call('hget',KEYS[1],ARGV[1])\nif not base then\n    return nil\nend\nif redis.call('hset',KEYS[1],ARGV[1],ARGV[2])==1 then\n    return true\nelse\n    return false\nend\n"
  },
  {
    "path": "acimage_common/src/main/resources/sensitive_word.txt",
    "content": "爱爱\n色情\n独裁\n专政\n暴乱\n杀人\n强奸\n嫖娼\n吸毒\n赌博\n做爱\n小姐\n妓女\n包夜\n3P\n狼友\n技师\n推油\n胸推\n毒龙\n口爆\n楼凤\n足交\n口暴\n口交\nSM\n桑拿\n吞精\n咪咪\n婊子\n乳方\n操逼\n性伴侣\n爱液\n按摩棒\n爆草\n包二奶\n暴干\n暴奸\n暴乳\n爆乳\n暴淫\n被操\n被插\n逼奸\n仓井空\n插暴\n操逼\n操黑\n操烂\n肏你\n肏死\n操死\n操我\n操你\n草你妈\n厕奴\n插比\n插b\n插逼\n插你\n插我\n插阴\n潮吹\n潮喷\n成人电影\n成人论坛\n成人色情\n成人网站\n成人文学\n成人小说\n艳情小说\n成人游戏\n吃精\n抽插\n春药\n大波\n大力抽送\n大乳\n荡妇\n荡女\n盗撮\n发浪\n放尿\n肥逼\n粉穴\n风月大陆\n干死你\n干穴\n肛交\n肛门\n龟头\n裹本\n国产av\n好嫩\n豪乳\n黑逼\n后庭\n后穴\n虎骑\n换妻俱乐部\n黄片\n几吧\n鸡吧\n鸡巴\n鸡奸\n奸情\n叫床\n脚交\n精液\n巨屌\n菊花洞\n菊门\n巨奶\n巨乳\n菊穴\n开苞\n口活\n口射\n口淫\n裤袜\n狂操\n狂插\n浪逼\n浪妇\n浪叫\n浪女\n聊性\n凌辱\n漏乳\n露b\n乱交\n乱伦\n轮暴\n轮操\n轮奸\n裸陪\n买春\n美逼\n美少妇\n美乳\n美穴\n美幼\n秘唇\n迷奸\n密穴\n蜜穴\n蜜液\n摸奶\n摸胸\n母奸\n奈美\n奶子\n男奴\n内射\n嫩逼\n嫩女\n嫩穴\n捏弄\n女优\n喷精\n屁眼\n强jian\n强暴\n强奸处女\n情趣用品\n情色\n拳交\n群交\n人妻\n人兽\n日逼\n日烂\n肉棒\n肉逼\n肉唇\n肉洞\n肉缝\n肉棍\n肉茎\n肉具\n揉乳\n肉穴\n肉欲\n乳爆\n乳房\n乳沟\n乳交\n乳头\n骚逼\n骚比\n骚女\n骚水\n骚穴\n色逼\n色界\n色猫\n色盟\n色情网站\n色区\n色诱\n色欲\n色b\n少年阿宾\n射爽\n射颜\n食精\n释欲\n兽奸\n兽交\n手淫\n傻逼\n傻b\n傻吊\n傻叉\n兽欲\n熟妇\n熟母\n熟女\n爽片\n双臀\n死逼\n丝袜\n丝诱\n松岛枫\n酥痒\n汤加丽\n套弄\n体奸\n体位\n舔脚\n舔阴\n偷欢\n脱内裤\n文做\n舞女\n吸精\n夏川纯\n相奸\n小逼\n校鸡\n小穴\n小xue\n性感妖娆\n性感诱惑\n性虎\n性饥渴\n性技巧\n性交\n性奴\n性虐\n性息\n性欲\n胸推\n穴口\n穴图\n亚情\n颜射\n阳具\n杨思敏\n要射了\n夜勤病栋\n一本道\n一夜欢\n一夜情\n一ye情\n阴部\n淫虫\n阴唇\n淫荡\n阴道\n淫电影\n阴阜\n淫妇\n淫河\n阴核\n阴户\n淫贱\n淫叫\n淫教师\n阴茎\n阴精\n淫浪\n淫媚\n淫糜\n淫魔\n淫母\n淫女\n淫虐\n淫妻\n淫情\n淫色\n淫声浪语\n淫兽学园\n淫书\n淫术炼金士\n淫水\n淫娃\n淫威\n淫亵\n淫样\n淫液\n淫照\n阴b\n应召\n幼交\n欲火\n欲女\n玉乳\n玉穴\n援交\n原味内衣\n援助交际\n招鸡\n招妓\n抓胸\n自慰\n作爱\na片\nbitch\nfuck\ngay片\ng点\nh动画\nh动漫\n失身粉\n淫荡自慰器\n习近平\n平近习\nxjp\n习太子\n习明泽\n老习\n温家宝\n温加宝\n温x\n温jia宝\n温宝宝\n温加饱\n温加保\n张培莉\n温云松\n温如春\n温jb\n胡温\n胡x\n胡jt\n胡boss\n胡总\n胡王八\nhujintao\n胡jintao\n胡j涛\n胡惊涛\n胡景涛\n胡紧掏\n湖紧掏\n胡紧套\n锦涛\nhjt\n胡派\n胡主席\n刘永清\n胡海峰\n胡海清\n江泽民\n民泽江\n江胡\n江哥\n江主席\n江书记\n江浙闽\n江沢民\n江浙民\n择民\n则民\n茳泽民\nzemin\nze民\n老江\n老j\n江core\n江x\n江派\n江zm\njzm\n江戏子\n江蛤蟆\n江某某\n江贼\n江猪\n江氏集团\n江绵恒\n江绵康\n王冶坪\n江泽慧\n邓小平\n平小邓\nxiao平\n邓xp\n邓晓平\n邓朴方\n邓榕\n邓质方\n毛泽东\n猫泽东\n猫则东\n猫贼洞\n毛zd\n毛zx\nz东\nze东\n泽d\nzedong\n毛太祖\n毛相\n主席画像\n改革历程\n朱镕基\n朱容基\n朱镕鸡\n朱容鸡\n朱云来\n李鹏\n李peng\n里鹏\n李月月鸟\n李小鹏\n李小琳\n华主席\n华国\n国锋\n国峰\n锋同志\n白春礼\n薄熙来\n薄一波\n蔡赴朝\n蔡武\n曹刚川\n常万全\n陈炳德\n陈德铭\n陈建国\n陈良宇\n陈绍基\n陈同海\n陈至立\n戴秉国\n丁一平\n董建华\n杜德印\n杜世成\n傅锐\n郭伯雄\n郭金龙\n贺国强\n胡春华\n耀邦\n华建敏\n黄华华\n黄丽满\n黄兴国\n回良玉\n贾庆林\n贾廷安\n靖志远\n李长春\n李春城\n李建国\n李克强\n李岚清\n李沛瑶\n李荣融\n李瑞环\n李铁映\n李先念\n李学举\n李源潮\n栗智\n梁光烈\n廖锡龙\n林树森\n林炎志\n林左鸣\n令计划\n柳斌杰\n刘奇葆\n刘少奇\n刘延东\n刘云山\n刘志军\n龙新民\n路甬祥\n罗箭\n吕祖善\n马飚\n马恺\n孟建柱\n欧广源\n强卫\n沈跃跃\n宋平顺\n粟戎生\n苏树林\n孙家正\n铁凝\n屠光绍\n王东明\n汪东兴\n王鸿举\n王沪宁\n王乐泉\n王洛林\n王岐山\n王胜俊\n王太华\n王学军\n王兆国\n王振华\n吴邦国\n吴定富\n吴官正\n无官正\n吴胜利\n吴仪\n奚国华\n习仲勋\n徐才厚\n许其亮\n徐绍史\n杨洁篪\n叶剑英\n由喜贵\n于幼军\n俞正声\n袁纯清\n曾培炎\n曾庆红\n曾宪梓\n曾荫权\n张德江\n张定发\n张高丽\n张立昌\n张荣坤\n张志国\n赵洪祝\n紫阳\n周生贤\n周永康\n朱海仑\n中南海\n大陆当局\n中国当局\n北京当局\n共产党\n党产共\n共贪党\n阿共\n产党共\n公产党\n工产党\n共c党\n共x党\n共铲\n供产\n共惨\n供铲党\n供铲谠\n供铲裆\n共残党\n共残主义\n共产主义的幽灵\n拱铲\n老共\n中共\n中珙\n中gong\ngc党\n贡挡\ngong党\ng产\n狗产蛋\n共残裆\n恶党\n邪党\n共产专制\n共产王朝\n裆中央\n土共\n土g\n共狗\ng匪\n共匪\n仇共\n政府\n症腐\n政腐\n政付\n正府\n政俯\n政f\nzhengfu\n政zhi\n挡中央\n档中央\n中央领导\n中国zf\n中央zf\n国wu院\n中华帝国\ngong和\n大陆官方\n北京政权\n胡锦涛\n习仲勋\n贺国强\n贺子珍\n周永康\n李长春\n李德生\n王岐山\n姚依林\n回良玉\n李源潮\n李干成\n戴秉国\n黄镇\n刘延东\n刘瑞龙\n俞正声\n黄敬\n薄熙\n薄一波\n周小川\n周建南\n温云松\n徐明\n江泽慧\n江绵恒\n江绵康\n李小鹏\n李鹏\n李小琳\n朱云来\n朱容基\n李洪志\n新疆骚乱\n腐败中国\n三个呆婊\n你办事我放心\n社会主义灭亡\n打倒中国\n打倒共产党\n打倒共产主义\n打倒胡锦涛\n打倒江泽民\n打倒江主席\n打倒李鹏\n打倒罗干\n打倒温家宝\n打倒中共\n打倒朱镕\n抵制共产党\n抵制共产主义\n抵制胡锦涛\n抵制江泽民\n抵制江主席\n抵制李鹏\n抵制罗干\n抵制温家宝\n抵制中共\n抵制朱镕基\n灭亡中国\n亡党亡国\n粉碎四人帮\n激流中国\n特供\n特贡\n特共\nzf大楼\n殃视\n贪污腐败\n强制拆除\n形式主义\n政治风波\n太子党\n上海帮\n北京帮\n清华帮\n红色贵族\n权贵集团\n河蟹社会\n喝血社会\n九风\n9风\n十七大\n十7大\n17da\n九学\n9学\n四风\n4风\n双规\n南街村\n最淫官员\n警匪\n官匪\n独夫民贼\n官商勾结\n城管暴力执法\n强制捐款\n毒豺\n一党执政\n一党专制\n一党专政\n专制政权\n宪法\n法院\n胡平\n苏晓康\n贺卫方\n谭作人\n焦国标\n万润南\n张志新\n辛灝年\n高勤荣\n王炳章\n高智晟\n司马璐\n刘晓竹\n刘宾雁\n魏京生\n寻找林昭的灵魂\n别梦成灰\n谁是新中国\n讨伐中宣部\n异议人士\n民运人士\n启蒙派\n选国家主席\n民一主\nmin主\n民竹\n民珠\n民猪\nchinesedemocracy\n大赦国际\n国际特赦\nda选\n投公\n公头\n宪政\n平反\n党章\n维权\n昝爱宗\n宪章\n08宪\n08xz\n抿主\n敏主\n人拳\n人木又\n人quan\nrenquan\n中国人权\n中国新民党\n群体事件\n群体性事件\n上中央\n去中央\n讨说法\n请愿\n请命\n公开信\n联名上书\n万人大签名\n万人骚动\n截访\n上访\nshangfang\n信访\n访民\n集合\n集会\n组织集体\n静坐\n静zuo\njing坐\n示威\n示wei\n游行\nyou行\n油行\n游xing\nyouxing\n官逼民反\n反party\n反共\n抗议\n亢议\n抵制\n低制\n底制\ndi制\n抵zhi\ndizhi\nboycott\n血书\n焚烧中国国旗\nbaoluan\n流血冲突\n出现暴动\n发生暴动\n引起暴动\nbaodong\n灭共\n杀毙\n罢工\n霸工\n罢考\n罢餐\n霸餐\n罢参\n罢饭\n罢吃\n罢食\n罢课\n罢ke\n霸课\nba课\n罢教\n罢学\n罢运\n网特\n网评员\n网络评论员\n五毛党\n五毛们\n5毛党\n戒严\njieyan\njie严\n戒yan\n8的平方事件\n知道64\n八九年\n贰拾年\n2o年\n20和谐年\n贰拾周年\n六四\n六河蟹四\n六百度四\n六和谐四\n陆四\n陆肆\n198964\n5月35\n89年春夏之交\n64惨案\n64时期\n64运动\n4事件\n四事件\n北京风波\n学潮\n学chao\nxuechao\n学百度潮\n门安天\n天按门\n坦克压大学生\n民主女神\n历史的伤口\n高自联\n北高联\n血洗京城\n四二六社论\n王丹\n柴玲\n沈彤\n封从德\n王超华\n王维林\n吾尔开希\n吾尔开西\n侯德健\n阎明复\n方励之\n蒋捷连\n丁子霖\n辛灏年\n蒋彦永\n严家其\n陈一咨\n中华局域网\n党的喉舌\n互联网审查\n当局严密封锁\n新闻封锁\n封锁消息\n爱国者同盟\n关闭所有论坛\n网络封锁\n金盾工程\ngfw\n无界浏览\n无界网络\n自由门\n何清涟\n中国的陷阱\n汪兆钧\n记者无疆界\n境外媒体\n维基百科\n纽约时报\nbbc中文网\n华盛顿邮报\n世界日报\n东森新闻网\n东森电视\n星岛日报\nwikipedia\nyoutube\ngoogleblogger\n美国广播公司\n英国金融时报\n自由亚洲\n自由时报\n中国时报\n反分裂\n威胁论\n左翼联盟\n钓鱼岛\n保钓组织\n主权\n弓单\n火乍\n木仓\n石肖\n核蛋\n步qiang\nbao炸\n爆zha\nbaozha\nzha药\nzha弹\n炸dan\n炸yao\nzhadan\nzhayao\nhmtd\n三硝基甲苯\n六氟化铀\n炸药配方\n弹药配方\n炸弹配方\n皮箱炸弹\n火药配方\n人体炸弹\n人肉炸弹\n解放军\n兵力部署\n军转\n军事社\n8341部队\n第21集团军\n七大军区\n7大军区\n北京军区\n沈阳军区\n济南军区\n成都军区\n广州军区\n南京军区\n兰州军区\n颜色革命\n规模冲突\n塔利班\n基地组织\n恐怖分子\n恐怖份子\n三股势力\n印尼屠华\n印尼事件\n蒋公纪念歌\n马英九\nmayingjiu\n李天羽\n苏贞昌\n林文漪\n陈水扁\n陈s扁\n陈随便\n阿扁\na扁\n告全国同胞书\n台百度湾\n台完\n台wan\ntaiwan\n台弯\n湾台\n台湾国\n台湾共和国\n台军\n台独\n台毒\n台du\ntaidu\ntwdl\n一中一台\n打台湾\n两岸战争\n攻占台湾\n支持台湾\n进攻台湾\n占领台湾\n统一台湾\n收复台湾\n登陆台湾\n解放台湾\n解放tw\n解决台湾\n光复民国\n台湾独立\n台湾问题\n台海问题\n台海危机\n台海统一\n台海大战\n台海战争\n台海局势\n入联\n入耳关\n中华联邦\n国民党\nx民党\n民进党\n青天白日\n闹独立\nduli\nfenlie\n日本万岁\n小泽一郎\n劣等民族\n汉人\n汉维\n维汉\n维吾\n吾尔\n热比娅\n伊力哈木\n疆独\n东突厥斯坦解放组织\n东突解放组织\n蒙古分裂分子\n列确\n阿旺晋美\n藏人\n臧人\nzang人\n藏民\n藏m\n达赖\n赖达\ndalai\n哒赖\ndl喇嘛\n丹增嘉措\n打砸抢\n西独\n藏独\n葬独\n臧独\n藏毒\n藏du\nzangdu\n支持zd\n藏暴乱\n藏青会\n雪山狮子旗\n拉萨\n啦萨\n啦沙\n啦撒\n拉sa\nlasa\nla萨\n西藏\n藏西\n藏春阁\n藏獨\n藏独\n藏独立\n藏妇会\n藏青会\n藏字石\nxizang\nxi藏\nx藏\n西z\ntibet\n希葬\n希藏\n硒藏\n稀藏\n西脏\n西奘\n西葬\n西臧\n援藏\n王千源\n安拉\n回教\n回族\n回回\n回民\n穆斯林\n穆罕穆德\n穆罕默德\n默罕默德\n伊斯兰\n圣战组织\n清真\n真主\n阿拉伯\n高丽棒子\n韩国狗\n满洲第三帝国\n满狗\n鞑子\n江丑闻\n江嫡系\n江毒\n江独裁\n江蛤蟆\n江核心\n江黑心\n江胡内斗\n江祸心\n江家帮\n江绵恒\n江派和胡派\n江派人马\n江泉集团\n江人马\n江三条腿\n江氏家族\n江氏政治局\n江氏政治委员\n江梳头\n江太上\n江戏子\n江系人\n江系人马\n江宰民\n江贼\n江贼民\n江主席\n麻果丸\n麻将透\n麻醉弹\n麻醉狗\n麻醉枪\n麻醉槍\n麻醉药\n麻醉藥\n台湾版假币\n台湾独立\n台湾应该独立\n台湾有权独立\n天灭中共\n中共帮凶\n中共保命\n中共裁\n中共党文化\n中共腐败\n中共的血旗\n中共的罪恶\n中共帝国\n中共独裁\n中共封锁\n中共封网\n中共黑\n中共黑帮\n中共解体\n中共近期权力斗争\n中共恐惧\n中共权力斗争\n中共任用\n中共退党\n中共洗脑\n中共邪教\n中共邪毒素\n中共政治游戏"
  },
  {
    "path": "acimage_community/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>acimage</artifactId>\n        <groupId>com.acimage</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n\n    <artifactId>acimage_community</artifactId>\n    <name>acimage_community</name>\n    <!-- FIXME change it to the project's website -->\n    <url>http://www.example.com</url>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_common</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_feign</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-actuator</artifactId>-->\n<!--        </dependency>-->\n\n        <!--druid依赖-->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid</artifactId>\n        </dependency>\n\n        <!--mysql依赖-->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n\n        <!--rabbitmq-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n        <!--quartz-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-quartz</artifactId>\n        </dependency>\n        <!--minio-->\n        <dependency>\n            <groupId>io.minio</groupId>\n            <artifactId>minio</artifactId>\n        </dependency>\n        <!--敏感词过滤-->\n        <dependency>\n            <groupId>io.github.toolgood</groupId>\n            <artifactId>toolgood-words</artifactId>\n        </dependency>\n        <!--es-->\n        <dependency>\n            <groupId>org.springframework.data</groupId>\n            <artifactId>spring-data-elasticsearch</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.ben-manes.caffeine</groupId>\n            <artifactId>caffeine</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.nintha</groupId>\n            <artifactId>webp-imageio-core</artifactId>\n            <version>0.1.0</version>\n            <scope>system</scope>\n            <systemPath>${pom.basedir}/lib/webp-imageio-core-0.1.0.jar</systemPath>\n        </dependency>\n\n<!--        <dependency>-->\n<!--            <groupId>org.quartz-scheduler</groupId>-->\n<!--            <artifactId>quartz</artifactId>-->\n<!--            <version>2.3.2</version>-->\n<!--        </dependency>-->\n\n    </dependencies>\n\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <includeSystemScope>true</includeSystemScope>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <skipTests>true</skipTests><!--跳过测试包-->\n                </configuration>\n            </plugin>\n        </plugins>\n\n    </build>\n</project>\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/CommunityApplication.java",
    "content": "package com.acimage.community;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n\n@Slf4j\n@SpringBootApplication\n@EnableDiscoveryClient\n@EnableScheduling\n@EnableFeignClients(basePackages=\"com.acimage.feign\")\n@MapperScan(\"com.acimage.community.dao\")\n@ComponentScan(value={\"com.acimage\"})\npublic class CommunityApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(CommunityApplication.class, args);\n\t\tlog.info(\"------------->>>Community启动<<<-------------\");\n\t}\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/CategoryDao.java",
    "content": "package com.acimage.community.dao;\n\n\nimport com.acimage.common.model.domain.community.Category;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface CategoryDao extends BaseMapper<Category> {\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/CmtyUserDao.java",
    "content": "package com.acimage.community.dao;\n\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\n\nimport java.util.List;\n\npublic interface CmtyUserDao extends BaseMapper<CmtyUser> {\n\n//    @Select(\"select * from tb_user_basic where id=#{id}\")\n//    CmtyUser selectCmtyUserById(@Param(\"id\") long id);\n\n    Integer batchUpdateStarCount(@Param(\"userIdAndIncrements\") List<Pair<Long,Integer>> userIdAndIncrements);\n    Integer batchUpdateTopicCount(@Param(\"userIdAndIncrements\") List<Pair<Long,Integer>> userIdAndIncrements);\n\n    @Update(\"update tb_cmty_user set topic_count=topic_count+#{increment} where id=#{userId}\")\n    Integer updateTopicCountByIncrement(@Param(\"userId\") long userId, @Param(\"increment\") int increment);\n\n    @Update(\"update tb_cmty_user set star_count=star_count+#{increment} where id=#{userId}\")\n    Integer updateStarCountByIncrement(@Param(\"userId\") long userId, @Param(\"increment\") int increment);\n\n    @Select(\"select * from tb_cmty_user order by ${column} desc limit #{startIndex},#{recordNum}\")\n    List<CmtyUser> selectListOrderByColumn(@Param(\"column\") String underlineColumnName,\n                                                         @Param(\"startIndex\") int startIndex,\n                                                         @Param(\"recordNum\") Integer recordNum);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/CommentDao.java",
    "content": "package com.acimage.community.dao;\n\nimport com.acimage.common.model.domain.community.Comment;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\n\nimport java.util.List;\n\npublic interface CommentDao extends BaseMapper<Comment> {\n\n    @Delete(\"delete from tb_comment where topic_id=#{topicId}\")\n    Integer deleteByTopicId(@Param(\"topicId\") long topicId);\n    @Update(\"update tb_comment set content=#{content},update_time=now() where id=#{id}\")\n    Integer updateContentById(@Param(\"id\") long id, @Param(\"content\") String content);\n\n    List<Comment> selectCommentsWithUserByTopicId(@Param(\"topicId\") long topicId);\n\n    @Select(\"select count(*) as comment_count from tb_comment where topic_id=#{topicId}\")\n    Integer countCommentsByTopicId(@Param(\"topicId\") long topicId);\n\n    @Select(\"select count(*) as count from tb_comment where user_id=#{userId}\")\n    Integer countCommentsByUserId(@Param(\"userId\") long userId);\n\n    List<Comment> selectCommentsWithUser(@Param(\"topicId\") long topicId,@Param(\"startIndex\") int startIndex,@Param(\"recordNumber\") int recordNumber);\n\n    List<Comment> selectCommentsWithTopicOrderByCreateTime(@Param(\"userId\") long userId, @Param(\"startIndex\") int startIndex, @Param(\"recordNumber\") int recordNumber);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/HomeCarrouselDao.java",
    "content": "package com.acimage.community.dao;\n\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface HomeCarrouselDao extends BaseMapper<HomeCarousel> {\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/ImageDao.java",
    "content": "package com.acimage.community.dao;\n\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.model.domain.image.Image;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\npublic interface ImageDao extends BaseMapper<Image> {\n\n    Integer insertList(List<Image> images);\n\n    Integer updateDescription(List<Pair<Long,String>> idAndDescriptions);\n\n    @Select(\"select * from tb_image where topic_id=#{topicId} order by id\")\n    List<Image> selectListOrderById(@Param(\"topicId\") long topicId);\n\n\n    List<Image> selectImagesWithTopic(List<Long> imageIds);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/StarDao.java",
    "content": "package com.acimage.community.dao;\n\n\nimport com.acimage.common.model.domain.community.Star;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\npublic interface StarDao extends BaseMapper<Star> {\n\n    @Delete(\"delete from tb_star where user_id=#{userId} and topic_id=#{topicId}\")\n    Integer deleteByUserIdAndTopicId(@Param(\"userId\") long userId, @Param(\"topicId\") long topicId);\n\n    @Delete(\"delete from tb_star where topic_id=#{topicId}\")\n    Integer deleteByTopicId(@Param(\"topicId\") long topicId);\n\n//    @Select(\"select * from tb_star where user_id=#{userId} and topic_id=#{topicId}\")\n//    Star selectOne(@Param(\"userId\") long userId,@Param(\"topicId\") long topicId);\n\n    List<Star> selectStarsWithTopicOrderByCreateTime(@Param(\"userId\") long userId, @Param(\"startIndex\") int startIndex, @Param(\"recordNumber\") int recordNumber);\n\n    Integer countStarsOwnedBy(@Param(\"userId\") long userId);\n\n    @Select(\"select count(*) as star_count from tb_star where topic_id=#{topicId}\")\n    Integer countStarsByTopicId(@Param(\"topicId\") long topicId);\n\n    @Select(\"select count(*) as star_count from tb_star where user_id=#{userId}\")\n    Integer countStarsByUserId(@Param(\"userId\") long userId);\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/TagDao.java",
    "content": "package com.acimage.community.dao;\n\nimport com.acimage.common.model.domain.community.Tag;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\npublic interface TagDao extends BaseMapper<Tag> {\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/TagTopicDao.java",
    "content": "package com.acimage.community.dao;\n\nimport com.acimage.common.model.domain.community.TagTopic;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\npublic interface TagTopicDao extends BaseMapper<TagTopic> {\n\n    void insertBatch(List<TagTopic> tagTopicList);\n\n    @Select(\"select tag_id from tb_tag_topic where topic_id=#{topicId} and deleted=0\")\n    List<Integer> selectTagIds(@Param(\"topicId\") long topicId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/TopicDao.java",
    "content": "package com.acimage.community.dao;\n\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport reactor.util.annotation.Nullable;\n\nimport java.util.Date;\nimport java.util.List;\n\npublic interface TopicDao extends BaseMapper<Topic> {\n\n    @Update(\"update tb_topic set title=#{title},content=#{content},update_time=now() where id=#{id} and deleted=0\")\n    Integer updateTopic(@Param(\"id\") long id, @Param(\"title\") String title, @Param(\"content\") String content);\n\n    Integer updatePvByIncrement(@Param(\"idAndIncrements\") List<Pair<Long, Integer>> idAndIncrements);\n\n    Integer batchUpdateColumnByIncrement(@Param(\"column\") String underlineColumnName, @Param(\"idAndIncrements\") List<Pair<Long, Integer>> idAndIncrements);\n\n    @Update(\"update tb_topic set ${column}=${column}+#{increment} where id=#{id} and deleted=0\")\n    Integer updateColumnByIncrement(@Param(\"column\") String column, @Param(\"id\") long id, @Param(\"increment\") int increment);\n\n    @Update(\"update tb_topic set activity_time=#{activityTime} where id=#{id} and deleted=0\")\n    Integer updateActivityTime(@Param(\"id\") long id, @Param(\"activityTime\") Date activityTime);\n\n    Integer batchUpdateActivityTime(@Param(\"idAndActivityTimes\") List<Pair<Long, Date>> idAndActivityTimes);\n\n    List<Topic> selectTopicsWithUserOrderByPageView(@Param(\"startTime\") String startTime, @Nullable @Param(\"limit\") Integer limit);\n\n    List<Topic> selectTopicsWithUserOrderBy(@Param(\"column\") String columnForOrder, @Param(\"limit\") int limit);\n\n    Topic selectTopicWithUser(@Param(\"id\") long id);\n\n    List<Topic> selectTopicsWithUserOrderByCreateTime(@Param(\"userId\") long userId, @Param(\"startIndex\") int startIndex, @Param(\"recordNumber\") int recordNumber);\n\n    List<Topic> selectTopicsWithUserByIds(@Param(\"ids\") List<Long> ids);\n\n    @Select(\"select count(*) from tb_topic where user_id=#{userId} and deleted=0\")\n    Integer countTopics(@Param(\"userId\") long userId);\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/dao/TopicHtmlDao.java",
    "content": "package com.acimage.community.dao;\n\nimport com.acimage.common.model.domain.community.TopicHtml;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface TopicHtmlDao extends BaseMapper<TopicHtml> {\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/CmtyUserDaoBak.java",
    "content": "package com.acimage.community.depreted;\n\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.deprecated.UserCommunityStatistic;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Update;\n\nimport java.util.List;\n\npublic interface CmtyUserDaoBak extends BaseMapper<UserCommunityStatistic> {\n    Integer batchUpdateStarCount(@Param(\"userIdAndIncrements\") List<Pair<Long,Integer>> userIdAndIncrements);\n    Integer batchUpdateTopicCount(@Param(\"userIdAndIncrements\") List<Pair<Long,Integer>> userIdAndIncrements);\n\n    @Update(\"update tb_cmty_user set topic_count=topic_count+#{increment} where user_id=#{userId}\")\n    Integer updateTopicCountByIncrement(@Param(\"userId\") long userId, @Param(\"increment\") int increment);\n\n    @Update(\"update tb_cmty_user set star_count=star_count+#{increment} where user_id=#{userId}\")\n    Integer updateStarCountByIncrement(@Param(\"userId\") long userId, @Param(\"increment\") int increment);\n\n\n    List<UserCommunityStatistic> selectListOrderByColumn(@Param(\"column\") String underlineColumnName,\n                                                         @Param(\"startIndex\") int startIndex,\n                                                         @Param(\"recordNum\") Integer recordNum);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/RabbitmqConvertConfig.java",
    "content": "package com.acimage.community.depreted;\n\n\nimport org.springframework.amqp.core.AcknowledgeMode;\nimport org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;\nimport org.springframework.amqp.rabbit.connection.ConnectionFactory;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;\nimport org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;\nimport org.springframework.context.annotation.Bean;\n\n@Deprecated\npublic class RabbitmqConvertConfig {\n    //发送方序列化\n    @Bean\n    public RabbitTemplate jacksonRabbitTemplate(ConnectionFactory connectionFactory) {\n        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);\n        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());\n        return rabbitTemplate;\n    }\n\n    //消费者序列化\n    @Bean\n    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){\n        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();\n        factory.setConnectionFactory(connectionFactory);\n        factory.setMessageConverter(new Jackson2JsonMessageConverter());\n        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);\n        return factory;\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/UserMixWriteService.java",
    "content": "package com.acimage.community.depreted;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\n\npublic interface UserMixWriteService {\n    void addUserBasicAndUserCommunityStatistic(CmtyUser cmtyUser);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/UserMixWriteServiceImpl.java",
    "content": "package com.acimage.community.depreted;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.community.service.cmtyuser.CmtyUserWriteService;\nimport com.acimage.community.depreted.UserMixWriteService;\nimport com.acimage.community.depreted.userstatistic.UserCsWriteService;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n\npublic class UserMixWriteServiceImpl implements UserMixWriteService {\n    @Autowired\n    CmtyUserWriteService cmtyUserWriteService;\n    @Autowired\n    UserCsWriteService userCsWriteService;\n\n    @Override\n    public void addUserBasicAndUserCommunityStatistic(CmtyUser cmtyUser){\n        cmtyUserWriteService.save(cmtyUser);\n        userCsWriteService.save(cmtyUser.getId());\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/userstatistic/UserCsQueryService.java",
    "content": "package com.acimage.community.depreted.userstatistic;\n\nimport com.acimage.common.deprecated.UserCommunityStatistic;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.community.depreted.userstatistic.consts.KeyConstants;\n\npublic interface UserCsQueryService {\n    @QueryRedis(keyPrefix = KeyConstants.STRINGKP_CMTY_USER,expire = 10L)\n    UserCommunityStatistic getUserCommunityStatistic(long userId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/userstatistic/UserCsRankService.java",
    "content": "package com.acimage.community.depreted.userstatistic;\n\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.redis.annotation.QueryRedis;\n\nimport java.util.List;\n\npublic interface UserCsRankService {\n\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:topicCount\",expire = 5L)\n    List<User> pageUserRankByTopicCount(int pageNo, int pageSize);\n\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:starCount\",expire = 5L)\n    List<User> pageUserRankByStarCount(int pageNo, int pageSize);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/userstatistic/UserCsWriteService.java",
    "content": "package com.acimage.community.depreted.userstatistic;\n\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\npublic interface UserCsWriteService {\n    Integer updateStarCountByIncrements(List<Long> userIds, List<Integer> starCounts);\n\n    Integer updateTopicCountByIncrements(List<Long> userIds, List<Integer> starCounts);\n\n    Integer updateTopicCountByIncrement(long userId, int increment);\n\n    Integer updateStarCountByIncrement(long userId, int increment);\n\n    void save(long userId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/userstatistic/consts/KeyConstants.java",
    "content": "package com.acimage.community.depreted.userstatistic.consts;\n\npublic class KeyConstants {\n\n    public static final String STRINGKP_CMTY_USER =\"acimage:community:cmtyUser:userId:\";\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/userstatistic/impl/UserCsQueryServiceImpl.java",
    "content": "package com.acimage.community.depreted.userstatistic.impl;\n\nimport com.acimage.common.deprecated.UserCommunityStatistic;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.redis.enums.DataType;\nimport com.acimage.community.depreted.CmtyUserDaoBak;\nimport com.acimage.community.depreted.userstatistic.consts.KeyConstants;\nimport com.acimage.community.depreted.userstatistic.UserCsQueryService;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n\npublic class UserCsQueryServiceImpl implements UserCsQueryService {\n    @Autowired\n    CmtyUserDaoBak userCsDao;\n\n    @Override\n    @QueryRedis(keyPrefix = KeyConstants.STRINGKP_CMTY_USER, expire = 10L, dataType = DataType.HASH)\n    public UserCommunityStatistic getUserCommunityStatistic(long userId) {\n        return userCsDao.selectById(userId);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/userstatistic/impl/UserCsRankServiceImpl.java",
    "content": "package com.acimage.community.depreted.userstatistic.impl;\n\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.deprecated.UserCommunityStatistic;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.acimage.community.depreted.CmtyUserDaoBak;\nimport com.acimage.community.depreted.userstatistic.UserCsRankService;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic class UserCsRankServiceImpl implements UserCsRankService {\n\n    @Autowired\n    CmtyUserDaoBak userCsDao;\n\n\n    private List<User> pageUserRankBy(SFunction<UserCommunityStatistic, Integer> attr, int pageNo, int pageSize){\n        int start= PageUtils.startIndexOf(pageNo,pageSize);\n        String column= LambdaUtils.underlineColumnNameOf(attr);\n        List<UserCommunityStatistic> userCsList= userCsDao.selectListOrderByColumn(column,start,pageSize);\n        List<User> users =new ArrayList<>();\n        for(UserCommunityStatistic userCs:userCsList){\n            User user =new User();\n            user.setTopicCount(userCs.getTopicCount());\n            user.setStarCount(userCs.getStarCount());\n            user.setId(userCs.getUserId());\n            user.setUsername(userCs.getCmtyUser().getUsername());\n            user.setPhotoUrl(userCs.getCmtyUser().getPhotoUrl());\n\n            users.add(user);\n        }\n        return users;\n    }\n\n    @Override\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:topicCount:\",expire = 5L)\n    public List<User> pageUserRankByTopicCount(int pageNo, int pageSize){\n        return pageUserRankBy(UserCommunityStatistic::getTopicCount,pageNo,pageSize);\n    }\n\n    @Override\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:starCount:\",expire = 5L)\n    public List<User> pageUserRankByStarCount(int pageNo, int pageSize){\n        return pageUserRankBy(UserCommunityStatistic::getStarCount,pageNo,pageSize);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/depreted/userstatistic/impl/UserCsWriteServiceImpl.java",
    "content": "package com.acimage.community.depreted.userstatistic.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.deprecated.UserCommunityStatistic;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.common.utils.common.PairUtils;\nimport com.acimage.community.depreted.CmtyUserDaoBak;\nimport com.acimage.community.depreted.userstatistic.consts.KeyConstants;\nimport com.acimage.community.depreted.userstatistic.UserCsWriteService;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n\npublic class UserCsWriteServiceImpl implements UserCsWriteService {\n    @Autowired\n    CmtyUserDaoBak userCsDao;\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public Integer updateStarCountByIncrements(List<Long> userIds, List<Integer> starCounts){\n        List<Pair<Long,Integer>> userIdAndStarCounts= PairUtils.combine(userIds,starCounts);\n        if(CollectionUtil.isEmpty(userIds)){\n            return 0;\n        }\n        return userCsDao.batchUpdateStarCount(userIdAndStarCounts);\n    }\n\n    @Override\n    public Integer updateTopicCountByIncrements(List<Long> userIds, List<Integer> starCounts){\n        List<Pair<Long,Integer>> userIdAndStarCounts= PairUtils.combine(userIds,starCounts);\n        if(CollectionUtil.isEmpty(userIds)){\n            return 0;\n        }\n        return userCsDao.batchUpdateTopicCount(userIdAndStarCounts);\n    }\n\n    @Override\n    public Integer updateTopicCountByIncrement( long userId, int increment){\n        redisUtils.delete(KeyConstants.STRINGKP_CMTY_USER +userId);\n        return userCsDao.updateTopicCountByIncrement(userId,increment);\n    }\n\n    @Override\n    public Integer updateStarCountByIncrement(long userId, int increment) {\n        redisUtils.delete(KeyConstants.STRINGKP_CMTY_USER +userId);\n        return userCsDao.updateStarCountByIncrement(userId,increment);\n    }\n\n    @Override\n    public void save(long userId){\n        UserCommunityStatistic userCs=new UserCommunityStatistic();\n        userCs.setUserId(userId);\n        userCsDao.insert(userCs);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/esdao/UserEsDao.java",
    "content": "package com.acimage.community.esdao;\n\nimport com.acimage.common.model.Index.TopicIndex;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface UserEsDao extends ElasticsearchRepository<TopicIndex,Long> {\n\n}"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/annotation/RecordPageView.java",
    "content": "package com.acimage.community.global.annotation;\n\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 带有该注解的接口，被调用时会记录到对应话题的浏览量中\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface RecordPageView {\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/annotation/TopicId.java",
    "content": "package com.acimage.community.global.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 注解在参数或javabean对象的成员变量上\n */\n@Target({ElementType.FIELD,ElementType.PARAMETER})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface TopicId {\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/aop/RecordPageViewAdvice.java",
    "content": "package com.acimage.community.global.aop;\n\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.utils.common.AopUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.global.annotation.TopicId;\nimport com.acimage.community.service.topic.TopicSpAttrQueryService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.AfterReturning;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n@Aspect\n@Component\n@Slf4j\n@Order(10)\npublic class RecordPageViewAdvice {\n    private static final String POINT_CUT= \"@annotation(com.acimage.community.global.annotation.RecordPageView)\";\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicSpAttrQueryService topicSpAttrQueryService;\n\n    @Pointcut(POINT_CUT)\n    public void recordPageViewPointCut() {\n    }\n\n    @AfterReturning(pointcut = \"recordPageViewPointCut()\", returning = \"result\")\n    public Object around(JoinPoint joinPoint, Object result) throws Throwable {\n\n        Long topicId=AopUtils.annotatedArgOrArgFieldOf(joinPoint, TopicId.class, Long.class);\n        if (topicId==null) {\n            return result;\n        }\n\n        String ipAddress = UserContext.getIp();\n        // 获取存入的key\n        String topicPvLogKey = TopicKeyConstants.LOGKP_TOPIC_PV + topicId;\n\n        //记录哪些ip访问过这个话题，以下这两行代码顺序不可交换！\n        Long count = redisUtils.addForHyperLogLog(topicPvLogKey, ipAddress);\n        //记录当前哪些话题有在统计浏览量\n        redisUtils.addForSet(TopicKeyConstants.SETK_RECORDING_PV_INCREMENT, topicId.toString());\n\n        if (count == 0) {\n            log.debug(\"ip：{} 已访问过 话题{}\", ipAddress, topicId);\n        } else {\n            log.debug(\"ip：{} 访问话题{} 累计访问量 {}\", ipAddress, topicId, redisUtils.sizeForHyperLogLog(topicPvLogKey));\n            //更新浏览量排行榜\n            redisUtils.addForZSet(TopicKeyConstants.ZSETK_TOPIC_PV, topicId.toString(), topicSpAttrQueryService.getPageView(topicId));\n        }\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/config/JobFactory.java",
    "content": "package com.acimage.community.global.config;\n\nimport org.quartz.spi.TriggerFiredBundle;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.springframework.scheduling.quartz.AdaptableJobFactory;\n\n\n\npublic class JobFactory extends AdaptableJobFactory {\n    @Autowired\n    private AutowireCapableBeanFactory capableBeanFactory;\n\n    @Override\n    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {\n        // 调用父类的方法\n        Object jobInstance = super.createJobInstance(bundle);\n        // 进行注入\n        capableBeanFactory.autowireBean(jobInstance);\n        return jobInstance;\n    }\n}\n\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/config/WebMvcConfig.java",
    "content": "package com.acimage.community.global.config;\n\n\n\nimport com.acimage.common.web.interceptor.JwtInterceptor;\nimport com.acimage.common.web.interceptor.AccessInterceptor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Slf4j\n@Configuration\npublic class WebMvcConfig implements WebMvcConfigurer {\n\n    @Autowired\n    JwtInterceptor jwtInterceptor;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n\n        registry.addInterceptor(jwtInterceptor).addPathPatterns(\"/**\").order(20);\n\n        registry.addInterceptor(new AccessInterceptor()).addPathPatterns(\"/**\").order(30);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/consts/CommentKeyConstants.java",
    "content": "package com.acimage.community.global.consts;\n\npublic class CommentKeyConstants {\n    public static final String STRINGKP_COMMENT_COUNT = \"acimage:community:comments:commentCount:topicId:\";\n    public static final String STRINGKP_TOPIC_COMMENTS = \"acimage:community:comments:topicId:pageNo:\";\n    public static final String STRINGKP_USER_COMMENTS = \"acimage:community:comments:userId:pageNo:\";\n\n    public static final String STRINGKP_PUBLISHED_COMMENTS = \"acimage:community:comments:published:userId:\";\n\n    public static String keyOfTopicComments(long topicId, int pageNo) {\n        return String.format(\"%s%s:%s\", STRINGKP_TOPIC_COMMENTS, topicId, pageNo);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/consts/CoverImageConstants.java",
    "content": "package com.acimage.community.global.consts;\n\npublic class CoverImageConstants {\n    public static final int WIDTH=405;\n    public static final int HEIGHT=390;\n    //压缩后大小不能超过40kb\n    public static final int LIMIT_COMPRESS_SIZE =40*1000;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/consts/PageSizeConstants.java",
    "content": "package com.acimage.community.global.consts;\n\npublic class PageSizeConstants {\n    public static final int TOPIC_COMMENTS =10;\n    public static final int ACTIVITY_TOPICS =5;\n    public static final int ACTIVITY_COMMENTS =5;\n    public static final int ACTIVITY_STARS =5;\n    public static final int FORUM_TOPICS =12;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/consts/StarKeyConstants.java",
    "content": "package com.acimage.community.global.consts;\n\npublic class StarKeyConstants {\n    public static final String STRINGKP_TOPIC_STAR_COUNT = \"acimage:community:stars:starCount:topicId:\";\n    public static final String STRINGKP_USER_STAR_COUNT = \"acimage:community:stars:starCount:userId:\";\n    public static final String STRINGKP_STAR_USER_TOPIC = \"acimage:community:stars:isStar:userId:topicId:\";\n\n    public static String keyOfIsStar(long userId, long topicId) {\n        return STRINGKP_STAR_USER_TOPIC + userId + \":\" + topicId;\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/consts/TopicKeyConstants.java",
    "content": "package com.acimage.community.global.consts;\n\npublic class TopicKeyConstants {\n    public static final String HASHKP_TOPIC = \"acimage:community:topics:id:\";\n    public static final String HASHKP_TOPIC_HTML=\"acimage:community:topicHtml:topicId:\";\n\n    public static final String STRINGKP_PUBLISHED_TOPIC_TITLE=\"acimage:community:topics:published:userId:\";\n\n\n    public static final String STRINGKP_TOPIC_STAR_COUNT_INCREMENT = \"acimage:community:topics:starCountIncrement:id:\";\n    public static final String STRINGKP_TOPIC_COMMENT_COUNT_INCREMENT = \"acimage:community:topics:commentCountIncrement:id:\";\n    public static final String STRINGKP_TOPIC_ACTIVITY_TIME = \"acimage:community:topics:activityTime:id:\";\n    /**\n     * key前缀，统计浏览了对应话题ip数，redis类型：HyperLogLog\n     */\n    public static final String LOGKP_TOPIC_PV =\"acimage:community:topics:pageViewLog:id:\";\n\n    /**\n     * key，记录所有被记录浏览量增量的话题id， redis类型：set\n     */\n    public static final String SETK_RECORDING_PV_INCREMENT =\"acimage:community:topics:recording:pageViewLog\";\n    /**\n     * key，记录所有被记录收藏量增量的话题id， redis类型：set\n     */\n    public static final String SETK_RECORDING_STAR_COUNT_INCREMENT =\"acimage:community:topics:recording:starCountIncrement\";\n    /**\n     * key，记录所有被记录评论数增量的话题id， redis类型：set\n     */\n    public static final String SETK_RECORDING_COMMENT_COUNT_INCREMENT =\"acimage:community:topics:recording:commentCountIncrement\";\n    public static final String SETK_RECORDING_ACTIVITY_TIME =\"acimage:community:topics:recording:activityTime\";\n\n\n    public static final String ZSETK_TOPIC_STAR_COUNT =\"acimage:community:topics:rank:starCount\";\n    public static final String ZSETK_TOPIC_PV =\"acimage:community:topics:rank:pageView\";\n    /**\n     * key前缀，记录话题的最新变化时间（如新增评论等）\n     */\n    public static final String ZSETK_TOPIC_ACTIVITY_TIME =\"acimage:community:topics:rank:activityTime\";\n    public static final String ZSETK_TOPIC_COMMENT_COUNT =\"acimage:community:topics:rank:commentCount\";\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/enums/SortMode.java",
    "content": "package com.acimage.community.global.enums;\n\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.utils.LambdaUtils;\nimport org.elasticsearch.search.sort.FieldSortBuilder;\nimport org.elasticsearch.search.sort.SortOrder;\n\npublic enum SortMode {\n    NORMAL,\n    CREATE_TIME,\n    ACTIVITY_TIME,\n    STAR_COUNT,\n    PAGE_VIEW,\n    COMMENT_COUNT;\n\n    public String toColumn() {\n        switch (this) {\n            case NORMAL:\n                return null;\n            case CREATE_TIME:\n                return LambdaUtils.columnOf(TopicIndex::getCreateTime);\n            case ACTIVITY_TIME:\n                return LambdaUtils.columnOf(TopicIndex::getActivityTime);\n            case STAR_COUNT:\n                return LambdaUtils.columnOf(TopicIndex::getStarCount);\n            case PAGE_VIEW:\n                return LambdaUtils.columnOf(TopicIndex::getPageView);\n            case COMMENT_COUNT:\n                return LambdaUtils.columnOf(TopicIndex::getCommentCount);\n        }\n        return null;\n    }\n\n    public FieldSortBuilder toSortBuilder(){\n        if(this==NORMAL){\n            return null;\n        }\n        if(this.toColumn()==null){\n            return null;\n        }\n        return new FieldSortBuilder(this.toColumn())\n                .order(SortOrder.DESC);\n    }\n\n    public static FieldSortBuilder toSortBuilder(SortMode sortMode){\n        if(sortMode==null){\n            return null;\n        }\n        if(sortMode==NORMAL){\n            return null;\n        }\n        if(sortMode.toColumn()==null){\n            return null;\n        }\n        return new FieldSortBuilder(sortMode.toColumn())\n                .order(SortOrder.DESC);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/global/enums/TopicAttribute.java",
    "content": "package com.acimage.community.global.enums;\n\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\n\nimport java.util.*;\n\npublic enum TopicAttribute {\n    STAR_COUNT,\n    PAGE_VIEW,\n    COMMENT_COUNT,\n    ACTIVITY_TIME;\n    public static final Map<TopicAttribute, String> toZSetKeyForRank;\n    public static final Map<TopicAttribute, String> toSetKey;\n    public static final Map<TopicAttribute, String> toKeyPrefix;\n    public static final Map<TopicAttribute, SFunction<Topic,Object>> toTopicField;\n\n    static {\n        toZSetKeyForRank = new HashMap<TopicAttribute, String>() {{\n            put(STAR_COUNT, TopicKeyConstants.ZSETK_TOPIC_STAR_COUNT);\n            put(PAGE_VIEW, TopicKeyConstants.ZSETK_TOPIC_PV);\n            put(COMMENT_COUNT, TopicKeyConstants.ZSETK_TOPIC_COMMENT_COUNT);\n            put(ACTIVITY_TIME, TopicKeyConstants.ZSETK_TOPIC_ACTIVITY_TIME);\n        }};\n\n        toSetKey = new HashMap<TopicAttribute, String>() {{\n            put(STAR_COUNT, TopicKeyConstants.SETK_RECORDING_STAR_COUNT_INCREMENT);\n            put(PAGE_VIEW, TopicKeyConstants.SETK_RECORDING_PV_INCREMENT);\n            put(COMMENT_COUNT, TopicKeyConstants.SETK_RECORDING_COMMENT_COUNT_INCREMENT);\n            put(ACTIVITY_TIME, TopicKeyConstants.SETK_RECORDING_ACTIVITY_TIME);\n        }};\n\n        toKeyPrefix = new HashMap<TopicAttribute, String>() {{\n            put(STAR_COUNT, TopicKeyConstants.STRINGKP_TOPIC_STAR_COUNT_INCREMENT);\n            put(PAGE_VIEW, TopicKeyConstants.LOGKP_TOPIC_PV);\n            put(COMMENT_COUNT, TopicKeyConstants.STRINGKP_TOPIC_COMMENT_COUNT_INCREMENT);\n            put(ACTIVITY_TIME, TopicKeyConstants.STRINGKP_TOPIC_ACTIVITY_TIME);\n        }};\n\n        toTopicField = new HashMap<TopicAttribute, SFunction<Topic,Object>>() {{\n            put(STAR_COUNT, Topic::getStarCount);\n            put(PAGE_VIEW, Topic::getPageView);\n            put(COMMENT_COUNT, Topic::getCommentCount);\n            put(ACTIVITY_TIME, Topic::getActivityTime);\n        }};\n    }\n\n    public String zSetKey() {\n        return toZSetKeyForRank.get(this);\n    }\n\n    public String setKeyForRecordingId() {\n        return toSetKey.get(this);\n    }\n\n    public String keyPrefix() {\n        return toKeyPrefix.get(this);\n    }\n\n    public String toUnderlineColumnName() {\n        return LambdaUtils.underlineColumnNameOf(toTopicField.get(this));\n    }\n\n    public String toFieldName() {\n        return LambdaUtils.columnOf(toTopicField.get(this));\n    }\n\n    public SFunction<Topic,Object> toGetFunction(){\n        return toTopicField.get(this);\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/listener/CommentEventListener.java",
    "content": "package com.acimage.community.listener;\n\n\nimport com.acimage.community.listener.event.CommentEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CommentEventListener implements ApplicationListener<CommentEvent> {\n\n    @Override\n    public void onApplicationEvent(CommentEvent event) {\n        //增加用户发表话题数\n\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/listener/PublishTopicEventListener.java",
    "content": "package com.acimage.community.listener;\n\n\nimport com.acimage.community.listener.event.TopicEvent;\nimport com.acimage.community.service.cmtyuser.CmtyUserWriteService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class PublishTopicEventListener implements ApplicationListener<TopicEvent> {\n    @Autowired\n    CmtyUserWriteService cmtyUserWriteService;\n\n    @Override\n    public void onApplicationEvent(TopicEvent event) {\n        //增加用户发表话题数\n        cmtyUserWriteService.updateTopicCountByIncrement(event.getUserId(),1);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/listener/StarEventListener.java",
    "content": "package com.acimage.community.listener;\n\n\nimport com.acimage.community.listener.event.StarEvent;\nimport com.acimage.community.service.cmtyuser.CmtyUserWriteService;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class StarEventListener implements ApplicationListener<StarEvent> {\n    @Autowired\n    CmtyUserWriteService cmtyUserWriteService;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n\n    @Override\n    public void onApplicationEvent(StarEvent event) {\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/listener/event/CommentEvent.java",
    "content": "package com.acimage.community.listener.event;\n\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.springframework.context.ApplicationEvent;\n\n@Getter\n@Setter\npublic class CommentEvent extends ApplicationEvent {\n    private Long userId;\n    private Long topicId;\n\n    public CommentEvent(Object source, Long userId, Long topicId) {\n        super(source);\n        this.userId = userId;\n        this.topicId = topicId;\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/listener/event/StarEvent.java",
    "content": "package com.acimage.community.listener.event;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.springframework.context.ApplicationEvent;\n\n\n@Getter\n@Setter\npublic class StarEvent extends ApplicationEvent {\n    Long topicId;\n    Long ownerId;\n    Long fromUserId;\n\n    public StarEvent(Object source,  Long topicId, Long ownerId, Long fromUserId) {\n        super(source);\n        this.topicId = topicId;\n        this.ownerId = ownerId;\n        this.fromUserId = fromUserId;\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/listener/event/TopicEvent.java",
    "content": "package com.acimage.community.listener.event;\n\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.springframework.context.ApplicationEvent;\n\n@Getter\n@Setter\npublic class TopicEvent extends ApplicationEvent {\n    private Long userId;\n    private Long topicId;\n\n    public TopicEvent(Object source, Long userId, Long topicId) {\n        super(source);\n        this.userId = userId;\n        this.topicId = topicId;\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/CommentAddReq.java",
    "content": "package com.acimage.community.model.request;\n\n\nimport com.acimage.common.model.domain.community.Comment;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\npublic class CommentAddReq {\n\n    @Positive\n    @NotNull(message = \"话题id不能为空\")\n    private Long topicId;\n\n    @Size(min = Comment.CONTENT_MIN, max = Comment.CONTENT_MAX, message = Comment.CONTENT_VALIDATION_MSG)\n    private String content;\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/CommentModifyReq.java",
    "content": "package com.acimage.community.model.request;\n\n\nimport com.acimage.common.model.domain.community.Comment;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\npublic class CommentModifyReq {\n\n    @Positive\n    @NotNull(message = \"id不能为空\")\n    Long id;\n\n    @Size(min = Comment.CONTENT_MIN, max = Comment.CONTENT_MAX, message = Comment.CONTENT_VALIDATION_MSG)\n    String content;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/TopicAddReq.java",
    "content": "package com.acimage.community.model.request;\n\nimport com.acimage.common.model.domain.community.Category;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.domain.community.TopicHtml;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class TopicAddReq {\n    @Size(min = Topic.TITLE_MIN, max = Topic.TITLE_MAX, message = Topic.TITLE_VALIDATION_MSG)\n    private String title;\n    @Size(min = TopicHtml.HTML_MIN, max = TopicHtml.HTML_MAX, message = TopicHtml.HTML_VALIDATION_MSG)\n    private String html;\n    @Positive\n    @NotNull\n    Integer CategoryId;\n\n    Integer[] tagIds;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/TopicModifyHtmlReq.java",
    "content": "package com.acimage.community.model.request;\n\nimport com.acimage.common.model.domain.community.TopicHtml;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n\n@Data\n@NoArgsConstructor\npublic class TopicModifyHtmlReq {\n    @Positive\n    Long id;\n    @Size(min = TopicHtml.HTML_MIN, max = TopicHtml.HTML_MAX, message = TopicHtml.HTML_VALIDATION_MSG)\n    private String html;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/TopicQueryByCategoryIdReq.java",
    "content": "package com.acimage.community.model.request;\n\nimport com.acimage.community.global.enums.SortMode;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.hibernate.validator.constraints.Range;\n\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\n\n@Data\n@NoArgsConstructor\npublic class TopicQueryByCategoryIdReq {\n    @Positive\n    @NotNull\n    private Integer categoryId;\n    @Range(min=1,max=100,message = \"页码在1到100之间\")\n    private Integer pageNo;\n    @Range(min=4,max=20,message = \"页大小在4到20之间\")\n    private Integer pageSize;\n    private SortMode sortMode;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/TopicQueryBySortReq.java",
    "content": "package com.acimage.community.model.request;\n\nimport com.acimage.community.global.enums.SortMode;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.hibernate.validator.constraints.Range;\n\nimport javax.validation.constraints.NotNull;\n\n@Data\n@NoArgsConstructor\npublic class TopicQueryBySortReq {\n    @Range(min=1,max=100,message = \"页码在1到100之间\")\n    private Integer pageNo;\n    @Range(min=4,max=20,message = \"页大小在4到20之间\")\n    private Integer pageSize;\n    @NotNull\n    private SortMode sortMode;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/TopicQueryByTagIdReq.java",
    "content": "package com.acimage.community.model.request;\n\nimport com.acimage.community.global.enums.SortMode;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Min;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\n\n@Data\n@NoArgsConstructor\npublic class TopicQueryByTagIdReq {\n    @Positive\n    @NotNull\n    private Integer tagId;\n    @Min(1)\n    @Max(50)\n    private Integer pageNo;\n    @Min(1)\n    @Max(20)\n    private Integer pageSize;\n    private SortMode sortMode;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/TopicSearchReq.java",
    "content": "package com.acimage.community.model.request;\n\n\nimport com.acimage.community.global.enums.SortMode;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TopicSearchReq {\n\n\n    private Integer categoryId;\n    private Integer tagId;\n    @Positive\n    @NotNull\n    @Max(30)\n    private Integer pageNo;\n    @Size(max = 15, message = \"搜索字数不超过15\")\n    private String search;\n    private SortMode sortMode;\n}\n\n\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/UserLoginReq.java",
    "content": "package com.acimage.community.model.request;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.Size;\n\n@Data\npublic class UserLoginReq {\n\n    @Size(min = 2, max = 12, message = \"用户名长度在2到12之间\")\n    private String username;\n\n    @Size(min = 100, max = 2000, message = \"非法密码\")\n    String password;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/request/UserRegisterReq.java",
    "content": "package com.acimage.community.model.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.*;\n\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserRegisterReq {\n\n    @Size(min=2,max=12,message = \"用户名长度在2到12之间\")\n    private String username;\n\n    @Size(min=100,max=2000,message = \"非法密码\")\n    String password;\n\n    @Email(message = \"邮箱格式错误\")\n    @Size(min=6,max=32,message = \"邮箱长度在6到32之间\")\n    private String email;\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/model/vo/TopicInfoVo.java",
    "content": "package com.acimage.community.model.vo;\n\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.domain.user.User;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\nimport java.util.List;\n\n@Data\n@NoArgsConstructor\npublic class TopicInfoVo {\n\n\n    private Long id;\n    private Long userId;\n    private String content;\n    private String title;\n    private Integer starCount;\n    private Integer pageView;\n    private Integer commentCount;\n    private String coverImageUrl;\n    private Integer categoryId;\n    private List<Integer> tagIds;\n\n    private Date activityTime;\n    private Date createTime;\n    private Date updateTime;\n\n    String html;\n    User user;\n    List<Comment> comments;\n    List<Topic> similarTopics;\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/config/SyncEsMqConfig.java",
    "content": "package com.acimage.community.mq.config;\n\nimport com.acimage.common.global.consts.MqConstants;\nimport org.springframework.amqp.core.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class SyncEsMqConfig {\n    @Autowired\n    AmqpAdmin rabbitAdmin;\n\n    @Bean\n    public Queue syncEsQueue() {\n        // durable:是否持久化,默认是false,持久化队列：会被存储在磁盘上，当消息代理重启时仍然存在，暂存队列：当前连接有效\n        // exclusive:默认也是false，只能被当前创建的连接使用，而且当连接关闭后队列即被删除。此参考优先级高于durable\n        // autoDelete:是否自动删除，当没有生产者或者消费者使用此队列，该队列会自动删除。\n        //   return new Queue(\"TestDirectQueue\",true,true,false);\n\n        //一般设置一下队列的持久化就好,其余两个就是默认false\n        return new Queue(MqConstants.SYNC_ES_QUEUE, true);\n    }\n\n\n    @Bean\n    DirectExchange syncEsExchange() {\n        return new DirectExchange(MqConstants.SYNC_ES_EXCHANGE, true, false);\n    }\n\n    //绑定  将队列和交换机绑定, 并设置用于匹配键\n    @Bean\n    Binding bindingSyncEs() {\n        return BindingBuilder.bind(syncEsQueue()).to(syncEsExchange()).with(MqConstants.SYNC_ES_ROUTE);\n    }\n\n    //创建交换机和队列\n    @Bean\n    public void createExchangeQueueForSyncEs() {\n        rabbitAdmin.declareExchange(syncEsExchange());\n        rabbitAdmin.declareQueue(syncEsQueue());\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/config/SyncImagesMqConfig.java",
    "content": "package com.acimage.community.mq.config;\n\nimport com.acimage.common.global.consts.MqConstants;\nimport org.springframework.amqp.core.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class SyncImagesMqConfig {\n\n    @Autowired\n    AmqpAdmin rabbitAdmin;\n    @Bean\n    public Queue syncImagesQueue() {\n        // durable:是否持久化,默认是false,持久化队列：会被存储在磁盘上，当消息代理重启时仍然存在，暂存队列：当前连接有效\n        // exclusive:默认也是false，只能被当前创建的连接使用，而且当连接关闭后队列即被删除。此参考优先级高于durable\n        // autoDelete:是否自动删除，当没有生产者或者消费者使用此队列，该队列会自动删除。\n        //   return new Queue(\"TestDirectQueue\",true,true,false);\n\n        //一般设置一下队列的持久化就好,其余两个就是默认false\n        return new Queue(MqConstants.SYNC_IMAGES_QUEUE, true);\n    }\n\n\n    @Bean\n    DirectExchange topicImagesExchange() {\n        return new DirectExchange(MqConstants.TOPIC_IMAGES_EXCHANGE, true, false);\n    }\n\n    //绑定  将队列和交换机绑定, 并设置用于匹配键\n    @Bean\n    Binding bindingSyncImages() {\n        return BindingBuilder.bind(syncImagesQueue()).to(topicImagesExchange()).with(MqConstants.SYNC_IMAGES_ROUTE);\n    }\n\n    //创建交换机和队列\n    @Bean\n    public void createExchangeQueueForSyncImages() {\n        rabbitAdmin.declareExchange(topicImagesExchange());\n        rabbitAdmin.declareQueue(syncImagesQueue());\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/config/UserMsgMqConfig.java",
    "content": "package com.acimage.community.mq.config;\n\nimport com.acimage.common.global.consts.MqConstants;\nimport org.springframework.amqp.core.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class UserMsgMqConfig {\n    @Autowired\n    AmqpAdmin rabbitAdmin;\n\n    @Bean\n    public Queue userMsgQueue() {\n        return new Queue(MqConstants.USER_MSG_QUEUE, true);\n    }\n\n    @Bean\n    DirectExchange communityUserExchange() {\n        return new DirectExchange(MqConstants.COMMUNITY_USER_EXCHANGE, true, false);\n    }\n\n    //绑定  将队列和交换机绑定, 并设置用于匹配键\n    @Bean\n    Binding bindingUserMsg() {\n        return BindingBuilder.bind(userMsgQueue()).to(communityUserExchange()).with(MqConstants.USER_MSG_ROUTE);\n    }\n\n\n    //创建交换机和队列\n    @Bean\n    public void createExchangeQueueForUserMsg() {\n        rabbitAdmin.declareExchange(communityUserExchange());\n        rabbitAdmin.declareQueue(userMsgQueue());\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/consumer/SyncEsConsumer.java",
    "content": "package com.acimage.community.mq.consumer;\n\nimport com.acimage.common.global.consts.EsConstants;\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.mq.dto.EsAddDto;\nimport com.acimage.common.model.mq.dto.EsDeleteDto;\nimport com.acimage.common.model.mq.dto.EsUpdateByIdDto;\nimport com.acimage.common.model.mq.dto.EsUpdateByTermDto;\nimport com.acimage.common.utils.EsUtils;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.rabbitmq.client.Channel;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\n\n@Slf4j\n@Component\n@RabbitListener(queues = MqConstants.SYNC_ES_QUEUE)\npublic class SyncEsConsumer {\n\n    @Autowired\n    EsUtils esUtils;\n\n    @RabbitHandler\n    public void syncAdd(Channel channel, Message message, EsAddDto esAddDto) {\n        log.info(\"同步es数据：{}\", esAddDto);\n        try {\n            esUtils.save(esAddDto);\n\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"同步es数据失败 error:{} data：{}\", e.getMessage(), esAddDto);\n\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"同步es数据ack失败 error:{} message:{}\", e.getMessage(), messageBody);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(ex);\n                    log.error(\"同步es数据reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n                }\n            }\n        }\n    }\n\n\n    @RabbitHandler\n    public void syncDelete(Channel channel, Message message, EsDeleteDto esDeleteDto) {\n        log.info(\"同步es数据：{}\", esDeleteDto);\n        try {\n            esUtils.remove(esDeleteDto);\n\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"同步es数据失败 error:{} data：{}\", e.getMessage(), esDeleteDto);\n\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"同步es数据ack失败 error:{} message:{}\", e.getMessage(), messageBody);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(ex);\n                    log.error(\"同步es数据reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n                }\n            }\n        }\n    }\n\n    @RabbitHandler\n    public void syncUpdate(Channel channel, Message message, EsUpdateByIdDto esUpdateDto) {\n        log.info(\"同步es数据：{}\", esUpdateDto);\n        try {\n            esUtils.updateById(esUpdateDto);\n\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"同步es数据失败 error:{} data：{}\", e.getMessage(), esUpdateDto);\n\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"同步es数据ack失败 error:{} message:{}\", e.getMessage(), messageBody);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(ex);\n                    log.error(\"同步es数据reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n                }\n            }\n        }\n    }\n\n//    @RabbitHandler\n//    public void syncBatchUpdate(Channel channel, Message message, EsUpdateByTermDto updateDto) {\n//        log.info(\"同步es数据：{}\", updateDto);\n//        try {\n//            esUtils.UpdateByTerm(updateDto);\n//\n//        } catch (Exception e) {\n//            e.printStackTrace();\n//            log.error(\"同步es数据失败 error:{} data：{}\", e.getMessage(), updateDto);\n//\n//        } finally {\n//            String messageBody = new String(message.getBody());\n//            try {\n//                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n//            } catch (IOException e) {\n//                e.printStackTrace();\n//                log.error(\"同步es数据ack失败 error:{} message:{}\", e.getMessage(), messageBody);\n//                try {\n//                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n//                } catch (IOException ex) {\n//                    ex.printStackTrace();\n//                    log.error(\"同步es数据reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n//                }\n//            }\n//        }\n//    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/consumer/SyncUserConsumer.java",
    "content": "package com.acimage.community.mq.consumer;\n\n\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.mq.dto.EsUpdateByTermDto;\nimport com.acimage.common.model.mq.dto.UserIdWithPhotoUrl;\nimport com.acimage.common.model.mq.dto.UserIdWithUsername;\n\nimport com.acimage.common.utils.EsUtils;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.SpringContextUtils;\nimport com.acimage.community.service.cmtyuser.CmtyUserWriteService;\nimport com.rabbitmq.client.Channel;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\nimport java.util.List;\n\n@Slf4j\n@Component\n@RabbitListener(queues = MqConstants.SYNC_USER_QUEUE)\npublic class SyncUserConsumer {\n\n    @Autowired\n    CmtyUserWriteService cmtyUserWriteService;\n    @Autowired\n    EsUtils esUtils;\n\n    @RabbitHandler\n    public void syncUsername(Channel channel, Message message, UserIdWithUsername userIdWithUsername) {\n        log.info(\"同步用户名：{}\", userIdWithUsername);\n        try {\n            cmtyUserWriteService.updateUsername(userIdWithUsername.getUserId(), userIdWithUsername.getUsername());\n            TopicIndex topicIndex = TopicIndex.builder()\n                    .userId(userIdWithUsername.getUserId())\n                    .username(userIdWithUsername.getUsername())\n                    .build();\n            EsUpdateByTermDto updateDto = new EsUpdateByTermDto();\n            updateDto.with(topicIndex);\n            String termColumn = LambdaUtils.columnOf(TopicIndex::getUserId);\n            List<String> columns = LambdaUtils.columnsFrom(TopicIndex::getUsername);\n            updateDto.setTermColumn(termColumn);\n            updateDto.setColumns(columns);\n            esUtils.UpdateByTerm(updateDto);\n\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"同步用户名任务失败 error:{} 对象：{}\", e.getMessage(), userIdWithUsername);\n\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"同步用户名ack失败 error:{} message:{}\", e.getMessage(), messageBody);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(e);\n                    log.error(\"同步用户名reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n                }\n            }\n        }\n\n    }\n\n\n    @RabbitHandler\n    public void syncPhotoUrl(Channel channel, Message message, UserIdWithPhotoUrl userIdWithPhotoUrl) {\n        log.info(\"同步头像地址：{}\", userIdWithPhotoUrl);\n        try {\n            cmtyUserWriteService.updatePhotoUrl(userIdWithPhotoUrl.getUserId(), userIdWithPhotoUrl.getPhotoUrl());\n            TopicIndex topicIndex = TopicIndex.builder()\n                    .userId(userIdWithPhotoUrl.getUserId())\n                    .photoUrl(userIdWithPhotoUrl.getPhotoUrl())\n                    .build();\n            EsUpdateByTermDto updateDto = new EsUpdateByTermDto();\n            updateDto.with(topicIndex);\n            String termColumn = LambdaUtils.columnOf(TopicIndex::getUserId);\n            List<String> columns = LambdaUtils.columnsFrom(TopicIndex::getPhotoUrl);\n            updateDto.setTermColumn(termColumn);\n            updateDto.setColumns(columns);\n            esUtils.UpdateByTerm(updateDto);\n\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"同步头像地址任务失败 error:{} data:{}\", e.getMessage(), userIdWithPhotoUrl);\n\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"同步头像地址ack失败 error:{} message:{}\", e.getMessage(), messageBody);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(e);\n                    log.error(\"同步头像地址reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n                }\n            }\n        }\n    }\n\n    @RabbitHandler\n    public void addUser(Channel channel, Message message, CmtyUser cmtyUser) {\n        log.info(\"新增用户：{}\", cmtyUser);\n        try {\n            cmtyUserWriteService.save(cmtyUser);\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"新增用户 error:{} 对象：{}\", e.getMessage(), cmtyUser);\n\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"新增用户ack失败 error:{} message:{}\", e.getMessage(), messageBody);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(e);\n                    log.error(\"新增用户reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n                }\n            }\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/producer/SyncEsMqProducer.java",
    "content": "package com.acimage.community.mq.producer;\n\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.mq.dto.EsAddDto;\nimport com.acimage.common.model.mq.dto.EsDeleteDto;\nimport com.acimage.common.model.mq.dto.EsUpdateByIdDto;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\npublic class SyncEsMqProducer {\n\n    @Autowired\n    RabbitTemplate rabbitTemplate;\n\n    public void sendAddMessage(Object entity) {\n        EsAddDto esAddDto = new EsAddDto(entity);\n        rabbitTemplate.convertAndSend(MqConstants.SYNC_ES_EXCHANGE, MqConstants.SYNC_ES_ROUTE, esAddDto);\n    }\n\n    public void sendUpdateMessage(Object entity, List<String> columns) {\n        EsUpdateByIdDto esUpdateDto = new EsUpdateByIdDto();\n        esUpdateDto.with(entity);\n        esUpdateDto.setColumns(columns);\n        rabbitTemplate.convertAndSend(MqConstants.SYNC_ES_EXCHANGE, MqConstants.SYNC_ES_ROUTE, esUpdateDto);\n    }\n\n    public void sendDeleteMessage(String id,Class<?> type) {\n        EsDeleteDto esDeleteDto=new EsDeleteDto(id,type);\n        rabbitTemplate.convertAndSend(MqConstants.SYNC_ES_EXCHANGE, MqConstants.SYNC_ES_ROUTE, esDeleteDto);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/producer/UserMsgMqProducer.java",
    "content": "package com.acimage.community.mq.producer;\n\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.domain.user.CommentMsg;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class UserMsgMqProducer {\n\n    @Autowired\n    RabbitTemplate rabbitTemplate;\n\n    public void sendCommentMessage(CommentMsg commentMsg) {\n        rabbitTemplate.convertAndSend(MqConstants.COMMUNITY_USER_EXCHANGE, MqConstants.USER_MSG_ROUTE, commentMsg);\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/mq/producer/syncImagesMqProducer.java",
    "content": "package com.acimage.community.mq.producer;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.mq.dto.SyncImagesUpdateDto;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class syncImagesMqProducer {\n\n    @Autowired\n    RabbitTemplate rabbitTemplate;\n\n//    public void sendUploadImageMessage(long imageId, String url) {\n//        ImageIdWithUrl imageIdWithUrl = new ImageIdWithUrl(imageId, url);\n//        rabbitTemplate.convertAndSend(MqConstants.TOPIC_IMAGES_EXCHANGE, MqConstants.HASH_IMAGE_ROUTE, imageIdWithUrl);\n//    }\n//\n//    public void sendHashImagesMessage(long topicId) {\n//        rabbitTemplate.convertAndSend(MqConstants.TOPIC_IMAGES_EXCHANGE, MqConstants.HASH_IMAGE_ROUTE, topicId);\n//    }\n\n    public void sendSyncImagesMessage(SyncImagesUpdateDto updateDto) {\n        if (!CollectionUtil.isEmpty(updateDto.getAddImageUrls()) ||\n                !CollectionUtil.isEmpty(updateDto.getRemoveImageUrls())) {\n            rabbitTemplate.convertAndSend(MqConstants.TOPIC_IMAGES_EXCHANGE, MqConstants.SYNC_IMAGES_ROUTE, updateDto);\n        }\n    }\n\n//    public void sendRemoveTopicMessage(long topicId){\n//\n//        rabbitTemplate.convertAndSend(MqConstants.TOPIC_IMAGES_EXCHANGE,\n//                MqConstants.REMOVE_TOPIC_IMAGE_ROUTE, topicId);\n//    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/runner/CreateIndexRunner.java",
    "content": "package com.acimage.community.runner;\n\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.utils.EsUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\npublic class CreateIndexRunner implements ApplicationRunner {\n\n    @Autowired\n    EsUtils esUtils;\n\n\n    @Override\n    public void run(ApplicationArguments args) {\n        esUtils.createIndexIfNotExist(TopicIndex.class);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/runner/PreheatApplicationRunner.java",
    "content": "package com.acimage.community.runner;\n\nimport com.acimage.community.service.topic.TopicPreheatService;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Component\npublic class PreheatApplicationRunner implements ApplicationRunner {\n\n    @Autowired\n    TopicPreheatService topicPreheatService;\n\n    @Override\n    public void run(ApplicationArguments args) {\n        log.info(\"start 预热热点topic\");\n        int initialRankSize = 30;\n        int cacheSize = 10;\n        long expireSeconds = 3L;\n\n        for (TopicAttribute attr : TopicAttribute.values()) {\n            log.info(\"start 根据{}预热\", attr);\n            int size=initialRankSize;\n            if(attr==TopicAttribute.ACTIVITY_TIME){\n                size=1000;\n            }\n            topicPreheatService.preheatTopicsOrderBy(attr, size, cacheSize,\n                    expireSeconds, TimeUnit.SECONDS);\n        }\n        log.info(\"end 预热热点topic\");\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdateActivityTimeJob.java",
    "content": "package com.acimage.community.schedule;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\nimport org.springframework.stereotype.Component;\n\n\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * 不再定时任务更新活跃时间，直接在增加评论或修改话题的时候直接更新\n */\n@Deprecated\n@Slf4j\npublic class UpdateActivityTimeJob extends QuartzJobBean {\n\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n\n    /**\n     * 从redis中获取话题的新增浏览量并写入到数据库中\n     */\n    @Override\n    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {\n        //批量更新到数据库的大小\n        final int BATCH_SIZE = 10;\n        log.info(\"start 系统定时任务：保存活跃时间\");\n        //获取哪些话题评论数有变化\n        List<Long> topicIdList = redisUtils.membersForSet(TopicKeyConstants.SETK_RECORDING_ACTIVITY_TIME, Long.class);\n        if (CollectionUtil.isEmpty(topicIdList)) {\n            return;\n        }\n\n        StringBuilder logString = new StringBuilder();\n\n        int index = 0;\n\n        //获取话题id，评论数增量，相应redis的key\n        List<String> activityTimeKeys = new ArrayList<>(BATCH_SIZE);\n        List<Long> batchTopicIds = new ArrayList<>(BATCH_SIZE);\n        List<Date> batchActivityTime = new ArrayList<>(BATCH_SIZE);\n\n        for (Long topicId : topicIdList) {\n\n            index++;\n            String activityTimeKey = TopicKeyConstants.STRINGKP_TOPIC_ACTIVITY_TIME + topicId;\n\n            //获取活跃时间\n            Date activityTime = redisUtils.getObjectFromString(activityTimeKey, Date.class);\n\n            if (activityTime != null) {\n                batchTopicIds.add(topicId);\n                batchActivityTime.add(activityTime);\n            }\n            activityTimeKeys.add(activityTimeKey);\n\n            //日志记录浏览量增加的信息\n            logString.append(String.format(\"%s活跃时间更新为%s \", topicId, activityTime));\n\n            if (index % BATCH_SIZE == 0 || index == topicIdList.size()) {\n                //数据库批量增加浏览量\n                try {\n                    topicSpAttrWriteService.updateBatchActivityTime(batchTopicIds, batchActivityTime);\n                } catch (Exception e) {\n                    log.error(\"更新activityTime失败id:{} activityTime:{}\",batchTopicIds,batchActivityTime);\n                }\n\n                //批量移除对应值或删除对应键值，这两者顺序不可交换！\n                redisUtils.removeForSet(TopicKeyConstants.SETK_RECORDING_ACTIVITY_TIME, batchTopicIds);\n                redisUtils.delete(activityTimeKeys);\n\n                log.info(logString.toString());\n\n                //清空\n                batchTopicIds.clear();\n                batchActivityTime.clear();\n                activityTimeKeys.clear();\n                //重新初始化\n                index=0;\n                logString = new StringBuilder();\n            }\n        }\n\n\n        log.info(\"end 系统定时任务：保存活跃时间\");\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdateActivityTimeJobConfig.java",
    "content": "package com.acimage.community.schedule;\n\nimport org.quartz.*;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Deprecated\npublic class UpdateActivityTimeJobConfig {\n\n    private static String cron =\"0 */5 * * * ?\";\n\n    @Bean\n    public JobDetail updateActivityTimeJobDetail() {\n        JobDetail jobDetail = JobBuilder.newJob(UpdateActivityTimeJob.class)\n                .withIdentity(\"updateActivityTime\", \"topicGroup\")\n                .storeDurably()\n                .build();\n        return jobDetail;\n    }\n\n    @Bean\n    public Trigger updateActivityTimeJobTrigger() {\n        Trigger trigger = TriggerBuilder.newTrigger()\n                .forJob(updateActivityTimeJobDetail())\n                .withIdentity(\"updateActivityTimeTrigger\", \"topicTrigger\")\n                .startNow()\n                .withSchedule(CronScheduleBuilder.cronSchedule(cron))\n                .build();\n        return trigger;\n    }\n}\n\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdateCommentCountJob.java",
    "content": "package com.acimage.community.schedule;\n\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Deprecated\n@Slf4j\npublic class UpdateCommentCountJob extends QuartzJobBean {\n\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n\n    @Override\n    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {\n        //批量更新到数据库的大小\n        final int BATCH_SIZE = 10;\n        log.info(\"start 系统定时任务：保存评论数变化\");\n        //获取哪些话题评论数有变化\n        List<Long> topicIdList = redisUtils.membersForSet(TopicKeyConstants.SETK_RECORDING_COMMENT_COUNT_INCREMENT, Long.class);\n        if (CollectionUtil.isEmpty(topicIdList)) {\n            return;\n        }\n        StringBuilder logString = new StringBuilder();\n\n        int index = 0;\n\n        List<Long> batchTopicIds = new ArrayList<>(BATCH_SIZE);\n        List<Integer> batchCcIncrements = new ArrayList<>(BATCH_SIZE);\n\n        for (Long topicId : topicIdList) {\n\n            index++;\n            String ccIncrementKey = TopicKeyConstants.STRINGKP_TOPIC_COMMENT_COUNT_INCREMENT + topicId;\n            String hashKeyForTopic= TopicKeyConstants.HASHKP_TOPIC+topicId;\n            String fieldName=LambdaUtils.columnOf(Topic::getCommentCount);\n            Long ccIncrement = redisUtils.getAndCombineAndDelete(ccIncrementKey, hashKeyForTopic, fieldName);\n\n            //记录话题id，评论数增量，相应redis的key、value\n            if (ccIncrement != null) {\n                batchTopicIds.add(topicId);\n                batchCcIncrements.add(ccIncrement.intValue());\n            }else {\n                redisUtils.removeForSet(TopicKeyConstants.SETK_RECORDING_COMMENT_COUNT_INCREMENT, Long.toString(topicId));\n            }\n\n            //日志记录变化量\n            logString.append(String.format(\"%s评论变化量为%d \", topicId, ccIncrement));\n\n            if (index % BATCH_SIZE == 0 || index == topicIdList.size()) {\n                //数据库批量增加浏览量\n                try {\n                    topicSpAttrWriteService.updateCommentCountByIncrement(batchTopicIds, batchCcIncrements);\n                } catch (Exception e) {\n                    log.error(\"error:更新commentCount变化失败 ids:{} increments:{}\",batchTopicIds,batchCcIncrements);\n                }\n\n                //批量移除对应值或删除对应键值，这两者顺序不可交换！\n                redisUtils.removeForSet(TopicKeyConstants.SETK_RECORDING_COMMENT_COUNT_INCREMENT, batchTopicIds);\n\n                log.debug(logString.toString());\n                //清空\n                batchTopicIds.clear();\n                batchCcIncrements.clear();\n\n                //重新初始化\n                index=0;\n                logString = new StringBuilder();\n            }\n        }\n        log.info(\"end 系统定时任务：保存评论数变化\");\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdateCommentCountJobConfig.java",
    "content": "package com.acimage.community.schedule;\n\nimport org.quartz.*;\nimport org.springframework.context.annotation.Bean;\n\n@Deprecated\npublic class UpdateCommentCountJobConfig {\n\n    private String cron =\"0 */3 * * * ?\";\n\n    @Bean\n    public JobDetail updateCommentCountJobDetail() {\n        JobDetail jobDetail = JobBuilder.newJob(UpdateCommentCountJob.class)\n                .withIdentity(\"updateCommentCount\", \"topicGroup\")\n                .storeDurably()\n                .build();\n        return jobDetail;\n    }\n\n    @Bean\n    public Trigger updateCommentCountJobTrigger() {\n        Trigger trigger = TriggerBuilder.newTrigger()\n                .forJob(updateCommentCountJobDetail())\n                .withIdentity(\"updateCommentCountTrigger\", \"topicTrigger\")\n                .startNow()\n                .withSchedule(CronScheduleBuilder.cronSchedule(cron))\n                .build();\n        return trigger;\n    }\n}\n\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdatePageViewJob.java",
    "content": "package com.acimage.community.schedule;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.EsUtils;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.service.topic.TopicQueryService;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n@Component\n@Slf4j\npublic class UpdatePageViewJob extends QuartzJobBean {\n    private final long FIXED_RATE_MINUTES = 57L;\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n    @Autowired\n    TopicQueryService topicQueryService;\n    @Autowired\n    EsUtils esUtils;\n\n    /**\n     * 从redis中获取话题的新增浏览量并写入到数据库中\n     */\n    @Override\n    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {\n        //批量更新大小\n        final int BATCH_SIZE = 10;\n        log.info(\"start 系统定时任务：保存浏览量变化\");\n\n        //获取哪些话题被记录了浏览量\n        List<Long> topicIdList = redisUtils.membersForSet(TopicKeyConstants.SETK_RECORDING_PV_INCREMENT, Long.class);\n        if (CollectionUtil.isEmpty(topicIdList)) {\n            return;\n        }\n        StringBuilder logString = new StringBuilder();\n        int index = 0;\n\n        List<String> batchPvLogKeys = new ArrayList<>(BATCH_SIZE);\n        List<Long> batchTopicIds = new ArrayList<>(BATCH_SIZE);\n        List<Integer> batchPvIncrements = new ArrayList<>(BATCH_SIZE);\n\n        for (Long topicId : topicIdList) {\n            index++;\n            String pvLogKey = TopicKeyConstants.LOGKP_TOPIC_PV + topicId;\n            Long pvIncrement = redisUtils.sizeForHyperLogLog(pvLogKey);\n\n            //记录话题id，浏览量增量，相应redis的key、value\n            if (pvIncrement != null) {\n                batchTopicIds.add(topicId);\n                batchPvIncrements.add(pvIncrement.intValue());\n            } else {\n                redisUtils.removeForSet(TopicKeyConstants.SETK_RECORDING_PV_INCREMENT,Long.toString(topicId) );\n            }\n            batchPvLogKeys.add(pvLogKey);\n\n            //日志记录浏览量增加的信息\n            logString.append(String.format(\"%s增加浏览量%d \", topicId, pvIncrement));\n\n            if (index % BATCH_SIZE == 0 || index == topicIdList.size()) {\n                //数据库批量增加浏览量\n                try {\n                    topicSpAttrWriteService.updatePageViewByIncrement(batchTopicIds, batchPvIncrements);\n                } catch (Exception e) {\n                    log.error(\"error:数据库批量更新pageView变化失败 ids:{} increments:{}\",batchTopicIds,batchPvIncrements);\n                }\n\n                List<Topic> topicList=topicQueryService.listTopicsByIds(batchTopicIds);\n                List<TopicIndex> topicIndexList= topicList.stream().map(TopicIndex::from).collect(Collectors.toList());\n                List<String> columns= LambdaUtils.columnsFrom(TopicIndex::getPageView);\n                try {\n                    esUtils.batchUpdateById(topicIndexList,columns);\n                } catch (Exception e) {\n                    List<Integer> pageViews= ListUtils.extract(Topic::getPageView,topicList);\n                    log.error(\"error:es批量更新pageView ids:{} pvs:{}\",batchTopicIds,pageViews);\n                }\n\n\n                //批量移除对应值或删除对应键值，这两者顺序不可交换！\n                redisUtils.removeForSet(TopicKeyConstants.SETK_RECORDING_PV_INCREMENT, batchTopicIds);\n                redisUtils.delete(batchPvLogKeys);\n\n                //清空\n                batchTopicIds.clear();\n                batchPvIncrements.clear();\n                batchPvLogKeys.clear();\n                //重新初始化\n                index = 0;\n                log.info(logString.toString());\n                logString = new StringBuilder();\n            }\n        }\n\n\n        log.info(\"end 系统定时任务：保存浏览量变化\");\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdatePageViewJobConfig.java",
    "content": "package com.acimage.community.schedule;\n\nimport org.quartz.*;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class UpdatePageViewJobConfig {\n\n    @Value(\"${cron.page-view-job}\")\n    private String cron ;\n\n    @Bean\n    public JobDetail updatePageViewJobDetail() {\n        JobDetail jobDetail = JobBuilder.newJob(UpdatePageViewJob.class)\n                .withIdentity(\"updatePageView\", \"topicGroup\")\n                .storeDurably()\n                .build();\n        return jobDetail;\n    }\n\n    @Bean\n    public Trigger updatePageViewJobTrigger() {\n        Trigger trigger = TriggerBuilder.newTrigger()\n                .forJob(updatePageViewJobDetail())\n                .withIdentity(\"updatePageViewTrigger\", \"topicTrigger\")\n                .withSchedule(CronScheduleBuilder.cronSchedule(cron))\n                .startNow()\n                .build();\n        return trigger;\n    }\n}\n\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdateStarCountJob.java",
    "content": "package com.acimage.community.schedule;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n@Deprecated\n@Slf4j\npublic class UpdateStarCountJob extends QuartzJobBean {\n    private final long FIXED_RATE_MINUTES = 11L;\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n\n    /**\n     * 从redis中获取话题的新增浏览量并写入到数据库中\n     */\n    @Override\n    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {\n        //批量更新到数据库的大小\n        final int BATCH_SIZE = 10;\n        log.info(\"START 系统定时任务：保存收藏数变化\");\n        //获取哪些话题评论数有变化\n        List<Long> topicIdList = redisUtils.membersForSet(TopicKeyConstants.SETK_RECORDING_STAR_COUNT_INCREMENT, Long.class);\n        if (CollectionUtil.isEmpty(topicIdList)) {\n            return;\n        }\n\n\n        StringBuilder logString = new StringBuilder();\n        int index = 0;\n\n\n        List<Long> batchTopicIds = new ArrayList<>(BATCH_SIZE);\n        List<Integer> batchScIncrements = new ArrayList<>(BATCH_SIZE);\n\n        for (Long topicId : topicIdList) {\n            index++;\n            String scIncrementKey = TopicKeyConstants.STRINGKP_TOPIC_STAR_COUNT_INCREMENT + topicId;\n            String hashKeyForTopic= TopicKeyConstants.HASHKP_TOPIC+topicId;\n            String fieldName= LambdaUtils.columnOf(Topic::getStarCount);\n            Long scIncrement = redisUtils.getAndCombineAndDelete(scIncrementKey, hashKeyForTopic, fieldName);\n\n\n            //记录话题id，评论数增量，相应redis的key、value\n            if (scIncrement != null) {\n                batchTopicIds.add(topicId);\n                batchScIncrements.add(scIncrement.intValue());\n            }else{\n                redisUtils.removeForSet(TopicKeyConstants.SETK_RECORDING_STAR_COUNT_INCREMENT,Long.toString(topicId));\n            }\n\n\n            //日志记录浏览量增加的信息\n            logString.append(String.format(\"%s收藏变化量为%d \", topicId, scIncrement));\n\n            if (index % BATCH_SIZE == 0 || index == topicIdList.size()) {\n                //数据库批量增加浏览量\n                try {\n                    topicSpAttrWriteService.updateStarCountByIncrement(batchTopicIds, batchScIncrements);\n                } catch (Exception e) {\n                    log.error(\"error:更新starCount变化失败 ids:{} increments:{}\",batchTopicIds,batchScIncrements);\n                }\n\n                //批量移除对应值或删除对应键值，这两者顺序不可交换！\n                redisUtils.removeForSet(TopicKeyConstants.SETK_RECORDING_STAR_COUNT_INCREMENT, batchTopicIds);\n\n                log.info(logString.toString());\n\n                //清空\n                batchTopicIds.clear();\n                batchScIncrements.clear();\n\n                //重新初始化\n                index=0;\n                logString = new StringBuilder();\n            }\n        }\n\n        log.info(\"END 系统定时任务：保存收藏数变化\");\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/schedule/UpdateStarCountJobConfig.java",
    "content": "package com.acimage.community.schedule;\n\nimport org.quartz.*;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Deprecated\npublic class UpdateStarCountJobConfig {\n    //3分钟\n    private String cron =\"0 */3 * * * ?\";\n\n    @Bean\n    public JobDetail updateStarCountJobDetail() {\n        JobDetail jobDetail = JobBuilder.newJob(UpdateStarCountJob.class)\n                .withIdentity(\"updateStarCount\", \"topicGroup\")\n                .storeDurably()\n                .build();\n        return jobDetail;\n    }\n\n    @Bean\n    public Trigger updateStarCountJobTrigger() {\n        Trigger trigger = TriggerBuilder.newTrigger()\n                .forJob(updateStarCountJobDetail())\n                .withIdentity(\"updateStarCountTrigger\", \"topicTrigger\")\n                .startNow()\n                .withSchedule(CronScheduleBuilder.cronSchedule(cron))\n                .build();\n        return trigger;\n    }\n}\n\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/categoty/CategoryQueryService.java",
    "content": "package com.acimage.community.service.categoty;\n\nimport com.acimage.common.model.domain.community.Category;\n\nimport java.util.List;\n\npublic interface CategoryQueryService {\n    List<Category> listAll();\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/categoty/impl/CategoryQueryServiceImpl.java",
    "content": "package com.acimage.community.service.categoty.impl;\n\nimport com.acimage.common.model.domain.community.Category;\nimport com.acimage.community.dao.CategoryDao;\nimport com.acimage.community.service.categoty.CategoryQueryService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class CategoryQueryServiceImpl implements CategoryQueryService {\n\n    @Autowired\n    CategoryDao categoryDao;\n\n    @Override\n    public List<Category> listAll(){\n        return categoryDao.selectList(null);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/cmtyuser/CmtyUserQueryService.java",
    "content": "package com.acimage.community.service.cmtyuser;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.redis.enums.DataType;\nimport com.acimage.community.depreted.userstatistic.consts.KeyConstants;\n\npublic interface CmtyUserQueryService {\n    @QueryRedis(keyPrefix = KeyConstants.STRINGKP_CMTY_USER, expire = 10L, dataType = DataType.HASH)\n    CmtyUser getCmtyUser(long userId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/cmtyuser/CmtyUserRankService.java",
    "content": "package com.acimage.community.service.cmtyuser;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.redis.annotation.QueryRedis;\n\nimport java.util.List;\n\npublic interface CmtyUserRankService {\n    MyPage<CmtyUser> pageUserRankBy(String column, int pageNo, int pageSize);\n\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:topicCount:\",expire = 5L)\n    List<CmtyUser> pageUserRankByTopicCount(int pageNo, int pageSize);\n\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:starCount:\",expire = 5L)\n    List<CmtyUser> pageUserRankByStarCount(int pageNo, int pageSize);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/cmtyuser/CmtyUserWriteService.java",
    "content": "package com.acimage.community.service.cmtyuser;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.List;\n\npublic interface CmtyUserWriteService {\n    void updateUsername(long userId, String username);\n\n    void updatePhotoUrl(long userId, String photoUrl);\n\n    @Transactional\n    void save(CmtyUser cmtyUser);\n\n    Integer updateStarCountByIncrements(List<Long> userIds, List<Integer> starCounts);\n\n    Integer updateTopicCountByIncrements(List<Long> userIds, List<Integer> starCounts);\n\n    Integer updateTopicCountByIncrement(long userId, int increment);\n\n    Integer updateStarCountByIncrement(long userId, int increment);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/cmtyuser/impl/CmtyUserQueryServiceImpl.java",
    "content": "package com.acimage.community.service.cmtyuser.impl;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.redis.enums.DataType;\nimport com.acimage.community.dao.CmtyUserDao;\nimport com.acimage.community.service.cmtyuser.CmtyUserQueryService;\nimport com.acimage.community.depreted.userstatistic.consts.KeyConstants;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class CmtyUserQueryServiceImpl implements CmtyUserQueryService {\n    @Autowired\n    CmtyUserDao cmtyUserDao;\n\n    @Override\n    @QueryRedis(keyPrefix = KeyConstants.STRINGKP_CMTY_USER, expire = 10L, dataType = DataType.HASH)\n    public CmtyUser getCmtyUser(long userId) {\n        return cmtyUserDao.selectById(userId);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/cmtyuser/impl/CmtyUserRankServiceImpl.java",
    "content": "package com.acimage.community.service.cmtyuser.impl;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.acimage.community.dao.CmtyUserDao;\nimport com.acimage.community.service.cmtyuser.CmtyUserRankService;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class CmtyUserRankServiceImpl implements CmtyUserRankService {\n    @Autowired\n    CmtyUserDao cmtyUserDao;\n\n    private List<CmtyUser> pageUserRankBy(SFunction<CmtyUser, Integer> attr, int pageNo, int pageSize) {\n        int start = PageUtils.startIndexOf(pageNo, pageSize);\n        String column = LambdaUtils.underlineColumnNameOf(attr);\n        List<CmtyUser> cmtyUserList = cmtyUserDao.selectListOrderByColumn(column, start, pageSize);\n        return cmtyUserList;\n    }\n\n    @Override\n    public MyPage<CmtyUser> pageUserRankBy(String column, int pageNo, int pageSize) {\n        String underlineColumn = StringUtils.camelToUnderline(column);\n\n        IPage<CmtyUser> page = new Page<>(pageNo, pageSize);\n        QueryWrapper<CmtyUser> qw = new QueryWrapper<>();\n        qw.orderByDesc(underlineColumn);\n\n        return MyPage.from(cmtyUserDao.selectPage(page, qw));\n\n    }\n\n    @Override\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:topicCount:\", expire = 5L)\n    public List<CmtyUser> pageUserRankByTopicCount(int pageNo, int pageSize) {\n        return pageUserRankBy(CmtyUser::getTopicCount, pageNo, pageSize);\n    }\n\n    @Override\n    @QueryRedis(keyPrefix = \"acimage:community:users:rank:starCount:\", expire = 5L)\n    public List<CmtyUser> pageUserRankByStarCount(int pageNo, int pageSize) {\n        return pageUserRankBy(CmtyUser::getStarCount, pageNo, pageSize);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/cmtyuser/impl/CmtyUserWriteServiceImpl.java",
    "content": "package com.acimage.community.service.cmtyuser.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.common.PairUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.dao.CmtyUserDao;\nimport com.acimage.community.service.cmtyuser.CmtyUserWriteService;\nimport com.acimage.community.depreted.userstatistic.consts.KeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class CmtyUserWriteServiceImpl implements CmtyUserWriteService {\n    @Autowired\n    CmtyUserDao cmtyUserDao;\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public void updateUsername(long userId, String username) {\n        LambdaUpdateWrapper<CmtyUser> uw = new LambdaUpdateWrapper<>();\n        uw.set(CmtyUser::getUsername, username)\n                .eq(CmtyUser::getId, userId);\n        cmtyUserDao.update(null, uw);\n        redisUtils.delete(KeyConstants.STRINGKP_CMTY_USER);\n    }\n\n    @Override\n    public void updatePhotoUrl(long userId, String photoUrl) {\n        LambdaUpdateWrapper<CmtyUser> uw = new LambdaUpdateWrapper<>();\n        uw.set(CmtyUser::getPhotoUrl, photoUrl)\n                .eq(CmtyUser::getId, userId);\n        cmtyUserDao.update(null, uw);\n        redisUtils.delete(KeyConstants.STRINGKP_CMTY_USER);\n    }\n\n    @Override\n    public void save(CmtyUser cmtyUser) {\n        cmtyUserDao.insert(cmtyUser);\n    }\n\n\n    @Override\n    public Integer updateStarCountByIncrements(List<Long> userIds, List<Integer> starCounts){\n        List<Pair<Long,Integer>> userIdAndStarCounts= PairUtils.combine(userIds,starCounts);\n        if(CollectionUtil.isEmpty(userIds)){\n            return 0;\n        }\n        return cmtyUserDao.batchUpdateStarCount(userIdAndStarCounts);\n    }\n\n    @Override\n    public Integer updateTopicCountByIncrements(List<Long> userIds, List<Integer> starCounts){\n        List<Pair<Long,Integer>> userIdAndStarCounts= PairUtils.combine(userIds,starCounts);\n        if(CollectionUtil.isEmpty(userIds)){\n            return 0;\n        }\n        return cmtyUserDao.batchUpdateTopicCount(userIdAndStarCounts);\n    }\n\n    @Override\n    public Integer updateTopicCountByIncrement( long userId, int increment){\n        redisUtils.delete(KeyConstants.STRINGKP_CMTY_USER +userId);\n        return cmtyUserDao.updateTopicCountByIncrement(userId,increment);\n    }\n\n    @Override\n    public Integer updateStarCountByIncrement(long userId, int increment) {\n        int col=cmtyUserDao.updateStarCountByIncrement(userId,increment);\n        String key=KeyConstants.STRINGKP_CMTY_USER +userId;\n        String column= LambdaUtils.columnOf(CmtyUser::getStarCount);\n        redisUtils.incrementIfPresentForFieldKey(key,column,increment);\n        return col;\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/CommentInfoQueryService.java",
    "content": "package com.acimage.community.service.comment;\n\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.model.page.MyPage;\n\nimport java.util.List;\n\npublic interface CommentInfoQueryService {\n    List<Comment> pageCommentsWithUser(long topicId, int pageNo,int pageSize);\n\n    MyPage<Comment> pageCommentsWithTopicOrderByCreateTime(long userId, int pageNo,int pageSize);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/CommentQueryService.java",
    "content": "package com.acimage.community.service.comment;\n\nimport com.acimage.common.model.domain.community.Comment;\n\npublic interface CommentQueryService {\n    Comment getComment(long commentId);\n    Integer getCommentCount(long topicId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/CommentWriteService.java",
    "content": "package com.acimage.community.service.comment;\n\nimport com.acimage.community.model.request.CommentAddReq;\nimport com.acimage.community.model.request.CommentModifyReq;\nimport org.springframework.transaction.annotation.Transactional;\n\npublic interface CommentWriteService {\n\n    Integer saveComment(CommentAddReq commentAddReq);\n\n\n    Integer removeComment(long commentId);\n\n    Integer removeCommentWithoutVerification(long commentId);\n\n    Integer removeComments(long topicId);\n\n    Integer updateComment(CommentModifyReq commentModifyReq);\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/annotation/Operation.java",
    "content": "package com.acimage.community.service.comment.annotation;\n\npublic enum Operation {\n    ADD,\n    SUB,\n    REMOVE_ALL\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/annotation/UpdateCcByReturn.java",
    "content": "package com.acimage.community.service.comment.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 根据被注解的方法的返回值更改对应评论的话题数\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface UpdateCcByReturn {\n    Operation operation() default Operation.ADD;\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/impl/CommentInfoQueryServiceImpl.java",
    "content": "package com.acimage.community.service.comment.impl;\n\nimport com.acimage.common.redis.annotation.KeyParam;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.acimage.community.dao.CommentDao;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.community.service.comment.CommentInfoQueryService;\nimport com.acimage.community.global.consts.CommentKeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class CommentInfoQueryServiceImpl implements CommentInfoQueryService {\n\n    @Autowired\n    CommentDao commentDao;\n\n    @QueryRedis(keyPrefix = CommentKeyConstants.STRINGKP_TOPIC_COMMENTS, expire = 3L, unit = TimeUnit.SECONDS)\n    public List<Comment> pageCommentsWithUser(long topicId, int pageNo, int pageSize) {\n        //如果没有则查数据库\n        int startIndex = PageUtils.startIndexOf(pageNo, pageSize);\n        return commentDao.selectCommentsWithUser(topicId, startIndex, pageSize);\n    }\n\n    @QueryRedis(keyPrefix = CommentKeyConstants.STRINGKP_USER_COMMENTS, expire = 5, unit = TimeUnit.SECONDS)\n    @Override\n    public MyPage<Comment> pageCommentsWithTopicOrderByCreateTime(long userId, int pageNo, int pageSize) {\n        int startIndex = PageUtils.startIndexOf(pageNo, pageSize);\n        List<Comment> comments = commentDao.selectCommentsWithTopicOrderByCreateTime(userId, startIndex, pageSize);\n\n        //查询总数\n        LambdaQueryWrapper<Comment> qw = new LambdaQueryWrapper<>();\n        qw.eq(Comment::getUserId, userId);\n        int totalCount = commentDao.selectCount(qw);\n\n        return new MyPage<>(totalCount, comments);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/impl/CommentQueryServiceImpl.java",
    "content": "package com.acimage.community.service.comment.impl;\n\nimport com.acimage.common.redis.annotation.KeyParam;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.community.dao.CommentDao;\nimport com.acimage.community.service.comment.CommentQueryService;\nimport com.acimage.community.global.consts.CommentKeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.TimeUnit;\n\n\n@Service\npublic class CommentQueryServiceImpl implements CommentQueryService {\n    @Autowired\n    CommentDao commentDao;\n    @QueryRedis(keyPrefix = \"acimage:comments:id:\",expire = 5L, unit = TimeUnit.SECONDS)\n    @Override\n    public Comment getComment(@KeyParam long commentId) {\n        return commentDao.selectById(commentId);\n    }\n\n    @QueryRedis(keyPrefix = CommentKeyConstants.STRINGKP_COMMENT_COUNT,expire = 129L)\n    @Override\n    public Integer getCommentCount(@KeyParam long topicId) {\n        LambdaQueryWrapper<Comment> qw=new LambdaQueryWrapper<>();\n        qw.eq(Comment::getTopicId,topicId);\n        return commentDao.selectCount(qw);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/comment/impl/CommentWriteServiceImpl.java",
    "content": "package com.acimage.community.service.comment.impl;\n\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.domain.user.CommentMsg;\nimport com.acimage.common.utils.SensitiveWordUtils;\nimport com.acimage.common.utils.common.BeanUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.community.listener.event.CommentEvent;\nimport com.acimage.community.model.request.CommentAddReq;\nimport com.acimage.community.model.request.CommentModifyReq;\nimport com.acimage.community.mq.producer.UserMsgMqProducer;\nimport com.acimage.community.service.comment.CommentQueryService;\nimport com.acimage.community.service.comment.CommentWriteService;\nimport com.acimage.community.global.consts.CommentKeyConstants;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.dao.CommentDao;\nimport com.acimage.community.service.topic.TopicQueryService;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n\n@Service\n@Slf4j\npublic class CommentWriteServiceImpl implements CommentWriteService {\n    @Autowired\n    CommentDao commentDao;\n    @Autowired\n    CommentQueryService commentQueryService;\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n    @Autowired\n    TopicQueryService topicQueryService;\n    @Autowired\n    UserMsgMqProducer userMsgMqProducer;\n\n    @Override\n    public Integer saveComment(CommentAddReq commentAddReq) {\n        String publishedContent = redisUtils.getForString(CommentKeyConstants.STRINGKP_PUBLISHED_COMMENTS);\n        if (publishedContent != null && publishedContent.equals(commentAddReq.getContent())) {\n            log.warn(\"user:{}重复发表评论 title:{}\", UserContext.getUsername(), commentAddReq.getContent());\n            throw new BusinessException(\"短期已经发表过该评论了，请刷新尝试\");\n        }\n        Comment comment = new Comment();\n        BeanUtil.copyProperties(commentAddReq, comment);\n\n        Date now = new Date();\n        long id = IdGenerator.getSnowflakeNextId();\n        comment.setId(id);\n        comment.setUserId(UserContext.getUserId());\n        comment.setCreateTime(now);\n        comment.setUpdateTime(now);\n        //敏感词过滤\n        String filterContent = SensitiveWordUtils.filter(commentAddReq.getContent());\n        comment.setContent(filterContent);\n        int col = commentDao.insert(comment);\n\n        long topicId = commentAddReq.getTopicId();\n        //删除首页评论\n        String firstPageKey = CommentKeyConstants.keyOfTopicComments(topicId, 1);\n        redisUtils.delete(firstPageKey);\n        //更新评论数\n        topicSpAttrWriteService.increaseCommentCount(topicId, col);\n        //更新话题的最新活跃时间\n        topicSpAttrWriteService.changeActivityTime(topicId, new Date());\n\n        //发送消息通知用户\n        Topic topic = topicQueryService.getTopic(topicId);\n        CommentMsg commentMsg = CommentMsg.builder()\n                .commentId(id)\n                .content(filterContent)\n                .createTime(now)\n                .fromUserId(UserContext.getUserId())\n                .toUserId(topic.getUserId())\n                .topicTitle(topic.getTitle())\n                .topicId(topicId)\n                .build();\n        userMsgMqProducer.sendCommentMessage(commentMsg);\n\n        long timeout = 10L;\n        redisUtils.setAsString(CommentKeyConstants.STRINGKP_PUBLISHED_COMMENTS + UserContext.getUserId(),\n                commentAddReq.getContent(), timeout, TimeUnit.SECONDS);\n\n        return col;\n    }\n\n\n    @Override\n    public Integer removeComment(long commentId) {\n        Comment comment = commentQueryService.getComment(commentId);\n        if (comment == null) {\n            log.error(\"user:{} 删除评论{} 错误：评论不存在\", UserContext.getUsername(), commentId);\n            throw new BusinessException(\"评论不存在\");\n        }\n        if (!comment.getUserId().equals(UserContext.getUserId())) {\n            log.error(\"user:{} 删除 评论{} 错误：非评论持有者\", UserContext.getUsername(), commentId);\n            throw new BusinessException(\"非法操作\");\n        }\n        int col = commentDao.deleteById(commentId);\n\n        //如果影响redis话题首页评论，则删除\n        long topicId = comment.getTopicId();\n        String firstPageKey = CommentKeyConstants.keyOfTopicComments(topicId, 1);\n\n        redisUtils.delete(firstPageKey);\n\n        //更新评论数\n        topicSpAttrWriteService.increaseCommentCount(topicId, -col);\n\n        return col;\n    }\n\n    @Override\n    public Integer removeCommentWithoutVerification(long commentId) {\n        Comment comment = commentQueryService.getComment(commentId);\n        if (comment == null) {\n            log.error(\"user:{} 删除评论{} 错误：评论不存在\", UserContext.getUsername(), commentId);\n            throw new BusinessException(\"评论不存在\");\n        }\n        int col = commentDao.deleteById(commentId);\n\n        //如果影响redis话题首页评论，则删除\n        long topicId = comment.getTopicId();\n        String firstPageKey = CommentKeyConstants.keyOfTopicComments(topicId, 1);\n\n        redisUtils.delete(firstPageKey);\n\n        //更新评论数\n        topicSpAttrWriteService.increaseCommentCount(topicId, -col);\n\n        return col;\n    }\n\n    /**\n     * 删除话题时调用\n     */\n    @Override\n    public Integer removeComments(long topicId) {\n        int col = commentDao.deleteByTopicId(topicId);\n        //删除redis数据\n        redisUtils.delete(CommentKeyConstants.STRINGKP_TOPIC_COMMENTS + topicId);\n        return col;\n    }\n\n    @Override\n    public Integer updateComment(CommentModifyReq commentModifyReq) {\n        Date now = new Date();\n\n        Comment modifiedComment = BeanUtils.copyPropertiesTo(commentModifyReq, Comment.class);\n        //过滤敏感词\n        String filterContent = SensitiveWordUtils.filter(commentModifyReq.getContent());\n        modifiedComment.setContent(filterContent);\n\n        modifiedComment.setUpdateTime(now);\n        log.info(\"评论修改为{}\", modifiedComment);\n\n        long commentId = commentModifyReq.getId();\n\n        LambdaUpdateWrapper<Comment> uw = new LambdaUpdateWrapper<>();\n        uw.eq(Comment::getId, commentId)\n                .eq(Comment::getUserId, UserContext.getUserId());\n        int col = commentDao.update(modifiedComment, uw);\n\n        if (col == 0) {\n            log.error(\"用户{} 修改 评论{} 错误：评论已被删除或评论非当前用户所有\", UserContext.getUsername(), commentId);\n            throw new BusinessException(\"非法操作，更新失败\");\n        }\n\n        //找到评论\n        Comment originComment = commentQueryService.getComment(commentId);\n\n        //如果影响redis话题首页评论，则删除redis数据\n        long topicId = originComment.getTopicId();\n        String firstPageCommentsKey = CommentKeyConstants.keyOfTopicComments(topicId, 1);\n        List<Comment> comments = redisUtils.getListFromString(firstPageCommentsKey, Comment.class);\n        if (comments != null && ListUtils.extract(Comment::getId, comments).contains(commentId)) {\n            redisUtils.delete(firstPageCommentsKey);\n        }\n\n        //更新对应话题的最新活跃时间\n        topicSpAttrWriteService.changeActivityTime(topicId, now);\n        return col;\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/homecarousel/HomeCarouselQueryService.java",
    "content": "package com.acimage.community.service.homecarousel;\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\n\nimport java.util.List;\n\npublic interface HomeCarouselQueryService {\n\n    List<HomeCarousel> listAll();\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/homecarousel/impl/HomeCarouselQueryServiceImpl.java",
    "content": "package com.acimage.community.service.homecarousel.impl;\n\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport com.acimage.common.redis.annotation.QueryRedis;\n\nimport com.acimage.community.service.homecarousel.HomeCarouselQueryService;\nimport com.acimage.community.dao.HomeCarrouselDao;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n\n@Service\npublic class HomeCarouselQueryServiceImpl implements HomeCarouselQueryService {\n    public static final String STRINGK_HOME_CAROUSEL = \"acimage:community:homeCarousels:list\";\n    @Autowired\n    HomeCarrouselDao homeCarrouselDao;\n\n    @QueryRedis(keyPrefix = STRINGK_HOME_CAROUSEL, expire = 2L )\n    @Override\n    public List<HomeCarousel> listAll() {\n        LambdaQueryWrapper<HomeCarousel> qw = new LambdaQueryWrapper<>();\n        qw.orderByAsc(HomeCarousel::getLocation);\n        return homeCarrouselDao.selectList(qw);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/star/StarMixQueryService.java",
    "content": "package com.acimage.community.service.star;\n\nimport com.acimage.common.model.domain.community.Star;\nimport com.acimage.common.model.page.MyPage;\n\npublic interface StarMixQueryService {\n    MyPage<Star> pageStarsWithTopic(long userId, int pageNo);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/star/StarQueryService.java",
    "content": "package com.acimage.community.service.star;\n\npublic interface StarQueryService {\n    boolean isStar(long userId,long topicId);\n\n    Integer getTopicStarCount(long topicId);\n\n    Integer getStarCountOwnedBy(long userId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/star/StarWriteService.java",
    "content": "package com.acimage.community.service.star;\n\n\npublic interface StarWriteService {\n\n\n    void saveStar(long userId,long topicId);\n\n    void removeStar(long userId,long topicId);\n\n    void removeStars(long topicId);\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/star/impl/StarMixQueryServiceImpl.java",
    "content": "package com.acimage.community.service.star.impl;\n\nimport com.acimage.common.model.domain.community.Star;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.community.dao.StarDao;\nimport com.acimage.community.global.consts.PageSizeConstants;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.community.service.star.StarMixQueryService;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class StarMixQueryServiceImpl implements StarMixQueryService {\n    @Autowired\n    StarDao starDao;\n    @QueryRedis(keyPrefix = \"acimage:stars:userId:pageNo:\", expire = 30L, unit = TimeUnit.SECONDS)\n    @Override\n    public MyPage<Star> pageStarsWithTopic(long userId, int pageNo) {\n        int startIndex = (pageNo - 1) * PageSizeConstants.ACTIVITY_STARS;\n        List<Star> stars = starDao.selectStarsWithTopicOrderByCreateTime(userId, startIndex, PageSizeConstants.ACTIVITY_STARS);\n\n        LambdaUpdateWrapper<Star> qw=new LambdaUpdateWrapper<>();\n        qw.eq(Star::getUserId,userId);\n        int totalCount = starDao.selectCount(qw);\n\n        return new MyPage<>(totalCount, stars);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/star/impl/StarQueryServiceImpl.java",
    "content": "package com.acimage.community.service.star.impl;\n\nimport com.acimage.common.model.domain.community.Star;\nimport com.acimage.common.redis.annotation.KeyParam;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.community.dao.StarDao;\nimport com.acimage.community.service.star.StarQueryService;\nimport com.acimage.community.global.consts.StarKeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class StarQueryServiceImpl implements StarQueryService {\n    @Autowired\n    StarDao starDao;\n\n    @QueryRedis(keyPrefix = StarKeyConstants.STRINGKP_STAR_USER_TOPIC)\n    @Override\n    public boolean isStar(@KeyParam long userId,@KeyParam long topicId) {\n        LambdaQueryWrapper<Star> qw=new LambdaQueryWrapper<>();\n        qw.eq(Star::getUserId,userId)\n                .eq(Star::getTopicId,topicId);\n        return starDao.selectOne(qw) != null;\n    }\n\n    @QueryRedis(keyPrefix = StarKeyConstants.STRINGKP_TOPIC_STAR_COUNT,expire = 31L)\n    @Override\n    public Integer getTopicStarCount(@KeyParam long topicId) {\n        LambdaQueryWrapper<Star> qw=new LambdaQueryWrapper<>();\n        qw.eq(Star::getTopicId,topicId);\n        return starDao.selectCount(qw);\n    }\n\n    @QueryRedis(keyPrefix = StarKeyConstants.STRINGKP_USER_STAR_COUNT, expire = 31L)\n    @Override\n    public Integer getStarCountOwnedBy(@KeyParam long userId) {\n        return starDao.countStarsOwnedBy(userId);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/star/impl/StarWriteServiceImpl.java",
    "content": "package com.acimage.community.service.star.impl;\n\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.community.Star;\nimport com.acimage.community.service.cmtyuser.CmtyUserWriteService;\nimport com.acimage.community.global.consts.StarKeyConstants;\nimport com.acimage.community.service.star.StarQueryService;\nimport com.acimage.community.service.topic.TopicQueryService;\nimport com.acimage.community.service.star.StarWriteService;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.dao.StarDao;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Date;\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Service\npublic class StarWriteServiceImpl implements StarWriteService {\n\n    @Autowired\n    StarDao starDao;\n    @Autowired\n    StarQueryService starQueryService;\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicQueryService topicQueryService;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n    @Autowired\n    CmtyUserWriteService cmtyUserWriteService;\n\n    @Override\n    public void saveStar(long userId, long topicId) {\n        if (starQueryService.isStar(userId, topicId)) {\n            log.info(\"user:{}已经点赞过话题topicId:{}\", UserContext.getUsername(), topicId);\n            throw new BusinessException(\"已经点过赞了\");\n        }\n\n        Star star = new Star(userId, topicId);\n        star.setUserId(userId);\n        star.setTopicId(topicId);\n        star.setCreateTime(new Date());\n\n        try {\n            starDao.insert(star);\n        } catch (DuplicateKeyException e) {\n            log.warn(\"user:{} 收藏过了话题 topicId:{}\", UserContext.getUsername(), topicId);\n            throw new BusinessException(\"已经收藏过了，请刷新重试\");\n        }\n\n        int increment = 1;\n        topicSpAttrWriteService.increaseStarCount(topicId, increment);\n        //更新主人star\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic != null) {\n            cmtyUserWriteService.updateStarCountByIncrement(topic.getUserId(), increment);\n        }\n\n        long timeout = 3L;\n        redisUtils.setObjectJson(StarKeyConstants.keyOfIsStar(userId, topicId), Boolean.TRUE, timeout, TimeUnit.SECONDS);\n    }\n\n    @Override\n    public void removeStar(long userId, long topicId) {\n        if (!starQueryService.isStar(userId, topicId)) {\n            log.info(\"user:{}已经取消点赞过话题topicId:{}\", UserContext.getUsername(), topicId);\n            throw new BusinessException(\"已经取消过点赞了\");\n        }\n        int col = starDao.deleteByUserIdAndTopicId(userId, topicId);\n        //删除缓存\n        redisUtils.delete(StarKeyConstants.keyOfIsStar(userId, topicId));\n\n        //更新话题、话题主人收藏量\n        int starIncrement = -col;\n        topicSpAttrWriteService.increaseStarCount(topicId, starIncrement);\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic != null) {\n            long ownerId = topic.getUserId();\n            cmtyUserWriteService.updateStarCountByIncrement(ownerId, starIncrement);\n        }\n    }\n\n\n    @Override\n    public void removeStars(long topicId) {\n        LambdaQueryWrapper<Star> qw = new LambdaQueryWrapper<>();\n        qw.eq(Star::getTopicId, topicId);\n        int starIncrement = -starDao.delete(qw);\n\n        //更新话题主人收藏量\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic != null) {\n            long ownerId = topic.getUserId();\n            cmtyUserWriteService.updateStarCountByIncrement(ownerId, starIncrement);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/TagQueryService.java",
    "content": "package com.acimage.community.service.tag;\n\nimport com.acimage.common.model.domain.community.Tag;\n\nimport java.util.List;\n\npublic interface TagQueryService {\n    List<Tag> listAll();\n\n    List<Tag> checkAndListTags(List<Integer> tagIds);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/TagTopicQueryService.java",
    "content": "package com.acimage.community.service.tag;\n\nimport java.util.List;\n\npublic interface TagTopicQueryService {\n    List<Integer> listTagIds(long topicId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/TagTopicWriteService.java",
    "content": "package com.acimage.community.service.tag;\n\nimport java.util.List;\n\npublic interface TagTopicWriteService {\n    void save(long topicId, List<Integer> tagIdList);\n\n    void remove(long topicId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/TagWriteService.java",
    "content": "package com.acimage.community.service.tag;\n\nimport java.util.List;\n\npublic interface TagWriteService {\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/impl/TagQueryServiceImpl.java",
    "content": "package com.acimage.community.service.tag.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.community.Tag;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.community.dao.TagDao;\nimport com.acimage.community.service.tag.TagQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class TagQueryServiceImpl implements TagQueryService {\n    @Autowired\n    TagDao tagDao;\n\n    @Override\n    public List<Tag> listAll() {\n        return tagDao.selectList(null);\n    }\n\n    @Override\n    public List<Tag> checkAndListTags(List<Integer> tagIds) {\n        if (CollectionUtil.isEmpty(tagIds)) {\n            return new ArrayList<>();\n        }\n        List<Tag> allTags = listAll();\n        List<Integer> allTagIds = ListUtils.extract(Tag::getId, allTags);\n        for (Integer tagId : tagIds) {\n            if (!allTagIds.contains(tagId)) {\n                log.error(\"cmtyUser:{} error:标签不存在 tagId:{}\", UserContext.getUsername(), tagId);\n                throw new BusinessException(\"标签不存在\");\n            }\n        }\n        int i = 0;\n        List<Tag> tags = new ArrayList<>();\n        while (i < tagIds.size()-1) {\n            for (Tag item : allTags) {\n                if (item.getId().equals(tagIds.get(i))) {\n                    tags.add(item);\n                    i++;\n                    break;\n                }\n            }\n        }\n\n        return tags;\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/impl/TagTopicQueryServiceImpl.java",
    "content": "package com.acimage.community.service.tag.impl;\n\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.community.dao.TagTopicDao;\nimport com.acimage.community.service.tag.TagTopicQueryService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class TagTopicQueryServiceImpl implements TagTopicQueryService {\n    @Autowired\n    TagTopicDao tagTopicDao;\n\n    @QueryRedis(keyPrefix = \"acimage:community:tags:topicId:\")\n    @Override\n    public List<Integer> listTagIds(long topicId){\n        return tagTopicDao.selectTagIds(topicId);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/impl/TagTopicWriteServiceImpl.java",
    "content": "package com.acimage.community.service.tag.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.model.domain.community.TagTopic;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.community.dao.TagTopicDao;\nimport com.acimage.community.service.tag.TagQueryService;\nimport com.acimage.community.service.tag.TagTopicWriteService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class TagTopicWriteServiceImpl implements TagTopicWriteService {\n    @Autowired\n    TagTopicDao tagTopicDao;\n    @Autowired\n    TagQueryService tagQueryService;\n\n    @Override\n    public void save(long topicId, List<Integer> tagIdList) {\n//        //检查标签是否存在\n//        List<Tag> tagList = tagQueryService.listAll();\n//        List<Integer> allTagIds = ListUtils.extract(Tag::getId, tagList);\n//        for (Integer tagId : tagIdList) {\n//            if (!allTagIds.contains(tagId)) {\n//                log.error(\"cmtyUser:{} error:标签不存在 tagId:{}\", UserContext.getUsername(), tagId);\n//                throw new BusinessException(\"标签不存在\");\n//            }\n//        }\n\n        if (CollectionUtil.isEmpty(tagIdList)) {\n            return;\n        }\n\n        List<TagTopic> tagTopicList = new ArrayList<>();\n        Date now = new Date();\n\n        for (Integer tagId : tagIdList) {\n            TagTopic tagTopic = TagTopic.builder()\n                    .id(IdGenerator.getSnowflakeNextId())\n                    .topicId(topicId)\n                    .tagId(tagId)\n                    .createTime(now)\n                    .build();\n            tagTopicList.add(tagTopic);\n        }\n        tagTopicDao.insertBatch(tagTopicList);\n    }\n\n    @Override\n    public void remove(long topicId){\n        LambdaQueryWrapper<TagTopic> qw=new LambdaQueryWrapper<>();\n        qw.eq(TagTopic::getTopicId,topicId);\n        tagTopicDao.delete(qw);\n\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/tag/impl/TagWriteServiceImpl.java",
    "content": "package com.acimage.community.service.tag.impl;\n\nimport com.acimage.community.service.tag.TagWriteService;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class TagWriteServiceImpl implements TagWriteService {\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicEsSearchServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.model.domain.community.Category;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.utils.EsUtils;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.community.global.enums.SortMode;\nimport com.acimage.community.model.request.TopicQueryByCategoryIdReq;\nimport com.acimage.community.model.request.TopicQueryBySortReq;\nimport com.acimage.community.model.request.TopicQueryByTagIdReq;\nimport com.acimage.community.model.request.TopicSearchReq;\nimport com.acimage.community.service.categoty.CategoryQueryService;\nimport com.acimage.community.service.tag.TagQueryService;\nimport com.acimage.community.service.topic.TopicEsSearchService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.elasticsearch.index.query.BoolQueryBuilder;\nimport org.elasticsearch.index.query.MultiMatchQueryBuilder;\nimport org.elasticsearch.index.query.QueryBuilders;\nimport org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;\nimport org.elasticsearch.search.sort.FieldSortBuilder;\nimport org.elasticsearch.search.sort.SortOrder;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;\nimport org.springframework.data.elasticsearch.core.SearchHit;\nimport org.springframework.data.elasticsearch.core.SearchHits;\nimport org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;\nimport org.springframework.stereotype.Service;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n@Slf4j\n@Service\npublic class TopicEsSearchServiceImpl implements TopicEsSearchService {\n    @Autowired\n    EsUtils esUtils;\n    @Autowired\n    ElasticsearchRestTemplate esTemplate;\n    @Autowired\n    CategoryQueryService categoryQueryService;\n    @Autowired\n    TagQueryService tagQueryService;\n\n    @Override\n    public List<Topic> searchSimilar(long topicId, int size) {\n        List<String> columns = LambdaUtils.columnsFrom(Topic::getContent, Topic::getTitle);\n        List<TopicIndex> topicIndices = esUtils.similarQuery(Long.toString(topicId), TopicIndex.class, columns, 1, size);\n        return TopicIndex.toTopicList(topicIndices);\n    }\n\n    @Override\n    public List<Topic> searchSimilarByTitle(long topicId, String title, int size) {\n        String column = LambdaUtils.columnOf(TopicIndex::getTitle);\n        float score = 0.2f;\n        List<TopicIndex> topicIndices = esUtils.matchQuery(TopicIndex.class, column, title, 1, size + 1, score);\n        List<Topic> topicList = TopicIndex.toTopicList(topicIndices).stream()\n                .filter(o -> !o.getId().equals(topicId))\n                .collect(Collectors.toList());\n        if (topicList.size() == size + 1) {\n            return topicList.subList(0, size);\n        } else {\n            return topicList;\n        }\n    }\n\n    @Override\n    public MyPage<Topic> searchByTagId(TopicQueryByTagIdReq queryReq) {\n        Integer tagId=queryReq.getTagId();\n        int pageNo=queryReq.getPageNo();\n        int pageSize=queryReq.getPageSize();\n        SortMode sortMode=queryReq.getSortMode();\n        //检查tagId是否存在\n        tagQueryService.checkAndListTags(Collections.singletonList(tagId));\n        //获取sortBuilder\n        FieldSortBuilder sortBuilder=SortMode.toSortBuilder(sortMode);\n\n        String column = LambdaUtils.columnOf(Topic::getTagIds);\n        MyPage<TopicIndex> topicIndexPage = esUtils.termQuery(column, tagId, TopicIndex.class, pageNo, pageSize,sortBuilder);\n        return TopicIndex.toTopicPage(topicIndexPage);\n    }\n\n    @Override\n    public MyPage<Topic> searchBySort(TopicQueryByCategoryIdReq queryReq) {\n        int categoryId=queryReq.getCategoryId();\n        int pageNo=queryReq.getPageNo();\n        int pageSize=queryReq.getPageSize();\n        SortMode sortMode=queryReq.getSortMode();\n\n        FieldSortBuilder sortBuilder=SortMode.toSortBuilder(sortMode);\n\n        //校验分类是否存在\n        List<Integer> categoryIds = ListUtils.extract(Category::getId, categoryQueryService.listAll());\n        if(!categoryIds.contains(categoryId)){\n            log.warn(\"用户查询分类 id:{}不存在\",categoryId);\n            throw new BusinessException(\"分类不存在\");\n        }\n        String column=LambdaUtils.columnOf(TopicIndex::getCategoryId);\n\n        MyPage<TopicIndex> topicIndexPage=esUtils.termQuery(column,categoryId, TopicIndex.class,pageNo,pageSize,sortBuilder);\n        return TopicIndex.toTopicPage(topicIndexPage);\n    }\n\n    @Override\n    public MyPage<Topic> searchBySort(TopicQueryBySortReq queryReq) {\n        int pageNo=queryReq.getPageNo();\n        int pageSize=queryReq.getPageSize();\n        SortMode sortMode=queryReq.getSortMode();\n\n        FieldSortBuilder sortBuilder=SortMode.toSortBuilder(sortMode);\n\n        MyPage<TopicIndex> topicIndexPage=esUtils.queryBySort(TopicIndex.class,pageNo,pageSize,sortBuilder);\n        return TopicIndex.toTopicPage(topicIndexPage);\n    }\n\n\n    @Override\n    public MyPage<Topic> search(TopicSearchReq topicSearchReq) {\n        String search = topicSearchReq.getSearch();\n        Integer categoryId = topicSearchReq.getCategoryId();\n        Integer tagId = topicSearchReq.getTagId();\n        Integer pageNo = topicSearchReq.getPageNo();\n        SortMode sort = topicSearchReq.getSortMode();\n\n\n        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();\n        HighlightBuilder highlightBuilder = null;\n\n        String titleColumn = LambdaUtils.columnOf(TopicIndex::getTitle);\n        String contentColumn = LambdaUtils.columnOf(TopicIndex::getContent);\n\n        if (!StrUtil.isBlank(search)) {\n            //有关键词则高亮\n            HighlightBuilder.Field titleField = new HighlightBuilder.Field(titleColumn);\n\n            HighlightBuilder.Field contentField = new HighlightBuilder.Field(contentColumn);\n\n            MultiMatchQueryBuilder matchQuery = QueryBuilders.multiMatchQuery(search, titleColumn, contentColumn);\n            highlightBuilder = new HighlightBuilder()\n                    .field(titleField)\n                    .field(contentField)\n                    .preTags(\"<span style='color:red'>\")\n                    .postTags(\"</span>\");\n            boolQuery.must().add(matchQuery);\n        }\n        if (tagId != null) {\n            String tagIdsColumn = LambdaUtils.columnOf(TopicIndex::getTagIds);\n            boolQuery.filter().add(QueryBuilders.termQuery(tagIdsColumn, tagId));\n        }\n        if (categoryId != null) {\n            String categoryIdColumn = LambdaUtils.columnOf(TopicIndex::getCategoryId);\n            boolQuery.filter().add(QueryBuilders.termQuery(categoryIdColumn, categoryId));\n        }\n\n\n        NativeSearchQueryBuilder nativeSearchQuery = new NativeSearchQueryBuilder()\n                .withQuery(boolQuery)\n                .withPageable(PageRequest.of(pageNo - 1, 10));\n        if (highlightBuilder != null) {\n            nativeSearchQuery.withHighlightBuilder(highlightBuilder);\n        }\n        if (sort != null && sort.toColumn() != null) {\n            FieldSortBuilder sortBuilder = new FieldSortBuilder(Objects.requireNonNull(sort.toColumn()))\n                    .order(SortOrder.DESC);\n            nativeSearchQuery.withSort(sortBuilder);\n        }\n\n        //整合结果\n        List<TopicIndex> topicIndexList = new ArrayList<>();\n        SearchHits<TopicIndex> searchHits = esTemplate.search(nativeSearchQuery.build(), TopicIndex.class);\n        int total = (int) searchHits.getTotalHits();\n\n        for (SearchHit<TopicIndex> searchHit : searchHits.getSearchHits()) {\n            TopicIndex topicIndex = searchHit.getContent();\n\n            if (!StrUtil.isBlank(search)) {\n                //title高亮\n                StringBuilder newTitle = new StringBuilder();\n                List<String> titleHighlights = searchHit.getHighlightFields().get(titleColumn);\n                if (!CollectionUtil.isEmpty(titleHighlights)) {\n                    if (titleHighlights.size() > 0) {\n                        for (String highlight : titleHighlights) {\n                            newTitle.append(highlight);\n                        }\n                    }\n                    topicIndex.setTitle(newTitle.toString());\n                }\n                //content高亮\n                StringBuilder newContent = new StringBuilder();\n                List<String> contentHighlights = searchHit.getHighlightFields().get(contentColumn);\n                if (!CollectionUtil.isEmpty(contentHighlights)) {\n                    if (contentHighlights.size() > 0) {\n                        for (String highlight : contentHighlights) {\n                            newContent.append(highlight)\n                                    .append(\" \");\n                        }\n                    }\n                    topicIndex.setContent(newContent.toString());\n                }\n            }\n            topicIndexList.add(topicIndex);\n        }\n\n        return new MyPage<>(total, TopicIndex.toTopicList(topicIndexList));\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicHtmlQueryServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport com.acimage.common.model.domain.community.TopicHtml;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.utils.SensitiveWordUtils;\nimport com.acimage.community.dao.TopicHtmlDao;\nimport com.acimage.community.service.topic.TopicHtmlQueryService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class TopicHtmlQueryServiceImpl implements TopicHtmlQueryService {\n    @Autowired\n    TopicHtmlDao topicHtmlDao;\n\n    @Override\n    @QueryRedis(keyPrefix = TopicKeyConstants.HASHKP_TOPIC_HTML, expire = 12L)\n    public TopicHtml getTopicHtml(long topicId) {\n        TopicHtml topicHtml = topicHtmlDao.selectById(topicId);\n        topicHtml.setHtml(SensitiveWordUtils.filter(topicHtml.getHtml()));\n        return topicHtml;\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicHtmlWriteServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport com.acimage.common.model.domain.community.TopicHtml;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.dao.TopicHtmlDao;\nimport com.acimage.community.service.topic.TopicHtmlWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class TopicHtmlWriteServiceImpl implements TopicHtmlWriteService {\n    @Autowired\n    TopicHtmlDao topicHtmlDao;\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public TopicHtml save(long topicId, String html) {\n        TopicHtml topicHtml = new TopicHtml();\n        topicHtml.setTopicId(topicId);\n        topicHtml.setHtml(html);\n        topicHtmlDao.insert(topicHtml);\n        return topicHtml;\n    }\n\n    @Override\n    public void remove(long topicId) {\n        topicHtmlDao.deleteById(topicId);\n        redisUtils.delete(TopicKeyConstants.HASHKP_TOPIC_HTML+topicId);\n    }\n\n\n    @Override\n    public void update(long topicId, String html) {\n        LambdaUpdateWrapper<TopicHtml> uw=new LambdaUpdateWrapper<>();\n        uw.eq(TopicHtml::getTopicId,topicId)\n                .set(TopicHtml::getHtml,html);\n        topicHtmlDao.update(null,uw);\n        redisUtils.delete(TopicKeyConstants.HASHKP_TOPIC_HTML+topicId);\n     }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicInfoQueryServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport cn.hutool.core.bean.BeanUtil;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.common.BeanUtils;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.acimage.community.dao.TopicDao;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.community.global.consts.PageSizeConstants;\nimport com.acimage.community.model.vo.TopicInfoVo;\nimport com.acimage.community.service.cmtyuser.CmtyUserQueryService;\nimport com.acimage.community.service.comment.CommentInfoQueryService;\nimport com.acimage.community.service.tag.TagTopicQueryService;\nimport com.acimage.community.service.topic.*;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Service\npublic class TopicInfoQueryServiceImpl implements TopicInfoQueryService {\n    @Autowired\n    TopicDao topicDao;\n    @Autowired\n    CommentInfoQueryService commentInfoQueryService;\n    @Autowired\n    TopicQueryService topicQueryService;\n    @Autowired\n    TopicSpAttrQueryService topicSpQueryService;\n    @Autowired\n    TopicRankQueryService topicRankQueryService;\n    @Autowired\n    TopicEsSearchService topicEsSearchService;\n    @Autowired\n    CmtyUserQueryService cmtyUserQueryService;\n    @Autowired\n    TagTopicQueryService tagTopicQueryService;\n    @Autowired\n    TopicHtmlQueryService topicHtmlQueryService;\n\n\n    @Override\n    public Topic getTopicWithUserTagIds(long topicId) {\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            return null;\n        }\n        List<Integer> tagIds = tagTopicQueryService.listTagIds(topicId);\n        topic.setTagIds(tagIds);\n        return topic;\n    }\n\n    @Override\n    public TopicInfoVo getTopicInfoAndFirstCommentPage(long topicId) {\n\n        Topic topic = this.getTopicWithUserTagIds(topicId);\n\n        if (topic == null) {\n            log.warn(\"user:{} 查询 话题:{} error：不存在或已被删除\", UserContext.getUsername(), topicId);\n            throw new BusinessException(\"话题不存在或已被删除\");\n        }\n        TopicInfoVo topicInfoVo = new TopicInfoVo();\n        //设置浏览量、收藏量、评论数\n        topicSpQueryService.setAttrIntoTopic(topic, TopicAttribute.COMMENT_COUNT, TopicAttribute.STAR_COUNT, TopicAttribute.PAGE_VIEW);\n\n        BeanUtil.copyProperties(topic, topicInfoVo, false);\n        //查找首页评论\n        int pageNo = 1;\n        List<Comment> comments = commentInfoQueryService.pageCommentsWithUser(topicId, pageNo, PageSizeConstants.TOPIC_COMMENTS);\n        topicInfoVo.setComments(comments);\n\n        CmtyUser cmtyUser = cmtyUserQueryService.getCmtyUser(topic.getUserId());\n\n        User user = BeanUtils.copyPropertiesTo(cmtyUser, User.class);\n        topicInfoVo.setUser(user);\n        String html = topicHtmlQueryService.getTopicHtml(topicId).getHtml();\n        topicInfoVo.setHtml(html);\n        topicInfoVo.setSimilarTopics(topicEsSearchService.searchSimilarByTitle(topicId, topicInfoVo.getTitle(), 10));\n\n        return topicInfoVo;\n    }\n\n    @QueryRedis(keyPrefix = \"acimage:community:topics:userId:\",expire = 8L,unit = TimeUnit.SECONDS)\n    @Override\n    public MyPage<Topic> pageUserTopicsInfoOrderByCreateTime(long userId, int pageNo, int pageSize) {\n        int starIndex = PageUtils.startIndexOf(pageNo, pageSize);\n        List<Topic> topics = topicDao.selectTopicsWithUserOrderByCreateTime(userId, starIndex, pageSize);\n        return new MyPage<>(topicDao.countTopics(userId), topics);\n    }\n\n    @Override\n    public List<Topic> listTopicsInfoSortBy(TopicAttribute attr, int pageNo, int pageSize) {\n\n        List<Long> rankList = topicRankQueryService.listTopicIdsInRank(attr, pageNo, pageSize);\n        List<Topic> topicList = new ArrayList<>();\n\n        if (pageNo == 1 && rankList.size() < pageSize) {\n            String column = attr.toUnderlineColumnName();\n            topicList = topicDao.selectTopicsWithUserOrderBy(column, pageSize);\n        } else {\n            for (Long topicId : rankList) {\n                Topic topic = getTopicWithUserTagIds(topicId);\n                if (topic != null) {\n                    topicList.add(topic);\n                }\n            }\n        }\n\n        for (Topic topic : topicList) {\n            //设置浏览量\n            topicSpQueryService.setAttrIntoTopic(topic, TopicAttribute.PAGE_VIEW);\n        }\n\n        return topicList;\n    }\n\n    @Override\n    public List<Topic> listRandomTopicsInRank(int size) {\n        //获取随机属性\n        TopicAttribute[] attrs = TopicAttribute.values();\n        int len = attrs.length;\n        int i = (int) (System.currentTimeMillis() % len);\n        TopicAttribute attr=attrs[i];\n        //从随机属性排行中获取随机话题\n        List<Long> rankList = topicRankQueryService.listRandomTopicIdsInRank(attr, size);\n        List<Topic> topicList = new ArrayList<>();\n        for (Long topicId : rankList) {\n            Topic topic = getTopicWithUserTagIds(topicId);\n            if (topic != null) {\n                topicList.add(topic);\n            }\n        }\n\n        for (Topic topic : topicList) {\n            //设置浏览量\n            topicSpQueryService.setAttrIntoTopic(topic, TopicAttribute.PAGE_VIEW);\n        }\n        return topicList;\n\n    }\n\n    @Override\n    public MyPage<Topic> pageTopicsInfoRankByActivityTime(int pageNo, int pageSize) {\n        List<Long> topicIdList = topicRankQueryService.listTopicIdsInRank(TopicAttribute.ACTIVITY_TIME, pageNo, pageSize);\n        List<Topic> topicList = new ArrayList<>();\n        for (Long topicId : topicIdList) {\n            Topic topic = getTopicWithUserTagIds(topicId);\n            if (topic != null) {\n                //设置浏览量\n                topicSpQueryService.setAttrIntoTopic(topic, TopicAttribute.PAGE_VIEW);\n                topicList.add(topic);\n            }\n        }\n        return new MyPage<>(topicRankQueryService.countTopicIdsInRank(TopicAttribute.ACTIVITY_TIME), topicList);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicInfoWriteServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.common.global.consts.FileFormatConstants;\nimport com.acimage.common.global.enums.ServiceType;\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.domain.community.TopicHtml;\nimport com.acimage.common.model.mq.dto.SyncImagesUpdateDto;\nimport com.acimage.common.utils.*;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.common.utils.minio.MinioUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.global.consts.CoverImageConstants;\nimport com.acimage.common.global.consts.StorePrefixConstants;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport com.acimage.community.listener.event.TopicEvent;\nimport com.acimage.community.model.request.TopicAddReq;\nimport com.acimage.community.model.request.TopicModifyHtmlReq;\nimport com.acimage.community.mq.producer.syncImagesMqProducer;\nimport com.acimage.community.mq.producer.SyncEsMqProducer;\nimport com.acimage.community.service.cmtyuser.CmtyUserWriteService;\nimport com.acimage.community.service.comment.CommentWriteService;\nimport com.acimage.community.service.star.StarWriteService;\nimport com.acimage.community.service.tag.TagQueryService;\nimport com.acimage.community.service.tag.TagTopicWriteService;\nimport com.acimage.community.service.topic.*;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.exception.BusinessException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\n\nimport javax.annotation.Resource;\nimport java.io.InputStream;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\n\n@Slf4j\n@Service\npublic class TopicInfoWriteServiceImpl implements TopicInfoWriteService {\n\n    @Autowired\n    StarWriteService starWriteService;\n    @Autowired\n    CommentWriteService commentWriteService;\n    @Autowired\n    TopicQueryService topicQueryService;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n    @Autowired\n    TopicRankWriteService topicRankWriteService;\n    @Autowired\n    TopicWriteService topicWriteService;\n    @Autowired\n    TagQueryService tagQueryService;\n    @Autowired\n    syncImagesMqProducer syncImagesMqProducer;\n    @Autowired\n    SyncEsMqProducer syncEsMqProducer;\n    @Autowired\n    CmtyUserWriteService cmtyUserWriteService;\n    @Autowired\n    TopicHtmlWriteService topicHtmlWriteService;\n    @Autowired\n    TopicHtmlQueryService topicHtmlQueryService;\n    @Autowired\n    TagTopicWriteService tagTopicWriteService;\n    @Autowired\n    MinioUtils minioUtils;\n    @Autowired\n    RedisUtils redisUtils;\n    @Resource\n    ApplicationContext applicationContext;\n\n    @Override\n    public long saveTopicInfo(TopicAddReq topicAddReq, MultipartFile coverImage) {\n        String publishedTitle = redisUtils.getForString(TopicKeyConstants.STRINGKP_PUBLISHED_TOPIC_TITLE);\n        if (publishedTitle != null && publishedTitle.equals(topicAddReq.getTitle())) {\n            log.warn(\"user:{}重复发表话题 title:{}\", UserContext.getUsername(), topicAddReq.getTitle());\n            throw new BusinessException(\"已经发表过该话题了，请刷新\");\n        }\n        //生成id\n        long topicId = IdGenerator.getSnowflakeNextId();\n        Date now = new Date();\n        String suffix = String.format(\"%s.%s\", topicId, FileFormatConstants.WEBP);\n        String url = minioUtils.generateBaseUrl(StorePrefixConstants.COVER_IMAGE, now, suffix);\n        //图片压缩\n        InputStream inputStream = ImageUtils.compressAsFixedWebpImage(coverImage,\n                CoverImageConstants.WIDTH,\n                CoverImageConstants.HEIGHT,\n                CoverImageConstants.LIMIT_COMPRESS_SIZE);\n\n        String coverImageUrl = minioUtils.upload(inputStream, url, FileFormatConstants.WEBP_CONTENT_TYPE);\n        //过滤标题\n        String filterTile = SensitiveWordUtils.filter(topicAddReq.getTitle());\n        //过滤内容\n        String text = HtmlUtils.html2Text(topicAddReq.getHtml());\n        String content = SensitiveWordUtils.filter(text);\n        //提取前200个字作为文本内容\n        String subContent = StrUtil.subPre(content, Topic.CONTENT_MAX);\n        //转化\n        Topic topic = Topic.builder()\n                .id(topicId)\n                .title(filterTile)\n                .content(subContent)\n                .coverImageUrl(coverImageUrl)\n                .createTime(now)\n                .updateTime(now)\n                .activityTime(now)\n                .userId(UserContext.getUserId())\n                .categoryId(topicAddReq.getCategoryId())\n                .build();\n        if (UserContext.getUserId() != null) {\n            CmtyUser user = new CmtyUser();\n            user.setUsername(UserContext.getUsername());\n            user.setPhotoUrl(UserContext.getPhotoUrl());\n            user.setId(UserContext.getUserId());\n            topic.setUser(user);\n        }\n\n        //检查标签是否存在\n        List<Integer> noRepeatTagIds = ListUtils.removeRepeat(Arrays.asList(topicAddReq.getTagIds()));\n        tagQueryService.checkAndListTags(noRepeatTagIds);\n\n        //保存topic\n        topicWriteService.save(topic);\n        //保存话题html\n        topicHtmlWriteService.save(topicId, topicAddReq.getHtml());\n        //保存标签\n        tagTopicWriteService.save(topicId, noRepeatTagIds);\n\n        //更新活跃排行榜\n        topicRankWriteService.updateRank(TopicAttribute.ACTIVITY_TIME, topicId, now.getTime());\n        //获取话题内的站内图片链接\n        List<String> newImageUrlList = HtmlUtils.getInnerImageUrls(topicAddReq.getHtml());\n        SyncImagesUpdateDto updateDto = SyncImagesUpdateDto.builder()\n                .addImageUrls(newImageUrlList)\n                .topicId(topicId)\n                .serviceType(ServiceType.ADD)\n                .build();\n\n\n        //同步到es\n        TopicIndex topicIndex = TopicIndex.from(topic);\n        topicIndex.setTagIds(noRepeatTagIds);\n        //设置完整的content\n        topicIndex.setContent(content);\n        topicIndex.setStarCount(0);\n        topicIndex.setCommentCount(0);\n        topicIndex.setPageView(0);\n\n        syncEsMqProducer.sendAddMessage(topicIndex);\n        //发送到mq，用于以图识图\n        syncImagesMqProducer.sendSyncImagesMessage(updateDto);\n\n        TopicEvent topicEvent = new TopicEvent(this, UserContext.getUserId(), topicId);\n        applicationContext.publishEvent(topicEvent);\n\n        //保存，用于幂等校验\n        long timeout = 10L;\n        redisUtils.setAsString(TopicKeyConstants.STRINGKP_PUBLISHED_TOPIC_TITLE + UserContext.getUserId(),\n                topicAddReq.getTitle(), timeout, TimeUnit.SECONDS);\n\n        return topicId;\n    }\n\n    @Override\n    public void removeTopicInfo(long topicId) {\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            log.error(\"话题不存在 话题:{} 用户:{}\", topicId, UserContext.getUsername());\n            throw new BusinessException(\"话题不存在~~\");\n        }\n\n        if (!UserContext.getUserId().equals(topic.getUserId())) {\n            log.error(\"非法删除：用户非话题主人 话题:{} 用户:{}\", topicId, UserContext.getUsername());\n            throw new BusinessException(\"非法操作！话题不属于你\");\n        }\n        this.removeTopicInfoWithoutVerification(topicId);\n\n//        //删除star\n//        starWriteService.removeStars(topicId);\n//        //删除评论\n//        commentWriteService.removeComments(topicId);\n//        //删除话题\n//        topicHtmlWriteService.remove(topicId);\n//        topicWriteService.remove(topicId);\n//        //删除标签\n//        tagTopicWriteService.remove(topicId);\n//        //删除相关属性\n//        topicSpAttrWriteService.removeAttributes(topicId);\n//        //更新用户统计数据\n//        cmtyUserWriteService.updateTopicCountByIncrement(userId, -1);\n//        //发送删除图片的消息\n//        removeTopicImagesMqProducer.sendRemoveTopicMessage(topicId);\n//        //同步es数据\n//        syncEsMqProducer.sendDeleteMessage(Long.toString(topicId), TopicIndex.class);\n//        //同步到以图识图\n//        SyncImagesUpdateDto updateDto = SyncImagesUpdateDto.builder()\n//                .topicId(topicId)\n//                .serviceType(ServiceType.DELETE)\n//                .build();\n//        syncImagesMqProducer.sendHashImagesMessage(updateDto);\n    }\n\n    @Override\n    public void removeTopicInfoWithoutVerification(long topicId) {\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            log.error(\"话题不存在 话题:{} 用户:{}\", topicId, UserContext.getUsername());\n            throw new BusinessException(\"话题不存在~~\");\n        }\n        long userId = topic.getUserId();\n        //删除star\n        starWriteService.removeStars(topicId);\n        //删除评论\n        commentWriteService.removeComments(topicId);\n        //删除话题\n        topicHtmlWriteService.remove(topicId);\n        topicWriteService.remove(topicId);\n        //删除标签\n        tagTopicWriteService.remove(topicId);\n        //删除相关属性\n        topicSpAttrWriteService.removeAttributes(topicId);\n        //更新用户统计数据\n        cmtyUserWriteService.updateTopicCountByIncrement(userId, -1);\n\n        //同步es数据\n        syncEsMqProducer.sendDeleteMessage(Long.toString(topicId), TopicIndex.class);\n        //同步图片\n        SyncImagesUpdateDto updateDto = SyncImagesUpdateDto.builder()\n                .topicId(topicId)\n                .serviceType(ServiceType.DELETE)\n                .build();\n        syncImagesMqProducer.sendSyncImagesMessage(updateDto);\n    }\n\n    @Override\n    public void updateHtml(TopicModifyHtmlReq modifyReq) {\n        long topicId = modifyReq.getId();\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            log.error(\"话题不存在 话题:{} 用户:{}\", topicId, UserContext.getUsername());\n            throw new BusinessException(\"话题不存在~~\");\n        }\n\n        if (!UserContext.getUserId().equals(topic.getUserId())) {\n            log.error(\"异常修改：用户非话题主人 话题:{} 用户:{}\", topicId, UserContext.getUsername());\n            throw new BusinessException(\"非法操作！话题不属于你\");\n        }\n\n        //过滤内容\n        String text = HtmlUtils.html2Text(modifyReq.getHtml());\n        String content = SensitiveWordUtils.filter(text);\n        //提取前200个字作为文本内容\n        String subContent = StrUtil.subPre(content, Topic.CONTENT_MAX);\n\n        //获取变化的图片链接\n        SyncImagesUpdateDto updateDto = null;\n        TopicHtml topicHtml = topicHtmlQueryService.getTopicHtml(topicId);\n        if (topicHtml != null) {\n            List<String> oldUrls = HtmlUtils.getInnerImageUrls(topicHtml.getHtml());\n            List<String> newUrls = HtmlUtils.getInnerImageUrls(modifyReq.getHtml());\n            List<String> addImageUrls = ListUtils.differenceSetOfV2(newUrls, oldUrls);\n            List<String> removeImageUrls = ListUtils.differenceSetOfV2(oldUrls, newUrls);\n            updateDto = SyncImagesUpdateDto.builder()\n                    .topicId(topicId)\n                    .addImageUrls(addImageUrls)\n                    .removeImageUrls(removeImageUrls)\n                    .serviceType(ServiceType.UPDATE)\n                    .build();\n        }\n\n        topicWriteService.updateContent(topicId, subContent);\n        topicHtmlWriteService.update(topicId, SensitiveWordUtils.filter(modifyReq.getHtml()));\n\n        TopicIndex topicIndex = TopicIndex.builder()\n                .updateTime(new Date())\n                .content(content)\n                .id(topicId)\n                .build();\n        List<String> columns = LambdaUtils.columnsFrom(TopicIndex::getContent, TopicIndex::getUpdateTime);\n        syncEsMqProducer.sendUpdateMessage(topicIndex, columns);\n\n        //同步图片哈希\n        if (updateDto != null) {\n            syncImagesMqProducer.sendSyncImagesMessage(updateDto);\n        }\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicPreheatServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.dao.TopicDao;\nimport com.acimage.community.service.topic.TopicPreheatService;\nimport com.acimage.community.service.topic.TopicRankWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class TopicPreheatServiceImpl implements TopicPreheatService {\n\n    @Autowired\n    TopicDao topicDao;\n    @Autowired\n    TopicRankWriteService topicRankWriteService;\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public void preheatTopicsOrderBy(TopicAttribute attr, int rankSize, int cacheSize, long timeout, TimeUnit timeUnit) {\n        String underlineColumnName = attr.toUnderlineColumnName();\n        LambdaQueryWrapper<Topic> qw = new LambdaQueryWrapper<>();\n        qw.last(String.format(\" order by %s desc limit %d\", underlineColumnName, rankSize));\n\n        List<Topic> hotTopics = topicDao.selectList(qw);\n//        List<Topic> hotTopics = topicDao.selectTopicsWithUserOrderBy(underlineColumnName, rankSize);\n\n\n        for (int i = 0; i < hotTopics.size(); i++) {\n            Topic topic = hotTopics.get(i);\n            topicRankWriteService.updateRank(attr, topic);\n//\n//            if (i < cacheSize) {\n//                String key = TopicKeyConstants.HASHKP_TOPIC + topic.getId();\n//                redisUtils.setObjectForHash(key, topic, timeout, timeUnit);\n//            }\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicQueryServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.redis.enums.DataType;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.dao.TopicDao;\nimport com.acimage.community.service.topic.TopicQueryService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class TopicQueryServiceImpl implements TopicQueryService {\n    @Autowired\n    TopicDao topicDao;\n\n    @QueryRedis(expire = 11L, keyPrefix = TopicKeyConstants.HASHKP_TOPIC, dataType = DataType.HASH)\n    @Override\n    public Topic getTopic(long id) {\n        Topic topic = topicDao.selectTopicWithUser(id);\n        if (topic == null) {\n            log.error(\"user:{} 查询 对象：话题{} error：话题不存在\", UserContext.getUsername(), id);\n            return null;\n        }\n        return topic;\n    }\n\n    @Override\n    public List<Topic> listTopicWithUser(List<Long> ids) {\n        if(CollectionUtil.isEmpty(ids)){\n            return new ArrayList<>();\n        }\n        return topicDao.selectTopicsWithUserByIds(ids);\n    }\n\n    @Override\n    public List<Topic> listTopicsByIds(List<Long> ids) {\n        return topicDao.selectBatchIds(ids);\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicRankQueryServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.acimage.community.service.topic.TopicRankQueryService;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Service\npublic class TopicRankQueryServiceImpl implements TopicRankQueryService {\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public List<Long> listTopicIdsInRank(TopicAttribute topicAttribute, int pageNo, int pageSize) {\n        List<Pair<Long, Double>> topicIdAndScores = listTopicIdWithScoresInRank(topicAttribute, pageNo, pageSize);\n        return ListUtils.extractKeyFrom(topicIdAndScores);\n    }\n\n    @Override\n    public List<Long> listRandomTopicIdsInRank(TopicAttribute topicAttribute, int size) {\n        return redisUtils.randomMembersForZSet(topicAttribute.zSetKey(),size)\n                .stream()\n                .map(Long::parseLong)\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public List<Pair<Long, Double>> listTopicIdWithScoresInRank(TopicAttribute topicAttribute, int pageNo, int pageSize) {\n        int startIndex = PageUtils.startIndexOf(pageNo, pageSize);\n        int endIndex = PageUtils.endIndexOf(pageNo, pageSize);\n        String zSetKey = topicAttribute.zSetKey();\n\n        if (zSetKey == null) {\n            return null;\n        }\n        List<Pair<String, Double>> topicIdStringAndScores = redisUtils.reverseRangeWithScoreForZSet(zSetKey, startIndex, endIndex);\n        return ListUtils.convertToLongDoublePairFrom(topicIdStringAndScores);\n    }\n\n    @Override\n    public Integer countTopicIdsInRank(@NotNull TopicAttribute topicAttribute) {\n        String rankingListKey = topicAttribute.zSetKey();\n        Long count = redisUtils.sizeForZSet(rankingListKey);\n        if (count == null) {\n            return null;\n        } else {\n            return count.intValue();\n        }\n    }\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicRankWriteServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.service.topic.TopicRankWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.Date;\n\n@Service\npublic class TopicRankWriteServiceImpl implements TopicRankWriteService {\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public void updateRank(TopicAttribute attr, long topicId, double newScore) {\n\n        String key = attr.zSetKey();\n        if (key == null) {\n            return;\n        }\n\n        //更新对应排行榜\n        redisUtils.addForZSet(key, Long.toString(topicId), newScore);\n    }\n\n    @Override\n    public void updateRank(TopicAttribute attr, @NotNull Topic topic) {\n        if (topic == null) {\n            return;\n        }\n\n        if (attr == TopicAttribute.ACTIVITY_TIME) {\n            Date activityTime = redisUtils.getObjectFromString(TopicKeyConstants.STRINGKP_TOPIC_ACTIVITY_TIME + topic.getId(), Date.class);\n            if (activityTime == null) {\n                activityTime = topic.getActivityTime();\n            }\n            //更新对应排行榜\n            redisUtils.addForZSet(attr.zSetKey(), topic.getId().toString(), activityTime.getTime());\n\n        } else if (attr == TopicAttribute.PAGE_VIEW) {\n            String logKey = TopicKeyConstants.LOGKP_TOPIC_PV + topic.getId();\n            int increment = redisUtils.sizeForHyperLogLog(logKey).intValue();\n            int latestPv = topic.getPageView() + increment;\n            //更新对应排行榜\n            redisUtils.addForZSet(attr.zSetKey(), topic.getId().toString(), latestPv);\n\n        } else if (attr == TopicAttribute.COMMENT_COUNT || attr == TopicAttribute.STAR_COUNT) {\n            String key = attr.keyPrefix() + topic.getId();\n            Integer increment = redisUtils.getForString(key, Integer.class);\n            int baseValue = (Integer) attr.toGetFunction().apply(topic);\n            if (increment != null) {\n                baseValue += increment;\n            }\n            //更新对应排行榜\n            redisUtils.addForZSet(attr.zSetKey(), topic.getId().toString(), baseValue);\n\n        }\n\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicSpAttrQueryServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.service.topic.TopicQueryService;\nimport com.acimage.community.service.topic.TopicSpAttrQueryService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\n@Service\npublic class TopicSpAttrQueryServiceImpl implements TopicSpAttrQueryService {\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicQueryService topicQueryService;\n\n    @Override\n    public Integer getPageView(long topicId) {\n        //获取基础浏览量\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            return null;\n        }\n        int pageView = topic.getPageView();\n\n        //从redis中获取新增浏览量\n        String topicPvLogKey = TopicKeyConstants.LOGKP_TOPIC_PV + topicId;\n        Long pvIncrement = redisUtils.sizeForHyperLogLog(topicPvLogKey);\n        int increment = pvIncrement == null ? 0 : pvIncrement.intValue();\n\n        return pageView + increment;\n    }\n\n    @Override\n    public Date getActivityTime(long topicId) {\n        //先从redis获取\n        Date activityTime = redisUtils.getObjectFromString(TopicKeyConstants.STRINGKP_TOPIC_ACTIVITY_TIME + topicId, Date.class);\n        if (activityTime != null) {\n            return activityTime;\n        }\n\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            return null;\n        }\n        return topic.getActivityTime();\n    }\n\n    @Override\n    public Integer getStarCount(long topicId) {\n        //获取基础浏览量\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            return null;\n        }\n        int starCount = topic.getStarCount();\n\n        //从redis中获取新增浏览量\n        Long starCountIncrement = redisUtils.getForString(TopicAttribute.STAR_COUNT.keyPrefix() + topicId, Long.class);\n        int increment = starCountIncrement == null ? 0 : starCountIncrement.intValue();\n\n        return starCount + increment;\n    }\n\n    @Override\n    public Integer getCommentCount(long topicId) {\n        //获取基础浏览量\n        Topic topic = topicQueryService.getTopic(topicId);\n        if (topic == null) {\n            return null;\n        }\n        int commentCount = topic.getCommentCount();\n\n        //从redis中获取新增浏览量\n        Long commentIncrement = redisUtils.getForString(TopicAttribute.COMMENT_COUNT.keyPrefix() + topicId, Long.class);\n        int increment = commentIncrement == null ? 0 : commentIncrement.intValue();\n\n        return commentCount + increment;\n    }\n\n    @Override\n    public void setAttrIntoTopic(Topic topic, TopicAttribute... attrs) {\n        if (topic == null) {\n            return;\n        }\n\n        List<TopicAttribute> typeList = Arrays.asList(attrs);\n        long topicId = topic.getId();\n\n        if (typeList.contains(TopicAttribute.PAGE_VIEW)) {\n            Integer pv = this.getPageView(topicId);\n            topic.setPageView(pv);\n//            topicSpAttrWriteService.updateRank(TopicAttribute.PAGE_VIEW,topicId,pv);\n        }\n\n        if (typeList.contains(TopicAttribute.STAR_COUNT)) {\n            Integer starCount = this.getStarCount(topicId);\n            topic.setStarCount(starCount);\n//            topicSpAttrWriteService.updateRank(TopicAttribute.STAR_COUNT,topicId,starCount);\n\n        }\n\n        if (typeList.contains(TopicAttribute.COMMENT_COUNT)) {\n            Integer commentCount = this.getCommentCount(topicId);\n            topic.setCommentCount(commentCount);\n//            topicSpAttrWriteService.updateRank(TopicAttribute.COMMENT_COUNT,topicId,commentCount);\n        }\n\n        if (typeList.contains(TopicAttribute.ACTIVITY_TIME)) {\n            Date activityTime = this.getActivityTime(topicId);\n            topic.setActivityTime(activityTime);\n//            topicSpAttrWriteService.updateRank(TopicAttribute.ACTIVITY_TIME,topicId,activityTime.getTime());\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicSpAttrWriteServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.common.utils.common.PairUtils;\nimport com.acimage.community.dao.TopicDao;\nimport com.acimage.community.mq.producer.SyncEsMqProducer;\nimport com.acimage.community.service.topic.TopicRankWriteService;\nimport com.acimage.community.service.topic.TopicSpAttrQueryService;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n\nimport java.util.Date;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class TopicSpAttrWriteServiceImpl implements TopicSpAttrWriteService {\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    TopicDao topicDao;\n    @Autowired\n    TopicSpAttrQueryService topicSpAttrQueryService;\n    @Autowired\n    TopicRankWriteService topicRankWriteService;\n    @Autowired\n    SyncEsMqProducer syncEsMqProducer;\n\n    @Override\n    public void removeAttributes(long topicId) {\n        for (TopicAttribute attr : TopicAttribute.values()) {\n            //删除保存属性最新变化的数据\n            redisUtils.delete(attr.keyPrefix() + topicId);\n            //删除set中记录的id\n            redisUtils.removeForSet(attr.setKeyForRecordingId(), Long.toString(topicId));\n            //删除排行榜中的id\n            redisUtils.removeForZSet(attr.zSetKey(), topicId);\n        }\n    }\n\n    @Override\n    public void updateStarCountByIncrement(List<Long> topicIds, List<Integer> increments) {\n        batchUpdateByIncrements(TopicAttribute.STAR_COUNT, topicIds, increments);\n    }\n\n    @Override\n    public void updateCommentCountByIncrement(List<Long> topicIds, List<Integer> increments) {\n        batchUpdateByIncrements(TopicAttribute.COMMENT_COUNT, topicIds, increments);\n    }\n\n    @Override\n    public void updatePageViewByIncrement(List<Long> topicIds, List<Integer> increments) {\n        batchUpdateByIncrements(TopicAttribute.PAGE_VIEW, topicIds, increments);\n    }\n\n    private void batchUpdateByIncrements(TopicAttribute attr, List<Long> topicIds, List<Integer> increments) {\n        String underlineColumnName = attr.toUnderlineColumnName();\n        List<Pair<Long, Integer>> pairList = PairUtils.combine(topicIds, increments);\n        if (!CollectionUtil.isEmpty(pairList)) {\n            topicDao.batchUpdateColumnByIncrement(underlineColumnName, pairList);\n        }\n    }\n\n    private void updateByIncrement(SFunction<Topic, ?> column, long topicId, int increment) {\n        String underlineName = LambdaUtils.underlineColumnNameOf(column);\n        topicDao.updateColumnByIncrement(underlineName, topicId, increment);\n    }\n\n\n    @Override\n    public void updateBatchActivityTime(List<Long> topicIds, List<Date> activityTimes) {\n        List<Pair<Long, Date>> pairList = PairUtils.combine(topicIds, activityTimes);\n        if (!CollectionUtil.isEmpty(pairList)) {\n            topicDao.batchUpdateActivityTime(pairList);\n        }\n    }\n\n    @Override\n    public void changeActivityTime(long topicId, Date date) {\n        //更新数据库\n        topicDao.updateActivityTime(topicId, date);\n        String key = TopicKeyConstants.HASHKP_TOPIC + topicId;\n        String column = LambdaUtils.columnOf(Topic::getActivityTime);\n        //保存最新值\n        redisUtils.setIfPresentForFieldKey(key, column, Long.toString(date.getTime()));\n        //更新排行榜\n        topicRankWriteService.updateRank(TopicAttribute.ACTIVITY_TIME, topicId, date.getTime());\n        //同步到es\n        TopicIndex topicIndex = TopicIndex.builder()\n                .activityTime(date)\n                .id(topicId)\n                .build();\n        List<String> columns = LambdaUtils.columnsFrom(TopicIndex::getActivityTime);\n        syncEsMqProducer.sendUpdateMessage(topicIndex, columns);\n    }\n\n    @Override\n    public void increaseStarCount(long topicId, int increment) {\n\n        if (increment != 0) {\n            //改数据库\n            this.updateByIncrement(Topic::getStarCount, topicId, increment);\n            //同步到redis\n            String column = LambdaUtils.columnOf(Topic::getStarCount);\n            String key = TopicKeyConstants.HASHKP_TOPIC + topicId;\n            //获取最新值更新排行榜\n            Long starCount = redisUtils.incrementIfPresentForFieldKey(key, column, increment);\n            if (starCount == null) {\n                Integer cnt = topicSpAttrQueryService.getStarCount(topicId);\n                if (cnt == null) {\n                    return;\n                }\n                starCount = cnt.longValue();\n            }\n\n            //更新排行榜\n            topicRankWriteService.updateRank(TopicAttribute.STAR_COUNT, topicId, starCount);\n\n            //同步到es\n            TopicIndex topicIndex = TopicIndex.builder()\n                    .starCount(starCount.intValue())\n                    .id(topicId)\n                    .build();\n            List<String> columns = LambdaUtils.columnsFrom(TopicIndex::getStarCount);\n            syncEsMqProducer.sendUpdateMessage(topicIndex, columns);\n        }\n    }\n\n    @Override\n    public void increaseCommentCount(long topicId, int increment) {\n        if (increment != 0) {\n            //改数据库\n            this.updateByIncrement(Topic::getCommentCount, topicId, increment);\n            //同步到redis\n            String column = LambdaUtils.columnOf(Topic::getCommentCount);\n            String key = TopicKeyConstants.HASHKP_TOPIC + topicId;\n            //获取最新值更新排行榜\n            Long commentCount = redisUtils.incrementIfPresentForFieldKey(key, column, increment);\n            if (commentCount == null) {\n                Integer cnt = topicSpAttrQueryService.getCommentCount(topicId);\n                if (cnt == null) {\n                    return;\n                }\n                commentCount = cnt.longValue();\n            }\n            topicRankWriteService.updateRank(TopicAttribute.COMMENT_COUNT, topicId, commentCount);\n\n            //同步到es\n            TopicIndex topicIndex = TopicIndex.builder()\n                    .commentCount(commentCount.intValue())\n                    .id(topicId)\n                    .build();\n            List<String> columns = LambdaUtils.columnsFrom(TopicIndex::getCommentCount);\n            syncEsMqProducer.sendUpdateMessage(topicIndex, columns);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/Impl/TopicWriteServiceImpl.java",
    "content": "package com.acimage.community.service.topic.Impl;\n\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.Index.TopicIndex;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.common.utils.SensitiveWordUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.community.dao.TopicDao;\nimport com.acimage.community.model.request.TopicModifyHtmlReq;\nimport com.acimage.community.mq.producer.SyncEsMqProducer;\nimport com.acimage.community.service.topic.TopicSpAttrWriteService;\nimport com.acimage.community.service.topic.TopicWriteService;\nimport com.acimage.community.global.consts.TopicKeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Date;\nimport java.util.List;\n\n\n@Slf4j\n@Service\npublic class TopicWriteServiceImpl implements TopicWriteService {\n\n    @Autowired\n    TopicDao topicDao;\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    SyncEsMqProducer syncEsMqProducer;\n    @Autowired\n    TopicSpAttrWriteService topicSpAttrWriteService;\n\n    @Override\n    public void save(Topic topic) {\n        topicDao.insert(topic);\n        log.info(\"用户：{} 插入 话题{}\", UserContext.getUsername(), topic);\n    }\n\n    @Override\n    public void remove(long id) {\n        topicDao.deleteById(id);\n        //删除redis数据\n        redisUtils.delete(TopicKeyConstants.HASHKP_TOPIC + id);\n        log.info(\"用户：{} 删除 话题{}\", UserContext.getUsername(), id);\n    }\n\n    @Override\n    public void update(long id, String title, String content) {\n\n        Topic topic = new Topic();\n        topic.setTitle(title);\n        topic.setContent(content);\n        topic.setUpdateTime(new Date());\n        topicDao.updateById(topic);\n\n//        LambdaUpdateWrapper<Topic> qw=new LambdaUpdateWrapper<>();\n//        qw.set(Topic::getContent,content)\n//                .set(Topic::getTitle,title)\n//                .eq(Topic::getId,id);\n//        topicDao.update(null,qw);\n//        topicDao.updateTopic(id, title, content);\n        //删除redis数据\n\n        redisUtils.delete(TopicKeyConstants.HASHKP_TOPIC + id);\n        log.info(\"用户：{} 修改 话题{}\", UserContext.getUsername(), id);\n    }\n\n\n    @Override\n    public void updateTitle(long id, String title) {\n        log.info(\"user：{} 修改 话题标题{} title:{}\", UserContext.getUsername(), id, title);\n        String filterTile = SensitiveWordUtils.filter(title);\n        LambdaUpdateWrapper<Topic> uw = new LambdaUpdateWrapper<>();\n        uw.eq(Topic::getId, id)\n                .set(Topic::getTitle, filterTile)\n                .set(Topic::getUpdateTime, new Date());\n        topicDao.update(null, uw);\n\n        //删除缓存\n        redisUtils.delete(TopicKeyConstants.HASHKP_TOPIC + id);\n        //更新话题活跃时间\n        topicSpAttrWriteService.changeActivityTime(id, new Date());\n        //同步到es\n        TopicIndex topicIndex = TopicIndex.builder()\n                .id(id)\n                .title(filterTile)\n                .build();\n        topicIndex.setTitle(filterTile);\n        List<String> columns= LambdaUtils.columnsFrom(TopicIndex::getTitle);\n        syncEsMqProducer.sendUpdateMessage(topicIndex,columns);\n    }\n\n    @Deprecated\n    @Override\n    public void updateContent(TopicModifyHtmlReq topicModifyHtmlReq) {\n\n        long id = topicModifyHtmlReq.getId();\n        String content = topicModifyHtmlReq.getHtml();\n        log.info(\"user：{} 修改 话题标题{} content:{}\", UserContext.getUsername(), id, content);\n\n        LambdaUpdateWrapper<Topic> uw = new LambdaUpdateWrapper<>();\n        uw.eq(Topic::getId, id)\n                .set(Topic::getContent, content)\n                .set(Topic::getUpdateTime, new Date());\n        topicDao.update(null, uw);\n        //删除缓存\n        redisUtils.delete(TopicKeyConstants.HASHKP_TOPIC + id);\n        //更新话题活跃时间\n        topicSpAttrWriteService.changeActivityTime(id, new Date());\n\n    }\n\n    @Override\n    public void updateContent(long id, String content) {\n\n        log.info(\"user：{} 修改 话题标题{} content:{}\", UserContext.getUsername(), id, content);\n\n        LambdaUpdateWrapper<Topic> uw = new LambdaUpdateWrapper<>();\n        uw.eq(Topic::getId, id)\n                .set(Topic::getContent, content)\n                .set(Topic::getUpdateTime, new Date());\n        topicDao.update(null, uw);\n        //删除缓存\n        redisUtils.delete(TopicKeyConstants.HASHKP_TOPIC + id);\n        //更新话题活跃时间\n        topicSpAttrWriteService.changeActivityTime(id, new Date());\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicEsSearchService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.community.model.request.TopicQueryByCategoryIdReq;\nimport com.acimage.community.model.request.TopicQueryBySortReq;\nimport com.acimage.community.model.request.TopicQueryByTagIdReq;\nimport com.acimage.community.model.request.TopicSearchReq;\n\nimport java.util.List;\n\npublic interface TopicEsSearchService {\n    List<Topic> searchSimilar(long topicId, int size);\n\n    List<Topic> searchSimilarByTitle(long topicId,String title, int size);\n    MyPage<Topic> searchByTagId(TopicQueryByTagIdReq queryReq);\n    MyPage<Topic> searchBySort(TopicQueryByCategoryIdReq queryReq);\n\n    MyPage<Topic> searchBySort(TopicQueryBySortReq queryReq);\n\n    MyPage<Topic> search(TopicSearchReq topicSearchReq);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicHtmlQueryService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.TopicHtml;\n\npublic interface TopicHtmlQueryService {\n\n    TopicHtml getTopicHtml(long topicId);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicHtmlWriteService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.TopicHtml;\n\npublic interface TopicHtmlWriteService {\n    TopicHtml save(long topicId, String html);\n\n    void remove(long topicId);\n\n    void update(long topicId, String html);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicInfoQueryService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport com.acimage.community.model.vo.TopicInfoVo;\n\nimport java.util.List;\n\npublic interface TopicInfoQueryService {\n    Topic getTopicWithUserTagIds(long topicId);\n    TopicInfoVo getTopicInfoAndFirstCommentPage(long topicId);\n    MyPage<Topic> pageUserTopicsInfoOrderByCreateTime(long userId, int pageNo, int pageSize);\n    List<Topic> listTopicsInfoSortBy(TopicAttribute attribute, int pageNo, int pageSize);\n\n    List<Topic> listRandomTopicsInRank(int size);\n\n    MyPage<Topic> pageTopicsInfoRankByActivityTime(int pageNo, int pageSize);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicInfoWriteService.java",
    "content": "package com.acimage.community.service.topic;\n\n\nimport com.acimage.community.model.request.TopicAddReq;\nimport com.acimage.community.model.request.TopicModifyHtmlReq;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.multipart.MultipartFile;\n\n\nimport java.io.IOException;\n\npublic interface TopicInfoWriteService {\n\n    @Transactional(rollbackFor = {Exception.class})\n    long saveTopicInfo(TopicAddReq topicAddReq, MultipartFile coverImage);\n\n    @Transactional(rollbackFor = {Exception.class})\n    void removeTopicInfo(long topicId);\n\n    @Transactional(rollbackFor = {Exception.class})\n    void removeTopicInfoWithoutVerification(long topicId);\n\n    @Transactional\n    void updateHtml(TopicModifyHtmlReq modifyReq);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicPreheatService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.community.global.enums.TopicAttribute;\n\nimport java.util.concurrent.TimeUnit;\n\npublic interface TopicPreheatService {\n    void preheatTopicsOrderBy(TopicAttribute attr, int rankSize, int cacheSize, long timeout, TimeUnit timeUnit);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicQueryService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.Topic;\n\nimport java.util.List;\n\npublic interface TopicQueryService {\n    Topic getTopic(long id);\n    List<Topic> listTopicWithUser(List<Long> ids);\n    List<Topic> listTopicsByIds(List<Long> ids);\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicRankQueryService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.community.global.enums.TopicAttribute;\n\nimport java.util.List;\n\n\n/**\n * 负责话题的 浏览量、收藏数、评论数、最新活跃时间 相关的服务\n */\npublic interface TopicRankQueryService {\n\n    List<Long> listTopicIdsInRank(TopicAttribute topicAttribute, int pageNo, int pageSize);\n\n    List<Long> listRandomTopicIdsInRank(TopicAttribute topicAttribute, int size);\n\n    List<Pair<Long, Double>> listTopicIdWithScoresInRank(TopicAttribute topicAttribute, int pageNo, int pageSize);\n    Integer countTopicIdsInRank(TopicAttribute topicAttribute);\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicRankWriteService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.community.global.enums.TopicAttribute;\n\nimport javax.validation.constraints.NotNull;\n\npublic interface TopicRankWriteService {\n\n    void updateRank(TopicAttribute attr, long topicId, double newScore);\n\n    void updateRank(TopicAttribute attr, @NotNull Topic topic);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicSpAttrQueryService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.community.global.enums.TopicAttribute;\n\nimport java.util.Date;\n\npublic interface TopicSpAttrQueryService {\n    Integer getPageView(long topicId);\n    Date getActivityTime(long topicId);\n    Integer getStarCount(long topicId);\n    Integer getCommentCount(long topicId);\n    void setAttrIntoTopic(Topic topic, TopicAttribute... attrs);\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicSpAttrWriteService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport java.util.Date;\nimport java.util.List;\n\npublic interface TopicSpAttrWriteService {\n    void removeAttributes(long topicId);\n    void updateStarCountByIncrement(List<Long> topicIds,List<Integer> increments);\n    void updateCommentCountByIncrement(List<Long> topicIds,List<Integer> increments);\n    void updatePageViewByIncrement(List<Long> topicIds,List<Integer> increments);\n    void updateBatchActivityTime(List<Long> topicIds, List<Date> activityTimes);\n    void changeActivityTime(long topicId, Date date);\n\n    void increaseStarCount(long topicId, int increment);\n    void increaseCommentCount(long topicId, int increment);\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/service/topic/TopicWriteService.java",
    "content": "package com.acimage.community.service.topic;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.community.model.request.TopicModifyHtmlReq;\n\npublic interface TopicWriteService {\n    void save(Topic topic);\n    void remove(long id);\n    void update(long id,String title,String content);\n\n    void updateTitle(long id, String title);\n\n    void updateContent(TopicModifyHtmlReq topicModifyHtmlReq);\n\n    void updateContent(long id, String content);\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/utils/RsaUtils.java",
    "content": "package com.acimage.community.utils;\n\nimport cn.hutool.crypto.asymmetric.KeyType;\nimport cn.hutool.crypto.asymmetric.RSA;\n\npublic class RsaUtils {\n    private static final String privateKey;\n    private static final String publicKey;\n\n    static {\n        RSA rsa = new RSA();\n        privateKey=rsa.getPrivateKeyBase64();\n        publicKey =rsa.getPublicKeyBase64();\n    }\n    public static String decrypt(String privateKeyBase64,String encryptBase64){\n        RSA rsa=new RSA(privateKeyBase64,null);\n        return rsa.decryptStr(encryptBase64, KeyType.PrivateKey);\n    }\n\n    public static String getPrivateKey(){\n        return privateKey;\n    }\n    public static String getPublicKey(){\n        return publicKey;\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/CategoryQueryController.java",
    "content": "package com.acimage.community.web.controller;\n\nimport com.acimage.common.model.domain.community.Category;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.service.categoty.CategoryQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n@RestController\n@Slf4j\n@Validated\n@RequestMapping(\"/api/community/categories/query\")\npublic class CategoryQueryController {\n    @Autowired\n    CategoryQueryService categoryQueryService;\n\n    @GetMapping(\"/all\")\n    public Result<List<Category>> queryAllCategories() {\n        return Result.ok(categoryQueryService.listAll());\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/CommentOperateController.java",
    "content": "package com.acimage.community.web.controller;\n\n\n\nimport com.acimage.common.global.consts.TimeConstants;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.model.request.CommentAddReq;\nimport com.acimage.community.model.request.CommentModifyReq;\nimport com.acimage.community.service.comment.CommentWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@Validated\n@RequestMapping(\"/api/community/comments/operate\")\npublic class CommentOperateController {\n    @Autowired\n    CommentWriteService commentWriteService;\n\n    @RequestLimit(limitTimes = {1,20},\n            durations = {3, TimeConstants.DAY_SECONDS},\n            penaltyTimes = {-1,-1},\n            targets = {LimitTarget.IP,LimitTarget.USER})\n    @PostMapping\n    public Result<?> addComment(@Validated @RequestBody CommentAddReq commentAddReq){\n        String trimContent=commentAddReq.getContent().trim();\n        commentAddReq.setContent(trimContent);\n        if(trimContent.length()<2){\n            return Result.fail(\"评论有效字数不能少于2个字\");\n        }\n        commentWriteService.saveComment(commentAddReq);\n        log.info(\"评论了 {}\", commentAddReq);\n        return Result.ok(\"评论成功\");\n    }\n\n    @RequestLimit(limitTimes = {1,20},\n            durations = {3, TimeConstants.DAY_SECONDS},\n            penaltyTimes = {-1,-1},\n            targets = {LimitTarget.IP,LimitTarget.USER})\n    @PutMapping\n    public Result<?> modifyComment(@Validated @RequestBody CommentModifyReq commentModifyReq){\n        String trimContent=commentModifyReq.getContent().trim();\n        commentModifyReq.setContent(trimContent);\n        if(trimContent.length()<2){\n            return Result.fail(\"评论有效字数不能少于2个字\");\n        }\n        log.info(\"修改了 {}\", commentModifyReq);\n        commentWriteService.updateComment(commentModifyReq);\n        return Result.ok();\n    }\n\n    @RequestLimit(limitTimes = {1,20},\n            durations = {3, TimeConstants.DAY_SECONDS},\n            penaltyTimes = {-1,-1},\n            targets = {LimitTarget.IP,LimitTarget.USER})\n    @DeleteMapping(\"/{id}\")\n    public Result<?> deleteComment(@Positive @NotNull @PathVariable(\"id\") Long id){\n        log.info(\"删除 评论{}\",id);\n        commentWriteService.removeComment(id);\n        return Result.ok();\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/CommentQueryController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.result.Result;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.community.global.consts.PageSizeConstants;\nimport com.acimage.community.service.comment.CommentInfoQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.hibernate.validator.constraints.Range;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@Validated\n@RequestMapping(\"/api/community/comments/query\")\npublic class CommentQueryController {\n    @Autowired\n    CommentInfoQueryService commentInfoQueryService;\n\n    @GetMapping(\"/topicComments\")\n    public Result<?> pageTopicComments(@Positive @RequestParam(\"topicId\") Long topicId,\n                                       @Range(min=1,max=100,message=\"页码在1-100之间\") @RequestParam(\"pageNo\") Integer pageNo) {\n        List<Comment> comments = commentInfoQueryService.pageCommentsWithUser(topicId, pageNo, PageSizeConstants.TOPIC_COMMENTS);\n        return Result.ok(comments);\n    }\n\n    @GetMapping(\"/mine/{pageNo}\")\n    public Result<?> pageMyComments(@Range(min=1,max=100,message=\"页码在1-100之间\") @PathVariable(\"pageNo\") Integer pageNo) {\n        return Result.ok(commentInfoQueryService.pageCommentsWithTopicOrderByCreateTime(UserContext.getUserId(),\n                pageNo,\n                PageSizeConstants.ACTIVITY_COMMENTS));\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/HomeCarouselQueryController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.enums.AuthenticationType;\nimport com.acimage.common.model.domain.community.HomeCarousel;\nimport com.acimage.common.result.Result;\n\nimport com.acimage.community.service.homecarousel.HomeCarouselQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n@RestController\n@Slf4j\n@Validated\n@Authentication(type = AuthenticationType.NONE)\n@RequestMapping(\"/api/community/homeCarousels\")\npublic class HomeCarouselQueryController {\n    @Autowired\n    HomeCarouselQueryService homeCarouselQueryService;\n\n    @GetMapping(\"/list\")\n    public Result<List<HomeCarousel>> queryHomeCarousel() {\n        List<HomeCarousel> homeCarousels = homeCarouselQueryService.listAll();\n        return Result.ok(homeCarousels);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/StarOperateController.java",
    "content": "package com.acimage.community.web.controller;\n\n\n\nimport com.acimage.common.global.consts.TimeConstants;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.community.service.star.StarMixQueryService;\nimport com.acimage.community.service.star.StarQueryService;\nimport com.acimage.community.service.star.StarWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@Validated\n@Authentication\n@RequestMapping(\"/api/community/stars/operate\")\npublic class StarOperateController {\n    @Autowired\n    StarWriteService starWriteService;\n    @Autowired\n    StarQueryService starQueryService;\n    @Autowired\n    StarMixQueryService starMixQueryService;\n\n    @RequestLimit(limitTimes = {1},\n            durations = {3},\n            penaltyTimes = {-1},\n            targets = {LimitTarget.USER})\n    @PostMapping(\"/{topicId}\")\n    public Result<?> addStar(@Positive @PathVariable(\"topicId\") Long topicId) {\n        starWriteService.saveStar(UserContext.getUserId(),topicId);\n        return Result.ok();\n    }\n\n    @RequestLimit(limitTimes = {1},\n            durations = {3},\n            penaltyTimes = {-1},\n            targets = {LimitTarget.USER})\n    @DeleteMapping(\"/{topicId}\")\n    public Result<?> deleteStar(@Positive @PathVariable(\"topicId\") Long topicId) {\n        starWriteService.removeStar(UserContext.getUserId(),topicId);\n        return Result.ok();\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/StarQueryController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.service.star.StarMixQueryService;\nimport com.acimage.community.service.star.StarQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\n\n@RestController\n@Slf4j\n@Validated\n@Authentication\n@RequestMapping(\"/api/community/stars/query\")\npublic class StarQueryController {\n    @Autowired\n    StarQueryService starQueryService;\n    @Autowired\n    StarMixQueryService starMixQueryService;\n\n    @GetMapping(\"/mine/{pageNo}\")\n    public Result<?> pageMyStars(@Positive @PathVariable(\"pageNo\") Integer pageNo) {\n        return Result.ok(starMixQueryService.pageStarsWithTopic(UserContext.getUserId(),pageNo));\n    }\n\n    @GetMapping(\"/isStar/{topicId}\")\n    public Result<?> isStar(@Positive @PathVariable(\"topicId\") Long topicId){\n        return Result.ok(starQueryService.isStar(UserContext.getUserId(),topicId));\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/TagQueryController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.model.domain.community.Tag;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.service.tag.TagQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n@RestController\n@Slf4j\n@Validated\n@RequestMapping(\"/api/community/tags/query\")\npublic class TagQueryController {\n    @Autowired\n    TagQueryService tagQueryService;\n\n    @GetMapping(\"/all\")\n    public Result<List<Tag>> queryAllTags() {\n        return Result.ok(tagQueryService.listAll());\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/TopicOperateController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.consts.FileFormatConstants;\nimport com.acimage.common.global.consts.TimeConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.common.FileUtils;\nimport com.acimage.community.model.request.TopicAddReq;\nimport com.acimage.community.model.request.TopicModifyHtmlReq;\nimport com.acimage.community.service.topic.TopicInfoWriteService;\nimport com.acimage.community.service.topic.TopicWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\n\nimport javax.validation.constraints.Positive;\nimport javax.validation.constraints.Size;\n\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/community/topics/operate\")\n@Validated\n@Authentication\npublic class TopicOperateController {\n\n    @Autowired\n    TopicInfoWriteService topicInfoWriteService;\n    @Autowired\n    TopicWriteService topicWriteService;\n\n    @RequestLimit(limitTimes = {1, 10}, durations = {3, TimeConstants.DAY_SECONDS}, penaltyTimes = {-1, -1}, targets = {LimitTarget.IP, LimitTarget.USER})\n    @PostMapping\n    public Result<Long> addTopic(@Validated @ModelAttribute TopicAddReq topicAddReq, @RequestParam(\"coverImage\") MultipartFile coverImage) {\n        String title = topicAddReq.getTitle().trim();\n        topicAddReq.setTitle(title);\n        if (title.length() < Topic.TITLE_MIN || title.length() > Topic.TITLE_MAX) {\n            return Result.fail(Topic.TAG_VALIDATION_MSG);\n        }\n\n\n        log.info(\"用户：{} 请求新增话题{}\", UserContext.getUsername(), topicAddReq);\n        String format = FileUtils.formatOf(coverImage);\n        if (!FileFormatConstants.ALLOWED_COVER_IMAGE_FORMAT.contains(format)) {\n            return Result.fail(\"封面图片格式需为\" + FileFormatConstants.ALLOWED_IMAGE_FORMAT);\n        }\n\n        Integer[] tagIds = topicAddReq.getTagIds();\n        if (tagIds==null||tagIds.length > Topic.TAG_MAX || tagIds.length < Topic.TAG_MIN) {\n            return Result.fail(Topic.TAG_VALIDATION_MSG);\n        }\n\n        log.info(\"用户：{} 话题: 新增话题{}\", UserContext.getUsername(), topicAddReq);\n        long topicId = topicInfoWriteService.saveTopicInfo(topicAddReq, coverImage);\n        return Result.ok(topicId);\n    }\n\n    @RequestLimit(limitTimes = {1, 20}, durations = {3, TimeConstants.DAY_SECONDS}, penaltyTimes = {-1, -1}, targets = {LimitTarget.IP, LimitTarget.USER})\n    @PutMapping(\"/title/{id}/{title}\")\n    public Result<?> modifyTitle(@Positive @PathVariable Long id,\n                                 @PathVariable @Size(min = Topic.TITLE_MIN, max = Topic.TITLE_MAX, message = Topic.TITLE_VALIDATION_MSG) String title) {\n        String trimTitle = title.trim();\n        if (trimTitle.length() < Topic.TITLE_MIN) {\n            return Result.fail(String.format(\"标题有效长度不少于%s\", Topic.TITLE_MIN));\n        }\n        topicWriteService.updateTitle(id, trimTitle);\n        return Result.ok();\n    }\n\n    @RequestLimit(limitTimes = {1, 15}, durations = {3, TimeConstants.DAY_SECONDS}, penaltyTimes = {-1, -1}, targets = {LimitTarget.IP, LimitTarget.USER})\n    @PutMapping(\"/html\")\n    public Result<?> modifyHtml(@Validated @RequestBody TopicModifyHtmlReq topicModifyHtmlReq) {\n        topicInfoWriteService.updateHtml(topicModifyHtmlReq);\n        return Result.ok();\n    }\n\n    @RequestLimit(limitTimes = {1, 15}, durations = {3, TimeConstants.DAY_SECONDS}, penaltyTimes = {-1, -1}, targets = {LimitTarget.IP, LimitTarget.USER})\n    @DeleteMapping(\"/{id}\")\n    public Result<?> deleteTopic(@Positive @PathVariable(\"id\") Long id) {\n        log.info(\"删除了话题：{}\", id);\n        topicInfoWriteService.removeTopicInfo(id);\n        return Result.ok();\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/TopicQueryController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.community.global.annotation.TopicId;\nimport com.acimage.community.global.consts.PageSizeConstants;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport com.acimage.community.model.request.TopicQueryByCategoryIdReq;\nimport com.acimage.community.model.request.TopicQueryBySortReq;\nimport com.acimage.community.model.request.TopicQueryByTagIdReq;\nimport com.acimage.community.model.vo.TopicInfoVo;\nimport com.acimage.community.service.topic.TopicEsSearchService;\nimport com.acimage.community.service.topic.TopicInfoQueryService;\nimport com.acimage.community.global.annotation.RecordPageView;\nimport com.acimage.common.global.context.UserContext;\nimport lombok.extern.slf4j.Slf4j;\nimport org.hibernate.validator.constraints.Range;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\n\nimport javax.validation.constraints.Positive;\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/community/topics/query\")\n@Validated\n@Authentication\npublic class TopicQueryController {\n    @Autowired\n    TopicInfoQueryService topicInfoQueryService;\n    @Autowired\n    TopicEsSearchService topicEsSearchService;\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @RecordPageView\n    @GetMapping(\"/info/{id}\")\n    public Result<TopicInfoVo> queryTopicAndFirstCommentPage(@TopicId @Positive @PathVariable(\"id\") Long id) {\n        TopicInfoVo topicInfoVo = topicInfoQueryService.getTopicInfoAndFirstCommentPage(id);\n        return Result.ok(topicInfoVo);\n    }\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/byTagId\")\n    public Result<MyPage<Topic>> pageByTagId(@Validated @ModelAttribute TopicQueryByTagIdReq queryReq) {\n        return Result.ok(topicEsSearchService.searchByTagId(queryReq));\n    }\n\n    @RequestLimit(limitTimes = {20}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/byCategoryId\")\n    public Result<MyPage<Topic>> pageByCategoryId(@Validated @ModelAttribute TopicQueryByCategoryIdReq queryReq) {\n        return Result.ok(topicEsSearchService.searchBySort(queryReq));\n    }\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/bySort\")\n    public Result<MyPage<Topic>> pageTopicsBySort(@Validated @ModelAttribute TopicQueryBySortReq queryReq) {\n        return Result.ok(topicEsSearchService.searchBySort(queryReq));\n    }\n\n    /**\n     * @return 三个列表，分别是最多评论，最多star，最多浏览的评论\n     */\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/hot/3attrs\")\n    public Result<List<List<Topic>>> combineHotTopics() {\n        List<List<Topic>> list=new ArrayList<>();\n        list.add(topicInfoQueryService.listTopicsInfoSortBy(TopicAttribute.COMMENT_COUNT,1,10));\n        list.add(topicInfoQueryService.listTopicsInfoSortBy(TopicAttribute.STAR_COUNT,1,10));\n        list.add(topicInfoQueryService.listTopicsInfoSortBy(TopicAttribute.PAGE_VIEW,1,10));\n        return Result.ok(list);\n    }\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/recentHot\")\n    public Result<List<Topic>> queryRecentHotTopics() {\n        int pageNo = 1;\n        int pageSize=10;\n        return Result.ok(topicInfoQueryService.listTopicsInfoSortBy(TopicAttribute.PAGE_VIEW,pageNo,pageSize));\n    }\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/recommend\")\n    public Result<List<Topic>> queryRecommendedTopics() {\n        int size=4;\n        return Result.ok(topicInfoQueryService.listRandomTopicsInRank(size));\n    }\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/most/commentCount\")\n    public Result<List<Topic>> queryMostCommentCountTopics() {\n        int pageNo = 1;\n        int pageSize=10;\n        return Result.ok(topicInfoQueryService.listTopicsInfoSortBy(TopicAttribute.COMMENT_COUNT,pageNo,pageSize));\n    }\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/most/active/{pageNo}/{pageSize}\")\n    public Result<MyPage<Topic>> pageActiveTopics(@Range(min = 1, max = 100, message = \"页码在1到100之间\") @PathVariable int pageNo,\n                                                  @Range(min = 4, max = 20, message = \"页大小在4到20之间\") @PathVariable int pageSize) {\n        return Result.ok(topicInfoQueryService.pageTopicsInfoRankByActivityTime(pageNo, pageSize));\n    }\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/mine/{pageNo}\")\n    public Result<MyPage<Topic>> queryMyTopics(@Range(min = 1, max = 100, message = \"页码在1到100之间\") @PathVariable(\"pageNo\") int pageNo) {\n        return Result.ok(topicInfoQueryService.pageUserTopicsInfoOrderByCreateTime(UserContext.getUserId(), pageNo, PageSizeConstants.ACTIVITY_TOPICS));\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/TopicSearchController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.model.request.TopicSearchReq;\nimport com.acimage.community.service.topic.TopicEsSearchService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/community/topics/search\")\n@Validated\npublic class TopicSearchController {\n\n    @Autowired\n    TopicEsSearchService topicEsSearchService;\n\n    @RequestLimit(limitTimes = {2}, durations = {1}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/multiSearch\")\n    public Result<MyPage<Topic>> searchTopics(@Validated @ModelAttribute TopicSearchReq topicSearchReq) {\n        String search= topicSearchReq.getSearch();\n        if(search!=null){\n            topicSearchReq.setSearch(search.trim());\n        }\n        return Result.ok(topicEsSearchService.search(topicSearchReq));\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/controller/UserRankController.java",
    "content": "package com.acimage.community.web.controller;\n\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.community.service.cmtyuser.CmtyUserRankService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.hibernate.validator.constraints.Range;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Max;\nimport javax.validation.constraints.Min;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/api/community/users/rank\")\n@Validated\npublic class UserRankController {\n\n    @Autowired\n    CmtyUserRankService cmtyUserRankService;\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/byColumn\")\n    public Result<MyPage<CmtyUser>> rankByTopicCount(@RequestParam @Range(min=1,max=10) Integer pageNo,\n                                                       @RequestParam String column) {\n        List<String> allowedColumns= LambdaUtils.columnsFrom(CmtyUser::getTopicCount,CmtyUser::getStarCount);\n        if(!allowedColumns.contains(column)){\n            return Result.fail(\"非法字段\");\n        }\n        int pageSize=10;\n        return Result.ok(cmtyUserRankService.pageUserRankBy(column,pageNo, pageSize));\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/provider/CmtyUserProvider.java",
    "content": "package com.acimage.community.web.provider;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.service.cmtyuser.CmtyUserQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/community/cmtyUsers\")\n@Validated\npublic class CmtyUserProvider {\n    @Autowired\n    CmtyUserQueryService cmtyUserQueryService;\n\n    @GetMapping(\"/userId/{userId}\")\n    public Result<CmtyUser> queryCmtyUser(@PathVariable Long userId) {\n        return Result.ok(cmtyUserQueryService.getCmtyUser(userId));\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/provider/CommentProvider.java",
    "content": "package com.acimage.community.web.provider;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.service.comment.CommentWriteService;\nimport com.acimage.community.service.topic.TopicInfoWriteService;\nimport com.acimage.community.service.topic.TopicQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/community/comments\")\n@Validated\npublic class CommentProvider {\n\n    @Autowired\n    CommentWriteService commentWriteService;\n\n\n    @RequestLimit(limitTimes = {1},durations = {3},penaltyTimes = {1},targets = {LimitTarget.USER})\n    @DeleteMapping(\"/{id}\")\n    public Result<?> deleteComment(@Positive @NotNull @PathVariable(\"id\") Long id){\n        log.info(\"删除 评论{}\",id);\n        commentWriteService.removeCommentWithoutVerification(id);\n        return Result.ok();\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/java/com/acimage/community/web/provider/TopicProvider.java",
    "content": "package com.acimage.community.web.provider;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.result.Result;\nimport com.acimage.community.service.topic.TopicInfoWriteService;\nimport com.acimage.community.service.topic.TopicQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.Positive;\nimport java.util.List;\n\n@RestController\n@Slf4j\n@RequestMapping(\"/community/topics\")\n@Validated\npublic class TopicProvider {\n\n    @Autowired\n    TopicQueryService topicQueryService;\n    @Autowired\n    TopicInfoWriteService topicInfoWriteService;\n\n    @GetMapping(\"/ids\")\n    public Result<List<Topic>> queryTopics(@RequestParam(\"topicIds\") List<Long> topicIds) {\n        return Result.ok(topicQueryService.listTopicWithUser(topicIds));\n    }\n\n    @DeleteMapping(\"/{topicId}\")\n    public Result<?> delete(@PathVariable @Positive Long topicId) {\n        topicInfoWriteService.removeTopicInfoWithoutVerification(topicId);\n        return Result.ok();\n    }\n\n}\n"
  },
  {
    "path": "acimage_community/src/main/resources/application-dev.yml",
    "content": "\nserver:\n  port: 8080\n\nspring:\n  config:\n    activate:\n      on-profile:\n        - dev\n  rabbitmq:\n    virtual-host: /acimage\n    host: 192.168.130.128\n    port: 5672\n    username: acimage\n    password: acimage\n    listener:\n      simple:\n        auto-startup: false #消费者是否自动启动\n      direct:\n        auto-startup: false #生产者是否自动启动\n  datasource:\n    url: jdbc:mysql://localhost:3306/acimage_community?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n    username: root\n    password: mysql\n  redis:\n    host: 192.168.130.128\n    port: 6379\n    password: redis\n    lettuce:\n      pool:\n        max-active: 8\n        max-idle: 8 #最大空闲连接\n        min-idle: 0 #最小空闲连接\n        max-wait: 100ms #连接等待时间\n  cloud:\n    nacos:\n      server-addr: localhost:8848 #nacos 服务地址"
  },
  {
    "path": "acimage_community/src/main/resources/application.yml",
    "content": "\nspring:\n  profiles:\n    include: common,common-secret\n    active: dev2\n  servlet:\n    multipart:\n      max-file-size: 10MB\n      max-request-size: 55MB\n  application:\n    name: community-service #服务名称\n  datasource:\n    type: com.alibaba.druid.pool.DruidDataSource\n    driver-class-name: com.mysql.cj.jdbc.Driver\n\n\n\nmybatis-plus:\n  mapper-locations: classpath:mapper/*.xml\n  type-aliases-package: com.acimage.common.model.domain,com.acimage.community.model.domain\n  configuration:\n    map-underscore-to-camel-case: true\n#    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl\n  global-config:\n    db-config:\n      table-prefix: tb_\n\nfeign:\n  okhttp:\n    enabled: true\n  httpclient:\n    max-connections: 20 # 最大的连接数\n    max-connections-per-route: 5 # 每个路径的最大连接数\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "acimage_community/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- 配置文件修改时重新加载，默认true -->\n<configuration scan=\"true\">\n\n    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->\n    <springProperty scope=\"context\" name=\"spring.application.name\" source=\"spring.application.name\"/>\n    <springProperty scope=\"context\" name=\"LOG_HOME\" source=\"logback.base\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%20.20thread{20}] %40logger{40} : %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"info\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>INFO</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-info.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>8</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"warn\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>WARN</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-warn.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"error\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>ERROR</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-error.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n\n    <springProfile name=\"dev2\">\n        <logger name=\"com.acimage.community.dao\" level=\"INFO\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <logger name=\"com.acimage\" level=\"DEBUG\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n    <springProfile name=\"prod,server\">\n        <logger name=\"com.acimage\" level=\"info\" additivity=\"false\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n\n</configuration>"
  },
  {
    "path": "acimage_community/src/main/resources/mapper/CmtyUserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.community.dao.CmtyUserDao\">\n<!--    <resultMap id=\"withUserBasic\" type=\"UserCommunityStatistic\">-->\n<!--        <id property=\"userId\" column=\"user_id\"/>-->\n<!--        <result property=\"starCount\" column=\"star_count\"/>-->\n<!--        <result property=\"topicCount\" column=\"topic_count\"/>-->\n<!--        <association property=\"user\" javaType=\"UserBasic\" column=\"user_id\"-->\n<!--                     select=\"com.acimage.community.dao.CmtyUserDao.selectById\"/>-->\n<!--    </resultMap>-->\n\n    <update id=\"batchUpdateStarCount\">\n        <foreach collection=\"userIdAndIncrements\" item=\"userIdAndIncrement\" index=\"index\" separator=\";\">\n            update tb_cmty_user set star_count = #{userIdAndIncrement.value} where id=#{userIdAndIncrement.key}\n        </foreach>\n    </update>\n\n    <update id=\"batchUpdateTopicCount\">\n        <foreach collection=\"userIdAndIncrements\" item=\"userIdAndIncrement\" index=\"index\" separator=\";\">\n            update tb_cmty_user set topic_count = #{userIdAndIncrement.value} where id=#{userIdAndIncrement.key}\n        </foreach>\n    </update>\n\n<!--    <select id=\"selectListOrderByColumn\" resultMap=\"withUserBasic\">-->\n<!--        select * from tb_user_community_statistic order by ${column} desc limit #{startIndex},#{recordNum}-->\n<!--    </select>-->\n\n</mapper>"
  },
  {
    "path": "acimage_community/src/main/resources/mapper/CommentMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.community.dao.CommentDao\" >\n    <resultMap id=\"commentWithTopic\" type=\"Comment\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"topicId\" column=\"topic_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <association property=\"topic\" javaType=\"Topic\" column=\"topic_id\" select=\"com.acimage.community.dao.TopicDao.selectById\"/>\n    </resultMap>\n\n    <resultMap id=\"commentWithUser\" type=\"Comment\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"topicId\" column=\"topic_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <association property=\"user\" javaType=\"CmtyUser\" column=\"user_id\" select=\"com.acimage.community.dao.CmtyUserDao.selectById\"/>\n    </resultMap>\n\n    <select id=\"selectCommentsWithUserByTopicId\" resultMap=\"commentWithUser\">\n        select * from tb_comment where topic_id = #{topicId} and deleted=0 order by create_time desc\n    </select>\n\n    <select id=\"selectCommentsWithUser\" resultMap=\"commentWithUser\">\n        select * from tb_comment where topic_id=#{topicId} and deleted=0 order by create_time desc limit #{startIndex},#{recordNumber}\n    </select>\n\n    <select id=\"selectCommentsWithTopicOrderByCreateTime\" resultMap=\"commentWithTopic\">\n        select * from tb_comment where user_id=#{userId} and deleted=0 order by create_time desc limit #{startIndex},#{recordNumber}\n    </select>\n\n</mapper>"
  },
  {
    "path": "acimage_community/src/main/resources/mapper/ImageMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.community.dao.ImageDao\" >\n\n    <resultMap id=\"imageWithTopic\" type=\"Image\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"topicId\" column=\"topic_id\"/>\n        <result property=\"description\" column=\"description\"/>\n        <association property=\"topic\" javaType=\"Topic\" column=\"topic_id\" select=\"com.acimage.community.dao.TopicDao.selectTopicWithUser\"/>\n    </resultMap>\n\n    <insert id=\"insertList\" >\n            insert into tb_image(id,topic_id,size,description,url) values\n            <foreach collection=\"list\" item=\"image\" index=\"index\" separator=\",\">\n                ( #{image.id},#{image.topicId},#{image.size},#{image.description},#{image.url} )\n            </foreach>\n    </insert>\n\n    <update id=\"updateDescription\" >\n            <foreach collection=\"list\" item=\"idAndDescription\" index=\"index\" separator=\";\">\n                update tb_image set description=#{idAndDescription.value} where id=#{idAndDescription.key}\n            </foreach>\n    </update>\n\n    <select id=\"selectImagesWithTopic\" resultMap=\"imageWithTopic\">\n        select * from tb_image where id in\n        <foreach collection=\"list\" item=\"imageId\" index=\"index\" separator=\",\" open=\"(\" close=\")\">\n           #{imageId}\n        </foreach>\n    </select>\n\n</mapper>"
  },
  {
    "path": "acimage_community/src/main/resources/mapper/StarMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.community.dao.StarDao\" >\n    <resultMap id=\"starWithTopic\" type=\"Star\">\n        <result property=\"topicId\" column=\"topic_id\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <association property=\"topic\" javaType=\"Topic\" column=\"topic_id\" select=\"com.acimage.community.dao.TopicDao.selectById\"/>\n    </resultMap>\n\n    <select id=\"countStarsOwnedBy\" resultType=\"Integer\">\n        with temp_topic as (select id from tb_topic where user_id=#{userId})\n        select count(*) as starCount from temp_topic inner join tb_star on temp_topic.id=tb_star.topic_id\n    </select>\n\n    <select id=\"selectStarsWithTopicOrderByCreateTime\" resultMap=\"starWithTopic\">\n        select * from tb_star where user_id=#{userId} order by create_time desc limit #{startIndex},#{recordNumber}\n    </select>\n\n</mapper>"
  },
  {
    "path": "acimage_community/src/main/resources/mapper/TagTopicMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.community.dao.TagTopicDao\">\n\n    <insert id=\"insertBatch\">\n        insert into tb_tag_topic(id,topic_id,tag_id,create_time) values\n        <foreach collection=\"list\" item=\"item\" index=\"index\" separator=\",\">\n            (#{item.id},#{item.topicId},#{item.tagId},#{item.createTime})\n        </foreach>\n    </insert>\n\n</mapper>"
  },
  {
    "path": "acimage_community/src/main/resources/mapper/TopicMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.community.dao.TopicDao\">\n    <resultMap id=\"topicWithUser\" type=\"Topic\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"activityTime\" column=\"activity_time\"/>\n        <result property=\"pageView\" column=\"page_view\"/>\n        <result property=\"starCount\" column=\"star_count\"/>\n        <result property=\"commentCount\" column=\"comment_count\"/>\n        <result property=\"coverImageUrl\" column=\"cover_image_url\"/>\n        <result property=\"categoryId\" column=\"category_id\"/>\n        <association property=\"user\" javaType=\"CmtyUser\" column=\"user_id\"\n                     select=\"com.acimage.community.dao.CmtyUserDao.selectById\"/>\n    </resultMap>\n\n\n    <resultMap id=\"topicWithUserImages\" type=\"Topic\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"pageView\" column=\"page_view\"/>\n        <result property=\"starCount\" column=\"star_count\"/>\n        <result property=\"commentCount\" column=\"comment_count\"/>\n        <result property=\"activityTime\" column=\"activity_time\"/>\n        <result property=\"coverImageUrl\" column=\"cover_image_url\"/>\n        <result property=\"categoryId\" column=\"category_id\"/>\n        <association property=\"user\" javaType=\"CmtyUser\" column=\"user_id\"\n                     select=\"com.acimage.community.dao.CmtyUserDao.selectById\"/>\n    </resultMap>\n\n    <resultMap id=\"topicWithUserTagIds\" type=\"Topic\">\n        <id property=\"id\" column=\"id\"/>\n        <result property=\"title\" column=\"title\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"userId\" column=\"user_id\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <result property=\"updateTime\" column=\"update_time\"/>\n        <result property=\"pageView\" column=\"page_view\"/>\n        <result property=\"starCount\" column=\"star_count\"/>\n        <result property=\"commentCount\" column=\"comment_count\"/>\n        <result property=\"activityTime\" column=\"activity_time\"/>\n        <result property=\"coverImageUrl\" column=\"cover_image_url\"/>\n        <result property=\"categoryId\" column=\"category_id\"/>\n        <association property=\"user\" javaType=\"CmtyUser\" column=\"user_id\"\n                     select=\"com.acimage.community.dao.CmtyUserDao.selectById\"/>\n        <collection property=\"tagIds\" ofType=\"Integer\" column=\"id\"\n                    select=\"com.acimage.community.dao.tagTopicDao.selectTagIds\"/>\n    </resultMap>\n\n\n    <update id=\"updatePvByIncrement\">\n        <foreach collection=\"idAndIncrements\" item=\"idAndIncrement\" index=\"index\" separator=\";\">\n            update tb_topic set page_view=page_view+#{idAndIncrement.value} where id=#{idAndIncrement.key} and deleted=0\n        </foreach>\n    </update>\n\n    <update id=\"batchUpdateColumnByIncrement\">\n        <foreach collection=\"idAndIncrements\" item=\"idAndIncrement\" index=\"index\" separator=\";\">\n            update tb_topic set ${column} = ${column} + #{idAndIncrement.value} where id=#{idAndIncrement.key} and deleted=0\n        </foreach>\n    </update>\n\n    <update id=\"batchUpdateActivityTime\">\n        <foreach collection=\"idAndActivityTimes\" item=\"idAndActivityTime\" index=\"index\" separator=\";\">\n            update tb_topic set activity_time = #{idAndActivityTime.value} where id=#{idAndActivityTime.key} and deleted=0\n        </foreach>\n    </update>\n\n\n    <select id=\"selectTopicWithUser\" resultMap=\"topicWithUser\">\n        select * from tb_topic where id=#{id} and deleted=0\n    </select>\n\n    <select id=\"selectTopicsWithUserOrderByPageView\" resultMap=\"topicWithUser\">\n        select * from tb_topic where create_time>#{startTime} order by page_view desc\n        <if test=\"limit!=null\">\n            limit #{limit}\n        </if>\n    </select>\n\n    <select id=\"selectTopicsWithUserOrderBy\" resultMap=\"topicWithUser\">\n        select * from tb_topic where deleted=0 order by ${column} desc limit #{limit}\n    </select>\n\n    <select id=\"selectTopicsWithUserByIds\" resultMap=\"topicWithUser\">\n        select * from tb_topic where deleted=0 and id in\n        <foreach collection=\"ids\" item=\"id\" index=\"index\" separator=\",\" open=\"(\" close=\")\">\n            #{id}\n        </foreach>\n    </select>\n\n    <select id=\"selectTopicsWithUserOrderByCreateTime\" resultMap=\"topicWithUser\">\n        select * from tb_topic where user_id=#{userId} and deleted=0 order by create_time desc limit #{startIndex},#{recordNumber}\n    </select>\n\n\n\n\n\n\n\n</mapper>"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/CasualTest.java",
    "content": "package com.acimage.community;\n\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.community.global.enums.TopicAttribute;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.*;\nimport java.util.regex.Pattern;\n\n@SpringBootTest\npublic class CasualTest {\n\n\n    @Test\n    public void testGetSnowflakeId(){\n        long id=IdUtil.getSnowflake().nextId();\n        System.out.println(id);\n        System.out.println(Long.toString(id).length());\n        System.out.println(System.currentTimeMillis());\n        TopicAttribute topicAttribute = TopicAttribute.ACTIVITY_TIME;\n\n\n    }\n\n\n    @Test\n    public void testMd5(){\n        String txt=\"b3cfd02862e5565ebecfa608035911d612346546\";\n        System.out.println(DigestUtil.md5Hex(txt).length());\n    }\n\n    @Test\n    public void testUUID(){\n        String uuid=IdUtil.simpleUUID();\n        System.out.println(uuid.length());\n    }\n\n    @Test\n    public void testPatternMatch(){\n        final String PASSWORD_PATTERN=\"^(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$\";\n        String password=\"5555555----\";\n        System.out.println(Pattern.matches(PASSWORD_PATTERN,password));\n    }\n\n    @Test\n    public void testDateNow(){\n        Date beginOfDay = DateUtil.beginOfDay(new Date());\n        //获取昨天开始时间\n        Date startDate=DateUtil.offsetDay(beginOfDay,-1);\n        String startTime=DateUtil.format(startDate,\"yyyy-MM-dd HH:mm:ss\");\n        System.out.println(startTime);\n    }\n\n    @Test\n    public void testSubAfter(){\n        String str=\"558.968.jpeg\";\n        System.out.println(StrUtil.subAfter(str,'.',true));\n        List<String> s= Arrays.asList(\"jpg\",\"png\");\n        System.out.println(s.contains(\"jpg\"));\n    }\n\n    @Test\n    public void testListUtils(){\n        List<Image> images=new ArrayList<>();\n        images.add(new Image(1L,null,null,null));\n        images.add(new Image(2L,null,null,null));\n        images.add(new Image(3L,null,null,null));\n        System.out.println(ListUtils.extract(Image::getId,images));\n    }\n\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/CommunityApplicationTests.java",
    "content": "package com.acimage.community;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\nclass CommunityApplicationTests {\n\n\t@Test\n\tvoid contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/dao/CommentDaoTest.java",
    "content": "package com.acimage.community.dao;\n\n\nimport com.acimage.common.model.domain.community.Comment;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.List;\n\n@SpringBootTest\npublic class CommentDaoTest {\n    @Autowired\n    CommentDao commentDao;\n    @Test\n    public void testSelectList(){\n        long topicId=1572508721685839872L;\n        List<Comment> commentList=commentDao.selectCommentsWithUser(topicId,1,5);\n        for(Comment comment:commentList){\n            System.out.println(comment);\n        }\n        System.out.println(commentList.size());\n    }\n\n    @Test\n    public void testCountComments(){\n        long topicId=1572508721685839872L;\n        System.out.println(commentDao.countCommentsByTopicId(topicId));\n    }\n\n    @Test\n    public void testSelectCommentsWithTopicOrderByCreateTime(){\n        long userId=1572443275490078720L;\n        System.out.println(commentDao.selectCommentsWithTopicOrderByCreateTime(userId,0,10));\n    }\n\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/dao/ImageDaoTest.java",
    "content": "package com.acimage.community.dao;\n\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n@SpringBootTest\npublic class ImageDaoTest {\n    @Autowired\n    ImageDao imageDao;\n\n    @Test\n    public void selectImagesWithTopic(){\n        List<Long> imageIds= Arrays.asList(1572508721903943680L,1572508721903943681L);\n        System.out.println(imageDao.selectImagesWithTopic(imageIds));\n\n    }\n}\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/dao/StarDaoTest.java",
    "content": "package com.acimage.community.dao;\n\n\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n\n\n@SpringBootTest\npublic class StarDaoTest {\n    @Autowired\n    StarDao starDao;\n\n\n    @Test\n    public void countStarsByUserId(){\n\n        long userId=1572443275490078720L;\n        System.out.println(starDao.countStarsOwnedBy(userId));\n\n\n\n    }\n\n    @Test\n    public void countSelectStarsWithTopic(){\n\n        long userId=1572443275490078720L;\n        System.out.println(starDao.selectStarsWithTopicOrderByCreateTime(userId,1,5));\n\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/dao/TopicDaoTest.java",
    "content": "package com.acimage.community.dao;\n\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.LambdaUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.List;\n\n@SpringBootTest\npublic class TopicDaoTest {\n    @Autowired\n    TopicDao topicDao;\n\n\n    @Test\n    public void selectTopicWithUserImagesComments() {\n        long id = 1572508721685839872L;\n    }\n\n    @Test\n    public void selectTopicsOrderByScan() {\n        String startTime = \"2022-09-23 00:00:00\";\n        List<Topic> topics = topicDao.selectTopicsWithUserOrderByPageView(startTime, null);\n        System.out.println(topics);\n    }\n\n    @Test\n    public void getTopicCount() {\n        long userId = 1572443275490078720L;\n    }\n\n    @Test\n    public void testSelectTopicCount() {\n        long userId = 0;\n\n        System.out.println(topicDao.countTopics(userId));\n    }\n\n    @Test\n    public void testSelectTopicsWithUserOrderBy() {\n        String column = LambdaUtils.underlineColumnNameOf(Topic::getPageView);\n        List<Topic> topicList = topicDao.selectTopicsWithUserOrderBy(column, 100);\n        System.out.println(topicList);\n        System.out.println(topicList.size());\n\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/service/CommentWriteServiceTest.java",
    "content": "package com.acimage.community.service;\n\nimport com.acimage.common.model.domain.community.Comment;\nimport com.acimage.community.service.comment.CommentInfoQueryService;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.List;\n\n@SpringBootTest\npublic class CommentWriteServiceTest {\n    @Autowired\n    CommentInfoQueryService commentIInfoService;\n\n}\n\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/service/TopicEsSearchServiceTest.java",
    "content": "package com.acimage.community.service;\n\nimport com.acimage.community.service.topic.TopicEsSearchService;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\npublic class TopicEsSearchServiceTest {\n    @Autowired\n    TopicEsSearchService topicEsSearchService;\n\n}\n\n"
  },
  {
    "path": "acimage_community/src/test/java/com/acimage/community/utils/RedisTest.java",
    "content": "package com.acimage.community.utils;\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.acimage.community.dao.TopicDao;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.data.redis.core.ZSetOperations;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.TimeUnit;\n\n@SpringBootTest\npublic class RedisTest {\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    private static final ObjectMapper mapper=new ObjectMapper();\n\n    @Autowired\n    TopicDao topicDao;\n\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Test\n    void testStringRedisTemplate(){\n        stringRedisTemplate.opsForValue().set(\"test:Name\",\"xxxyyy\");\n        System.out.println(stringRedisTemplate.opsForValue().get(\"testName\"));\n    }\n\n    @Test\n    void testOpsForList(){\n        final String KEY=\"recentHotTopics\";\n        String startTime=\"2022-09-23 00:00:00\";\n        List<Topic> topics = topicDao.selectTopicsWithUserOrderByPageView(startTime,null);\n        System.out.println(stringRedisTemplate.opsForList());\n    }\n\n    @Test\n    void testOpsForValue(){\n        final String KEY=\"recentHotTopics\";\n        String startTime=\"2022-09-23 00:00:00\";\n        List<Topic> topics = topicDao.selectTopicsWithUserOrderByPageView(startTime,null);\n        String jsonTopics=null;\n        try {\n            jsonTopics=mapper.writeValueAsString(topics);\n            System.out.println(jsonTopics);\n        } catch (JsonProcessingException e) {\n            throw new RuntimeException(e);\n        }\n\n        try {\n            List<Topic> topic2 =mapper.readValue(jsonTopics,new TypeReference<List<Topic>>() { });\n            System.out.println(topic2);\n        } catch (JsonProcessingException e) {\n            throw new RuntimeException(e);\n        }\n\n//        stringRedisTemplate.opsForValue().set(KEY,topics.toString())\n//        System.out.println(stringRedisTemplate.opsForList());\n    }\n\n    @Test\n    void testRedisUtilsForList(){\n        final String KEY=\"recentHotTopics\";\n        String startTime=\"2022-09-23 00:00:00\";\n        List<Topic> topics = topicDao.selectTopicsWithUserOrderByPageView(startTime,null);\n\n//        try {\n//            RedisUtils.setListAsString(stringRedisTemplate,KEY,topics,5, TimeUnit.SECONDS);\n//        } catch (JsonProcessingException e) {\n//            throw new RuntimeException(e);\n//        }\n        try {\n            List<Topic> topics1=redisUtils.getListFromString(KEY, Topic.class);\n            System.out.println(topics1);\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            throw new RuntimeException();\n        }\n    }\n\n    @Test\n    void testRedisUtilsForObject(){\n        final String KEY=\"topics\";\n        String startTime=\"2022-09-23 00:00:00\";\n        long id=1574286298175799296L;\n        Topic topic = topicDao.selectById(id);\n\n        redisUtils.setObjectJson(KEY, topic,50, TimeUnit.SECONDS);\n\n\n        Topic topic1 =redisUtils.getObjectFromString(KEY, Topic.class);\n        System.out.println(topic1);\n\n    }\n\n    @Test\n    void testZSet(){\n        final String KEY=\"test-list\";\n        //往ordered set设置值\n        stringRedisTemplate.opsForZSet().incrementScore(KEY,\"value1\",1);\n        stringRedisTemplate.opsForZSet().incrementScore(KEY,\"value2\",5);\n        stringRedisTemplate.opsForZSet().incrementScore(KEY,\"value3\",7);\n\n        //查找\n        Set<ZSetOperations.TypedTuple<String>> set= stringRedisTemplate.opsForZSet().rangeWithScores(KEY,0L,9999999999L);\n        Iterator<ZSetOperations.TypedTuple<String>> iterator = set.iterator();\n        while(iterator.hasNext()){\n            ZSetOperations.TypedTuple<String> typedTuple=iterator.next();\n            System.out.println(typedTuple.getScore().longValue());\n            System.out.println(typedTuple.getValue());\n        }\n        String[] t=new String[]{\"value1\"};\n        //删除\n        stringRedisTemplate.opsForZSet().remove(KEY,t);\n        Double d=stringRedisTemplate.opsForZSet().score(KEY,\"value4\");\n        System.out.println(d);\n\n    }\n\n    @Test\n    void testAddForZSet(){\n        final String KEY=\"test:zset\";\n        stringRedisTemplate.opsForZSet().add(KEY,\"v1\",10);\n        stringRedisTemplate.opsForZSet().add(KEY,\"v1\",20);\n\n    }\n\n    @Test\n    void testRedisTransaction(){\n        stringRedisTemplate.opsForValue().set(\"X\",\"Y\");\n        stringRedisTemplate.setEnableTransactionSupport(true);\n        stringRedisTemplate.multi();\n        stringRedisTemplate.exec();\n        stringRedisTemplate.setEnableTransactionSupport(false);\n        System.out.println(redisUtils.delete(\"X\"));\n    }\n\n    @Test\n    void testHyperLogLog(){\n        String key=\"HyperLogLog\";\n        long add=redisUtils.addForHyperLogLog(key,\"v1\",\"v2\",\"v3\");\n        Long size=redisUtils.sizeForHyperLogLog(key+\"ddd\");\n        System.out.println(\"add:\"+add);\n        System.out.println(\"size:\"+size);\n        redisUtils.deleteForHyperLogLog(\"hhhh\");\n    }\n\n    @Test\n    void testSet(){\n        System.out.println(redisUtils.sizeForHyperLogLog(\"logKey\"));\n\n    }\n\n    @Test\n    void testExpire(){\n        String key=\"test:expire\";\n        long timeout=100;\n        stringRedisTemplate.opsForValue().set(key,\"hahah\");\n        boolean flag=redisUtils.expire(key,timeout,TimeUnit.SECONDS);\n\n        System.out.println(flag);\n    }\n\n    @Test\n    void testHash(){\n        stringRedisTemplate.opsForHash().increment(\"test:hash-key\",\"star\",10);\n        System.out.println(stringRedisTemplate.opsForHash().get(\"test:hash-key\",\"star\"));\n    }\n\n    @Test\n    void testLuaScriptIncrementIfPresent(){\n        String key=\"test:lua1\";\n        redisUtils.setAsString(key,\"0\");\n        System.out.println(redisUtils.incrementIfPresent(\"test:lua1\",10086L));\n    }\n}\n"
  },
  {
    "path": "acimage_feign/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>acimage</artifactId>\n        <groupId>com.acimage</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>acimage_feign</artifactId>\n    <packaging>jar</packaging>\n\n    <name>acimage_feign</name>\n    <url>http://maven.apache.org</url>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_common</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n            <optional>true</optional>\n        </dependency>\n\n        <!--feign-->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-loadbalancer</artifactId>\n        </dependency>\n\n        <!--httpclient-->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-httpclient</artifactId>\n        </dependency>\n\n        <!--feign文件上传所需依赖-->\n        <dependency>\n            <groupId>io.github.openfeign.form</groupId>\n            <artifactId>feign-form</artifactId>\n            <version>3.8.0</version>\n        </dependency>\n\n        <dependency>\n            <groupId>io.github.openfeign.form</groupId>\n            <artifactId>feign-form-spring</artifactId>\n            <version>3.8.0</version>\n        </dependency>\n\n        <!-- 解决编译时，报程序包javax.servlet不存在的错误 -->\n        <dependency>\n            <groupId>javax.servlet</groupId>\n            <artifactId>javax.servlet-api</artifactId>\n            <!-- 只在编译和测试的时候用 -->\n            <scope>provided</scope>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <classifier>exec</classifier>\n                    <skip>true</skip><!--不需要main类打包-->\n                </configuration>\n            </plugin>\n\n            <!-- maven打包 跳过测试包 -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <skipTests>true</skipTests><!--跳过测试包-->\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/FeignMain.java",
    "content": "package com.acimage.feign;\n\n/**\n * Hello world!\n *\n */\npublic class FeignMain\n{\n    public static void main( String[] args ) {\n\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/client/CmtyUserClient.java",
    "content": "package com.acimage.feign.client;\n\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.result.Result;\nimport com.acimage.feign.fallback.CmtyUserClientFallbackFactory;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\n\n@FeignClient(value=\"community-service/community/cmtyUsers\",fallbackFactory = CmtyUserClientFallbackFactory.class)\npublic interface CmtyUserClient {\n\n    @GetMapping(\"/userId/{userId}\")\n    Result<CmtyUser> queryCmtyUser(@PathVariable Long userId);\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/client/CommentClient.java",
    "content": "package com.acimage.feign.client;\n\n\nimport com.acimage.common.result.Result;\nimport com.acimage.feign.fallback.CommentClientFallbackFactory;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\n\n\nimport java.util.List;\n\n@FeignClient(value=\"community-service/community/comments\",fallbackFactory = CommentClientFallbackFactory.class)\npublic interface CommentClient {\n\n    @DeleteMapping(\"/{id}\")\n    public Result<?> delete(@PathVariable(\"id\") Long id) ;\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/client/TopicClient.java",
    "content": "package com.acimage.feign.client;\n\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.result.Result;\nimport com.acimage.feign.fallback.TopicClientFallbackFactory;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.List;\n\n@FeignClient(value=\"community-service/community/topics\",fallbackFactory = TopicClientFallbackFactory.class)\npublic interface TopicClient {\n\n    @GetMapping(\"/ids\")\n    Result<List<Topic>> queryTopics(@RequestParam(\"topicIds\") List<Long> topicIds);\n\n    @DeleteMapping(\"/{topicId}\")\n    public Result<?> delete(@PathVariable(\"topicId\") Long topicId) ;\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/client/UserClient.java",
    "content": "package com.acimage.feign.client;\n\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.result.Result;\nimport com.acimage.feign.fallback.UserClientFallbackFactory;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.*;\n\n\n@FeignClient(value=\"user-service/user/users\",fallbackFactory = UserClientFallbackFactory.class)\npublic interface UserClient {\n    @GetMapping(\"/id/{id}\")\n    Result<User> queryUser(@PathVariable Long id);\n\n    @PutMapping(\"/photoUrl\")\n    Result<String> modifyPhotoUrl(@RequestBody String photoUrl);\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/config/FallbackFactoryBean.java",
    "content": "package com.acimage.feign.config;\n\nimport com.acimage.feign.fallback.ImageClientFallbackFactory;\nimport com.acimage.feign.fallback.UserClientFallbackFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n\n\npublic class FallbackFactoryBean {\n\n    @Bean\n    public ImageClientFallbackFactory imageClientFallbackFactory(){\n        return new ImageClientFallbackFactory();\n    }\n\n    @Bean\n    public UserClientFallbackFactory userClientFallbackFactory(){\n        return new UserClientFallbackFactory();\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/config/FeignMultipartSupportConfig.java",
    "content": "package com.acimage.feign.config;\n\nimport feign.codec.Encoder;\nimport feign.form.spring.SpringFormEncoder;\nimport org.springframework.beans.factory.ObjectFactory;\nimport org.springframework.boot.autoconfigure.http.HttpMessageConverters;\nimport org.springframework.cloud.openfeign.support.SpringEncoder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class FeignMultipartSupportConfig {\n\n    @Bean\n    public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {\n        return new SpringFormEncoder(new SpringEncoder(messageConverters));\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/config/FeignRequestInterceptorConfig.java",
    "content": "package com.acimage.feign.config;\n\n\nimport com.acimage.common.global.consts.HeaderKeyConstants;\nimport com.acimage.common.global.context.UserContext;\nimport feign.RequestInterceptor;\nimport feign.RequestTemplate;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Enumeration;\n\n@Slf4j\n@Configuration\npublic class FeignRequestInterceptorConfig implements RequestInterceptor {\n\n    @Override\n    public void apply(RequestTemplate requestTemplate) {\n        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();\n        if(attrs==null){\n            log.error(\"feign请求为空\");\n            return;\n        }\n        HttpServletRequest request = attrs.getRequest();\n        Enumeration<String> attributeNames = request.getHeaderNames();\n        //设置header\n        if (attributeNames != null) {\n            while (attributeNames.hasMoreElements()) {\n                String name = attributeNames.nextElement();\n                String value = request.getHeader(name);\n                requestTemplate.header(name,value);\n//                String KEY_COOKIE=\"cookie\";\n//                if(KEY_COOKIE.equals(name)){\n//                    requestTemplate.header(name,value);\n//                    return;\n//                }\n            }\n        }\n        //往header设置用户原始ip\n        requestTemplate.header(HeaderKeyConstants.FEIGN_X_USER_IP, UserContext.getIp());\n\n\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/depreted/FileClient.java",
    "content": "package com.acimage.feign.depreted;\n\nimport com.acimage.common.result.Result;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.util.List;\n\n@FeignClient(value=\"image-service/api/image/hashImages\")\npublic interface FileClient {\n    @GetMapping(\"sayHello\")\n    String sayHello();\n\n    @DeleteMapping\n    Result deleteImageHashes(@RequestParam(\"imageIds\") List<Long> imageIds);\n\n    /**\n     * 废弃原因：图片不再存储在本地，存储在七牛云\n     * @param imageFiles\n     * @param imageIds\n     * @return\n     */\n    @Deprecated\n    @PostMapping(value =\"/imageFiles\",produces = {MediaType.APPLICATION_JSON_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE)\n    Result storeImageFiles(@RequestPart(\"imageFiles\") MultipartFile[] imageFiles, @RequestParam(\"imageIds\") List<Long> imageIds);\n\n    @PostMapping(value = \"/uploadPhoto\",produces = {MediaType.APPLICATION_JSON_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE)\n    Result storePhotoFiles(@RequestPart(\"photoFile\") MultipartFile photoFile);\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/depreted/ImageClient.java",
    "content": "package com.acimage.feign.depreted;\n\n\n\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.feign.fallback.ImageClientFallbackFactory;\nimport com.acimage.common.result.Result;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.List;\n\n@FeignClient(value=\"image-service/image/images\",fallbackFactory = ImageClientFallbackFactory.class)\npublic interface ImageClient {\n    @GetMapping(\"/imageIds\")\n    Result queryImagesWithTopic(@RequestParam(\"imageIds\") List<Long> imageIds);\n\n    @GetMapping(\"/topicId/{topicId}\")\n    Result<List<Image>> queryTopicImages(@PathVariable(\"topicId\") Long topicId) ;\n\n\n    @GetMapping(\"/preparedTopicImages\")\n    Result<String> updateTopicIdAndReturnFirstImageUrl(@RequestParam(\"serviceToken\") String serviceToken,\n                                                              @RequestParam(\"topicId\") Long topicId);\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/fallback/CmtyUserClientFallbackFactory.java",
    "content": "package com.acimage.feign.fallback;\n\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.feign.client.CmtyUserClient;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.openfeign.FallbackFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Slf4j\npublic class CmtyUserClientFallbackFactory implements FallbackFactory<CmtyUserClient> {\n    @Override\n    public CmtyUserClient create(Throwable cause) {\n        return new CmtyUserClient() {\n            @Override\n            public Result<CmtyUser> queryCmtyUser(Long userId) {\n                ExceptionUtils.printIfDev(cause);\n                log.error(\"查询UserCommunityStatistic失败，userId:{}\", userId);\n                return Result.ok(new CmtyUser());\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/fallback/CommentClientFallbackFactory.java",
    "content": "package com.acimage.feign.fallback;\n\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.feign.client.CommentClient;\nimport com.acimage.feign.client.TopicClient;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.openfeign.FallbackFactory;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\n@Component\npublic class CommentClientFallbackFactory implements FallbackFactory<CommentClient> {\n    @Override\n    public CommentClient create(Throwable cause) {\n        return new CommentClient() {\n            @Override\n            public Result<?> delete(Long id) {\n                log.error(\"删除评论失败 id:{} error:{}\", id, cause.getMessage());\n                return Result.fail(\"删除评论失败\");\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/fallback/ImageClientFallbackFactory.java",
    "content": "package com.acimage.feign.fallback;\n\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.common.utils.SpringContextUtils;\nimport com.acimage.feign.depreted.ImageClient;\nimport com.acimage.common.result.Result;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.openfeign.FallbackFactory;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n@Component\n@Slf4j\npublic class ImageClientFallbackFactory implements FallbackFactory<ImageClient> {\n\n    @Override\n    public ImageClient create(Throwable cause) {\n        return new ImageClient() {\n            @Override\n            public Result queryImagesWithTopic(List<Long> imageIds) {\n                if(SpringContextUtils.isDev()){\n                    ExceptionUtils.printIfDev(cause);\n                }\n\n                log.error(\"feign 查询失败，imageIds:{}\",imageIds);\n                return Result.fail(\"服务繁忙，查询失败\");\n            }\n\n            @Override\n            public Result<List<Image>> queryTopicImages(Long topicId) {\n                ExceptionUtils.printIfDev(cause);\n                log.error(\"feign 查询话题图片失败，topicId:{}\",topicId);\n                return Result.ok(new ArrayList<>());\n            }\n\n            @Override\n            public Result<String> updateTopicIdAndReturnFirstImageUrl(String serviceToken, Long topicId) {\n                ExceptionUtils.printIfDev(cause);\n                log.error(\"feign topicId提交到image-service失败，topicId:{}\",topicId);\n                return Result.fail(\"服务繁忙，查询失败\");\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/fallback/TopicClientFallbackFactory.java",
    "content": "package com.acimage.feign.fallback;\n\n\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.feign.client.TopicClient;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.openfeign.FallbackFactory;\nimport org.springframework.stereotype.Component;\n\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Slf4j\n@Component\npublic class TopicClientFallbackFactory implements FallbackFactory<TopicClient> {\n    @Override\n    public TopicClient create(Throwable cause) {\n        return new TopicClient() {\n            @Override\n            public Result<List<Topic>> queryTopics(List<Long> topicIds) {\n                ExceptionUtils.printIfDev(cause);\n                log.error(\"查询多个话题失败 topicIds:{} error:{}\", topicIds,cause.getMessage());\n                return Result.ok(new ArrayList<>());\n            }\n\n            @Override\n            public Result<?> delete(Long topicId) {\n                log.error(\"删除话题失败 id:{} error:{}\",topicId,cause.getMessage());\n                return Result.fail(\"删除话题失败\");\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "acimage_feign/src/main/java/com/acimage/feign/fallback/UserClientFallbackFactory.java",
    "content": "package com.acimage.feign.fallback;\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.feign.client.UserClient;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.openfeign.FallbackFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Slf4j\npublic class UserClientFallbackFactory implements FallbackFactory<UserClient> {\n    @Override\n    public UserClient create(Throwable cause) {\n        return new UserClient() {\n            @Override\n            public Result<User> queryUser(Long id) {\n                ExceptionUtils.printIfDev(cause);\n                log.error(\"查询失败，用户id:{}\", id);\n                return Result.ok(new User());\n            }\n\n            @Override\n            public Result<String> modifyPhotoUrl(String photoUrl) {\n                ExceptionUtils.printIfDev(cause);\n                log.error(\"修改photoUrl失败，用户id:{}\", UserContext.getUsername());\n                return Result.fail(\"头像修改失败\");\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>acimage</artifactId>\n        <groupId>com.acimage</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>acimage_gateway</artifactId>\n    <packaging>jar</packaging>\n\n    <name>acimage_gateway</name>\n    <url>http://maven.apache.org</url>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_common</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-starter-web</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!--druid依赖-->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid</artifactId>\n        </dependency>\n\n        <!--mysql依赖-->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n\n\n        <!-- spring cloud gateway 依赖 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n        <!-- Feign Client for loadBalancing -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-loadbalancer</artifactId>\n        </dependency>\n\n\n        <dependency>\n            <groupId>com.github.ben-manes.caffeine</groupId>\n            <artifactId>caffeine</artifactId>\n        </dependency>\n\n\n\n    </dependencies>\n\n    <build>\n        <plugins>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <skipTests>true</skipTests><!--跳过测试包-->\n                </configuration>\n            </plugin>\n        </plugins>\n\n    </build>\n</project>\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/GatewayApplication.java",
    "content": "package com.acimage.gateway;\n\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.ComponentScans;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@EnableDiscoveryClient\n@SpringBootApplication\n@MapperScan(\"com.acimage.gateway.dao\")\n@ComponentScan(value = {\"com.acimage.gateway\",\"com.acimage.common.utils\"})\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n        log.info(\"------------->>>Gateway启动<<<-------------\");\n    }\n\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/apitree/ApiTree.java",
    "content": "package com.acimage.gateway.apitree;\n\nimport com.acimage.common.global.enums.MyHttpMethod;\nimport com.acimage.common.model.domain.sys.Api;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.springframework.http.HttpMethod;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentMap;\n\n@NoArgsConstructor\n@Data\npublic class ApiTree {\n    /**\n     * 路径id\n//     */\n//    private List<Integer> ids=new ArrayList<>();\n//    /**\n//     * 权限id\n//     */\n//    private List<Integer> permissionIds=new ArrayList<>();\n//    private List<MyHttpMethod> methods=new ArrayList<>();\n    private List<Api> apiList=new ArrayList<>();\n    private ConcurrentMap<String, ApiTree> children;\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/apitree/ApiTreeFactory.java",
    "content": "package com.acimage.gateway.apitree;\n\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Slf4j\n@Data\npublic class ApiTreeFactory {\n    private ApiTree prefixMatchApiTree;\n    private ApiTree exactMatchApiTree;\n    private ApiTree apiTree;\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/apitree/ApiTreeUtils.java",
    "content": "package com.acimage.gateway.apitree;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.global.enums.MyHttpMethod;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.gateway.global.consts.NotationConstants;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.HttpMethod;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n@Slf4j\npublic class ApiTreeUtils {\n\n    public static Api getMatchApi(ApiTree apiTree, String path, HttpMethod method) {\n        if (apiTree == null) {\n            return null;\n        }\n        if (path.startsWith(\"/\")) {\n            if (path.length() == 1) {\n                return null;\n            }\n            path = path.substring(1);\n\n        }\n        List<String> splits = Arrays.asList(path.split(\"/\"));\n        if (CollectionUtil.isEmpty(splits)) {\n            return null;\n        }\n        return recursiveMatch(apiTree, splits, 0, method);\n\n    }\n\n    private static Api recursiveMatch(ApiTree apiTree, List<String> splits, int beginIndex, HttpMethod method) {\n        if (beginIndex == splits.size() || apiTree == null||apiTree.getChildren()==null) {\n            return null;\n        }\n\n        for (int i = beginIndex; i < splits.size(); i++) {\n            String s = splits.get(i);\n            ConcurrentMap<String, ApiTree> children = apiTree.getChildren();\n            //待匹配字符串\n            List<String> matchStrings = Arrays.asList(s, NotationConstants.ASTERISK, NotationConstants.DOUBLE_ASTERISK);\n            for (String matchString : matchStrings) {\n                ApiTree child = children.get(matchString);\n                //刚好匹配到根节点或匹配到**\n                if (child != null && (i == splits.size() - 1 || matchString.equals(NotationConstants.DOUBLE_ASTERISK))) {\n                    int matchIndex = -1;\n                    List<Api> apiList = child.getApiList();\n                    //找到匹配的api\n                    for (int j = 0; j < apiList.size(); j++) {\n                        if (apiList.get(j).getMethod().equals(MyHttpMethod.from(method))) {\n                            matchIndex = j;\n                            break;\n                        } else if (apiList.get(j).getMethod().equals(MyHttpMethod.ALL)) {\n                            matchIndex = j;\n                        }\n                    }\n                    //匹配到则返回\n                    if (matchIndex != -1) {\n                        return apiList.get(matchIndex);\n                    }\n\n                    //再看看下个结点有没有**的子节点\n                    if (i == splits.size() - 1 && child.getChildren() != null) {\n                        for (String str : Arrays.asList(NotationConstants.ASTERISK, NotationConstants.DOUBLE_ASTERISK)) {\n                            ApiTree tempChild=child.getChildren().get(str);\n                            if(tempChild==null){\n                                continue;\n                            }\n                            matchIndex = -1;\n                            apiList =tempChild.getApiList();\n                            //找到匹配的api\n                            for (int j = 0; j < apiList.size(); j++) {\n                                if (apiList.get(j).getMethod().equals(MyHttpMethod.from(method))) {\n                                    matchIndex = j;\n                                    break;\n                                } else if (apiList.get(j).getMethod().equals(MyHttpMethod.ALL)) {\n                                    matchIndex = j;\n                                }\n                            }\n                            //匹配到则返回\n                            if (matchIndex != -1) {\n                                return apiList.get(matchIndex);\n                            }\n                        }\n\n                    }\n\n                } else if (child != null) {\n                    //递归匹配\n                    Api result = recursiveMatch(child, splits, i + 1, method);\n                    if (result != null) {\n                        return result;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    public static ApiTree buildApiTreeFrom(List<Api> apiList) {\n        //按/分割路径\n        //统一去掉斜杆开头\n        for (Api api : apiList) {\n            String path = api.getPath();\n            if (path.startsWith(\"/\")) {\n                api.setPath(path.substring(1));\n            }\n        }\n        apiList.sort(ApiTreeUtils::comparator);\n\n        List<List<String>> splits = new ArrayList<>();\n        for (Api api : apiList) {\n            splits.add(Arrays.asList(api.getPath().split(\"/\")));\n        }\n        ApiTree apiTree = new ApiTree();\n\n        for (int i = 0; i < splits.size(); i++) {\n            ApiTree head = apiTree;\n            List<String> pathSplits = splits.get(i);\n            for (int j = 0; j < pathSplits.size(); j++) {\n                if (head.getChildren() == null) {\n                    head.setChildren(new ConcurrentHashMap<>());\n                }\n                ConcurrentMap<String, ApiTree> children = head.getChildren();\n                ApiTree child = children.get(pathSplits.get(j));\n                if (child == null) {\n                    children.put(pathSplits.get(j), new ApiTree());\n                }\n\n                child = children.get(pathSplits.get(j));\n\n                if (j == pathSplits.size() - 1) {\n                    child.getApiList().add(apiList.get(i));\n                }\n\n                head = child;\n            }\n        }\n\n        return apiTree;\n    }\n\n    private static int comparator(Api next, Api cur) {\n        List<String> nextList = Arrays.asList(next.getPath().split(\"/\"));\n        List<String> curList = Arrays.asList(cur.getPath().split(\"/\"));\n        for (int i = 0; i < nextList.size() && i < curList.size(); i++) {\n            String sNext = nextList.get(i);\n            String sCur = curList.get(i);\n\n            if (sNext.compareTo(sCur) == 0) {\n                continue;\n            } else if (orderOf(sNext) != orderOf(sCur)) {\n                return orderOf(sNext) - orderOf(sCur);\n            }\n            {\n                return sNext.compareTo(sCur);\n            }\n        }\n        return curList.size() - nextList.size();\n    }\n\n    private static int orderOf(String s) {\n        if (NotationConstants.DOUBLE_ASTERISK.equals(s)) {\n            return 2;\n        }\n        if (NotationConstants.ASTERISK.equals(s)) {\n            return 1;\n        }\n        return 0;\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/apitree/InitApiTreeApplicationRunner.java",
    "content": "package com.acimage.gateway.apitree;\n\n\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.gateway.serivce.ApiQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\npublic class InitApiTreeApplicationRunner implements ApplicationRunner {\n\n    @Autowired\n    ApiQueryService apiQueryService;\n    @Autowired\n    ApiTreeFactory apiTreeFactory;\n\n\n    @Override\n    public void run(ApplicationArguments args) {\n        log.info(\"开始初始化apiTree\");\n        ApiTree apiTree=ApiTreeUtils.buildApiTreeFrom(apiQueryService.listEnableApis());\n        apiTreeFactory.setApiTree(apiTree);\n        log.info(\"初始化api tree完成\");\n\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/config/KeySolverConfig.java",
    "content": "package com.acimage.gateway.config;\n\n\nimport com.acimage.common.utils.IpUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport reactor.core.publisher.Mono;\n\n@Slf4j\n@Configuration\npublic class KeySolverConfig {\n\n    @Bean\n    public KeyResolver ipKeyResolver() {\n        return exchange ->{\n            ServerHttpRequest request=exchange.getRequest();\n           return  Mono.just(IpUtils.getUserIp(request));\n        };\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/config/RoleConfig.java",
    "content": "package com.acimage.gateway.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@Data\n@ConfigurationProperties(prefix = \"role\")\npublic class RoleConfig {\n    private int visitorId;\n    private int userId;\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/dao/ApiDao.java",
    "content": "package com.acimage.gateway.dao;\n\nimport com.acimage.common.model.domain.sys.Api;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface ApiDao extends BaseMapper<Api> {\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/dao/AuthorizeDao.java",
    "content": "package com.acimage.gateway.dao;\n\nimport com.acimage.common.model.domain.sys.Authorize;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface AuthorizeDao extends BaseMapper<Authorize> {\n\n\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/dao/PermissionDao.java",
    "content": "package com.acimage.gateway.dao;\n\nimport com.acimage.common.model.domain.sys.Permission;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\npublic interface PermissionDao extends BaseMapper<Permission> {\n\n    List<Permission> selectTreeByParentId(@Nullable @Param(\"parentId\") Integer parentId);\n\n    List<Permission> selectPermissionsWithParent(@Param(\"startIndex\") int startIndex,@Param(\"recordNumber\") int recordNumber);\n\n\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/dao/RoleDao.java",
    "content": "package com.acimage.gateway.dao;\n\nimport com.acimage.common.model.domain.sys.Role;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface RoleDao extends BaseMapper<Role> {\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/dao/UserRoleDao.java",
    "content": "package com.acimage.gateway.dao;\n\n\nimport com.acimage.common.model.domain.sys.UserRole;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UserRoleDao extends BaseMapper<UserRole> {\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/global/consts/NotationConstants.java",
    "content": "package com.acimage.gateway.global.consts;\n\npublic class NotationConstants {\n    public static final String ASTERISK=\"*\";\n    public static final String DOUBLE_ASTERISK=\"**\";\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/globalfilter/AuthenticationFilter.java",
    "content": "package com.acimage.gateway.globalfilter;\n\n\n\nimport com.acimage.common.global.consts.SysKeyConstants;\nimport com.acimage.common.global.exception.NullTokenException;\nimport com.acimage.common.global.consts.HeaderKeyConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.service.impl.TokenServiceImpl;\nimport com.acimage.common.utils.IpUtils;\nimport com.acimage.common.utils.JwtUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.auth0.jwt.exceptions.TokenExpiredException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.core.annotation.Order;\n\n\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\n@Slf4j\n@Order(20)\n@Component\npublic class AuthenticationFilter implements GlobalFilter {\n\n\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n\n        ServerHttpRequest request = exchange.getRequest();\n        String url = request.getURI().getPath();\n\n        String token = request.getHeaders().getFirst(HeaderKeyConstants.AUTHORIZATION);\n        String ip = IpUtils.getUserIp(request);\n        UserContext.setIp(ip);\n        //记录接口访问次数\n        redisUtils.increment(SysKeyConstants.STRINGK_INTERFACE_TOTAL,1);\n        //记录访问量\n        redisUtils.addForHyperLogLog(SysKeyConstants.LOGK_PAGE_VIEW,ip);\n\n        boolean isException = false;\n        //验证token\n        try {\n            JwtUtils.verifyToken(token);\n        } catch (NullTokenException e) {\n            log.debug(\"access 无token 访问:{} ip:{}\", url, ip);\n            isException = true;\n\n        } catch (TokenExpiredException e) {\n            log.info(\"access token过期 用户:{}  访问:{} ip:{}\",\n                    JwtUtils.getUsername(token), url, ip);\n            isException = true;\n\n        } catch (JWTVerificationException e1) {\n            log.warn(\"access 非法token 访问:{} ip:{} token:{}\", url, ip,token);\n            isException = true;\n        }\n\n        if (!isException) {\n            if (!this.hasRecorded(token)) {\n                log.info(\"access token未被记录 用户:{} 访问:{} ip:{}\",\n                        JwtUtils.getUsername(token), url, ip);\n                return chain.filter(exchange);\n            }\n\n            String method = request.getMethodValue();\n            log.debug(\"access 用户:{} 访问:{} {} ip:{}\",\n                    JwtUtils.getUsername(token), url, method, ip);\n\n            UserContext.setUserId(JwtUtils.getUserId(token));\n            UserContext.setUsername(JwtUtils.getUsername(token));\n            UserContext.setIp(ip);\n        }\n\n\n        return chain.filter(exchange);\n\n    }\n\n    private boolean hasRecorded(String token) {\n        return redisUtils.getForString(TokenServiceImpl.STRINGKP_TOKEN+token)!=null;\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/globalfilter/CustomWebsocketRoutingFilter.java",
    "content": "package com.acimage.gateway.globalfilter;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;\nimport org.springframework.cloud.gateway.support.ServerWebExchangeUtils;\nimport org.springframework.core.Ordered;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.socket.WebSocketHandler;\nimport org.springframework.web.reactive.socket.WebSocketMessage;\nimport org.springframework.web.reactive.socket.WebSocketSession;\nimport org.springframework.web.reactive.socket.client.WebSocketClient;\nimport org.springframework.web.reactive.socket.server.WebSocketService;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport reactor.core.publisher.Mono;\n\nimport java.net.URI;\nimport java.util.*;\n\n/**\n * 解决websocket关闭异常 问题\n * @author admin\n * @Desc websocket客户端主动断开连接,网关服务报错1005\n * @date 2022/8/24 14:30\n */\n@Component\npublic class CustomWebsocketRoutingFilter implements GlobalFilter, Ordered {\n\n    public static final String SEC_WEBSOCKET_PROTOCOL = \"Sec-WebSocket-Protocol\";\n    private static final Log log = LogFactory.getLog(CustomWebsocketRoutingFilter.class);\n    private final WebSocketClient webSocketClient;\n    private final WebSocketService webSocketService;\n    private final ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider;\n    private volatile List<HttpHeadersFilter> headersFilters;\n\n    public CustomWebsocketRoutingFilter(WebSocketClient webSocketClient, WebSocketService webSocketService, ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider) {\n        this.webSocketClient = webSocketClient;\n        this.webSocketService = webSocketService;\n        this.headersFiltersProvider = headersFiltersProvider;\n    }\n\n    static String convertHttpToWs(String scheme) {\n        scheme = scheme.toLowerCase();\n        return \"http\".equals(scheme) ? \"ws\" : (\"https\".equals(scheme) ? \"wss\" : scheme);\n    }\n\n    @Override\n    public int getOrder() {\n        return 2147483645;\n    }\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        changeSchemeIfIsWebSocketUpgrade(exchange);\n        URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);\n        //log.debug(requestUrl);\n        String scheme = requestUrl.getScheme();\n        if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && (\"ws\".equals(scheme) || \"wss\".equals(scheme))) {\n            ServerWebExchangeUtils.setAlreadyRouted(exchange);\n            HttpHeaders headers = exchange.getRequest().getHeaders();\n            HttpHeaders filtered = HttpHeadersFilter.filterRequest(this.getHeadersFilters(), exchange);\n            List<String> protocols = this.getProtocols(headers);\n            return this.webSocketService.handleRequest(exchange, new CustomWebsocketRoutingFilter.ProxyWebSocketHandler(requestUrl, this.webSocketClient, filtered, protocols));\n        } else {\n            return chain.filter(exchange);\n        }\n    }\n\n    List<String> getProtocols(HttpHeaders headers) {\n        List<String> protocols = headers.get(\"Sec-WebSocket-Protocol\");\n        if (protocols != null) {\n            ArrayList<String> updatedProtocols = new ArrayList();\n\n            for(int i = 0; i < ((List)protocols).size(); ++i) {\n                String protocol = (String)((List)protocols).get(i);\n                updatedProtocols.addAll(Arrays.asList(StringUtils.tokenizeToStringArray(protocol, \",\")));\n            }\n\n            protocols = updatedProtocols;\n        }\n\n        return (List)protocols;\n    }\n\n    List<HttpHeadersFilter> getHeadersFilters() {\n        if (this.headersFilters == null) {\n            this.headersFilters = (List)this.headersFiltersProvider.getIfAvailable(ArrayList::new);\n            this.headersFilters.add((headers, exchange) -> {\n                HttpHeaders filtered = new HttpHeaders();\n                filtered.addAll(headers);\n                filtered.remove(\"Host\");\n                boolean preserveHost = (Boolean)exchange.getAttributeOrDefault(ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE, false);\n                if (preserveHost) {\n                    String host = exchange.getRequest().getHeaders().getFirst(\"Host\");\n                    filtered.add(\"Host\", host);\n                }\n\n                return filtered;\n            });\n            this.headersFilters.add((headers, exchange) -> {\n                HttpHeaders filtered = new HttpHeaders();\n                Iterator var3 = headers.entrySet().iterator();\n\n                while(var3.hasNext()) {\n                    Map.Entry<String, List<String>> entry = (Map.Entry)var3.next();\n                    if (!((String)entry.getKey()).toLowerCase().startsWith(\"sec-websocket\")) {\n                        filtered.addAll((String)entry.getKey(), (List)entry.getValue());\n                    }\n                }\n\n                return filtered;\n            });\n        }\n\n        return this.headersFilters;\n    }\n\n    static void changeSchemeIfIsWebSocketUpgrade(ServerWebExchange exchange) {\n        URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);\n        String scheme = requestUrl.getScheme().toLowerCase();\n        String upgrade = exchange.getRequest().getHeaders().getUpgrade();\n        if (\"WebSocket\".equalsIgnoreCase(upgrade) && (\"http\".equals(scheme) || \"https\".equals(scheme))) {\n            String wsScheme = convertHttpToWs(scheme);\n            boolean encoded = ServerWebExchangeUtils.containsEncodedParts(requestUrl);\n            URI wsRequestUrl = UriComponentsBuilder.fromUri(requestUrl).scheme(wsScheme).build(encoded).toUri();\n            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, wsRequestUrl);\n            if (log.isTraceEnabled()) {\n                log.trace(\"changeSchemeTo:[\" + wsRequestUrl + \"]\");\n            }\n        }\n\n    }\n\n    private static class ProxyWebSocketHandler implements WebSocketHandler {\n        private final WebSocketClient client;\n        private final URI url;\n        private final HttpHeaders headers;\n        private final List<String> subProtocols;\n\n        ProxyWebSocketHandler(URI url, WebSocketClient client, HttpHeaders headers, List<String> protocols) {\n            this.client = client;\n            this.url = url;\n            this.headers = headers;\n            if (protocols != null) {\n                this.subProtocols = protocols;\n            } else {\n                this.subProtocols = Collections.emptyList();\n            }\n\n        }\n\n        @Override\n        public List<String> getSubProtocols() {\n            return this.subProtocols;\n        }\n\n        @Override\n        public Mono<Void> handle(WebSocketSession session) {\n            return this.client.execute(this.url, this.headers, new WebSocketHandler() {\n                @Override\n                public Mono<Void> handle(WebSocketSession proxySession) {\n                    Mono<Void> serverClose = proxySession.closeStatus().filter(__ -> session.isOpen())\n                            .flatMap(session::close);\n                    Mono<Void> proxyClose = session.closeStatus().filter(__ -> proxySession.isOpen())\n                            .flatMap(proxySession::close);\n                    // Use retain() for Reactor Netty\n                    Mono<Void> proxySessionSend = proxySession\n                            .send(session.receive().doOnNext(WebSocketMessage::retain));\n                    Mono<Void> serverSessionSend = session\n                            .send(proxySession.receive().doOnNext(WebSocketMessage::retain));\n                    return Mono.zip(proxySessionSend, serverSessionSend, serverClose, proxyClose).then();\n                }\n\n                @Override\n                public List<String> getSubProtocols() {\n                    return CustomWebsocketRoutingFilter.ProxyWebSocketHandler.this.subProtocols;\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/globalfilter/PermissionFilter.java",
    "content": "package com.acimage.gateway.globalfilter;\n\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.gateway.apitree.ApiTreeFactory;\nimport com.acimage.gateway.apitree.ApiTreeUtils;\nimport com.acimage.gateway.config.RoleConfig;\nimport com.acimage.gateway.serivce.AuthorizeQueryService;\nimport com.acimage.gateway.serivce.RoleQueryService;\nimport com.acimage.gateway.serivce.UserRoleQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Slf4j\n@Order(30)\n@Component\npublic class PermissionFilter implements GlobalFilter {\n    @Autowired\n    private ApiTreeFactory apiTreeFactory;\n    @Autowired\n    private AuthorizeQueryService authorizeQueryService;\n    @Autowired\n    private UserRoleQueryService userRoleQueryService;\n    @Autowired\n    private RoleConfig roleConfig;\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        ServerHttpRequest request = exchange.getRequest();\n        String url = request.getURI().getPath();\n        HttpMethod httpMethod = request.getMethod();\n        if (httpMethod != HttpMethod.GET) {\n            log.info(\"{} {} ip:{} user:{}\", url, httpMethod, UserContext.getIp(), UserContext.getUsername());\n        }\n        //获取匹配的api树\n        Api api = ApiTreeUtils.getMatchApi(apiTreeFactory.getApiTree(), url, httpMethod);\n\n        //api不存在\n        if (api == null) {\n            log.info(url + \" 不存在\");\n            UserContext.remove();\n            return exchange.getResponse().setComplete();\n        }\n        Map<Integer, List<Integer>> map = authorizeQueryService.getRolePermissionIdsMap();\n\n        //获取访客权限\n        List<Integer> permissionIds = map.get(roleConfig.getVisitorId());\n        if (permissionIds != null && permissionIds.contains(api.getPermissionId())) {\n            log.debug(api.getPath() + api.getMethod() + \"通过\");\n            UserContext.remove();\n            return chain.filter(exchange);\n        }\n\n        //获取用户权限\n        if (UserContext.getUserId() != null) {\n            permissionIds = map.get(roleConfig.getUserId());\n            if (permissionIds != null && permissionIds.contains(api.getPermissionId())) {\n                log.debug(api.getPath() + api.getMethod() + \"通过\");\n                UserContext.remove();\n                return chain.filter(exchange);\n            }\n        }\n\n        if (UserContext.getUserId() != null) {\n            //获取用户具体角色的权限\n            List<Integer> roleIds = userRoleQueryService.listRoleIds(UserContext.getUserId());\n            for (Integer roleId : roleIds) {\n                permissionIds = map.get(roleId);\n                if (permissionIds != null && permissionIds.contains(api.getPermissionId())) {\n                    log.debug(api.getPath() + api.getMethod() + \"通过\");\n                    UserContext.remove();\n                    return chain.filter(exchange);\n                }\n            }\n        }\n\n        log.info(\"{} {} 权限不足 ip:{}\", api.getPath(), api.getMethod(), UserContext.getIp());\n        UserContext.remove();\n        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\n        return exchange.getResponse().setComplete();\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/globalfilter/RemoveContextFilter.java",
    "content": "package com.acimage.gateway.globalfilter;\n\n\nimport com.acimage.common.global.context.UserContext;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\n@Slf4j\n@Order(Integer.MAX_VALUE)\npublic class RemoveContextFilter implements GlobalFilter {\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        UserContext.remove();\n        return chain.filter(exchange);\n\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/globalfilter/RequestLimitFilter.java",
    "content": "package com.acimage.gateway.globalfilter;\n\n\nimport com.acimage.common.utils.redis.RedisUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.cloud.gateway.support.ServerWebExchangeUtils;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\nimport java.net.URI;\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Order(5)\n@Component\npublic class RequestLimitFilter implements GlobalFilter {\n    @Autowired\n    private RedisUtils redisUtils;\n\n    public static final int TOTAL_LIMIT = 1000;\n    public static final String STRINGK_TOTAL_LIMIT = \"acimage:gateway:limit:total\";\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n\n        int increment = 1;\n        long timeoutSeconds = 1L;\n        Long count = redisUtils.increment(STRINGK_TOTAL_LIMIT, increment);\n        if (count == 1) {\n            redisUtils.expire(STRINGK_TOTAL_LIMIT, timeoutSeconds, TimeUnit.SECONDS);\n        } else if (count >= TOTAL_LIMIT) {\n            exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);\n            return exchange.getResponse().setComplete();\n        }\n        return chain.filter(exchange);\n\n\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/schedule/RefreshApiTreeSchedule.java",
    "content": "package com.acimage.gateway.schedule;\n\nimport com.acimage.gateway.apitree.ApiTree;\nimport com.acimage.gateway.apitree.ApiTreeFactory;\nimport com.acimage.gateway.apitree.ApiTreeUtils;\nimport com.acimage.gateway.serivce.ApiQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.annotation.EnableScheduling;\nimport org.springframework.scheduling.annotation.Scheduled;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\n@EnableScheduling\npublic class RefreshApiTreeSchedule {\n\n    @Autowired\n    private ApiQueryService apiQueryService;\n    @Autowired\n    private ApiTreeFactory apiTreeFactory;\n    public static final int FIX_RATE=10*1000;\n\n    /**\n     * 10分钟刷新一次api tree\n     */\n    @Scheduled(cron =\"0 */10 * * * ?\")\n    public void refreshApiTree(){\n        log.info(\"开始刷新api tree\");\n        ApiTree apiTree= ApiTreeUtils.buildApiTreeFrom(apiQueryService.listEnableApis());\n        apiTreeFactory.setApiTree(apiTree);\n        log.info(\"刷新api tree完成\");\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/ApiQueryService.java",
    "content": "package com.acimage.gateway.serivce;\n\nimport com.acimage.common.model.domain.sys.Api;\n\nimport java.util.List;\n\npublic interface ApiQueryService {\n    List<Api> listEnableApis();\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/AuthorizeQueryService.java",
    "content": "package com.acimage.gateway.serivce;\n\nimport com.acimage.common.model.domain.sys.Authorize;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface AuthorizeQueryService {\n\n\n    Map<Integer, List<Integer>> getRolePermissionIdsMap();\n\n    List<Integer> listPermissionIds(Integer roleId);\n\n    List<Authorize> listAll();\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/RoleQueryService.java",
    "content": "package com.acimage.gateway.serivce;\n\nimport java.util.List;\n\npublic interface RoleQueryService {\n    List<Integer> listAllIds();\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/UserRoleQueryService.java",
    "content": "package com.acimage.gateway.serivce;\n\nimport java.util.List;\n\npublic interface UserRoleQueryService {\n    List<Integer> listRoleIds(long userId);\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/impl/ApiQueryQueryServiceImpl.java",
    "content": "package com.acimage.gateway.serivce.impl;\n\nimport com.acimage.common.global.enums.MatchRule;\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.gateway.dao.ApiDao;\nimport com.acimage.gateway.serivce.ApiQueryService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n@Service\npublic class ApiQueryQueryServiceImpl implements ApiQueryService {\n    @Autowired\n    ApiDao apiDao;\n\n    @Override\n    public List<Api> listEnableApis() {\n        LambdaQueryWrapper<Api> qw = new LambdaQueryWrapper<>();\n        qw.eq(Api::isEnable, true);\n        return apiDao.selectList(qw);\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/impl/AuthorizeQueryServiceImpl.java",
    "content": "package com.acimage.gateway.serivce.impl;\n\nimport com.acimage.common.model.domain.sys.Authorize;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.gateway.dao.AuthorizeDao;\nimport com.acimage.gateway.serivce.AuthorizeQueryService;\nimport com.acimage.gateway.serivce.RoleQueryService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.annotation.Scheduled;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class AuthorizeQueryServiceImpl implements AuthorizeQueryService {\n    @Autowired\n    AuthorizeDao authorizeDao;\n\n    private Map<Integer, List<Integer>> rolePermissionIdsMap;\n    @Override\n    public Map<Integer, List<Integer>> getRolePermissionIdsMap() {\n        return rolePermissionIdsMap;\n    }\n\n    @Override\n    public List<Integer> listPermissionIds(Integer roleId) {\n        LambdaQueryWrapper<Authorize> qw = new LambdaQueryWrapper<>();\n        qw.eq(Authorize::getRoleId, roleId)\n                .select(Authorize::getPermissionId);\n        return ListUtils.extract(Authorize::getPermissionId, authorizeDao.selectList(qw));\n    }\n\n    @Override\n    public List<Authorize> listAll() {\n        return authorizeDao.selectList(null);\n    }\n\n    @Scheduled(fixedRate = 5L, timeUnit = TimeUnit.MINUTES)\n    private Map<Integer, List<Integer>> refreshRoleIdToPermissionIdsMap() {\n        //为了方便直接保存在内存\n        Map<Integer, List<Integer>> map = new HashMap<>();\n        List<Authorize> authorizeList = authorizeDao.selectList(null);\n        for (Authorize authorize : authorizeList) {\n            Integer roleId = authorize.getRoleId();\n            Integer permissionId = authorize.getPermissionId();\n            List<Integer> permissionIds = map.get(roleId);\n            if (permissionIds == null) {\n                List<Integer> tempList = new ArrayList<>();\n                tempList.add(permissionId);\n                map.put(roleId, tempList);\n            } else {\n                permissionIds.add(permissionId);\n            }\n        }\n        this.rolePermissionIdsMap = map;\n        return map;\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/impl/RoleQueryServiceImpl.java",
    "content": "package com.acimage.gateway.serivce.impl;\n\nimport com.acimage.common.model.domain.sys.Role;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.gateway.dao.RoleDao;\nimport com.acimage.gateway.serivce.RoleQueryService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class RoleQueryServiceImpl implements RoleQueryService {\n    @Autowired\n    RoleDao roleDao;\n\n    @Override\n    public List<Integer> listAllIds(){\n        LambdaQueryWrapper<Role> qw=new LambdaQueryWrapper<>();\n        qw.select(Role::getId);\n        List<Role> roles= roleDao.selectList(qw);\n        return ListUtils.extract(Role::getId,roles);\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/java/com/acimage/gateway/serivce/impl/UserRoleQueryQueryServiceImpl.java",
    "content": "package com.acimage.gateway.serivce.impl;\n\nimport com.acimage.common.model.domain.sys.UserRole;\nimport com.acimage.common.redis.annotation.QueryRedis;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.gateway.dao.UserRoleDao;\nimport com.acimage.gateway.serivce.UserRoleQueryService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class UserRoleQueryQueryServiceImpl implements UserRoleQueryService {\n    @Autowired\n    UserRoleDao userRoleDao;\n\n    @QueryRedis(keyPrefix = \"acimage:gateway:permissionIds:userId:\",expire = 5L)\n    @Override\n    public List<Integer> listRoleIds(long userId){\n        LambdaQueryWrapper<UserRole> qw=new LambdaQueryWrapper<>();\n        qw.select(UserRole::getRoleId)\n                .eq(UserRole::getUserId,userId);\n        return ListUtils.extract(UserRole::getRoleId,userRoleDao.selectList(qw));\n    }\n}\n"
  },
  {
    "path": "acimage_gateway/src/main/resources/application-dev.yml",
    "content": "server:\n  port: 8070\n\nspring:\n  config:\n    activate:\n      on-profile:\n        - dev\n  redis:\n    host: 192.168.130.128\n    port: 6379\n    password: redis\n    lettuce:\n      pool:\n        max-active: 8\n        max-idle: 8 #最大空闲连接\n        min-idle: 0 #最小空闲连接\n        max-wait: 100ms #连接等待时间\n  cloud:\n    nacos:\n      server-addr: localhost:8848 #nacos 服务地址\n      discovery:\n        enabled: true #关闭nacos发现，用于调试\n\n"
  },
  {
    "path": "acimage_gateway/src/main/resources/application.yml",
    "content": "spring:\n  profiles:\n    include: common,common-secret\n    active: dev2\n\n  application:\n    name: gateway-server\n  datasource:\n    type: com.alibaba.druid.pool.DruidDataSource\n    driver-class-name: com.mysql.cj.jdbc.Driver\n  cloud:\n    gateway:\n      discovery:\n        locator: false\n      default-filters:\n        - name: RequestRateLimiter #reids版本太低会无法生效\n          args:\n            redis-rate-limiter.replenishRate: 20 #补充速率\n            redis-rate-limiter.burstCapacity: 30  #桶容量\n            key-resolver: '#{@ipKeyResolver}' #这个必须要配置，否则返回403\n      routes:\n        - id: community-service-route\n          uri: lb://community-service\n          predicates:\n            - Path=/api/community/**\n\n        - id: image-service-route\n          uri: lb://image-service\n          predicates:\n            - Path=/api/image/**\n\n        - id: user-service-route\n          uri: lb://user-service\n          predicates:\n            - Path=/api/user/**\n\n        - id: websocket-route\n          uri: lb:ws://user-service\n          predicates:\n            - Path=/websocket\n\n        - id: admin-service-route\n          uri: lb://admin-service\n          predicates:\n            - Path=/api/admin/**\n\n\nmybatis-plus:\n  mapper-locations: classpath:mapper/*.xml\n  type-aliases-package: com.acimage.common.model.domain\n  type-handlers-package: com.acimage.common.config.typehandler\n  global-config:\n    db-config:\n      table-prefix: tb_\n\nrole:\n  visitorId: 2\n  userId: 1\n\n\n"
  },
  {
    "path": "acimage_gateway/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- 配置文件修改时重新加载，默认true -->\n<configuration scan=\"true\">\n\n    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->\n    <springProperty scope=\"context\" name=\"spring.application.name\" source=\"spring.application.name\"/>\n    <springProperty scope=\"context\" name=\"LOG_HOME\" source=\"logback.base\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%20.20thread{20}] %40logger{40} : %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"info\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>INFO</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-info.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>8</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"warn\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>WARN</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-warn.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"error\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>ERROR</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-error.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n\n    <springProfile name=\"dev2\">\n        <logger name=\"com.acimage.gateway.dao\" level=\"INFO\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <logger name=\"com.acimage\" level=\"DEBUG\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n    <springProfile name=\"prod,server\">\n        <logger name=\"com.acimage\" level=\"info\" additivity=\"false\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n\n</configuration>"
  },
  {
    "path": "acimage_gateway/src/test/java/com/acimage/gateway/GatewayApplicationTest.java",
    "content": "package com.acimage.gateway;\n\n\nimport com.acimage.gateway.apitree.ApiTree;\nimport com.acimage.gateway.apitree.ApiTreeUtils;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.HttpMethod;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n\n@SpringBootTest\npublic class GatewayApplicationTest\n{\n\n\n}\n"
  },
  {
    "path": "acimage_gateway/src/test/java/com/acimage/gateway/apitree/ApiTreeTest.java",
    "content": "package com.acimage.gateway.apitree;\n\nimport com.acimage.common.model.domain.sys.Api;\nimport com.acimage.gateway.serivce.ApiQueryService;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.HttpMethod;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n@SpringBootTest\npublic class ApiTreeTest {\n\n    @Autowired\n    ApiQueryService apiQueryService;\n\n    @Test\n    public void testApiTree() {\n        ApiTree apiTree = ApiTreeUtils.buildApiTreeFrom(apiQueryService.listEnableApis());\n        List<String> paths = Arrays.asList(\"/api/community/topics/operate\",\n                \"/api/community/topics/query\",\n                \"/api/topics/community/query\",\n                \"/api/community/topics/jkhakhsd\",\n                \"/api/community/topics/xxx\");\n        for (String path : paths) {\n            System.out.println(path + \": \" + ApiTreeUtils.getMatchApi(apiTree, path, HttpMethod.GET));\n        }\n    }\n\n    @Test\n    public void testApiTree2() {\n        ApiTree apiTree = ApiTreeUtils.buildApiTreeFrom(apiQueryService.listEnableApis());\n        List<String> paths = Arrays.asList(\n                \"/api/community/topics/operate\");\n\n        for (String path : paths) {\n            Api api = ApiTreeUtils.getMatchApi(apiTree, path, HttpMethod.GET);\n            System.out.println(api);\n        }\n\n    }\n}\n"
  },
  {
    "path": "acimage_image/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>acimage</artifactId>\n        <groupId>com.acimage</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>acimage_image</artifactId>\n    <packaging>jar</packaging>\n\n    <name>acimage_image</name>\n    <url>http://maven.apache.org</url>\n\n    <!--    <properties>-->\n    <!--        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->\n    <!--        <maven.compiler.source>18</maven.compiler.source>-->\n    <!--        <maven.compiler.target>18</maven.compiler.target>-->\n    <!--    </properties>-->\n\n    <dependencies>\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_common</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_feign</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!--druid依赖-->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid</artifactId>\n        </dependency>\n\n        <!--mysql依赖-->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n\n        <!--图像处理库-->\n        <dependency>\n            <groupId>net.coobird</groupId>\n            <artifactId>thumbnailator</artifactId>\n        </dependency>\n\n        <!--rabbitmq-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!--minio-->\n        <dependency>\n            <groupId>io.minio</groupId>\n            <artifactId>minio</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.ben-manes.caffeine</groupId>\n            <artifactId>caffeine</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.nintha</groupId>\n            <artifactId>webp-imageio-core</artifactId>\n            <version>0.1.0</version>\n            <scope>system</scope>\n            <systemPath>${pom.basedir}/lib/webp-imageio-core-0.1.0.jar</systemPath>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <includeSystemScope>true</includeSystemScope>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <skipTests>true</skipTests><!--跳过测试包-->\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/ImageApplication.java",
    "content": "package com.acimage.image;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n@Slf4j\n@SpringBootApplication\n@EnableScheduling\n@EnableDiscoveryClient\n@EnableFeignClients(basePackages=\"com.acimage.feign\")\n@MapperScan(\"com.acimage.image.dao\")\n@ComponentScan(value={\"com.acimage\"})\npublic class ImageApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ImageApplication.class, args);\n\t\tlog.info(\"------------->>>Image启动<<<-------------\");\n\t}\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/dao/ImageDao.java",
    "content": "package com.acimage.image.dao;\n\nimport cn.hutool.core.lang.Pair;\nimport com.acimage.common.model.domain.image.Image;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\npublic interface ImageDao extends BaseMapper<Image> {\n\n    Integer insertList(List<Image> images);\n\n    Integer updateDescription(List<Pair<Long,String>> idAndDescriptions);\n\n    @Select(\"select * from tb_image where topic_id=#{topicId} order by id\")\n    List<Image> selectListOrderById(@Param(\"topicId\") long topicId);\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/dao/ImageHashDao.java",
    "content": "package com.acimage.image.dao;\n\nimport com.acimage.common.model.domain.image.ImageHash;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface ImageHashDao extends BaseMapper<ImageHash> {\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/global/consts/MyFileConstants.java",
    "content": "package com.acimage.image.global.consts;\n\npublic class MyFileConstants {\n    public static final String IMAGE_FORMAT=\"jpeg\";\n    public static final String ZIP_FORMAT=\"zip\";\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/global/consts/TopicImageKeyConstants.java",
    "content": "package com.acimage.image.global.consts;\n\npublic class TopicImageKeyConstants {\n    public static final String STRINGKP_TOPIC_IMAGES=\"acimage:image:images:topicId:\";\n    public static final String STRINGKP_IMAGE=\"acimage:image:images:imageId:\";\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/global/context/DirectoryContext.java",
    "content": "package com.acimage.image.global.context;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\n\n\npublic class DirectoryContext {\n    @Value(\"${my-config.images-directory}\")\n    public String imagesDirectory;\n    @Value(\"${my-config.photos-directory}\")\n    public String photosDirectory;\n\n    public static String IMAGES_DIRECTORY;\n\n    public static String PHOTOS_DIRECTORY;\n\n    @PostConstruct\n    void init(){\n        IMAGES_DIRECTORY= imagesDirectory;\n        PHOTOS_DIRECTORY= photosDirectory;\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/mq/consumer/SyncImagesConsumer.java",
    "content": "package com.acimage.image.mq.consumer;\n\n\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.mq.dto.SyncImagesUpdateDto;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.common.utils.SpringContextUtils;\nimport com.acimage.image.service.image.ImageMixWriteService;\nimport com.rabbitmq.client.Channel;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\n\n\nimport javax.annotation.PostConstruct;\nimport java.io.*;\n\n\n@Slf4j\n@Configuration\n@RabbitListener(queues = MqConstants.SYNC_IMAGES_QUEUE)\npublic class SyncImagesConsumer {\n    @Autowired\n    ImageMixWriteService imageMixWriteService;\n\n    @RabbitHandler\n    public void syncImages(Channel channel, Message message, SyncImagesUpdateDto updateDto) {\n        log.info(\"开始同步话题图片\");\n        try {\n            imageMixWriteService.updateImageAndHash(updateDto);\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"同步图片消费者任务失败 error:{} data:{}\", e.getLocalizedMessage(), updateDto);\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"同步图片ack失败 error:{} message:{} data:{}\", e.getMessage(), messageBody, updateDto);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(e);\n                    log.error(\"同步图片reject失败 error:{} message:{} data:{}\", e.getMessage(), messageBody, updateDto);\n                }\n            }\n        }\n\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/config/DhashTaskPoolConfig.java",
    "content": "package com.acimage.image.service.config;\n\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.annotation.EnableAsync;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\nimport java.util.concurrent.Executor;\n\n@Deprecated\n@EnableAsync\npublic class DhashTaskPoolConfig {\n        public static final String DHASH_TASK_POOL=\"dhashTask\";\n\n        @Bean(name = DHASH_TASK_POOL)\n        public Executor imageDhashExecutor() {\n            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n            //核心线程池大小\n            executor.setCorePoolSize(4);\n            //最大线程数\n            executor.setMaxPoolSize(10);\n            //队列容量\n            executor.setQueueCapacity(100);\n            //活跃时间\n            executor.setKeepAliveSeconds(60);\n            //线程名字前缀\n            executor.setThreadNamePrefix(\"dhash-task\");\n            // 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean\n            executor.setWaitForTasksToCompleteOnShutdown(true);\n//            // 线程池对拒绝任务的处理策略,当线程池没有处理能力的时候，该策略会直接在 execute 方法的调用线程中运行被拒绝的任务；如果执行程序已关闭，则会丢弃该任务\n//            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());\n            executor.initialize();\n            return executor;\n        }\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/image/ImageMixWriteService.java",
    "content": "package com.acimage.image.service.image;\n\nimport com.acimage.common.model.mq.dto.SyncImagesUpdateDto;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.util.List;\n\npublic interface ImageMixWriteService {\n\n\n    String saveImage(MultipartFile imageFile);\n\n    void removeTopicPartialImages(long topicId, List<String> imageUrls);\n\n    void removeTopicImages(long topicId);\n\n    void updateImageAndHash(SyncImagesUpdateDto updateDto);\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/image/ImageQueryService.java",
    "content": "package com.acimage.image.service.image;\n\nimport com.acimage.common.model.domain.image.Image;\n\nimport java.util.List;\n\npublic interface ImageQueryService {\n    List<Image> listImagesOrderById(long topicId);\n\n    List<Image> listImagesByIds(List<Long> imageIds);\n\n    List<Long> listImageIds(long topicId, List<String> imageUrls);\n\n    List<Long> listImageIds(long topicId);\n\n    List<Image> listImagesForHavingNullTopicId(List<String> imageUrls);\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/image/ImageWriteService.java",
    "content": "package com.acimage.image.service.image;\n\n\nimport com.acimage.common.model.domain.image.Image;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.util.List;\n\n\npublic interface ImageWriteService extends IService<Image> {\n\n    @Deprecated\n    void saveImages(List<Image> images);\n    Image saveImage(String url,int size,String fileName);\n    void removeImages(long topicId);\n\n    int removeImages(long topicId, List<String> imageUrls);\n\n    void updateTopicId(List<Long> imageIds,long topicId);\n\n    void updateTopicIdForHavingNullTopicId(List<Long> imageIds, long topicId);\n\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/image/impl/ImageMixWriteServiceImpl.java",
    "content": "package com.acimage.image.service.image.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.util.IdUtil;\nimport com.acimage.common.global.consts.StorePrefixConstants;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.global.consts.FileFormatConstants;\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.model.mq.dto.SyncImagesUpdateDto;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.common.utils.ImageUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.common.utils.minio.MinioUtils;\nimport com.acimage.image.service.image.ImageMixWriteService;\nimport com.acimage.image.service.image.ImageQueryService;\nimport com.acimage.image.service.image.ImageWriteService;\nimport com.acimage.image.service.imagehash.ImageHashWriteService;\nimport com.acimage.image.service.imagehash.SearchImageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.annotation.PostConstruct;\nimport java.io.*;\nimport java.util.Date;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class ImageMixWriteServiceImpl implements ImageMixWriteService {\n\n    public String tempDirectory;\n\n    @PostConstruct\n    public void init() {\n        tempDirectory = System.getProperty(\"user.dir\") + \"//temp\";\n        //创建目录\n        File directory = new File(tempDirectory);\n        if (directory.mkdir()) {\n            log.info(\"创建临时目录：{}\", tempDirectory);\n        }\n    }\n\n    @Autowired\n    ImageWriteService imageWriteService;\n    @Autowired\n    ImageQueryService imageQueryService;\n    @Autowired\n    MinioUtils minioUtils;\n    @Autowired\n    ImageHashWriteService imageHashWriteService;\n    @Autowired\n    SearchImageService searchImageService;\n\n    @Override\n    public String saveImage(MultipartFile imageFile) {\n        long imageId = IdGenerator.getSnowflakeNextId();\n        String suffix = String.format(\"%s.%s\", imageId, FileFormatConstants.WEBP);\n        String url = minioUtils.generateBaseUrl(StorePrefixConstants.TOPIC_IMAGE, new Date(), suffix);\n        //压缩为webp,压缩后不超过200kb\n        int limitSize = 200 * 1000;\n        int limitLength=1000;\n        int size;\n        String totalUrl;\n        try (InputStream inputStream = ImageUtils.compressAsWebpImage(imageFile, limitSize,limitLength)) {\n            size = inputStream.available();\n            //上传\n            totalUrl = minioUtils.upload(inputStream, url, FileFormatConstants.WEBP_CONTENT_TYPE);\n        }catch (IOException e) {\n            log.error(e.getMessage());\n            throw new BusinessException(\"文件上传失败\");\n        }\n\n        //保存到数据库\n        String fileName = imageFile.getOriginalFilename();\n        imageWriteService.saveImage(totalUrl, size, fileName);\n        return totalUrl;\n    }\n\n\n    @Override\n    public void removeTopicPartialImages(long topicId, List<String> imageUrls) {\n        if (CollectionUtil.isEmpty(imageUrls)) {\n            return;\n        }\n        //找到要删除的图片id\n        List<Long> imageIds = imageQueryService.listImageIds(topicId, imageUrls);\n        //删除图片\n        imageWriteService.removeImages(topicId, imageUrls);\n        //删除图片哈希\n        imageHashWriteService.removeImageHashes(imageIds);\n    }\n\n    @Override\n    public void removeTopicImages(long topicId) {\n        //找到要删除的图片id\n        List<Long> imageIds = imageQueryService.listImageIds(topicId);\n        //删除图片\n        imageWriteService.removeImages(topicId);\n        //删除图片哈希\n        imageHashWriteService.removeImageHashes(imageIds);\n    }\n\n\n    @Override\n    public void updateImageAndHash(SyncImagesUpdateDto updateDto) {\n        long topicId = updateDto.getTopicId();\n        switch (updateDto.getServiceType()) {\n            case ADD:\n            case UPDATE:\n                log.info(\"开始哈希图片 {}\", updateDto);\n                List<String> addImageUrlList = updateDto.getAddImageUrls();\n                //获取实际存在的图片\n                List<Image> images = imageQueryService.listImagesForHavingNullTopicId(addImageUrlList);\n                for (Image image : images) {\n                    //下载图片到本地\n                    String tempFilePath = String.format(\"%s/%s\", tempDirectory, image.getId() + IdUtil.fastSimpleUUID());\n                    try {\n                        minioUtils.downloadTo(image.getUrl(), tempFilePath);\n                    } catch (Exception e) {\n                        log.error(\"哈希图片时下载出错 error:{} url:{}\", e.getLocalizedMessage(), image.getUrl());\n                        continue;\n                    }\n                    File tempFile = new File(tempFilePath);\n                    FileInputStream is = null;\n                    try {\n                        is = new FileInputStream(tempFilePath);\n                        //将图片哈希并存到数据库\n                        searchImageService.hashImageByDhashAlgorithm(is, image.getId());\n                    } catch (FileNotFoundException e) {\n                        log.error(\"系统错误 文件不存在{}\", tempFilePath);\n                        return;\n                    } finally {\n                        if (is != null) {\n                            try {\n                                is.close();\n                            } catch (IOException e) {\n                                log.error(\"inputStream 关闭失败 {}\", e.getMessage());\n                                throw new RuntimeException(e);\n                            }\n                        }\n                    }\n                    tempFile.delete();\n                }\n                //更新图片对应话题id\n                List<Long> imageIds = ListUtils.extract(Image::getId, images);\n                imageWriteService.updateTopicIdForHavingNullTopicId(imageIds, topicId);\n\n                //删除图片\n                List<String> removeImageUrlList = updateDto.getRemoveImageUrls();\n                this.removeTopicPartialImages(topicId, removeImageUrlList);\n                break;\n\n            case DELETE:\n                this.removeTopicImages(topicId);\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/image/impl/ImageQueryServiceImpl.java",
    "content": "package com.acimage.image.service.image.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.redis.annotation.QueryRedis;\n\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.image.dao.ImageDao;\nimport com.acimage.image.service.image.ImageQueryService;\nimport com.acimage.image.global.consts.TopicImageKeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Service\npublic class ImageQueryServiceImpl implements ImageQueryService {\n    @Autowired\n    ImageDao imageDao;\n\n    @QueryRedis(keyPrefix = TopicImageKeyConstants.STRINGKP_TOPIC_IMAGES, expire = 3L)\n    @Override\n    public List<Image> listImagesOrderById(long topicId) {\n        LambdaQueryWrapper<Image> qw = new LambdaQueryWrapper<>();\n        qw.select(Image::getUrl,Image::getSize,Image::getId,Image::getTopicId)\n                .orderByAsc(Image::getId)\n                .eq(Image::getTopicId, topicId);\n        return imageDao.selectList(qw);\n    }\n\n    @Override\n    public List<Image> listImagesByIds(List<Long> imageIds) {\n        if(CollectionUtil.isEmpty(imageIds)){\n            return new ArrayList<>();\n        }\n\n        LambdaQueryWrapper<Image> qw = new LambdaQueryWrapper<>();\n        qw.select(Image::getUrl,Image::getSize,Image::getId,Image::getTopicId)\n                .in(Image::getId, imageIds);\n        List<Image> images = imageDao.selectList(qw);\n\n        //按照给出的imageIds顺序排好\n        List<Image> orderedImages = new ArrayList<>();\n        for (Long imageId : imageIds) {\n            for (Image image : images) {\n                if (imageId.equals(image.getId())) {\n                    orderedImages.add(image);\n                }\n            }\n        }\n\n        return orderedImages;\n    }\n\n    @Override\n    public List<Long> listImageIds(long topicId, List<String> imageUrls) {\n        if(CollectionUtil.isEmpty(imageUrls)){\n            return new ArrayList<>();\n        }\n\n        LambdaQueryWrapper<Image> qw = new LambdaQueryWrapper<>();\n        qw.in(Image::getUrl, imageUrls)\n                .eq(Image::getTopicId,topicId)\n                .select(Image::getId);\n        List<Image> images = imageDao.selectList(qw);\n\n        return ListUtils.extract(Image::getId,images);\n    }\n\n    @Override\n    public List<Long> listImageIds(long topicId) {\n        LambdaQueryWrapper<Image> qw = new LambdaQueryWrapper<>();\n        qw.eq(Image::getTopicId,topicId)\n                .select(Image::getId);\n        List<Image> images = imageDao.selectList(qw);\n\n        return ListUtils.extract(Image::getId,images);\n    }\n\n    @Override\n    public List<Image> listImagesForHavingNullTopicId(List<String> imageUrls){\n        if(CollectionUtil.isEmpty(imageUrls)){\n            return new ArrayList<>();\n        }\n\n        LambdaQueryWrapper<Image> qw = new LambdaQueryWrapper<>();\n        qw.in(Image::getUrl, imageUrls)\n                .isNull(Image::getTopicId);\n        List<Image> images = imageDao.selectList(qw);\n\n        return images;\n    }\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/image/impl/ImageWriteServiceImpl.java",
    "content": "package com.acimage.image.service.image.impl;\n\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.Pair;\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.common.deprecated.QiniuUtils;\nimport com.acimage.common.utils.minio.MinioUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.common.utils.common.FileUtils;\nimport com.acimage.common.utils.common.PairUtils;\nimport com.acimage.image.dao.ImageDao;\nimport com.acimage.image.service.image.ImageQueryService;\nimport com.acimage.image.service.image.ImageWriteService;\nimport com.acimage.image.global.consts.TopicImageKeyConstants;\nimport com.acimage.image.service.imagehash.SearchImageService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\nimport reactor.util.annotation.Nullable;\n\nimport javax.validation.constraints.NotNull;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n@Slf4j\n@Service\npublic class ImageWriteServiceImpl extends ServiceImpl<ImageDao, Image> implements ImageWriteService {\n\n\n    @Autowired\n    ImageDao imageDao;\n    @Autowired\n    ImageQueryService imageQueryService;\n    @Autowired(required = false)\n    QiniuUtils qiniuUtils;\n    @Autowired\n    RedisUtils redisUtils;\n\n\n    @Override\n    public void saveImages(@Nullable List<Image> images) {\n        if (images == null || images.size() == 0) {\n            return;\n        }\n        imageDao.insertList(images);\n    }\n\n    @Override\n    public Image saveImage(String url, int size, String fileName) {\n        long imageId = IdGenerator.getSnowflakeNextId();\n        Date now = new Date();\n        Image image = Image.builder()\n                .url(url)\n                .id(imageId)\n                .size(size)\n                .fileName(fileName)\n                .description(fileName)\n                .createTime(now)\n                .updateTime(now)\n                .build();\n        imageDao.insert(image);\n        return image;\n    }\n\n    @Override\n    public void removeImages(long topicId) {\n        LambdaQueryWrapper<Image> qw = new LambdaQueryWrapper<>();\n        qw.eq(Image::getTopicId, topicId);\n        imageDao.delete(qw);\n        //删除话题图片\n        redisUtils.delete(TopicImageKeyConstants.STRINGKP_TOPIC_IMAGES);\n    }\n\n    @Override\n    public int removeImages(long topicId, List<String> imageUrls) {\n        LambdaQueryWrapper<Image> qw = new LambdaQueryWrapper<>();\n        qw.in(Image::getUrl, imageUrls)\n                .eq(Image::getTopicId, topicId);\n        return imageDao.delete(qw);\n    }\n\n    @Override\n    public void updateTopicId(List<Long> imageIds, long topicId) {\n        if (CollectionUtil.isEmpty(imageIds)) {\n            return;\n        }\n        LambdaUpdateWrapper<Image> uw = new LambdaUpdateWrapper<>();\n        uw.in(Image::getId, imageIds)\n                .set(Image::getTopicId, topicId);\n        imageDao.update(null, uw);\n    }\n\n    @Override\n    public void updateTopicIdForHavingNullTopicId(List<Long> imageIds, long topicId) {\n        if (CollectionUtil.isEmpty(imageIds)) {\n            return;\n        }\n        LambdaUpdateWrapper<Image> uw = new LambdaUpdateWrapper<>();\n        uw.in(Image::getId, imageIds)\n                .isNull(Image::getTopicId)\n                .set(Image::getTopicId, topicId);\n        imageDao.update(null, uw);\n    }\n\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/imagehash/ImageHashWriteService.java",
    "content": "package com.acimage.image.service.imagehash;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic interface ImageHashWriteService {\n\n    void removeImageHashes(List<Long> imageIds);\n\n    void HashImagesByDhash(InputStream imageInputStream, long imageId) ;\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/imagehash/SearchImageService.java",
    "content": "package com.acimage.image.service.imagehash;\n\nimport com.acimage.common.model.domain.image.Image;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic interface SearchImageService {\n    @Deprecated\n    void processImagesHashForNotProcessedImages();\n\n    void hashImageByDhashAlgorithm(InputStream imageInputStream, long imageId) ;\n\n    List<Image> searchMostSimilarImages(MultipartFile imageFile);\n\n    void removeImageHashes(List<Long> imageIds);\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/imagehash/impl/ImageHashWriteServiceImpl.java",
    "content": "package com.acimage.image.service.imagehash.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.image.dao.ImageHashDao;\nimport com.acimage.common.model.domain.image.ImageHash;\nimport com.acimage.image.service.imagehash.ImageHashWriteService;\nimport com.acimage.image.utils.BitUtils;\nimport com.acimage.image.utils.DhashUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.stereotype.Service;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\n@Service\n@Slf4j\npublic class ImageHashWriteServiceImpl implements ImageHashWriteService {\n    @Autowired\n    ImageHashDao imageHashDao;\n\n    @Override\n    public void removeImageHashes(List<Long> imageIds) {\n        if (CollectionUtil.isEmpty(imageIds)) {\n            return;\n        }\n        imageHashDao.deleteBatchIds(imageIds);\n    }\n\n    @Override\n    public void HashImagesByDhash(InputStream imageInputStream, long imageId) {\n        long hashValue;\n        try {\n            hashValue = DhashUtils.getImageDhashFrom(imageInputStream);\n        } catch (IOException e) {\n            log.error(\"imageId:{} 对应文件IO异常\", imageId);\n            ExceptionUtils.printIfDev(e);\n            throw new RuntimeException(e);\n        }\n        int hashSum = BitUtils.sumOfBits(hashValue);\n\n        try {\n            imageHashDao.insert(new ImageHash(imageId, hashValue, hashSum));\n        } catch (DuplicateKeyException e) {\n            log.error(\"保存图片哈希值时，插入数据库imageId：{}重复\", imageId);\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/imagehash/impl/SearchImageServiceImpl.java",
    "content": "package com.acimage.image.service.imagehash.impl;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.model.domain.community.Topic;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.feign.client.TopicClient;\nimport com.acimage.image.dao.ImageHashDao;\nimport com.acimage.image.global.context.DirectoryContext;\nimport com.acimage.common.model.domain.image.ImageHash;\nimport com.acimage.image.service.image.ImageQueryService;\nimport com.acimage.image.service.imagehash.SearchImageService;\nimport com.acimage.image.utils.BitUtils;\nimport com.acimage.image.utils.DhashUtils;\nimport com.acimage.image.utils.ImageFileUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.*;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\n\n@Slf4j\n@Service\npublic class SearchImageServiceImpl implements SearchImageService {\n    @Autowired\n    ImageHashDao imageDhashDao;\n    @Autowired\n    ImageQueryService imageQueryService;\n    @Autowired\n    TopicClient topicClient;\n\n    /**\n     * 利用dhash算法处理未被处理过的图片文件，保存到数据库中，在高并发环境下可能会有重复处理的问题\n     * 废弃原因：图片存到七牛云，而不是本地了\n     */\n    @Override\n    @Deprecated\n    public void processImagesHashForNotProcessedImages() {\n        //获取所有保存的图片\n        String directoryPath = DirectoryContext.IMAGES_DIRECTORY;\n        File directory = new File(directoryPath);\n        File[] files;\n        if (directory.isDirectory()) {\n            files = directory.listFiles();\n        } else {\n            log.error(\"{} 不是目录或不存在\", directoryPath);\n            return;\n        }\n        if (files == null) {\n            return;\n        }\n\n        //获取数据库中已经被处理过的图片\n        List<ImageHash> imageHashList = imageDhashDao.selectList(null);\n        List<Long> imageIdsInDb = ListUtils.extract(ImageHash::getImageId, imageHashList);\n        List<Long> imageIdsInDirectory = new ArrayList<>();\n        for (File file : files) {\n            String stringId = StrUtil.subBefore(file.getName(), '.', true);\n            if (stringId.length() > 12) {\n                //排除掉默认图片0.jpeg等\n                imageIdsInDirectory.add(Long.parseLong(stringId));\n            }\n        }\n\n        //得到没被处理过的图片\n        List<Long> differenceList = ListUtils.differenceSetOf(imageIdsInDirectory, imageIdsInDb);\n\n        //将图片dhash值保存到数据库中\n        for (Long imageId : differenceList) {\n            String filePath = ImageFileUtils.imageIdToImagePath(imageId);\n            File file = new File(filePath);\n            try {\n                //此处是同步进行，因为该方法非代理对象的方法\n                hashImageByDhashAlgorithm(new FileInputStream(file), imageId);\n            } catch (FileNotFoundException e1) {\n                log.error(\"提取图片dhash时 文件：{}不存在\", filePath);\n            }\n        }\n    }\n\n\n    @Override\n    public void hashImageByDhashAlgorithm(InputStream imageInputStream, long imageId) {\n        long hashValue;\n        try {\n            hashValue = DhashUtils.getImageDhashFrom(imageInputStream);\n        } catch (IOException e) {\n            log.error(\"imageId:{} 对应文件IO异常\", imageId);\n            ExceptionUtils.printIfDev(e);\n            throw new RuntimeException(e);\n        }\n        int hashSum = BitUtils.sumOfBits(hashValue);\n\n        try {\n            imageDhashDao.insert(new ImageHash(imageId, hashValue, hashSum));\n        } catch (DuplicateKeyException e) {\n            log.error(\"保存图片dhash时，插入数据库imageId：{}重复\", imageId);\n        }\n    }\n\n    @Override\n    public List<Image> searchMostSimilarImages(MultipartFile imageFile) {\n        int rankEnd = 10;\n        final int threshold = 20;\n\n        InputStream inputStream = null;\n        try {\n            inputStream = imageFile.getInputStream();\n        } catch (IOException e) {\n            log.error(\"用户：{} 以图搜图 错误：传入文件getInputStream异常\", UserContext.getUsername());\n\n            try {\n                inputStream.close();\n            } catch (IOException ex) {\n                log.error(e.getMessage());\n                throw new RuntimeException(ex);\n            }\n\n            throw new BusinessException(\"文件IO异常\");\n        }\n        long hashValue;\n        try {\n            hashValue = DhashUtils.getImageDhashFrom(inputStream);\n        } catch (IOException e) {\n            try {\n                inputStream.close();\n            } catch (IOException ex) {\n                log.error(e.getMessage());\n                throw new RuntimeException(ex);\n            }\n            throw new BusinessException(\"文件IO异常\");\n        }\n        List<ImageHash> imageHashList = imageDhashDao.selectList(null);\n        List<ImageHash> resultList = new ArrayList<>();\n        for (ImageHash imageHash : imageHashList) {\n            int distance = DhashUtils.distanceBetween(hashValue, imageHash.getHashValue());\n            imageHash.setDistance(distance);\n            if (distance <= threshold) {\n                resultList.add(imageHash);\n            }\n        }\n        resultList.sort(Comparator.comparing(ImageHash::getDistance));\n        int toIndex = Math.min(rankEnd, resultList.size());\n        log.debug(\"搜索结果：{}\", resultList);\n\n        //找到图片对象\n        List<Long> imageIds = ListUtils.extract(ImageHash::getImageId, resultList.subList(0, toIndex));\n        List<Image> images = imageQueryService.listImagesByIds(imageIds);\n        if (CollectionUtil.isEmpty(images)) {\n            return new ArrayList<>();\n        }\n        //找到对应话题\n        List<Long> topicIds = ListUtils.extract(Image::getTopicId, images);\n        List<Topic> topics = topicClient.queryTopics(topicIds).getData();\n        List<Image> imageWithTopics = new ArrayList<>();\n        for (Image image : images) {\n            for (Topic topic : topics) {\n                if (topic.getId().equals(image.getTopicId())) {\n                    image.setTopic(topic);\n                    imageWithTopics.add(image);\n                }\n            }\n        }\n\n        if (inputStream != null) {\n            try {\n                inputStream.close();\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return imageWithTopics;\n    }\n\n    @Override\n    public void removeImageHashes(List<Long> imageIds) {\n        imageDhashDao.deleteBatchIds(imageIds);\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/photo/PhotoService.java",
    "content": "package com.acimage.image.service.photo;\n\nimport org.springframework.web.multipart.MultipartFile;\n\npublic interface PhotoService {\n    String uploadPhotoAndUpdatePhotoUrl(MultipartFile photoFile);\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/service/photo/impl/PhotoServiceImpl.java",
    "content": "package com.acimage.image.service.photo.impl;\n\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.global.consts.FileFormatConstants;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.common.utils.ImageUtils;\nimport com.acimage.common.utils.minio.MinioUtils;\nimport com.acimage.feign.client.UserClient;\nimport com.acimage.common.global.consts.StorePrefixConstants;\nimport com.acimage.image.service.photo.PhotoService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.InputStream;\nimport java.util.Date;\n\n@Service\npublic class PhotoServiceImpl implements PhotoService {\n    @Autowired\n    UserClient userClient;\n    @Autowired\n    MinioUtils minioUtils;\n\n    /**\n     * 返回新token\n     */\n    @Override\n    public String uploadPhotoAndUpdatePhotoUrl(MultipartFile photoFile){\n        //上传头像到七牛云\n        Date now = new Date();\n        String suffix = String.format(\"%s.%s\", IdGenerator.getSnowflakeNextId(), FileFormatConstants.WEBP);\n        //压缩后限制大小\n        int limitSize=20*1000;\n        int width=200;\n        int height=200;\n        InputStream inputStream= ImageUtils.compressAsFixedWebpImage(photoFile,width,height,limitSize);\n        //上传\n        String photoUrl = minioUtils.generateBaseUrl(StorePrefixConstants.USER_PHOTO, now, suffix);\n        photoUrl=minioUtils.upload(inputStream, photoUrl, FileFormatConstants.WEBP_CONTENT_TYPE);\n\n        Result<String> result=userClient.modifyPhotoUrl(photoUrl);\n        if(result.isOk()){\n            return result.getData();\n        }else{\n            throw new BusinessException(\"头像修改失败\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/utils/BitUtils.java",
    "content": "package com.acimage.image.utils;\n\npublic class BitUtils {\n    public static int getBit(long value, int index) {\n        if (index < 0 || index > 63) {\n            throw new IllegalArgumentException(\"index范围为0-63\");\n        }\n\n        if (index == 63) {\n            if (value < 0) {\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            if (value < 0) {\n                //使value变为value补码去掉符号位之后的值\n                value = value + (1L << 62) + (1L << 62);\n            }\n            return (int) ((value >> index) & 1);\n        }\n    }\n\n    /**\n     *\n     * @param str64 64位补码\n     * @return 补码对应的long值\n     */\n    public static long str64ToLong(String str64){\n        if(str64==null||str64.length()!=64){\n            throw new IllegalArgumentException(\"str64传参为：\"+str64+\"；str64必须为64位长度的0-1字符串！\");\n        }\n\n        int sgn=str64.charAt(0)=='1'?-1:1;\n        //获取符合外之外的值\n        long sum=0;\n        for(int i=0;i<63;i++){\n            long bit=0L;\n            if(str64.charAt(63-i)=='0'){\n                bit=0L;\n            } else if (str64.charAt(63 - i) == '1') {\n                bit =1L;\n            }else{\n                throw new IllegalArgumentException(\"str64传参为：\"+str64+\"；str64必须为64位长度的0-1字符串！\");\n            }\n            sum+=(bit<<i);\n        }\n        if(sgn<0){\n            //获取原码:根据公式 补码=反码+1=（2^63-原码-1）+1=2^63-原码\n            sum=(1L<<62)-sum+(1L<<62);\n        }\n        return sgn*sum;\n    }\n\n    public static  String longToStr64(long value){\n        StringBuilder stringBuilder=new StringBuilder(64);\n        for(int i=0;i<64;i++){\n            stringBuilder.append(getBit(value,63-i));\n        }\n        return stringBuilder.toString();\n    }\n\n    public static int sumOfBits(long value){\n        String str64=longToStr64(value);\n        int sum=0;\n        for(int i=0;i<64;i++){\n            if(str64.charAt(i)=='1'){\n                sum++;\n            }\n        }\n        return sum;\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/utils/DhashUtils.java",
    "content": "package com.acimage.image.utils;\n\nimport net.coobird.thumbnailator.Thumbnails;\n\nimport javax.validation.constraints.NotNull;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class DhashUtils {\n    public static long getImageDhashFrom(@NotNull InputStream inputStream) throws IOException {\n        int width = 9;\n        int height = 8;\n        List<InputStream> inputStreams = Collections.singletonList(inputStream);\n        Thumbnails.Builder<InputStream> inputStreamBuilder = Thumbnails.fromInputStreams(inputStreams).forceSize(width, height);\n        BufferedImage bufferedImage = inputStreamBuilder.asBufferedImage();\n\n        StringBuilder str64 = new StringBuilder((width - 1) * height);\n\n        int[][] grays = new int[width][height];\n        for (int hi = 0; hi < height; hi++) {\n            for (int wi = 0; wi < width; wi++) {\n                Color color = new Color(bufferedImage.getRGB(wi, hi));\n                grays[wi][hi] = ImageUtils.rgb2Gray(color.getRed(), color.getGreen(), color.getBlue());\n                if (wi > 0) {\n                    int hashBit = 0;\n                    if (grays[wi][hi] > grays[wi - 1][hi]) {\n                        hashBit = 1;\n                    }\n                    str64.append(hashBit);\n                }\n            }\n        }\n        return BitUtils.str64ToLong(str64.toString());\n    }\n\n    public static int distanceBetween(@NotNull InputStream inputStream1, @NotNull InputStream inputStream2) throws IOException {\n        String str1 = BitUtils.longToStr64(getImageDhashFrom(inputStream1));\n        String str2 = BitUtils.longToStr64(getImageDhashFrom(inputStream2));\n        return hammingDistanceBetween(str1,str2);\n    }\n\n    public static int distanceBetween(long hashValue1, long hashValue2) {\n        String str1 = BitUtils.longToStr64(hashValue1);\n        String str2 = BitUtils.longToStr64(hashValue2);\n        return hammingDistanceBetween(str1,str2);\n    }\n\n    private static int hammingDistanceBetween(@NotNull String str1, @NotNull String str2) {\n        int distance = 0;\n        if (str1.length() != str2.length()) {\n            throw new IllegalArgumentException(\"参数长度不一致\");\n        }\n        for (int i = 0; i < str1.length(); i++) {\n            if (str1.charAt(i) != str2.charAt(i)) {\n                distance++;\n            }\n        }\n        return distance;\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/utils/ImageFileUtils.java",
    "content": "package com.acimage.image.utils;\n\nimport com.acimage.image.global.consts.MyFileConstants;\nimport com.acimage.image.global.context.DirectoryContext;\n\nimport javax.validation.constraints.NotNull;\n\npublic class ImageFileUtils {\n    public static String imageIdToImagePath(@NotNull Long imageId){\n        return DirectoryContext.IMAGES_DIRECTORY+\"/\"+imageId+\".\"+ MyFileConstants.IMAGE_FORMAT;\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/utils/ImageUtils.java",
    "content": "package com.acimage.image.utils;\n\npublic class ImageUtils {\n    public static int rgb2Gray(int r, int g, int b) {\n        return (int) Math.round(r * 0.299 + g * 0.581 + b * 0.114);\n    }\n}\n\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/web/config/WebMvcConfig.java",
    "content": "package com.acimage.image.web.config;\n\n\n\n\nimport com.acimage.common.web.interceptor.JwtInterceptor;\nimport com.acimage.common.web.interceptor.AccessInterceptor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n@Slf4j\n@Configuration\npublic class WebMvcConfig implements WebMvcConfigurer {\n\n    @Autowired\n    JwtInterceptor jwtInterceptor;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        List<String> commonExcludePathPatterns = new ArrayList<>(\n                Arrays.asList(\"/templates/**\", \"/static/**\", \"/\", \"/storage/**\", \"/favicon.ico\", \"/error\"));\n\n        registry.addInterceptor(jwtInterceptor).addPathPatterns(\"/**\")\n                .excludePathPatterns(commonExcludePathPatterns).order(20);\n\n        registry.addInterceptor(new AccessInterceptor()).addPathPatterns(\"/**\")\n                .excludePathPatterns(commonExcludePathPatterns).order(30);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/web/controller/ImageOperateController.java",
    "content": "package com.acimage.image.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.consts.FileFormatConstants;\nimport com.acimage.common.global.consts.TimeConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.common.utils.common.FileUtils;\nimport com.acimage.image.service.image.ImageMixWriteService;\nimport com.acimage.image.service.image.ImageWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\n\n@RestController\n@Slf4j\n@Validated\n@Authentication\n@RequestMapping(\"/api/image/images/operate\")\npublic class ImageOperateController {\n    @Autowired\n    ImageMixWriteService imageMixWriteService;\n\n    @RequestLimit(limitTimes = {1, 20},\n            durations = {2, TimeConstants.DAY_SECONDS},\n            penaltyTimes = {-1, -1},\n            targets = {LimitTarget.IP, LimitTarget.USER})\n    @PostMapping(\"/upload/topicImage\")\n    public Result<String> uploadTopicImage(@RequestParam(\"imageFile\") MultipartFile imageFile) {\n\n        String originName = imageFile.getOriginalFilename();\n\n        String format = FileUtils.formatOf(originName);\n        if (!FileFormatConstants.ALLOWED_IMAGE_FORMAT.contains(format)) {\n            return Result.fail(\"图片格式需为\" + FileFormatConstants.ALLOWED_IMAGE_FORMAT);\n        }\n        log.info(\"用户：{} 话题: 上传话题图片\", UserContext.getUsername());\n        return Result.ok(imageMixWriteService.saveImage(imageFile));\n    }\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/web/controller/PhotoOperateController.java",
    "content": "package com.acimage.image.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.consts.TimeConstants;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.image.service.photo.impl.PhotoServiceImpl;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n\n@RestController\n@Slf4j\n@Validated\n@Authentication\n@RequestMapping(\"/api/image/photos/operate\")\npublic class PhotoOperateController {\n    @Autowired\n    PhotoServiceImpl photoService;\n\n    @RequestLimit(limitTimes = {1,3},\n            durations = {2, TimeConstants.DAY_SECONDS},\n            penaltyTimes = {-1,-1},\n            targets = {LimitTarget.IP,LimitTarget.USER})\n    @PostMapping(\"/upload\")\n    public Result<String> uploadPhoto(@RequestParam(\"photoFile\") MultipartFile photoFile) {\n        String url = photoService.uploadPhotoAndUpdatePhotoUrl(photoFile);\n        return Result.ok(url);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/web/controller/SearchImageController.java",
    "content": "package com.acimage.image.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.image.service.imagehash.SearchImageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.util.List;\n\n@RestController\n@Slf4j\n@Validated\n@Authentication\n@RequestMapping(\"/api/image/images\")\npublic class SearchImageController {\n    @Autowired\n    SearchImageService searchImageService;\n\n\n    @RequestLimit(limitTimes = {1,10}, durations = {3,1}, penaltyTimes = {-1,-1}, targets = {LimitTarget.IP,LimitTarget.ALL})\n    @PostMapping(\"/searchByImage\")\n    public Result<List<Image>> searchImageWithTopicByImage(@RequestParam(\"imageFile\") MultipartFile multipartFile) {\n        return Result.ok(searchImageService.searchMostSimilarImages(multipartFile));\n    }\n}\n"
  },
  {
    "path": "acimage_image/src/main/java/com/acimage/image/web/provider/ImageProvider.java",
    "content": "package com.acimage.image.web.provider;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.enums.AuthenticationType;\nimport com.acimage.common.model.domain.image.Image;\nimport com.acimage.common.result.Result;\nimport com.acimage.image.service.image.ImageMixWriteService;\nimport com.acimage.image.service.image.ImageQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n\n@RestController\n@Slf4j\n@Validated\n@RequestMapping(\"/image/images\")\npublic class ImageProvider {\n    @Autowired\n    ImageMixWriteService imageMixWriteService;\n    @Autowired\n    ImageQueryService imageQueryService;\n\n    @GetMapping(\"/topicId/{topicId}\")\n    public Result<List<Image>> queryTopicImages(@PathVariable Long topicId) {\n        return Result.ok(imageQueryService.listImagesOrderById(topicId));\n    }\n\n}\n"
  },
  {
    "path": "acimage_image/src/main/resources/application-dev.yml",
    "content": "server:\n  port: 8090\n\nspring:\n  config:\n    activate:\n      on-profile:\n        - dev\n  datasource:\n    type: com.alibaba.druid.pool.DruidDataSource\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://localhost:3306/acimage_image?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n    username: root\n    password: mysql\n  rabbitmq:\n    host: 192.168.130.128\n    port: 5672\n    username: acimage\n    password: acimage\n    virtual-host: /acimage\n    listener:\n      simple:\n        acknowledge-mode: manual # 手动应答\n        auto-startup: false #消费者是否自动启动\n        prefetch: 1 #每次从队列中取一个,轮询分发，默认是公平分发\n        retry:\n          max-attempts: 5 # 重试次数\n          enabled: true # 开启重试\n      direct:\n        auto-startup: false #生产者是否自动启动\n        acknowledge-mode: manual # 手动应答\n  redis:\n    host: 192.168.130.128\n    port: 6379\n    password: redis\n    lettuce:\n      pool:\n        max-active: 8\n        max-idle: 8 #最大空闲连接\n        min-idle: 0 #最小空闲连接\n        max-wait: 100ms #连接等待时间\n  cloud:\n    nacos:\n      server-addr: localhost:8848 #nacos 服务地址\n"
  },
  {
    "path": "acimage_image/src/main/resources/application.yml",
    "content": "spring:\n  profiles:\n    include: common,common-secret\n    active: dev2\n  application:\n    name: image-service\n  servlet:\n    multipart:\n      max-file-size: 2MB\n      max-request-size: 4MB\n\nfeign:\n  okhttp:\n    enabled: true\n  httpclient:\n    max-connections: 20 # 最大的连接数\n    max-connections-per-route: 5 # 每个路径的最大连接数\n\nmybatis-plus:\n  mapper-locations: classpath:mapper/*.xml\n  type-aliases-package: com.acimage.image.model.domain,com.acimage.common.model.domain\n  configuration:\n    map-underscore-to-camel-case: true\n    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl\n  global-config:\n    db-config:\n      table-prefix: tb_\n\n\n"
  },
  {
    "path": "acimage_image/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- 配置文件修改时重新加载，默认true -->\n<configuration scan=\"true\">\n\n    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->\n    <springProperty scope=\"context\" name=\"spring.application.name\" source=\"spring.application.name\"/>\n    <springProperty scope=\"context\" name=\"LOG_HOME\" source=\"logback.base\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%20.20thread{20}] %40logger{40} : %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"info\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>INFO</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-info.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>8</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"warn\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>WARN</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-warn.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"error\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>ERROR</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-error.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n\n    <springProfile name=\"dev2\">\n        <logger name=\"com.acimage.image.dao\" level=\"INFO\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <logger name=\"com.acimage\" level=\"DEBUG\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n    <springProfile name=\"prod,server\">\n        <logger name=\"com.acimage\" level=\"info\" additivity=\"false\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n\n</configuration>"
  },
  {
    "path": "acimage_image/src/main/resources/mapper/ImageMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.image.dao.ImageDao\" >\n\n    <insert id=\"insertList\" >\n            insert into tb_image(id,topic_id,size,description,url) values\n            <foreach collection=\"list\" item=\"image\" index=\"index\" separator=\",\">\n                ( #{image.id},#{image.topicId},#{image.size},#{image.description},#{image.url} )\n            </foreach>\n    </insert>\n\n    <update id=\"updateDescription\" >\n            <foreach collection=\"list\" item=\"idAndDescription\" index=\"index\" separator=\";\">\n                update tb_image set description=#{idAndDescription.value} where id=#{idAndDescription.key}\n            </foreach>\n    </update>\n\n</mapper>"
  },
  {
    "path": "acimage_image/src/test/java/com/acimage/image/CasualTest.java",
    "content": "package com.acimage.image;\n\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport com.acimage.common.utils.common.ListUtils;\nimport com.acimage.image.utils.BitUtils;\nimport com.acimage.image.utils.DhashUtils;\nimport net.coobird.thumbnailator.Thumbnails;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport javax.imageio.ImageIO;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.*;\nimport java.util.*;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\n@SpringBootTest\npublic class CasualTest {\n\n    @Test\n    public void testGetSnowflakeId(){\n        long id=IdUtil.getSnowflake().nextId();\n        System.out.println(id);\n        System.out.println(Long.toString(id).length());\n\n        System.out.println();\n    }\n\n\n    @Test\n    public void testMd5(){\n        String txt=\"b3cfd02862e5565ebecfa608035911d612346546\";\n        System.out.println(DigestUtil.md5Hex(txt).length());\n    }\n\n    @Test\n    public void testUUID(){\n        String uuid=IdUtil.simpleUUID();\n        System.out.println(uuid.length());\n    }\n\n    @Test\n    public void testPatternMatch(){\n        final String PASSWORD_PATTERN=\"^(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$\";\n        String password=\"5555555----\";\n        System.out.println(Pattern.matches(PASSWORD_PATTERN,password));\n    }\n\n    @Test\n    public void testDateNow(){\n\n        Date beginOfDay = DateUtil.beginOfDay(new Date());\n        //获取昨天开始时间\n        Date startDate=DateUtil.offsetDay(beginOfDay,-1);\n        String startTime=DateUtil.format(startDate,\"yyyy-MM-dd HH:mm:ss\");\n        System.out.println(startTime);\n    }\n\n    @Test\n    public void testSubAfter(){\n        String str=\"558.968.jpeg\";\n        System.out.println(StrUtil.subAfter(str,'.',true));\n        List<String> s= Arrays.asList(\"jpg\",\"png\");\n        System.out.println(s.contains(\"jpg\"));\n    }\n\n    @Test\n    public void testTh(){\n\n        BufferedImage inputStream= null;\n        try {\n            inputStream = ImageIO.read(new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\bg2.png\"));\n//            inputStream = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\1.jpeg\");\n            List<BufferedImage> inputStreams=new ArrayList<>(List.of(inputStream));\n            Thumbnails.Builder<BufferedImage> inputStreamBuilder=Thumbnails.fromImages(inputStreams);\n            inputStreamBuilder.forceSize(9,8);\n//            inputStreamBuilder.asBufferedImage().toFile(new File(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\"));\n            ImageIO.write(inputStreamBuilder.asBufferedImage(),\"jpeg\",new File(\"F:\\\\MyImage\\\\素材\\\\0bg2.png\"));\n//            inputStreamBuilder.asFiles(new ArrayList<>(List.of(new File(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\"))));\n\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Test\n    public void testGetImageDhashFrom(){\n\n        InputStream inputStream1= null;\n        InputStream inputStream2= null;\n        try {\n            inputStream1 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\1.jpeg\");\n            inputStream2 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\");\n            System.out.println(DhashUtils.distanceBetween(inputStream1,inputStream2));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Test\n    public void testImageScale(){\n        Image img=Toolkit.getDefaultToolkit().getImage(System.getProperty(\"user.dir\"));\n\n        InputStream inputStream1= null;\n        InputStream inputStream2= null;\n        try {\n            inputStream1 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\");\n            inputStream2 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\1.jpeg\");\n            System.out.println(DhashUtils.distanceBetween(inputStream1,inputStream2));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Test\n    public void testBitUtils(){\n        long a=18588L;\n        System.out.println(BitUtils.longToStr64(a));\n        System.out.println(BitUtils.sumOfBits(a));\n        System.out.println(BitUtils.str64ToLong(BitUtils.longToStr64(a)));\n        new ArrayList<>(Arrays.asList()).subList(0,10);\n    }\n\n    @Test\n    public void test89(){\n        List<Long> list1=new ArrayList<>(Arrays.asList(1L,2L,5L,7L,10089L,999999L));\n        List<Long> list2=new ArrayList<>(Arrays.asList(2L,4L,6L,7L,10086L));\n        System.out.println(ListUtils.differenceSetOf(list1,list2));\n    }\n\n\n\n\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/test/java/com/acimage/image/ImageApplicationTests.java",
    "content": "package com.acimage.image;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\nclass ImageApplicationTests {\n\n\t@Test\n\tvoid contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "acimage_image/src/test/java/com/acimage/image/dao/ImageDaoTest.java",
    "content": "package com.acimage.image.dao;\n\n\nimport com.acimage.common.model.domain.image.Image;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.List;\n\n\n@SpringBootTest\npublic class ImageDaoTest {\n    @Autowired\n    ImageDao imageDao;\n\n    @Test\n    public void testSelectAll() {\n        long topicId = 1585529145054986240L;\n        List<Image> imageList = imageDao.selectListOrderById(topicId);\n        System.out.println(imageList);\n        System.out.println(imageList.size());\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/test/java/com/acimage/image/dao/ImageHashDaoDaoTest.java",
    "content": "package com.acimage.image.dao;\n\n\n\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n\n@SpringBootTest\npublic class ImageHashDaoDaoTest {\n    @Autowired\n    ImageHashDao imageHashDao;\n\n    @Test\n    public void testSelectAll(){\n        System.out.println(imageHashDao.selectList(null));\n    }\n\n\n\n}\n"
  },
  {
    "path": "acimage_image/src/test/java/com/acimage/image/service/FileServiceTest.java",
    "content": "package com.acimage.image.service;\n\n\n\nimport com.acimage.image.service.imagehash.SearchImageService;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.mock.web.MockMultipartFile;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\n@SpringBootTest\npublic class FileServiceTest {\n\n\n\n    @Autowired\n    SearchImageService searchImageService;\n\n    @Test\n    void searchMostSimilarImagesTest(){\n        File file=new File(\"F:\\\\MyImage\\\\素材\\\\爱丽丝.jpeg\");\n        file=new File(\"F:\\\\MyImage\\\\素材\\\\大忍.jpg\");\n        MultipartFile cMultiFile = null;\n        try {\n            cMultiFile = new MockMultipartFile(\"file\", file.getName(), null, new FileInputStream(file));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        System.out.println(searchImageService.searchMostSimilarImages(cMultiFile));\n    }\n\n}\n"
  },
  {
    "path": "acimage_image/src/test/java/com/acimage/image/service/SearchImageServiceTest.java",
    "content": "package com.acimage.image.service;\n\n\n\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.image.service.imagehash.SearchImageService;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\n\n@SpringBootTest\npublic class SearchImageServiceTest {\n\n    @Autowired\n    SearchImageService searchImageService;\n\n    @Test\n    void processImagesHashForNotProcessedImagesTest(){\n        InputStream inputStream=null;\n        try {\n            inputStream = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\");\n        } catch (FileNotFoundException e) {\n            ExceptionUtils.printIfDev(e);\n            throw new RuntimeException(e);\n        }\n\n        searchImageService.hashImageByDhashAlgorithm(inputStream,1L);\n        try {\n            Thread.sleep(4000);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "acimage_image/src/test/java/com/acimage/image/utils/ImageHashDaoUtilsTest.java",
    "content": "package com.acimage.image.utils;\n\n\nimport net.coobird.thumbnailator.Thumbnails;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport javax.imageio.ImageIO;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@SpringBootTest\npublic class ImageHashDaoUtilsTest {\n\n    @Test\n    public void scaleImageTest(){\n\n        BufferedImage inputStream= null;\n        try {\n            inputStream = ImageIO.read(new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\bg2.png\"));\n//            inputStream = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\1.jpeg\");\n            List<BufferedImage> inputStreams=new ArrayList<>(List.of(inputStream));\n            Thumbnails.Builder<BufferedImage> inputStreamBuilder=Thumbnails.fromImages(inputStreams);\n            inputStreamBuilder.forceSize(9,8);\n//            inputStreamBuilder.asBufferedImage().toFile(new File(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\"));\n            ImageIO.write(inputStreamBuilder.asBufferedImage(),\"jpeg\",new File(\"F:\\\\MyImage\\\\素材\\\\0bg2.png\"));\n//            inputStreamBuilder.asFiles(new ArrayList<>(List.of(new File(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\"))));\n\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Test\n    public void getImageDhashFromTest(){\n\n        InputStream inputStream1= null;\n        InputStream inputStream2= null;\n        try {\n            inputStream1 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\1.jpeg\");\n            inputStream2 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\");\n            System.out.println(DhashUtils.distanceBetween(inputStream1,inputStream2));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Test\n    public void imageDistanceTest(){\n        Image img=Toolkit.getDefaultToolkit().getImage(System.getProperty(\"user.dir\"));\n\n        InputStream inputStream1= null;\n        InputStream inputStream2= null;\n        try {\n            inputStream1 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\0001.jpeg\");\n            inputStream2 = new FileInputStream(\"F:\\\\MyImage\\\\素材\\\\1.jpeg\");\n            System.out.println(DhashUtils.distanceBetween(inputStream1,inputStream2));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Test\n    public void bitUtilsTest(){\n        long a=185L;\n        System.out.println(BitUtils.longToStr64(a));\n        System.out.println(BitUtils.str64ToLong(BitUtils.longToStr64(a)));\n    }\n\n\n\n\n\n}\n"
  },
  {
    "path": "acimage_user/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>acimage</artifactId>\n        <groupId>com.acimage</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>acimage_user</artifactId>\n    <packaging>jar</packaging>\n\n    <name>acimage_user</name>\n    <url>http://maven.apache.org</url>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_common</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.acimage</groupId>\n            <artifactId>acimage_feign</artifactId>\n            <version>0.0.1-SNAPSHOT</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n        <!--websocket-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n        <!--邮箱-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-mail</artifactId>\n        </dependency>\n\n        <!--druid依赖-->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid</artifactId>\n        </dependency>\n\n        <!--mysql依赖-->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n\n        <!--rabbitmq-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!--敏感词过滤-->\n        <dependency>\n            <groupId>io.github.toolgood</groupId>\n            <artifactId>toolgood-words</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.ben-manes.caffeine</groupId>\n            <artifactId>caffeine</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <includeSystemScope>true</includeSystemScope>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <skipTests>true</skipTests><!--跳过测试包-->\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/UserApplication.java",
    "content": "package com.acimage.user;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n\n@Slf4j\n@SpringBootApplication\n@EnableDiscoveryClient\n@EnableScheduling\n@EnableFeignClients(basePackages=\"com.acimage.feign\")\n@MapperScan(\"com.acimage.user.dao\")\n@ComponentScan(value={\"com.acimage\"})\npublic class UserApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(UserApplication.class, args);\n\t\tlog.info(\"------------->>>User中心启动<<<-------------\");\n\t}\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/dao/CommentMsgDao.java",
    "content": "package com.acimage.user.dao;\n\nimport com.acimage.common.model.domain.user.CommentMsg;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\npublic interface CommentMsgDao extends BaseMapper<CommentMsg> {\n\n    List<CommentMsg> selectCommentMsgsWithUser(@Param(\"toUserId\") long toUserId,@Param(\"startIndex\") int startIndex,@Param(\"size\") int size);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/dao/UserDao.java",
    "content": "package com.acimage.user.dao;\n\n\nimport com.acimage.common.model.domain.user.User;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UserDao extends BaseMapper<User> {\n\n//    @Insert(\"insert into tb_user(id,username,pwd,email,salt) values (#{id},#{username},#{password},#{email},#{salt})\")\n//    void insert(@Param(\"id\") long id,@Param(\"username\") String username,@Param(\"password\") String passwordDigest,\n//                @Param(\"email\") String email,@Param(\"salt\") String salt);\n\n//    UserPrivacy selectUserInfoWithTopicCountStarCountById(@Param(\"id\") long id);\n\n\n//    @Insert(\"insert into tb_user(id,username,pwd,email,salt) values (#{id},#{username},#{password},#{email},#{salt})\")\n//    void insert(@Param(\"id\") long id,@Param(\"username\") String username,@Param(\"password\") String passwordDigest,\n//                @Param(\"email\") String email,@Param(\"salt\") String salt);\n//\n//    @Update(\"update tb_user set username=#{username} where id=#{id}\")\n//    Integer updateUsername(@Param(\"id\") long id, @Param(\"username\") String username);\n//\n//\n//    @Select(\"select * from tb_user where username=#{username}\")\n//    UserPrivacy selectUserInfoByUsername(@Param(\"username\") String username);\n//\n//    @Select(\"select * from tb_user where email=#{email}\")\n//    UserPrivacy selectUserInfoByEmail(@Param(\"email\") String email);\n//\n//    UserPrivacy selectUserInfoWithTopicCountStarCountById(@Param(\"id\") long id);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/dao/UserMsgDao.java",
    "content": "package com.acimage.user.dao;\n\nimport com.acimage.common.model.domain.user.UserMsg;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Update;\n\npublic interface UserMsgDao extends BaseMapper<UserMsg> {\n\n    @Update(\"update tb_user_msg set ${column}=${column}+#{increment} where user_id=#{userId}\")\n    void increaseColumn(@Param(\"userId\") long userId,@Param(\"column\") String column,@Param(\"increment\") int increment);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/dao/UserPrivacyDao.java",
    "content": "package com.acimage.user.dao;\n\n\n\n\nimport com.acimage.common.model.domain.user.UserPrivacy;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UserPrivacyDao extends BaseMapper<UserPrivacy> {\n\n//    @Insert(\"insert into tb_user(id,username,pwd,email,salt) values (#{id},#{username},#{password},#{email},#{salt})\")\n//    void insert(@Param(\"id\") long id,@Param(\"username\") String username,@Param(\"password\") String passwordDigest,\n//                @Param(\"email\") String email,@Param(\"salt\") String salt);\n\n//    @Update(\"update tb_user set username=#{username} where id=#{id}\")\n//    Integer updateUsername(@Param(\"id\") long id, @Param(\"username\") String username);\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/global/config/WebMvcConfig.java",
    "content": "package com.acimage.user.global.config;\n\n\n\nimport com.acimage.common.web.interceptor.JwtInterceptor;\nimport com.acimage.common.web.interceptor.AccessInterceptor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n@Slf4j\n@Configuration\npublic class WebMvcConfig implements WebMvcConfigurer {\n\n    @Autowired\n    JwtInterceptor jwtInterceptor;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        List<String> commonExcludePathPatterns = new ArrayList<>(\n                Arrays.asList( \"/\", \"/error\"));\n\n        registry.addInterceptor(jwtInterceptor).addPathPatterns(\"/**\")\n                .excludePathPatterns(commonExcludePathPatterns).order(20);\n\n        registry.addInterceptor(new AccessInterceptor()).addPathPatterns(\"/**\")\n                .excludePathPatterns(commonExcludePathPatterns).order(30);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/global/config/WebSocketConfig.java",
    "content": "package com.acimage.user.global.config;\n\nimport com.acimage.user.web.websocket.MyWebSocketHandler;\nimport com.acimage.user.web.websocket.MyHandshakeInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.socket.config.annotation.*;\nimport org.springframework.web.socket.server.standard.ServerEndpointExporter;\n\n@Configuration\n@EnableWebSocket\n//@EnableWebSocketMessageBroker\npublic class WebSocketConfig implements WebSocketConfigurer, WebSocketMessageBrokerConfigurer {\n\n    @Bean\n    public ServerEndpointExporter serverEndpointExporter() {\n        return new ServerEndpointExporter();\n    }\n\n    @Autowired\n    MyHandshakeInterceptor interceptor;\n    @Autowired\n    MyWebSocketHandler myWebSocketHandler;\n\n\n    @Override\n    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {\n        registry.addHandler(myWebSocketHandler, \"/websocket\")\n                .setAllowedOrigins(\"*\")\n                .addInterceptors(interceptor);\n\n    }\n\n//    @Override\n//    public void registerStompEndpoints(StompEndpointRegistry registry) {\n//        System.out.println(\"注册阿萨德老好saddddd看\");\n//        registry.addEndpoint(\"/wsx.ws\")\n//                .addInterceptors(interceptor)//拦截器方式1,暂不用\n//                .setAllowedOrigins(\"*\");//开启socketJs\n//    }\n\n\n\n}\n\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/global/consts/PageSizeConsts.java",
    "content": "package com.acimage.user.global.consts;\n\npublic class PageSizeConsts {\n    public static final int TOPIC_COMMENTS =5;\n\n    public static final int ACTIVITY_TOPICS =5;\n\n    public static final int ACTIVITY_COMMENTS =5;\n\n    public static final int ACTIVITY_STARS =5;\n\n    public static final int FORUM_TOPICS =10;\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/global/consts/StorePrefixConst.java",
    "content": "package com.acimage.user.global.consts;\n\npublic class StorePrefixConst {\n    public final static String TOPIC_IMAGE=\"topicImage\";\n    public final static String USER_PHOTO=\"userPhoto\";\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/global/consts/WebSocketSessionConstants.java",
    "content": "package com.acimage.user.global.consts;\n\npublic class WebSocketSessionConstants {\n    public static final String KEY_USER_ID=\"userId\";\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/model/request/UserLoginReq.java",
    "content": "package com.acimage.user.model.request;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.Size;\n\n@Data\n@NoArgsConstructor\npublic class UserLoginReq {\n\n    @Email(message = \"邮箱格式错误\")\n    @Size(min=6,max=32,message = \"邮箱长度在6到32之间\")\n    private String email;\n\n    @Size(min = 100, max = 2000, message = \"非法密码\")\n    String password;\n\n    @Size(min=4,max=6,message = \"验证码长度不符\")\n    String verifyCode;\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/model/request/UserRegisterReq.java",
    "content": "package com.acimage.user.model.request;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport javax.validation.constraints.*;\n\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserRegisterReq {\n    public static final int VERIFY_CODE_LENGTH=8;\n\n    @Size(min=2,max=12,message = \"用户名长度在2到12之间\")\n    private String username;\n\n    @Size(min=100,max=2000,message = \"非法密码\")\n    private String password;\n\n    @Email(message = \"邮箱格式错误\")\n    @Size(min=6,max=32,message = \"邮箱长度在6到32之间\")\n    private String email;\n\n    /**\n     * 有可能带空格\n     */\n    @Size(min=VERIFY_CODE_LENGTH,max = VERIFY_CODE_LENGTH+2)\n    private String verifyCode;\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/model/vo/ProfileVo.java",
    "content": "package com.acimage.user.model.vo;\n\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Data;\n\nimport java.util.Date;\n\n@Data\npublic class ProfileVo {\n    String username;\n    String email;\n    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n    Date registerTime;\n    Integer topicCount;\n    Integer starCount;\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/mq/config/SyncUserMqConfig.java",
    "content": "package com.acimage.user.mq.config;\n\n\nimport com.acimage.common.global.consts.MqConstants;\nimport org.springframework.amqp.core.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class SyncUserMqConfig {\n\n    @Autowired\n    AmqpAdmin rabbitAdmin;\n    @Bean\n    public Queue syncUserQueue() {\n        // durable:是否持久化,默认是false,持久化队列：会被存储在磁盘上，当消息代理重启时仍然存在，暂存队列：当前连接有效\n        // exclusive:默认也是false，只能被当前创建的连接使用，而且当连接关闭后队列即被删除。此参考优先级高于durable\n        // autoDelete:是否自动删除，当没有生产者或者消费者使用此队列，该队列会自动删除。\n        //   return new Queue(\"TestDirectQueue\",true,true,false);\n\n        //一般设置一下队列的持久化就好,其余两个就是默认false\n        return new Queue(MqConstants.SYNC_USER_QUEUE, true);\n    }\n\n\n    @Bean\n    DirectExchange syncUserExchange() {\n        return new DirectExchange(MqConstants.COMMUNITY_USER_EXCHANGE, true, false);\n    }\n\n    //绑定  将队列和交换机绑定, 并设置用于匹配键\n    @Bean\n    Binding syncUserBinding() {\n        return BindingBuilder.bind(syncUserQueue()).to(syncUserExchange()).with(MqConstants.SYNC_USER_ROUTE);\n    }\n\n    //创建交换机和对列\n    @Bean\n    public void createExchangeQueueForSyncUser() {\n        rabbitAdmin.declareExchange(syncUserExchange());\n        rabbitAdmin.declareQueue(syncUserQueue());\n    }\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/mq/consumer/UserMsgConsumer.java",
    "content": "package com.acimage.user.mq.consumer;\n\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.domain.user.CommentMsg;\nimport com.acimage.common.model.domain.user.UserMsg;\nimport com.acimage.common.model.mq.dto.EsAddDto;\nimport com.acimage.common.model.mq.dto.EsDeleteDto;\nimport com.acimage.common.model.mq.dto.EsUpdateByIdDto;\nimport com.acimage.common.utils.EsUtils;\nimport com.acimage.common.utils.ExceptionUtils;\nimport com.acimage.user.service.commentmsg.CommentMsgWriteService;\nimport com.acimage.user.service.usermsg.UserMsgQueryService;\nimport com.acimage.user.service.usermsg.UserMsgWriteService;\nimport com.acimage.user.web.websocket.MyWebSocketHandler;\nimport com.rabbitmq.client.Channel;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\n\n@Slf4j\n@Component\n@RabbitListener(queues = MqConstants.USER_MSG_QUEUE)\npublic class UserMsgConsumer {\n\n    @Autowired\n    UserMsgWriteService userMsgWriteService;\n    @Autowired\n    CommentMsgWriteService commentMsgWriteService;\n    @Autowired\n    MyWebSocketHandler webSocketHandler;\n\n    @RabbitHandler\n    public void commentMsg(Channel channel, Message message, CommentMsg commentMsg) {\n        log.info(\"用户评论消息通知：{}\", commentMsg);\n        try {\n            //插入用户消息\n            commentMsgWriteService.insert(commentMsg);\n            //评论消息+1\n            userMsgWriteService.increaseMsg(commentMsg.getToUserId(), UserMsg::getCommentMsgCount, 1);\n            //推送消息给用户\n            webSocketHandler.sendMsgCount(commentMsg.getToUserId());\n\n        } catch (Exception e) {\n            ExceptionUtils.printIfDev(e);\n            log.error(\"用户评论消息通知失败 error:{} data：{}\", e.getMessage(), commentMsg);\n\n        } finally {\n            String messageBody = new String(message.getBody());\n            try {\n                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);\n            } catch (IOException e) {\n                ExceptionUtils.printIfDev(e);\n                log.error(\"用户评论消息通知失败 error:{} message:{}\", e.getMessage(), messageBody);\n                try {\n                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);\n                } catch (IOException ex) {\n                    ExceptionUtils.printIfDev(ex);\n                    log.error(\"用户评论消息通知reject失败 error:{} message:{}\", ex.getMessage(), messageBody);\n                }\n            }\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/mq/producer/SyncUserMqProducer.java",
    "content": "package com.acimage.user.mq.producer;\n\nimport com.acimage.common.global.consts.MqConstants;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.mq.dto.UserIdWithPhotoUrl;\nimport com.acimage.common.model.mq.dto.UserIdWithUsername;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SyncUserMqProducer {\n\n    @Autowired\n    RabbitTemplate rabbitTemplate;\n\n    public void sendSyncUsernameMessage(UserIdWithUsername userIdWithUsername){\n        rabbitTemplate.convertAndSend(MqConstants.COMMUNITY_USER_EXCHANGE, MqConstants.SYNC_USER_ROUTE,userIdWithUsername);\n    }\n\n    public void sendSyncUserPhotoUrlMessage(UserIdWithPhotoUrl userIdWithPhotoUrl){\n        rabbitTemplate.convertAndSend(MqConstants.COMMUNITY_USER_EXCHANGE, MqConstants.SYNC_USER_ROUTE,userIdWithPhotoUrl);\n    }\n\n    public void sendAddUserMessage(CmtyUser cmtyUser){\n        rabbitTemplate.convertAndSend(MqConstants.COMMUNITY_USER_EXCHANGE, MqConstants.SYNC_USER_ROUTE, cmtyUser);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/commentmsg/CommentMsgQueryService.java",
    "content": "package com.acimage.user.service.commentmsg;\n\nimport com.acimage.common.model.domain.user.CommentMsg;\nimport com.acimage.common.model.page.MyPage;\n\npublic interface CommentMsgQueryService {\n    MyPage<CommentMsg> pageMyCommentMsg(long userId, int pageNo, int pageSize);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/commentmsg/CommentMsgWriteService.java",
    "content": "package com.acimage.user.service.commentmsg;\n\nimport com.acimage.common.model.domain.user.CommentMsg;\n\npublic interface CommentMsgWriteService {\n    void insert(CommentMsg commentMsg);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/commentmsg/impl/CommentMsgQueryServiceImpl.java",
    "content": "package com.acimage.user.service.commentmsg.impl;\n\nimport com.acimage.common.model.domain.user.CommentMsg;\nimport com.acimage.common.model.page.MyPage;\nimport com.acimage.common.utils.common.PageUtils;\nimport com.acimage.user.dao.CommentMsgDao;\nimport com.acimage.user.service.commentmsg.CommentMsgQueryService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class CommentMsgQueryServiceImpl implements CommentMsgQueryService {\n    @Autowired\n    CommentMsgDao commentMsgDao;\n\n    @Override\n    public MyPage<CommentMsg> pageMyCommentMsg(long userId, int pageNo, int pageSize) {\n        LambdaQueryWrapper<CommentMsg> qw = new LambdaQueryWrapper<>();\n        qw.eq(CommentMsg::getToUserId, userId);\n        int startIndex = PageUtils.startIndexOf(pageNo, pageSize);\n        List<CommentMsg> commentMsgList = commentMsgDao.selectCommentMsgsWithUser(userId, startIndex, pageSize);\n        int totalCount = commentMsgDao.selectCount(qw);\n        return new MyPage<>(totalCount, commentMsgList);\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/commentmsg/impl/CommentMsgWriteServiceImpl.java",
    "content": "package com.acimage.user.service.commentmsg.impl;\n\nimport com.acimage.common.model.domain.user.CommentMsg;\nimport com.acimage.user.dao.CommentMsgDao;\nimport com.acimage.user.service.commentmsg.CommentMsgWriteService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class CommentMsgWriteServiceImpl implements CommentMsgWriteService {\n    @Autowired\n    CommentMsgDao commentMsgDao;\n\n    @Override\n    public void insert(CommentMsg commentMsg){\n        commentMsgDao.insert(commentMsg);\n    }\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/mail/MainService.java",
    "content": "package com.acimage.user.service.mail;\n\nimport java.util.concurrent.TimeUnit;\n\npublic interface MainService {\n    void sendTextMailMessage(String to, String subject, String text);\n\n    void sendVerifyCodeMailMessage(String to, String code, int timeoutMinute);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/mail/impl/MailServiceImpl.java",
    "content": "package com.acimage.user.service.mail.impl;\n\nimport com.acimage.user.service.mail.MainService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.mail.javamail.JavaMailSender;\nimport org.springframework.mail.javamail.MimeMessageHelper;\nimport org.springframework.stereotype.Service;\n\nimport javax.mail.MessagingException;\nimport java.util.Date;\n\n@Service\n@Slf4j\npublic class MailServiceImpl implements MainService {\n    /**\n     * 注入邮件工具类\n     */\n    @Autowired\n    JavaMailSender javaMailSender;\n\n    @Value(\"${spring.mail.username}\")\n    String sendMailer;\n\n    /**\n     * 发送纯文本邮件\n     */\n    @Override\n    public void sendTextMailMessage(String to, String subject, String text) {\n\n        try {\n            //true 代表支持复杂的类型\n            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(), true);\n            //邮件发信人\n            mimeMessageHelper.setFrom(sendMailer);\n            //邮件收信人\n            mimeMessageHelper.setTo(to);\n            //邮件主题\n            mimeMessageHelper.setSubject(subject);\n            //邮件内容\n            mimeMessageHelper.setText(text);\n            //邮件发送时间\n            mimeMessageHelper.setSentDate(new Date());\n\n            //发送邮件\n            javaMailSender.send(mimeMessageHelper.getMimeMessage());\n            log.info(\"发送邮件成功：{}->{} subject；{} text；{}\", sendMailer, to, subject, text);\n\n        } catch (MessagingException e) {\n            log.error(\"发送邮件失败：{}->{} subject；{} text；{} error:{}\", sendMailer, to, subject, text, e.getMessage());\n        }\n    }\n\n\n    /**\n     * 发送html邮件\n     */\n    @Override\n    public void sendVerifyCodeMailMessage(String to, String code,int timeoutMinute) {\n\n        String content = \"acimage网站正在验证邮箱<br/>\" +\n                \"验证码：\" + code+\"<br/>\"+\n                \"有效时间\"+timeoutMinute+\"分钟\";\n\n        try {\n            //true 代表支持复杂的类型\n            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(), true);\n            //邮件发信人\n            mimeMessageHelper.setFrom(sendMailer);\n            //邮件收信人  1或多个\n            mimeMessageHelper.setTo(to);\n            //邮件主题\n            mimeMessageHelper.setSubject(\"acimage网站验证邮箱\");\n            //邮件内容   true 代表支持html\n            mimeMessageHelper.setText(content, true);\n            //邮件发送时间\n            mimeMessageHelper.setSentDate(new Date());\n\n            //发送邮件\n            javaMailSender.send(mimeMessageHelper.getMimeMessage());\n            log.info(\"发送验证码邮件成功：to:{} code；{} \",  to, code);\n\n        } catch (MessagingException e) {\n            log.error(\"发送验证码邮件失败：to:{} code；{} error:{}\",  to, code,e.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/LoginService.java",
    "content": "package com.acimage.user.service.user;\n\n\n\nimport com.acimage.user.model.request.UserLoginReq;\nimport com.acimage.user.model.request.UserRegisterReq;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n\npublic interface LoginService {\n\n    String getPublicKey();\n\n    @Transactional\n    String registerUser(UserRegisterReq userRegister);\n\n    @Transactional\n    String login(UserLoginReq userLogin);\n\n\n    void logout(HttpServletRequest request);\n\n    void checkAndSendCodeToEmail(String email);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/UserInfoService.java",
    "content": "package com.acimage.user.service.user;\n\n\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.user.model.vo.ProfileVo;\n\n\npublic interface UserInfoService {\n    ProfileVo getProfile();\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/UserQueryService.java",
    "content": "package com.acimage.user.service.user;\n\n\nimport com.acimage.common.model.domain.user.User;\n\npublic interface UserQueryService {\n    User getUser(long userId);\n    Boolean isUsernameExist(String username);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/UserRankService.java",
    "content": "package com.acimage.user.service.user;\n\nimport com.acimage.common.model.domain.user.User;\n\nimport java.util.List;\n\npublic interface UserRankService {\n\n    List<User> pageUsersRankByStarCount();\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/UserWriteService.java",
    "content": "package com.acimage.user.service.user;\n\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\n\npublic interface UserWriteService {\n    @Transactional\n    String updateUsername(String username);\n\n    @Deprecated\n    @Transactional(rollbackFor = Exception.class)\n    String uploadPhotoAndUpdatePhotoUrl(MultipartFile photoFile);\n\n\n    String updatePhotoUrl(String newPhotoUrl);\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/consts/KeyConstants.java",
    "content": "package com.acimage.user.service.user.consts;\n\npublic class KeyConstants {\n    public static final String STRINGKP_USER = \"acimage:users:id:\";\n    public static final String STRINGKP_USERNAME = \"acimage:users:username:\";\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/impl/LoginServiceImpl.java",
    "content": "package com.acimage.user.service.user.impl;\n\n\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport com.acimage.common.global.consts.JwtConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.exception.BusinessException;\n\nimport com.acimage.common.global.consts.HeaderKeyConstants;\nimport com.acimage.common.model.domain.community.CmtyUser;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.common.utils.SensitiveWordUtils;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.user.dao.UserDao;\nimport com.acimage.user.dao.UserPrivacyDao;\nimport com.acimage.common.model.domain.user.UserPrivacy;\nimport com.acimage.user.model.request.UserLoginReq;\nimport com.acimage.user.model.request.UserRegisterReq;\nimport com.acimage.user.mq.producer.SyncUserMqProducer;\nimport com.acimage.user.service.user.LoginService;\nimport com.acimage.common.utils.RsaUtils;\nimport com.acimage.user.service.usermsg.UserMsgWriteService;\nimport com.acimage.user.service.verify.VerifyCodeService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.concurrent.TimeUnit;\nimport java.util.regex.Pattern;\n\n@Slf4j\n@Service\npublic class LoginServiceImpl implements LoginService {\n    public static final String PASSWORD_PATTERN = \"^(?![a-zA-Z]+$)[0-9A-Za-z\\\\W]{6,16}$\";\n\n    public static final String STRINGKP_SEND_EMAIL = \"acimage:user:logins:sendEmailCode:email:\";\n\n    @Autowired\n    UserDao userDao;\n    @Autowired\n    UserPrivacyDao userPrivacyDao;\n    @Autowired\n    UserMsgWriteService userMsgWriteService;\n    @Autowired\n    TokenService tokenService;\n    @Autowired\n    SyncUserMqProducer syncUserMqProducer;\n    @Autowired\n    VerifyCodeService verifyCodeService;\n    @Autowired\n    RedisUtils redisUtils;\n\n    @Override\n    public String getPublicKey() {\n        return RsaUtils.getPublicKey();\n    }\n\n    @Override\n    public String registerUser(UserRegisterReq userRegister) {\n        //获取私钥\n        String privateKey = RsaUtils.getPrivateKey();\n        //解密并校验密码\n        String passwordDecrypt = RsaUtils.decrypt(privateKey, userRegister.getPassword());\n//        log.info(\" 解密为：{}\", passwordDecrypt);\n        if (!Pattern.matches(PASSWORD_PATTERN, passwordDecrypt)) {\n            throw new BusinessException(\"密码长度为6至16位，且只含数字、字母和特殊字符\");\n        }\n\n        //判断username是否存在\n        String username = SensitiveWordUtils.filter(userRegister.getUsername());\n        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();\n        qw.eq(User::getUsername, username);\n        User userByUsername = userDao.selectOne(qw);\n        if (userByUsername != null) {\n            log.info(\"用户：无 注册 错误：用户名{}已存在\", username);\n            throw new BusinessException(\"用户名已存在\");\n        }\n\n        //判断email是否存在\n        String email = userRegister.getEmail();\n        LambdaQueryWrapper<UserPrivacy> qwForEmail = new LambdaQueryWrapper<>();\n        qwForEmail.eq(UserPrivacy::getEmail, email);\n        UserPrivacy userByEmail = userPrivacyDao.selectOne(qwForEmail);\n        if (userByEmail != null) {\n            log.warn(\"用户注册 error：邮箱{}已存在\", email);\n            throw new BusinessException(\"email已存在\");\n        }\n\n        //生成随机盐\n        String salt = IdUtil.simpleUUID();\n        log.debug(\"随机盐为 {}\", salt);\n        //利用随机盐进行密钥摘要或加密\n        String passwordDigest = DigestUtil.md5Hex(salt + passwordDecrypt);\n        log.debug(\" md5摘要为：{}\", passwordDigest);\n\n        //生成用户id\n        long userId = IdGenerator.getSnowflakeNextId();\n        log.debug(\"雪花算法生成id为：{}\", userId);\n\n        //插入用户\n        User insertedUser = new User(userId, username);\n        userDao.insert(insertedUser);\n        UserPrivacy userPrivacy = new UserPrivacy(userId, passwordDigest, salt, email);\n        userPrivacyDao.insert(userPrivacy);\n        userMsgWriteService.insert(userId);\n\n\n        //返回token\n        String defaultPhotoUrl = \"\";\n        //mq发送消息，同步数据\n        CmtyUser cmtyUser = new CmtyUser();\n        cmtyUser.setId(userId);\n        cmtyUser.setUsername(username);\n        cmtyUser.setPhotoUrl(defaultPhotoUrl);\n        syncUserMqProducer.sendAddUserMessage(cmtyUser);\n\n        return tokenService.createAndRecordToken(userId, username, defaultPhotoUrl, JwtConstants.USER_EXPIRE_DAYS);\n\n    }\n\n    @Override\n    public String login(UserLoginReq userLogin) {\n        String email = userLogin.getEmail();\n        String password = userLogin.getPassword();\n\n        LambdaQueryWrapper<UserPrivacy> qw = new LambdaQueryWrapper<>();\n        qw.eq(UserPrivacy::getEmail, email);\n        UserPrivacy userPrivacy = userPrivacyDao.selectOne(qw);\n        if (userPrivacy == null) {\n            throw new BusinessException(\"邮箱不存在\");\n        }\n\n        //找到密码\n        long userId = userPrivacy.getUserId();\n        String salt = userPrivacy.getSalt();\n        String passwordDigest = userPrivacy.getPwd();\n\n        //获取私钥\n        String privateKey = RsaUtils.getPrivateKey();\n        //解密密码\n        String passwordDecrypt = RsaUtils.decrypt(privateKey, password);\n//        log.info(\" 解密为：{}\", passwordDecrypt);\n        //判断密码正确性\n        if (!DigestUtil.md5Hex(salt + passwordDecrypt).equals(passwordDigest)) {\n            log.warn(\"登录 错误：邮箱{} 或密码错误\", email);\n            throw new BusinessException(\"用户名或密码错误\");\n        }\n\n        //返回token\n        User user = userDao.selectById(userId);\n        return tokenService.createAndRecordToken(userId, user.getUsername(), user.getPhotoUrl(), JwtConstants.USER_EXPIRE_DAYS);\n\n    }\n\n\n    @Override\n    public void logout(HttpServletRequest request) {\n        //获取cookie中的token\n        String token = request.getHeader(HeaderKeyConstants.AUTHORIZATION);\n        tokenService.invalidate(token);\n    }\n\n    @Override\n    public void checkAndSendCodeToEmail(String email) {\n        if (redisUtils.getForString(STRINGKP_SEND_EMAIL + email) != null) {\n            log.info(\"ip:{} 已发送过邮箱验证码了\", UserContext.getIp());\n            throw new BusinessException(\"email验证码已发送过了，每30s可尝试一次\");\n        }\n\n        //检查邮箱是否存在\n        LambdaQueryWrapper<UserPrivacy> qw = new LambdaQueryWrapper<>();\n        qw.select(UserPrivacy::getEmail).eq(UserPrivacy::getEmail, email);\n        if (userPrivacyDao.selectOne(qw) != null) {\n            throw new BusinessException(\"邮箱已存在\");\n        }\n        verifyCodeService.sendVerifyCodeToEmail(email, UserRegisterReq.VERIFY_CODE_LENGTH);\n        long timeout = 30;\n\n        redisUtils.setAsString(STRINGKP_SEND_EMAIL + email, \"0\", timeout, TimeUnit.SECONDS);\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/impl/UserInfoServiceImpl.java",
    "content": "package com.acimage.user.service.user.impl;\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.community.CmtyUser;\n\nimport com.acimage.common.redis.annotation.QueryRedis;\n\nimport com.acimage.feign.client.CmtyUserClient;\nimport com.acimage.user.dao.UserDao;\nimport com.acimage.user.dao.UserPrivacyDao;\nimport com.acimage.common.model.domain.user.UserPrivacy;\nimport com.acimage.user.model.vo.ProfileVo;\nimport com.acimage.user.service.user.UserInfoService;\nimport com.acimage.user.service.user.UserQueryService;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.TimeUnit;\n\n@Service\n@Slf4j\npublic class UserInfoServiceImpl implements UserInfoService {\n    @Autowired\n    UserQueryService userQueryService;\n    @Autowired\n    UserDao userDao;\n    @Autowired\n    UserPrivacyDao userPrivacyDao;\n    @Autowired\n    CmtyUserClient cmtyUserClient;\n\n    @QueryRedis(keyPrefix = \"acimage:users:profile:userId:\", expire = 2L, unit = TimeUnit.SECONDS)\n    @Override\n    public ProfileVo getProfile() {\n        CmtyUser cmtyUser = cmtyUserClient.queryCmtyUser(UserContext.getUserId()).getData();\n\n        ProfileVo profileVo = new ProfileVo();\n        profileVo.setStarCount(cmtyUser.getStarCount());\n        profileVo.setTopicCount(cmtyUser.getTopicCount());\n\n        LambdaQueryWrapper<UserPrivacy> qw = new LambdaQueryWrapper<>();\n        qw.eq(UserPrivacy::getUserId, UserContext.getUserId());\n        UserPrivacy userPrivacy = userPrivacyDao.selectOne(qw);\n\n        profileVo.setEmail(userPrivacy.getEmail());\n        profileVo.setRegisterTime(userPrivacy.getRegisterTime());\n        profileVo.setUsername(UserContext.getUsername());\n\n        return profileVo;\n    }\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/impl/UserQueryServiceImpl.java",
    "content": "package com.acimage.user.service.user.impl;\n\nimport com.acimage.common.global.exception.BusinessException;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.redis.annotation.KeyParam;\nimport com.acimage.common.redis.annotation.QueryRedis;\n\nimport com.acimage.user.dao.UserDao;\nimport com.acimage.user.service.user.UserQueryService;\nimport com.acimage.user.service.user.consts.KeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Service\npublic class UserQueryServiceImpl implements UserQueryService {\n    @Autowired\n    UserDao userDao;\n\n    @QueryRedis(keyPrefix = KeyConstants.STRINGKP_USER, expire = 37L)\n    @Override\n    public User getUser(@KeyParam long userId) {\n        User user = userDao.selectById(userId);\n        if (user == null) {\n            log.error(\"用户:{} 查询 用户{} 错误：用户不存在\", UserContext.getUsername(), userId);\n            throw new BusinessException(\"该用户不存在\");\n        }\n        return user;\n    }\n\n    @QueryRedis(keyPrefix = KeyConstants.STRINGKP_USERNAME, expire = 17L, unit = TimeUnit.SECONDS)\n    @Override\n    public Boolean isUsernameExist(@KeyParam String username) {\n        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();\n        qw.eq(User::getUsername, username);\n        return userDao.selectOne(qw) != null;\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/impl/UserRankServiceImpl.java",
    "content": "package com.acimage.user.service.user.impl;\n\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.user.service.user.UserRankService;\n\n\nimport java.util.List;\n\npublic class UserRankServiceImpl implements UserRankService {\n    @Override\n    public List<User> pageUsersRankByStarCount() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/user/impl/UserWriteServiceImpl.java",
    "content": "package com.acimage.user.service.user.impl;\n\n\nimport cn.hutool.core.util.StrUtil;\nimport com.acimage.common.global.consts.JwtConstants;\nimport com.acimage.common.global.context.UserContext;\n\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.model.mq.dto.UserIdWithPhotoUrl;\nimport com.acimage.common.model.mq.dto.UserIdWithUsername;\nimport com.acimage.common.utils.SensitiveWordUtils;\nimport com.acimage.common.utils.common.FileUtils;\nimport com.acimage.common.utils.IdGenerator;\nimport com.acimage.common.deprecated.QiniuUtils;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.user.dao.UserDao;\nimport com.acimage.user.global.consts.StorePrefixConst;\nimport com.acimage.user.mq.producer.SyncUserMqProducer;\nimport com.acimage.user.service.user.UserWriteService;\nimport com.acimage.user.service.user.consts.KeyConstants;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Date;\n\n\n@Slf4j\n@Service\npublic class UserWriteServiceImpl implements UserWriteService {\n\n    @Autowired\n    UserDao userDao;\n    @Autowired\n    TokenService tokenService;\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired(required = false)\n    QiniuUtils qiniuUtils;\n    @Autowired\n    SyncUserMqProducer syncUserMqProducer;\n\n    @Override\n    public String updateUsername(String username) {\n        String filterUsername=SensitiveWordUtils.filter(username);\n        LambdaUpdateWrapper<User> qw = new LambdaUpdateWrapper<>();\n        qw.eq(User::getId, UserContext.getUserId())\n                .set(User::getUsername, filterUsername);\n        userDao.update(null, qw);\n\n        String token = tokenService.createAndRecordToken(UserContext.getUserId(),\n                filterUsername,\n                UserContext.getPhotoUrl(),\n                JwtConstants.USER_EXPIRE_DAYS);\n        //删除redis数据\n        String key = KeyConstants.STRINGKP_USER + UserContext.getUserId();\n        redisUtils.delete(key);\n\n        //mq发送同步用户名消息\n        syncUserMqProducer.sendSyncUsernameMessage(new UserIdWithUsername(UserContext.getUserId(),filterUsername));\n        return token;\n    }\n\n    @Override\n    public String uploadPhotoAndUpdatePhotoUrl(MultipartFile photoFile) {\n        //上传头像到七牛云\n        Date now = new Date();\n        String format = FileUtils.formatOf(photoFile.getOriginalFilename());\n        String suffix = String.format(\"%s.%s\", IdGenerator.getSnowflakeNextId(), format);\n        String newPhotoUrl = qiniuUtils.generateUrl(suffix, now, StorePrefixConst.USER_PHOTO);\n\n        //更新photoUrl\n        LambdaUpdateWrapper<User> qw = new LambdaUpdateWrapper<>();\n        qw.set(User::getPhotoUrl, newPhotoUrl)\n                .eq(User::getId, UserContext.getUserId());\n        userDao.update(null, qw);\n\n        qiniuUtils.upload(photoFile, newPhotoUrl);\n        //删除旧头像\n        if (!StrUtil.isEmpty(UserContext.getPhotoUrl())) {\n            qiniuUtils.deleteFile(UserContext.getPhotoUrl());\n        }\n\n        //返回新凭证\n        String token = tokenService.createAndRecordToken(UserContext.getUserId(),\n                UserContext.getUsername(),\n                newPhotoUrl,\n                JwtConstants.USER_EXPIRE_DAYS);\n\n        //删除redis数据\n        String key = KeyConstants.STRINGKP_USER + UserContext.getUserId();\n        redisUtils.delete(key);\n\n        return token;\n    }\n\n    @Override\n    public String updatePhotoUrl(String newPhotoUrl) {\n        //更新photoUrl\n        LambdaUpdateWrapper<User> qw = new LambdaUpdateWrapper<>();\n        qw.set(User::getPhotoUrl, newPhotoUrl)\n                .eq(User::getId, UserContext.getUserId());\n        userDao.update(null, qw);\n\n        //删除缓存的数据\n        String key = KeyConstants.STRINGKP_USER + UserContext.getUserId();\n        redisUtils.delete(key);\n\n        //mq发送同步用户头像消息\n        syncUserMqProducer.sendSyncUserPhotoUrlMessage(new UserIdWithPhotoUrl(UserContext.getUserId(),newPhotoUrl));\n        //返回新token\n        return tokenService.createAndRecordToken(UserContext.getUserId(),\n                UserContext.getUsername(),\n                newPhotoUrl,\n                JwtConstants.USER_EXPIRE_DAYS);\n\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/usermsg/UserMsgQueryService.java",
    "content": "package com.acimage.user.service.usermsg;\n\nimport com.acimage.common.model.domain.user.UserMsg;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\n\npublic interface UserMsgQueryService {\n\n    Integer getMsgCount(long userId);\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/usermsg/UserMsgWriteService.java",
    "content": "package com.acimage.user.service.usermsg;\n\nimport com.acimage.common.model.domain.user.UserMsg;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\n\npublic interface UserMsgWriteService {\n\n    void insert(long userId);\n\n    void increaseMsg(long userId, SFunction<UserMsg, ?> column, int increment);\n\n    void resetMsgCount(long userId, SFunction<UserMsg, ?> column);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/usermsg/impl/UserMsgQueryServiceImpl.java",
    "content": "package com.acimage.user.service.usermsg.impl;\n\nimport com.acimage.common.model.domain.user.UserMsg;\nimport com.acimage.user.dao.UserMsgDao;\nimport com.acimage.user.service.usermsg.UserMsgQueryService;\nimport com.acimage.user.web.websocket.MyWebSocketHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Service\npublic class UserMsgQueryServiceImpl implements UserMsgQueryService {\n    @Autowired\n    UserMsgDao userMsgDao;\n\n    @Override\n    public Integer getMsgCount(long userId) {\n        UserMsg userMsg = userMsgDao.selectById(userId);\n        if (userMsg == null) {\n            return null;\n        }\n        int commentMsgCount = userMsg.getCommentMsgCount();\n        int starMsgCount = userMsg.getStarMsgCount();\n        return commentMsgCount + starMsgCount;\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/usermsg/impl/UserMsgWriteServiceImpl.java",
    "content": "package com.acimage.user.service.usermsg.impl;\n\nimport com.acimage.common.model.domain.user.UserMsg;\nimport com.acimage.common.utils.LambdaUtils;\nimport com.acimage.user.dao.UserMsgDao;\nimport com.acimage.user.service.usermsg.UserMsgWriteService;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserMsgWriteServiceImpl implements UserMsgWriteService {\n    @Autowired\n    UserMsgDao userMsgDao;\n\n    @Override\n    public void insert(long userId) {\n        UserMsg userMsg = new UserMsg();\n        userMsg.setUserId(userId);\n        userMsgDao.insert(userMsg);\n    }\n\n    @Override\n    public void increaseMsg(long userId, SFunction<UserMsg, ?> column, int increment) {\n        String underlineColumn = LambdaUtils.underlineColumnNameOf(column);\n        userMsgDao.increaseColumn(userId, underlineColumn, increment);\n    }\n\n    @Override\n    public void resetMsgCount(long userId, SFunction<UserMsg, ?> column) {\n        LambdaUpdateWrapper<UserMsg> uw = new LambdaUpdateWrapper<>();\n        uw.eq(UserMsg::getUserId, userId)\n                .set(UserMsg::getCommentMsgCount, 0);\n        userMsgDao.update(null, uw);\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/verify/VerifyCodeService.java",
    "content": "package com.acimage.user.service.verify;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic interface VerifyCodeService {\n    void writeCodeImageToResponseAndRecord(HttpServletRequest request, HttpServletResponse response);\n\n    boolean verifyAndRemoveIfSuccess(HttpServletRequest request, String code);\n\n    void sendVerifyCodeToEmail(String email,int length);\n\n    boolean verifyEmailByVerifyCode(String email, String code);\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/service/verify/impl/VerifyCodeServiceImpl.java",
    "content": "package com.acimage.user.service.verify.impl;\n\nimport cn.hutool.captcha.CaptchaUtil;\nimport cn.hutool.captcha.ShearCaptcha;\nimport cn.hutool.core.util.RandomUtil;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.user.service.mail.MainService;\nimport com.acimage.user.service.verify.VerifyCodeService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.awt.*;\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\n\n@Slf4j\n@Service\npublic class VerifyCodeServiceImpl implements VerifyCodeService {\n    public static final String STRINGKP_VERIFY_CODE = \"acimage:user:verifyCode:sessionId:\";\n    public static final String STRINGKP_EMAIL_VERIFY = \"acimage:user:logins:verify:email\";\n    @Autowired\n    RedisUtils redisUtils;\n    @Autowired\n    MainService mainService;\n\n    @Override\n    public void writeCodeImageToResponseAndRecord(HttpServletRequest request, HttpServletResponse response) {\n        int width = 100;\n        int height = 40;\n        int codeCount = 4;\n        int thickness = 4;\n        /**\n         * 注意验证码需要系统有默认字体，如果使用docker alpine，会因为没有默认字体而出错\n         */\n        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(width, height, codeCount, thickness);\n        //图形验证码写出，可以写出到文件，也可以写出到流\n        ServletOutputStream outputStream=null;\n        try {\n            outputStream = response.getOutputStream();\n            captcha.write(outputStream);\n        } catch (IOException e) {\n            log.error(e.getMessage());\n        }\n\n        //获取验证码中的文字内容\n        String verifyCode = captcha.getCode();\n        //记录到redis\n        String sessionId = request.getSession().getId();\n\n        long timeout = 30L;\n\n        redisUtils.setAsString(STRINGKP_VERIFY_CODE + sessionId, verifyCode, timeout, TimeUnit.SECONDS);\n\n\n\n    }\n\n    @Override\n    public boolean verifyAndRemoveIfSuccess(HttpServletRequest request, String code) {\n        String key = STRINGKP_VERIFY_CODE + request.getSession().getId();\n\n        String trueCode = redisUtils.getForString(key);\n        if (trueCode == null) {\n            return false;\n        }\n\n        if (trueCode.equals(code)) {\n            redisUtils.delete(key);\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    @Override\n    public void sendVerifyCodeToEmail(String email, int length) {\n        String code = RandomUtil.randomString(length);\n        int timeoutMinute = 3;\n        mainService.sendVerifyCodeMailMessage(email, code, timeoutMinute);\n        String key = STRINGKP_EMAIL_VERIFY + email;\n        redisUtils.setAsString(key, code, timeoutMinute, TimeUnit.MINUTES);\n    }\n\n    @Override\n    public boolean verifyEmailByVerifyCode(String email, String code) {\n        String key = STRINGKP_EMAIL_VERIFY + email;\n        String trueCode = redisUtils.getForString(key);\n        if (trueCode == null) {\n            return false;\n        }\n\n        if (trueCode.equals(code)) {\n            redisUtils.delete(key);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/controller/LoginController.java",
    "content": "package com.acimage.user.web.controller;\n\n\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\n\nimport com.acimage.user.model.request.UserLoginReq;\nimport com.acimage.user.model.request.UserRegisterReq;\nimport com.acimage.user.service.user.LoginService;\nimport com.acimage.user.service.user.UserQueryService;\nimport com.acimage.user.service.verify.VerifyCodeService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.Size;\n\n\n@Slf4j\n@RestController\n@Validated\n@RequestMapping(\"/api/user/logins\")\npublic class LoginController {\n    @Autowired\n    VerifyCodeService verifyCodeService;\n    @Autowired\n    LoginService loginService;\n    @Autowired\n    UserQueryService userQueryService;\n\n\n\n    @RequestLimit(limitTimes = {1}, durations = {1}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/isExist/{username}\")\n    public Result<Boolean> isUsernameExist(@Size(min = 2, max = 12, message = \"用户名长度在2到12之间\") @PathVariable String username) {\n        return Result.ok(userQueryService.isUsernameExist(username));\n    }\n\n\n    @GetMapping(\"/getPublicKey\")\n    public Result<?> getPublicKey() {\n        return Result.ok(loginService.getPublicKey());\n    }\n\n    @RequestLimit(limitTimes = {1,5}, durations = {2,1}, penaltyTimes = {-1,-1}, targets = {LimitTarget.IP,LimitTarget.ALL})\n    @PostMapping(\"/sendCode\")\n    public Result<?> sendVerifyCodeToEmail(@Email @Size(min=6,max=32,message = \"邮箱长度在6到32之间\") @RequestParam(\"email\") String email,\n                                           HttpServletRequest request) {\n        loginService.checkAndSendCodeToEmail(email);\n        return Result.ok();\n    }\n\n    @RequestLimit(limitTimes = {1}, durations = {2}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @PostMapping(\"/doRegister\")\n    public Result<?> register(@Validated @RequestBody UserRegisterReq userRegister) {\n        String username=userRegister.getUsername().trim();\n        String code=userRegister.getVerifyCode().trim();\n        String email= userRegister.getEmail().trim();\n        userRegister.setVerifyCode(code);\n        userRegister.setEmail(email);\n        userRegister.setUsername(username);\n\n        if(username.length()<2){\n            return Result.fail(\"用户名有效长度不能少于2\");\n        }\n\n        if(code.length()!=UserRegisterReq.VERIFY_CODE_LENGTH){\n            return Result.fail(\"邮箱验证码错误\");\n        }\n        boolean verifyCorrect =verifyCodeService.verifyEmailByVerifyCode(email,code);\n        if(!verifyCorrect){\n            log.warn(\"邮箱验证码错误 ip:{} 邮箱:{}\", UserContext.getIp(),email);\n            return Result.fail(\"邮箱验证码错误\");\n        }\n        String token = loginService.registerUser(userRegister);\n        return Result.ok(token);\n    }\n\n    @RequestLimit(limitTimes = {1}, durations = {2}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @PostMapping(\"/doLogin\")\n    public Result<?> login(@Validated @RequestBody UserLoginReq userLogin, HttpServletRequest request) {\n        String code = userLogin.getVerifyCode();\n        boolean verifyCorrect = verifyCodeService.verifyAndRemoveIfSuccess(request, code);\n        if (!verifyCorrect) {\n            return Result.fail(\"验证码错误，请重新验证\");\n        }\n\n        String token = loginService.login(userLogin);\n        return Result.ok(token);\n    }\n\n    @PostMapping(\"/logout\")\n    public Result<?> logout(HttpServletRequest request) {\n        loginService.logout(request);\n        return Result.ok();\n    }\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/controller/MessageController.java",
    "content": "package com.acimage.user.web.controller;\n\n\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.model.domain.user.UserMsg;\nimport com.acimage.common.result.Result;\nimport com.acimage.user.service.commentmsg.CommentMsgQueryService;\nimport com.acimage.user.service.commentmsg.CommentMsgWriteService;\nimport com.acimage.user.service.usermsg.UserMsgWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.hibernate.validator.constraints.Range;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@Slf4j\n@RestController\n@RequestMapping(\"/api/user/messages/query\")\n@Validated\npublic class MessageController {\n    @Autowired\n    CommentMsgQueryService commentMsgQueryService;\n    @Autowired\n    UserMsgWriteService userMsgWriteService;\n\n    @GetMapping(\"/comments/page/{pageNo}/{pageSize}\")\n    public Result<?> pageCommentMessages(@Range(min = 1, max = 100, message = \"页码在1-100之间\") @PathVariable(\"pageNo\") Integer pageNo,\n                                         @Range(min = 5, max = 10, message = \"页大小在5-10之间\") @PathVariable(\"pageSize\") Integer pageSize) {\n        userMsgWriteService.resetMsgCount(UserContext.getUserId(), UserMsg::getCommentMsgCount);\n        return Result.ok(commentMsgQueryService.pageMyCommentMsg(UserContext.getUserId(),\n                pageNo, pageSize));\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/controller/UserOperateController.java",
    "content": "package com.acimage.user.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.user.model.vo.ProfileVo;\nimport com.acimage.user.service.user.UserInfoService;\nimport com.acimage.user.service.user.UserQueryService;\nimport com.acimage.user.service.user.UserWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.Size;\n\n@Slf4j\n@RestController\n@RequestMapping(\"/api/user/users/operate\")\n@Validated\n@Authentication\npublic class UserOperateController {\n    @Autowired\n    UserWriteService userWriteService;\n\n    @RequestLimit(limitTimes = {1}, durations = {2}, penaltyTimes = {-1}, targets = {LimitTarget.USER})\n    @PutMapping(\"/username/{username}\")\n    public Result<?> modifyUsername(@Size(min = 2, max = 12, message = \"用户名长度在2到12之间\") @PathVariable String username, HttpServletResponse resp) {\n        String trimUsername = username.trim();\n        if (trimUsername.length() < 2 || trimUsername.length() > 12) {\n            return Result.fail(\"用户名有效长度在2-12之间\");\n        }\n        String newToken = userWriteService.updateUsername(trimUsername);\n        return Result.ok(newToken);\n    }\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/controller/UserQueryController.java",
    "content": "package com.acimage.user.web.controller;\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.redis.annotation.RequestLimit;\nimport com.acimage.common.redis.enums.LimitTarget;\nimport com.acimage.common.result.Result;\nimport com.acimage.user.model.vo.ProfileVo;\nimport com.acimage.user.service.user.UserInfoService;\nimport com.acimage.user.service.user.UserQueryService;\nimport com.acimage.user.service.user.UserWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.validation.constraints.Size;\n\n@Slf4j\n@RestController\n@RequestMapping(\"/api/user/users/query\")\n@Validated\npublic class UserQueryController {\n\n    @Autowired\n    UserInfoService userInfoService;\n\n    @RequestLimit(limitTimes = {15}, durations = {5}, penaltyTimes = {-1}, targets = {LimitTarget.IP})\n    @GetMapping(\"/me\")\n    public Result<ProfileVo> me() {\n        return Result.ok(userInfoService.getProfile());\n    }\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/controller/VerifyCodeController.java",
    "content": "package com.acimage.user.web.controller;\n\n\nimport com.acimage.common.result.Result;\nimport com.acimage.user.service.verify.VerifyCodeService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n\n@Slf4j\n@RestController\n@Validated\n@RequestMapping(\"/api/user/verifies\")\npublic class VerifyCodeController {\n    @Autowired\n    VerifyCodeService verifyCodeService;\n\n\n    @GetMapping(\"/commonCode\")\n    public Result getCommonVerifyCode(HttpServletRequest request,HttpServletResponse response) {\n        verifyCodeService.writeCodeImageToResponseAndRecord(request,response);\n        return Result.ok();\n    }\n\n\n\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/provider/UserProvider.java",
    "content": "package com.acimage.user.web.provider;\n\n\n\nimport com.acimage.common.deprecated.annotation.Authentication;\nimport com.acimage.common.global.enums.AuthenticationType;\nimport com.acimage.common.model.domain.user.User;\nimport com.acimage.common.result.Result;\nimport com.acimage.user.service.user.UserQueryService;\nimport com.acimage.user.service.user.UserWriteService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Positive;\n\n@Slf4j\n@RestController\n@RequestMapping(\"/user/users\")\n@Validated\npublic class UserProvider {\n    @Autowired\n    UserQueryService userQueryService;\n    @Autowired\n    UserWriteService userWriteService;\n\n    @GetMapping(\"/id/{id}\")\n    public Result<User> queryUser(@PathVariable @Positive Long id) {\n        User user = userQueryService.getUser(id);\n        return Result.ok(user);\n    }\n\n    @PutMapping(\"/photoUrl\")\n    Result<String> modifyPhotoUrl(@RequestBody @NotNull String photoUrl){\n        String newToken=userWriteService.updatePhotoUrl(photoUrl);\n        return Result.ok(newToken);\n    }\n}\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/websocket/MyHandshakeInterceptor.java",
    "content": "package com.acimage.user.web.websocket;\n\nimport com.acimage.common.global.consts.HeaderKeyConstants;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.global.exception.NullTokenException;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.IpUtils;\nimport com.acimage.common.utils.JwtUtils;\nimport com.acimage.user.global.consts.WebSocketSessionConstants;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.auth0.jwt.exceptions.TokenExpiredException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.server.HandshakeInterceptor;\n\nimport java.util.*;\n\n@Slf4j\n@Component\npublic class MyHandshakeInterceptor implements HandshakeInterceptor {\n    @Autowired\n    TokenService tokenService;\n\n    /**\n     * 握手之前，若返回false，则不建立链接 *\n     *\n     * @return\n     */\n    @Override\n    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,\n                                   WebSocketHandler wsHandler, Map<String, Object> attributes) {\n\n        //将用户id放入socket处理器的会话(WebSocketSession)中\n        ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;\n        //获取参数\n        String token = serverHttpRequest.getServletRequest().getHeader(HeaderKeyConstants.SEC_WEBSOCKET_PROTOCOL);\n        try {\n            JwtUtils.verifyToken(token);\n        } catch (TokenExpiredException e1) {\n            Date date = JwtUtils.getExpire(token);\n            //过时毫秒数\n            long expireMillis = System.currentTimeMillis() - date.getTime();\n            //过时限制不超过10s,可以继续解析token\n            long boundMillis = 10 * 1000;\n            if (expireMillis > boundMillis) {\n                return false;\n            }\n        } catch (JWTVerificationException e2) {\n            if (!(e2 instanceof NullTokenException)) {\n                log.warn(\"非法token\");\n            }\n            return false;\n        }\n\n        if (!tokenService.hasRecorded(token)) {\n            return false;\n        }\n\n        //设置当前用户信息\n        attributes.put(WebSocketSessionConstants.KEY_USER_ID, JwtUtils.getUserId(token));\n        response.getHeaders().put(HeaderKeyConstants.SEC_WEBSOCKET_PROTOCOL, Collections.singletonList(token));\n        return true;\n    }\n\n    @Override\n    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse\n            response, WebSocketHandler wsHandler, Exception exception) {\n\n    }\n\n\n}\n\n"
  },
  {
    "path": "acimage_user/src/main/java/com/acimage/user/web/websocket/MyWebSocketHandler.java",
    "content": "package com.acimage.user.web.websocket;\n\nimport cn.hutool.core.convert.Convert;\nimport com.acimage.common.global.context.UserContext;\nimport com.acimage.common.service.TokenService;\nimport com.acimage.common.utils.redis.RedisUtils;\nimport com.acimage.user.global.consts.WebSocketSessionConstants;\nimport com.acimage.user.service.usermsg.UserMsgQueryService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.checkerframework.checker.units.qual.A;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.socket.*;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\nimport javax.websocket.Session;\nimport java.io.IOException;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n@Slf4j\n@Component\npublic class MyWebSocketHandler extends TextWebSocketHandler {\n\n    public static final String WEBSOCKET_KEY = \"acimage:user:websocket:online\";\n    @Autowired\n    private RedisUtils redisUtils;\n    @Autowired\n    private UserMsgQueryService userMsgQueryService;\n    public static final ConcurrentMap<Long, WebSocketSession> sessionPool = new ConcurrentHashMap<>();\n\n    @Override\n    public void afterConnectionEstablished(WebSocketSession session) throws Exception {\n        long userId = getUserId(session);\n        redisUtils.increment(WEBSOCKET_KEY, 1);\n        log.info(\"websocket消息: 有新的连接，总数为:{} 用户id:{}\", redisUtils.getForString(WEBSOCKET_KEY), userId);\n\n        sessionPool.put(userId, session);\n        sendMsgCount(userId);\n\n    }\n\n    @Override\n    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {\n        long userId = getUserId(session);\n        redisUtils.increment(WEBSOCKET_KEY, -1);\n        log.info(\"websocket消息: 关闭连接，总数为:\" + redisUtils.getForString(WEBSOCKET_KEY));\n\n        try (WebSocketSession webSocketSession = sessionPool.remove(userId)) {\n        }\n    }\n\n    private long getUserId(WebSocketSession session) {\n        return Convert.convert(Long.class, session.getAttributes().get(WebSocketSessionConstants.KEY_USER_ID));\n    }\n\n    public void sendMessage(long userId, int msgNum) {\n        WebSocketSession session = sessionPool.get(userId);\n        if (session != null) {\n            WebSocketMessage<?> message = new TextMessage(Integer.toString(msgNum));\n            try {\n                session.sendMessage(message);\n            } catch (IOException e) {\n                log.error(e.getMessage());\n                throw new RuntimeException(e);\n            }\n        }\n\n    }\n\n    public void sendMsgCount(long userId) {\n        Integer msgNum = userMsgQueryService.getMsgCount(userId);\n        if (msgNum != null && msgNum != 0) {\n            sendMessage(userId, msgNum);\n        }\n    }\n\n    @Override\n    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {\n        log.info(\"websocket消息: 收到客户端消息:\" + message.getPayload());\n    }\n\n}\n\n"
  },
  {
    "path": "acimage_user/src/main/resources/application-dev.yml",
    "content": "\nserver:\n  port: 8100\n\nspring:\n  config:\n    activate:\n      on-profile:\n        - dev\n  rabbitmq:\n    host: 192.168.130.128\n    port: 5672\n    virtual-host: /acimage\n    username: acimage\n    password: acimage\n    listener:\n      simple:\n        auto-startup: false #消费者是否自动启动\n      direct:\n        auto-startup: false #生产者是否自动启动\n  datasource:\n    url: jdbc:mysql://localhost:3306/acimage_user?useSSl=false&allowMultiQueries=true&serverTimezone=UTC\n    username: root\n    password: mysql\n  redis:\n    host: 192.168.130.128\n    port: 6379\n    password: redis\n    lettuce:\n      pool:\n        max-active: 8\n        max-idle: 8 #最大空闲连接\n        min-idle: 0 #最小空闲连接\n        max-wait: 100ms #连接等待时间\n  cloud:\n    nacos:\n      server-addr: localhost:8848 #nacos 服务地址"
  },
  {
    "path": "acimage_user/src/main/resources/application.yml",
    "content": "spring:\n  profiles:\n    include: common,common-secret\n    active: dev2\n  servlet:\n    multipart:\n      max-file-size: 3MB\n      max-request-size: 6MB\n  application:\n    name: user-service #服务名称\n  datasource:\n    type: com.alibaba.druid.pool.DruidDataSource\n    driver-class-name: com.mysql.cj.jdbc.Driver\n  rabbitmq:\n\nmybatis-plus:\n  mapper-locations: classpath:mapper/*.xml\n  type-aliases-package: com.acimage.common.model.domain,com.acimage.user.model.domain,\n  configuration:\n    map-underscore-to-camel-case: true\n  global-config:\n    db-config:\n      table-prefix: tb_\n\nfeign:\n  okhttp:\n    enabled: true\n  httpclient:\n    max-connections: 20 # 最大的连接数\n    max-connections-per-route: 5 # 每个路径的最大连接数\n\n"
  },
  {
    "path": "acimage_user/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- 配置文件修改时重新加载，默认true -->\n<configuration scan=\"true\">\n\n    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->\n    <springProperty scope=\"context\" name=\"spring.application.name\" source=\"spring.application.name\"/>\n    <springProperty scope=\"context\" name=\"LOG_HOME\" source=\"logback.base\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%20.20thread{20}] %40logger{40} : %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"info\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>INFO</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-info.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>8</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"warn\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>WARN</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-warn.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <appender name=\"error\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n            <level>ERROR</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>${LOG_HOME}/${spring.application.name}/%d{yyyy-MM-dd}-error.%i.log</FileNamePattern>\n            <MaxFileSize>5MB</MaxFileSize>\n            <maxHistory>15</maxHistory>\n        </rollingPolicy>\n        <encoder charset=\"UTF-8\">\n            <!-- 输出日志记录格式 -->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n\n    <springProfile name=\"dev2\">\n        <logger name=\"com.acimage.user.dao\" level=\"INFO\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <logger name=\"com.acimage\" level=\"DEBUG\" additivity=\"false\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n    <springProfile name=\"prod,server\">\n        <logger name=\"com.acimage\" level=\"info\" additivity=\"false\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </logger>\n        <!-- 设置日志输出级别 -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"info\"/>\n            <appender-ref ref=\"warn\"/>\n            <appender-ref ref=\"error\"/>\n        </root>\n    </springProfile>\n\n\n</configuration>"
  },
  {
    "path": "acimage_user/src/main/resources/mapper/CommentMsgMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.user.dao.CommentMsgDao\" >\n    <resultMap id=\"commentMsgWithUser\" type=\"CommentMsg\">\n        <id property=\"commentId\" column=\"comment_id\"/>\n        <result property=\"content\" column=\"content\"/>\n        <result property=\"fromUserId\" column=\"from_user_id\"/>\n        <result property=\"toUserId\" column=\"to_user_id\"/>\n        <result property=\"topicId\" column=\"topic_id\"/>\n        <result property=\"topicTitle\" column=\"topic_title\"/>\n        <result property=\"createTime\" column=\"create_time\"/>\n        <association property=\"user\" javaType=\"User\" column=\"from_user_id\" select=\"com.acimage.user.dao.UserDao.selectById\"/>\n    </resultMap>\n\n    <select id=\"selectCommentMsgsWithUser\" resultMap=\"commentMsgWithUser\">\n        select * from tb_comment_msg where to_user_id=#{toUserId} order by comment_id desc limit #{startIndex},#{size}\n    </select>\n\n</mapper>"
  },
  {
    "path": "acimage_user/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.acimage.user.dao.UserDao\" >\n<!--    <resultMap id=\"UserInfoWithTopicCountStarCount\" type=\"UserPrivacy\">-->\n<!--        <id property=\"id\" column=\"id\"/>-->\n<!--        <result property=\"username\" column=\"username\"/>-->\n<!--        <result property=\"email\" column=\"email\"/>-->\n<!--        <result property=\"photoUrl\" column=\"photo_url\"/>-->\n<!--        <result property=\"registerTime\" column=\"register_time\"/>-->\n<!--        <association property=\"topicCount\" javaType=\"Integer\" column=\"id\" select=\"com.acimage.community.dao.TopicDao.countTopics\"/>-->\n<!--        <association property=\"starCount\" javaType=\"Integer\" column=\"id\" select=\"com.acimage.community.dao.StarDao.countStarsOwnedBy\"/>-->\n\n<!--    </resultMap>-->\n\n<!--    <select id=\"selectUserInfoWithTopicCountStarCountById\" resultMap=\"UserInfoWithTopicCountStarCount\">-->\n<!--        select * from tb_user where id=#{id}-->\n<!--    </select>-->\n\n</mapper>"
  },
  {
    "path": "acimage_user/src/test/java/com/acimage/user/AppTest.java",
    "content": "package com.acimage.user;\n\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\npublic class AppTest\n{\n    @Test\n    public void testApp ()\n    {    }\n\n\n}\n"
  },
  {
    "path": "doc/sql/.gitignore",
    "content": "/*.bat\n"
  },
  {
    "path": "doc/sql/acimage_community.sql",
    "content": "-- MySQL dump 10.13  Distrib 8.0.29, for Win64 (x86_64)\n--\n-- Host: localhost    Database: acimage_community\n-- ------------------------------------------------------\n-- Server version\t8.0.29\n\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n/*!50503 SET NAMES utf8mb4 */;\n/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;\n/*!40103 SET TIME_ZONE='+00:00' */;\n/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n\n--\n-- Table structure for table `tb_category`\n--\n\nDROP TABLE IF EXISTS `tb_category`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_category` (\n  `id` int NOT NULL AUTO_INCREMENT,\n  `label` varchar(50) NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint DEFAULT '0',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `label` (`label`)\n) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_category`\n--\n\nLOCK TABLES `tb_category` WRITE;\n/*!40000 ALTER TABLE `tb_category` DISABLE KEYS */;\nINSERT INTO `tb_category` VALUES (1,'番剧茶馆','2023-01-28 12:30:59','2023-01-28 12:30:59',0),(2,'漫画杂谈','2023-01-28 12:30:59','2023-01-28 12:30:59',0),(3,'游戏交流','2023-01-28 12:30:59','2023-01-28 12:30:59',0),(4,'新番点评','2023-01-28 12:31:39','2023-01-28 12:31:39',0),(5,'漫画推荐','2023-01-29 18:10:46','2023-01-29 18:10:46',0),(6,'美图分享','2023-01-28 12:31:56','2023-01-28 12:31:56',0),(7,'技术','2023-02-11 22:53:12','2023-02-11 22:53:12',0),(8,'生活','2023-02-12 01:03:46','2023-02-12 01:03:46',0),(9,'其它','2023-02-17 23:32:57','2023-02-17 23:32:57',0);\n/*!40000 ALTER TABLE `tb_category` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_cmty_user`\n--\n\nDROP TABLE IF EXISTS `tb_cmty_user`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_cmty_user` (\n  `id` bigint NOT NULL,\n  `username` varchar(12) NOT NULL,\n  `photo_url` varchar(60) DEFAULT NULL,\n  `topic_count` int DEFAULT '0',\n  `star_count` int DEFAULT '0',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `id` (`id`),\n  UNIQUE KEY `username` (`username`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_cmty_user`\n--\n\nLOCK TABLES `tb_cmty_user` WRITE;\n/*!40000 ALTER TABLE `tb_cmty_user` DISABLE KEYS */;\nINSERT INTO `tb_cmty_user` VALUES (1572443275490078720,'xlg','/acfile-test/userPhoto/2023/02/15/1625783460550000640.webp',20,2),(1626532367060037632,'真·站长','',1,0),(1626603310067335168,'站长','',0,0);\n/*!40000 ALTER TABLE `tb_cmty_user` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_comment`\n--\n\nDROP TABLE IF EXISTS `tb_comment`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_comment` (\n  `id` bigint NOT NULL,\n  `topic_id` bigint NOT NULL,\n  `user_id` bigint NOT NULL,\n  `content` varchar(200) DEFAULT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint DEFAULT '0',\n  PRIMARY KEY (`id`),\n  KEY `fk_comment_user_id` (`user_id`),\n  KEY `fk_comment_topic_id` (`topic_id`),\n  CONSTRAINT `fk_comment_topic_id` FOREIGN KEY (`topic_id`) REFERENCES `tb_topic` (`id`),\n  CONSTRAINT `fk_comment_user_id` FOREIGN KEY (`user_id`) REFERENCES `tb_cmty_user` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_comment`\n--\n\nLOCK TABLES `tb_comment` WRITE;\n/*!40000 ALTER TABLE `tb_comment` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_comment` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_home_carousel`\n--\n\nDROP TABLE IF EXISTS `tb_home_carousel`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_home_carousel` (\n  `id` int NOT NULL AUTO_INCREMENT,\n  `description` varchar(30) DEFAULT NULL,\n  `url` varchar(60) NOT NULL,\n  `link` varchar(100) NOT NULL,\n  `size` int DEFAULT NULL,\n  `location` int DEFAULT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_home_carousel`\n--\n\nLOCK TABLES `tb_home_carousel` WRITE;\n/*!40000 ALTER TABLE `tb_home_carousel` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_home_carousel` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_star`\n--\n\nDROP TABLE IF EXISTS `tb_star`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_star` (\n  `user_id` bigint NOT NULL,\n  `topic_id` bigint NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`user_id`,`topic_id`),\n  KEY `index_star_topic_id` (`topic_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_star`\n--\n\nLOCK TABLES `tb_star` WRITE;\n/*!40000 ALTER TABLE `tb_star` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_star` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_tag`\n--\n\nDROP TABLE IF EXISTS `tb_tag`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_tag` (\n  `id` int NOT NULL AUTO_INCREMENT,\n  `label` varchar(30) NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint DEFAULT '0',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `label` (`label`)\n) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_tag`\n--\n\nLOCK TABLES `tb_tag` WRITE;\n/*!40000 ALTER TABLE `tb_tag` DISABLE KEYS */;\nINSERT INTO `tb_tag` VALUES (1,'冒险','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(2,'恋爱','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(3,'剧情','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(4,'搞笑','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(5,'交流','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(6,'温馨','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(7,'原创','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(8,'热血','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(9,'科幻','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(10,'运动','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(11,'竞技','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(12,'吐槽','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(13,'治愈','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(14,'致郁','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(15,'音乐','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(16,'美食','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(17,'异世界','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(18,'日常','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(19,'校园','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(20,'资讯','2023-01-28 16:51:16','2023-01-28 16:51:16',0),(21,'新番','2023-01-28 16:54:03','2023-01-28 16:54:03',0),(22,'老番','2023-01-28 16:54:03','2023-01-28 16:54:03',0),(23,'技术','2023-02-12 01:34:03','2023-02-12 01:34:03',0),(24,'超能力','2023-02-15 21:25:50','2023-02-15 21:25:50',0),(25,'美少女','2023-02-15 21:26:05','2023-02-15 21:26:05',0);\n/*!40000 ALTER TABLE `tb_tag` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_tag_topic`\n--\n\nDROP TABLE IF EXISTS `tb_tag_topic`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_tag_topic` (\n  `id` bigint NOT NULL,\n  `topic_id` bigint NOT NULL,\n  `tag_id` int NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint DEFAULT '0',\n  PRIMARY KEY (`id`),\n  KEY `topic_id` (`topic_id`),\n  KEY `tag_id` (`tag_id`),\n  CONSTRAINT `tb_tag_topic_ibfk_1` FOREIGN KEY (`topic_id`) REFERENCES `tb_topic` (`id`),\n  CONSTRAINT `tb_tag_topic_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tb_tag` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_tag_topic`\n--\n\nLOCK TABLES `tb_tag_topic` WRITE;\n/*!40000 ALTER TABLE `tb_tag_topic` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_tag_topic` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_topic`\n--\n\nDROP TABLE IF EXISTS `tb_topic`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_topic` (\n  `id` bigint NOT NULL,\n  `user_id` bigint NOT NULL,\n  `title` varchar(30) NOT NULL COMMENT '长度≥4',\n  `content` varchar(600) NOT NULL,\n  `cover_image_url` varchar(100) DEFAULT NULL,\n  `star_count` int DEFAULT '0',\n  `comment_count` int DEFAULT '0',\n  `page_view` int DEFAULT '0',\n  `activity_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint DEFAULT '0',\n  `category_id` int NOT NULL,\n  PRIMARY KEY (`id`),\n  KEY `fk_topic_user_id` (`user_id`),\n  KEY `fk_topic_category_id_idx` (`category_id`),\n  CONSTRAINT `fk_topic_category_id` FOREIGN KEY (`category_id`) REFERENCES `tb_category` (`id`),\n  CONSTRAINT `fk_topic_user_id` FOREIGN KEY (`user_id`) REFERENCES `tb_cmty_user` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_topic`\n--\n\nLOCK TABLES `tb_topic` WRITE;\n/*!40000 ALTER TABLE `tb_topic` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_topic` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_topic_html`\n--\n\nDROP TABLE IF EXISTS `tb_topic_html`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_topic_html` (\n  `topic_id` bigint NOT NULL,\n  `html` varchar(5000) NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint DEFAULT '0',\n  PRIMARY KEY (`topic_id`),\n  CONSTRAINT `tb_topic_html_ibfk_1` FOREIGN KEY (`topic_id`) REFERENCES `tb_topic` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_topic_html`\n--\n\nLOCK TABLES `tb_topic_html` WRITE;\n/*!40000 ALTER TABLE `tb_topic_html` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_topic_html` ENABLE KEYS */;\nUNLOCK TABLES;\n/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\n\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n\n-- Dump completed on 2023-02-17 23:34:07\n"
  },
  {
    "path": "doc/sql/acimage_image.sql",
    "content": "-- MySQL dump 10.13  Distrib 8.0.29, for Win64 (x86_64)\n--\n-- Host: localhost    Database: acimage_image\n-- ------------------------------------------------------\n-- Server version\t8.0.29\n\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n/*!50503 SET NAMES utf8mb4 */;\n/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;\n/*!40103 SET TIME_ZONE='+00:00' */;\n/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n\n--\n-- Table structure for table `tb_image`\n--\n\nDROP TABLE IF EXISTS `tb_image`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_image` (\n  `id` bigint NOT NULL,\n  `topic_id` bigint DEFAULT NULL,\n  `url` varchar(80) NOT NULL,\n  `size` int NOT NULL,\n  `description` varchar(30) DEFAULT NULL,\n  `file_name` varchar(80) DEFAULT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint DEFAULT '0',\n  PRIMARY KEY (`id`),\n  KEY `index_image_url` (`url`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_image`\n--\n\nLOCK TABLES `tb_image` WRITE;\n/*!40000 ALTER TABLE `tb_image` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_image` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_image_hash`\n--\n\nDROP TABLE IF EXISTS `tb_image_hash`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_image_hash` (\n  `image_id` bigint NOT NULL,\n  `hash_value` bigint NOT NULL COMMENT '图片哈希值，逻辑上表示为64个比特，话题里的图片都会被哈希处理，用于识图功能',\n  `hash_sum` int NOT NULL COMMENT '哈希值的比特之和，当两张图哈希距离较小时，哈希和应该比较接近，用来过滤',\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`image_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_image_hash`\n--\n\nLOCK TABLES `tb_image_hash` WRITE;\n/*!40000 ALTER TABLE `tb_image_hash` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_image_hash` ENABLE KEYS */;\nUNLOCK TABLES;\n/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\n\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n\n-- Dump completed on 2023-02-17 23:34:07\n"
  },
  {
    "path": "doc/sql/acimage_sys.sql",
    "content": "-- MySQL dump 10.13  Distrib 8.0.29, for Win64 (x86_64)\n--\n-- Host: localhost    Database: acimage_sys\n-- ------------------------------------------------------\n-- Server version\t8.0.29\n\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n/*!50503 SET NAMES utf8mb4 */;\n/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;\n/*!40103 SET TIME_ZONE='+00:00' */;\n/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n\n--\n-- Table structure for table `tb_api`\n--\n\nDROP TABLE IF EXISTS `tb_api`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_api` (\n  `id` int NOT NULL AUTO_INCREMENT,\n  `path` varchar(200) NOT NULL,\n  `method` varchar(20) NOT NULL,\n  `permission_id` int NOT NULL,\n  `enable` tinyint DEFAULT '1',\n  `note` varchar(100) DEFAULT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `deleted` tinyint(1) DEFAULT '0',\n  PRIMARY KEY (`id`),\n  KEY `fk_api_permissionId` (`permission_id`),\n  CONSTRAINT `fk_api_permissionId` FOREIGN KEY (`permission_id`) REFERENCES `tb_permission` (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_api`\n--\n\nLOCK TABLES `tb_api` WRITE;\n/*!40000 ALTER TABLE `tb_api` DISABLE KEYS */;\nINSERT INTO `tb_api` VALUES (6,'/api/community/topics/query/**','ALL',30,1,'查询话题','2023-02-06 14:15:49','2023-02-06 14:15:49',0),(7,'/api/community/topics/operate/**','ALL',35,1,'操作话题','2023-02-06 10:24:10','2023-02-06 10:24:10',0),(9,'/api/community/*/xxx','GET',36,1,NULL,'2023-02-06 09:52:13','2023-02-06 09:52:13',1),(10,'/api/community/topics/operate','POST',11,1,'发表话题','2023-02-06 10:16:05','2023-02-06 10:16:05',0),(11,'/api/community/comments/query/**','GET',29,1,'查看话题','2023-02-06 10:24:17','2023-02-06 10:24:17',0),(12,'/api/community/comments/operate/**','ALL',34,1,'操作话题','2023-02-06 15:07:01','2023-02-06 15:07:01',0),(13,'/api/community/categories/query/**','GET',42,1,'分类查询','2023-02-06 14:21:10','2023-02-06 14:21:10',0),(14,'/api/community/tags/query/**','GET',42,1,'标签查询','2023-02-06 14:20:47','2023-02-06 14:20:47',0),(15,'/api/community/stars/query/**','GET',43,1,'查询点赞','2023-02-06 14:53:29','2023-02-06 14:53:29',0),(16,'/api/community/stars/operate/**','ALL',44,1,'star操作','2023-02-06 14:57:27','2023-02-06 14:57:27',0),(17,'/api/community/topics/search/**','ALL',36,1,'搜索话题','2023-02-11 07:08:16','2023-02-11 07:08:16',0),(18,'/api/user/logins/**','ALL',47,1,'登录注册等','2023-02-09 08:58:18','2023-02-09 08:58:18',0),(19,'/api/user/verifies/**','ALL',47,1,'请求验证码','2023-02-09 09:00:41','2023-02-09 09:00:41',0),(20,'/api/image/homeCarousels/**','GET',47,1,'轮播图','2023-02-09 13:58:41','2023-02-09 13:58:41',1),(21,'/api/image/images/operate/**','ALL',35,1,'上传话题图片','2023-02-11 07:06:38','2023-02-11 07:06:38',0),(22,'/api/user/**','GET',43,1,NULL,'2023-02-09 15:14:00','2023-02-09 15:14:00',1),(23,'/api/user/users/query/**','GET',43,1,'查询用户信息','2023-02-14 06:02:16','2023-02-14 06:02:16',0),(24,'/api/user/users/operate/**','ALL',44,1,'用户操作自身信息','2023-02-09 15:24:48','2023-02-09 15:24:48',0),(25,'/api/community/users/rank/**','GET',47,1,'查询用户排名','2023-02-11 07:07:49','2023-02-11 07:07:49',0),(26,'/api/image/photos/operate/**','ALL',44,1,'头像操作','2023-02-11 07:07:37','2023-02-11 07:07:37',0),(27,'/api/admin/**','ALL',46,1,'admin操作','2023-02-11 12:22:04','2023-02-11 12:22:04',0),(28,'/api/community/topics/query/mine/**','GET',43,1,'查询用户话题','2023-02-11 13:57:07','2023-02-11 13:57:07',0),(29,'/api/community/homeCarousels/**','GET',47,1,'查询首页','2023-02-14 06:02:38','2023-02-14 06:02:38',0),(30,'/api/image/images/searchByImage','ALL',43,1,'以图搜图','2023-02-14 09:53:50','2023-02-14 09:53:50',0),(31,'/api/community/comments/query/mine/**','GET',43,1,'查询自己话题','2023-02-14 13:39:47','2023-02-14 13:39:47',0),(32,'/api/community/stars/query/mine/**','GET',43,1,'查询自己点赞','2023-02-14 13:41:42','2023-02-14 13:41:42',0),(33,'/api/admin/authorizes/operate/**','ALL',27,1,'操作权限','2023-02-15 08:49:16','2023-02-15 08:49:16',0),(34,'/api/admin/logins/**','ALL',47,1,'管理员登录','2023-02-15 08:51:42','2023-02-15 08:51:42',0);\n/*!40000 ALTER TABLE `tb_api` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_authorize`\n--\n\nDROP TABLE IF EXISTS `tb_authorize`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_authorize` (\n  `id` int NOT NULL AUTO_INCREMENT,\n  `role_id` int NOT NULL,\n  `permission_id` int NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_role_permission` (`role_id`,`permission_id`),\n  KEY `fk_authorize_permission_id_idx` (`permission_id`),\n  CONSTRAINT `fk_authorize_permission_id` FOREIGN KEY (`permission_id`) REFERENCES `tb_permission` (`id`),\n  CONSTRAINT `tb_authorize_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `tb_role` (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=87 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_authorize`\n--\n\nLOCK TABLES `tb_authorize` WRITE;\n/*!40000 ALTER TABLE `tb_authorize` DISABLE KEYS */;\nINSERT INTO `tb_authorize` VALUES (2,2,14,'2023-01-16 22:31:31'),(3,1,15,'2023-01-16 22:32:06'),(10,3,18,'2023-01-16 22:37:00'),(12,3,21,'2023-01-16 22:37:04'),(13,3,22,'2023-01-16 22:37:05'),(14,3,24,'2023-01-16 22:37:07'),(15,2,29,'2023-01-16 23:56:39'),(16,2,30,'2023-01-16 23:56:46'),(17,1,29,'2023-01-16 23:58:10'),(18,1,30,'2023-01-16 23:58:12'),(19,3,31,'2023-01-16 23:58:18'),(20,3,32,'2023-01-16 23:58:21'),(21,3,33,'2023-01-16 23:58:26'),(22,3,26,'2023-01-16 23:58:28'),(23,6,18,'2023-01-16 23:58:55'),(25,6,31,'2023-01-16 23:58:58'),(26,6,21,'2023-01-16 23:58:59'),(27,6,22,'2023-01-16 23:59:01'),(28,6,32,'2023-01-16 23:59:02'),(29,6,24,'2023-01-16 23:59:04'),(30,6,33,'2023-01-16 23:59:06'),(31,6,26,'2023-01-16 23:59:08'),(34,6,27,'2023-02-03 22:01:50'),(36,6,11,'2023-02-03 22:02:28'),(40,2,42,'2023-02-05 23:54:08'),(41,2,36,'2023-02-05 23:55:04'),(42,1,35,'2023-02-05 23:55:41'),(43,1,36,'2023-02-05 23:55:42'),(44,1,42,'2023-02-05 23:55:45'),(45,1,34,'2023-02-05 23:56:02'),(47,7,11,'2023-02-06 18:14:56'),(48,1,43,'2023-02-06 22:54:10'),(49,1,44,'2023-02-06 22:55:41'),(51,2,47,'2023-02-09 16:55:38'),(52,1,47,'2023-02-09 16:55:45'),(53,3,47,'2023-02-09 16:55:52'),(54,6,47,'2023-02-09 16:55:56'),(55,7,47,'2023-02-09 16:56:01'),(56,6,46,'2023-02-11 20:22:30'),(57,7,35,'2023-02-11 20:23:07'),(58,7,36,'2023-02-11 20:23:09'),(59,7,30,'2023-02-11 20:23:11'),(60,7,34,'2023-02-11 20:23:12'),(61,7,29,'2023-02-11 20:23:14'),(63,7,43,'2023-02-11 20:24:10'),(64,7,44,'2023-02-11 20:24:11'),(65,7,15,'2023-02-11 20:24:18'),(66,3,15,'2023-02-11 20:24:36'),(67,3,29,'2023-02-11 20:24:37'),(68,3,34,'2023-02-11 20:24:38'),(69,3,11,'2023-02-11 20:24:40'),(70,3,30,'2023-02-11 20:24:43'),(71,3,35,'2023-02-11 20:24:44'),(72,3,36,'2023-02-11 20:24:47'),(73,3,42,'2023-02-11 20:24:48'),(74,3,43,'2023-02-11 20:25:01'),(75,3,44,'2023-02-11 20:25:04'),(76,6,29,'2023-02-11 20:25:21'),(77,6,34,'2023-02-11 20:25:23'),(78,6,15,'2023-02-11 20:25:25'),(79,6,30,'2023-02-11 20:25:26'),(80,6,35,'2023-02-11 20:25:28'),(81,6,36,'2023-02-11 20:25:29'),(82,6,42,'2023-02-11 20:25:31'),(83,6,43,'2023-02-11 20:25:35'),(84,6,44,'2023-02-11 20:25:37'),(86,1,11,'2023-02-14 22:28:44');\n/*!40000 ALTER TABLE `tb_authorize` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_permission`\n--\n\nDROP TABLE IF EXISTS `tb_permission`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_permission` (\n  `id` int NOT NULL AUTO_INCREMENT,\n  `parent_id` int DEFAULT NULL,\n  `code` varchar(50) DEFAULT NULL,\n  `note` varchar(20) DEFAULT NULL,\n  `module` tinyint NOT NULL COMMENT '是权限模块还是处于叶子节点的权限',\n  `label` varchar(20) NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`),\n  KEY `fk_tb_permission_parent_id_idx` (`parent_id`),\n  CONSTRAINT `fk_tb_permission_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `tb_permission` (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_permission`\n--\n\nLOCK TABLES `tb_permission` WRITE;\n/*!40000 ALTER TABLE `tb_permission` DISABLE KEYS */;\nINSERT INTO `tb_permission` VALUES (1,NULL,NULL,'用户社区权限模块',1,'社区','2023-01-16 16:16:53','2023-01-16 15:06:42'),(2,1,NULL,'',1,'用户','2023-01-16 16:36:49','2023-01-16 15:06:59'),(3,1,NULL,'',1,'评论','2023-01-16 16:37:48','2023-01-16 15:07:04'),(4,NULL,NULL,'',1,'管理','2023-01-16 16:43:22','2023-01-16 15:06:53'),(6,1,NULL,'',1,'话题','2023-01-16 16:46:07','2023-01-16 15:07:13'),(11,6,'topic:add','',0,'发表话题','2023-01-16 17:01:47','2023-01-16 15:07:47'),(14,2,'user:register','',0,'注册','2023-01-16 17:13:43','2023-01-16 15:08:17'),(15,2,'user:update','和用户隐私信息修改不同',0,'用户基本信息修改','2023-01-16 17:15:01','2023-01-16 15:08:38'),(16,4,NULL,'',1,'社区管理','2023-01-16 17:16:00','2023-01-16 15:51:03'),(17,16,NULL,'',1,'评论管理','2023-01-16 17:18:39','2023-01-16 15:51:10'),(18,17,'system:comment:operate','',0,'操作评论-管理','2023-01-16 17:19:38','2023-02-05 10:00:25'),(20,16,NULL,'',1,'话题管理','2023-01-16 17:21:21','2023-01-16 15:51:23'),(21,20,'system:topic:operate','',0,'操作话题-管理','2023-01-16 17:22:17','2023-02-05 09:50:20'),(22,20,'system:topic:delete','',0,'话题删除-管理','2023-01-16 17:23:26','2023-01-16 17:23:26'),(23,16,NULL,'',1,'用户管理','2023-01-16 17:25:22','2023-01-16 15:51:46'),(24,23,'system:user:operate','',0,'操作用户信息-管理','2023-01-16 17:26:16','2023-02-05 09:49:42'),(25,4,NULL,'',1,'权限管理','2023-01-16 18:18:56','2023-01-16 15:51:35'),(26,25,'auth:query','',0,'权限查看','2023-01-16 22:43:42','2023-01-16 22:43:42'),(27,25,'auth:operate','',0,'操作权限','2023-01-16 22:44:32','2023-02-05 10:01:43'),(29,3,'comment:query','',0,'查看评论','2023-01-16 22:47:19','2023-01-16 15:51:55'),(30,6,'topic:query','',0,'查看话题','2023-01-16 22:49:17','2023-01-16 15:57:03'),(31,17,'system:comment:query','',0,'查看评论-管理','2023-01-16 23:53:10','2023-01-16 15:53:25'),(32,20,'system:topic:query','',0,'查看话题-管理','2023-01-16 23:54:00','2023-01-16 15:54:19'),(33,23,'system:user:query','',0,'查看用户-管理','2023-01-16 23:55:02','2023-01-16 23:55:02'),(34,3,'comment:operate','',0,'操作评论','2023-02-05 17:44:46','2023-02-05 17:44:46'),(35,6,'topic:operate','',0,'操作话题','2023-02-05 17:48:34','2023-02-05 17:48:34'),(36,6,'topic:search','',0,'搜索话题','2023-02-05 18:02:27','2023-02-05 18:02:27'),(41,1,NULL,'',1,'总体','2023-02-05 23:51:30','2023-02-08 13:53:26'),(42,41,'visitor:base:query','',0,'基本查询','2023-02-05 23:51:54','2023-02-06 14:56:16'),(43,41,'user:base:query','',0,'注册用户基本查询权限','2023-02-06 22:52:44','2023-02-06 22:52:44'),(44,41,'user:base:operate','',0,'注册用户基本操作权限','2023-02-06 22:55:30','2023-02-06 22:55:30'),(45,4,NULL,'',1,'总体管理','2023-02-08 21:54:02','2023-02-08 21:54:02'),(46,45,'admin:operate','',0,'管理员权限','2023-02-08 21:54:37','2023-02-08 21:54:37'),(47,41,'none','',0,'无权限','2023-02-09 16:51:13','2023-02-09 16:51:13');\n/*!40000 ALTER TABLE `tb_permission` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_role`\n--\n\nDROP TABLE IF EXISTS `tb_role`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_role` (\n  `id` int NOT NULL AUTO_INCREMENT,\n  `role_name` varchar(20) NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `note` varchar(20) DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `role_name` (`role_name`)\n) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_role`\n--\n\nLOCK TABLES `tb_role` WRITE;\n/*!40000 ALTER TABLE `tb_role` DISABLE KEYS */;\nINSERT INTO `tb_role` VALUES (1,'user','2023-01-15 18:22:25','2023-01-21 14:15:59','社区用户'),(2,'visitor','2023-01-15 18:30:03','2023-01-15 18:30:03','游客'),(3,'admin','2023-01-15 18:30:15','2023-01-15 18:30:15','管理员'),(6,'superAdmin','2023-01-16 23:58:44','2023-01-16 23:58:44','超级管理员'),(7,'verifiedUser','2023-02-05 23:57:55','2023-02-05 23:57:55','认证用户');\n/*!40000 ALTER TABLE `tb_role` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_user_role`\n--\n\nDROP TABLE IF EXISTS `tb_user_role`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_user_role` (\n  `id` bigint NOT NULL,\n  `user_id` mediumtext NOT NULL,\n  `role_id` int NOT NULL,\n  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`),\n  KEY `role_id` (`role_id`),\n  CONSTRAINT `tb_user_role_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `tb_role` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_user_role`\n--\n\nLOCK TABLES `tb_user_role` WRITE;\n/*!40000 ALTER TABLE `tb_user_role` DISABLE KEYS */;\nINSERT INTO `tb_user_role` VALUES (1621894312265109504,'1572443275490078720',6,'2023-02-04 15:31:52'),(1625875967283638272,'1572443275490078720',3,'2023-02-15 15:13:33'),(1626533157371764736,'1626532367060037632',3,'2023-02-17 18:44:59'),(1626533175579238400,'1626532367060037632',6,'2023-02-17 18:45:03'),(1626603759369457664,'1626603310067335168',3,'2023-02-17 23:25:32'),(1626603780563275776,'1626603310067335168',6,'2023-02-17 23:25:37');\n/*!40000 ALTER TABLE `tb_user_role` ENABLE KEYS */;\nUNLOCK TABLES;\n/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\n\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n\n-- Dump completed on 2023-02-17 23:34:07\n"
  },
  {
    "path": "doc/sql/acimage_user.sql",
    "content": "-- MySQL dump 10.13  Distrib 8.0.29, for Win64 (x86_64)\n--\n-- Host: localhost    Database: acimage_user\n-- ------------------------------------------------------\n-- Server version\t8.0.29\n\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n/*!50503 SET NAMES utf8mb4 */;\n/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;\n/*!40103 SET TIME_ZONE='+00:00' */;\n/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n\n--\n-- Table structure for table `tb_user`\n--\n\nDROP TABLE IF EXISTS `tb_user`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_user` (\n  `id` bigint NOT NULL,\n  `username` varchar(12) NOT NULL,\n  `photo_url` varchar(60) DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `id` (`id`),\n  UNIQUE KEY `username` (`username`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_user`\n--\n\nLOCK TABLES `tb_user` WRITE;\n/*!40000 ALTER TABLE `tb_user` DISABLE KEYS */;\nINSERT INTO `tb_user` VALUES (1626532367060037632,'真·站长',NULL),(1626603310067335168,'站长',NULL);\n/*!40000 ALTER TABLE `tb_user` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_user_msg`\n--\n\nDROP TABLE IF EXISTS `tb_user_msg`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_user_msg` (\n  `user_id` bigint NOT NULL,\n  `star_msg_count` int DEFAULT '0',\n  `read_star_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  `comment_msg_count` int DEFAULT '0',\n  `read_comment_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`user_id`),\n  CONSTRAINT `tb_user_msg_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_user_msg`\n--\n\nLOCK TABLES `tb_user_msg` WRITE;\n/*!40000 ALTER TABLE `tb_user_msg` DISABLE KEYS */;\n/*!40000 ALTER TABLE `tb_user_msg` ENABLE KEYS */;\nUNLOCK TABLES;\n\n--\n-- Table structure for table `tb_user_privacy`\n--\n\nDROP TABLE IF EXISTS `tb_user_privacy`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!50503 SET character_set_client = utf8mb4 */;\nCREATE TABLE `tb_user_privacy` (\n  `user_id` bigint NOT NULL,\n  `pwd` char(32) NOT NULL,\n  `salt` char(32) NOT NULL,\n  `email` varchar(32) NOT NULL,\n  `register_time` datetime DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`user_id`),\n  UNIQUE KEY `id` (`user_id`),\n  UNIQUE KEY `email` (`email`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Dumping data for table `tb_user_privacy`\n--\n\nLOCK TABLES `tb_user_privacy` WRITE;\n/*!40000 ALTER TABLE `tb_user_privacy` DISABLE KEYS */;\nINSERT INTO `tb_user_privacy` VALUES (1626532367060037632,'b5c2e07c00c75534a925c5952f060419','794187bad2594716b375bec82f881b4f','xlg@qq.com','2023-02-17 18:41:50'),(1626603310067335168,'eb4285b7c07ddecedfa07dffa1f74b8d','db7f6862f4884ae1b30fc6b86aee5735','xlg1@qq.com','2023-02-17 23:23:44');\n/*!40000 ALTER TABLE `tb_user_privacy` ENABLE KEYS */;\nUNLOCK TABLES;\n/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\n\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n\n-- Dump completed on 2023-02-17 23:34:07\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n    <modules>\n        <module>acimage_community</module>\n        <module>acimage_image</module>\n        <module>acimage_common</module>\n        <module>acimage_gateway</module>\n        <module>acimage_admin</module>\n        <module>acimage_feign</module>\n        <module>acimage_user</module>\n    </modules>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.6.11</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <groupId>com.acimage</groupId>\n    <artifactId>acimage</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>acimage</name>\n    <description>community to topic acg images</description>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n\n        <java.version>1.8</java.version>\n        <springcloud.version>2021.0.3</springcloud.version>\n<!--                <springboot.version>2.3.9.RELEASE</springboot.version>-->\n        <springboot.version>2.6.11</springboot.version>\n        <eureka.version>2.0.1.RELEASE</eureka.version>\n        <druid.version>1.2.9</druid.version>\n        <mybatis-plus.version>3.4.1</mybatis-plus.version>\n        <nacos.version>2.2.5.RELEASE</nacos.version>\n        <sentinel.version>2021.0.1.0</sentinel.version>\n        <jwt.version>3.8.3</jwt.version>\n        <hutool.version>5.8.6</hutool.version>\n        <mysql-connector.version>8.0.30</mysql-connector.version>\n        <openfeign.version>3.1.1</openfeign.version>\n        <loadbalancer.version>3.1.1</loadbalancer.version>\n        <qiniu.version>[7.7.0, 7.10.99]</qiniu.version>\n        <fastjson.version>2.0.21</fastjson.version>\n        <dynamic-datesource.version>3.5.1</dynamic-datesource.version>\n        <minio.version>8.2.1</minio.version>\n        <sensitive-word.version>0.2.0</sensitive-word.version>\n        <toolgood-words.version>3.0.3.1</toolgood-words.version>\n        <elasticsearch.version>7.12.0</elasticsearch.version>\n        <data-elasticsearch.version>4.2.12</data-elasticsearch.version>\n        <thumbnailator.version>0.4.8</thumbnailator.version>\n        <caffeine.version>2.8.8</caffeine.version>\n<!--        <quartz.version>3.0.1</quartz.version>-->\n<!--        <quartz-scheduler.version>2.3.2</quartz-scheduler.version>-->\n\n    </properties>\n\n    <dependencies>\n\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-web</artifactId>-->\n<!--        </dependency>-->\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-devtools</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <!--lombok-->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n\n        <dependencies>\n            <!--springcloud管理依赖-->\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${springcloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <!--nacos-->\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n                <version>${nacos.version}</version>\n            </dependency>\n            <!--nacos管理依赖-->\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${nacos.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <!--sentinel-->\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n                <version>${sentinel.version}</version>\n            </dependency>\n\n            <!--七牛云管理依赖-->\n            <dependency>\n                <groupId>com.qiniu</groupId>\n                <artifactId>qiniu-java-sdk</artifactId>\n                <version>${qiniu.version}</version>\n            </dependency>\n\n            <!--\t\t<dependency>-->\n            <!--\t\t\t<groupId>org.springframework.cloud</groupId>-->\n            <!--\t\t\t<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>-->\n            <!--\t\t\t<version>${}</version>-->\n            <!--\t\t</dependency>-->\n            <!--druid-->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>druid</artifactId>\n                <version>${druid.version}</version>\n            </dependency>\n\n            <!--mybatis-plus-->\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-boot-starter</artifactId>\n                <version>${mybatis-plus.version}</version>\n            </dependency>\n            <!--jwt-->\n            <dependency>\n                <groupId>com.auth0</groupId>\n                <artifactId>java-jwt</artifactId>\n                <version>${jwt.version}</version>\n            </dependency>\n            <!--hutool-->\n            <dependency>\n                <groupId>cn.hutool</groupId>\n                <artifactId>hutool-all</artifactId>\n                <version>${hutool.version}</version>\n            </dependency>\n            <!--mysql-->\n            <dependency>\n                <groupId>mysql</groupId>\n                <artifactId>mysql-connector-java</artifactId>\n                <version>${mysql-connector.version}</version>\n            </dependency>\n            <!--feign-->\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-starter-openfeign</artifactId>\n                <version>${openfeign.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-starter-loadbalancer</artifactId>\n                <version>${loadbalancer.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.github.ben-manes.caffeine</groupId>\n                <artifactId>caffeine</artifactId>\n                <version>${caffeine.version}</version>\n            </dependency>\n            <!--多数据源-->\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>\n                <version>${dynamic-datesource.version}</version>\n            </dependency>\n            <!--minio-->\n            <dependency>\n                <groupId>io.minio</groupId>\n                <artifactId>minio</artifactId>\n                <version>${minio.version}</version>\n            </dependency>\n            <!--敏感词过滤-->\n            <dependency>\n                <groupId>com.github.houbb</groupId>\n                <artifactId>sensitive-word</artifactId>\n            <version>${sensitive-word.version}</version>\n            </dependency>\n            <!--敏感词过滤2-->\n            <dependency>\n                <groupId>io.github.toolgood</groupId>\n                <artifactId>toolgood-words</artifactId>\n                <version>${toolgood-words.version}</version>\n            </dependency>\n            <!--elasticsearch-->\n            <dependency>\n                <groupId>org.springframework.data</groupId>\n                <artifactId>spring-data-elasticsearch</artifactId>\n                <version>${data-elasticsearch.version}</version>\n            </dependency>\n            <!--图片处理-->\n            <dependency>\n                <groupId>net.coobird</groupId>\n                <artifactId>thumbnailator</artifactId>\n                <version>${thumbnailator.version}</version>\n            </dependency>\n\n<!--            <dependency>-->\n<!--                <groupId>com.github.nintha</groupId>-->\n<!--                <artifactId>webp-imageio-core</artifactId>-->\n<!--                <version>0.1.0</version>-->\n<!--                <scope>system</scope>-->\n<!--                <systemPath>${project.basedir}/lib/webp-imageio-core-0.1.0.jar</systemPath>-->\n<!--            </dependency>-->\n\n\n\n<!--            <dependency>-->\n<!--                <groupId>org.springframework.boot</groupId>-->\n<!--                <artifactId>spring-boot-starter-quartz</artifactId>-->\n<!--                <version>${quartz.version}</version>-->\n<!--            </dependency>-->\n\n<!--            <dependency>-->\n<!--                <groupId>org.quartz-scheduler</groupId>-->\n<!--                <artifactId>quartz</artifactId>-->\n<!--                <version>${quartz-scheduler.version}</version>-->\n<!--            </dependency>-->\n\n        </dependencies>\n\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${springboot.version}</version>\n            </plugin>\n        </plugins>\n\n        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->\n            <plugins>\n                <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->\n                <plugin>\n                    <artifactId>maven-clean-plugin</artifactId>\n                    <version>3.1.0</version>\n                </plugin>\n                <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->\n                <plugin>\n                    <artifactId>maven-resources-plugin</artifactId>\n                    <version>3.0.2</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>3.8.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>2.22.2</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-jar-plugin</artifactId>\n                    <version>3.0.2</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-install-plugin</artifactId>\n                    <version>2.5.2</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-deploy-plugin</artifactId>\n                    <version>2.8.2</version>\n                </plugin>\n                <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->\n                <plugin>\n                    <artifactId>maven-site-plugin</artifactId>\n                    <version>3.7.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-project-info-reports-plugin</artifactId>\n                    <version>3.0.0</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>spring-milestones</id>\n            <name>Spring Milestones</name>\n            <url>https://repo.spring.io/milestone</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </repository>\n        <repository>\n            <id>spring-snapshots</id>\n            <name>Spring Snapshots</name>\n            <url>https://repo.spring.io/snapshot</url>\n            <releases>\n                <enabled>false</enabled>\n            </releases>\n        </repository>\n    </repositories>\n    <pluginRepositories>\n        <pluginRepository>\n            <id>spring-milestones</id>\n            <name>Spring Milestones</name>\n            <url>https://repo.spring.io/milestone</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </pluginRepository>\n        <pluginRepository>\n            <id>spring-snapshots</id>\n            <name>Spring Snapshots</name>\n            <url>https://repo.spring.io/snapshot</url>\n            <releases>\n                <enabled>false</enabled>\n            </releases>\n        </pluginRepository>\n    </pluginRepositories>\n</project>\n"
  },
  {
    "path": "vue-manage-system/.github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: https://lin-xin.gitee.io/images/weixin.jpg\n"
  },
  {
    "path": "vue-manage-system/.gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "vue-manage-system/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016-2023 vue-manage-system\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "vue-manage-system/README.md",
    "content": "# vue-manage-system\n\n<a href=\"https://github.com/vuejs/vue\">\n    <img src=\"https://img.shields.io/badge/vue-3.1.2-brightgreen.svg\" alt=\"vue\">\n  </a>\n  <a href=\"https://github.com/vuejs/pinia\">\n    <img src=\"https://img.shields.io/badge/pinia-2.0.14-brightgreen.svg\" alt=\"pinia\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/mashape/apistatus.svg\" alt=\"license\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/releases\">\n    <img src=\"https://img.shields.io/github/release/lin-xin/vue-manage-system.svg\" alt=\"GitHub release\">\n  </a>\n  <a href=\"https://lin-xin.gitee.io/example/work/#/donate\">\n    <img src=\"https://img.shields.io/badge/%24-donate-ff69b4.svg\" alt=\"donate\">\n  </a>\n\n基于 Vue3 + pinia + Element Plus 的后台管理系统解决方案。[线上地址](https://lin-xin.gitee.io/example/work/)\n\n> Vue2 版本请看 [tag-V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0)\n\n[English document](https://github.com/lin-xin/manage-system/blob/master/README_EN.md)\n\n## 赞助商\n\n### 好问\n\n[<img src=\"https://static.bestqa.net/logo/bestqa_haowen.png\" width=\"220\" height=\"100\">](https://www.bestqa.net/home/index.html)\n\n专业问卷服务，一对一客服，按需定制 \n\n## 支持作者\n\n请作者喝杯咖啡吧！(微信号：linxin_20)\n\n![微信扫一扫](https://lin-xin.gitee.io/images/weixin.jpg)\n\n## 前言\n\n该方案作为一套多功能的后台框架模板，适用于绝大部分的后台管理系统开发。基于 Vue3 + pinia + typescript，引用 Element Plus 组件库，方便开发。实现逻辑简单，适合外包项目，快速交付。\n\n## 功能\n\n-   [x] Element Plus\n-   [x] vite 3\n-   [x] pinia\n-   [x] typescript\n-   [x] 登录/注销\n-   [x] Dashboard\n-   [x] 表格\n-   [x] Tab 选项卡\n-   [x] 表单\n-   [x] 图表 :bar_chart:\n-   [x] 富文本/markdown编辑器\n-   [x] 图片拖拽/裁剪上传\n-   [x] 权限管理\n-   [x] 三级菜单\n-   [x] 自定义图标\n\n\n## 安装步骤\n> 因为使用vite3，node版本需要 14.18+\n\n```\ngit clone https://github.com/lin-xin/vue-manage-system.git      // 把模板下载到本地\ncd vue-manage-system    // 进入模板目录\nnpm install         // 安装项目依赖，等待安装完成之后，安装失败可用 cnpm 或 yarn\n\n// 运行\nnpm run dev\n\n// 执行构建命令，生成的dist文件夹放在服务器下即可访问\nnpm run build\n```\n\n## 组件使用说明与演示\n\n### vue-schart\n\nvue.js 封装 sChart.js 的图表组件。访问地址：[vue-schart](https://github.com/lin-xin/vue-schart#/) \n\n<p><a href=\"https://www.npmjs.com/package/vue-schart\"><img src=\"https://img.shields.io/npm/dm/vue-schart.svg\" alt=\"Downloads\"></a></p>\n\n```html\n<template>\n    <div>\n        <schart class=\"wrapper\" canvasId=\"myCanvas\" :options=\"options\"></schart>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport Schart from \"vue-schart\"; // 导入Schart组件\nconst options = ref({\n    type: \"bar\",\n    title: {\n        text: \"最近一周各品类销售图\",\n    },\n    labels: [\"周一\", \"周二\", \"周三\", \"周四\", \"周五\"],\n    datasets: [\n        {\n            label: \"家电\",\n            data: [234, 278, 270, 190, 230],\n        },\n        {\n            label: \"百货\",\n            data: [164, 178, 190, 135, 160],\n        },\n        {\n            label: \"食品\",\n            data: [144, 198, 150, 235, 120],\n        },\n    ],\n})\n</script>\n<style>\n    .wrapper {\n        width: 7rem;\n        height: 5rem;\n    }\n</style>\n```\n\n## 项目截图\n\n### 登录\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms3.png)\n\n### 首页\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)\n\n## License\n\n[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)\n"
  },
  {
    "path": "vue-manage-system/README_EN.md",
    "content": "# vue-manage-system\n\n<a href=\"https://github.com/vuejs/vue\">\n    <img src=\"https://img.shields.io/badge/vue-2.6.10-brightgreen.svg\" alt=\"vue\">\n  </a>\n  <a href=\"https://github.com/ElemeFE/element\">\n    <img src=\"https://img.shields.io/badge/element--ui-2.8.2-brightgreen.svg\" alt=\"element-ui\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/mashape/apistatus.svg\" alt=\"license\">\n  </a>\n  <a href=\"https://github.com/lin-xin/vue-manage-system/releases\">\n    <img src=\"https://img.shields.io/github/release/lin-xin/vue-manage-system.svg\" alt=\"GitHub release\">\n  </a>\n  <a href=\"https://lin-xin.gitee.io/example/work/#/donate\">\n    <img src=\"https://img.shields.io/badge/%24-donate-ff69b4.svg\" alt=\"donate\">\n  </a>\n\nThe web management system solution based on Vue3 and ElementPlus。[live demo](https://lin-xin.gitee.io/example/work/)\n\nPlease check the version of vue2 in [tag V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0)\n\n## Donation\n\n![WeChat](https://lin-xin.gitee.io/images/weixin.jpg)\n\n## Preface\n\nThe scheme as a set of multi-function background frame templates, suitable for most of the WEB management system development. Convenient development fast simple good components based on Vue3 and ElementPlus. Color separation of color style, support manual switch themes, and it is convenient to use a custom theme color.\n\n## Function\n\n-   [x] Element-UI\n-   [x] Login/Logout\n-   [x] Dashboard\n-   [x] Table\n-   [x] Tabs\n-   [x] From\n-   [x] Chart :bar_chart:\n-   [x] Editor\n-   [x] Markdown\n-   [x] Upload pictures by clipping or dragging\n-   [x] Permission\n-   [x] Three level menu\n-   [x] Custom icon\n\n## Installation steps\n\n    git clone https://github.com/lin-xin/vue-manage-system.git\t\t// Clone templates\n    cd vue-manage-system\t\t\t\t\t\t\t\t\t\t\t// Enter template directory\n    npm install\t\t\t\t\t\t\t\t\t\t\t\t\t// Installation dependency\n\n## Local development\n\n    npm run dev\n\n## Constructing production\n\n    // Constructing project\n    npm run build\n\n## Component description and presentation\n\n### vue-schart\n\nVue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/lin-xin/vue-schart#/)\n\n```html\n<template>\n    <div>\n        <schart class=\"wrapper\" canvasId=\"myCanvas\" :options=\"options\"></schart>\n    </div>\n</template>\n<script setup>\nimport { ref } from 'vue';\nimport Schart from \"vue-schart\"; // 导入Schart组件\nconst options = ref({\n    type: \"bar\",\n    title: {\n        text: \"最近一周各品类销售图\",\n    },\n    labels: [\"周一\", \"周二\", \"周三\", \"周四\", \"周五\"],\n    datasets: [\n        {\n            label: \"家电\",\n            data: [234, 278, 270, 190, 230],\n        },\n        {\n            label: \"百货\",\n            data: [164, 178, 190, 135, 160],\n        },\n        {\n            label: \"食品\",\n            data: [144, 198, 150, 235, 120],\n        },\n    ],\n})\n</script>\n<style>\n    .wrapper {\n        width: 7rem;\n        height: 5rem;\n    }\n</style>\n```\n\n## Screenshot\n\n### Default theme\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)\n\n### Login\n\n![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms3.png)\n\n## License\n\n[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)\n"
  },
  {
    "path": "vue-manage-system/auto-imports.d.ts",
    "content": "// Generated by 'unplugin-auto-import'\nexport {}\ndeclare global {\n\n}\n"
  },
  {
    "path": "vue-manage-system/components.d.ts",
    "content": "// generated by unplugin-vue-components\n// We suggest you to commit this file into source control\n// Read more: https://github.com/vuejs/core/pull/3399\nimport '@vue/runtime-core'\n\nexport {}\n\ndeclare module '@vue/runtime-core' {\n  export interface GlobalComponents {\n    ElAvatar: typeof import('element-plus/es')['ElAvatar']\n    ElButton: typeof import('element-plus/es')['ElButton']\n    ElCard: typeof import('element-plus/es')['ElCard']\n    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']\n    ElCol: typeof import('element-plus/es')['ElCol']\n    ElDialog: typeof import('element-plus/es')['ElDialog']\n    ElDropdown: typeof import('element-plus/es')['ElDropdown']\n    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']\n    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']\n    ElForm: typeof import('element-plus/es')['ElForm']\n    ElFormItem: typeof import('element-plus/es')['ElFormItem']\n    ElIcon: typeof import('element-plus/es')['ElIcon']\n    ElImage: typeof import('element-plus/es')['ElImage']\n    ElInput: typeof import('element-plus/es')['ElInput']\n    ElMenu: typeof import('element-plus/es')['ElMenu']\n    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']\n    ElOption: typeof import('element-plus/es')['ElOption']\n    ElPagination: typeof import('element-plus/es')['ElPagination']\n    ElProgress: typeof import('element-plus/es')['ElProgress']\n    ElRadio: typeof import('element-plus/es')['ElRadio']\n    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']\n    ElRow: typeof import('element-plus/es')['ElRow']\n    ElSelect: typeof import('element-plus/es')['ElSelect']\n    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']\n    ElTable: typeof import('element-plus/es')['ElTable']\n    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']\n    ElTag: typeof import('element-plus/es')['ElTag']\n    ElTooltip: typeof import('element-plus/es')['ElTooltip']\n    ElTree: typeof import('element-plus/es')['ElTree']\n    ElUpload: typeof import('element-plus/es')['ElUpload']\n    Header: typeof import('./src/components/header.vue')['default']\n    RouterLink: typeof import('vue-router')['RouterLink']\n    RouterView: typeof import('vue-router')['RouterView']\n    Sidebar: typeof import('./src/components/sidebar.vue')['default']\n    Tags: typeof import('./src/components/tags.vue')['default']\n  }\n}\n"
  },
  {
    "path": "vue-manage-system/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n  <title>vue-manage-system</title>\n  <link rel=\"stylesheet\" href=\"https://at.alicdn.com/t/font_830376_qzecyukz0s.css\">\n</head>\n\n<body>\n  <noscript>\n    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.\n      Please enable it to continue.</strong>\n  </noscript>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"/src/main.ts\"></script>\n  <!-- built files will be auto injected -->\n</body>\n\n</html>"
  },
  {
    "path": "vue-manage-system/package.json",
    "content": "{\n\t\"name\": \"vue-manage-system\",\n\t\"version\": \"5.3.0\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"dev\": \"vite\",\n\t\t\"build\": \"vue-tsc --noEmit && vite build\",\n\t\t\"serve\": \"vite preview\"\n\t},\n\t\"dependencies\": {\n\t\t\"@element-plus/icons-vue\": \"^2.0.9\",\n\t\t\"axios\": \"^0.27.2\",\n\t\t\"element-plus\": \"^2.2.14\",\n\t\t\"jsencrypt\": \"^3.3.1\",\n\t\t\"md-editor-v3\": \"^2.2.1\",\n\t\t\"pinia\": \"^2.0.20\",\n\t\t\"vue\": \"^3.2.37\",\n\t\t\"vue-cropperjs\": \"^5.0.0\",\n\t\t\"vue-router\": \"^4.1.3\",\n\t\t\"vue-schart\": \"^2.0.0\",\n\t\t\"wangeditor\": \"^4.7.15\",\n\t\t\"xlsx\": \"^0.18.5\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@vitejs/plugin-vue\": \"^3.0.0\",\n\t\t\"@vue/compiler-sfc\": \"^3.1.2\",\n\t\t\"typescript\": \"^4.6.4\",\n\t\t\"unplugin-auto-import\": \"^0.11.2\",\n\t\t\"unplugin-vue-components\": \"^0.22.4\",\n\t\t\"vite\": \"^3.0.0\",\n\t\t\"vite-plugin-vue-setup-extend\": \"^0.4.0\",\n\t\t\"vue-tsc\": \"^0.38.4\"\n\t},\n\t\"browserslist\": [\n\t\t\"> 1%\",\n\t\t\"last 2 versions\",\n\t\t\"not dead\"\n\t]\n}\n"
  },
  {
    "path": "vue-manage-system/public/table.json",
    "content": "{\n    \"list\": [{\n            \"id\": 1,\n            \"name\": \"张三\",\n            \"money\": 123,\n            \"address\": \"广东省东莞市长安镇\",\n            \"state\": \"成功\",\n            \"date\": \"2019-11-1\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/wms.png\"\n        },\n        {\n            \"id\": 2,\n            \"name\": \"李四\",\n            \"money\": 456,\n            \"address\": \"广东省广州市白云区\",\n            \"state\": \"成功\",\n            \"date\": \"2019-10-11\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/node3.png\"\n        },\n        {\n            \"id\": 3,\n            \"name\": \"王五\",\n            \"money\": 789,\n            \"address\": \"湖南省长沙市\",\n            \"state\": \"失败\",\n            \"date\": \"2019-11-11\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/parcel.png\"\n        },\n        {\n            \"id\": 4,\n            \"name\": \"赵六\",\n            \"money\": 1011,\n            \"address\": \"福建省厦门市鼓浪屿\",\n            \"state\": \"成功\",\n            \"date\": \"2019-10-20\",\n            \"thumb\": \"https://lin-xin.gitee.io/images/post/notice.png\"\n        }\n    ],\n    \"pageTotal\": 4\n}"
  },
  {
    "path": "vue-manage-system/src/App.vue",
    "content": "<template>\n    <el-config-provider :locale=\"zhCn\">\n        <router-view />\n    </el-config-provider>\n</template>\n\n<script setup lang=\"ts\">\nimport { ElConfigProvider } from 'element-plus';\nimport zhCn from 'element-plus/es/locale/lang/zh-cn';\n</script>\n<style>\n@import './assets/css/main.css';\n@import './assets/css/color-dark.css';\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/api/HomeCarousel.ts",
    "content": "import request from '@/utils/request'\n\n\n// export function addImageIntoHomeCarousel(reqData, _this) {\n// \t$.ajax({\n// \t\turl: \"/api/admin/SpImages/upload\",\n// \t\ttype: \"post\", //请求方式\n// \t\tdata: reqData, //请求数据\n// \t\tprocessData: false, //是否将请求数据转换为对象\n// \t\tcontentType: false, //发送数据到服务器时所使用的内容类型，false表示不需要ajax处理\n// \t\tdataType: 'json',\n// \t\tsuccess: function(result) {\n// \t\t\tif (result.code == Code.OK) {\n// \t\t\t\treturn result.data;\n// \t\t\t} else {\n// \t\t\t\t_this.$global.popupHint(result.msg);\n// \t\t\t\treturn undefined;\n// \t\t\t}\n// \t\t},\n// \t\terror: function() {\n// \t\t\t_this.$global.popupHint(\"提交失败\");\n// \t\t\treturn 'ssss';\n// \t\t}\n// \t});\n// }\n\nexport function add(formData) {\n\tconst config : any = { 'Content-type': 'multipart/form-data' };\n\treturn request.post(\"/api/admin/homeCarousels\", formData, config);\n}\n\nexport function deleteById(id) {\n\treturn request.delete(\"/api/admin/homeCarousels/\"+id);\n}\n\nexport function modifyDescription(params) {\n\tlet config = { params: params };\n\treturn request.put(\"/api/admin/homeCarousels/descriptionAndLink\", {}, config);\n}\n\nexport function coverImage(reqData) {\n\treturn request.post(\"/api/admin/homeCarousels/cover\", reqData);\n}\n\nexport function queryCurrent() {\n\treturn request.get(\"/api/admin/homeCarousels/current\");\n}\n"
  },
  {
    "path": "vue-manage-system/src/api/UserRole.ts",
    "content": "import request from '@/utils/request'\n\n//查\nexport function addRoleForUser(userId, roleId) {\n\tlet params = { userId: userId, roleId: roleId };\n\tlet config = { params: params }\n\treturn request.post(\"/api/admin/userRoles/operate\", {}, config);\n}\n\nexport function deleteRoleForUser(userId, roleId) {\n\tlet params = { userId: userId, roleId: roleId };\n\tlet config = { params: params }\n\treturn request.delete(\"/api/admin/userRoles/operate/\"+userId+'/'+roleId);\n}\n\n//操作\n\n\n\n"
  },
  {
    "path": "vue-manage-system/src/api/WebsiteData.ts",
    "content": "import request from '@/utils/request'\n\n\nexport function queryWebsiteData() {\n\treturn request.get(\"/api/admin/websites/accessData\");\n}\n\n\n\n\n"
  },
  {
    "path": "vue-manage-system/src/api/api.ts",
    "content": "import request from '@/utils/request'\n\nexport function searchApis(params) {\n\treturn request.get(\"/api/admin/apis/query/search\",{params:params});\n}\n\n\nexport function addApi(data) {\n\treturn request.post(\"/api/admin/apis/operate\",data);\n}\n\nexport function modifyApi(data) {\n\treturn request.put(\"/api/admin/apis/operate\",data);\n}\n\nexport function deleteApi(id) {\n\treturn request.delete(\"/api/admin/apis/operate/\"+id);\n}\n\n\n\n\n\n"
  },
  {
    "path": "vue-manage-system/src/api/authorize.ts",
    "content": "import request from '@/utils/request'\n\n//查\nexport function queryRoleAuthorize(roleId) {\n\treturn request.get(\"/api/admin/authorizes/roleId/\"+roleId);\n}\n\n//写\nexport function addAuthorize(formData) {\n\tlet config={params:formData}\n\treturn request.post(\"/api/admin/authorizes\",formData,config);\n}\n\nexport function deleteAuthorize(roleId,permissionId) {\n\treturn request.delete(\"/api/admin/authorizes/\"+roleId+'/'+permissionId);\n}\n"
  },
  {
    "path": "vue-manage-system/src/api/category.ts",
    "content": "import request from '@/utils/request'\n\n//查\nexport function queryAllCategories() {\n\treturn request.get(\"/api/admin/categories/all\");\n}\n\n\n"
  },
  {
    "path": "vue-manage-system/src/api/comment.ts",
    "content": "import request from '@/utils/request'\n\n//查\nexport function queryCommentsBy(params) {\n\treturn request.get(\"/api/admin/comments/query/by\", { params: params });\n}\n\n//操作\nexport function deleteComment(commentId) {\n\treturn request.delete(\"/api/admin/comments/operate/\" + commentId);\n}"
  },
  {
    "path": "vue-manage-system/src/api/index.ts",
    "content": "import request from '../utils/requestx';\n\nexport const fetchData = () => {\n    return request({\n        url: './table.json',\n        method: 'get'\n    });\n};\n"
  },
  {
    "path": "vue-manage-system/src/api/login.ts",
    "content": "import request from '@/utils/request'\n\nexport function getPublicKey(){\n\treturn request.get(\"/api/admin/logins/publicKey\");\n}\n\nexport function doLogin(data){\n\treturn request.post(\"/api/admin/logins/doLogin\",data);\n}"
  },
  {
    "path": "vue-manage-system/src/api/permission.ts",
    "content": "import request from '@/utils/request'\n\n//查\nexport function queryPermissionTree() {\n\treturn request.get(\"/api/admin/permissions/tree\");\n}\n\nexport function pagePermission(pageNo,pageSize) {\n\treturn request.get(\"/api/admin/permissions/page/\"+pageNo+'/'+pageSize);\n}\n\nexport function queryModules() {\n\treturn request.get(\"/api/admin/permissions/modules\");\n}\n\nexport function queryNonModules() {\n\treturn request.get(\"/api/admin/permissions/nonModules\");\n}\n\n//写\nexport function addPermission(data) {\n\treturn request.post(\"/api/admin/permissions\",data);\n}\n\nexport function modifyPermission(data) {\n\treturn request.put(\"/api/admin/permissions\",data);\n}\n\nexport function deletePermission(id) {\n\treturn request.delete(\"/api/admin/permissions/\"+id);\n}\n"
  },
  {
    "path": "vue-manage-system/src/api/role.ts",
    "content": "import request from '@/utils/request'\n\n\nexport function queryAllRoles() {\n\treturn request.get(\"/api/admin/roles/all\");\n}\n\nexport function addRole(data) {\n\treturn request.post(\"/api/admin/roles\", data);\n}\n\nexport function deleteRole(id) {\n\treturn request.delete(\"/api/admin/roles/\"+id);\n}\n\nexport function modifyRole(data) {\n\treturn request.put(\"/api/admin/roles\", data);\n}\n\n\n\n"
  },
  {
    "path": "vue-manage-system/src/api/topic.ts",
    "content": "import request from '@/utils/request'\n\n//查\nexport function queryTopicsOrderBy(params) {\n\treturn request.get(\"/api/admin/topics/query/orderBy\", { params: params });\n}\n\n//操作\nexport function deleteTopic(topicId) {\n\treturn request.delete(\"/api/admin/topics/operate/\" + topicId);\n}\n\n\n"
  },
  {
    "path": "vue-manage-system/src/api/user.ts",
    "content": "import request from '@/utils/request'\n\n//查\nexport function queryUsers(params) {\n\treturn request.get(\"/api/admin/users/query/search\", { params: params });\n}\n\n//操作\n\n\n\n"
  },
  {
    "path": "vue-manage-system/src/assets/css/color-dark.css",
    "content": ".header{\n    background-color: #242f42;\n}\n.login-wrap{\n    background: #324157;\n}\n.plugins-tips{\n    background: #eef1f6;\n}\n.plugins-tips a{\n    color: #20a0ff;\n}\n\n.tags-li.active {\n    border: 1px solid #409EFF;\n    background-color: #409EFF;\n}\n.message-title{\n    color: #20a0ff;\n}\n.collapse-btn:hover{\n    background: rgb(40,52,70);\n}"
  },
  {
    "path": "vue-manage-system/src/assets/css/icon.css",
    "content": "[class*=\" el-icon-lx\"],\n[class^=el-icon-lx] {\n    font-family: lx-iconfont !important;\n}"
  },
  {
    "path": "vue-manage-system/src/assets/css/main.css",
    "content": "* {\n    margin: 0;\n    padding: 0;\n}\n\nhtml,\nbody,\n#app,\n.wrapper {\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n}\n\nbody {\n    font-family: 'PingFang SC', \"Helvetica Neue\", Helvetica, \"microsoft yahei\", arial, STHeiTi, sans-serif;\n}\n\na {\n    text-decoration: none\n}\n\n\n.content-box {\n    position: absolute;\n    left: 250px;\n    right: 0;\n    top: 70px;\n    bottom: 0;\n    padding-bottom: 30px;\n    -webkit-transition: left .3s ease-in-out;\n    transition: left .3s ease-in-out;\n    background: #f0f0f0;\n}\n\n.content {\n    width: auto;\n    height: 100%;\n    padding: 10px;\n    overflow-y: scroll;\n    box-sizing: border-box;\n}\n\n.content-collapse {\n    left: 65px;\n}\n\n.container {\n    padding: 30px;\n    background: #fff;\n    border: 1px solid #ddd;\n    border-radius: 5px;\n}\n\n.crumbs {\n    margin: 10px 0;\n}\n\n.el-table th {\n    background-color: #f5f7fa !important;\n}\n\n.pagination {\n    margin: 20px 0;\n    text-align: right;\n}\n\n.plugins-tips {\n    padding: 20px 10px;\n    margin-bottom: 20px;\n}\n\n.el-button+.el-tooltip {\n    margin-left: 10px;\n}\n\n.el-table tr:hover {\n    background: #f6faff;\n}\n\n.mgb20 {\n    margin-bottom: 20px;\n}\n\n.move-enter-active,\n.move-leave-active {\n    transition: opacity .1s ease;\n}\n\n.move-enter-from,\n.move-leave-to {\n    opacity: 0;\n}\n\n/*BaseForm*/\n\n.form-box {\n    width: 600px;\n}\n\n.form-box .line {\n    text-align: center;\n}\n\n.el-time-panel__content::after,\n.el-time-panel__content::before {\n    margin-top: -7px;\n}\n\n.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {\n    padding-bottom: 0;\n}\n\n\n[class*=\" el-icon-\"], [class^=el-icon-] {\n    speak: none;\n    font-style: normal;\n    font-weight: 400;\n    font-variant: normal;\n    text-transform: none;\n    line-height: 1;\n    vertical-align: baseline;\n    display: inline-block;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n.el-sub-menu [class^=el-icon-] {\n    vertical-align: middle;\n    margin-right: 5px;\n    width: 24px;\n    text-align: center;\n    font-size: 18px;\n}\n\n[hidden]{\n    display: none !important;\n}"
  },
  {
    "path": "vue-manage-system/src/components/header.vue",
    "content": "<template>\n\t<div class=\"header\">\n\t\t<!-- 折叠按钮 -->\n\t\t<div class=\"collapse-btn\" @click=\"collapseChage\">\n\t\t\t<el-icon v-if=\"sidebar.collapse\"><Expand /></el-icon>\n\t\t\t<el-icon v-else><Fold /></el-icon>\n\t\t</div>\n\t\t<div class=\"logo\">后台管理系统</div>\n\t\t<div class=\"header-right\">\n\t\t\t<div class=\"header-user-con\">\n\t\t\t\t<!-- 消息中心 -->\n\t\t\t\t<div class=\"btn-bell\" @click=\"router.push('/tabs')\">\n\t\t\t\t\t<el-tooltip\n\t\t\t\t\t\teffect=\"dark\"\n\t\t\t\t\t\t:content=\"message ? `有${message}条未读消息` : `消息中心`\"\n\t\t\t\t\t\tplacement=\"bottom\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<i class=\"el-icon-lx-notice\"></i>\n\t\t\t\t\t</el-tooltip>\n\t\t\t\t\t<span class=\"btn-bell-badge\" v-if=\"message\"></span>\n\t\t\t\t</div>\n\t\t\t\t<!-- 用户头像 -->\n\t\t\t\t<el-avatar class=\"user-avator\" :size=\"30\" :src=\"imgurl\" />\n\t\t\t\t<!-- 用户名下拉菜单 -->\n\t\t\t\t<el-dropdown class=\"user-name\" trigger=\"click\" @command=\"handleCommand\">\n\t\t\t\t\t<span class=\"el-dropdown-link\">\n\t\t\t\t\t\t{{ username }}\n\t\t\t\t\t\t<el-icon class=\"el-icon--right\">\n\t\t\t\t\t\t\t<arrow-down />\n\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t</span>\n\t\t\t\t\t<template #dropdown>\n\t\t\t\t\t\t<el-dropdown-menu>\n\t\t\t\t\t\t\t<a href=\"https://github.com/lin-xin/vue-manage-system\" target=\"_blank\">\n\t\t\t\t\t\t\t\t<el-dropdown-item>项目仓库</el-dropdown-item>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<el-dropdown-item command=\"user\">个人中心</el-dropdown-item>\n\t\t\t\t\t\t\t<el-dropdown-item divided command=\"loginout\">退出登录</el-dropdown-item>\n\t\t\t\t\t\t</el-dropdown-menu>\n\t\t\t\t\t</template>\n\t\t\t\t</el-dropdown>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n<script setup lang=\"ts\">\nimport { onMounted } from 'vue';\nimport { useSidebarStore } from '../store/sidebar';\nimport { useRouter } from 'vue-router';\nimport {useStore} from '@/store/store'\nimport imgurl from '../assets/img/img.jpg';\n\nconst username: string | null = localStorage.getItem('ms_username');\nconst message: number = 2;\n\nconst sidebar = useSidebarStore();\n// 侧边栏折叠\nconst collapseChage = () => {\n\tsidebar.handleCollapse();\n};\n\nconst store=useStore();\n\nonMounted(() => {\n\tif (document.body.clientWidth < 1500) {\n\t\tcollapseChage();\n\t}\n});\n\n// 用户名下拉菜单选择事件\nconst router = useRouter();\nconst handleCommand = (command: string) => {\n\tif (command == 'loginout') {\n\t\tlocalStorage.removeItem('ms_username');\n\t\tstore.removeToken();\n\t\trouter.push('/login');\n\t} else if (command == 'user') {\n\t\trouter.push('/user');\n\t}\n};\n</script>\n<style scoped>\n.header {\n\tposition: relative;\n\tbox-sizing: border-box;\n\twidth: 100%;\n\theight: 70px;\n\tfont-size: 22px;\n\tcolor: #fff;\n}\n.collapse-btn {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\theight: 100%;\n\tfloat: left;\n\tpadding: 0 21px;\n\tcursor: pointer;\n}\n.header .logo {\n\tfloat: left;\n\twidth: 250px;\n\tline-height: 70px;\n}\n.header-right {\n\tfloat: right;\n\tpadding-right: 50px;\n}\n.header-user-con {\n\tdisplay: flex;\n\theight: 70px;\n\talign-items: center;\n}\n.btn-fullscreen {\n\ttransform: rotate(45deg);\n\tmargin-right: 5px;\n\tfont-size: 24px;\n}\n.btn-bell,\n.btn-fullscreen {\n\tposition: relative;\n\twidth: 30px;\n\theight: 30px;\n\ttext-align: center;\n\tborder-radius: 15px;\n\tcursor: pointer;\n\tdisplay: flex;\n\talign-items: center;\n}\n.btn-bell-badge {\n\tposition: absolute;\n\tright: 4px;\n\ttop: 0px;\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 4px;\n\tbackground: #f56c6c;\n\tcolor: #fff;\n}\n.btn-bell .el-icon-lx-notice {\n\tcolor: #fff;\n}\n.user-name {\n\tmargin-left: 10px;\n}\n.user-avator {\n\tmargin-left: 20px;\n}\n.el-dropdown-link {\n\tcolor: #fff;\n\tcursor: pointer;\n\tdisplay: flex;\n\talign-items: center;\n}\n.el-dropdown-menu__item {\n\ttext-align: center;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/components/sidebar.vue",
    "content": "<template>\n\t<div class=\"sidebar\">\n\t\t<el-menu class=\"sidebar-el-menu\" :default-active=\"onRoutes\" :collapse=\"sidebar.collapse\"\n\t\t\tbackground-color=\"#324157\" text-color=\"#bfcbd9\" active-text-color=\"#20a0ff\" unique-opened router>\n\t\t\t<template v-for=\"item in items\">\n\t\t\t\t<template v-if=\"item.subs\">\n\t\t\t\t\t<el-sub-menu :index=\"item.index\" :key=\"item.index\" v-permiss=\"item.permiss\">\n\t\t\t\t\t\t<template #title>\n\t\t\t\t\t\t\t<el-icon>\n\t\t\t\t\t\t\t\t<component :is=\"item.icon\"></component>\n\t\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t\t\t<span>{{ item.title }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-for=\"subItem in item.subs\">\n\t\t\t\t\t\t\t<el-sub-menu v-if=\"subItem.subs\" :index=\"subItem.index\" :key=\"subItem.index\"\n\t\t\t\t\t\t\t\tv-permiss=\"item.permiss\">\n\t\t\t\t\t\t\t\t<template #title>{{ subItem.title }}</template>\n\t\t\t\t\t\t\t\t<el-menu-item v-for=\"(threeItem, i) in subItem.subs\" :key=\"i\" :index=\"threeItem.index\">\n\t\t\t\t\t\t\t\t\t{{ threeItem.title }}\n\t\t\t\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t\t\t</el-sub-menu>\n\t\t\t\t\t\t\t<el-menu-item v-else :index=\"subItem.index\" v-permiss=\"item.permiss\">\n\t\t\t\t\t\t\t\t{{ subItem.title }}\n\t\t\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-sub-menu>\n\t\t\t\t</template>\n\t\t\t\t<template v-else>\n\t\t\t\t\t<el-menu-item :index=\"item.index\" :key=\"item.index\" v-permiss=\"item.permiss\">\n\t\t\t\t\t\t<el-icon>\n\t\t\t\t\t\t\t<component :is=\"item.icon\"></component>\n\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t\t<template #title>{{ item.title }}</template>\n\t\t\t\t\t</el-menu-item>\n\t\t\t\t</template>\n\t\t\t</template>\n\t\t</el-menu>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\n\timport { computed } from 'vue';\n\timport { useSidebarStore } from '../store/sidebar';\n\timport { useRoute } from 'vue-router';\n\n\n\tconst items: any = [{\n\t\t\ticon: 'Odometer',\n\t\t\tindex: '/dashboard',\n\t\t\ttitle: '系统首页',\n\t\t\tpermiss: '1',\n\t\t},\n\t\t{\n\t\t\ticon: 'Calendar',\n\t\t\tindex: '1',\n\t\t\ttitle: '首页管理',\n\t\t\tpermiss: '2',\n\t\t\tsubs: [{\n\t\t\t\tindex: '/HomeCarousel',\n\t\t\t title: '走马灯',\n\t\t\t\tpermiss: '2',\n\t\t\t}, ],\n\t\t},\n\t\t{\n\t\t\ticon: 'Calendar',\n\t\t\tindex: '2',\n\t\t\ttitle: '社区管理',\n\t\t\tpermiss: '2',\n\t\t\tsubs: [{\n\t\t\t\t\tindex: '/topic',\n\t\t\t\t\ttitle: '话题',\n\t\t\t\t\tpermiss: '2',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tindex: '/comment',\n\t\t\t\t\ttitle: '评论',\n\t\t\t\t\tpermiss: '2',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tindex: '/user',\n\t\t\t\t\ttitle: '用户',\n\t\t\t\t\tpermiss: '2',\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\t{\n\t\t\ticon: 'Edit',\n\t\t\tindex: '3',\n\t\t\ttitle: '权限管理',\n\t\t\tpermiss: '4',\n\t\t\tsubs: [{\n\t\t\t\t\tindex: '/role',\n\t\t\t\t\ttitle: '角色',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t  index: '/permission/1',\n\t\t\t\t\ttitle: '权限',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tindex: '/authorize',\n\t\t\t\t\ttitle: '授权',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\t{\n\t\t\ticon: 'Edit',\n\t\t\tindex: '34',\n\t\t\ttitle: '接口管理',\n\t\t\tpermiss: '4',\n\t\t\tsubs: [{\n\t\t\t\tindex: '/api',\n\t\t\t\ttitle: '接口',\n\t\t\t\tpermiss: '1',\n\t\t\t}, ],\n\t\t},\n\t];\n\n\tconst route = useRoute();\n\tconst onRoutes = computed(() => {\n\t\treturn route.path;\n\t});\n\n\tconst sidebar = useSidebarStore();\n</script>\n\n<style scoped>\n\t.sidebar {\n\t\tdisplay: block;\n\t\tposition: absolute;\n\t\tleft: 0;\n\t\ttop: 70px;\n\t\tbottom: 0;\n\t\toverflow-y: scroll;\n\t}\n\n\t.sidebar::-webkit-scrollbar {\n\t\twidth: 0;\n\t}\n\n\t.sidebar-el-menu:not(.el-menu--collapse) {\n\t\twidth: 250px;\n\t}\n\n\t.sidebar>ul {\n\t\theight: 100%;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/components/tags.vue",
    "content": "<template>\n\t<div class=\"tags\" v-if=\"tags.show\">\n\t\t<ul>\n\t\t\t<li\n\t\t\t\tclass=\"tags-li\"\n\t\t\t\tv-for=\"(item, index) in tags.list\"\n\t\t\t\t:class=\"{ active: isActive(item.path) }\"\n\t\t\t\t:key=\"index\"\n\t\t\t>\n\t\t\t\t<router-link :to=\"item.path\" class=\"tags-li-title\">{{ item.title }}</router-link>\n\t\t\t\t<el-icon @click=\"closeTags(index)\"><Close /></el-icon>\n\t\t\t</li>\n\t\t</ul>\n\t\t<div class=\"tags-close-box\">\n\t\t\t<el-dropdown @command=\"handleTags\">\n\t\t\t\t<el-button size=\"small\" type=\"primary\">\n\t\t\t\t\t标签选项\n\t\t\t\t\t<el-icon class=\"el-icon--right\">\n\t\t\t\t\t\t<arrow-down />\n\t\t\t\t\t</el-icon>\n\t\t\t\t</el-button>\n\t\t\t\t<template #dropdown>\n\t\t\t\t\t<el-dropdown-menu size=\"small\">\n\t\t\t\t\t\t<el-dropdown-item command=\"other\">关闭其他</el-dropdown-item>\n\t\t\t\t\t\t<el-dropdown-item command=\"all\">关闭所有</el-dropdown-item>\n\t\t\t\t\t</el-dropdown-menu>\n\t\t\t\t</template>\n\t\t\t</el-dropdown>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useTagsStore } from '../store/tags';\nimport { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';\n\nconst route = useRoute();\nconst router = useRouter();\nconst isActive = (path: string) => {\n\treturn path === route.fullPath;\n};\n\nconst tags = useTagsStore();\n// 关闭单个标签\nconst closeTags = (index: number) => {\n\tconst delItem = tags.list[index];\n\ttags.delTagsItem(index);\n\tconst item = tags.list[index] ? tags.list[index] : tags.list[index - 1];\n\tif (item) {\n\t\tdelItem.path === route.fullPath && router.push(item.path);\n\t} else {\n\t\trouter.push('/');\n\t}\n};\n\n// 设置标签\nconst setTags = (route: any) => {\n\tconst isExist = tags.list.some(item => {\n\t\treturn item.path === route.fullPath;\n\t});\n\tif (!isExist) {\n\t\tif (tags.list.length >= 8) tags.delTagsItem(0);\n\t\ttags.setTagsItem({\n\t\t\tname: route.name,\n\t\t\ttitle: route.meta.title,\n\t\t\tpath: route.fullPath\n\t\t});\n\t}\n};\nsetTags(route);\nonBeforeRouteUpdate(to => {\n\tsetTags(to);\n});\n\n// 关闭全部标签\nconst closeAll = () => {\n\ttags.clearTags();\n\trouter.push('/');\n};\n// 关闭其他标签\nconst closeOther = () => {\n\tconst curItem = tags.list.filter(item => {\n\t\treturn item.path === route.fullPath;\n\t});\n\ttags.closeTagsOther(curItem);\n};\nconst handleTags = (command: string) => {\n\tcommand === 'other' ? closeOther() : closeAll();\n};\n\n// 关闭当前页面的标签页\n// tags.closeCurrentTag({\n//     $router: router,\n//     $route: route\n// });\n</script>\n\n<style>\n.tags {\n\tposition: relative;\n\theight: 30px;\n\toverflow: hidden;\n\tbackground: #fff;\n\tpadding-right: 120px;\n\tbox-shadow: 0 5px 10px #ddd;\n}\n\n.tags ul {\n\tbox-sizing: border-box;\n\twidth: 100%;\n\theight: 100%;\n}\n\n.tags-li {\n\tdisplay: flex;\n\talign-items: center;\n\tfloat: left;\n\tmargin: 3px 5px 2px 3px;\n\tborder-radius: 3px;\n\tfont-size: 12px;\n\toverflow: hidden;\n\tcursor: pointer;\n\theight: 23px;\n\tborder: 1px solid #e9eaec;\n\tbackground: #fff;\n\tpadding: 0 5px 0 12px;\n\tcolor: #666;\n\t-webkit-transition: all 0.3s ease-in;\n\t-moz-transition: all 0.3s ease-in;\n\ttransition: all 0.3s ease-in;\n}\n\n.tags-li:not(.active):hover {\n\tbackground: #f8f8f8;\n}\n\n.tags-li.active {\n\tcolor: #fff;\n}\n\n.tags-li-title {\n\tfloat: left;\n\tmax-width: 80px;\n\toverflow: hidden;\n\twhite-space: nowrap;\n\ttext-overflow: ellipsis;\n\tmargin-right: 5px;\n\tcolor: #666;\n}\n\n.tags-li.active .tags-li-title {\n\tcolor: #fff;\n}\n\n.tags-close-box {\n\tposition: absolute;\n\tright: 0;\n\ttop: 0;\n\tbox-sizing: border-box;\n\tpadding-top: 1px;\n\ttext-align: center;\n\twidth: 110px;\n\theight: 30px;\n\tbackground: #fff;\n\tbox-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);\n\tz-index: 10;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/config.ts",
    "content": "export default{\n\tdomainOfImages:'http://rof8epeiz.hn-bkt.clouddn.com/',\n}"
  },
  {
    "path": "vue-manage-system/src/main.ts",
    "content": "import { createApp } from 'vue';\nimport { createPinia } from 'pinia';\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue';\nimport App from './App.vue';\nimport router from './router';\nimport { usePermissStore } from './store/permiss';\nimport 'element-plus/dist/index.css';\nimport './assets/css/icon.css';\n\nconst app = createApp(App);\napp.use(createPinia());\napp.use(router);\n\n// 注册elementplus图标\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n    app.component(key, component);\n}\n// 自定义权限指令\nconst permiss = usePermissStore();\napp.directive('permiss', {\n    mounted(el, binding) {\n        if (!permiss.key.includes(String(binding.value))) {\n            el['hidden'] = true;\n        }\n    },\n});\n\napp.mount('#app');\n"
  },
  {
    "path": "vue-manage-system/src/router/index.ts",
    "content": "import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';\nimport { usePermissStore } from '../store/permiss';\nimport CommonUtils from '@/utils/CommonUtils'\nimport Home from '../views/home.vue';\nimport Table from '../views/table.vue'\nimport HomeCarousel from '../views/HomeCarousel/HomeCarousel.vue'\n\nconst routes: RouteRecordRaw[] = [\n\t{\n\t\tpath: '/',\n\t\tredirect: '/dashboard',\n\t},\n\t{\n\t\tpath: '/',\n\t\tname: 'Home',\n\t\tcomponent: Home,\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tpath: '/dashboard',\n\t\t\t\tname: 'dashboard',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '系统首页',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"dashboard\" */ '../views/dashboard.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/HomeCarousel',\n\t\t\t\tname: 'HomeCarousel',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '走马灯',\n\t\t\t\t\tpermiss: '2',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"HomeCarousel\" */ '../views/HomeCarousel/HomeCarousel.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/role',\n\t\t\t\tname: 'role',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '角色',\n\t\t\t\t\tpermiss: '5',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"role\" */ '../views/role/role.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/permission/:pageNo',\n\t\t\t\tname: 'permission',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '权限',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"permission\" */ '../views/permission/permission.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/authorize',\n\t\t\t\tname: 'authorize',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '授权',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"authorize\" */ '../views/authorize/authorize.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/api',\n\t\t\t\tname: 'api',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '接口',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"api\" */ '../views/api/api.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/topic',\n\t\t\t\tname: 'topic',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '话题',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"topic\" */ '../views/topic/topic.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/user',\n\t\t\t\tname: 'user',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '用户',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"user\" */ '../views/user/user.vue'),\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/comment',\n\t\t\t\tname: 'comment',\n\t\t\t\tmeta: {\n\t\t\t\t\ttitle: '评论',\n\t\t\t\t\tpermiss: '1',\n\t\t\t\t},\n\t\t\t\tcomponent: () => import(/* webpackChunkName: \"comment\" */ '../views/comment/comment.vue'),\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tpath: '/login',\n\t\tname: 'Login',\n\t\tmeta: {\n\t\t\ttitle: '登录',\n\t\t},\n\t\tcomponent: () => import(/* webpackChunkName: \"login\" */ '../views/login.vue'),\n\t},\n\t{\n\t\tpath: '/403',\n\t\tname: '403',\n\t\tmeta: {\n\t\t\ttitle: '没有权限',\n\t\t},\n\t\tcomponent: () => import(/* webpackChunkName: \"403\" */ '../views/403.vue'),\n\t},\n];\n\nconst router = createRouter({\n\thistory: createWebHashHistory(),\n\troutes,\n});\n\nrouter.beforeEach((to, from, next) => {\n\tdocument.title = `${to.meta.title} | vue-manage-system`;\n\tconst token = localStorage.getItem('token');\n\n\tif (!!CommonUtils.isEmpty(token) && to.path !== '/login') {\n\t\tnext('/login');\n\t} /*else if (to.meta.permiss && !permiss.key.includes(to.meta.permiss)) {\n        // 如果没有权限，则进入403\n        next('/403');\n    }*/ else {\n\t\tnext();\n\t}\n});\n\nexport default router;\n"
  },
  {
    "path": "vue-manage-system/src/store/permiss.ts",
    "content": "import { defineStore } from 'pinia';\n\ninterface ObjectList {\n\t[key: string]: string[];\n}\n\nexport const usePermissStore = defineStore('permiss', {\n\tstate: () => {\n\t\tconst keys = localStorage.getItem('ms_keys');\n\t\t// const keys = localStorage.getItem('token');\n\t\treturn {\n\t\t\tkey: keys ? JSON.parse(keys) : <string[]>[],\n\t\t\tdefaultList: <ObjectList>{\n\t\t\t\tadmin: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'],\n\t\t\t\tuser: ['1', '2', '3', '11', '13', '14', '15']\n\t\t\t}\n\t\t};\n\t},\n\tactions: {\n\t\thandleSet(val: string[]) {\n\t\t\tthis.key = val;\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "vue-manage-system/src/store/sidebar.ts",
    "content": "import { defineStore } from 'pinia';\n\nexport const useSidebarStore = defineStore('sidebar', {\n\tstate: () => {\n\t\treturn {\n\t\t\tcollapse: true\n\t\t};\n\t},\n\tgetters: {},\n\tactions: {\n\t\thandleCollapse() {\n\t\t\tthis.collapse = !this.collapse;\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "vue-manage-system/src/store/store.ts",
    "content": "import { defineStore } from 'pinia';\nimport { queryAllCategories } from '@/api/category'\nimport { queryAllRoles } from '@/api/role';\nimport { Code } from '@/utils/result'\n\ninterface Category {\n\tid: number;\n\tlabel: string;\n\tcreateTime: string;\n\tupdateTime: string;\n}\n\nimport { Role } from '@/views/role/role.vue'\n\nexport const useStore = defineStore('store', {\n\tstate: () => {\n\t\treturn {\n\t\t\tcategoryList: <Category[]>[],\n\t\t\troleList: <Role[]>[],\n\t\t\ttoken:''\n\t\t};\n\t},\n\tgetters: {\n\t\tcategoryLabel(state) {\n\t\t\treturn (id) => {\n\t\t\t\tfor (let item of state.categoryList) {\n\t\t\t\t\tif (item.id == id) {\n\t\t\t\t\t\treturn item.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\troleName(state) {\n\t\t\treturn (id) => {\n\t\t\t\tfor (let item of state.roleList) {\n\t\t\t\t\tif (item.id == id) {\n\t\t\t\t\t\treturn item.roleName;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\n\t},\n\tactions: {\n\t\tinit() {\n\t\t\tlet _this = this;\n\t\t\tif (this.categoryList.length == 0) {\n\t\t\t\tqueryAllCategories().then((res: any) => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t_this.categoryList = res.data;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (this.roleList.length == 0) {\n\t\t\t\tqueryAllRoles().then((res: any) => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tthis.roleList = res.data;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\t\tsetToken(token){\n\t\t\tlocalStorage.setItem('token', token);\n\t\t},\n\t\tremoveToken(){\n\t\t\tlocalStorage.removeItem('token')\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "vue-manage-system/src/store/tags.ts",
    "content": "import { defineStore } from 'pinia';\n\ninterface ListItem {\n\tname: string;\n\tpath: string;\n\ttitle: string;\n}\n\nexport const useTagsStore = defineStore('tags', {\n\tstate: () => {\n\t\treturn {\n\t\t\tlist: <ListItem[]>[]\n\t\t};\n\t},\n\tgetters: {\n\t\tshow: state => {\n\t\t\treturn state.list.length > 0;\n\t\t},\n\t\tnameList: state => {\n\t\t\treturn state.list.map(item => item.name);\n\t\t}\n\t},\n\tactions: {\n\t\tdelTagsItem(index: number) {\n\t\t\tthis.list.splice(index, 1);\n\t\t},\n\t\tsetTagsItem(data: ListItem) {\n\t\t\tthis.list.push(data);\n\t\t},\n\t\tclearTags() {\n\t\t\tthis.list = [];\n\t\t},\n\t\tcloseTagsOther(data: ListItem[]) {\n\t\t\tthis.list = data;\n\t\t},\n\t\tcloseCurrentTag(data: any) {\n\t\t\tfor (let i = 0, len = this.list.length; i < len; i++) {\n\t\t\t\tconst item = this.list[i];\n\t\t\t\tif (item.path === data.$route.fullPath) {\n\t\t\t\t\tif (i < len - 1) {\n\t\t\t\t\t\tdata.$router.push(this.list[i + 1].path);\n\t\t\t\t\t} else if (i > 0) {\n\t\t\t\t\t\tdata.$router.push(this.list[i - 1].path);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdata.$router.push('/');\n\t\t\t\t\t}\n\t\t\t\t\tthis.list.splice(i, 1);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "vue-manage-system/src/utils/CommonUtils.ts",
    "content": "import MessageUtils from '@/utils/MessageUtils'\nimport { Code } from \"@/utils/result\"\nexport default {\n\tisEmpty(object) {\n\t\tif (object == undefined || object == '' || object == null || object == {} || object == []) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tpopMsgAndRefreshIfOk(request, msg, delaySeconds: void) {\n\t\trequest.then(result => {\n\t\t\tconsole.log(result);\n\t\t\tif (result.code == Code.OK) {\n\t\t\t\tMessageUtils.success(msg, 1);\n\t\t\t\tif (!this.isEmpty(delaySeconds)) {\n\t\t\t\t\tthis.delayRefresh(delaySeconds);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t},\n\tpopMsgIfOk(request, msg) {\n\t\tthis.popMsgAndRefreshIfOk(request, msg);\n\t},\n\n\tdelayRefresh(delaySeconds) {\n\t\tsetTimeout(() => {\n\t\t\tlocation.reload();\n\t\t}, delaySeconds * 1000);\n\t},\n\n\tgetUrlParams(url) {\n\t\tlet firstIndex = url.indexOf('?');\n\t\tif (firstIndex == -1) return;\n\t\tlet argStr = url.slice(firstIndex + 1, url.length);\n\n\t\tlet args = argStr.split(\"&\");\n\t\tlet params = {};\n\t\tfor (let i = 0; i < args.length; i++) {\n\t\t\tlet keyAndValues = args[i].split(\"=\");\n\t\t\tparams[keyAndValues[0]] = keyAndValues[1];\n\t\t}\n\t\treturn params;\n\t},\n}\n"
  },
  {
    "path": "vue-manage-system/src/utils/MessageUtils.ts",
    "content": "import {\n\tElMessageBox,\n\tElNotification\n} from 'element-plus';\nexport default{\n\tnotice(msg,durationSeconds){\n\t\tlet newDuration=durationSeconds||2;\n\t\tElNotification({\n\t\t\ttitle: '提示',\n\t\t\tmessage: msg,\n\t\t\ttype: 'warning',\n\t\t\tduration: newDuration*1000\n\t\t});\n\t},\n\tsuccess(msg,durationSeconds){\n\t\tlet newDuration=durationSeconds||4;\n\t\tElNotification({\n\t\t\ttitle: '成功',\n\t\t\tmessage: msg,\n\t\t\ttype: 'success',\n\t\t\tduration: newDuration*1000\n\t\t});\n\t},\n\tconfirm(msg){\n\t\treturn ElMessageBox.confirm(msg, '提示', {\n\t\t\tconfirmButtonText: '确定',\n\t\t\tcancelButtonText: '取消',\n\t\t\ttype: 'warning'\n\t\t})\n\t},\n\n\n}"
  },
  {
    "path": "vue-manage-system/src/utils/StringUtils.ts",
    "content": "export default {\n\thtml2Text(htmlString) {\n\t\treturn htmlString.toString().replace(/<(style|script|iframe)[^>]*?>[\\s\\S]+?<\\/\\1\\s*>/gi, '')\n\t\t\t.replace(/<[^>]+?>/g, '')\n\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t.replace(/ /g, ' ')\n\t\t\t.replace(/>/g, ' ')\n\t\t\t.replace(/&nbsp;/gm,' ');\n\t}\n}\n"
  },
  {
    "path": "vue-manage-system/src/utils/global.ts",
    "content": "import Vue from 'vue'\nimport CommonUtils from '@/utils/CommonUtils'\nimport MessageUtils from '@/utils/MessageUtils'\nimport Config from '@/config'\nimport { Code } from '@/utils/result'\nimport StringUtils from '@/utils/StringUtils'\n\nlet global: any = {};\n\n\n//消息提示功能\n// global.popupHint = function(msg) {\n// \tthis.$notify({\n// \t\ttitle: '提示',\n// \t\tmessage: msg,\n// \t\ttype: 'warning'\n// \t});\n// }\n\n// global.popupSuccess = function(msg) {\n// \tthis.$notify({\n// \t\ttitle: '成功',\n// \t\tmessage: msg,\n// \t\ttype: 'success'\n// \t});\n// }\n\n// global.popupMsgIfOk = function(request, msg) {\n// \trequest.then(result => {\n// \t\tconsole.log(result);\n// \t\tif (result.code == Code.OK) {\n// \t\t\tglobal.popupSuccess(msg);\n// \t\t}\n// \t});\n// }\n\n// global.openConfirm = function(msg) {\n// \treturn MessageBox.confirm(msg, '提示', {\n// \t\tconfirmButtonText: '确定',\n// \t\tcancelButtonText: '取消',\n// \t\ttype: 'warning'\n// \t})\n// }\n\n\n//真实图片链接\nglobal.baseImageUrl = Config.domainOfImages;\n\nglobal.trueImageUrl = function(url) {\n\tif (CommonUtils.isEmpty(url) || url == '#') {\n\t\treturn '#';\n\t}\n\treturn global.baseImageUrl + url;\n}\n\n//获取头像路径\nglobal.truePhotoUrl = function(url) {\n\tif (CommonUtils.isEmpty(url) || url == '#') {\n\t\treturn global.baseImageUrl + 'userPhoto/default.jpeg';\n\n\t}\n\treturn global.baseImageUrl + url;\n}\n\n//获取分享链接\nglobal.getTopicUrl = function(topicId: number) {\n\treturn '/topic/' + topicId;\n}\n\n// //token及token内信息\n// global.TOKEN = function() {\n// \tlet token = localStorage.getItem(\"token\");\n// \tif (CommonUtils.isEmpty(token)) {\n// \t\treturn \"\";\n// \t} else {\n// \t\treturn token;\n// \t}\n// }\n\n// global.setToken = function(token) {\n// \tlocalStorage.setItem(\"token\", token);\n// }\n\n// global.removeToken = function() {\n// \tlocalStorage.removeItem(\"token\");\n// }\n\n// global.PHOTO_URL = function() {\n// \tlet token = global.TOKEN();\n// \tif (!CommonUtils.isEmpty(token)) {\n// \t\treturn global.truePhotoUrl(jwtDecode(token).photoUrl);\n// \t}\n// \treturn '#';\n// }\n\n// global.USERNAME = function() {\n// \tlet token = global.TOKEN();\n// \tif (!CommonUtils.isEmpty(token)) {\n// \t\treturn jwtDecode(token).username;\n// \t}\n// \treturn '';\n// }\n\n// global.USER_ID = function() {\n// \tlet token = global.TOKEN();\n// \tif (!CommonUtils.isEmpty(token)) {\n// \t\treturn jwtDecode(token).userId;\n// \t}\n// \treturn '';\n// }\n\n//限制显示长度\nglobal.omitStr = function(content, totalLength) {\n\tif (content.length > totalLength) {\n\t\treturn content.slice(0, totalLength - 3) + '...'\n\t} else {\n\t\treturn content;\n\t}\n}\n\nglobal.html2Text = function(contentHtml, limitLength) {\n\tlet content = StringUtils.html2Text(contentHtml);\n\tif (limitLength == undefined) {\n\t\treturn content;\n\t}\n\tif (content.length > limitLength) {\n\t\treturn content.slice(0, limitLength - 3) + '...'\n\t} else {\n\t\treturn content;\n\t}\n}\n\nexport default global;\n\n"
  },
  {
    "path": "vue-manage-system/src/utils/request.ts",
    "content": "import axios from 'axios';\nimport { ElMessage, ElMessageBox, ElLoading, ElNotification } from 'element-plus';\nimport MessageUtils from '@/utils/MessageUtils'\nimport { Code } from '@/utils/result';\n\n// const service = axios.create({\n// \tbaseURL: \"http://127.0.0.1:8080/projectName\",//请求地址前缀\n// \ttimeout: 0\n// });\nconst service = axios.create();\n\nvar requestNum = 0;\nvar loading: any = null;\n\n// 请求拦截器\nservice.interceptors.request.use(\n\t(config: any) => {\n\t\t//添加请求头部参数\n\t\t// config.headers['arg1'] = \"arg1Value\";\n\n\t\t//开始loading\n\t\tconfig.headers['authorization'] = localStorage.getItem('token');\n\t\trequestNum++;\n\t\tif (loading == null) {\n\t\t\tloading = ElLoading.service({ fullscreen: true, text: '正在努力加载中~' });\n\t\t} else if (loading != null && requestNum > 0) {\n\t\t\tloading = ElLoading.service({ fullscreen: true, text: '正在努力加载中~' });\n\t\t}\n\t\treturn config;\n\t},\n\terror => {\n\t\trequestNum = 0;\n\t\tif (loading) {\n\t\t\tloading.close();\n\t\t}\n\t\treturn Promise.reject(error);\n\t}\n);\n\n// 响应拦截器\nservice.interceptors.response.use(\n\tresponse => {\n\t\t//拦截到成功的数据\n\t\trequestNum--;\n\t\tif (loading == null || requestNum <= 0) {\n\t\t\tloading.close()\n\t\t}\n\t\tconst result = response.data;\n\t\tif (result.code == Code.ERR) {\n\t\t\tMessageUtils.notice(result.msg, 2)\n\t\t} else if (result.code == Code.OK) {\n\t\t\treturn response.data;\n\t\t} else {\n\t\t\t// 出错了直接关闭loading\n\t\t\trequestNum = 0\n\t\t\tloading.close();\n\t\t}\n\t},\n\terror => {\n\t\t//拦截到失败的数据\n\t\tconsole.log('错误码', error)\n\t\t// 出错了直接关闭loading\n\t\trequestNum = 0\n\t\tloading.close();\n\n\t\tMessageUtils.notice(error, 4)\n\t\treturn Promise.reject(error);\n\t}\n);\n\nexport default service;\n"
  },
  {
    "path": "vue-manage-system/src/utils/requestx.ts",
    "content": "import axios, {AxiosInstance, AxiosError, AxiosResponse, AxiosRequestConfig} from 'axios';\n\nconst service:AxiosInstance = axios.create({\n    timeout: 5000\n});\n\nservice.interceptors.request.use(\n    (config: AxiosRequestConfig) => {\n        return config;\n    },\n    (error: AxiosError) => {\n        console.log(error);\n        return Promise.reject();\n    }\n);\n\nservice.interceptors.response.use(\n    (response: AxiosResponse) => {\n        if (response.status === 200) {\n            return response;\n        } else {\n            Promise.reject();\n        }\n    },\n    (error: AxiosError) => {\n        console.log(error);\n        return Promise.reject();\n    }\n);\n\nexport default service;\n"
  },
  {
    "path": "vue-manage-system/src/utils/result.ts",
    "content": "// 状态码\nexport let Code= {\n\tOK: 20000,\n\tERR: 20001,\n\tDATA_NOT_EXIST: 20011\n}\n\n\n\n// //获取图片路径\n// Vue.prototype.getImageUrl = function(imageId) {\n// \treturn \"../storage/images/\" + imageId + \".jpeg\";\n// }\n// //获取头像路径\n// Vue.prototype.getPhotoUrl = function(photoId) {\n// \treturn \"../storage/photos/\" + photoId + \".jpeg\";\n// }\n\n// //获取分享链接\n// Vue.prototype.getTopicUrl = function(shareId) {\n// \treturn '/templates/ShowTopic.html?id=' + shareId\n// }\n\n// /*获取存储在浏览器cookie的用户信息*/\n// Vue.prototype.PHOTO_URL = function() {\n// \tlet photoId = $.cookie('photoId');\n// \tif (!isEmpty(photoId)) {\n// \t\treturn this.getPhotoUrl(photoId);\n// \t}\n// \treturn '#';\n// }\n\n// Vue.prototype.USERNAME = function() {\n// \tlet username = $.cookie('username');\n// \tif (!isEmpty(username)) {\n// \t\treturn username;\n// \t}\n// \treturn '';\n// }\n\n// Vue.prototype.USER_ID = function() {\n// \tlet userId = $.cookie('userId');\n// \tif (!isEmpty(userId)) {\n// \t\treturn userId;\n// \t}\n// \treturn '';\n// }\n// /*End cookie信息*/\n\n// /*消息提示，进一步封装elementUI的消息提示*/\n// Vue.prototype.popupHint = function(msg) {\n// \tthis.$notify({\n// \t\ttitle: '提示',\n// \t\tmessage: msg,\n// \t\ttype: 'warning'\n// \t});\n// }\n\n// Vue.prototype.popupSuccess = function(msg) {\n// \tthis.$notify({\n// \t\ttitle: '成功',\n// \t\tmessage: msg,\n// \t\ttype: 'success'\n// \t});\n// }\n\n\n//控制显示长度\n// Vue.prototype.omitStr = function(content, totalLength) {\n// \tif (content.length > totalLength) {\n// \t\treturn content.slice(0, totalLength - 3) + '...'\n// \t} else {\n// \t\treturn content;\n// \t}\n// }\n\n\n//axios拦截\n// axios.interceptors.response.use(\n// \tfunction(response) {\n// \t\tlet result = response.data;\n// \t\tif (result.code == Code.ERR) {\n// \t\t\tVue.prototype.popupHint(result.msg);\n// \t\t\treturn false;\n// \t\t}\n// \t\treturn response;\n// \t},\n// \tfunction(error) {\n// \t\t// 对响应错误进行操作\n// \t\treturn Promise.reject(error);\n// \t}\n// );\n"
  },
  {
    "path": "vue-manage-system/src/utils/utils.js",
    "content": "\n"
  },
  {
    "path": "vue-manage-system/src/views/403.vue",
    "content": "<template>\n\t<div class=\"error-page\">\n\t\t<div class=\"error-code\">4<span>0</span>3</div>\n\t\t<div class=\"error-desc\">啊哦~ 你没有权限访问该页面哦</div>\n\t\t<div class=\"error-handle\">\n\t\t\t<router-link to=\"/\">\n\t\t\t\t<el-button type=\"primary\" size=\"large\">返回首页</el-button>\n\t\t\t</router-link>\n\t\t\t<el-button class=\"error-btn\" type=\"primary\" size=\"large\" @click=\"goBack\">返回上一页</el-button>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"403\">\nimport { useRouter } from 'vue-router';\n\nconst router = useRouter();\nconst goBack = () => {\n\trouter.go(-2);\n};\n</script>\n\n<style scoped>\n.error-page {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: #f3f3f3;\n\tbox-sizing: border-box;\n}\n.error-code {\n\tline-height: 1;\n\tfont-size: 250px;\n\tfont-weight: bolder;\n\tcolor: #f02d2d;\n}\n.error-code span {\n\tcolor: #00a854;\n}\n.error-desc {\n\tfont-size: 30px;\n\tcolor: #777;\n}\n.error-handle {\n\tmargin-top: 30px;\n\tpadding-bottom: 200px;\n}\n.error-btn {\n\tmargin-left: 100px;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/404.vue",
    "content": "<template>\n\t<div class=\"error-page\">\n\t\t<div class=\"error-code\">4<span>0</span>4</div>\n\t\t<div class=\"error-desc\">啊哦~ 你所访问的页面不存在</div>\n\t\t<div class=\"error-handle\">\n\t\t\t<router-link to=\"/\">\n\t\t\t\t<el-button type=\"primary\" size=\"large\">返回首页</el-button>\n\t\t\t</router-link>\n\t\t\t<el-button class=\"error-btn\" type=\"primary\" size=\"large\" @click=\"goBack\">返回上一页</el-button>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"404\">\nimport { useRouter } from 'vue-router';\n\nconst router = useRouter();\nconst goBack = () => {\n\trouter.go(-1);\n};\n</script>\n\n<style scoped>\n.error-page {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tflex-direction: column;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: #f3f3f3;\n\tbox-sizing: border-box;\n}\n.error-code {\n\tline-height: 1;\n\tfont-size: 250px;\n\tfont-weight: bolder;\n\tcolor: #2d8cf0;\n}\n.error-code span {\n\tcolor: #00a854;\n}\n.error-desc {\n\tfont-size: 30px;\n\tcolor: #777;\n}\n.error-handle {\n\tmargin-top: 30px;\n\tpadding-bottom: 200px;\n}\n.error-btn {\n\tmargin-left: 100px;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/HomeCarousel/HomeCarousel.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<!-- \t\t\t\t<el-select v-model=\"query.address\" placeholder=\"地址\" class=\"handle-select mr10\">\n\t\t\t\t\t<el-option key=\"1\" label=\"广东省\" value=\"广东省\"></el-option>\n\t\t\t\t\t<el-option key=\"2\" label=\"湖南省\" value=\"湖南省\"></el-option>\n\t\t\t\t</el-select>\n\t\t\t\t<el-input v-model=\"query.name\" placeholder=\"用户名\" class=\"handle-input mr10\"></el-input>\n\t\t\t\t<el-button type=\"primary\" :icon=\"Search\" @click=\"handleSearch\">搜索</el-button> -->\n\t\t\t\t<el-button type=\"primary\" :icon=\"Plus\" @click=\"addImageVisible=true\">新增</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"homeCarousel\" border class=\"table\" ref=\"multipleTable\"\n\t\t\t\theader-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column label=\"序号\" width=\"55\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{scope.$index+1}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"50\" align=\"center\"></el-table-column>\n\n\t\t\t\t<el-table-column label=\"图片\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-image class=\"table-td-thumb\" :src=\"scope.row.url\" :z-index=\"10\"\n\t\t\t\t\t\t\t:preview-src-list=\"[scope.row.url]\" preview-teleported>\n\t\t\t\t\t\t</el-image>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"description\" label=\"描述\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"link\" label=\"链接\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"updateTime\" label=\"更新时间\"></el-table-column>\n\t\t\t\t<el-table-column label=\"文件大小\">\n\t\t\t\t\t<template #default=\"scope\">{{ Math.ceil(scope.row.size/1000) }}KB</template>\n\t\t\t\t</el-table-column>\n\n\t\t\t\t<el-table-column label=\"操作\" width=\"220\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button size=\"small\" @click=\"handleEdit(scope.$index, scope.row)\" v-permiss=\"15\">\n\t\t\t\t\t\t\t编辑\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t<el-button type=\"primary\" size=\"small\" @click=\"handleCover(scope.row)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t覆盖\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t<el-button type=\"danger\" size=\"small\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<!-- \t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" :current-page=\"query.pageIndex\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"pageTotal\" @current-change=\"handlePageChange\"></el-pagination>\n\t\t\t</div> -->\n\t\t</div>\n\n\t\t<!-- 编辑弹出框 -->\n\t\t<el-dialog title=\"编辑\" v-model=\"editVisible\">\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"新的描述\">\n\t\t\t\t\t<el-input v-model=\"editForm.description\" maxlength=\"30\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"新的链接\">\n\t\t\t\t\t<el-input v-model=\"editForm.link\" maxlength=\"100\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"editVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveEdit\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t\t<!-- 覆盖图片对话框 -->\n\t\t<el-dialog title=\"覆盖图片\" v-model=\"coverVisible\">\n\t\t\t<el-form>\n\t\t\t\t<el-form-item>\n\t\t\t\t\t<el-upload action=\"#\" ref=\"cover\" :class=\"{'hide':coverImageList.length >= 1}\"\n\t\t\t\t\t\tlist-type=\"picture-card\" :limit=\"1\" :auto-upload=\"false\" accept=\"image/*\"\n\t\t\t\t\t\tv-model:file-list=\"coverImageList\">\n\t\t\t\t\t\t<el-icon>\n\t\t\t\t\t\t\t<Plus />\n\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t</el-upload>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"coverVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveCover()\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t\t<!-- 新增图片对话框 -->\n\t\t<el-dialog title=\"新增图片\" v-model=\"addImageVisible\">\n\t\t\t<el-form :model=\"addImageForm\">\n\t\t\t\t<el-form-item>\n\t\t\t\t\t<el-upload action=\"#\" ref=\"upload\" :class=\"{'hide':addImageList.length>=1}\" list-type=\"picture-card\"\n\t\t\t\t\t\t:limit=\"1\" :auto-upload=\"false\" accept=\"image/*\" v-model:file-list=\"addImageList\">\n\t\t\t\t\t\t<el-icon>\n\t\t\t\t\t\t\t<Plus />\n\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t</el-upload>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"描述\" prop=\"description\">\n\t\t\t\t\t<el-input v-model=\"addImageForm.description\" maxlength=\"30\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"链接\" prop=\"link\">\n\t\t\t\t\t<el-input v-model=\"addImageForm.link\" maxlength=\"100\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"addImageVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"addImage\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"HomeCarousel\">\n\timport { ref, reactive, onMounted } from 'vue';\n\timport { UploadFile } from 'element-plus';\n\timport { Plus } from '@element-plus/icons-vue';\n\n\timport {\n\t\tadd,\n\t\tmodifyDescription,\n\t\tdeleteById,\n\t\tcoverImage,\n\t\tqueryCurrent\n\t} from '@/api/HomeCarousel';\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\timport global from '@/utils/global'\n\t\n\n\tinterface Image {\n\t\tid: number;\n\t\tupdateTime: string;\n\t\tdescription: string;\n\t\tlink:string\n\t\turl: string;\n\t\tsize: number;\n\t}\n\t//查询首页图片\n\tlet homeCarousel = ref < Image[] > ([{\n\t\tid: 1008610086,\n\t\tupdateTime: '2016-05-02 22:22:22',\n\t\tdescription: '好看的',\n\t\turl: 'homeCarousel/2023/01/07/1611730070081744896.jpeg',\n\t\tlink:'',\n\t\tsize: 1008610,\n\t}]);\n\n\tconst getData = () => {\n\t\tqueryCurrent().then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\thomeCarousel.value = res.data;\n\t\t\t}\n\t\t});\n\t}\n\tgetData();\n\n\t//编辑\n\tlet editForm = reactive({\n\t\tid: 0,\n\t\tdescription: '',\n\t\tlink:''\n\t});\n\tlet editVisible = ref(false);\n\tconst handleEdit = (index: number, row: Image) => {\n\t\teditForm.id = homeCarousel.value[index].id;\n\t\teditForm.description = row.description;\n\t\teditForm.link = row.link;\n\t\teditVisible.value = true;\n\t};\n\tconst saveEdit = () => {\n\t\tmodifyDescription(editForm).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"修改成功\", 1);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t}\n\t\t})\n\t};\n\n\t//覆盖\n\tlet coverVisible = ref(false);\n\n\tlet coverId = -1;\n\tlet coverImageList = ref < UploadFile[] > ([]);\n\n\tconst handleCover = (row: Image) => {\n\t\tcoverVisible.value = true;\n\t\tcoverId = row.id;\n\n\t};\n\tconst saveCover = () => {\n\t\tif (coverImageList.value.length == 0) {\n\t\t\tMessageUtils.notice(\"至少选择一张图\", 2);\n\t\t\treturn;\n\t\t}\n\t\tlet reqData: any = new FormData();\n\t\treqData.append(\"id\", coverId);\n\t\treqData.append(\"image\", coverImageList.value[0].raw);\n\t\tcoverImage(reqData).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"成功\", 1);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t}\n\t\t})\n\t\tcoverVisible.value = false;\n\t}\n\n\t//新增\n\tlet addImageVisible = ref(false);\n\tlet addImageForm = reactive({\n\t\tdescription: '',\n\t\tlink:'',\n\t});\n\tlet addImageList = ref < UploadFile[] > ([]);\n\n\tlet addImage = () => {\n\t\tif (addImageList.value.length == 0) {\n\t\t\tMessageUtils.notice(\"至少选择一张图\", 2);\n\t\t\treturn;\n\t\t}\n\t\tif (addImageForm.description.length < 2 || addImageForm.description.length > 30) {\n\t\t\tMessageUtils.notice(\"描述需要在2-30之间\", 2);\n\t\t\treturn;\n\t\t}\n\n\t\tlet reqData: any = new FormData();\n\t\treqData.append(\"description\", addImageForm.description);\n\t\treqData.append(\"link\", addImageForm.link);\n\t\treqData.append(\"image\", addImageList.value[0].raw);\n\n\t\tadd(reqData).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"新增成功\", 1);\n\t\t\t\tCommonUtils.delayRefresh(0.5);\n\t\t\t}\n\t\t});\n\t\taddImageVisible.value = false;\n\t};\n\n\t//删除\n\tconst handleDelete = (index: number) => {\n\t\tMessageUtils.confirm(\"确定删除吗？操作不可逆！\").then(() => {\n\t\t\tconst deleteId = homeCarousel.value[index].id;\n\t\t\tdeleteById(deleteId)\n\t\t\t\t.then((res: any) => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"删除成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}).catch(e => e);\n\n\t}\n\n\n\n\n\t// let currentModifyId: number = 0;\n\t// let newDescription = '';\n\n\t// interface TableItem {\n\t// \tid: number;\n\t// \tname: string;\n\t// \tmoney: string;\n\t// \tstate: string;\n\t// \tdate: string;\n\t// \taddress: string;\n\t// }\n\n\t// const query = reactive({\n\t// \taddress: '',\n\t// \tname: '',\n\t// \tpageIndex: 1,\n\t// \tpageSize: 10\n\t// });\n\t// const tableData = ref < TableItem[] > ([]);\n\t// const pageTotal = ref(0);\n\t// // 获取表格数据\n\t// // const getData = () => {\n\t// // \tfetchData().then((res: any) => {\n\t// // \t\ttableData.value = res.data.list;\n\t// // \t\tpageTotal.value = res.data.pageTotal || 50;\n\t// // \t});\n\t// // };\n\t// // getData();\n\n\t// // 查询操作\n\t// const handleSearch = () => {\n\t// \tquery.pageIndex = 1;\n\t// \t// getData();\n\t// };\n\t// // 分页导航\n\t// const handlePageChange = (val: number) => {\n\t// \tquery.pageIndex = val;\n\t// \t// getData();\n\t// };\n\n\t// 删除操作\n\t// const handleDelete = (index: number) => {\n\t// \t// 二次确认删除\n\t// \tElMessageBox.confirm('确定要删除吗？', '提示', {\n\t// \t\t\ttype: 'warning'\n\t// \t\t})\n\t// \t\t.then(() => {\n\t// \t\t\tElMessage.success('删除成功');\n\t// \t\t\ttableData.value.splice(index, 1);\n\t// \t\t})\n\t// \t\t.catch(() => {});\n\t// };\n\n\t// 表格编辑时弹窗和保存\n\t// let form = reactive({\n\t// \tname: '',\n\t// \taddress: ''\n\t// });\n\t// let idx: number = -1;\n\t// const handleEdit = (index: number, row: any) => {\n\t// \tidx = index;\n\t// \tform.name = row.name;\n\t// \tform.address = row.address;\n\t// \teditVisible.value = true;\n\t// };\n\t// const saveEdit = () => {\n\t// \teditVisible.value = false;\n\t// \tElMessage.success(`修改第 ${idx + 1} 行成功`);\n\t// \ttableData.value[idx].name = form.name;\n\t// \ttableData.value[idx].address = form.address;\n\t// };\n</script>\n\n<style scoped>\n\t.hide :deep() .el-upload--picture-card {\n\t\tdisplay: none;\n\t}\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 120px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/api/api.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<el-input placeholder=\"路径关键字\" class=\"handle-input mr10\" v-model=\"query.keyword\"></el-input>\n\t\t\t\t<el-button type=\"primary\" @click=\"getData\">搜索</el-button>\n\t\t\t\t<el-button type=\"primary\" @click=\"addVisible=true\">新增</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"apis\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column label=\"序号\" width=\"55\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{scope.$index+1}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"50\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"path\" label=\"路径\" width=\"200\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"method\" label=\"方法\" width=\"60\"></el-table-column>\n\t\t\t\t<el-table-column label=\"权限码\" width=\"160\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{getPermissionCode(scope.row.permissionId)}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"note\" label=\"note\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"enable\" label=\"是否启用\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"createTime\" label=\"创建时间\" width=\"70\"></el-table-column>\n\n\t\t\t\t<el-table-column label=\"操作\" width=\"150\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button size=\"small\" @click=\"handleEdit(scope.$index, scope.row)\">\n\t\t\t\t\t\t\t编辑\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t<el-button type=\"danger\" size=\"small\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" v-model:current-page=\"query.pageNo\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"totalCount\" @current-change=\"handlePageChange\"></el-pagination>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- 新增弹出框 -->\n\t\t<el-dialog title=\"新增\" v-model=\"addVisible\">\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"路径\">\n\t\t\t\t\t<el-input v-model=\"addForm.path\" maxlength=\"200\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"权限\">\n\t\t\t\t\t<el-select v-model=\"addForm.permissionId\" placeholder=\"权限\" class=\"handle-select mr10\">\n\t\t\t\t\t\t<el-option v-for=\"permission in permissions\" :key=\"permission.id\" :label=\"permission.code\"\n\t\t\t\t\t\t\t:value=\"permission.id\">\n\t\t\t\t\t\t</el-option>\n\t\t\t\t\t</el-select>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"备注\">\n\t\t\t\t\t<el-input v-model=\"addForm.note\" maxlength=\"100\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"启用\">\n\t\t\t\t\t<el-radio-group v-model=\"addForm.enable\">\n\t\t\t\t\t\t<el-radio :label=\"true\">启用</el-radio>\n\t\t\t\t\t\t<el-radio :label=\"false\">禁用</el-radio>\n\t\t\t\t\t</el-radio-group>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"请求方法\">\n\t\t\t\t\t<el-radio-group v-model=\"addForm.method\">\n\t\t\t\t\t\t<el-radio label=\"GET\">GET</el-radio>\n\t\t\t\t\t\t<el-radio label=\"PUT\">PUT</el-radio>\n\t\t\t\t\t\t<el-radio label=\"POST\">POST</el-radio>\n\t\t\t\t\t\t<el-radio label=\"DELETE\">DELTE</el-radio>\n\t\t\t\t\t\t<el-radio label=\"ALL\">ALL</el-radio>\n\t\t\t\t\t</el-radio-group>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"addVisible = false\">关闭</el-button>\n\t\t\t\t\t<el-button @click=\"saveAdd\">确认</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\n\t\t<!-- 编辑弹出框 -->\n\t\t<el-dialog title=\"新增\" v-model=\"editVisible\">\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"路径\">\n\t\t\t\t\t<el-input v-model=\"editForm.path\" maxlength=\"200\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"权限\">\n\t\t\t\t\t<el-select v-model=\"editForm.permissionId\" placeholder=\"权限\" class=\"handle-select mr10\">\n\t\t\t\t\t\t<el-option v-for=\"permission in permissions\" :key=\"permission.id\" :label=\"permission.code\"\n\t\t\t\t\t\t\t:value=\"permission.id\">\n\t\t\t\t\t\t</el-option>\n\t\t\t\t\t</el-select>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"备注\">\n\t\t\t\t\t<el-input v-model=\"editForm.note\" maxlength=\"100\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"启用\">\n\t\t\t\t\t<el-radio-group v-model=\"editForm.enable\">\n\t\t\t\t\t\t<el-radio :label=\"true\">启用</el-radio>\n\t\t\t\t\t\t<el-radio :label=\"false\">禁用</el-radio>\n\t\t\t\t\t</el-radio-group>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"请求方法\">\n\t\t\t\t\t<el-radio-group v-model=\"editForm.method\">\n\t\t\t\t\t\t<el-radio label=\"GET\">GET</el-radio>\n\t\t\t\t\t\t<el-radio label=\"PUT\">PUT</el-radio>\n\t\t\t\t\t\t<el-radio label=\"POST\">POST</el-radio>\n\t\t\t\t\t\t<el-radio label=\"DELETE\">DELETE</el-radio>\n\t\t\t\t\t\t<el-radio label=\"ALL\">ALL</el-radio>\n\t\t\t\t\t</el-radio-group>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"editVisible = false\">关闭</el-button>\n\t\t\t\t\t<el-button @click=\"saveEdit\">确认</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"api\">\n\timport { ref, reactive, onMounted } from 'vue';\n\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\timport global from '@/utils/global'\n\n\timport { useStore } from '@/store/store';\n\timport { Permission } from '@/views/permission/permission.vue'\n\timport { queryNonModules } from '@/api/permission';\n\n\timport { searchApis, addApi, modifyApi, deleteApi } from '@/api/api'\n\n\tconst store = useStore();\n\tstore.init()\n\n\tinterface Api {\n\t\tid: number;\n\t\tpath: string;\n\t\tmethod: string;\n\t\tnote: string;\n\t\tenable: boolean;\n\t\tpermissionId: number;\n\t\tcreateTime: string;\n\t\tupdateTime: string;\n\t}\n\tlet apis = ref < Api[] > ([]);\n\n\tlet permissions = ref < Permission[] > ([]);\n\tconst getNonModules = () => {\n\t\tqueryNonModules().then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tpermissions.value = res.data;\n\t\t\t}\n\t\t})\n\t};\n\tgetNonModules();\n\tconst getPermissionCode = (permissionId) => {\n\t\tfor (let permission of permissions.value) {\n\t\t\tif (permission.id == permissionId) {\n\t\t\t\treturn permission.code;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tlet totalCount = ref(1);\n\tlet query = reactive({\n\t\tkeyword: null,\n\t\tpageNo: 1,\n\t\tpageSize: 20,\n\t});\n\tconst getData = () => {\n\t\tsearchApis(query).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tapis.value = res.data.dataList;\n\t\t\t\ttotalCount.value = res.data.totalCount;\n\t\t\t}\n\t\t});\n\t}\n\tgetData();\n\tconst handlePageChange = () => {\n\t\tgetData();\n\t};\n\n\t//编辑接口\n\tlet editForm = reactive({\n\t\tid: 0,\n\t\tpath: '',\n\t\tnote: '',\n\t\tpermissionId: -1,\n\t\tmethod: '',\n\t\tenable: true,\n\t});\n\tlet editVisible = ref(false);\n\tconst handleEdit = (index: number, row: Api) => {\n\t\teditForm.id = apis.value[index].id;\n\t\teditForm.path = apis.value[index].path;\n\t\teditForm.note = apis.value[index].note;\n\t\teditForm.permissionId = apis.value[index].permissionId;\n\t\teditForm.method = apis.value[index].method;\n\t\teditForm.enable = apis.value[index].enable;\n\t\teditVisible.value = true;\n\t};\n\n\tconst saveEdit = () => {\n\t\tmodifyApi(editForm).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"修改成功\", 2);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t}\n\t\t})\n\t}\n\n\t//新增接口\n\tlet addForm = reactive({\n\t\tpath: null,\n\t\tmethod: \"GET\",\n\t\tnote: null,\n\t\tpermissionId: null,\n\t\tenable: true\n\t});\n\tlet addVisible = ref(false);\n\n\tconst saveAdd = () => {\n\t\taddApi(addForm).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"增加成功\", 2);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t}\n\t\t})\n\t}\n\n\n\tconst handleDelete = (index: number) => {\n\t\tMessageUtils.confirm(\"是否确定删除？操作不可逆！\").then(() => {\n\t\t\tdeleteApi(apis.value[index].id).then((res: any) => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\tMessageUtils.success(\"修改成功\", 2);\n\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n</script>\n\n<style scoped>\n\t.hide :deep() .el-upload--picture-card {\n\t\tdisplay: none;\n\t}\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 120px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/authorize/authorize.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<el-select v-model=\"query.roleId\" placeholder=\"角色\" class=\"handle-select mr10\"\n\t\t\t\t\t@change=\"getRoleAuthorizeList\">\n\t\t\t\t\t<el-option v-for=\"item in roleList\" :key=\"item.id\" :label=\"item.roleName\" :value=\"item.id\">\n\t\t\t\t\t</el-option>\n\t\t\t\t</el-select>\n\t\t\t\t<el-button type=\"primary\" :icon=\"Search\" @click=\"getRoleAuthorizeList\">刷新</el-button>\n\t\t\t</div>\n\n\t\t\t<div class=\"mgb20 tree-wrapper\">\n\t\t\t\t权限树\n\t\t\t\t<el-tree class=\"leaf-chekabel-only\" ref=\"treeRef\" :data=\"permissionTree\" node-key=\"id\"\n\t\t\t\t\tdefault-expand-all check-strictly show-checkbox :default-checked-keys=\"checkedKeys\"\n\t\t\t\t\t@check=\"handleCheck\" />\n\t\t\t</div>\n\t\t</div>\n\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"authorize\">\n\timport { ref, reactive } from 'vue';\n\timport { ElTree } from 'element-plus'\n\timport { Role } from '@/views/role/role.vue'\n\timport { Permission } from '@/views/permission/permission.vue'\n\n\timport { queryAllRoles } from '@/api/role';\n\timport { queryPermissionTree } from '@/api/permission';\n\timport { queryRoleAuthorize, addAuthorize, deleteAuthorize } from '@/api/authorize';\n\timport { Search } from '@element-plus/icons-vue';\n\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\n\t//查询权限树\n\tconst combineAsLabel = (node: Permission) => {\n\t\tnode.label = CommonUtils.isEmpty(node.code) ? node.label : node.code + '|' + node.label;\n\t\tfor (let item of node.children!) {\n\t\t\tcombineAsLabel(item);\n\t\t}\n\t}\n\tlet permissionTree = ref < Permission[] > ([]);\n\tconst getPermissionTree = () => {\n\t\tqueryPermissionTree().then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tpermissionTree.value = res.data;\n\t\t\t\tfor (let item of permissionTree.value!) {\n\t\t\t\t\tcombineAsLabel(item);\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t};\n\tgetPermissionTree();\n\n\t//查询所有角色\n\tlet roleList = ref < Role[] > ([{\n\t\tid: 1,\n\t\troleName: 'user',\n\t\tcreateTime: '2022-2-22',\n\t\tupdateTime: '2022-2-22',\n\t\tnote: '用户',\n\t}]);\n\tconst getRoleList = () => {\n\t\tqueryAllRoles().then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\troleList.value = res.data;\n\t\t\t}\n\t\t})\n\t};\n\tgetRoleList();\n\n\n\t//查询角色权限\n\tlet roleAuthorizeList: any = ref([]);\n\tlet query = reactive < any > ({\n\t\troleId: null,\n\t})\n\tconst getRoleAuthorizeList = () => {\n\t\tif (query.roleId != null) {\n\t\t\tqueryRoleAuthorize(query.roleId).then((res: any) => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\troleAuthorizeList.value = res.data;\n\t\t\t\t\tinitCheckedKeys();\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\tlet checkedKeys = ref < number[] > ([]);\n\tconst initCheckedKeys = () => {\n\t\tcheckedKeys = ref < number[] > ([]);;\n\t\tfor (const item of roleAuthorizeList.value) {\n\t\t\tcheckedKeys.value.push(item.permissionId);\n\t\t}\n\t\ttreeRef.value!.setCheckedKeys(checkedKeys.value, false);\n\t}\n\n\t//选中节点复选框时触发\n\tinterface Tree {\n\t\tid: number\n\t\tlabel: string\n\t\tchildren ? : Tree[]\n\t}\n\tconst treeRef = ref < InstanceType < typeof ElTree >> ()\n\tconst handleCheck = (node: Permission) => {\n\t\tif (node.module) {\n\t\t\ttreeRef.value!.setCheckedKeys(checkedKeys.value, false);\n\t\t\tMessageUtils.notice(\"不可操作模块\", 1);\n\t\t\treturn false;\n\t\t}\n\t\tMessageUtils.confirm(\"是否改变该权限\").then(() => {\n\t\t\tif (checkedKeys.value.includes(node.id)) {\n\t\t\t\tsaveDelete(query.roleId, node.id);\n\t\t\t\tcheckedKeys.value.forEach((value, index) => {\n\t\t\t\t\tif (value == node.id) {\n\t\t\t\t\t\tcheckedKeys.value.slice(index, 1);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tsaveAdd(query.roleId, node.id);\n\t\t\t\tcheckedKeys.value.push(node.id);\n\t\t\t}\n\t\t\treturn false;\n\t\t}).catch(() => {\n\t\t\ttreeRef.value!.setCheckedKeys(checkedKeys.value, false);\n\t\t})\n\n\t\treturn true;\n\t};\n\n\tconst saveAdd = (roleId: number, permissionId: number) => {\n\t\tlet formData: FormData = new FormData();\n\t\tformData.append(\"roleId\", roleId.toString());\n\t\tformData.append(\"permissionId\", permissionId.toString());\n\t\taddAuthorize(formData).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"修改成功\", 1);\n\t\t\t}\n\t\t})\n\t}\n\n\tconst saveDelete = (roleId: number, permissionId: number) => {\n\t\tdeleteAuthorize(roleId, permissionId).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"修改成功\", 1);\n\t\t\t}\n\t\t})\n\t}\n</script>\n\n<style scoped>\n\t/* 只能选叶子节点 */\n\t.leaf-chekabel-only :deep() .el-tree-node {\n\n\t\t.is-leaf+.el-checkbox .el-checkbox__inner {\n\n\t\t\tdisplay: inline-block;\n\n\t\t}\n\n\t\t.el-checkbox__input>.el-checkbox__inner {\n\n\t\t\tdisplay: none;\n\n\t\t}\n\n\t}\n\n\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 200px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/charts.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\tvue-schart：vue.js封装sChart.js的图表组件。 访问地址：\n\t\t\t<a href=\"https://github.com/lin-xin/vue-schart\" target=\"_blank\">vue-schart</a>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">柱状图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"bar\" :options=\"options1\"></schart>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">折线图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"line\" :options=\"options2\"></schart>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">饼状图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"pie\" :options=\"options3\"></schart>\n\t\t</div>\n\t\t<div class=\"schart-box\">\n\t\t\t<div class=\"content-title\">环形图</div>\n\t\t\t<schart class=\"schart\" canvasId=\"ring\" :options=\"options4\"></schart>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"basecharts\">\nimport Schart from 'vue-schart';\n\nconst options1 = {\n\ttype: 'bar',\n\ttitle: {\n\t\ttext: '最近一周各品类销售图'\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['周一', '周二', '周三', '周四', '周五'],\n\tdatasets: [\n\t\t{\n\t\t\tlabel: '家电',\n\t\t\tfillColor: 'rgba(241, 49, 74, 0.5)',\n\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t},\n\t\t{\n\t\t\tlabel: '百货',\n\t\t\tdata: [164, 178, 190, 135, 160]\n\t\t},\n\t\t{\n\t\t\tlabel: '食品',\n\t\t\tdata: [144, 198, 150, 235, 120]\n\t\t}\n\t]\n};\nconst options2 = {\n\ttype: 'line',\n\ttitle: {\n\t\ttext: '最近几个月各品类销售趋势图'\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['6月', '7月', '8月', '9月', '10月'],\n\tdatasets: [\n\t\t{\n\t\t\tlabel: '家电',\n\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t},\n\t\t{\n\t\t\tlabel: '百货',\n\t\t\tdata: [164, 178, 150, 135, 160]\n\t\t},\n\t\t{\n\t\t\tlabel: '食品',\n\t\t\tdata: [114, 138, 200, 235, 190]\n\t\t}\n\t]\n};\nconst options3 = {\n\ttype: 'pie',\n\ttitle: {\n\t\ttext: '服装品类销售饼状图'\n\t},\n\tlegend: {\n\t\tposition: 'left'\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['T恤', '牛仔裤', '连衣裙', '毛衣', '七分裤', '短裙', '羽绒服'],\n\tdatasets: [\n\t\t{\n\t\t\tdata: [334, 278, 190, 235, 260, 200, 141]\n\t\t}\n\t]\n};\nconst options4 = {\n\ttype: 'ring',\n\ttitle: {\n\t\ttext: '环形三等分'\n\t},\n\tshowValue: false,\n\tlegend: {\n\t\tposition: 'bottom',\n\t\tbottom: 40\n\t},\n\tbgColor: '#fbfbfb',\n\tlabels: ['vue', 'react', 'angular'],\n\tdatasets: [\n\t\t{\n\t\t\tdata: [500, 500, 500]\n\t\t}\n\t]\n};\n</script>\n\n<style scoped>\n.schart-box {\n\tdisplay: inline-block;\n\tmargin: 20px;\n}\n.schart {\n\twidth: 600px;\n\theight: 400px;\n}\n.content-title {\n\tclear: both;\n\tfont-weight: 400;\n\tline-height: 50px;\n\tmargin: 10px 0;\n\tfont-size: 22px;\n\tcolor: #1f2f3d;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/comment/comment.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<el-input placeholder=\"关键字\" class=\"handle-input mr10\" v-model=\"query.keyword\"></el-input>\n\t\t\t\t<el-input placeholder=\"话题id\" class=\"handle-input mr10\" v-model=\"query.topicId\"></el-input>\n\t\t\t\t<el-button type=\"primary\" @click=\"getData\">搜索</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"comments\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column label=\"序号\" width=\"55\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{scope.$index+1}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"50\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"topicId\" label=\"话题ID\" width=\"100\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"userId\" label=\"用户ID\" width=\"100\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"content\" label=\"内容\" width=\"200\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{global.omitStr(scope.row.content,200)}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"updateTime\" label=\"更新时间\" width=\"70\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"createTime\" label=\"创建时间\" width=\"70\"></el-table-column>\n\n\t\t\t\t<el-table-column label=\"操作\" width=\"250\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button type=\"danger\" size=\"small\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" v-model:current-page=\"query.pageNo\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"totalCount\" @current-change=\"handlePageChange\"></el-pagination>\n\t\t\t</div>\n\t\t</div>\n\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"comment\">\n\timport { ref, reactive } from 'vue';\n\n\timport { queryCommentsBy, deleteComment } from '@/api/comment'\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\timport global from '@/utils/global'\n\n\n\tinterface Comment {\n\t\tid: number;\n\t\ttopicId: number;\n\t\tuserId: number;\n\t\tcontent: string;\n\t\tupdateTime: string;\n\t\tcreateTime: string;\n\t}\n\t//查询话题\n\tlet comments = ref < Comment[] > ([{\n\t\tid: 1008610086,\n\t\tuserId: 1054054,\n\t\ttopicId: 546545,\n\t\tcontent: \"阿坎吉合肥市拉开收到货拉愧疚但是\",\n\t\tupdateTime: '2016-05-02 22:22:22',\n\t\tcreateTime: '2016-05-02 22:22:22',\n\t}]);\n\n\tlet totalCount = ref(1);\n\n\tlet query = reactive < any > ({\n\t\tkeyword: '',\n\t\ttopicId: -1,\n\t\tpageNo: 1,\n\t\tpageSize: 10,\n\t});\n\tconst getData = () => {\n\t\tif (query.topicId < 0 || query.topicId == null || query.topicId.toString().trim().length == 0) {\n\t\t\tquery.topicId = null;\n\t\t}\n\t\tqueryCommentsBy(query).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tcomments.value = res.data.dataList;\n\t\t\t\ttotalCount.value = res.data.totalCount;\n\t\t\t}\n\t\t});\n\t}\n\tgetData();\n\tconst handlePageChange = () => {\n\t\tgetData();\n\t};\n\n\t//删除\n\tconst handleDelete = (index: number) => {\n\t\tMessageUtils.confirm(\"确定删除吗？操作不可逆！\").then(() => {\n\t\t\tconst deleteId = comments.value[index].id;\n\t\t\tdeleteComment(deleteId)\n\t\t\t\t.then((res: any) => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"删除成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}).catch(e => e);\n\t}\n</script>\n\n<style scoped>\n\t.hide :deep() .el-upload--picture-card {\n\t\tdisplay: none;\n\t}\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 120px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/dashboard.vue",
    "content": "<template>\n\t<div>\n\t\t<el-row :gutter=\"20\">\n\t\t\t<el-col :span=\"8\">\n\t\t\t\t<el-card shadow=\"hover\" class=\"mgb20\" style=\"height: 252px\">\n\t\t\t\t\t<div class=\"user-info\">\n\t\t\t\t\t\t<el-avatar :size=\"120\" :src=\"imgurl\" />\n\t\t\t\t\t\t<div class=\"user-info-cont\">\n\t\t\t\t\t\t\t<div class=\"user-info-name\">{{ name }}</div>\n\t\t\t\t\t\t\t<div>{{ role }}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"user-info-list\">\n\t\t\t\t\t\t上次登录时间：\n\t\t\t\t\t\t<span>2022-10-01</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"user-info-list\">\n\t\t\t\t\t\t上次登录地点：\n\t\t\t\t\t\t<span>东莞</span>\n\t\t\t\t\t</div>\n\t\t\t\t</el-card>\n\t\t\t\t<el-card shadow=\"hover\" style=\"height: 252px\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>语言详情</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t\tVue\n\t\t\t\t\t<el-progress :percentage=\"79.4\" color=\"#42b983\"></el-progress>\n\t\t\t\t\tTypeScript\n\t\t\t\t\t<el-progress :percentage=\"14\" color=\"#f1e05a\"></el-progress>\n\t\t\t\t\tCSS\n\t\t\t\t\t<el-progress :percentage=\"5.6\"></el-progress>\n\t\t\t\t\tHTML\n\t\t\t\t\t<el-progress :percentage=\"1\" color=\"#f56c6c\"></el-progress>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t\t<el-col :span=\"16\">\n\t\t\t\t<el-row :gutter=\"20\" class=\"mgb20\">\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<el-card shadow=\"hover\" :body-style=\"{ padding: '0px' }\">\n\t\t\t\t\t\t\t<div class=\"grid-content grid-con-1\">\n\t\t\t\t\t\t\t\t<el-icon class=\"grid-con-icon\">\n\t\t\t\t\t\t\t\t\t<User />\n\t\t\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t\t\t\t<div class=\"grid-cont-right\">\n\t\t\t\t\t\t\t\t\t<div class=\"grid-num\">{{websiteData.pageView}}</div>\n\t\t\t\t\t\t\t\t\t<div>网站访客量</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-card>\n\t\t\t\t\t</el-col>\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<el-card shadow=\"hover\" :body-style=\"{ padding: '0px' }\">\n\t\t\t\t\t\t\t<div class=\"grid-content grid-con-2\">\n\t\t\t\t\t\t\t\t<el-icon class=\"grid-con-icon\">\n\t\t\t\t\t\t\t\t\t<ChatDotRound />\n\t\t\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t\t\t\t<div class=\"grid-cont-right\">\n\t\t\t\t\t\t\t\t\t<div class=\"grid-num\">{{Math.round(websiteData.apiAccessCount/1000)}}K</div>\n\t\t\t\t\t\t\t\t\t<div>接口访问总量</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-card>\n\t\t\t\t\t</el-col>\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<el-card shadow=\"hover\" :body-style=\"{ padding: '0px' }\">\n\t\t\t\t\t\t\t<div class=\"grid-content grid-con-3\">\n\t\t\t\t\t\t\t\t<el-icon class=\"grid-con-icon\">\n\t\t\t\t\t\t\t\t\t<Goods />\n\t\t\t\t\t\t\t\t</el-icon>\n\t\t\t\t\t\t\t\t<div class=\"grid-cont-right\">\n\t\t\t\t\t\t\t\t\t<div class=\"grid-num\">无</div>\n\t\t\t\t\t\t\t\t\t<div>敬请期待</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-card>\n\t\t\t\t\t</el-col>\n\t\t\t\t</el-row>\n\t\t\t\t<el-card shadow=\"hover\" style=\"height: 403px\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>待办事项</span>\n\t\t\t\t\t\t\t<el-button style=\"float: right; padding: 3px 0\" text>添加</el-button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\n\t\t\t\t\t<el-table :show-header=\"false\" :data=\"todoList\" style=\"width: 100%\">\n\t\t\t\t\t\t<el-table-column width=\"40\">\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<el-checkbox v-model=\"scope.row.status\"></el-checkbox>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<div class=\"todo-item\" :class=\"{\n\t\t\t\t\t\t\t\t\t\t'todo-item-del': scope.row.status\n\t\t\t\t\t\t\t\t\t}\">\n\t\t\t\t\t\t\t\t\t{{ scope.row.title }}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t</el-table>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t</el-row>\n\t\t<el-row :gutter=\"20\">\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<schart ref=\"bar\" class=\"schart\" canvasId=\"bar\" :options=\"options\"></schart>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<schart ref=\"line\" class=\"schart\" canvasId=\"line\" :options=\"options2\"></schart>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t</el-row>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"dashboard\">\n\timport Schart from 'vue-schart';\n\timport { reactive, ref } from 'vue';\n\timport imgurl from '../assets/img/img.jpg';\n\n\timport { Code } from '@/utils/result';\n\timport { queryWebsiteData } from '@/api/WebsiteData'\n\n\tinterface WebsiteData {\n\t\tpageView: number;\n\t\tapiAccessCount: number;\n\t}\n\tlet websiteData = ref < WebsiteData > ({\n\t\tpageView: 0,\n\t\tapiAccessCount: 0\n\t});\n\tconst getData = () => {\n\t\tqueryWebsiteData().then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\twebsiteData.value = res.data;\n\t\t\t}\n\t\t})\n\t}\n\tgetData();\n\n\tconst name = localStorage.getItem('ms_username');\n\tconst role: string = name === 'admin' ? '超级管理员' : '普通用户';\n\n\tconst options = {\n\t\ttype: 'bar',\n\t\ttitle: {\n\t\t\ttext: '最近一周各品类销售图'\n\t\t},\n\t\txRorate: 25,\n\t\tlabels: ['周一', '周二', '周三', '周四', '周五'],\n\t\tdatasets: [{\n\t\t\t\tlabel: '家电',\n\t\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: '百货',\n\t\t\t\tdata: [164, 178, 190, 135, 160]\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: '食品',\n\t\t\t\tdata: [144, 198, 150, 235, 120]\n\t\t\t}\n\t\t]\n\t};\n\tconst options2 = {\n\t\ttype: 'line',\n\t\ttitle: {\n\t\t\ttext: '最近几个月各品类销售趋势图'\n\t\t},\n\t\tlabels: ['6月', '7月', '8月', '9月', '10月'],\n\t\tdatasets: [{\n\t\t\t\tlabel: '家电',\n\t\t\t\tdata: [234, 278, 270, 190, 230]\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: '百货',\n\t\t\t\tdata: [164, 178, 150, 135, 160]\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: '食品',\n\t\t\t\tdata: [74, 118, 200, 235, 90]\n\t\t\t}\n\t\t]\n\t};\n\tconst todoList = reactive([{\n\t\t\ttitle: '今天要修复100个bug',\n\t\t\tstatus: false\n\t\t},\n\t\t{\n\t\t\ttitle: '今天要修复100个bug',\n\t\t\tstatus: false\n\t\t},\n\t\t{\n\t\t\ttitle: '今天要写100行代码加几个bug吧',\n\t\t\tstatus: false\n\t\t},\n\t\t{\n\t\t\ttitle: '今天要修复100个bug',\n\t\t\tstatus: false\n\t\t},\n\t\t{\n\t\t\ttitle: '今天要修复100个bug',\n\t\t\tstatus: true\n\t\t},\n\t\t{\n\t\t\ttitle: '今天要写100行代码加几个bug吧',\n\t\t\tstatus: true\n\t\t}\n\t]);\n</script>\n\n<style scoped>\n\t.el-row {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.grid-content {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\theight: 100px;\n\t}\n\n\t.grid-cont-right {\n\t\tflex: 1;\n\t\ttext-align: center;\n\t\tfont-size: 14px;\n\t\tcolor: #999;\n\t}\n\n\t.grid-num {\n\t\tfont-size: 30px;\n\t\tfont-weight: bold;\n\t}\n\n\t.grid-con-icon {\n\t\tfont-size: 50px;\n\t\twidth: 100px;\n\t\theight: 100px;\n\t\ttext-align: center;\n\t\tline-height: 100px;\n\t\tcolor: #fff;\n\t}\n\n\t.grid-con-1 .grid-con-icon {\n\t\tbackground: rgb(45, 140, 240);\n\t}\n\n\t.grid-con-1 .grid-num {\n\t\tcolor: rgb(45, 140, 240);\n\t}\n\n\t.grid-con-2 .grid-con-icon {\n\t\tbackground: rgb(100, 213, 114);\n\t}\n\n\t.grid-con-2 .grid-num {\n\t\tcolor: rgb(100, 213, 114);\n\t}\n\n\t.grid-con-3 .grid-con-icon {\n\t\tbackground: rgb(242, 94, 67);\n\t}\n\n\t.grid-con-3 .grid-num {\n\t\tcolor: rgb(242, 94, 67);\n\t}\n\n\t.user-info {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tpadding-bottom: 20px;\n\t\tborder-bottom: 2px solid #ccc;\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.user-info-cont {\n\t\tpadding-left: 50px;\n\t\tflex: 1;\n\t\tfont-size: 14px;\n\t\tcolor: #999;\n\t}\n\n\t.user-info-cont div:first-child {\n\t\tfont-size: 30px;\n\t\tcolor: #222;\n\t}\n\n\t.user-info-list {\n\t\tfont-size: 14px;\n\t\tcolor: #999;\n\t\tline-height: 25px;\n\t}\n\n\t.user-info-list span {\n\t\tmargin-left: 70px;\n\t}\n\n\t.mgb20 {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.todo-item {\n\t\tfont-size: 14px;\n\t}\n\n\t.todo-item-del {\n\t\ttext-decoration: line-through;\n\t\tcolor: #999;\n\t}\n\n\t.schart {\n\t\twidth: 100%;\n\t\theight: 300px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/donate.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\t如果该框架对你有帮助，那就请作者喝杯饮料吧！<el-icon><ColdDrink /></el-icon> 加微信号linxin_20探讨问题。\n\t\t</div>\n\t\t<div>\n\t\t\t<img src=\"https://lin-xin.gitee.io/images/weixin.jpg\" />\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"donate\"></script>\n\n<style></style>\n"
  },
  {
    "path": "vue-manage-system/src/views/editor.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\twangEditor：轻量级 web 富文本编辑器，配置方便，使用简单。 访问地址：\n\t\t\t<a href=\"https://www.wangeditor.com/doc/\" target=\"_blank\">wangEditor</a>\n\t\t</div>\n\t\t<div class=\"mgb20\" ref=\"editor\"></div>\n\t\t<el-button type=\"primary\" @click=\"syncHTML\">提交</el-button>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"editor\">\nimport WangEditor from 'wangeditor';\nimport { ref, reactive, onMounted, onBeforeUnmount } from 'vue';\n\nconst editor = ref(null);\nconst content = reactive({\n\thtml: '',\n\ttext: ''\n});\nlet instance: any;\nonMounted(() => {\n\tinstance = new WangEditor(editor.value);\n\tinstance.config.zIndex = 1;\n\tinstance.create();\n});\nonBeforeUnmount(() => {\n\tinstance.destroy();\n\tinstance = null;\n});\nconst syncHTML = () => {\n\tcontent.html = instance.txt.html();\n\tconsole.log(content.html);\n};\n</script>\n\n<style></style>\n"
  },
  {
    "path": "vue-manage-system/src/views/home.vue",
    "content": "<template>\n\t<v-header />\n\t<v-sidebar />\n\t<div class=\"content-box\" :class=\"{ 'content-collapse': sidebar.collapse }\">\n\t\t<v-tags></v-tags>\n\t\t<div class=\"content\">\n\t\t\t<router-view v-slot=\"{ Component }\">\n\t\t\t\t<transition name=\"move\" mode=\"out-in\">\n\t\t\t\t\t<keep-alive :include=\"tags.nameList\">\n\t\t\t\t\t\t<component :is=\"Component\"></component>\n\t\t\t\t\t</keep-alive>\n\t\t\t\t</transition>\n\t\t\t</router-view>\n\t\t</div>\n\t</div>\n</template>\n<script setup lang=\"ts\">\nimport { useSidebarStore } from '../store/sidebar';\nimport { useTagsStore } from '../store/tags';\nimport vHeader from '../components/header.vue';\nimport vSidebar from '../components/sidebar.vue';\nimport vTags from '../components/tags.vue';\n\nconst sidebar = useSidebarStore();\nconst tags = useTagsStore();\n</script>\n"
  },
  {
    "path": "vue-manage-system/src/views/icon.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<h2>使用方法</h2>\n\t\t<p style=\"line-height: 50px\">\n\t\t\t直接通过设置类名为 el-icon-lx-iconName 来使用即可。例如：（共{{ iconList.length }}个图标）\n\t\t</p>\n\t\t<p class=\"example-p\">\n\t\t\t<i class=\"el-icon-lx-redpacket_fill\" style=\"font-size: 30px; color: #ff5900\"></i>\n\t\t\t<span>&lt;i class=&quot;el-icon-lx-redpacket_fill&quot;&gt;&lt;/i&gt;</span>\n\t\t</p>\n\t\t<p class=\"example-p\">\n\t\t\t<i class=\"el-icon-lx-weibo\" style=\"font-size: 30px; color: #fd5656\"></i>\n\t\t\t<span>&lt;i class=&quot;el-icon-lx-weibo&quot;&gt;&lt;/i&gt;</span>\n\t\t</p>\n\t\t<p class=\"example-p\">\n\t\t\t<i class=\"el-icon-lx-emojifill\" style=\"font-size: 30px; color: #ffc300\"></i>\n\t\t\t<span>&lt;i class=&quot;el-icon-lx-emojifill&quot;&gt;&lt;/i&gt;</span>\n\t\t</p>\n\t\t<br />\n\t\t<h2>图标</h2>\n\t\t<div class=\"search-box\">\n\t\t\t<el-input class=\"search\" size=\"large\" v-model=\"keyword\" clearable placeholder=\"请输入图标名称\"></el-input>\n\t\t</div>\n\t\t<ul>\n\t\t\t<li class=\"icon-li\" v-for=\"(item, index) in list\" :key=\"index\">\n\t\t\t\t<div class=\"icon-li-content\">\n\t\t\t\t\t<i :class=\"`el-icon-lx-${item}`\"></i>\n\t\t\t\t\t<span>{{ item }}</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"icon\">\nimport { computed, ref } from 'vue';\n\nconst iconList: Array<string> = [\n\t'attentionforbid',\n\t'attentionforbidfill',\n\t'attention',\n\t'attentionfill',\n\t'tag',\n\t'tagfill',\n\t'people',\n\t'peoplefill',\n\t'notice',\n\t'noticefill',\n\t'mobile',\n\t'mobilefill',\n\t'voice',\n\t'voicefill',\n\t'unlock',\n\t'lock',\n\t'home',\n\t'homefill',\n\t'delete',\n\t'deletefill',\n\t'notification',\n\t'notificationfill',\n\t'notificationforbidfill',\n\t'like',\n\t'likefill',\n\t'comment',\n\t'commentfill',\n\t'camera',\n\t'camerafill',\n\t'warn',\n\t'warnfill',\n\t'time',\n\t'timefill',\n\t'location',\n\t'locationfill',\n\t'favor',\n\t'favorfill',\n\t'skin',\n\t'skinfill',\n\t'news',\n\t'newsfill',\n\t'record',\n\t'recordfill',\n\t'emoji',\n\t'emojifill',\n\t'message',\n\t'messagefill',\n\t'goods',\n\t'goodsfill',\n\t'crown',\n\t'crownfill',\n\t'move',\n\t'add',\n\t'hot',\n\t'hotfill',\n\t'service',\n\t'servicefill',\n\t'present',\n\t'presentfill',\n\t'pic',\n\t'picfill',\n\t'rank',\n\t'rankfill',\n\t'male',\n\t'female',\n\t'down',\n\t'top',\n\t'recharge',\n\t'rechargefill',\n\t'forward',\n\t'forwardfill',\n\t'info',\n\t'infofill',\n\t'redpacket',\n\t'redpacket_fill',\n\t'roundadd',\n\t'roundaddfill',\n\t'friendadd',\n\t'friendaddfill',\n\t'cart',\n\t'cartfill',\n\t'more',\n\t'moreandroid',\n\t'back',\n\t'right',\n\t'shop',\n\t'shopfill',\n\t'question',\n\t'questionfill',\n\t'roundclose',\n\t'roundclosefill',\n\t'roundcheck',\n\t'roundcheckfill',\n\t'global',\n\t'mail',\n\t'punch',\n\t'exit',\n\t'upload',\n\t'read',\n\t'file',\n\t'link',\n\t'full',\n\t'group',\n\t'friend',\n\t'profile',\n\t'addressbook',\n\t'calendar',\n\t'text',\n\t'copy',\n\t'share',\n\t'wifi',\n\t'vipcard',\n\t'weibo',\n\t'remind',\n\t'refresh',\n\t'filter',\n\t'settings',\n\t'scan',\n\t'qrcode',\n\t'cascades',\n\t'apps',\n\t'sort',\n\t'searchlist',\n\t'search',\n\t'edit'\n];\nconst keyword = ref('');\nconst list = computed(() => {\n\treturn iconList.filter(item => {\n\t\treturn item.indexOf(keyword.value) !== -1;\n\t});\n});\n</script>\n\n<style scoped>\n.example-p {\n\theight: 45px;\n\tdisplay: flex;\n\talign-items: center;\n}\n.search-box {\n\ttext-align: center;\n\tmargin-top: 10px;\n}\n.search {\n\twidth: 300px;\n}\nul,\nli {\n\tlist-style: none;\n}\n.icon-li {\n\tdisplay: inline-block;\n\tpadding: 10px;\n\twidth: 120px;\n\theight: 120px;\n}\n.icon-li-content {\n\tdisplay: flex;\n\theight: 100%;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tcursor: pointer;\n}\n.icon-li-content i {\n\tfont-size: 36px;\n\tcolor: #606266;\n}\n.icon-li-content span {\n\tmargin-top: 10px;\n\tcolor: #787878;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/login.vue",
    "content": "<template>\n\t<div class=\"login-wrap\">\n\t\t<div class=\"ms-login\">\n\t\t\t<div class=\"ms-title\">后台管理系统</div>\n\t\t\t<el-form :model=\"param\" :rules=\"rules\" ref=\"login\" label-width=\"0px\" class=\"ms-content\">\n\t\t\t\t<el-form-item prop=\"email\">\n\t\t\t\t\t<el-input v-model=\"param.email\" placeholder=\"email\">\n\t\t\t\t\t\t<template #prepend>\n\t\t\t\t\t\t\t<el-button :icon=\"User\"></el-button>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item prop=\"password\">\n\t\t\t\t\t<el-input type=\"password\" placeholder=\"password\" v-model=\"param.password\" show-password>\n\t\t\t\t\t\t<template #prepend>\n\t\t\t\t\t\t\t<el-button :icon=\"Lock\"></el-button>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-input>\n\t\t\t\t</el-form-item>\n\n\t\t\t\t<el-input placeholder=\"验证码\" v-model=\"param.verifyCode\" maxlength=\"8\" style=\"width:130px;\" clearable>\n\t\t\t\t</el-input>\n\t\t\t\t<br />\n\t\t\t\t<img id=\"code-img\" alt=\"单击图片刷新!\" :src=\"codeSrc\"\n\t\t\t\t\tonclick=\"this.src='/api/admin/logins/commonCode?d='+new Date()*1\">\n\t\t\t\t<br />\n\t\t\t\t<div class=\"login-btn\">\n\t\t\t\t\t<el-button type=\"primary\" @click=\"submitForm(login)\">登录</el-button>\n\t\t\t\t</div>\n\t\t\t\t<p class=\"login-tips\">验证码过期时间30s</p>\n\t\t\t</el-form>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\n\timport { ref, reactive } from 'vue';\n\timport { useTagsStore } from '../store/tags';\n\timport { usePermissStore } from '../store/permiss';\n\timport { useRouter } from 'vue-router';\n\timport { ElMessage } from 'element-plus';\n\timport type { FormInstance, FormRules } from 'element-plus';\n\timport { Lock, User } from '@element-plus/icons-vue';\n\timport { JSEncrypt } from 'jsencrypt'\n\n\timport { getPublicKey, doLogin } from '@/api/login'\n\timport { useStore } from '@/store/store'\n\timport { Code } from '@/utils/result'\n\timport MessageUtils from '@/utils/MessageUtils';\n\n\n\tinterface LoginInfo {\n\t\temail: string;\n\t\tpassword: string;\n\t\tverifyCode: string;\n\t}\n\n\tconst store = useStore();\n\tconst router = useRouter();\n\tconst param = reactive < LoginInfo > ({\n\t\temail: '',\n\t\tpassword: '',\n\t\tverifyCode: ''\n\t});\n\n\tlet codeSrc=ref('/api/admin/logins/commonCode');\n\n\tconst rules: FormRules = {\n\t\temail: [{\n\t\t\trequired: true,\n\t\t\tmessage: '请输入邮箱',\n\t\t\ttrigger: 'blur'\n\t\t}],\n\t\tpassword: [{ required: true, message: '请输入密码', trigger: 'blur' }]\n\t};\n\tconst permiss = usePermissStore();\n\tconst login = ref < FormInstance > ();\n\tconst submitForm = (formEl: FormInstance | undefined) => {\n\t\tif (!formEl) return;\n\t\tformEl.validate((valid: boolean) => {\n\t\t\tif (valid) {\n\t\t\t\t// localStorage.setItem('ms_username', param.username);\n\t\t\t\tconst keys = permiss.defaultList['admin'];\n\t\t\t\tpermiss.handleSet(keys);\n\t\t\t\tlocalStorage.setItem('ms_keys', JSON.stringify(keys));\n\t\t\t\t// router.push('/');\n\t\t\t\tgetPublicKey().then(result => {\n\t\t\t\t\treturn result.data;\n\t\t\t\t}).then(publicKey => {\n\t\t\t\t\tlet encryptor = new JSEncrypt();\n\t\t\t\t\tencryptor.setPublicKey(publicKey);\n\t\t\t\t\t//加密\n\t\t\t\t\tlet encryptRes = encryptor.encrypt(param.password);\n\t\t\t\t\tif (encryptRes != false) {\n\t\t\t\t\t\tlet adminLoginReq = {\n\t\t\t\t\t\t\temail: param.email,\n\t\t\t\t\t\t\tpassword: encryptRes,\n\t\t\t\t\t\t\tverifyCode: param.verifyCode\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdoLogin(adminLoginReq).then((res: any) => {\n\t\t\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t\t\tstore.setToken(res.data)\n\t\t\t\t\t\t\t\tMessageUtils.success('登录成功', 1);\n\t\t\t\t\t\t\t\tsetTimeout(() => { router.push({ path: '/' }); }, 500);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tElMessage.error('失败');\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t};\n\n\tconst tags = useTagsStore();\n\ttags.clearTags();\n</script>\n\n<style scoped>\n\t.login-wrap {\n\t\tposition: relative;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tbackground-image: url(../assets/img/login-bg.jpg);\n\t\tbackground-size: 100%;\n\t}\n\n\t.ms-title {\n\t\twidth: 100%;\n\t\tline-height: 50px;\n\t\ttext-align: center;\n\t\tfont-size: 20px;\n\t\tcolor: #fff;\n\t\tborder-bottom: 1px solid #ddd;\n\t}\n\n\t.ms-login {\n\t\tposition: absolute;\n\t\tleft: 50%;\n\t\ttop: 50%;\n\t\twidth: 350px;\n\t\tmargin: -190px 0 0 -175px;\n\t\tborder-radius: 5px;\n\t\tbackground: rgba(255, 255, 255, 0.3);\n\t\toverflow: hidden;\n\t}\n\n\t.ms-content {\n\t\tpadding: 30px 30px;\n\t}\n\n\t.login-btn {\n\t\ttext-align: center;\n\t}\n\n\t.login-btn button {\n\t\twidth: 100%;\n\t\theight: 36px;\n\t\tmargin-bottom: 10px;\n\t}\n\n\t.login-tips {\n\t\tfont-size: 12px;\n\t\tline-height: 30px;\n\t\tcolor: #fff;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/markdown.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">\n\t\t\tmd-editor-v3：vue3版本的 markdown 编辑器，配置丰富，请详看文档。 访问地址：\n\t\t\t<a href=\"https://imzbf.github.io/md-editor-v3/index\" target=\"_blank\">md-editor-v3</a>\n\t\t</div>\n\t\t<md-editor class=\"mgb20\" v-model=\"text\" @on-upload-img=\"onUploadImg\" />\n\t\t<el-button type=\"primary\">提交</el-button>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"md\">\nimport { ref } from 'vue';\nimport MdEditor from 'md-editor-v3';\nimport 'md-editor-v3/lib/style.css';\n\nconst text = ref('Hello Editor!');\nconst onUploadImg = (files: any) => {\n\tconsole.log(files);\n};\n</script>\n"
  },
  {
    "path": "vue-manage-system/src/views/permission/permission.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<div class=\"handle-box\">\n\t\t\t\t\t<el-button type=\"primary\" :icon=\"Plus\" @click=\"addVisible=true\">新增</el-button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<el-table :data=\"permissionList\" border class=\"table\" ref=\"multipleTable\"\n\t\t\t\theader-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column label=\"序号\" width=\"55\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{scope.$index+1}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"55\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"label\" label=\"名称\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"code\" label=\"权限码\" width=\"120\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"module\" label=\"是否是模块\" width=\"60\"></el-table-column>\n\t\t\t\t<el-table-column label=\"父模块\"  align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{CommonUtils.isEmpty(scope.row.parent)?'无':scope.row.parent.label}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"note\" label=\"备注\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"createTime\" label=\"创建时间\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"updateTime\" label=\"修改时间\"></el-table-column>\n\n\t\t\t\t<el-table-column label=\"操作\" width=\"220\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button size=\"small\" @click=\"handleEdit(scope.row)\">\n\t\t\t\t\t\t\t编辑\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t<el-button type=\"danger\" size=\"small\" @click=\"handleDelete(scope.$index)\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" v-model:current-page=\"query.pageNo\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"totalCount\" @current-change=\"handlePageNoChange\">\n\t\t\t\t</el-pagination>\n\t\t\t</div>\n\n\n\t\t\t<div class=\"mgb20 tree-wrapper\">\n\t\t\t\t权限树\n\t\t\t\t<el-tree ref=\"tree\" :data=\"permissionTree\" node-key=\"id\" default-expand-all />\n\t\t\t</div>\n\t\t</div>\n\t\t<!-- 新增权限对话框 -->\n\t\t<el-dialog title=\"新增权限\" v-model=\"addVisible\">\n\t\t\t<el-form :model=\"addForm\">\n\t\t\t\t<el-form-item label=\"父级模块(可为空)\">\n\t\t\t\t\t<el-select v-model=\"addForm.parentId\" placeholder=\"父级模块(可为空)\" class=\"handle-select mr10\" clearable>\n\t\t\t\t\t\t<el-option v-for=\"item in moduleList\" :key=\"item.id\" :label=\"item.label\" :value=\"item.id\">\n\t\t\t\t\t\t</el-option>\n\t\t\t\t\t</el-select>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"权限码\">\n\t\t\t\t\t<el-input v-model=\"addForm.code\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"名称\">\n\t\t\t\t\t<el-input v-model=\"addForm.label\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"是否是模块\">\n\t\t\t\t\t<el-select v-model=\"addForm.module\" class=\"handle-select mr10\" clearable>\n\t\t\t\t\t\t<el-option key=\"1\" label=\"是\" :value=\"true\"></el-option>\n\t\t\t\t\t\t<el-option key=\"1\" label=\"否\" :value=\"false\"></el-option>\n\t\t\t\t\t</el-select>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"备注\">\n\t\t\t\t\t<el-input v-model=\"addForm.note\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"addVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveAdd\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t\t<!-- 编辑弹出框 -->\n\t\t<el-dialog title=\"编辑\" v-model=\"editVisible\">\n\t\t\t<el-form label-width=\"120px\">\n\t\t\t\t<el-form-item label=\"父级模块(可为空)\">\n\t\t\t\t\t<el-select v-model=\"editForm.parentId\" placeholder=\"父级模块(可为空)\" class=\"handle-select mr10\" clearable>\n\t\t\t\t\t\t<el-option v-for=\"item in moduleList\" :key=\"item.id\" :label=\"item.label\" :value=\"item.id\">\n\t\t\t\t\t\t</el-option>\n\t\t\t\t\t</el-select>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"权限码(慎重修改)\">\n\t\t\t\t\t<el-input v-model=\"editForm.code\" maxlength=\"50\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"名称\">\n\t\t\t\t\t<el-input v-model=\"editForm.label\" maxlength=\"20\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"备注\">\n\t\t\t\t\t<el-input v-model=\"editForm.note\" maxlength=\"20\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"editVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveEdit\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"authorize\">\n\timport { ref, reactive } from 'vue';\n\timport { useRoute, useRouter } from \"vue-router\"\n\timport { Plus } from '@element-plus/icons-vue';\n\timport {\n\t\tqueryModules,\n\t\tqueryPermissionTree,\n\t\tpagePermission,\n\t\taddPermission,\n\t\tmodifyPermission,\n\t\tdeletePermission\n\t} from '@/api/permission';\n\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\n\n\texport interface Permission {\n\t\tid: number;\n\t\tparentId: number;\n\t\tparent ? : null | Permission;\n\t\tcode: null | string;\n\t\tnote: string;\n\t\tmodule: boolean;\n\t\tlabel: string;\n\t\tcreateTime: string;\n\t\tupdateTime: string;\n\t\tchildren ? : null | Permission[];\n\t}\n\n\t//查询模块\n\tlet moduleList = ref < Permission[] > ([]);\n\tconst getModules = () => {\n\t\tqueryModules().then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tmoduleList.value = res.data;\n\t\t\t}\n\t\t})\n\t};\n\tgetModules();\n\n\t//查询权限树\n\tlet permissionTree = ref < Permission[] > ([{\n\t\tid: 1,\n\t\tcode: 'user:update',\n\t\tlabel: '用户更新信息',\n\t\tparentId: -1,\n\t\tparent: null,\n\t\tmodule: false,\n\t\tnote: '用户',\n\t\tcreateTime: '2022-2-22',\n\t\tupdateTime: '2022-2-22',\n\t\tchildren: []\n\t}]);\n\tconst combineAsLabel = (node: Permission) => {\n\t\tnode.label = CommonUtils.isEmpty(node.code) ? node.label : node.code + '|' + node.label;\n\t\tfor (let item of node.children!) {\n\t\t\tcombineAsLabel(item);\n\t\t}\n\t}\n\tconst getPermissionTree = () => {\n\t\tqueryPermissionTree().then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tpermissionTree.value = res.data;\n\t\t\t\tfor (let item of permissionTree.value!) {\n\t\t\t\t\tcombineAsLabel(item);\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t};\n\tgetPermissionTree();\n\n\t//查询权限列表\n\tconst route = useRoute();\n\t// const router=useRouter();\n\tlet permissionList = ref < Permission[] > ([]);\n\tlet query = ref({\n\t\tpageNo: parseInt(route.params.pageNo[0]),\n\t\tpageSize: 10\n\t});\n\tconst handlePageNoChange = () => {\n\t\twindow.location.href = \"/#/permission/\" + query.value.pageNo;\n\t\t// router.replace({name:\"permission\",params:{pageNo:queryPage.value.pageNo.toString()}})\n\t\t// route.path.replace('pageNo', queryPage.value.pageNo.toString())\n\t\t// route.params.pageNo=queryPage.value.pageNo.toString();\n\t\tgetPermissionPage();\n\t}\n\tconst totalCount = ref(1);\n\tconst getPermissionPage = () => {\n\t\tpagePermission(query.value.pageNo, query.value.pageSize).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tpermissionList.value = res.data.dataList;\n\t\t\t\ttotalCount.value = res.data.totalCount;\n\t\t\t}\n\t\t})\n\t};\n\tgetPermissionPage();\n\n\t//新增权限\n\tlet addVisible = ref(false);\n\tlet addForm = reactive({\n\t\tmodule: false,\n\t\tcode: '',\n\t\tlabel: '',\n\t\tparentId: null,\n\t\tnote: '',\n\t});\n\tconst saveAdd = () => {\n\t\taddPermission(addForm).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"增加成功\", 1);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t}\n\t\t});\n\t\taddVisible.value = false;\n\t};\n\n\t//编辑\n\tlet editVisible = ref(false);\n\tlet editForm = reactive({\n\t\tid: 0,\n\t\tcode: null,\n\t\tlabel: '',\n\t\tparentId: null,\n\t\tnote: '',\n\t});\n\tconst handleEdit = (row: any) => {\n\t\teditForm.id = row.id;\n\t\teditForm.code = row.code;\n\t\teditForm.label = row.label;\n\t\teditForm.note = row.note;\n\t\teditForm.parentId = row.parentId;\n\t\teditVisible.value = true;\n\t};\n\tconst saveEdit = () => {\n\t\tmodifyPermission(editForm).then((res: any) => {\n\t\t\tif (res.code = Code.OK) {\n\t\t\t\tMessageUtils.success(\"修改成功\", 1);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\teditVisible.value = false;\n\t\t\t}\n\t\t});\n\t};\n\n\t//删除\n\tconst handleDelete = (index: number) => {\n\t\tMessageUtils.confirm(\"确定删除吗？操作不可逆！\").then(() => {\n\t\t\tconst deleteId = permissionList.value[index].id;\n\t\t\tdeletePermission(deleteId)\n\t\t\t\t.then((res: any) => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"删除成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}).catch(e => e);\n\t}\n</script>\n\n<style scoped>\n\t.hide :deep() .el-upload--picture-card {\n\t\tdisplay: none;\n\t}\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 200px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/permissionx.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<div class=\"plugins-tips\">通过 v-permiss 自定义指令实现权限管理，使用非 admin 账号登录，可查看效果。</div>\n\t\t<div class=\"mgb20\">\n\t\t\t<span class=\"label\">角色：</span>\n\t\t\t<el-select v-model=\"role\" @change=\"handleChange\">\n\t\t\t\t<el-option label=\"超级管理员\" value=\"admin\"></el-option>\n\t\t\t\t<el-option label=\"普通用户\" value=\"user\"></el-option>\n\t\t\t</el-select>\n\t\t</div>\n\t\t<div class=\"mgb20 tree-wrapper\">\n\t\t\t<el-tree\n\t\t\t\tref=\"tree\"\n\t\t\t\t:data=\"data\"\n\t\t\t\tnode-key=\"id\"\n\t\t\t\tdefault-expand-all\n\t\t\t\tshow-checkbox\n\t\t\t\t:default-checked-keys=\"checkedKeys\"\n\t\t\t/>\n\t\t</div>\n\t\t<el-button type=\"primary\" @click=\"onSubmit\">保存权限</el-button>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"permissionx\">\nimport { ref } from 'vue';\nimport { ElTree } from 'element-plus';\nimport { usePermissStore } from '@/store/permiss';\n\nconst role = ref<string>('admin');\n\ninterface Tree {\n\tid: string;\n\tlabel: string;\n\tchildren?: Tree[];\n}\n\nconst data: Tree[] = [\n\t{\n\t\tid: '1',\n\t\tlabel: '系统首页'\n\t},\n\t{\n\t\tid: '2',\n\t\tlabel: '基础表格',\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tid: '15',\n\t\t\t\tlabel: '编辑'\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: '16',\n\t\t\t\tlabel: '删除'\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tid: '3',\n\t\tlabel: 'tab选项卡'\n\t},\n\t{\n\t\tid: '4',\n\t\tlabel: '表单相关',\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tid: '5',\n\t\t\t\tlabel: '基本表单'\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: '6',\n\t\t\t\tlabel: '文件上传'\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: '7',\n\t\t\t\tlabel: '三级菜单',\n\t\t\t\tchildren: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: '8',\n\t\t\t\t\t\tlabel: '富文本编辑器'\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: '9',\n\t\t\t\t\t\tlabel: 'markdown编辑器'\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tid: '10',\n\t\tlabel: '自定义图标'\n\t},\n\t{\n\t\tid: '11',\n\t\tlabel: 'schart图表'\n\t},\n\n\t{\n\t\tid: '13',\n\t\tlabel: '权限管理'\n\t},\n\t{\n\t\tid: '14',\n\t\tlabel: '支持作者'\n\t}\n];\n\nconst permiss = usePermissStore();\n\n// 获取当前权限\nconst checkedKeys = ref<string[]>([]);\nconst getPremission = () => {\n\t// 请求接口返回权限\n\tcheckedKeys.value = permiss.defaultList[role.value];\n};\ngetPremission();\n\n// 保存权限\nconst tree = ref<InstanceType<typeof ElTree>>();\nconst onSubmit = () => {\n\t// 获取选中的权限\n\tconsole.log(tree.value!.getCheckedKeys(false));\n};\n\nconst handleChange = (val: string[]) => {\n\ttree.value!.setCheckedKeys(permiss.defaultList[role.value]);\n};\n</script>\n\n<style scoped>\n.tree-wrapper {\n\tmax-width: 500px;\n}\n.label {\n\tfont-size: 14px;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/role/role.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<el-button type=\"primary\" :icon=\"Plus\" @click=\"addVisible=true\">新增</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"store.roleList\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column label=\"序号\" width=\"55\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{scope.$index+1}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"55\" align=\"center\"></el-table-column>\n\n\t\t\t\t<el-table-column prop=\"roleName\" label=\"角色名\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"note\" label=\"备注\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"createTime\" label=\"创建时间\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"updateTime\" label=\"修改时间\"></el-table-column>\n\n\t\t\t\t<el-table-column label=\"操作\" width=\"220\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button size=\"small\" @click=\"handleEdit(scope.row)\">\n\t\t\t\t\t\t\t编辑\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t<el-button type=\"danger\" size=\"small\" @click=\"handleDelete(scope.$index)\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<!-- \t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" :current-page=\"query.pageIndex\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"pageTotal\" @current-change=\"handlePageChange\"></el-pagination>\n\t\t\t</div> -->\n\t\t</div>\n\n\t\t<!-- 编辑弹出框 -->\n\t\t<el-dialog title=\"编辑\" v-model=\"editVisible\">\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"角色名\">\n\t\t\t\t\t<el-input v-model=\"editForm.roleName\" maxlength=\"20\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"备注\">\n\t\t\t\t\t<el-input v-model=\"editForm.note\" maxlength=\"20\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"editVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveEdit\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t\t<!-- 新增角色对话框 -->\n\t\t<el-dialog title=\"新增角色\" v-model=\"addVisible\">\n\t\t\t<el-form :model=\"addForm\">\n\t\t\t\t<el-form-item label=\"角色名\">\n\t\t\t\t\t<el-input v-model=\"addForm.roleName\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"备注\">\n\t\t\t\t\t<el-input v-model=\"addForm.note\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"addVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveAdd\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"role\">\n\timport { ref, reactive } from 'vue';\n\timport { Plus } from '@element-plus/icons-vue';\n\timport { queryAllRoles, addRole, deleteRole, modifyRole } from '@/api/role';\n\timport { useStore } from '@/store/store';\n\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\n\texport interface Role {\n\t\tid: number;\n\t\troleName: string;\n\t\tnote: string;\n\t\tcreateTime: string;\n\t\tupdateTime: string;\n\t}\n\n\tconst store = useStore();\n\tstore.init();\n\n\n\t//新增角色\n\tlet addVisible = ref(false);\n\tlet addForm = reactive({\n\t\troleName: '',\n\t\tnote: '',\n\t});\n\tconst saveAdd = () => {\n\t\taddRole(addForm).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tMessageUtils.success(\"增加成功\", 1);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t}\n\t\t});\n\t\taddVisible.value = false;\n\t};\n\n\t//编辑\n\tlet editVisible = ref(false);\n\tlet editForm = reactive({\n\t\tid: -1,\n\t\troleName: '',\n\t\tnote: '',\n\t});\n\tconst handleEdit = (row: any) => {\n\t\teditForm.id = row.id;\n\t\teditForm.roleName = row.roleName;\n\t\teditForm.note = row.note;\n\t\teditVisible.value = true;\n\t};\n\tconst saveEdit = () => {\n\t\tmodifyRole(editForm).then((res: any) => {\n\t\t\tif (res.code = Code.OK) {\n\t\t\t\tMessageUtils.success(\"修改成功\", 1);\n\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\teditVisible.value = false;\n\t\t\t}\n\t\t});\n\t};\n\n\t//删除\n\tconst handleDelete = (index: number) => {\n\t\tMessageUtils.confirm(\"确定删除吗？操作不可逆！\").then(() => {\n\t\t\tconst deleteId = store.roleList[index].id;\n\t\t\tdeleteRole(deleteId)\n\t\t\t\t.then((res: any) => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"删除成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}).catch(e => e);\n\n\t}\n</script>\n\n<style scoped>\n\t.hide :deep() .el-upload--picture-card {\n\t\tdisplay: none;\n\t}\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 120px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/table.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<el-select v-model=\"query.address\" placeholder=\"地址\" class=\"handle-select mr10\">\n\t\t\t\t\t<el-option key=\"1\" label=\"广东省\" value=\"广东省\"></el-option>\n\t\t\t\t\t<el-option key=\"2\" label=\"湖南省\" value=\"湖南省\"></el-option>\n\t\t\t\t</el-select>\n\t\t\t\t<el-input v-model=\"query.name\" placeholder=\"用户名\" class=\"handle-input mr10\"></el-input>\n\t\t\t\t<el-button type=\"primary\" :icon=\"Search\" @click=\"handleSearch\">搜索</el-button>\n\t\t\t\t<el-button type=\"primary\" :icon=\"Plus\">新增</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"tableData\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"55\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"name\" label=\"用户名\"></el-table-column>\n\t\t\t\t<el-table-column label=\"账户余额\">\n\t\t\t\t\t<template #default=\"scope\">￥{{ scope.row.money }}</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column label=\"头像(查看大图)\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-image class=\"table-td-thumb\" :src=\"scope.row.thumb\" :z-index=\"10\"\n\t\t\t\t\t\t\t:preview-src-list=\"[scope.row.thumb]\" preview-teleported>\n\t\t\t\t\t\t</el-image>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"address\" label=\"地址\"></el-table-column>\n\t\t\t\t<el-table-column label=\"状态\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-tag :type=\"scope.row.state === '成功' ? 'success' : scope.row.state === '失败' ? 'danger' : ''\">\n\t\t\t\t\t\t\t{{ scope.row.state }}\n\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\n\t\t\t\t<el-table-column prop=\"date\" label=\"注册时间\"></el-table-column>\n\t\t\t\t<el-table-column label=\"操作\" width=\"220\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button text :icon=\"Edit\" @click=\"handleEdit(scope.$index, scope.row)\" v-permiss=\"15\">\n\t\t\t\t\t\t\t编辑\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t<el-button text :icon=\"Delete\" class=\"red\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" :current-page=\"query.pageIndex\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"pageTotal\" @current-change=\"handlePageChange\"></el-pagination>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- 编辑弹出框 -->\n\t\t<el-dialog title=\"编辑\" v-model=\"editVisible\" width=\"30%\">\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"用户名\">\n\t\t\t\t\t<el-input v-model=\"form.name\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"地址\">\n\t\t\t\t\t<el-input v-model=\"form.address\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"editVisible = false\">取 消</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveEdit\">确 定</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"basetable\">\n\timport { ref, reactive } from 'vue';\n\timport { ElMessage, ElMessageBox } from 'element-plus';\n\timport { Delete, Edit, Search, Plus } from '@element-plus/icons-vue';\n\timport { fetchData } from '../api/index';\n\n\tinterface TableItem {\n\t\tid: number;\n\t\tname: string;\n\t\tmoney: string;\n\t\tstate: string;\n\t\tdate: string;\n\t\taddress: string;\n\t}\n\n\tconst query = reactive({\n\t\taddress: '',\n\t\tname: '',\n\t\tpageIndex: 1,\n\t\tpageSize: 10\n\t});\n\tconst tableData = ref < TableItem[] > ([]);\n\tconst pageTotal = ref(0);\n\t// 获取表格数据\n\tconst getData = () => {\n\t\tfetchData().then(res => {\n\t\t\ttableData.value = res.data.list;\n\t\t\tpageTotal.value = res.data.pageTotal || 50;\n\t\t});\n\t};\n\tgetData();\n\n\t// 查询操作\n\tconst handleSearch = () => {\n\t\tquery.pageIndex = 1;\n\t\tgetData();\n\t};\n\t// 分页导航\n\tconst handlePageChange = (val: number) => {\n\t\tquery.pageIndex = val;\n\t\tgetData();\n\t};\n\n\t// 删除操作\n\tconst handleDelete = (index: number) => {\n\t\t// 二次确认删除\n\t\tElMessageBox.confirm('确定要删除吗？', '提示', {\n\t\t\t\ttype: 'warning'\n\t\t\t})\n\t\t\t.then(() => {\n\t\t\t\tElMessage.success('删除成功');\n\t\t\t\ttableData.value.splice(index, 1);\n\t\t\t})\n\t\t\t.catch(() => {});\n\t};\n\n\t// 表格编辑时弹窗和保存\n\tconst editVisible = ref(false);\n\tlet form = reactive({\n\t\tname: '',\n\t\taddress: ''\n\t});\n\tlet idx: number = -1;\n\tconst handleEdit = (index: number, row: any) => {\n\t\tidx = index;\n\t\tform.name = row.name;\n\t\tform.address = row.address;\n\t\teditVisible.value = true;\n\t};\n\tconst saveEdit = () => {\n\t\teditVisible.value = false;\n\t\tElMessage.success(`修改第 ${idx + 1} 行成功`);\n\t\ttableData.value[idx].name = form.name;\n\t\ttableData.value[idx].address = form.address;\n\t};\n</script>\n\n<style scoped>\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 120px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/tabs.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<el-tabs v-model=\"message\">\n\t\t\t<el-tab-pane :label=\"`未读消息(${state.unread.length})`\" name=\"first\">\n\t\t\t\t<el-table :data=\"state.unread\" :show-header=\"false\" style=\"width: 100%\">\n\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t<span class=\"message-title\">{{ scope.row.title }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-table-column>\n\t\t\t\t\t<el-table-column prop=\"date\" width=\"180\"></el-table-column>\n\t\t\t\t\t<el-table-column width=\"120\">\n\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t<el-button size=\"small\" @click=\"handleRead(scope.$index)\">标为已读</el-button>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</el-table-column>\n\t\t\t\t</el-table>\n\t\t\t\t<div class=\"handle-row\">\n\t\t\t\t\t<el-button type=\"primary\">全部标为已读</el-button>\n\t\t\t\t</div>\n\t\t\t</el-tab-pane>\n\t\t\t<el-tab-pane :label=\"`已读消息(${state.read.length})`\" name=\"second\">\n\t\t\t\t<template v-if=\"message === 'second'\">\n\t\t\t\t\t<el-table :data=\"state.read\" :show-header=\"false\" style=\"width: 100%\">\n\t\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<span class=\"message-title\">{{ scope.row.title }}</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t\t<el-table-column prop=\"date\" width=\"150\"></el-table-column>\n\t\t\t\t\t\t<el-table-column width=\"120\">\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<el-button type=\"danger\" @click=\"handleDel(scope.$index)\">删除</el-button>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t</el-table>\n\t\t\t\t\t<div class=\"handle-row\">\n\t\t\t\t\t\t<el-button type=\"danger\">删除全部</el-button>\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</el-tab-pane>\n\t\t\t<el-tab-pane :label=\"`回收站(${state.recycle.length})`\" name=\"third\">\n\t\t\t\t<template v-if=\"message === 'third'\">\n\t\t\t\t\t<el-table :data=\"state.recycle\" :show-header=\"false\" style=\"width: 100%\">\n\t\t\t\t\t\t<el-table-column>\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<span class=\"message-title\">{{ scope.row.title }}</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t\t<el-table-column prop=\"date\" width=\"150\"></el-table-column>\n\t\t\t\t\t\t<el-table-column width=\"120\">\n\t\t\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t\t\t<el-button @click=\"handleRestore(scope.$index)\">还原</el-button>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</el-table-column>\n\t\t\t\t\t</el-table>\n\t\t\t\t\t<div class=\"handle-row\">\n\t\t\t\t\t\t<el-button type=\"danger\">清空回收站</el-button>\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</el-tab-pane>\n\t\t</el-tabs>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"tabs\">\nimport { ref, reactive } from 'vue';\n\nconst message = ref('first');\nconst state = reactive({\n\tunread: [\n\t\t{\n\t\t\tdate: '2018-04-19 20:00:00',\n\t\t\ttitle: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'\n\t\t},\n\t\t{\n\t\t\tdate: '2018-04-19 21:00:00',\n\t\t\ttitle: '今晚12点整发大红包，先到先得'\n\t\t}\n\t],\n\tread: [\n\t\t{\n\t\t\tdate: '2018-04-19 20:00:00',\n\t\t\ttitle: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'\n\t\t}\n\t],\n\trecycle: [\n\t\t{\n\t\t\tdate: '2018-04-19 20:00:00',\n\t\t\ttitle: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'\n\t\t}\n\t]\n});\n\nconst handleRead = (index: number) => {\n\tconst item = state.unread.splice(index, 1);\n\tstate.read = item.concat(state.read);\n};\nconst handleDel = (index: number) => {\n\tconst item = state.read.splice(index, 1);\n\tstate.recycle = item.concat(state.recycle);\n};\nconst handleRestore = (index: number) => {\n\tconst item = state.recycle.splice(index, 1);\n\tstate.read = item.concat(state.read);\n};\n</script>\n\n<style>\n.message-title {\n\tcursor: pointer;\n}\n.handle-row {\n\tmargin-top: 30px;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/topic/topic.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<el-select v-model=\"query.column\" placeholder=\"排序字段\" class=\"handle-select mr10\" style=\"width:160px;\">\n\t\t\t\t\t<el-option key=\"1\" label=\"createTime\" value=\"createTime\"></el-option>\n\t\t\t\t\t<el-option key=\"2\" label=\"updateTime\" value=\"updateTime\"></el-option>\n\t\t\t\t\t<el-option key=\"3\" label=\"starCount\" value=\"starCount\"></el-option>\n\t\t\t\t\t<el-option key=\"4\" label=\"commentCount\" value=\"commentCount\"></el-option>\n\t\t\t\t\t<el-option key=\"5\" label=\"pageView\" value=\"pageView\"></el-option>\n\t\t\t\t</el-select>\n\t\t\t\t<el-input placeholder=\"用户id\" class=\"handle-input mr10\"></el-input>\n\t\t\t\t<el-button type=\"primary\" @click=\"getData\">搜索</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"topics\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column label=\"序号\" width=\"55\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{scope.$index+1}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"50\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"userId\" label=\"用户ID\" width=\"50\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column label=\"封面\" align=\"center\" width=\"100\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-image class=\"table-td-thumb\" :src=\"scope.row.coverImageUrl\"\n\t\t\t\t\t\t\t:z-index=\"10\" :preview-src-list=\"[scope.row.coverImageUrl]\"\n\t\t\t\t\t\t\tpreview-teleported>\n\t\t\t\t\t\t</el-image>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"title\" label=\"标题\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"content\" label=\"内容\" width=\"200\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{global.omitStr(scope.row.content,100)}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"pageView\" label=\"浏览量\" width=\"50\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"starCount\" label=\"star\" width=\"50\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"commentCount\" label=\"评论数\" width=\"50\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"updateTime\" label=\"更新时间\" width=\"70\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"createTime\" label=\"创建时间\" width=\"70\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"activityTime\" label=\"活跃时间\" width=\"70\"></el-table-column>\n\n\t\t\t\t<el-table-column label=\"操作\" width=\"250\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button type=\"danger\" size=\"small\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" v-model:current-page=\"query.pageNo\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"totalCount\" @current-change=\"handlePageChange\"></el-pagination>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"topic\">\n\timport { ref, reactive, onMounted } from 'vue';\n\timport { useStore } from '@/store/store';\n\n\timport { queryTopicsOrderBy, deleteTopic } from '@/api/topic'\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\timport global from '@/utils/global'\n\n\n\tconst store = useStore();\n\tstore.init();\n\t\n\tinterface Topic {\n\t\tid: number;\n\t\ttitle: string;\n\t\tcontent: string;\n\t\tcoverImageUrl: string;\n\t\tuserId: number;\n\t\tstarCount: number;\n\t\tcommentCount: number;\n\t\tpageView: number;\n\t\tactivityTime: string;\n\t\tcategotyId: number;\n\t\tupdateTime: string;\n\t\tcreateTime: string;\n\t}\n\t//查询话题\n\tlet topics = ref < Topic[] > ([{\n\t\tid: 1008610086,\n\t\ttitle: \"表他说看得见哈肯德基刷卡单\",\n\t\tcontent: \"阿坎吉合肥市拉开收到货拉愧疚但是\",\n\t\tcoverImageUrl: 'homeCarousel/2023/01/07/1611730070081744896.jpeg',\n\t\tuserId: 64645,\n\t\t// user: { username: \"用户名用户名用户名用户名\" },\n\t\tstarCount: 54564,\n\t\tcommentCount: 9786,\n\t\tpageView: 64546,\n\t\tactivityTime: '2016-05-02 22:22:22',\n\t\tcategotyId: 3564,\n\t\tupdateTime: '2016-05-02 22:22:22',\n\t\tcreateTime: '2016-05-02 22:22:22',\n\t}]);\n\t\n\tlet totalCount = ref(1);\n\n\tlet query = reactive({\n\t\tcolumn: 'createTime',\n\t\tpageNo: 1,\n\t\tpageSize: 10,\n\t});\n\tconst getData = () => {\n\t\tqueryTopicsOrderBy(query).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\ttopics.value = res.data.dataList;\n\t\t\t\ttotalCount.value = res.data.totalCount;\n\t\t\t}\n\t\t});\n\t}\n\tgetData();\n\tconst handlePageChange = () => {\n\t\tgetData();\n\t};\n\n\t//删除\n\tconst handleDelete = (index: number) => {\n\t\tMessageUtils.confirm(\"确定删除吗？操作不可逆！\").then(() => {\n\t\t\tconst deleteId = topics.value[index].id;\n\t\t\tdeleteTopic(deleteId)\n\t\t\t\t.then((res: any) => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"删除成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}).catch(e => e);\n\t}\n\n\n\n</script>\n\n<style scoped>\n\t.hide :deep() .el-upload--picture-card {\n\t\tdisplay: none;\n\t}\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 120px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/upload.vue",
    "content": "<template>\n    <div class=\"container\">\n        <div class=\"content-title\">支持拖拽</div>\n        <div class=\"plugins-tips\">\n            Element Plus自带上传组件。 访问地址：\n            <a href=\"https://element-plus.org/zh-CN/component/upload.html\" target=\"_blank\">Element Plus Upload</a>\n        </div>\n        <el-upload\n            class=\"upload-demo\"\n            drag\n            action=\"http://jsonplaceholder.typicode.com/api/posts/\"\n            multiple\n            :on-change=\"handle\"\n        >\n            <el-icon class=\"el-icon--upload\"><upload-filled /></el-icon>\n            <div class=\"el-upload__text\">\n                将文件拖到此处，或\n                <em>点击上传</em>\n            </div>\n        </el-upload>\n\n        <div class=\"content-title\">支持裁剪</div>\n        <div class=\"plugins-tips\">\n            vue-cropperjs：一个封装了 cropperjs 的 Vue 组件。 访问地址：\n            <a href=\"https://github.com/Agontuk/vue-cropperjs\" target=\"_blank\">vue-cropperjs</a>。 示例请查看\n            <router-link to=\"/user\">个人中心</router-link>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nconst handle = (rawFile: any) => {\n    console.log(rawFile);\n};\n</script>\n\n<style scoped>\n.content-title {\n    font-weight: 400;\n    line-height: 50px;\n    margin: 10px 0;\n    font-size: 22px;\n    color: #1f2f3d;\n}\n.upload-demo {\n    width: 360px;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/user/user.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"handle-box\">\n\t\t\t\t<!-- \t\t\t\t<el-select placeholder=\"排序字段\" class=\"handle-select mr10\" style=\"width:160px;\">\n\t\t\t\t\t<el-option key=\"1\" label=\"createTime\" value=\"createTime\"></el-option>\n\t\t\t\t</el-select> -->\n\t\t\t\t<el-input placeholder=\"关键字\" class=\"handle-input mr10\" v-model=\"query.keyword\"></el-input>\n\t\t\t\t<el-button type=\"primary\" @click=\"getData\">搜索</el-button>\n\t\t\t</div>\n\t\t\t<el-table :data=\"users\" border class=\"table\" ref=\"multipleTable\" header-cell-class-name=\"table-header\">\n\t\t\t\t<el-table-column label=\"序号\" width=\"55\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t{{scope.$index+1}}\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"id\" label=\"ID\" width=\"50\" align=\"center\"></el-table-column>\n\t\t\t\t<el-table-column label=\"头像\" align=\"center\" width=\"100\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-image class=\"table-td-thumb\" :src=\"scope.row.photoUrl\" :z-index=\"10\"\n\t\t\t\t\t\t\t:preview-src-list=\"[scope.row.photoUrl]\" preview-teleported>\n\t\t\t\t\t\t</el-image>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"username\" label=\"用户名\"></el-table-column>\n\t\t\t\t<el-table-column prop=\"content\" label=\"角色\" width=\"200\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-tag v-for=\"roleId in scope.row.roleIds\" tyle=\"success\">\n\t\t\t\t\t\t\t{{store.roleName(roleId) }}\n\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t\t<el-table-column prop=\"registerTime\" label=\"注册时间\" width=\"70\"></el-table-column>\n\n\t\t\t\t<el-table-column label=\"操作\" width=\"250\" align=\"center\">\n\t\t\t\t\t<template #default=\"scope\">\n\t\t\t\t\t\t<el-button size=\"small\" @click=\"handleEdit(scope.$index, scope.row)\">\n\t\t\t\t\t\t\t编辑角色\n\t\t\t\t\t\t</el-button>\n<!-- \t\t\t\t\t\t<el-button type=\"danger\" size=\"small\" @click=\"handleDelete(scope.$index)\" v-permiss=\"16\">\n\t\t\t\t\t\t\t删除\n\t\t\t\t\t\t</el-button> -->\n\t\t\t\t\t</template>\n\t\t\t\t</el-table-column>\n\t\t\t</el-table>\n\t\t\t<div class=\"pagination\">\n\t\t\t\t<el-pagination background layout=\"total, prev, pager, next\" v-model:current-page=\"query.pageNo\"\n\t\t\t\t\t:page-size=\"query.pageSize\" :total=\"totalCount\" @current-change=\"handlePageChange\"></el-pagination>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- 编辑弹出框 -->\n\t\t<el-dialog title=\"编辑\" v-model=\"editVisible\">\n\t\t\t<el-form label-width=\"70px\">\n\t\t\t\t<el-form-item label=\"当前角色\">\n\t\t\t\t\t<el-tag v-for=\"id in editForm.roleIds\" closable @close=\"handleDeleteRole(id)\">\n\t\t\t\t\t\t{{store.roleName(id) }}\n\t\t\t\t\t</el-tag>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"所有角色\">\n\t\t\t\t\t<el-tag v-for=\"role in store.roleList\" type=\"success\" class=\"hover-pointer\"\n\t\t\t\t\t\tstyle=\"margin-left: 10px;\" size=\"large\" @click=\"handleAddRole(role.id)\">\n\t\t\t\t\t\t{{role.roleName }}\n\t\t\t\t\t</el-tag>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button @click=\"editVisible = false\">关闭</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"user\">\n\timport { ref, reactive, onMounted } from 'vue';\n\n\timport { Code } from '@/utils/result';\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport MessageUtils from '@/utils/MessageUtils';\n\timport global from '@/utils/global'\n\n\timport { useStore } from '@/store/store';\n\n\timport { queryUsers } from '@/api/user'\n\timport { addRoleForUser, deleteRoleForUser } from '@/api/UserRole'\n\n\tconst store = useStore();\n\tstore.init()\n\n\tinterface User {\n\t\tid: number;\n\t\tusername: string;\n\t\tphotoUrl: string;\n\t\tcreateTime: string;\n\t\troleIds: any;\n\t}\n\tlet users = ref < User[] > ([]);\n\tlet totalCount = ref(1);\n\tlet query = reactive({\n\t\tkeyword: null,\n\t\tpageNo: 1,\n\t\tpageSize: 10,\n\t});\n\tconst getData = () => {\n\t\tqueryUsers(query).then((res: any) => {\n\t\t\tif (res.code == Code.OK) {\n\t\t\t\tusers.value = res.data.dataList;\n\t\t\t\ttotalCount.value = res.data.totalCount;\n\t\t\t}\n\t\t});\n\t}\n\tgetData();\n\tconst handlePageChange = () => {\n\t\tgetData();\n\t};\n\n\n\t//编辑角色\n\tlet editForm = reactive({\n\t\tuserId: 0,\n\t\troleIds: [],\n\t});\n\tlet editVisible = ref(false);\n\tconst handleEdit = (index: number, row: User) => {\n\t\teditForm.userId = users.value[index].id;\n\t\teditForm.roleIds = users.value[index].roleIds;\n\t\teditVisible.value = true;\n\t};\n\n\tconst handleAddRole = (roleId) => {\n\t\tMessageUtils.confirm(\"确定为该用户增加角色吗\").then(() => {\n\t\t\taddRoleForUser(editForm.userId, roleId).then((res: any) => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\tMessageUtils.success(\"增加成功\", 2);\n\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t}\n\t\t\t})\n\t\t}).catch(e => e)\n\t}\n\n\tconst handleDeleteRole = (roleId) => {\n\t\tMessageUtils.confirm(\"确定删除角色吗\").then(() => {\n\t\t\tdeleteRoleForUser(editForm.userId, roleId).then((res: any) => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\tMessageUtils.success(\"删除成功\", 2);\n\t\t\t\t\tCommonUtils.delayRefresh(1)\n\t\t\t\t}\n\t\t\t})\n\t\t}).catch(e => e)\n\n\t}\n</script>\n\n<style scoped>\n\t.hide :deep() .el-upload--picture-card {\n\t\tdisplay: none;\n\t}\n\n\t.handle-box {\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.handle-select {\n\t\twidth: 120px;\n\t}\n\n\t.handle-input {\n\t\twidth: 300px;\n\t}\n\n\t.table {\n\t\twidth: 100%;\n\t\tfont-size: 14px;\n\t}\n\n\t.red {\n\t\tcolor: #F56C6C;\n\t}\n\n\t.mr10 {\n\t\tmargin-right: 10px;\n\t}\n\n\t.table-td-thumb {\n\t\tdisplay: block;\n\t\tmargin: auto;\n\t\twidth: 40px;\n\t\theight: 40px;\n\t}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/views/user.vue",
    "content": "<template>\n\t<div>\n\t\t<el-row :gutter=\"20\">\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>基础信息</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t\t<div class=\"info\">\n\t\t\t\t\t\t<div class=\"info-image\" @click=\"showDialog\">\n\t\t\t\t\t\t\t<el-avatar :size=\"100\" :src=\"avatarImg\" />\n\t\t\t\t\t\t\t<span class=\"info-edit\">\n\t\t\t\t\t\t\t\t<i class=\"el-icon-lx-camerafill\"></i>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"info-name\">{{ name }}</div>\n\t\t\t\t\t\t<div class=\"info-desc\">不可能！我的代码怎么可能会有bug！</div>\n\t\t\t\t\t</div>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t\t<el-col :span=\"12\">\n\t\t\t\t<el-card shadow=\"hover\">\n\t\t\t\t\t<template #header>\n\t\t\t\t\t\t<div class=\"clearfix\">\n\t\t\t\t\t\t\t<span>账户编辑</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t\t<el-form label-width=\"90px\">\n\t\t\t\t\t\t<el-form-item label=\"用户名：\"> {{ name }} </el-form-item>\n\t\t\t\t\t\t<el-form-item label=\"旧密码：\">\n\t\t\t\t\t\t\t<el-input type=\"password\" v-model=\"form.old\"></el-input>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t<el-form-item label=\"新密码：\">\n\t\t\t\t\t\t\t<el-input type=\"password\" v-model=\"form.new\"></el-input>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t<el-form-item label=\"个人简介：\">\n\t\t\t\t\t\t\t<el-input v-model=\"form.desc\"></el-input>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t<el-form-item>\n\t\t\t\t\t\t\t<el-button type=\"primary\" @click=\"onSubmit\">保存</el-button>\n\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t</el-form>\n\t\t\t\t</el-card>\n\t\t\t</el-col>\n\t\t</el-row>\n\t\t<el-dialog title=\"裁剪图片\" v-model=\"dialogVisible\" width=\"600px\">\n\t\t\t<vue-cropper\n\t\t\t\tref=\"cropper\"\n\t\t\t\t:src=\"imgSrc\"\n\t\t\t\t:ready=\"cropImage\"\n\t\t\t\t:zoom=\"cropImage\"\n\t\t\t\t:cropmove=\"cropImage\"\n\t\t\t\tstyle=\"width: 100%; height: 400px\"\n\t\t\t></vue-cropper>\n\n\t\t\t<template #footer>\n\t\t\t\t<span class=\"dialog-footer\">\n\t\t\t\t\t<el-button class=\"crop-demo-btn\" type=\"primary\"\n\t\t\t\t\t\t>选择图片\n\t\t\t\t\t\t<input class=\"crop-input\" type=\"file\" name=\"image\" accept=\"image/*\" @change=\"setImage\" />\n\t\t\t\t\t</el-button>\n\t\t\t\t\t<el-button type=\"primary\" @click=\"saveAvatar\">上传并保存</el-button>\n\t\t\t\t</span>\n\t\t\t</template>\n\t\t</el-dialog>\n\t</div>\n</template>\n\n<script setup lang=\"ts\" name=\"user\">\nimport { reactive, ref } from 'vue';\nimport VueCropper from 'vue-cropperjs';\nimport 'cropperjs/dist/cropper.css';\nimport avatar from '../assets/img/img.jpg';\n\nconst name = localStorage.getItem('ms_username');\nconst form = reactive({\n\told: '',\n\tnew: '',\n\tdesc: '不可能！我的代码怎么可能会有bug！'\n});\nconst onSubmit = () => {};\n\nconst avatarImg = ref(avatar);\nconst imgSrc = ref('');\nconst cropImg = ref('');\nconst dialogVisible = ref(false);\nconst cropper: any = ref();\n\nconst showDialog = () => {\n\tdialogVisible.value = true;\n\timgSrc.value = avatarImg.value;\n};\n\nconst setImage = (e: any) => {\n\tconst file = e.target.files[0];\n\tif (!file.type.includes('image/')) {\n\t\treturn;\n\t}\n\tconst reader = new FileReader();\n\treader.onload = (event: any) => {\n\t\tdialogVisible.value = true;\n\t\timgSrc.value = event.target.result;\n\t\tcropper.value && cropper.value.replace(event.target.result);\n\t};\n\treader.readAsDataURL(file);\n};\n\nconst cropImage = () => {\n\tcropImg.value = cropper.value.getCroppedCanvas().toDataURL();\n};\n\nconst saveAvatar = () => {\n\tavatarImg.value = cropImg.value;\n\tdialogVisible.value = false;\n};\n</script>\n\n<style scoped>\n.info {\n\ttext-align: center;\n\tpadding: 35px 0;\n}\n.info-image {\n\tposition: relative;\n\tmargin: auto;\n\twidth: 100px;\n\theight: 100px;\n\tbackground: #f8f8f8;\n\tborder: 1px solid #eee;\n\tborder-radius: 50px;\n\toverflow: hidden;\n}\n\n.info-edit {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: rgba(0, 0, 0, 0.5);\n\topacity: 0;\n\ttransition: opacity 0.3s ease;\n}\n.info-edit i {\n\tcolor: #eee;\n\tfont-size: 25px;\n}\n.info-image:hover .info-edit {\n\topacity: 1;\n}\n.info-name {\n\tmargin: 15px 0 10px;\n\tfont-size: 24px;\n\tfont-weight: 500;\n\tcolor: #262626;\n}\n.crop-demo-btn {\n\tposition: relative;\n}\n.crop-input {\n\tposition: absolute;\n\twidth: 100px;\n\theight: 40px;\n\tleft: 0;\n\ttop: 0;\n\topacity: 0;\n\tcursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "vue-manage-system/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n\ndeclare module '*.vue' {\n  import type { DefineComponent } from 'vue'\n  const component: DefineComponent<{}, {}, any>\n  export default component\n}\n\ndeclare module 'vue-schart';\ndeclare module 'vue-cropperjs';"
  },
  {
    "path": "vue-manage-system/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ESNext\",\n\t\t\"useDefineForClassFields\": true,\n\t\t\"module\": \"ESNext\",\n\t\t\"moduleResolution\": \"Node\",\n\t\t\"strict\": true,\n\t\t\"jsx\": \"preserve\",\n\t\t\"sourceMap\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"lib\": [\"ESNext\", \"DOM\"],\n\t\t\"skipLibCheck\": true,\n\t\t\"baseUrl\": \".\",\n\t\t\"noImplicitAny\": false,\n\t\t\"paths\": {\n\t\t\t\"@/*\": [\"src/*\"]\n\t\t}\n\t},\n\t\"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.vue\"],\n\t\"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "vue-manage-system/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "vue-manage-system/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport vue from '@vitejs/plugin-vue';\nimport VueSetupExtend from 'vite-plugin-vue-setup-extend';\nimport AutoImport from 'unplugin-auto-import/vite';\nimport Components from 'unplugin-vue-components/vite';\nimport { ElementPlusResolver } from 'unplugin-vue-components/resolvers';\nimport { resolve } from 'path'\n\nexport default defineConfig({\n\tbase: './',\n\tplugins: [\n\t\tvue(),\n\t\tVueSetupExtend(),\n\t\tAutoImport({\n\t\t\tresolvers: [ElementPlusResolver()]\n\t\t}),\n\t\tComponents({\n\t\t\tresolvers: [ElementPlusResolver()]\n\t\t})\n\t],\n\tresolve: {\n\t\talias: {\n\t\t\t'@': resolve('src')\n\t\t}\n\t},\n\toptimizeDeps: {\n\t\tinclude: ['schart.js']\n\t},\n\tserver: {\n\t\tport: 7010,\n\t\tproxy: {\n\t\t\t\"/api\": {\n\t\t\t\t// 代理名称   凡是使用/api开头的地址都是用此代理\n\t\t\t\ttarget: \"http://127.0.0.1:81/\", // 需要代理访问的api地址\n\t\t\t\tchangeOrigin: true, // 允许跨域请求\n\t\t\t\t// pathRewrite: {\n\t\t\t\t//     // 重写路径，替换请求地址中的指定路径\n\t\t\t\t//     \"^/api\": \"/\", // 将请求地址中的/api替换为空，也就是请求地址中不会包含/api/\n\t\t\t\t// },\n\t\t\t},\n\t\t\t\"/acfile\": {\n\t\t\t\t// 代理名称   凡是使用/api开头的地址都是用此代理\n\t\t\t\ttarget: \"http://127.0.0.1:81/\", // 需要代理访问的api地址\n\t\t\t\tchangeOrigin: true, // 允许跨域请求\n\t\t\t\t// pathRewrite: {\n\t\t\t\t//     // 重写路径，替换请求地址中的指定路径\n\t\t\t\t//     \"^/api\": \"/\", // 将请求地址中的/api替换为空，也就是请求地址中不会包含/api/\n\t\t\t\t// },\n\t\t\t},\n\t\t},\n\t}\n});\n"
  },
  {
    "path": "vue_acimage_web/.gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "vue_acimage_web/README.md",
    "content": "# vue_acimage_web\n\n> acimage网页端\n\n## clone仓库\n\n```\ngit clone https://github.com/ggggborn/SpringCloud-acimage.git\n```\n\n## 进入到vue_acimage_web目录\n\n```\ncd vue_acimage_web\n```\n\n## 安装依赖\n\n```\nnpm install -i\n```\n\n## 运行\n\n```\nnpm run serve\n```\n"
  },
  {
    "path": "vue_acimage_web/babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@vue/cli-plugin-babel/preset'\n  ]\n}\n"
  },
  {
    "path": "vue_acimage_web/jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"esnext\",\n    \"baseUrl\": \"./\",\n    \"moduleResolution\": \"node\",\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ]\n    },\n    \"lib\": [\n      \"esnext\",\n      \"dom\",\n      \"dom.iterable\",\n      \"scripthost\"\n    ]\n  }\n}\n"
  },
  {
    "path": "vue_acimage_web/mock/HomeCarousel.js",
    "content": "import Mock from 'mockjs'\nimport { Code } from '@/utils/result.js'\n\nMock.mock('/api/image/homeCarousels/all', 'get', {\n\tcode: Code.OK,\n\t'data|2': [{\n\t\t\tid: '@id()',\n\t\t\tdescription: '@cparagraph()',\n\t\t\turl: '/test/test1.jpeg',\n\t\t},\n\t\t{\n\t\t\tid: '@id()',\n\t\t\tdescription: '@cparagraph()',\n\t\t\turl: '/test/test2.jpeg',\n\t\t}\n\t]\n})\n"
  },
  {
    "path": "vue_acimage_web/mock/UserRank.js",
    "content": "import Mock from 'mockjs'\nimport { Code } from '@/utils/result.js'\n\nMock.mock('/api/community/users/rank/starCount/1', 'get', {\n\tcode: Code.OK,\n\t'data|4': [{\n\t\tid: '@id',\n\t\tusername: '@cname()',\n\t\tphotoUrl: '',\n\t\tstarCount: '@integer(0, 10000)',\n\t\ttopicCount: '@integer(0, 10000)',\n\t}]\n})\n\nMock.mock('/api/community/users/rank/topicCount/1', 'get', {\n\tcode: Code.OK,\n\t'data|5': [{\n\t\tid: '@id',\n\t\tusername: '@cname()',\n\t\tphotoUrl: '',\n\t\tstarCount: '@integer(0, 10000)',\n\t\ttopicCount: '@integer(0, 10000)',\n\t}]\n})\n"
  },
  {
    "path": "vue_acimage_web/mock/index.js",
    "content": "import Mock from 'mockjs'\nrequire('./topic.js')\nrequire('./HomeCarousel.js')\nrequire('./UserRank.js')"
  },
  {
    "path": "vue_acimage_web/mock/topic.js",
    "content": "import Mock from 'mockjs'\nimport { Code } from '@/utils/result.js'\n\nMock.mock('/api/community/topics/recentHot', 'get', {\n\tcode: Code.OK,\n\t'data|4': [{\n\t\t\tid: '@id()',\n\t\t\t'title|5': '@cname()',\n\t\t\tstarCount: 888,\n\t\t\tpageView: 88888,\n\t\t\tcreateTime: '@date()',\n\t\t\tuser: {\n\t\t\t\tusername: '@cname()',\n\t\t\t\tphotoUrl: ''\n\t\t\t},\n\t\t\tcoverImageUrl: 'test/test1.jpeg',\n\t\t\tcategoryId:1,\n\t\t},\n\t\t{\n\t\t\tid: '@id()',\n\t\t\t'title|15': '@cname()',\n\t\t\tstarCount: 888,\n\t\t\tpageView: 88888,\n\t\t\tcreateTime: '2022-2-22 22:22:22',\n\t\t\tuser: {\n\t\t\t\tusername: '@cname()',\n\t\t\t\tphotoUrl: ''\n\t\t\t},\n\t\t\tcoverImageUrl: 'topicImage/2022/12/02/1573240094587424768',\n\t\t\tcategoryId:1,\n\t\t},\n\t]\n})\n\nMock.mock('/api/community/topics/recommend', 'get', {\n\tcode: Code.OK,\n\t'data|2': [{\n\t\t\tid: '@integer()',\n\t\t\t'title|5': '@cname()',\n\t\t\tcommentCount:123,\n\t\t\tstarCount: 888,\n\t\t\tpageView: 88888,\n\t\t\tcreateTime: '@date()',\n\t\t\tuser: {\n\t\t\t\tusername: '@cname()',\n\t\t\t\tphotoUrl: ''\n\t\t\t},\n\t\t\tcoverImageUrl: 'test/test1.jpeg',\n\t\t},\n\t\t{\n\t\t\tid: '@integer()',\n\t\t\t'title|15': '@cname()',\n\t\t\tcommentCount:123,\n\t\t\tstarCount: 888,\n\t\t\tpageView: 88888,\n\t\t\tcreateTime: '2022-2-22 22:22:22',\n\t\t\tuser: {\n\t\t\t\tusername: '刚刚刚刚刚刚刚刚',\n\t\t\t\tphotoUrl: ''\n\t\t\t},\n\t\t\tcoverImageUrl: 'topicImage/2022/12/02/1573240094587424768',\n\t\t\tcategoryId:1,\n\t\t},\n\t]\n})\n\nMock.mock('/api/community/topics/pageRecentTopics/1', 'get', {\n\tcode: Code.OK,\n\tdata: {\n\t\t'dataList|5': [{\n\t\t\tid: '@integer()',\n\t\t\tuserId: 999,\n\t\t\t'title|10': \"@cname()\",\n\t\t\t'content': `不知道@cparagraph()`,\n\t\t\tactivityTime: '2022-2-22 2:22:22',\n\t\t\tstarCount: 666,\n\t\t\tpageView: 777,\n\t\t\tcommentCount: 888,\n\t\t\tcoverImageUrl: 'static/image/user-rank-header.jpg',\n\t\t\tcategoryId:1,\n\t\t\timages: [{\n\t\t\t\tid: 0\n\t\t\t}],\n\t\t\tuser: {\n\t\t\t\tphotoUrl: '',\n\t\t\t\t'username|2': \"@cname()\"\n\t\t\t}\n\t\t}, ],\n\t\ttotalCount: 10,\n\t}\n\n})\n"
  },
  {
    "path": "vue_acimage_web/package.json",
    "content": "{\n  \"name\": \"vue_acimage_web\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\",\n    \"dev\": \"vue-cli-service serve --mode dev\",\n    \"build\": \"vue-cli-service build\",\n    \"lint\": \"vue-cli-service lint\"\n  },\n  \"dependencies\": {\n    \"@tinymce/tinymce-vue\": \"^3.2.8\",\n    \"axios\": \"^1.2.0\",\n    \"core-js\": \"^3.8.3\",\n    \"element-ui\": \"^2.15.12\",\n    \"jsencrypt\": \"^3.3.1\",\n    \"jwt-decode\": \"^3.1.2\",\n    \"mockjs\": \"^1.1.0\",\n    \"vue\": \"^2.6.14\",\n    \"vue-cookie\": \"^1.1.4\",\n    \"vue-cropper\": \"^0.5.8\",\n    \"vue-dompurify-html\": \"^3.1.2\",\n    \"vue-router\": \"^3.6.5\",\n    \"vuex\": \"^3.6.2\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.12.16\",\n    \"@babel/eslint-parser\": \"^7.12.16\",\n    \"@vue/cli-plugin-babel\": \"~5.0.0\",\n    \"@vue/cli-plugin-eslint\": \"~5.0.0\",\n    \"@vue/cli-service\": \"~5.0.0\",\n    \"eslint\": \"^7.32.0\",\n    \"eslint-plugin-vue\": \"^8.0.3\",\n    \"vue-template-compiler\": \"^2.6.14\"\n  },\n  \"eslintConfig\": {\n    \"root\": true,\n    \"env\": {\n      \"node\": true\n    },\n    \"extends\": [\n      \"plugin:vue/essential\",\n      \"eslint:recommended\"\n    ],\n    \"parserOptions\": {\n      \"parser\": \"@babel/eslint-parser\"\n    },\n    \"rules\": {\n      \"no-unused-vars\": \"off\"\n    }\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\",\n    \"not dead\"\n  ]\n}\n"
  },
  {
    "path": "vue_acimage_web/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n    <link rel=\"icon\" href=\"static/favicon.ico\">\n    <title>次元印象o(*≧▽≦)ツ~动漫交流论坛</title>\n\t\n\t<link rel=\"stylesheet\" href=\"static/css/common.css\">\n\t<script src=\"tinymce/tinymce.min.js\"></script>\n<!-- \t<script src=\"http://lib.baomitu.com/tinymce/6.3.1/tinymce.min.js\"></script>\n\t<script src=\"http://lib.baomitu.com/tinymce/6.3.1/themes/silver/theme.min.js\"></script> -->\n\t\n\t\n  </head>\n  <body>\n    <noscript>\n      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>\n    </noscript>\n    <div id=\"app\"></div>\n    <!-- built files will be auto injected -->\n  </body>\n</html>\n"
  },
  {
    "path": "vue_acimage_web/public/static/css/common.css",
    "content": "body {\n\tmargin: 0px;\n\tpadding: 0px;\n}\n\n/* 隐藏上传图片时的+号 */\n.upload-plus-disabled .el-upload--picture-card {\n\tdisplay: none;\n}\n\n.no-underline {\n\ttext-decoration: none;\n}\n\n/* 改变router-link颜色变化 */\n.router-link-item {\n\tcolor: #666666;\n}\n\n.router-link-item:hover {\n\tcolor: #0074A0;\n}\n\n.hover-pointer:hover {\n\tcursor: pointer\n}\n\n.hover-shadow:hover{\n\tbox-shadow: 0px 0px 15px #E7E6E3 !important;\n\ttransition: 0.4s;\n}\n\n/* .hover-border-light:hover {\n\tborder: 1px solid skyblue !important;\n} */\n\n\n.c6 {\n\tcolor: #666666;\n}\n.c9 {\n\tcolor: #999999;\n}\n"
  },
  {
    "path": "vue_acimage_web/public/tinymce/langs/README.md",
    "content": "This is where language files should be placed.\n\nPlease DO NOT translate these directly use this service: https://www.transifex.com/projects/p/tinymce/\n"
  },
  {
    "path": "vue_acimage_web/public/tinymce/langs/zh-Hans.js",
    "content": "/*!\n * TinyMCE Language Pack\n *\n * Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.\n * Licensed under the Tiny commercial license. See https://www.tiny.cloud/legal/\n */\ntinymce.addI18n('zh-Hans', {\n\"Redo\": \"重做\",\n\"Undo\": \"撤销\",\n\"Cut\": \"剪切\",\n\"Copy\": \"复制\",\n\"Paste\": \"粘贴\",\n\"Select all\": \"全选\",\n\"New document\": \"新建文档\",\n\"Ok\": \"确定\",\n\"Cancel\": \"取消\",\n\"Visual aids\": \"网格线\",\n\"Bold\": \"粗体\",\n\"Italic\": \"斜体\",\n\"Underline\": \"下划线\",\n\"Strikethrough\": \"删除线\",\n\"Superscript\": \"上标\",\n\"Subscript\": \"下标\",\n\"Clear formatting\": \"清除格式\",\n\"Remove\": \"移除\",\n\"Align left\": \"左对齐\",\n\"Align center\": \"居中对齐\",\n\"Align right\": \"右对齐\",\n\"No alignment\": \"未对齐\",\n\"Justify\": \"两端对齐\",\n\"Bullet list\": \"无序列表\",\n\"Numbered list\": \"有序列表\",\n\"Decrease indent\": \"减少缩进\",\n\"Increase indent\": \"增加缩进\",\n\"Close\": \"关闭\",\n\"Formats\": \"格式\",\n\"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.\": \"你的浏览器不支持打开剪贴板，请使用Ctrl+X/C/V等快捷键。\",\n\"Headings\": \"标题\",\n\"Heading 1\": \"一级标题\",\n\"Heading 2\": \"二级标题\",\n\"Heading 3\": \"三级标题\",\n\"Heading 4\": \"四级标题\",\n\"Heading 5\": \"五级标题\",\n\"Heading 6\": \"六级标题\",\n\"Preformatted\": \"预先格式化的\",\n\"Div\": \"Div\",\n\"Pre\": \"前言\",\n\"Code\": \"代码\",\n\"Paragraph\": \"段落\",\n\"Blockquote\": \"引文区块\",\n\"Inline\": \"文本\",\n\"Blocks\": \"样式\",\n\"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.\": \"当前为纯文本粘贴模式，再次点击可以回到普通粘贴模式。\",\n\"Fonts\": \"字体\",\n\"Font sizes\": \"字体大小\",\n\"Class\": \"类型\",\n\"Browse for an image\": \"浏览图像\",\n\"OR\": \"或\",\n\"Drop an image here\": \"拖放一张图像至此\",\n\"Upload\": \"上传\",\n\"Uploading image\": \"上传图片\",\n\"Block\": \"块\",\n\"Align\": \"对齐\",\n\"Default\": \"预设\",\n\"Circle\": \"空心圆\",\n\"Disc\": \"实心圆\",\n\"Square\": \"实心方块\",\n\"Lower Alpha\": \"小写英文字母\",\n\"Lower Greek\": \"小写希腊字母\",\n\"Lower Roman\": \"小写罗马数字\",\n\"Upper Alpha\": \"大写英文字母\",\n\"Upper Roman\": \"大写罗马数字\",\n\"Anchor...\": \"锚点...\",\n\"Anchor\": \"锚点\",\n\"Name\": \"名称\",\n\"ID\": \"ID\",\n\"ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.\": \"ID应该以英文字母开头，后面只能有英文字母、数字、破折号、点、冒号或下划线。\",\n\"You have unsaved changes are you sure you want to navigate away?\": \"你还有文档尚未保存，确定要离开？\",\n\"Restore last draft\": \"恢复上次的草稿\",\n\"Special character...\": \"特殊字符...\",\n\"Special Character\": \"特殊字符\",\n\"Source code\": \"源代码\",\n\"Insert/Edit code sample\": \"插入/编辑代码示例\",\n\"Language\": \"语言\",\n\"Code sample...\": \"示例代码...\",\n\"Left to right\": \"由左到右\",\n\"Right to left\": \"由右到左\",\n\"Title\": \"标题\",\n\"Fullscreen\": \"全屏\",\n\"Action\": \"动作\",\n\"Shortcut\": \"快捷方式\",\n\"Help\": \"帮助\",\n\"Address\": \"地址\",\n\"Focus to menubar\": \"移动焦点到菜单栏\",\n\"Focus to toolbar\": \"移动焦点到工具栏\",\n\"Focus to element path\": \"移动焦点到元素路径\",\n\"Focus to contextual toolbar\": \"移动焦点到上下文菜单\",\n\"Insert link (if link plugin activated)\": \"插入链接 (如果链接插件已激活)\",\n\"Save (if save plugin activated)\": \"保存(如果保存插件已激活)\",\n\"Find (if searchreplace plugin activated)\": \"查找(如果查找替换插件已激活)\",\n\"Plugins installed ({0}):\": \"已安装插件 ({0}):\",\n\"Premium plugins:\": \"优秀插件：\",\n\"Learn more...\": \"了解更多...\",\n\"You are using {0}\": \"你正在使用 {0}\",\n\"Plugins\": \"插件\",\n\"Handy Shortcuts\": \"快捷键\",\n\"Horizontal line\": \"水平分割线\",\n\"Insert/edit image\": \"插入/编辑图片\",\n\"Alternative description\": \"替代描述\",\n\"Accessibility\": \"辅助功能\",\n\"Image is decorative\": \"图像是装饰性的\",\n\"Source\": \"源\",\n\"Dimensions\": \"尺寸\",\n\"Constrain proportions\": \"保持比例\",\n\"General\": \"一般\",\n\"Advanced\": \"高级\",\n\"Style\": \"样式\",\n\"Vertical space\": \"垂直间距\",\n\"Horizontal space\": \"水平间距\",\n\"Border\": \"框线\",\n\"Insert image\": \"插入图片\",\n\"Image...\": \"图片...\",\n\"Image list\": \"图片清单\",\n\"Resize\": \"调整大小\",\n\"Insert date/time\": \"插入日期/时间\",\n\"Date/time\": \"日期/时间\",\n\"Insert/edit link\": \"插入/编辑链接\",\n\"Text to display\": \"要显示的文本\",\n\"Url\": \"地址\",\n\"Open link in...\": \"链接打开位置...\",\n\"Current window\": \"当前窗口\",\n\"None\": \"无\",\n\"New window\": \"新窗口\",\n\"Open link\": \"打开链接\",\n\"Remove link\": \"移除链接\",\n\"Anchors\": \"锚点\",\n\"Link...\": \"链接...\",\n\"Paste or type a link\": \"粘贴或输入链接\",\n\"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?\": \"你所填写的URL地址为邮件地址，需要加上mailto: 前缀吗？\",\n\"The URL you entered seems to be an external link. Do you want to add the required http:// prefix?\": \"你所填写的URL地址属于外部链接，需要加上http:// 前缀吗？\",\n\"The URL you entered seems to be an external link. Do you want to add the required https:// prefix?\": \"您输入的 URL 似乎是一个外部链接。您想添加所需的 https:// 前缀吗？\",\n\"Link list\": \"链接清单\",\n\"Insert video\": \"插入视频\",\n\"Insert/edit video\": \"插入/编辑视频\",\n\"Insert/edit media\": \"插入/编辑媒体\",\n\"Alternative source\": \"镜像\",\n\"Alternative source URL\": \"替代来源网址\",\n\"Media poster (Image URL)\": \"封面(图片地址)\",\n\"Paste your embed code below:\": \"将内嵌代码粘贴在下面:\",\n\"Embed\": \"内嵌\",\n\"Media...\": \"多媒体...\",\n\"Nonbreaking space\": \"不间断空格\",\n\"Page break\": \"分页符\",\n\"Paste as text\": \"粘贴为文本\",\n\"Preview\": \"预览\",\n\"Print\": \"打印\",\n\"Print...\": \"打印...\",\n\"Save\": \"保存\",\n\"Find\": \"寻找\",\n\"Replace with\": \"替换为\",\n\"Replace\": \"替换\",\n\"Replace all\": \"替换全部\",\n\"Previous\": \"上一个\",\n\"Next\": \"下一个\",\n\"Find and Replace\": \"查找和替换\",\n\"Find and replace...\": \"查找并替换...\",\n\"Could not find the specified string.\": \"未找到搜索内容。\",\n\"Match case\": \"大小写匹配\",\n\"Find whole words only\": \"全字匹配\",\n\"Find in selection\": \"在选区中查找\",\n\"Insert table\": \"插入表格\",\n\"Table properties\": \"表格属性\",\n\"Delete table\": \"删除表格\",\n\"Cell\": \"单元格\",\n\"Row\": \"行\",\n\"Column\": \"栏目\",\n\"Cell properties\": \"单元格属性\",\n\"Merge cells\": \"合并单元格\",\n\"Split cell\": \"拆分单元格\",\n\"Insert row before\": \"在上方插入行\",\n\"Insert row after\": \"在下方插入行\",\n\"Delete row\": \"删除行\",\n\"Row properties\": \"行属性\",\n\"Cut row\": \"剪切行\",\n\"Cut column\": \"剪切列\",\n\"Copy row\": \"复制行\",\n\"Copy column\": \"复制列\",\n\"Paste row before\": \"粘贴行到上方\",\n\"Paste column before\": \"粘贴此列前\",\n\"Paste row after\": \"粘贴行到下方\",\n\"Paste column after\": \"粘贴后面的列\",\n\"Insert column before\": \"在左侧插入列\",\n\"Insert column after\": \"在右侧插入列\",\n\"Delete column\": \"删除列\",\n\"Cols\": \"列\",\n\"Rows\": \"行数\",\n\"Width\": \"宽度\",\n\"Height\": \"高度\",\n\"Cell spacing\": \"单元格外间距\",\n\"Cell padding\": \"单元格内边距\",\n\"Row clipboard actions\": \"行剪贴板操作\",\n\"Column clipboard actions\": \"列剪贴板操作\",\n\"Table styles\": \"表格样式\",\n\"Cell styles\": \"单元格样式\",\n\"Column header\": \"列标题\",\n\"Row header\": \"行头\",\n\"Table caption\": \"表格标题\",\n\"Caption\": \"标题\",\n\"Show caption\": \"显示标题\",\n\"Left\": \"左\",\n\"Center\": \"居中\",\n\"Right\": \"右\",\n\"Cell type\": \"储存格别\",\n\"Scope\": \"范围\",\n\"Alignment\": \"对齐\",\n\"Horizontal align\": \"水平对齐\",\n\"Vertical align\": \"垂直对齐\",\n\"Top\": \"上方对齐\",\n\"Middle\": \"居中对齐\",\n\"Bottom\": \"下方对齐\",\n\"Header cell\": \"表头单元格\",\n\"Row group\": \"行组\",\n\"Column group\": \"列组\",\n\"Row type\": \"行类型\",\n\"Header\": \"表头\",\n\"Body\": \"表体\",\n\"Footer\": \"表尾\",\n\"Border color\": \"框线颜色\",\n\"Solid\": \"实线\",\n\"Dotted\": \"虚线\",\n\"Dashed\": \"虚线\",\n\"Double\": \"双精度\",\n\"Groove\": \"凹槽\",\n\"Ridge\": \"海脊座\",\n\"Inset\": \"嵌入\",\n\"Outset\": \"外置\",\n\"Hidden\": \"隐藏\",\n\"Insert template...\": \"插入模板...\",\n\"Templates\": \"模板\",\n\"Template\": \"模板\",\n\"Insert Template\": \"插入模板\",\n\"Text color\": \"文本颜色\",\n\"Background color\": \"背景颜色\",\n\"Custom...\": \"自定义......\",\n\"Custom color\": \"自定义颜色\",\n\"No color\": \"无\",\n\"Remove color\": \"移除颜色\",\n\"Show blocks\": \"显示区块边框\",\n\"Show invisible characters\": \"显示不可见字符\",\n\"Word count\": \"字数\",\n\"Count\": \"计数\",\n\"Document\": \"文档\",\n\"Selection\": \"选择\",\n\"Words\": \"单词\",\n\"Words: {0}\": \"字数：{0}\",\n\"{0} words\": \"{0} 字\",\n\"File\": \"文件\",\n\"Edit\": \"编辑\",\n\"Insert\": \"插入\",\n\"View\": \"查看\",\n\"Format\": \"格式\",\n\"Table\": \"表格\",\n\"Tools\": \"工具\",\n\"Powered by {0}\": \"由{0}驱动\",\n\"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help\": \"编辑区。按ALT-F9打开菜单，按ALT-F10打开工具栏，按ALT-0查看帮助\",\n\"Image title\": \"图片标题\",\n\"Border width\": \"边框宽度\",\n\"Border style\": \"边框样式\",\n\"Error\": \"错误\",\n\"Warn\": \"警告\",\n\"Valid\": \"有效\",\n\"To open the popup, press Shift+Enter\": \"按Shitf+Enter键打开对话框\",\n\"Rich Text Area\": \"富文本区域\",\n\"Rich Text Area. Press ALT-0 for help.\": \"编辑区。按Alt+0键打开帮助。\",\n\"System Font\": \"系统字体\",\n\"Failed to upload image: {0}\": \"图片上传失败: {0}\",\n\"Failed to load plugin: {0} from url {1}\": \"插件加载失败: {0} 来自链接 {1}\",\n\"Failed to load plugin url: {0}\": \"插件加载失败 链接: {0}\",\n\"Failed to initialize plugin: {0}\": \"插件初始化失败: {0}\",\n\"example\": \"示例\",\n\"Search\": \"搜索\",\n\"All\": \"全部\",\n\"Currency\": \"货币\",\n\"Text\": \"文字\",\n\"Quotations\": \"引用\",\n\"Mathematical\": \"数学\",\n\"Extended Latin\": \"拉丁语扩充\",\n\"Symbols\": \"符号\",\n\"Arrows\": \"箭头\",\n\"User Defined\": \"自定义\",\n\"dollar sign\": \"美元符号\",\n\"currency sign\": \"货币符号\",\n\"euro-currency sign\": \"欧元符号\",\n\"colon sign\": \"冒号\",\n\"cruzeiro sign\": \"克鲁赛罗币符号\",\n\"french franc sign\": \"法郎符号\",\n\"lira sign\": \"里拉符号\",\n\"mill sign\": \"密尔符号\",\n\"naira sign\": \"奈拉符号\",\n\"peseta sign\": \"比塞塔符号\",\n\"rupee sign\": \"卢比符号\",\n\"won sign\": \"韩元符号\",\n\"new sheqel sign\": \"新谢克尔符号\",\n\"dong sign\": \"越南盾符号\",\n\"kip sign\": \"老挝基普符号\",\n\"tugrik sign\": \"图格里克符号\",\n\"drachma sign\": \"德拉克马符号\",\n\"german penny symbol\": \"德国便士符号\",\n\"peso sign\": \"比索符号\",\n\"guarani sign\": \"瓜拉尼符号\",\n\"austral sign\": \"澳元符号\",\n\"hryvnia sign\": \"格里夫尼亚符号\",\n\"cedi sign\": \"塞地符号\",\n\"livre tournois sign\": \"里弗弗尔符号\",\n\"spesmilo sign\": \"spesmilo符号\",\n\"tenge sign\": \"坚戈符号\",\n\"indian rupee sign\": \"印度卢比\",\n\"turkish lira sign\": \"土耳其里拉\",\n\"nordic mark sign\": \"北欧马克\",\n\"manat sign\": \"马纳特符号\",\n\"ruble sign\": \"卢布符号\",\n\"yen character\": \"日元字样\",\n\"yuan character\": \"人民币元字样\",\n\"yuan character, in hong kong and taiwan\": \"元字样（港台地区）\",\n\"yen/yuan character variant one\": \"元字样（大写）\",\n\"Emojis\": \"Emojis\",\n\"Emojis...\": \"Emojis...\",\n\"Loading emojis...\": \"正在加载Emojis...\",\n\"Could not load emojis\": \"无法加载Emojis\",\n\"People\": \"人类\",\n\"Animals and Nature\": \"动物和自然\",\n\"Food and Drink\": \"食物和饮品\",\n\"Activity\": \"活动\",\n\"Travel and Places\": \"旅游和地点\",\n\"Objects\": \"物件\",\n\"Flags\": \"旗帜\",\n\"Characters\": \"字符\",\n\"Characters (no spaces)\": \"字符(无空格)\",\n\"{0} characters\": \"{0} 个字符\",\n\"Error: Form submit field collision.\": \"错误: 表单提交字段冲突。\",\n\"Error: No form element found.\": \"错误: 没有表单控件。\",\n\"Color swatch\": \"颜色样本\",\n\"Color Picker\": \"选色器\",\n\"Invalid hex color code: {0}\": \"十六进制颜色代码无效： {0}\",\n\"Invalid input\": \"无效输入\",\n\"R\": \"R\",\n\"Red component\": \"红色部分\",\n\"G\": \"G\",\n\"Green component\": \"绿色部分\",\n\"B\": \"B\",\n\"Blue component\": \"白色部分\",\n\"#\": \"#\",\n\"Hex color code\": \"十六进制颜色代码\",\n\"Range 0 to 255\": \"范围0至255\",\n\"Turquoise\": \"青绿色\",\n\"Green\": \"绿色\",\n\"Blue\": \"蓝色\",\n\"Purple\": \"紫色\",\n\"Navy Blue\": \"海军蓝\",\n\"Dark Turquoise\": \"深蓝绿色\",\n\"Dark Green\": \"深绿色\",\n\"Medium Blue\": \"中蓝色\",\n\"Medium Purple\": \"中紫色\",\n\"Midnight Blue\": \"深蓝色\",\n\"Yellow\": \"黄色\",\n\"Orange\": \"橙色\",\n\"Red\": \"红色\",\n\"Light Gray\": \"浅灰色\",\n\"Gray\": \"灰色\",\n\"Dark Yellow\": \"暗黄色\",\n\"Dark Orange\": \"深橙色\",\n\"Dark Red\": \"深红色\",\n\"Medium Gray\": \"中灰色\",\n\"Dark Gray\": \"深灰色\",\n\"Light Green\": \"浅绿色\",\n\"Light Yellow\": \"浅黄色\",\n\"Light Red\": \"浅红色\",\n\"Light Purple\": \"浅紫色\",\n\"Light Blue\": \"浅蓝色\",\n\"Dark Purple\": \"深紫色\",\n\"Dark Blue\": \"深蓝色\",\n\"Black\": \"黑色\",\n\"White\": \"白色\",\n\"Switch to or from fullscreen mode\": \"切换全屏模式\",\n\"Open help dialog\": \"打开帮助对话框\",\n\"history\": \"历史\",\n\"styles\": \"样式\",\n\"formatting\": \"格式化\",\n\"alignment\": \"对齐\",\n\"indentation\": \"缩进\",\n\"Font\": \"字体\",\n\"Size\": \"字号\",\n\"More...\": \"更多...\",\n\"Select...\": \"选择...\",\n\"Preferences\": \"首选项\",\n\"Yes\": \"是\",\n\"No\": \"否\",\n\"Keyboard Navigation\": \"键盘指引\",\n\"Version\": \"版本\",\n\"Code view\": \"代码视图\",\n\"Open popup menu for split buttons\": \"打开弹出式菜单，用于拆分按钮\",\n\"List Properties\": \"列表属性\",\n\"List properties...\": \"标题字体属性\",\n\"Start list at number\": \"以数字开始列表\",\n\"Line height\": \"行高\",\n\"Dropped file type is not supported\": \"此文件类型不支持拖放\",\n\"Loading...\": \"加载中...\",\n\"ImageProxy HTTP error: Rejected request\": \"图片代理请求错误：请求被拒绝\",\n\"ImageProxy HTTP error: Could not find Image Proxy\": \"图片代理请求错误：无法找到图片代理\",\n\"ImageProxy HTTP error: Incorrect Image Proxy URL\": \"图片代理请求错误：图片代理地址错误\",\n\"ImageProxy HTTP error: Unknown ImageProxy error\": \"图片代理请求错误：未知的图片代理错误\"\n});"
  },
  {
    "path": "vue_acimage_web/public/tinymce/license.txt",
    "content": "MIT License\n\nCopyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "vue_acimage_web/public/tinymce/plugins/emoticons/js/emojiimages.js",
    "content": "window.tinymce.Resource.add(\"tinymce.plugins.emoticons\",{100:{keywords:[\"score\",\"perfect\",\"numbers\",\"century\",\"exam\",\"quiz\",\"test\",\"pass\",\"hundred\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💯\" src=\"1f4af.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},1234:{keywords:[\"numbers\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔢\" src=\"1f522.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},grinning:{keywords:[\"face\",\"smile\",\"happy\",\"joy\",\":D\",\"grin\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😀\" src=\"1f600.png\"/>',fitzpatrick_scale:false,category:\"people\"},grimacing:{keywords:[\"face\",\"grimace\",\"teeth\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😬\" src=\"1f62c.png\"/>',fitzpatrick_scale:false,category:\"people\"},grin:{keywords:[\"face\",\"happy\",\"smile\",\"joy\",\"kawaii\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😁\" src=\"1f601.png\"/>',fitzpatrick_scale:false,category:\"people\"},joy:{keywords:[\"face\",\"cry\",\"tears\",\"weep\",\"happy\",\"happytears\",\"haha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😂\" src=\"1f602.png\"/>',fitzpatrick_scale:false,category:\"people\"},rofl:{keywords:[\"face\",\"rolling\",\"floor\",\"laughing\",\"lol\",\"haha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤣\" src=\"1f923.png\"/>',fitzpatrick_scale:false,category:\"people\"},partying:{keywords:[\"face\",\"celebration\",\"woohoo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥳\" src=\"1f973.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiley:{keywords:[\"face\",\"happy\",\"joy\",\"haha\",\":D\",\":)\",\"smile\",\"funny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😃\" src=\"1f603.png\"/>',fitzpatrick_scale:false,category:\"people\"},smile:{keywords:[\"face\",\"happy\",\"joy\",\"funny\",\"haha\",\"laugh\",\"like\",\":D\",\":)\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😄\" src=\"1f604.png\"/>',fitzpatrick_scale:false,category:\"people\"},sweat_smile:{keywords:[\"face\",\"hot\",\"happy\",\"laugh\",\"sweat\",\"smile\",\"relief\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😅\" src=\"1f605.png\"/>',fitzpatrick_scale:false,category:\"people\"},laughing:{keywords:[\"happy\",\"joy\",\"lol\",\"satisfied\",\"haha\",\"face\",\"glad\",\"XD\",\"laugh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😆\" src=\"1f606.png\"/>',fitzpatrick_scale:false,category:\"people\"},innocent:{keywords:[\"face\",\"angel\",\"heaven\",\"halo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😇\" src=\"1f607.png\"/>',fitzpatrick_scale:false,category:\"people\"},wink:{keywords:[\"face\",\"happy\",\"mischievous\",\"secret\",\";)\",\"smile\",\"eye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😉\" src=\"1f609.png\"/>',fitzpatrick_scale:false,category:\"people\"},blush:{keywords:[\"face\",\"smile\",\"happy\",\"flushed\",\"crush\",\"embarrassed\",\"shy\",\"joy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😊\" src=\"1f60a.png\"/>',fitzpatrick_scale:false,category:\"people\"},slightly_smiling_face:{keywords:[\"face\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙂\" src=\"1f642.png\"/>',fitzpatrick_scale:false,category:\"people\"},upside_down_face:{keywords:[\"face\",\"flipped\",\"silly\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙃\" src=\"1f643.png\"/>',fitzpatrick_scale:false,category:\"people\"},relaxed:{keywords:[\"face\",\"blush\",\"massage\",\"happiness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☺️\" src=\"263a.png\"/>',fitzpatrick_scale:false,category:\"people\"},yum:{keywords:[\"happy\",\"joy\",\"tongue\",\"smile\",\"face\",\"silly\",\"yummy\",\"nom\",\"delicious\",\"savouring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😋\" src=\"1f60b.png\"/>',fitzpatrick_scale:false,category:\"people\"},relieved:{keywords:[\"face\",\"relaxed\",\"phew\",\"massage\",\"happiness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😌\" src=\"1f60c.png\"/>',fitzpatrick_scale:false,category:\"people\"},heart_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😍\" src=\"1f60d.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiling_face_with_three_hearts:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"hearts\",\"adore\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥰\" src=\"1f970.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_heart:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😘\" src=\"1f618.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing:{keywords:[\"love\",\"like\",\"face\",\"3\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😗\" src=\"1f617.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_smiling_eyes:{keywords:[\"face\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😙\" src=\"1f619.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_closed_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😚\" src=\"1f61a.png\"/>',fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_winking_eye:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"wink\",\"tongue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😜\" src=\"1f61c.png\"/>',fitzpatrick_scale:false,category:\"people\"},zany:{keywords:[\"face\",\"goofy\",\"crazy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤪\" src=\"1f92a.png\"/>',fitzpatrick_scale:false,category:\"people\"},raised_eyebrow:{keywords:[\"face\",\"distrust\",\"scepticism\",\"disapproval\",\"disbelief\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤨\" src=\"1f928.png\"/>',fitzpatrick_scale:false,category:\"people\"},monocle:{keywords:[\"face\",\"stuffy\",\"wealthy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧐\" src=\"1f9d0.png\"/>',fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_closed_eyes:{keywords:[\"face\",\"prank\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😝\" src=\"1f61d.png\"/>',fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😛\" src=\"1f61b.png\"/>',fitzpatrick_scale:false,category:\"people\"},money_mouth_face:{keywords:[\"face\",\"rich\",\"dollar\",\"money\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤑\" src=\"1f911.png\"/>',fitzpatrick_scale:false,category:\"people\"},nerd_face:{keywords:[\"face\",\"nerdy\",\"geek\",\"dork\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤓\" src=\"1f913.png\"/>',fitzpatrick_scale:false,category:\"people\"},sunglasses:{keywords:[\"face\",\"cool\",\"smile\",\"summer\",\"beach\",\"sunglass\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😎\" src=\"1f60e.png\"/>',fitzpatrick_scale:false,category:\"people\"},star_struck:{keywords:[\"face\",\"smile\",\"starry\",\"eyes\",\"grinning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤩\" src=\"1f929.png\"/>',fitzpatrick_scale:false,category:\"people\"},clown_face:{keywords:[\"face\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤡\" src=\"1f921.png\"/>',fitzpatrick_scale:false,category:\"people\"},cowboy_hat_face:{keywords:[\"face\",\"cowgirl\",\"hat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤠\" src=\"1f920.png\"/>',fitzpatrick_scale:false,category:\"people\"},hugs:{keywords:[\"face\",\"smile\",\"hug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤗\" src=\"1f917.png\"/>',fitzpatrick_scale:false,category:\"people\"},smirk:{keywords:[\"face\",\"smile\",\"mean\",\"prank\",\"smug\",\"sarcasm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😏\" src=\"1f60f.png\"/>',fitzpatrick_scale:false,category:\"people\"},no_mouth:{keywords:[\"face\",\"hellokitty\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😶\" src=\"1f636.png\"/>',fitzpatrick_scale:false,category:\"people\"},neutral_face:{keywords:[\"indifference\",\"meh\",\":|\",\"neutral\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😐\" src=\"1f610.png\"/>',fitzpatrick_scale:false,category:\"people\"},expressionless:{keywords:[\"face\",\"indifferent\",\"-_-\",\"meh\",\"deadpan\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😑\" src=\"1f611.png\"/>',fitzpatrick_scale:false,category:\"people\"},unamused:{keywords:[\"indifference\",\"bored\",\"straight face\",\"serious\",\"sarcasm\",\"unimpressed\",\"skeptical\",\"dubious\",\"side_eye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😒\" src=\"1f612.png\"/>',fitzpatrick_scale:false,category:\"people\"},roll_eyes:{keywords:[\"face\",\"eyeroll\",\"frustrated\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙄\" src=\"1f644.png\"/>',fitzpatrick_scale:false,category:\"people\"},thinking:{keywords:[\"face\",\"hmmm\",\"think\",\"consider\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤔\" src=\"1f914.png\"/>',fitzpatrick_scale:false,category:\"people\"},lying_face:{keywords:[\"face\",\"lie\",\"pinocchio\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤥\" src=\"1f925.png\"/>',fitzpatrick_scale:false,category:\"people\"},hand_over_mouth:{keywords:[\"face\",\"whoops\",\"shock\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤭\" src=\"1f92d.png\"/>',fitzpatrick_scale:false,category:\"people\"},shushing:{keywords:[\"face\",\"quiet\",\"shhh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤫\" src=\"1f92b.png\"/>',fitzpatrick_scale:false,category:\"people\"},symbols_over_mouth:{keywords:[\"face\",\"swearing\",\"cursing\",\"cussing\",\"profanity\",\"expletive\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤬\" src=\"1f92c.png\"/>',fitzpatrick_scale:false,category:\"people\"},exploding_head:{keywords:[\"face\",\"shocked\",\"mind\",\"blown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤯\" src=\"1f92f.png\"/>',fitzpatrick_scale:false,category:\"people\"},flushed:{keywords:[\"face\",\"blush\",\"shy\",\"flattered\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😳\" src=\"1f633.png\"/>',fitzpatrick_scale:false,category:\"people\"},disappointed:{keywords:[\"face\",\"sad\",\"upset\",\"depressed\",\":(\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😞\" src=\"1f61e.png\"/>',fitzpatrick_scale:false,category:\"people\"},worried:{keywords:[\"face\",\"concern\",\"nervous\",\":(\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😟\" src=\"1f61f.png\"/>',fitzpatrick_scale:false,category:\"people\"},angry:{keywords:[\"mad\",\"face\",\"annoyed\",\"frustrated\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😠\" src=\"1f620.png\"/>',fitzpatrick_scale:false,category:\"people\"},rage:{keywords:[\"angry\",\"mad\",\"hate\",\"despise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😡\" src=\"1f621.png\"/>',fitzpatrick_scale:false,category:\"people\"},pensive:{keywords:[\"face\",\"sad\",\"depressed\",\"upset\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😔\" src=\"1f614.png\"/>',fitzpatrick_scale:false,category:\"people\"},confused:{keywords:[\"face\",\"indifference\",\"huh\",\"weird\",\"hmmm\",\":/\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😕\" src=\"1f615.png\"/>',fitzpatrick_scale:false,category:\"people\"},slightly_frowning_face:{keywords:[\"face\",\"frowning\",\"disappointed\",\"sad\",\"upset\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙁\" src=\"1f641.png\"/>',fitzpatrick_scale:false,category:\"people\"},frowning_face:{keywords:[\"face\",\"sad\",\"upset\",\"frown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☹\" src=\"2639.png\"/>',fitzpatrick_scale:false,category:\"people\"},persevere:{keywords:[\"face\",\"sick\",\"no\",\"upset\",\"oops\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😣\" src=\"1f623.png\"/>',fitzpatrick_scale:false,category:\"people\"},confounded:{keywords:[\"face\",\"confused\",\"sick\",\"unwell\",\"oops\",\":S\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😖\" src=\"1f616.png\"/>',fitzpatrick_scale:false,category:\"people\"},tired_face:{keywords:[\"sick\",\"whine\",\"upset\",\"frustrated\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😫\" src=\"1f62b.png\"/>',fitzpatrick_scale:false,category:\"people\"},weary:{keywords:[\"face\",\"tired\",\"sleepy\",\"sad\",\"frustrated\",\"upset\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😩\" src=\"1f629.png\"/>',fitzpatrick_scale:false,category:\"people\"},pleading:{keywords:[\"face\",\"begging\",\"mercy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥺\" src=\"1f97a.png\"/>',fitzpatrick_scale:false,category:\"people\"},triumph:{keywords:[\"face\",\"gas\",\"phew\",\"proud\",\"pride\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😤\" src=\"1f624.png\"/>',fitzpatrick_scale:false,category:\"people\"},open_mouth:{keywords:[\"face\",\"surprise\",\"impressed\",\"wow\",\"whoa\",\":O\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😮\" src=\"1f62e.png\"/>',fitzpatrick_scale:false,category:\"people\"},scream:{keywords:[\"face\",\"munch\",\"scared\",\"omg\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😱\" src=\"1f631.png\"/>',fitzpatrick_scale:false,category:\"people\"},fearful:{keywords:[\"face\",\"scared\",\"terrified\",\"nervous\",\"oops\",\"huh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😨\" src=\"1f628.png\"/>',fitzpatrick_scale:false,category:\"people\"},cold_sweat:{keywords:[\"face\",\"nervous\",\"sweat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😰\" src=\"1f630.png\"/>',fitzpatrick_scale:false,category:\"people\"},hushed:{keywords:[\"face\",\"woo\",\"shh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😯\" src=\"1f62f.png\"/>',fitzpatrick_scale:false,category:\"people\"},frowning:{keywords:[\"face\",\"aw\",\"what\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😦\" src=\"1f626.png\"/>',fitzpatrick_scale:false,category:\"people\"},anguished:{keywords:[\"face\",\"stunned\",\"nervous\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😧\" src=\"1f627.png\"/>',fitzpatrick_scale:false,category:\"people\"},cry:{keywords:[\"face\",\"tears\",\"sad\",\"depressed\",\"upset\",\":'(\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😢\" src=\"1f622.png\"/>',fitzpatrick_scale:false,category:\"people\"},disappointed_relieved:{keywords:[\"face\",\"phew\",\"sweat\",\"nervous\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😥\" src=\"1f625.png\"/>',fitzpatrick_scale:false,category:\"people\"},drooling_face:{keywords:[\"face\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤤\" src=\"1f924.png\"/>',fitzpatrick_scale:false,category:\"people\"},sleepy:{keywords:[\"face\",\"tired\",\"rest\",\"nap\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😪\" src=\"1f62a.png\"/>',fitzpatrick_scale:false,category:\"people\"},sweat:{keywords:[\"face\",\"hot\",\"sad\",\"tired\",\"exercise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😓\" src=\"1f613.png\"/>',fitzpatrick_scale:false,category:\"people\"},hot:{keywords:[\"face\",\"feverish\",\"heat\",\"red\",\"sweating\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥵\" src=\"1f975.png\"/>',fitzpatrick_scale:false,category:\"people\"},cold:{keywords:[\"face\",\"blue\",\"freezing\",\"frozen\",\"frostbite\",\"icicles\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥶\" src=\"1f976.png\"/>',fitzpatrick_scale:false,category:\"people\"},sob:{keywords:[\"face\",\"cry\",\"tears\",\"sad\",\"upset\",\"depressed\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😭\" src=\"1f62d.png\"/>',fitzpatrick_scale:false,category:\"people\"},dizzy_face:{keywords:[\"spent\",\"unconscious\",\"xox\",\"dizzy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😵\" src=\"1f635.png\"/>',fitzpatrick_scale:false,category:\"people\"},astonished:{keywords:[\"face\",\"xox\",\"surprised\",\"poisoned\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😲\" src=\"1f632.png\"/>',fitzpatrick_scale:false,category:\"people\"},zipper_mouth_face:{keywords:[\"face\",\"sealed\",\"zipper\",\"secret\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤐\" src=\"1f910.png\"/>',fitzpatrick_scale:false,category:\"people\"},nauseated_face:{keywords:[\"face\",\"vomit\",\"gross\",\"green\",\"sick\",\"throw up\",\"ill\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤢\" src=\"1f922.png\"/>',fitzpatrick_scale:false,category:\"people\"},sneezing_face:{keywords:[\"face\",\"gesundheit\",\"sneeze\",\"sick\",\"allergy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤧\" src=\"1f927.png\"/>',fitzpatrick_scale:false,category:\"people\"},vomiting:{keywords:[\"face\",\"sick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤮\" src=\"1f92e.png\"/>',fitzpatrick_scale:false,category:\"people\"},mask:{keywords:[\"face\",\"sick\",\"ill\",\"disease\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😷\" src=\"1f637.png\"/>',fitzpatrick_scale:false,category:\"people\"},face_with_thermometer:{keywords:[\"sick\",\"temperature\",\"thermometer\",\"cold\",\"fever\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤒\" src=\"1f912.png\"/>',fitzpatrick_scale:false,category:\"people\"},face_with_head_bandage:{keywords:[\"injured\",\"clumsy\",\"bandage\",\"hurt\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤕\" src=\"1f915.png\"/>',fitzpatrick_scale:false,category:\"people\"},woozy:{keywords:[\"face\",\"dizzy\",\"intoxicated\",\"tipsy\",\"wavy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥴\" src=\"1f974.png\"/>',fitzpatrick_scale:false,category:\"people\"},sleeping:{keywords:[\"face\",\"tired\",\"sleepy\",\"night\",\"zzz\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😴\" src=\"1f634.png\"/>',fitzpatrick_scale:false,category:\"people\"},zzz:{keywords:[\"sleepy\",\"tired\",\"dream\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💤\" src=\"1f4a4.png\"/>',fitzpatrick_scale:false,category:\"people\"},poop:{keywords:[\"hankey\",\"shitface\",\"fail\",\"turd\",\"shit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💩\" src=\"1f4a9.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiling_imp:{keywords:[\"devil\",\"horns\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😈\" src=\"1f608.png\"/>',fitzpatrick_scale:false,category:\"people\"},imp:{keywords:[\"devil\",\"angry\",\"horns\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👿\" src=\"1f47f.png\"/>',fitzpatrick_scale:false,category:\"people\"},japanese_ogre:{keywords:[\"monster\",\"red\",\"mask\",\"halloween\",\"scary\",\"creepy\",\"devil\",\"demon\",\"japanese\",\"ogre\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👹\" src=\"1f479.png\"/>',fitzpatrick_scale:false,category:\"people\"},japanese_goblin:{keywords:[\"red\",\"evil\",\"mask\",\"monster\",\"scary\",\"creepy\",\"japanese\",\"goblin\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👺\" src=\"1f47a.png\"/>',fitzpatrick_scale:false,category:\"people\"},skull:{keywords:[\"dead\",\"skeleton\",\"creepy\",\"death\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💀\" src=\"1f480.png\"/>',fitzpatrick_scale:false,category:\"people\"},ghost:{keywords:[\"halloween\",\"spooky\",\"scary\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👻\" src=\"1f47b.png\"/>',fitzpatrick_scale:false,category:\"people\"},alien:{keywords:[\"UFO\",\"paul\",\"weird\",\"outer_space\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👽\" src=\"1f47d.png\"/>',fitzpatrick_scale:false,category:\"people\"},robot:{keywords:[\"computer\",\"machine\",\"bot\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤖\" src=\"1f916.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiley_cat:{keywords:[\"animal\",\"cats\",\"happy\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😺\" src=\"1f63a.png\"/>',fitzpatrick_scale:false,category:\"people\"},smile_cat:{keywords:[\"animal\",\"cats\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😸\" src=\"1f638.png\"/>',fitzpatrick_scale:false,category:\"people\"},joy_cat:{keywords:[\"animal\",\"cats\",\"haha\",\"happy\",\"tears\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😹\" src=\"1f639.png\"/>',fitzpatrick_scale:false,category:\"people\"},heart_eyes_cat:{keywords:[\"animal\",\"love\",\"like\",\"affection\",\"cats\",\"valentines\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😻\" src=\"1f63b.png\"/>',fitzpatrick_scale:false,category:\"people\"},smirk_cat:{keywords:[\"animal\",\"cats\",\"smirk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😼\" src=\"1f63c.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_cat:{keywords:[\"animal\",\"cats\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😽\" src=\"1f63d.png\"/>',fitzpatrick_scale:false,category:\"people\"},scream_cat:{keywords:[\"animal\",\"cats\",\"munch\",\"scared\",\"scream\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙀\" src=\"1f640.png\"/>',fitzpatrick_scale:false,category:\"people\"},crying_cat_face:{keywords:[\"animal\",\"tears\",\"weep\",\"sad\",\"cats\",\"upset\",\"cry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😿\" src=\"1f63f.png\"/>',fitzpatrick_scale:false,category:\"people\"},pouting_cat:{keywords:[\"animal\",\"cats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😾\" src=\"1f63e.png\"/>',fitzpatrick_scale:false,category:\"people\"},palms_up:{keywords:[\"hands\",\"gesture\",\"cupped\",\"prayer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤲\" src=\"1f932.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_hands:{keywords:[\"gesture\",\"hooray\",\"yea\",\"celebration\",\"hands\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙌\" src=\"1f64c.png\"/>',fitzpatrick_scale:true,category:\"people\"},clap:{keywords:[\"hands\",\"praise\",\"applause\",\"congrats\",\"yay\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👏\" src=\"1f44f.png\"/>',fitzpatrick_scale:true,category:\"people\"},wave:{keywords:[\"hands\",\"gesture\",\"goodbye\",\"solong\",\"farewell\",\"hello\",\"hi\",\"palm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👋\" src=\"1f44b.png\"/>',fitzpatrick_scale:true,category:\"people\"},call_me_hand:{keywords:[\"hands\",\"gesture\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤙\" src=\"1f919.png\"/>',fitzpatrick_scale:true,category:\"people\"},\"+1\":{keywords:[\"thumbsup\",\"yes\",\"awesome\",\"good\",\"agree\",\"accept\",\"cool\",\"hand\",\"like\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👍\" src=\"1f44d.png\"/>',fitzpatrick_scale:true,category:\"people\"},\"-1\":{keywords:[\"thumbsdown\",\"no\",\"dislike\",\"hand\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👎\" src=\"1f44e.png\"/>',fitzpatrick_scale:true,category:\"people\"},facepunch:{keywords:[\"angry\",\"violence\",\"fist\",\"hit\",\"attack\",\"hand\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👊\" src=\"1f44a.png\"/>',fitzpatrick_scale:true,category:\"people\"},fist:{keywords:[\"fingers\",\"hand\",\"grasp\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✊\" src=\"270a.png\"/>',fitzpatrick_scale:true,category:\"people\"},fist_left:{keywords:[\"hand\",\"fistbump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤛\" src=\"1f91b.png\"/>',fitzpatrick_scale:true,category:\"people\"},fist_right:{keywords:[\"hand\",\"fistbump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤜\" src=\"1f91c.png\"/>',fitzpatrick_scale:true,category:\"people\"},v:{keywords:[\"fingers\",\"ohyeah\",\"hand\",\"peace\",\"victory\",\"two\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✌\" src=\"270c.png\"/>',fitzpatrick_scale:true,category:\"people\"},ok_hand:{keywords:[\"fingers\",\"limbs\",\"perfect\",\"ok\",\"okay\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👌\" src=\"1f44c.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_hand:{keywords:[\"fingers\",\"stop\",\"highfive\",\"palm\",\"ban\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✋\" src=\"270b.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_back_of_hand:{keywords:[\"fingers\",\"raised\",\"backhand\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤚\" src=\"1f91a.png\"/>',fitzpatrick_scale:true,category:\"people\"},open_hands:{keywords:[\"fingers\",\"butterfly\",\"hands\",\"open\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👐\" src=\"1f450.png\"/>',fitzpatrick_scale:true,category:\"people\"},muscle:{keywords:[\"arm\",\"flex\",\"hand\",\"summer\",\"strong\",\"biceps\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💪\" src=\"1f4aa.png\"/>',fitzpatrick_scale:true,category:\"people\"},pray:{keywords:[\"please\",\"hope\",\"wish\",\"namaste\",\"highfive\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙏\" src=\"1f64f.png\"/>',fitzpatrick_scale:true,category:\"people\"},foot:{keywords:[\"kick\",\"stomp\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦶\" src=\"1f9b6.png\"/>',fitzpatrick_scale:true,category:\"people\"},leg:{keywords:[\"kick\",\"limb\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦵\" src=\"1f9b5.png\"/>',fitzpatrick_scale:true,category:\"people\"},handshake:{keywords:[\"agreement\",\"shake\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤝\" src=\"1f91d.png\"/>',fitzpatrick_scale:false,category:\"people\"},point_up:{keywords:[\"hand\",\"fingers\",\"direction\",\"up\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☝\" src=\"261d.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_up_2:{keywords:[\"fingers\",\"hand\",\"direction\",\"up\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👆\" src=\"1f446.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_down:{keywords:[\"fingers\",\"hand\",\"direction\",\"down\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👇\" src=\"1f447.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_left:{keywords:[\"direction\",\"fingers\",\"hand\",\"left\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👈\" src=\"1f448.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_right:{keywords:[\"fingers\",\"hand\",\"direction\",\"right\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👉\" src=\"1f449.png\"/>',fitzpatrick_scale:true,category:\"people\"},fu:{keywords:[\"hand\",\"fingers\",\"rude\",\"middle\",\"flipping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖕\" src=\"1f595.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_hand_with_fingers_splayed:{keywords:[\"hand\",\"fingers\",\"palm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖐\" src=\"1f590.png\"/>',fitzpatrick_scale:true,category:\"people\"},love_you:{keywords:[\"hand\",\"fingers\",\"gesture\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤟\" src=\"1f91f.png\"/>',fitzpatrick_scale:true,category:\"people\"},metal:{keywords:[\"hand\",\"fingers\",\"evil_eye\",\"sign_of_horns\",\"rock_on\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤘\" src=\"1f918.png\"/>',fitzpatrick_scale:true,category:\"people\"},crossed_fingers:{keywords:[\"good\",\"lucky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤞\" src=\"1f91e.png\"/>',fitzpatrick_scale:true,category:\"people\"},vulcan_salute:{keywords:[\"hand\",\"fingers\",\"spock\",\"star trek\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖖\" src=\"1f596.png\"/>',fitzpatrick_scale:true,category:\"people\"},writing_hand:{keywords:[\"lower_left_ballpoint_pen\",\"stationery\",\"write\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✍\" src=\"270d.png\"/>',fitzpatrick_scale:true,category:\"people\"},selfie:{keywords:[\"camera\",\"phone\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤳\" src=\"1f933.png\"/>',fitzpatrick_scale:true,category:\"people\"},nail_care:{keywords:[\"beauty\",\"manicure\",\"finger\",\"fashion\",\"nail\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💅\" src=\"1f485.png\"/>',fitzpatrick_scale:true,category:\"people\"},lips:{keywords:[\"mouth\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👄\" src=\"1f444.png\"/>',fitzpatrick_scale:false,category:\"people\"},tooth:{keywords:[\"teeth\",\"dentist\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦷\" src=\"1f9b7.png\"/>',fitzpatrick_scale:false,category:\"people\"},tongue:{keywords:[\"mouth\",\"playful\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👅\" src=\"1f445.png\"/>',fitzpatrick_scale:false,category:\"people\"},ear:{keywords:[\"face\",\"hear\",\"sound\",\"listen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👂\" src=\"1f442.png\"/>',fitzpatrick_scale:true,category:\"people\"},nose:{keywords:[\"smell\",\"sniff\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👃\" src=\"1f443.png\"/>',fitzpatrick_scale:true,category:\"people\"},eye:{keywords:[\"face\",\"look\",\"see\",\"watch\",\"stare\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👁\" src=\"1f441.png\"/>',fitzpatrick_scale:false,category:\"people\"},eyes:{keywords:[\"look\",\"watch\",\"stalk\",\"peek\",\"see\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👀\" src=\"1f440.png\"/>',fitzpatrick_scale:false,category:\"people\"},brain:{keywords:[\"smart\",\"intelligent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧠\" src=\"1f9e0.png\"/>',fitzpatrick_scale:false,category:\"people\"},bust_in_silhouette:{keywords:[\"user\",\"person\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👤\" src=\"1f464.png\"/>',fitzpatrick_scale:false,category:\"people\"},busts_in_silhouette:{keywords:[\"user\",\"person\",\"human\",\"group\",\"team\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👥\" src=\"1f465.png\"/>',fitzpatrick_scale:false,category:\"people\"},speaking_head:{keywords:[\"user\",\"person\",\"human\",\"sing\",\"say\",\"talk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗣\" src=\"1f5e3.png\"/>',fitzpatrick_scale:false,category:\"people\"},baby:{keywords:[\"child\",\"boy\",\"girl\",\"toddler\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👶\" src=\"1f476.png\"/>',fitzpatrick_scale:true,category:\"people\"},child:{keywords:[\"gender-neutral\",\"young\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧒\" src=\"1f9d2.png\"/>',fitzpatrick_scale:true,category:\"people\"},boy:{keywords:[\"man\",\"male\",\"guy\",\"teenager\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👦\" src=\"1f466.png\"/>',fitzpatrick_scale:true,category:\"people\"},girl:{keywords:[\"female\",\"woman\",\"teenager\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👧\" src=\"1f467.png\"/>',fitzpatrick_scale:true,category:\"people\"},adult:{keywords:[\"gender-neutral\",\"person\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧑\" src=\"1f9d1.png\"/>',fitzpatrick_scale:true,category:\"people\"},man:{keywords:[\"mustache\",\"father\",\"dad\",\"guy\",\"classy\",\"sir\",\"moustache\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨\" src=\"1f468.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman:{keywords:[\"female\",\"girls\",\"lady\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩\" src=\"1f469.png\"/>',fitzpatrick_scale:true,category:\"people\"},blonde_woman:{keywords:[\"woman\",\"female\",\"girl\",\"blonde\",\"person\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👱‍♀️\" src=\"1f471-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},blonde_man:{keywords:[\"man\",\"male\",\"boy\",\"blonde\",\"guy\",\"person\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👱\" src=\"1f471.png\"/>',fitzpatrick_scale:true,category:\"people\"},bearded_person:{keywords:[\"person\",\"bewhiskered\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧔\" src=\"1f9d4.png\"/>',fitzpatrick_scale:true,category:\"people\"},older_adult:{keywords:[\"human\",\"elder\",\"senior\",\"gender-neutral\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧓\" src=\"1f9d3.png\"/>',fitzpatrick_scale:true,category:\"people\"},older_man:{keywords:[\"human\",\"male\",\"men\",\"old\",\"elder\",\"senior\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👴\" src=\"1f474.png\"/>',fitzpatrick_scale:true,category:\"people\"},older_woman:{keywords:[\"human\",\"female\",\"women\",\"lady\",\"old\",\"elder\",\"senior\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👵\" src=\"1f475.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_with_gua_pi_mao:{keywords:[\"male\",\"boy\",\"chinese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👲\" src=\"1f472.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_with_headscarf:{keywords:[\"female\",\"hijab\",\"mantilla\",\"tichel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧕\" src=\"1f9d5.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_with_turban:{keywords:[\"female\",\"indian\",\"hinduism\",\"arabs\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👳‍♀️\" src=\"1f473-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_with_turban:{keywords:[\"male\",\"indian\",\"hinduism\",\"arabs\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👳\" src=\"1f473.png\"/>',fitzpatrick_scale:true,category:\"people\"},policewoman:{keywords:[\"woman\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👮‍♀️\" src=\"1f46e-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},policeman:{keywords:[\"man\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👮\" src=\"1f46e.png\"/>',fitzpatrick_scale:true,category:\"people\"},construction_worker_woman:{keywords:[\"female\",\"human\",\"wip\",\"build\",\"construction\",\"worker\",\"labor\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👷‍♀️\" src=\"1f477-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},construction_worker_man:{keywords:[\"male\",\"human\",\"wip\",\"guy\",\"build\",\"construction\",\"worker\",\"labor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👷\" src=\"1f477.png\"/>',fitzpatrick_scale:true,category:\"people\"},guardswoman:{keywords:[\"uk\",\"gb\",\"british\",\"female\",\"royal\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💂‍♀️\" src=\"1f482-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},guardsman:{keywords:[\"uk\",\"gb\",\"british\",\"male\",\"guy\",\"royal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💂\" src=\"1f482.png\"/>',fitzpatrick_scale:true,category:\"people\"},female_detective:{keywords:[\"human\",\"spy\",\"detective\",\"female\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕵️‍♀️\" src=\"1f575-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},male_detective:{keywords:[\"human\",\"spy\",\"detective\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕵\" src=\"1f575.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍⚕️\" src=\"1f469-200d-2695-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍⚕️\" src=\"1f468-200d-2695-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_farmer:{keywords:[\"rancher\",\"gardener\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🌾\" src=\"1f469-200d-1f33e.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_farmer:{keywords:[\"rancher\",\"gardener\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🌾\" src=\"1f468-200d-1f33e.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_cook:{keywords:[\"chef\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🍳\" src=\"1f469-200d-1f373.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_cook:{keywords:[\"chef\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🍳\" src=\"1f468-200d-1f373.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_student:{keywords:[\"graduate\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🎓\" src=\"1f469-200d-1f393.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_student:{keywords:[\"graduate\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🎓\" src=\"1f468-200d-1f393.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_singer:{keywords:[\"rockstar\",\"entertainer\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🎤\" src=\"1f469-200d-1f3a4.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_singer:{keywords:[\"rockstar\",\"entertainer\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🎤\" src=\"1f468-200d-1f3a4.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_teacher:{keywords:[\"instructor\",\"professor\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🏫\" src=\"1f469-200d-1f3eb.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_teacher:{keywords:[\"instructor\",\"professor\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🏫\" src=\"1f468-200d-1f3eb.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_factory_worker:{keywords:[\"assembly\",\"industrial\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🏭\" src=\"1f469-200d-1f3ed.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_factory_worker:{keywords:[\"assembly\",\"industrial\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🏭\" src=\"1f468-200d-1f3ed.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"woman\",\"human\",\"laptop\",\"computer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍💻\" src=\"1f469-200d-1f4bb.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"man\",\"human\",\"laptop\",\"computer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍💻\" src=\"1f468-200d-1f4bb.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_office_worker:{keywords:[\"business\",\"manager\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍💼\" src=\"1f469-200d-1f4bc.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_office_worker:{keywords:[\"business\",\"manager\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍💼\" src=\"1f468-200d-1f4bc.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_mechanic:{keywords:[\"plumber\",\"woman\",\"human\",\"wrench\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🔧\" src=\"1f469-200d-1f527.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_mechanic:{keywords:[\"plumber\",\"man\",\"human\",\"wrench\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🔧\" src=\"1f468-200d-1f527.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🔬\" src=\"1f469-200d-1f52c.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🔬\" src=\"1f468-200d-1f52c.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_artist:{keywords:[\"painter\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🎨\" src=\"1f469-200d-1f3a8.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_artist:{keywords:[\"painter\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🎨\" src=\"1f468-200d-1f3a8.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_firefighter:{keywords:[\"fireman\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🚒\" src=\"1f469-200d-1f692.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_firefighter:{keywords:[\"fireman\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🚒\" src=\"1f468-200d-1f692.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_pilot:{keywords:[\"aviator\",\"plane\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍✈️\" src=\"1f469-200d-2708-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_pilot:{keywords:[\"aviator\",\"plane\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍✈️\" src=\"1f468-200d-2708-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_astronaut:{keywords:[\"space\",\"rocket\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🚀\" src=\"1f469-200d-1f680.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_astronaut:{keywords:[\"space\",\"rocket\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🚀\" src=\"1f468-200d-1f680.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_judge:{keywords:[\"justice\",\"court\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍⚖️\" src=\"1f469-200d-2696-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_judge:{keywords:[\"justice\",\"court\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍⚖️\" src=\"1f468-200d-2696-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_superhero:{keywords:[\"woman\",\"female\",\"good\",\"heroine\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦸‍♀️\" src=\"1f9b8-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_superhero:{keywords:[\"man\",\"male\",\"good\",\"hero\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦸‍♂️\" src=\"1f9b8-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_supervillain:{keywords:[\"woman\",\"female\",\"evil\",\"bad\",\"criminal\",\"heroine\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦹‍♀️\" src=\"1f9b9-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_supervillain:{keywords:[\"man\",\"male\",\"evil\",\"bad\",\"criminal\",\"hero\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦹‍♂️\" src=\"1f9b9-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},mrs_claus:{keywords:[\"woman\",\"female\",\"xmas\",\"mother christmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤶\" src=\"1f936.png\"/>',fitzpatrick_scale:true,category:\"people\"},santa:{keywords:[\"festival\",\"man\",\"male\",\"xmas\",\"father christmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎅\" src=\"1f385.png\"/>',fitzpatrick_scale:true,category:\"people\"},sorceress:{keywords:[\"woman\",\"female\",\"mage\",\"witch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧙‍♀️\" src=\"1f9d9-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},wizard:{keywords:[\"man\",\"male\",\"mage\",\"sorcerer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧙‍♂️\" src=\"1f9d9-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_elf:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧝‍♀️\" src=\"1f9dd-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_elf:{keywords:[\"man\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧝‍♂️\" src=\"1f9dd-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_vampire:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧛‍♀️\" src=\"1f9db-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_vampire:{keywords:[\"man\",\"male\",\"dracula\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧛‍♂️\" src=\"1f9db-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_zombie:{keywords:[\"woman\",\"female\",\"undead\",\"walking dead\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧟‍♀️\" src=\"1f9df-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},man_zombie:{keywords:[\"man\",\"male\",\"dracula\",\"undead\",\"walking dead\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧟‍♂️\" src=\"1f9df-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},woman_genie:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧞‍♀️\" src=\"1f9de-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},man_genie:{keywords:[\"man\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧞‍♂️\" src=\"1f9de-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},mermaid:{keywords:[\"woman\",\"female\",\"merwoman\",\"ariel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧜‍♀️\" src=\"1f9dc-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},merman:{keywords:[\"man\",\"male\",\"triton\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧜‍♂️\" src=\"1f9dc-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_fairy:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧚‍♀️\" src=\"1f9da-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_fairy:{keywords:[\"man\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧚‍♂️\" src=\"1f9da-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},angel:{keywords:[\"heaven\",\"wings\",\"halo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👼\" src=\"1f47c.png\"/>',fitzpatrick_scale:true,category:\"people\"},pregnant_woman:{keywords:[\"baby\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤰\" src=\"1f930.png\"/>',fitzpatrick_scale:true,category:\"people\"},breastfeeding:{keywords:[\"nursing\",\"baby\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤱\" src=\"1f931.png\"/>',fitzpatrick_scale:true,category:\"people\"},princess:{keywords:[\"girl\",\"woman\",\"female\",\"blond\",\"crown\",\"royal\",\"queen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👸\" src=\"1f478.png\"/>',fitzpatrick_scale:true,category:\"people\"},prince:{keywords:[\"boy\",\"man\",\"male\",\"crown\",\"royal\",\"king\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤴\" src=\"1f934.png\"/>',fitzpatrick_scale:true,category:\"people\"},bride_with_veil:{keywords:[\"couple\",\"marriage\",\"wedding\",\"woman\",\"bride\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👰\" src=\"1f470.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_in_tuxedo:{keywords:[\"couple\",\"marriage\",\"wedding\",\"groom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤵\" src=\"1f935.png\"/>',fitzpatrick_scale:true,category:\"people\"},running_woman:{keywords:[\"woman\",\"walking\",\"exercise\",\"race\",\"running\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏃‍♀️\" src=\"1f3c3-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},running_man:{keywords:[\"man\",\"walking\",\"exercise\",\"race\",\"running\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏃\" src=\"1f3c3.png\"/>',fitzpatrick_scale:true,category:\"people\"},walking_woman:{keywords:[\"human\",\"feet\",\"steps\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚶‍♀️\" src=\"1f6b6-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},walking_man:{keywords:[\"human\",\"feet\",\"steps\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚶\" src=\"1f6b6.png\"/>',fitzpatrick_scale:true,category:\"people\"},dancer:{keywords:[\"female\",\"girl\",\"woman\",\"fun\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💃\" src=\"1f483.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_dancing:{keywords:[\"male\",\"boy\",\"fun\",\"dancer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕺\" src=\"1f57a.png\"/>',fitzpatrick_scale:true,category:\"people\"},dancing_women:{keywords:[\"female\",\"bunny\",\"women\",\"girls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👯\" src=\"1f46f.png\"/>',fitzpatrick_scale:false,category:\"people\"},dancing_men:{keywords:[\"male\",\"bunny\",\"men\",\"boys\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👯‍♂️\" src=\"1f46f-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},couple:{keywords:[\"pair\",\"people\",\"human\",\"love\",\"date\",\"dating\",\"like\",\"affection\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👫\" src=\"1f46b.png\"/>',fitzpatrick_scale:false,category:\"people\"},two_men_holding_hands:{keywords:[\"pair\",\"couple\",\"love\",\"like\",\"bromance\",\"friendship\",\"people\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👬\" src=\"1f46c.png\"/>',fitzpatrick_scale:false,category:\"people\"},two_women_holding_hands:{keywords:[\"pair\",\"friendship\",\"couple\",\"love\",\"like\",\"female\",\"people\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👭\" src=\"1f46d.png\"/>',fitzpatrick_scale:false,category:\"people\"},bowing_woman:{keywords:[\"woman\",\"female\",\"girl\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙇‍♀️\" src=\"1f647-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},bowing_man:{keywords:[\"man\",\"male\",\"boy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙇\" src=\"1f647.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_facepalming:{keywords:[\"man\",\"male\",\"boy\",\"disbelief\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤦‍♂️\" src=\"1f926-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_facepalming:{keywords:[\"woman\",\"female\",\"girl\",\"disbelief\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤦‍♀️\" src=\"1f926-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_shrugging:{keywords:[\"woman\",\"female\",\"girl\",\"confused\",\"indifferent\",\"doubt\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤷\" src=\"1f937.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_shrugging:{keywords:[\"man\",\"male\",\"boy\",\"confused\",\"indifferent\",\"doubt\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤷‍♂️\" src=\"1f937-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},tipping_hand_woman:{keywords:[\"female\",\"girl\",\"woman\",\"human\",\"information\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💁\" src=\"1f481.png\"/>',fitzpatrick_scale:true,category:\"people\"},tipping_hand_man:{keywords:[\"male\",\"boy\",\"man\",\"human\",\"information\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💁‍♂️\" src=\"1f481-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},no_good_woman:{keywords:[\"female\",\"girl\",\"woman\",\"nope\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙅\" src=\"1f645.png\"/>',fitzpatrick_scale:true,category:\"people\"},no_good_man:{keywords:[\"male\",\"boy\",\"man\",\"nope\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙅‍♂️\" src=\"1f645-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},ok_woman:{keywords:[\"women\",\"girl\",\"female\",\"pink\",\"human\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙆\" src=\"1f646.png\"/>',fitzpatrick_scale:true,category:\"people\"},ok_man:{keywords:[\"men\",\"boy\",\"male\",\"blue\",\"human\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙆‍♂️\" src=\"1f646-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},raising_hand_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙋\" src=\"1f64b.png\"/>',fitzpatrick_scale:true,category:\"people\"},raising_hand_man:{keywords:[\"male\",\"boy\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙋‍♂️\" src=\"1f64b-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},pouting_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙎\" src=\"1f64e.png\"/>',fitzpatrick_scale:true,category:\"people\"},pouting_man:{keywords:[\"male\",\"boy\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙎‍♂️\" src=\"1f64e-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},frowning_woman:{keywords:[\"female\",\"girl\",\"woman\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙍\" src=\"1f64d.png\"/>',fitzpatrick_scale:true,category:\"people\"},frowning_man:{keywords:[\"male\",\"boy\",\"man\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙍‍♂️\" src=\"1f64d-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},haircut_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💇\" src=\"1f487.png\"/>',fitzpatrick_scale:true,category:\"people\"},haircut_man:{keywords:[\"male\",\"boy\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💇‍♂️\" src=\"1f487-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},massage_woman:{keywords:[\"female\",\"girl\",\"woman\",\"head\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💆\" src=\"1f486.png\"/>',fitzpatrick_scale:true,category:\"people\"},massage_man:{keywords:[\"male\",\"boy\",\"man\",\"head\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💆‍♂️\" src=\"1f486-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_in_steamy_room:{keywords:[\"female\",\"woman\",\"spa\",\"steamroom\",\"sauna\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧖‍♀️\" src=\"1f9d6-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_in_steamy_room:{keywords:[\"male\",\"man\",\"spa\",\"steamroom\",\"sauna\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧖‍♂️\" src=\"1f9d6-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},couple_with_heart_woman_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💑\" src=\"1f491.png\"/>',fitzpatrick_scale:false,category:\"people\"},couple_with_heart_woman_woman:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍❤️‍👩\" src=\"1f469-200d-2764-fe0f-200d-1f469.png\"/>',fitzpatrick_scale:false,category:\"people\"},couple_with_heart_man_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍❤️‍👨\" src=\"1f468-200d-2764-fe0f-200d-1f468.png\"/>',fitzpatrick_scale:false,category:\"people\"},couplekiss_man_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💏\" src=\"1f48f.png\"/>',fitzpatrick_scale:false,category:\"people\"},couplekiss_woman_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍❤️‍💋‍👩\" src=\"1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png\"/>',fitzpatrick_scale:false,category:\"people\"},couplekiss_man_man:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍❤️‍💋‍👨\" src=\"1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy:{keywords:[\"home\",\"parents\",\"child\",\"mom\",\"dad\",\"father\",\"mother\",\"people\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👪\" src=\"1f46a.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👧\" src=\"1f468-200d-1f469-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👧‍👦\" src=\"1f468-200d-1f469-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👦‍👦\" src=\"1f468-200d-1f469-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👧‍👧\" src=\"1f468-200d-1f469-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👦\" src=\"1f469-200d-1f469-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👧\" src=\"1f469-200d-1f469-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👧‍👦\" src=\"1f469-200d-1f469-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👦‍👦\" src=\"1f469-200d-1f469-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👧‍👧\" src=\"1f469-200d-1f469-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👦\" src=\"1f468-200d-1f468-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👧\" src=\"1f468-200d-1f468-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👧‍👦\" src=\"1f468-200d-1f468-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👦‍👦\" src=\"1f468-200d-1f468-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👧‍👧\" src=\"1f468-200d-1f468-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👦\" src=\"1f469-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👧\" src=\"1f469-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👧‍👦\" src=\"1f469-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👦‍👦\" src=\"1f469-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👧‍👧\" src=\"1f469-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👦\" src=\"1f468-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👧\" src=\"1f468-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👧‍👦\" src=\"1f468-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👦‍👦\" src=\"1f468-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👧‍👧\" src=\"1f468-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},yarn:{keywords:[\"ball\",\"crochet\",\"knit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧶\" src=\"1f9f6.png\"/>',fitzpatrick_scale:false,category:\"people\"},thread:{keywords:[\"needle\",\"sewing\",\"spool\",\"string\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧵\" src=\"1f9f5.png\"/>',fitzpatrick_scale:false,category:\"people\"},coat:{keywords:[\"jacket\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧥\" src=\"1f9e5.png\"/>',fitzpatrick_scale:false,category:\"people\"},labcoat:{keywords:[\"doctor\",\"experiment\",\"scientist\",\"chemist\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥼\" src=\"1f97c.png\"/>',fitzpatrick_scale:false,category:\"people\"},womans_clothes:{keywords:[\"fashion\",\"shopping_bags\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👚\" src=\"1f45a.png\"/>',fitzpatrick_scale:false,category:\"people\"},tshirt:{keywords:[\"fashion\",\"cloth\",\"casual\",\"shirt\",\"tee\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👕\" src=\"1f455.png\"/>',fitzpatrick_scale:false,category:\"people\"},jeans:{keywords:[\"fashion\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👖\" src=\"1f456.png\"/>',fitzpatrick_scale:false,category:\"people\"},necktie:{keywords:[\"shirt\",\"suitup\",\"formal\",\"fashion\",\"cloth\",\"business\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👔\" src=\"1f454.png\"/>',fitzpatrick_scale:false,category:\"people\"},dress:{keywords:[\"clothes\",\"fashion\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👗\" src=\"1f457.png\"/>',fitzpatrick_scale:false,category:\"people\"},bikini:{keywords:[\"swimming\",\"female\",\"woman\",\"girl\",\"fashion\",\"beach\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👙\" src=\"1f459.png\"/>',fitzpatrick_scale:false,category:\"people\"},kimono:{keywords:[\"dress\",\"fashion\",\"women\",\"female\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👘\" src=\"1f458.png\"/>',fitzpatrick_scale:false,category:\"people\"},lipstick:{keywords:[\"female\",\"girl\",\"fashion\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💄\" src=\"1f484.png\"/>',fitzpatrick_scale:false,category:\"people\"},kiss:{keywords:[\"face\",\"lips\",\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💋\" src=\"1f48b.png\"/>',fitzpatrick_scale:false,category:\"people\"},footprints:{keywords:[\"feet\",\"tracking\",\"walking\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👣\" src=\"1f463.png\"/>',fitzpatrick_scale:false,category:\"people\"},flat_shoe:{keywords:[\"ballet\",\"slip-on\",\"slipper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥿\" src=\"1f97f.png\"/>',fitzpatrick_scale:false,category:\"people\"},high_heel:{keywords:[\"fashion\",\"shoes\",\"female\",\"pumps\",\"stiletto\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👠\" src=\"1f460.png\"/>',fitzpatrick_scale:false,category:\"people\"},sandal:{keywords:[\"shoes\",\"fashion\",\"flip flops\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👡\" src=\"1f461.png\"/>',fitzpatrick_scale:false,category:\"people\"},boot:{keywords:[\"shoes\",\"fashion\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👢\" src=\"1f462.png\"/>',fitzpatrick_scale:false,category:\"people\"},mans_shoe:{keywords:[\"fashion\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👞\" src=\"1f45e.png\"/>',fitzpatrick_scale:false,category:\"people\"},athletic_shoe:{keywords:[\"shoes\",\"sports\",\"sneakers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👟\" src=\"1f45f.png\"/>',fitzpatrick_scale:false,category:\"people\"},hiking_boot:{keywords:[\"backpacking\",\"camping\",\"hiking\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥾\" src=\"1f97e.png\"/>',fitzpatrick_scale:false,category:\"people\"},socks:{keywords:[\"stockings\",\"clothes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧦\" src=\"1f9e6.png\"/>',fitzpatrick_scale:false,category:\"people\"},gloves:{keywords:[\"hands\",\"winter\",\"clothes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧤\" src=\"1f9e4.png\"/>',fitzpatrick_scale:false,category:\"people\"},scarf:{keywords:[\"neck\",\"winter\",\"clothes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧣\" src=\"1f9e3.png\"/>',fitzpatrick_scale:false,category:\"people\"},womans_hat:{keywords:[\"fashion\",\"accessories\",\"female\",\"lady\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👒\" src=\"1f452.png\"/>',fitzpatrick_scale:false,category:\"people\"},tophat:{keywords:[\"magic\",\"gentleman\",\"classy\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎩\" src=\"1f3a9.png\"/>',fitzpatrick_scale:false,category:\"people\"},billed_hat:{keywords:[\"cap\",\"baseball\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧢\" src=\"1f9e2.png\"/>',fitzpatrick_scale:false,category:\"people\"},rescue_worker_helmet:{keywords:[\"construction\",\"build\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛑\" src=\"26d1.png\"/>',fitzpatrick_scale:false,category:\"people\"},mortar_board:{keywords:[\"school\",\"college\",\"degree\",\"university\",\"graduation\",\"cap\",\"hat\",\"legal\",\"learn\",\"education\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎓\" src=\"1f393.png\"/>',fitzpatrick_scale:false,category:\"people\"},crown:{keywords:[\"king\",\"kod\",\"leader\",\"royalty\",\"lord\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👑\" src=\"1f451.png\"/>',fitzpatrick_scale:false,category:\"people\"},school_satchel:{keywords:[\"student\",\"education\",\"bag\",\"backpack\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎒\" src=\"1f392.png\"/>',fitzpatrick_scale:false,category:\"people\"},luggage:{keywords:[\"packing\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧳\" src=\"1f9f3.png\"/>',fitzpatrick_scale:false,category:\"people\"},pouch:{keywords:[\"bag\",\"accessories\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👝\" src=\"1f45d.png\"/>',fitzpatrick_scale:false,category:\"people\"},purse:{keywords:[\"fashion\",\"accessories\",\"money\",\"sales\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👛\" src=\"1f45b.png\"/>',fitzpatrick_scale:false,category:\"people\"},handbag:{keywords:[\"fashion\",\"accessory\",\"accessories\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👜\" src=\"1f45c.png\"/>',fitzpatrick_scale:false,category:\"people\"},briefcase:{keywords:[\"business\",\"documents\",\"work\",\"law\",\"legal\",\"job\",\"career\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💼\" src=\"1f4bc.png\"/>',fitzpatrick_scale:false,category:\"people\"},eyeglasses:{keywords:[\"fashion\",\"accessories\",\"eyesight\",\"nerdy\",\"dork\",\"geek\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👓\" src=\"1f453.png\"/>',fitzpatrick_scale:false,category:\"people\"},dark_sunglasses:{keywords:[\"face\",\"cool\",\"accessories\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕶\" src=\"1f576.png\"/>',fitzpatrick_scale:false,category:\"people\"},goggles:{keywords:[\"eyes\",\"protection\",\"safety\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥽\" src=\"1f97d.png\"/>',fitzpatrick_scale:false,category:\"people\"},ring:{keywords:[\"wedding\",\"propose\",\"marriage\",\"valentines\",\"diamond\",\"fashion\",\"jewelry\",\"gem\",\"engagement\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💍\" src=\"1f48d.png\"/>',fitzpatrick_scale:false,category:\"people\"},closed_umbrella:{keywords:[\"weather\",\"rain\",\"drizzle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌂\" src=\"1f302.png\"/>',fitzpatrick_scale:false,category:\"people\"},dog:{keywords:[\"animal\",\"friend\",\"nature\",\"woof\",\"puppy\",\"pet\",\"faithful\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐶\" src=\"1f436.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cat:{keywords:[\"animal\",\"meow\",\"nature\",\"pet\",\"kitten\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐱\" src=\"1f431.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse:{keywords:[\"animal\",\"nature\",\"cheese_wedge\",\"rodent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐭\" src=\"1f42d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hamster:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐹\" src=\"1f439.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit:{keywords:[\"animal\",\"nature\",\"pet\",\"spring\",\"magic\",\"bunny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐰\" src=\"1f430.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fox_face:{keywords:[\"animal\",\"nature\",\"face\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦊\" src=\"1f98a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bear:{keywords:[\"animal\",\"nature\",\"wild\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐻\" src=\"1f43b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},panda_face:{keywords:[\"animal\",\"nature\",\"panda\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐼\" src=\"1f43c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},koala:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐨\" src=\"1f428.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger:{keywords:[\"animal\",\"cat\",\"danger\",\"wild\",\"nature\",\"roar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐯\" src=\"1f42f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},lion:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦁\" src=\"1f981.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cow:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐮\" src=\"1f42e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},pig:{keywords:[\"animal\",\"oink\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐷\" src=\"1f437.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},pig_nose:{keywords:[\"animal\",\"oink\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐽\" src=\"1f43d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},frog:{keywords:[\"animal\",\"nature\",\"croak\",\"toad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐸\" src=\"1f438.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},squid:{keywords:[\"animal\",\"nature\",\"ocean\",\"sea\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦑\" src=\"1f991.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},octopus:{keywords:[\"animal\",\"creature\",\"ocean\",\"sea\",\"nature\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐙\" src=\"1f419.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shrimp:{keywords:[\"animal\",\"ocean\",\"nature\",\"seafood\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦐\" src=\"1f990.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey_face:{keywords:[\"animal\",\"nature\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐵\" src=\"1f435.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},gorilla:{keywords:[\"animal\",\"nature\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦍\" src=\"1f98d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},see_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"haha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙈\" src=\"1f648.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hear_no_evil:{keywords:[\"animal\",\"monkey\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙉\" src=\"1f649.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},speak_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"omg\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙊\" src=\"1f64a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey:{keywords:[\"animal\",\"nature\",\"banana\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐒\" src=\"1f412.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},chicken:{keywords:[\"animal\",\"cluck\",\"nature\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐔\" src=\"1f414.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},penguin:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐧\" src=\"1f427.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bird:{keywords:[\"animal\",\"nature\",\"fly\",\"tweet\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐦\" src=\"1f426.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},baby_chick:{keywords:[\"animal\",\"chicken\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐤\" src=\"1f424.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hatching_chick:{keywords:[\"animal\",\"chicken\",\"egg\",\"born\",\"baby\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐣\" src=\"1f423.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hatched_chick:{keywords:[\"animal\",\"chicken\",\"baby\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐥\" src=\"1f425.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},duck:{keywords:[\"animal\",\"nature\",\"bird\",\"mallard\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦆\" src=\"1f986.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},eagle:{keywords:[\"animal\",\"nature\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦅\" src=\"1f985.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},owl:{keywords:[\"animal\",\"nature\",\"bird\",\"hoot\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦉\" src=\"1f989.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bat:{keywords:[\"animal\",\"nature\",\"blind\",\"vampire\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦇\" src=\"1f987.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},wolf:{keywords:[\"animal\",\"nature\",\"wild\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐺\" src=\"1f43a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},boar:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐗\" src=\"1f417.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},horse:{keywords:[\"animal\",\"brown\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐴\" src=\"1f434.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},unicorn:{keywords:[\"animal\",\"nature\",\"mystical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦄\" src=\"1f984.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},honeybee:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\",\"spring\",\"honey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐝\" src=\"1f41d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bug:{keywords:[\"animal\",\"insect\",\"nature\",\"worm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐛\" src=\"1f41b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},butterfly:{keywords:[\"animal\",\"insect\",\"nature\",\"caterpillar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦋\" src=\"1f98b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snail:{keywords:[\"slow\",\"animal\",\"shell\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐌\" src=\"1f40c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},beetle:{keywords:[\"animal\",\"insect\",\"nature\",\"ladybug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐞\" src=\"1f41e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ant:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐜\" src=\"1f41c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},grasshopper:{keywords:[\"animal\",\"cricket\",\"chirp\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦗\" src=\"1f997.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},spider:{keywords:[\"animal\",\"arachnid\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕷\" src=\"1f577.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},scorpion:{keywords:[\"animal\",\"arachnid\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦂\" src=\"1f982.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},crab:{keywords:[\"animal\",\"crustacean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦀\" src=\"1f980.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snake:{keywords:[\"animal\",\"evil\",\"nature\",\"hiss\",\"python\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐍\" src=\"1f40d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},lizard:{keywords:[\"animal\",\"nature\",\"reptile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦎\" src=\"1f98e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},\"t-rex\":{keywords:[\"animal\",\"nature\",\"dinosaur\",\"tyrannosaurus\",\"extinct\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦖\" src=\"1f996.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sauropod:{keywords:[\"animal\",\"nature\",\"dinosaur\",\"brachiosaurus\",\"brontosaurus\",\"diplodocus\",\"extinct\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦕\" src=\"1f995.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},turtle:{keywords:[\"animal\",\"slow\",\"nature\",\"tortoise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐢\" src=\"1f422.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tropical_fish:{keywords:[\"animal\",\"swim\",\"ocean\",\"beach\",\"nemo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐠\" src=\"1f420.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fish:{keywords:[\"animal\",\"food\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐟\" src=\"1f41f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},blowfish:{keywords:[\"animal\",\"nature\",\"food\",\"sea\",\"ocean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐡\" src=\"1f421.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dolphin:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"flipper\",\"fins\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐬\" src=\"1f42c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shark:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"jaws\",\"fins\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦈\" src=\"1f988.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},whale:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐳\" src=\"1f433.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},whale2:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐋\" src=\"1f40b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},crocodile:{keywords:[\"animal\",\"nature\",\"reptile\",\"lizard\",\"alligator\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐊\" src=\"1f40a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},leopard:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐆\" src=\"1f406.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},zebra:{keywords:[\"animal\",\"nature\",\"stripes\",\"safari\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦓\" src=\"1f993.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger2:{keywords:[\"animal\",\"nature\",\"roar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐅\" src=\"1f405.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},water_buffalo:{keywords:[\"animal\",\"nature\",\"ox\",\"cow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐃\" src=\"1f403.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ox:{keywords:[\"animal\",\"cow\",\"beef\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐂\" src=\"1f402.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cow2:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐄\" src=\"1f404.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},deer:{keywords:[\"animal\",\"nature\",\"horns\",\"venison\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦌\" src=\"1f98c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dromedary_camel:{keywords:[\"animal\",\"hot\",\"desert\",\"hump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐪\" src=\"1f42a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},camel:{keywords:[\"animal\",\"nature\",\"hot\",\"desert\",\"hump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐫\" src=\"1f42b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},giraffe:{keywords:[\"animal\",\"nature\",\"spots\",\"safari\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦒\" src=\"1f992.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},elephant:{keywords:[\"animal\",\"nature\",\"nose\",\"th\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐘\" src=\"1f418.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rhinoceros:{keywords:[\"animal\",\"nature\",\"horn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦏\" src=\"1f98f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},goat:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐐\" src=\"1f410.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ram:{keywords:[\"animal\",\"sheep\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐏\" src=\"1f40f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sheep:{keywords:[\"animal\",\"nature\",\"wool\",\"shipit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐑\" src=\"1f411.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},racehorse:{keywords:[\"animal\",\"gamble\",\"luck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐎\" src=\"1f40e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},pig2:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐖\" src=\"1f416.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rat:{keywords:[\"animal\",\"mouse\",\"rodent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐀\" src=\"1f400.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse2:{keywords:[\"animal\",\"nature\",\"rodent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐁\" src=\"1f401.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rooster:{keywords:[\"animal\",\"nature\",\"chicken\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐓\" src=\"1f413.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},turkey:{keywords:[\"animal\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦃\" src=\"1f983.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dove:{keywords:[\"animal\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕊\" src=\"1f54a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dog2:{keywords:[\"animal\",\"nature\",\"friend\",\"doge\",\"pet\",\"faithful\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐕\" src=\"1f415.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},poodle:{keywords:[\"dog\",\"animal\",\"101\",\"nature\",\"pet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐩\" src=\"1f429.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cat2:{keywords:[\"animal\",\"meow\",\"pet\",\"cats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐈\" src=\"1f408.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit2:{keywords:[\"animal\",\"nature\",\"pet\",\"magic\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐇\" src=\"1f407.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},chipmunk:{keywords:[\"animal\",\"nature\",\"rodent\",\"squirrel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐿\" src=\"1f43f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hedgehog:{keywords:[\"animal\",\"nature\",\"spiny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦔\" src=\"1f994.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},raccoon:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦝\" src=\"1f99d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},llama:{keywords:[\"animal\",\"nature\",\"alpaca\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦙\" src=\"1f999.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hippopotamus:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦛\" src=\"1f99b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},kangaroo:{keywords:[\"animal\",\"nature\",\"australia\",\"joey\",\"hop\",\"marsupial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦘\" src=\"1f998.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},badger:{keywords:[\"animal\",\"nature\",\"honey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦡\" src=\"1f9a1.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},swan:{keywords:[\"animal\",\"nature\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦢\" src=\"1f9a2.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},peacock:{keywords:[\"animal\",\"nature\",\"peahen\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦚\" src=\"1f99a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},parrot:{keywords:[\"animal\",\"nature\",\"bird\",\"pirate\",\"talk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦜\" src=\"1f99c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},lobster:{keywords:[\"animal\",\"nature\",\"bisque\",\"claws\",\"seafood\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦞\" src=\"1f99e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mosquito:{keywords:[\"animal\",\"nature\",\"insect\",\"malaria\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦟\" src=\"1f99f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},paw_prints:{keywords:[\"animal\",\"tracking\",\"footprints\",\"dog\",\"cat\",\"pet\",\"feet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐾\" src=\"1f43e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐉\" src=\"1f409.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon_face:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐲\" src=\"1f432.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cactus:{keywords:[\"vegetable\",\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌵\" src=\"1f335.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},christmas_tree:{keywords:[\"festival\",\"vacation\",\"december\",\"xmas\",\"celebration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎄\" src=\"1f384.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},evergreen_tree:{keywords:[\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌲\" src=\"1f332.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},deciduous_tree:{keywords:[\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌳\" src=\"1f333.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},palm_tree:{keywords:[\"plant\",\"vegetable\",\"nature\",\"summer\",\"beach\",\"mojito\",\"tropical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌴\" src=\"1f334.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},seedling:{keywords:[\"plant\",\"nature\",\"grass\",\"lawn\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌱\" src=\"1f331.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},herb:{keywords:[\"vegetable\",\"plant\",\"medicine\",\"weed\",\"grass\",\"lawn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌿\" src=\"1f33f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shamrock:{keywords:[\"vegetable\",\"plant\",\"nature\",\"irish\",\"clover\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☘\" src=\"2618.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},four_leaf_clover:{keywords:[\"vegetable\",\"plant\",\"nature\",\"lucky\",\"irish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍀\" src=\"1f340.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bamboo:{keywords:[\"plant\",\"nature\",\"vegetable\",\"panda\",\"pine_decoration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎍\" src=\"1f38d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tanabata_tree:{keywords:[\"plant\",\"nature\",\"branch\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎋\" src=\"1f38b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},leaves:{keywords:[\"nature\",\"plant\",\"tree\",\"vegetable\",\"grass\",\"lawn\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍃\" src=\"1f343.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fallen_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"leaves\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍂\" src=\"1f342.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},maple_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"ca\",\"fall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍁\" src=\"1f341.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ear_of_rice:{keywords:[\"nature\",\"plant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌾\" src=\"1f33e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hibiscus:{keywords:[\"plant\",\"vegetable\",\"flowers\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌺\" src=\"1f33a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sunflower:{keywords:[\"nature\",\"plant\",\"fall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌻\" src=\"1f33b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rose:{keywords:[\"flowers\",\"valentines\",\"love\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌹\" src=\"1f339.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},wilted_flower:{keywords:[\"plant\",\"nature\",\"flower\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥀\" src=\"1f940.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tulip:{keywords:[\"flowers\",\"plant\",\"nature\",\"summer\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌷\" src=\"1f337.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},blossom:{keywords:[\"nature\",\"flowers\",\"yellow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌼\" src=\"1f33c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cherry_blossom:{keywords:[\"nature\",\"plant\",\"spring\",\"flower\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌸\" src=\"1f338.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bouquet:{keywords:[\"flowers\",\"nature\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💐\" src=\"1f490.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mushroom:{keywords:[\"plant\",\"vegetable\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍄\" src=\"1f344.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},chestnut:{keywords:[\"food\",\"squirrel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌰\" src=\"1f330.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},jack_o_lantern:{keywords:[\"halloween\",\"light\",\"pumpkin\",\"creepy\",\"fall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎃\" src=\"1f383.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shell:{keywords:[\"nature\",\"sea\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐚\" src=\"1f41a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},spider_web:{keywords:[\"animal\",\"insect\",\"arachnid\",\"silk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕸\" src=\"1f578.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_americas:{keywords:[\"globe\",\"world\",\"USA\",\"international\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌎\" src=\"1f30e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_africa:{keywords:[\"globe\",\"world\",\"international\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌍\" src=\"1f30d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_asia:{keywords:[\"globe\",\"world\",\"east\",\"international\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌏\" src=\"1f30f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon:{keywords:[\"nature\",\"yellow\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌕\" src=\"1f315.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_gibbous_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\",\"waxing_gibbous_moon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌖\" src=\"1f316.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌗\" src=\"1f317.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌘\" src=\"1f318.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌑\" src=\"1f311.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌒\" src=\"1f312.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌓\" src=\"1f313.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_gibbous_moon:{keywords:[\"nature\",\"night\",\"sky\",\"gray\",\"twilight\",\"planet\",\"space\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌔\" src=\"1f314.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌚\" src=\"1f31a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌝\" src=\"1f31d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌛\" src=\"1f31b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌜\" src=\"1f31c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_with_face:{keywords:[\"nature\",\"morning\",\"sky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌞\" src=\"1f31e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},crescent_moon:{keywords:[\"night\",\"sleep\",\"sky\",\"evening\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌙\" src=\"1f319.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},star:{keywords:[\"night\",\"yellow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⭐\" src=\"2b50.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},star2:{keywords:[\"night\",\"sparkle\",\"awesome\",\"good\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌟\" src=\"1f31f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dizzy:{keywords:[\"star\",\"sparkle\",\"shoot\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💫\" src=\"1f4ab.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sparkles:{keywords:[\"stars\",\"shine\",\"shiny\",\"cool\",\"awesome\",\"good\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✨\" src=\"2728.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},comet:{keywords:[\"space\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☄\" src=\"2604.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sunny:{keywords:[\"weather\",\"nature\",\"brightness\",\"summer\",\"beach\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☀️\" src=\"2600.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_small_cloud:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌤\" src=\"1f324.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},partly_sunny:{keywords:[\"weather\",\"nature\",\"cloudy\",\"morning\",\"fall\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛅\" src=\"26c5.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_large_cloud:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌥\" src=\"1f325.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_rain_cloud:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌦\" src=\"1f326.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud:{keywords:[\"weather\",\"sky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☁️\" src=\"2601.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_rain:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌧\" src=\"1f327.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning_and_rain:{keywords:[\"weather\",\"lightning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛈\" src=\"26c8.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning:{keywords:[\"weather\",\"thunder\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌩\" src=\"1f329.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},zap:{keywords:[\"thunder\",\"weather\",\"lightning bolt\",\"fast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚡\" src=\"26a1.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fire:{keywords:[\"hot\",\"cook\",\"flame\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔥\" src=\"1f525.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},boom:{keywords:[\"bomb\",\"explode\",\"explosion\",\"collision\",\"blown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💥\" src=\"1f4a5.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snowflake:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❄️\" src=\"2744.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_snow:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌨\" src=\"1f328.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\",\"without_snow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛄\" src=\"26c4.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman_with_snow:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☃\" src=\"2603.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},wind_face:{keywords:[\"gust\",\"air\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌬\" src=\"1f32c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dash:{keywords:[\"wind\",\"air\",\"fast\",\"shoo\",\"fart\",\"smoke\",\"puff\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💨\" src=\"1f4a8.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tornado:{keywords:[\"weather\",\"cyclone\",\"twister\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌪\" src=\"1f32a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fog:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌫\" src=\"1f32b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},open_umbrella:{keywords:[\"weather\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☂\" src=\"2602.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},umbrella:{keywords:[\"rainy\",\"weather\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☔\" src=\"2614.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},droplet:{keywords:[\"water\",\"drip\",\"faucet\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💧\" src=\"1f4a7.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sweat_drops:{keywords:[\"water\",\"drip\",\"oops\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💦\" src=\"1f4a6.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ocean:{keywords:[\"sea\",\"water\",\"wave\",\"nature\",\"tsunami\",\"disaster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌊\" src=\"1f30a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},green_apple:{keywords:[\"fruit\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍏\" src=\"1f34f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},apple:{keywords:[\"fruit\",\"mac\",\"school\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍎\" src=\"1f34e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pear:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍐\" src=\"1f350.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tangerine:{keywords:[\"food\",\"fruit\",\"nature\",\"orange\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍊\" src=\"1f34a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},lemon:{keywords:[\"fruit\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍋\" src=\"1f34b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},banana:{keywords:[\"fruit\",\"food\",\"monkey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍌\" src=\"1f34c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},watermelon:{keywords:[\"fruit\",\"food\",\"picnic\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍉\" src=\"1f349.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},grapes:{keywords:[\"fruit\",\"food\",\"wine\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍇\" src=\"1f347.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},strawberry:{keywords:[\"fruit\",\"food\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍓\" src=\"1f353.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},melon:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍈\" src=\"1f348.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cherries:{keywords:[\"food\",\"fruit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍒\" src=\"1f352.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},peach:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍑\" src=\"1f351.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pineapple:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍍\" src=\"1f34d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},coconut:{keywords:[\"fruit\",\"nature\",\"food\",\"palm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥥\" src=\"1f965.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},kiwi_fruit:{keywords:[\"fruit\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥝\" src=\"1f95d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},mango:{keywords:[\"fruit\",\"food\",\"tropical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥭\" src=\"1f96d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},avocado:{keywords:[\"fruit\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥑\" src=\"1f951.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},broccoli:{keywords:[\"fruit\",\"food\",\"vegetable\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥦\" src=\"1f966.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tomato:{keywords:[\"fruit\",\"vegetable\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍅\" src=\"1f345.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},eggplant:{keywords:[\"vegetable\",\"nature\",\"food\",\"aubergine\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍆\" src=\"1f346.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cucumber:{keywords:[\"fruit\",\"food\",\"pickle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥒\" src=\"1f952.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},carrot:{keywords:[\"vegetable\",\"food\",\"orange\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥕\" src=\"1f955.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},hot_pepper:{keywords:[\"food\",\"spicy\",\"chilli\",\"chili\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌶\" src=\"1f336.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},potato:{keywords:[\"food\",\"tuber\",\"vegatable\",\"starch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥔\" src=\"1f954.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},corn:{keywords:[\"food\",\"vegetable\",\"plant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌽\" src=\"1f33d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},leafy_greens:{keywords:[\"food\",\"vegetable\",\"plant\",\"bok choy\",\"cabbage\",\"kale\",\"lettuce\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥬\" src=\"1f96c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sweet_potato:{keywords:[\"food\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍠\" src=\"1f360.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},peanuts:{keywords:[\"food\",\"nut\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥜\" src=\"1f95c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},honey_pot:{keywords:[\"bees\",\"sweet\",\"kitchen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍯\" src=\"1f36f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},croissant:{keywords:[\"food\",\"bread\",\"french\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥐\" src=\"1f950.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bread:{keywords:[\"food\",\"wheat\",\"breakfast\",\"toast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍞\" src=\"1f35e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},baguette_bread:{keywords:[\"food\",\"bread\",\"french\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥖\" src=\"1f956.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bagel:{keywords:[\"food\",\"bread\",\"bakery\",\"schmear\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥯\" src=\"1f96f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pretzel:{keywords:[\"food\",\"bread\",\"twisted\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥨\" src=\"1f968.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cheese:{keywords:[\"food\",\"chadder\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧀\" src=\"1f9c0.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},egg:{keywords:[\"food\",\"chicken\",\"breakfast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥚\" src=\"1f95a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bacon:{keywords:[\"food\",\"breakfast\",\"pork\",\"pig\",\"meat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥓\" src=\"1f953.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},steak:{keywords:[\"food\",\"cow\",\"meat\",\"cut\",\"chop\",\"lambchop\",\"porkchop\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥩\" src=\"1f969.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pancakes:{keywords:[\"food\",\"breakfast\",\"flapjacks\",\"hotcakes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥞\" src=\"1f95e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},poultry_leg:{keywords:[\"food\",\"meat\",\"drumstick\",\"bird\",\"chicken\",\"turkey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍗\" src=\"1f357.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},meat_on_bone:{keywords:[\"good\",\"food\",\"drumstick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍖\" src=\"1f356.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bone:{keywords:[\"skeleton\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦴\" src=\"1f9b4.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fried_shrimp:{keywords:[\"food\",\"animal\",\"appetizer\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍤\" src=\"1f364.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fried_egg:{keywords:[\"food\",\"breakfast\",\"kitchen\",\"egg\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍳\" src=\"1f373.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},hamburger:{keywords:[\"meat\",\"fast food\",\"beef\",\"cheeseburger\",\"mcdonalds\",\"burger king\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍔\" src=\"1f354.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fries:{keywords:[\"chips\",\"snack\",\"fast food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍟\" src=\"1f35f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},stuffed_flatbread:{keywords:[\"food\",\"flatbread\",\"stuffed\",\"gyro\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥙\" src=\"1f959.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},hotdog:{keywords:[\"food\",\"frankfurter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌭\" src=\"1f32d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pizza:{keywords:[\"food\",\"party\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍕\" src=\"1f355.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sandwich:{keywords:[\"food\",\"lunch\",\"bread\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥪\" src=\"1f96a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},canned_food:{keywords:[\"food\",\"soup\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥫\" src=\"1f96b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},spaghetti:{keywords:[\"food\",\"italian\",\"noodle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍝\" src=\"1f35d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},taco:{keywords:[\"food\",\"mexican\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌮\" src=\"1f32e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},burrito:{keywords:[\"food\",\"mexican\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌯\" src=\"1f32f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},green_salad:{keywords:[\"food\",\"healthy\",\"lettuce\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥗\" src=\"1f957.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},shallow_pan_of_food:{keywords:[\"food\",\"cooking\",\"casserole\",\"paella\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥘\" src=\"1f958.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},ramen:{keywords:[\"food\",\"japanese\",\"noodle\",\"chopsticks\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍜\" src=\"1f35c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},stew:{keywords:[\"food\",\"meat\",\"soup\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍲\" src=\"1f372.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fish_cake:{keywords:[\"food\",\"japan\",\"sea\",\"beach\",\"narutomaki\",\"pink\",\"swirl\",\"kamaboko\",\"surimi\",\"ramen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍥\" src=\"1f365.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fortune_cookie:{keywords:[\"food\",\"prophecy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥠\" src=\"1f960.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sushi:{keywords:[\"food\",\"fish\",\"japanese\",\"rice\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍣\" src=\"1f363.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bento:{keywords:[\"food\",\"japanese\",\"box\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍱\" src=\"1f371.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},curry:{keywords:[\"food\",\"spicy\",\"hot\",\"indian\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍛\" src=\"1f35b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},rice_ball:{keywords:[\"food\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍙\" src=\"1f359.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},rice:{keywords:[\"food\",\"china\",\"asian\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍚\" src=\"1f35a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},rice_cracker:{keywords:[\"food\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍘\" src=\"1f358.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},oden:{keywords:[\"food\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍢\" src=\"1f362.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},dango:{keywords:[\"food\",\"dessert\",\"sweet\",\"japanese\",\"barbecue\",\"meat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍡\" src=\"1f361.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},shaved_ice:{keywords:[\"hot\",\"dessert\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍧\" src=\"1f367.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},ice_cream:{keywords:[\"food\",\"hot\",\"dessert\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍨\" src=\"1f368.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},icecream:{keywords:[\"food\",\"hot\",\"dessert\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍦\" src=\"1f366.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pie:{keywords:[\"food\",\"dessert\",\"pastry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥧\" src=\"1f967.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cake:{keywords:[\"food\",\"dessert\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍰\" src=\"1f370.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cupcake:{keywords:[\"food\",\"dessert\",\"bakery\",\"sweet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧁\" src=\"1f9c1.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},moon_cake:{keywords:[\"food\",\"autumn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥮\" src=\"1f96e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},birthday:{keywords:[\"food\",\"dessert\",\"cake\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎂\" src=\"1f382.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},custard:{keywords:[\"dessert\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍮\" src=\"1f36e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},candy:{keywords:[\"snack\",\"dessert\",\"sweet\",\"lolly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍬\" src=\"1f36c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},lollipop:{keywords:[\"food\",\"snack\",\"candy\",\"sweet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍭\" src=\"1f36d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},chocolate_bar:{keywords:[\"food\",\"snack\",\"dessert\",\"sweet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍫\" src=\"1f36b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},popcorn:{keywords:[\"food\",\"movie theater\",\"films\",\"snack\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍿\" src=\"1f37f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},dumpling:{keywords:[\"food\",\"empanada\",\"pierogi\",\"potsticker\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥟\" src=\"1f95f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},doughnut:{keywords:[\"food\",\"dessert\",\"snack\",\"sweet\",\"donut\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍩\" src=\"1f369.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cookie:{keywords:[\"food\",\"snack\",\"oreo\",\"chocolate\",\"sweet\",\"dessert\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍪\" src=\"1f36a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},milk_glass:{keywords:[\"beverage\",\"drink\",\"cow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥛\" src=\"1f95b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},beer:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍺\" src=\"1f37a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},beers:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍻\" src=\"1f37b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},clinking_glasses:{keywords:[\"beverage\",\"drink\",\"party\",\"alcohol\",\"celebrate\",\"cheers\",\"wine\",\"champagne\",\"toast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥂\" src=\"1f942.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},wine_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍷\" src=\"1f377.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tumbler_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"liquor\",\"booze\",\"bourbon\",\"scotch\",\"whisky\",\"glass\",\"shot\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥃\" src=\"1f943.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cocktail:{keywords:[\"drink\",\"drunk\",\"alcohol\",\"beverage\",\"booze\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍸\" src=\"1f378.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tropical_drink:{keywords:[\"beverage\",\"cocktail\",\"summer\",\"beach\",\"alcohol\",\"booze\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍹\" src=\"1f379.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},champagne:{keywords:[\"drink\",\"wine\",\"bottle\",\"celebration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍾\" src=\"1f37e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sake:{keywords:[\"wine\",\"drink\",\"drunk\",\"beverage\",\"japanese\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍶\" src=\"1f376.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tea:{keywords:[\"drink\",\"bowl\",\"breakfast\",\"green\",\"british\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍵\" src=\"1f375.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cup_with_straw:{keywords:[\"drink\",\"soda\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥤\" src=\"1f964.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},coffee:{keywords:[\"beverage\",\"caffeine\",\"latte\",\"espresso\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☕\" src=\"2615.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},baby_bottle:{keywords:[\"food\",\"container\",\"milk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍼\" src=\"1f37c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},salt:{keywords:[\"condiment\",\"shaker\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧂\" src=\"1f9c2.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},spoon:{keywords:[\"cutlery\",\"kitchen\",\"tableware\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥄\" src=\"1f944.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fork_and_knife:{keywords:[\"cutlery\",\"kitchen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍴\" src=\"1f374.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},plate_with_cutlery:{keywords:[\"food\",\"eat\",\"meal\",\"lunch\",\"dinner\",\"restaurant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍽\" src=\"1f37d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bowl_with_spoon:{keywords:[\"food\",\"breakfast\",\"cereal\",\"oatmeal\",\"porridge\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥣\" src=\"1f963.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},takeout_box:{keywords:[\"food\",\"leftovers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥡\" src=\"1f961.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},chopsticks:{keywords:[\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥢\" src=\"1f962.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},soccer:{keywords:[\"sports\",\"football\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚽\" src=\"26bd.png\"/>',fitzpatrick_scale:false,category:\"activity\"},basketball:{keywords:[\"sports\",\"balls\",\"NBA\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏀\" src=\"1f3c0.png\"/>',fitzpatrick_scale:false,category:\"activity\"},football:{keywords:[\"sports\",\"balls\",\"NFL\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏈\" src=\"1f3c8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},baseball:{keywords:[\"sports\",\"balls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚾\" src=\"26be.png\"/>',fitzpatrick_scale:false,category:\"activity\"},softball:{keywords:[\"sports\",\"balls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥎\" src=\"1f94e.png\"/>',fitzpatrick_scale:false,category:\"activity\"},tennis:{keywords:[\"sports\",\"balls\",\"green\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎾\" src=\"1f3be.png\"/>',fitzpatrick_scale:false,category:\"activity\"},volleyball:{keywords:[\"sports\",\"balls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏐\" src=\"1f3d0.png\"/>',fitzpatrick_scale:false,category:\"activity\"},rugby_football:{keywords:[\"sports\",\"team\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏉\" src=\"1f3c9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},flying_disc:{keywords:[\"sports\",\"frisbee\",\"ultimate\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥏\" src=\"1f94f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"8ball\":{keywords:[\"pool\",\"hobby\",\"game\",\"luck\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎱\" src=\"1f3b1.png\"/>',fitzpatrick_scale:false,category:\"activity\"},golf:{keywords:[\"sports\",\"business\",\"flag\",\"hole\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛳\" src=\"26f3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},golfing_woman:{keywords:[\"sports\",\"business\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏌️‍♀️\" src=\"1f3cc-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},golfing_man:{keywords:[\"sports\",\"business\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏌\" src=\"1f3cc.png\"/>',fitzpatrick_scale:true,category:\"activity\"},ping_pong:{keywords:[\"sports\",\"pingpong\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏓\" src=\"1f3d3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},badminton:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏸\" src=\"1f3f8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},goal_net:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥅\" src=\"1f945.png\"/>',fitzpatrick_scale:false,category:\"activity\"},ice_hockey:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏒\" src=\"1f3d2.png\"/>',fitzpatrick_scale:false,category:\"activity\"},field_hockey:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏑\" src=\"1f3d1.png\"/>',fitzpatrick_scale:false,category:\"activity\"},lacrosse:{keywords:[\"sports\",\"ball\",\"stick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥍\" src=\"1f94d.png\"/>',fitzpatrick_scale:false,category:\"activity\"},cricket:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏏\" src=\"1f3cf.png\"/>',fitzpatrick_scale:false,category:\"activity\"},ski:{keywords:[\"sports\",\"winter\",\"cold\",\"snow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎿\" src=\"1f3bf.png\"/>',fitzpatrick_scale:false,category:\"activity\"},skier:{keywords:[\"sports\",\"winter\",\"snow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛷\" src=\"26f7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},snowboarder:{keywords:[\"sports\",\"winter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏂\" src=\"1f3c2.png\"/>',fitzpatrick_scale:true,category:\"activity\"},person_fencing:{keywords:[\"sports\",\"fencing\",\"sword\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤺\" src=\"1f93a.png\"/>',fitzpatrick_scale:false,category:\"activity\"},women_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤼‍♀️\" src=\"1f93c-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},men_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤼‍♂️\" src=\"1f93c-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},woman_cartwheeling:{keywords:[\"gymnastics\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤸‍♀️\" src=\"1f938-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_cartwheeling:{keywords:[\"gymnastics\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤸‍♂️\" src=\"1f938-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},woman_playing_handball:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤾‍♀️\" src=\"1f93e-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_playing_handball:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤾‍♂️\" src=\"1f93e-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},ice_skate:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛸\" src=\"26f8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},curling_stone:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥌\" src=\"1f94c.png\"/>',fitzpatrick_scale:false,category:\"activity\"},skateboard:{keywords:[\"board\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛹\" src=\"1f6f9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},sled:{keywords:[\"sleigh\",\"luge\",\"toboggan\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛷\" src=\"1f6f7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},bow_and_arrow:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏹\" src=\"1f3f9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},fishing_pole_and_fish:{keywords:[\"food\",\"hobby\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎣\" src=\"1f3a3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},boxing_glove:{keywords:[\"sports\",\"fighting\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥊\" src=\"1f94a.png\"/>',fitzpatrick_scale:false,category:\"activity\"},martial_arts_uniform:{keywords:[\"judo\",\"karate\",\"taekwondo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥋\" src=\"1f94b.png\"/>',fitzpatrick_scale:false,category:\"activity\"},rowing_woman:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚣‍♀️\" src=\"1f6a3-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},rowing_man:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚣\" src=\"1f6a3.png\"/>',fitzpatrick_scale:true,category:\"activity\"},climbing_woman:{keywords:[\"sports\",\"hobby\",\"woman\",\"female\",\"rock\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧗‍♀️\" src=\"1f9d7-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},climbing_man:{keywords:[\"sports\",\"hobby\",\"man\",\"male\",\"rock\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧗‍♂️\" src=\"1f9d7-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},swimming_woman:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏊‍♀️\" src=\"1f3ca-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},swimming_man:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏊\" src=\"1f3ca.png\"/>',fitzpatrick_scale:true,category:\"activity\"},woman_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤽‍♀️\" src=\"1f93d-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤽‍♂️\" src=\"1f93d-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},woman_in_lotus_position:{keywords:[\"woman\",\"female\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧘‍♀️\" src=\"1f9d8-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_in_lotus_position:{keywords:[\"man\",\"male\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧘‍♂️\" src=\"1f9d8-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},surfing_woman:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏄‍♀️\" src=\"1f3c4-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},surfing_man:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏄\" src=\"1f3c4.png\"/>',fitzpatrick_scale:true,category:\"activity\"},bath:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛀\" src=\"1f6c0.png\"/>',fitzpatrick_scale:true,category:\"activity\"},basketball_woman:{keywords:[\"sports\",\"human\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛹️‍♀️\" src=\"26f9-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},basketball_man:{keywords:[\"sports\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛹\" src=\"26f9.png\"/>',fitzpatrick_scale:true,category:\"activity\"},weight_lifting_woman:{keywords:[\"sports\",\"training\",\"exercise\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏋️‍♀️\" src=\"1f3cb-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},weight_lifting_man:{keywords:[\"sports\",\"training\",\"exercise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏋\" src=\"1f3cb.png\"/>',fitzpatrick_scale:true,category:\"activity\"},biking_woman:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚴‍♀️\" src=\"1f6b4-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},biking_man:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚴\" src=\"1f6b4.png\"/>',fitzpatrick_scale:true,category:\"activity\"},mountain_biking_woman:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚵‍♀️\" src=\"1f6b5-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},mountain_biking_man:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚵\" src=\"1f6b5.png\"/>',fitzpatrick_scale:true,category:\"activity\"},horse_racing:{keywords:[\"animal\",\"betting\",\"competition\",\"gambling\",\"luck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏇\" src=\"1f3c7.png\"/>',fitzpatrick_scale:true,category:\"activity\"},business_suit_levitating:{keywords:[\"suit\",\"business\",\"levitate\",\"hover\",\"jump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕴\" src=\"1f574.png\"/>',fitzpatrick_scale:true,category:\"activity\"},trophy:{keywords:[\"win\",\"award\",\"contest\",\"place\",\"ftw\",\"ceremony\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏆\" src=\"1f3c6.png\"/>',fitzpatrick_scale:false,category:\"activity\"},running_shirt_with_sash:{keywords:[\"play\",\"pageant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎽\" src=\"1f3bd.png\"/>',fitzpatrick_scale:false,category:\"activity\"},medal_sports:{keywords:[\"award\",\"winning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏅\" src=\"1f3c5.png\"/>',fitzpatrick_scale:false,category:\"activity\"},medal_military:{keywords:[\"award\",\"winning\",\"army\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎖\" src=\"1f396.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"1st_place_medal\":{keywords:[\"award\",\"winning\",\"first\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥇\" src=\"1f947.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"2nd_place_medal\":{keywords:[\"award\",\"second\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥈\" src=\"1f948.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"3rd_place_medal\":{keywords:[\"award\",\"third\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥉\" src=\"1f949.png\"/>',fitzpatrick_scale:false,category:\"activity\"},reminder_ribbon:{keywords:[\"sports\",\"cause\",\"support\",\"awareness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎗\" src=\"1f397.png\"/>',fitzpatrick_scale:false,category:\"activity\"},rosette:{keywords:[\"flower\",\"decoration\",\"military\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏵\" src=\"1f3f5.png\"/>',fitzpatrick_scale:false,category:\"activity\"},ticket:{keywords:[\"event\",\"concert\",\"pass\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎫\" src=\"1f3ab.png\"/>',fitzpatrick_scale:false,category:\"activity\"},tickets:{keywords:[\"sports\",\"concert\",\"entrance\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎟\" src=\"1f39f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},performing_arts:{keywords:[\"acting\",\"theater\",\"drama\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎭\" src=\"1f3ad.png\"/>',fitzpatrick_scale:false,category:\"activity\"},art:{keywords:[\"design\",\"paint\",\"draw\",\"colors\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎨\" src=\"1f3a8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},circus_tent:{keywords:[\"festival\",\"carnival\",\"party\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎪\" src=\"1f3aa.png\"/>',fitzpatrick_scale:false,category:\"activity\"},woman_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤹‍♀️\" src=\"1f939-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤹‍♂️\" src=\"1f939-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},microphone:{keywords:[\"sound\",\"music\",\"PA\",\"sing\",\"talkshow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎤\" src=\"1f3a4.png\"/>',fitzpatrick_scale:false,category:\"activity\"},headphones:{keywords:[\"music\",\"score\",\"gadgets\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎧\" src=\"1f3a7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},musical_score:{keywords:[\"treble\",\"clef\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎼\" src=\"1f3bc.png\"/>',fitzpatrick_scale:false,category:\"activity\"},musical_keyboard:{keywords:[\"piano\",\"instrument\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎹\" src=\"1f3b9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},drum:{keywords:[\"music\",\"instrument\",\"drumsticks\",\"snare\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥁\" src=\"1f941.png\"/>',fitzpatrick_scale:false,category:\"activity\"},saxophone:{keywords:[\"music\",\"instrument\",\"jazz\",\"blues\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎷\" src=\"1f3b7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},trumpet:{keywords:[\"music\",\"brass\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎺\" src=\"1f3ba.png\"/>',fitzpatrick_scale:false,category:\"activity\"},guitar:{keywords:[\"music\",\"instrument\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎸\" src=\"1f3b8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},violin:{keywords:[\"music\",\"instrument\",\"orchestra\",\"symphony\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎻\" src=\"1f3bb.png\"/>',fitzpatrick_scale:false,category:\"activity\"},clapper:{keywords:[\"movie\",\"film\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎬\" src=\"1f3ac.png\"/>',fitzpatrick_scale:false,category:\"activity\"},video_game:{keywords:[\"play\",\"console\",\"PS4\",\"controller\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎮\" src=\"1f3ae.png\"/>',fitzpatrick_scale:false,category:\"activity\"},space_invader:{keywords:[\"game\",\"arcade\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👾\" src=\"1f47e.png\"/>',fitzpatrick_scale:false,category:\"activity\"},dart:{keywords:[\"game\",\"play\",\"bar\",\"target\",\"bullseye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎯\" src=\"1f3af.png\"/>',fitzpatrick_scale:false,category:\"activity\"},game_die:{keywords:[\"dice\",\"random\",\"tabletop\",\"play\",\"luck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎲\" src=\"1f3b2.png\"/>',fitzpatrick_scale:false,category:\"activity\"},chess_pawn:{keywords:[\"expendable\"],char:\"♟\",fitzpatrick_scale:false,category:\"activity\"},slot_machine:{keywords:[\"bet\",\"gamble\",\"vegas\",\"fruit machine\",\"luck\",\"casino\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎰\" src=\"1f3b0.png\"/>',fitzpatrick_scale:false,category:\"activity\"},jigsaw:{keywords:[\"interlocking\",\"puzzle\",\"piece\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧩\" src=\"1f9e9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},bowling:{keywords:[\"sports\",\"fun\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎳\" src=\"1f3b3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},red_car:{keywords:[\"red\",\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚗\" src=\"1f697.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},taxi:{keywords:[\"uber\",\"vehicle\",\"cars\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚕\" src=\"1f695.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},blue_car:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚙\" src=\"1f699.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bus:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚌\" src=\"1f68c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},trolleybus:{keywords:[\"bart\",\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚎\" src=\"1f68e.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},racing_car:{keywords:[\"sports\",\"race\",\"fast\",\"formula\",\"f1\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏎\" src=\"1f3ce.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},police_car:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"law\",\"legal\",\"enforcement\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚓\" src=\"1f693.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ambulance:{keywords:[\"health\",\"911\",\"hospital\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚑\" src=\"1f691.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fire_engine:{keywords:[\"transportation\",\"cars\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚒\" src=\"1f692.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},minibus:{keywords:[\"vehicle\",\"car\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚐\" src=\"1f690.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},truck:{keywords:[\"cars\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚚\" src=\"1f69a.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},articulated_lorry:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"express\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚛\" src=\"1f69b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tractor:{keywords:[\"vehicle\",\"car\",\"farming\",\"agriculture\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚜\" src=\"1f69c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},kick_scooter:{keywords:[\"vehicle\",\"kick\",\"razor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛴\" src=\"1f6f4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motorcycle:{keywords:[\"race\",\"sports\",\"fast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏍\" src=\"1f3cd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bike:{keywords:[\"sports\",\"bicycle\",\"exercise\",\"hipster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚲\" src=\"1f6b2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motor_scooter:{keywords:[\"vehicle\",\"vespa\",\"sasha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛵\" src=\"1f6f5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rotating_light:{keywords:[\"police\",\"ambulance\",\"911\",\"emergency\",\"alert\",\"error\",\"pinged\",\"law\",\"legal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚨\" src=\"1f6a8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_police_car:{keywords:[\"vehicle\",\"law\",\"legal\",\"enforcement\",\"911\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚔\" src=\"1f694.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_bus:{keywords:[\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚍\" src=\"1f68d.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_automobile:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚘\" src=\"1f698.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_taxi:{keywords:[\"vehicle\",\"cars\",\"uber\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚖\" src=\"1f696.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},aerial_tramway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚡\" src=\"1f6a1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_cableway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚠\" src=\"1f6a0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},suspension_railway:{keywords:[\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚟\" src=\"1f69f.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},railway_car:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚃\" src=\"1f683.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},train:{keywords:[\"transportation\",\"vehicle\",\"carriage\",\"public\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚋\" src=\"1f68b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},monorail:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚝\" src=\"1f69d.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_side:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚄\" src=\"1f684.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_front:{keywords:[\"transportation\",\"vehicle\",\"speed\",\"fast\",\"public\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚅\" src=\"1f685.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},light_rail:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚈\" src=\"1f688.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_railway:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚞\" src=\"1f69e.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},steam_locomotive:{keywords:[\"transportation\",\"vehicle\",\"train\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚂\" src=\"1f682.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},train2:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚆\" src=\"1f686.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},metro:{keywords:[\"transportation\",\"blue-square\",\"mrt\",\"underground\",\"tube\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚇\" src=\"1f687.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tram:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚊\" src=\"1f68a.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},station:{keywords:[\"transportation\",\"vehicle\",\"public\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚉\" src=\"1f689.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},flying_saucer:{keywords:[\"transportation\",\"vehicle\",\"ufo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛸\" src=\"1f6f8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},helicopter:{keywords:[\"transportation\",\"vehicle\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚁\" src=\"1f681.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},small_airplane:{keywords:[\"flight\",\"transportation\",\"fly\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛩\" src=\"1f6e9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},airplane:{keywords:[\"vehicle\",\"transportation\",\"flight\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✈️\" src=\"2708.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},flight_departure:{keywords:[\"airport\",\"flight\",\"landing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛫\" src=\"1f6eb.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},flight_arrival:{keywords:[\"airport\",\"flight\",\"boarding\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛬\" src=\"1f6ec.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sailboat:{keywords:[\"ship\",\"summer\",\"transportation\",\"water\",\"sailing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛵\" src=\"26f5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motor_boat:{keywords:[\"ship\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛥\" src=\"1f6e5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},speedboat:{keywords:[\"ship\",\"transportation\",\"vehicle\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚤\" src=\"1f6a4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ferry:{keywords:[\"boat\",\"ship\",\"yacht\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛴\" src=\"26f4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},passenger_ship:{keywords:[\"yacht\",\"cruise\",\"ferry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛳\" src=\"1f6f3.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rocket:{keywords:[\"launch\",\"ship\",\"staffmode\",\"NASA\",\"outer space\",\"outer_space\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚀\" src=\"1f680.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},artificial_satellite:{keywords:[\"communication\",\"gps\",\"orbit\",\"spaceflight\",\"NASA\",\"ISS\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛰\" src=\"1f6f0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},seat:{keywords:[\"sit\",\"airplane\",\"transport\",\"bus\",\"flight\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💺\" src=\"1f4ba.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},canoe:{keywords:[\"boat\",\"paddle\",\"water\",\"ship\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛶\" src=\"1f6f6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},anchor:{keywords:[\"ship\",\"ferry\",\"sea\",\"boat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚓\" src=\"2693.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},construction:{keywords:[\"wip\",\"progress\",\"caution\",\"warning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚧\" src=\"1f6a7.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fuelpump:{keywords:[\"gas station\",\"petroleum\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛽\" src=\"26fd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},busstop:{keywords:[\"transportation\",\"wait\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚏\" src=\"1f68f.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},vertical_traffic_light:{keywords:[\"transportation\",\"driving\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚦\" src=\"1f6a6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},traffic_light:{keywords:[\"transportation\",\"signal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚥\" src=\"1f6a5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},checkered_flag:{keywords:[\"contest\",\"finishline\",\"race\",\"gokart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏁\" src=\"1f3c1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ship:{keywords:[\"transportation\",\"titanic\",\"deploy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚢\" src=\"1f6a2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ferris_wheel:{keywords:[\"photo\",\"carnival\",\"londoneye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎡\" src=\"1f3a1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},roller_coaster:{keywords:[\"carnival\",\"playground\",\"photo\",\"fun\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎢\" src=\"1f3a2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},carousel_horse:{keywords:[\"photo\",\"carnival\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎠\" src=\"1f3a0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},building_construction:{keywords:[\"wip\",\"working\",\"progress\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏗\" src=\"1f3d7.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},foggy:{keywords:[\"photo\",\"mountain\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌁\" src=\"1f301.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tokyo_tower:{keywords:[\"photo\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗼\" src=\"1f5fc.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},factory:{keywords:[\"building\",\"industry\",\"pollution\",\"smoke\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏭\" src=\"1f3ed.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fountain:{keywords:[\"photo\",\"summer\",\"water\",\"fresh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛲\" src=\"26f2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rice_scene:{keywords:[\"photo\",\"japan\",\"asia\",\"tsukimi\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎑\" src=\"1f391.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain:{keywords:[\"photo\",\"nature\",\"environment\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛰\" src=\"26f0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_snow:{keywords:[\"photo\",\"nature\",\"environment\",\"winter\",\"cold\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏔\" src=\"1f3d4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mount_fuji:{keywords:[\"photo\",\"mountain\",\"nature\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗻\" src=\"1f5fb.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},volcano:{keywords:[\"photo\",\"nature\",\"disaster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌋\" src=\"1f30b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},japan:{keywords:[\"nation\",\"country\",\"japanese\",\"asia\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗾\" src=\"1f5fe.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},camping:{keywords:[\"photo\",\"outdoors\",\"tent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏕\" src=\"1f3d5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tent:{keywords:[\"photo\",\"camping\",\"outdoors\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛺\" src=\"26fa.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},national_park:{keywords:[\"photo\",\"environment\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏞\" src=\"1f3de.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motorway:{keywords:[\"road\",\"cupertino\",\"interstate\",\"highway\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛣\" src=\"1f6e3.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},railway_track:{keywords:[\"train\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛤\" src=\"1f6e4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise:{keywords:[\"morning\",\"view\",\"vacation\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌅\" src=\"1f305.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise_over_mountains:{keywords:[\"view\",\"vacation\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌄\" src=\"1f304.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},desert:{keywords:[\"photo\",\"warm\",\"saharah\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏜\" src=\"1f3dc.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},beach_umbrella:{keywords:[\"weather\",\"summer\",\"sunny\",\"sand\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏖\" src=\"1f3d6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},desert_island:{keywords:[\"photo\",\"tropical\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏝\" src=\"1f3dd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunrise:{keywords:[\"photo\",\"good morning\",\"dawn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌇\" src=\"1f307.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunset:{keywords:[\"photo\",\"evening\",\"sky\",\"buildings\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌆\" src=\"1f306.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},cityscape:{keywords:[\"photo\",\"night life\",\"urban\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏙\" src=\"1f3d9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},night_with_stars:{keywords:[\"evening\",\"city\",\"downtown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌃\" src=\"1f303.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bridge_at_night:{keywords:[\"photo\",\"sanfrancisco\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌉\" src=\"1f309.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},milky_way:{keywords:[\"photo\",\"space\",\"stars\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌌\" src=\"1f30c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},stars:{keywords:[\"night\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌠\" src=\"1f320.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sparkler:{keywords:[\"stars\",\"night\",\"shine\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎇\" src=\"1f387.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fireworks:{keywords:[\"photo\",\"festival\",\"carnival\",\"congratulations\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎆\" src=\"1f386.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rainbow:{keywords:[\"nature\",\"happy\",\"unicorn_face\",\"photo\",\"sky\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌈\" src=\"1f308.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},houses:{keywords:[\"buildings\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏘\" src=\"1f3d8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},european_castle:{keywords:[\"building\",\"royalty\",\"history\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏰\" src=\"1f3f0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},japanese_castle:{keywords:[\"photo\",\"building\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏯\" src=\"1f3ef.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},stadium:{keywords:[\"photo\",\"place\",\"sports\",\"concert\",\"venue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏟\" src=\"1f3df.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},statue_of_liberty:{keywords:[\"american\",\"newyork\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗽\" src=\"1f5fd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},house:{keywords:[\"building\",\"home\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏠\" src=\"1f3e0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},house_with_garden:{keywords:[\"home\",\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏡\" src=\"1f3e1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},derelict_house:{keywords:[\"abandon\",\"evict\",\"broken\",\"building\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏚\" src=\"1f3da.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},office:{keywords:[\"building\",\"bureau\",\"work\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏢\" src=\"1f3e2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},department_store:{keywords:[\"building\",\"shopping\",\"mall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏬\" src=\"1f3ec.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},post_office:{keywords:[\"building\",\"envelope\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏣\" src=\"1f3e3.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},european_post_office:{keywords:[\"building\",\"email\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏤\" src=\"1f3e4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},hospital:{keywords:[\"building\",\"health\",\"surgery\",\"doctor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏥\" src=\"1f3e5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bank:{keywords:[\"building\",\"money\",\"sales\",\"cash\",\"business\",\"enterprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏦\" src=\"1f3e6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},hotel:{keywords:[\"building\",\"accomodation\",\"checkin\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏨\" src=\"1f3e8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},convenience_store:{keywords:[\"building\",\"shopping\",\"groceries\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏪\" src=\"1f3ea.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},school:{keywords:[\"building\",\"student\",\"education\",\"learn\",\"teach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏫\" src=\"1f3eb.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},love_hotel:{keywords:[\"like\",\"affection\",\"dating\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏩\" src=\"1f3e9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},wedding:{keywords:[\"love\",\"like\",\"affection\",\"couple\",\"marriage\",\"bride\",\"groom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💒\" src=\"1f492.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},classical_building:{keywords:[\"art\",\"culture\",\"history\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏛\" src=\"1f3db.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},church:{keywords:[\"building\",\"religion\",\"christ\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛪\" src=\"26ea.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mosque:{keywords:[\"islam\",\"worship\",\"minaret\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕌\" src=\"1f54c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},synagogue:{keywords:[\"judaism\",\"worship\",\"temple\",\"jewish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕍\" src=\"1f54d.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},kaaba:{keywords:[\"mecca\",\"mosque\",\"islam\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕋\" src=\"1f54b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},shinto_shrine:{keywords:[\"temple\",\"japan\",\"kyoto\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛩\" src=\"26e9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},watch:{keywords:[\"time\",\"accessories\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⌚\" src=\"231a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},iphone:{keywords:[\"technology\",\"apple\",\"gadgets\",\"dial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📱\" src=\"1f4f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},calling:{keywords:[\"iphone\",\"incoming\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📲\" src=\"1f4f2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},computer:{keywords:[\"technology\",\"laptop\",\"screen\",\"display\",\"monitor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💻\" src=\"1f4bb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},keyboard:{keywords:[\"technology\",\"computer\",\"type\",\"input\",\"text\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⌨\" src=\"2328.png\"/>',fitzpatrick_scale:false,category:\"objects\"},desktop_computer:{keywords:[\"technology\",\"computing\",\"screen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖥\" src=\"1f5a5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},printer:{keywords:[\"paper\",\"ink\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖨\" src=\"1f5a8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},computer_mouse:{keywords:[\"click\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖱\" src=\"1f5b1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},trackball:{keywords:[\"technology\",\"trackpad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖲\" src=\"1f5b2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},joystick:{keywords:[\"game\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕹\" src=\"1f579.png\"/>',fitzpatrick_scale:false,category:\"objects\"},clamp:{keywords:[\"tool\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗜\" src=\"1f5dc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},minidisc:{keywords:[\"technology\",\"record\",\"data\",\"disk\",\"90s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💽\" src=\"1f4bd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},floppy_disk:{keywords:[\"oldschool\",\"technology\",\"save\",\"90s\",\"80s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💾\" src=\"1f4be.png\"/>',fitzpatrick_scale:false,category:\"objects\"},cd:{keywords:[\"technology\",\"dvd\",\"disk\",\"disc\",\"90s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💿\" src=\"1f4bf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dvd:{keywords:[\"cd\",\"disk\",\"disc\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📀\" src=\"1f4c0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},vhs:{keywords:[\"record\",\"video\",\"oldschool\",\"90s\",\"80s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📼\" src=\"1f4fc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},camera:{keywords:[\"gadgets\",\"photography\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📷\" src=\"1f4f7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},camera_flash:{keywords:[\"photography\",\"gadgets\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📸\" src=\"1f4f8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},video_camera:{keywords:[\"film\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📹\" src=\"1f4f9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},movie_camera:{keywords:[\"film\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎥\" src=\"1f3a5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},film_projector:{keywords:[\"video\",\"tape\",\"record\",\"movie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📽\" src=\"1f4fd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},film_strip:{keywords:[\"movie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎞\" src=\"1f39e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},telephone_receiver:{keywords:[\"technology\",\"communication\",\"dial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📞\" src=\"1f4de.png\"/>',fitzpatrick_scale:false,category:\"objects\"},phone:{keywords:[\"technology\",\"communication\",\"dial\",\"telephone\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☎️\" src=\"260e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pager:{keywords:[\"bbcall\",\"oldschool\",\"90s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📟\" src=\"1f4df.png\"/>',fitzpatrick_scale:false,category:\"objects\"},fax:{keywords:[\"communication\",\"technology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📠\" src=\"1f4e0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},tv:{keywords:[\"technology\",\"program\",\"oldschool\",\"show\",\"television\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📺\" src=\"1f4fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},radio:{keywords:[\"communication\",\"music\",\"podcast\",\"program\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📻\" src=\"1f4fb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},studio_microphone:{keywords:[\"sing\",\"recording\",\"artist\",\"talkshow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎙\" src=\"1f399.png\"/>',fitzpatrick_scale:false,category:\"objects\"},level_slider:{keywords:[\"scale\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎚\" src=\"1f39a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},control_knobs:{keywords:[\"dial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎛\" src=\"1f39b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},compass:{keywords:[\"magnetic\",\"navigation\",\"orienteering\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧭\" src=\"1f9ed.png\"/>',fitzpatrick_scale:false,category:\"objects\"},stopwatch:{keywords:[\"time\",\"deadline\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏱\" src=\"23f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},timer_clock:{keywords:[\"alarm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏲\" src=\"23f2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},alarm_clock:{keywords:[\"time\",\"wake\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏰\" src=\"23f0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mantelpiece_clock:{keywords:[\"time\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕰\" src=\"1f570.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hourglass_flowing_sand:{keywords:[\"oldschool\",\"time\",\"countdown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏳\" src=\"23f3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hourglass:{keywords:[\"time\",\"clock\",\"oldschool\",\"limit\",\"exam\",\"quiz\",\"test\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⌛\" src=\"231b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},satellite:{keywords:[\"communication\",\"future\",\"radio\",\"space\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📡\" src=\"1f4e1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},battery:{keywords:[\"power\",\"energy\",\"sustain\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔋\" src=\"1f50b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},electric_plug:{keywords:[\"charger\",\"power\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔌\" src=\"1f50c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bulb:{keywords:[\"light\",\"electricity\",\"idea\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💡\" src=\"1f4a1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},flashlight:{keywords:[\"dark\",\"camping\",\"sight\",\"night\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔦\" src=\"1f526.png\"/>',fitzpatrick_scale:false,category:\"objects\"},candle:{keywords:[\"fire\",\"wax\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕯\" src=\"1f56f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},fire_extinguisher:{keywords:[\"quench\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧯\" src=\"1f9ef.png\"/>',fitzpatrick_scale:false,category:\"objects\"},wastebasket:{keywords:[\"bin\",\"trash\",\"rubbish\",\"garbage\",\"toss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗑\" src=\"1f5d1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},oil_drum:{keywords:[\"barrell\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛢\" src=\"1f6e2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},money_with_wings:{keywords:[\"dollar\",\"bills\",\"payment\",\"sale\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💸\" src=\"1f4b8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dollar:{keywords:[\"money\",\"sales\",\"bill\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💵\" src=\"1f4b5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},yen:{keywords:[\"money\",\"sales\",\"japanese\",\"dollar\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💴\" src=\"1f4b4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},euro:{keywords:[\"money\",\"sales\",\"dollar\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💶\" src=\"1f4b6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pound:{keywords:[\"british\",\"sterling\",\"money\",\"sales\",\"bills\",\"uk\",\"england\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💷\" src=\"1f4b7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},moneybag:{keywords:[\"dollar\",\"payment\",\"coins\",\"sale\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💰\" src=\"1f4b0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},credit_card:{keywords:[\"money\",\"sales\",\"dollar\",\"bill\",\"payment\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💳\" src=\"1f4b3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gem:{keywords:[\"blue\",\"ruby\",\"diamond\",\"jewelry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💎\" src=\"1f48e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},balance_scale:{keywords:[\"law\",\"fairness\",\"weight\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚖\" src=\"2696.png\"/>',fitzpatrick_scale:false,category:\"objects\"},toolbox:{keywords:[\"tools\",\"diy\",\"fix\",\"maintainer\",\"mechanic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧰\" src=\"1f9f0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},wrench:{keywords:[\"tools\",\"diy\",\"ikea\",\"fix\",\"maintainer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔧\" src=\"1f527.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hammer:{keywords:[\"tools\",\"build\",\"create\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔨\" src=\"1f528.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hammer_and_pick:{keywords:[\"tools\",\"build\",\"create\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚒\" src=\"2692.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hammer_and_wrench:{keywords:[\"tools\",\"build\",\"create\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛠\" src=\"1f6e0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pick:{keywords:[\"tools\",\"dig\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛏\" src=\"26cf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},nut_and_bolt:{keywords:[\"handy\",\"tools\",\"fix\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔩\" src=\"1f529.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gear:{keywords:[\"cog\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚙\" src=\"2699.png\"/>',fitzpatrick_scale:false,category:\"objects\"},brick:{keywords:[\"bricks\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧱\" src=\"1f9f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},chains:{keywords:[\"lock\",\"arrest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛓\" src=\"26d3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},magnet:{keywords:[\"attraction\",\"magnetic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧲\" src=\"1f9f2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gun:{keywords:[\"violence\",\"weapon\",\"pistol\",\"revolver\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔫\" src=\"1f52b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bomb:{keywords:[\"boom\",\"explode\",\"explosion\",\"terrorism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💣\" src=\"1f4a3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},firecracker:{keywords:[\"dynamite\",\"boom\",\"explode\",\"explosion\",\"explosive\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧨\" src=\"1f9e8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hocho:{keywords:[\"knife\",\"blade\",\"cutlery\",\"kitchen\",\"weapon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔪\" src=\"1f52a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dagger:{keywords:[\"weapon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗡\" src=\"1f5e1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crossed_swords:{keywords:[\"weapon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚔\" src=\"2694.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shield:{keywords:[\"protection\",\"security\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛡\" src=\"1f6e1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},smoking:{keywords:[\"kills\",\"tobacco\",\"cigarette\",\"joint\",\"smoke\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚬\" src=\"1f6ac.png\"/>',fitzpatrick_scale:false,category:\"objects\"},skull_and_crossbones:{keywords:[\"poison\",\"danger\",\"deadly\",\"scary\",\"death\",\"pirate\",\"evil\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☠\" src=\"2620.png\"/>',fitzpatrick_scale:false,category:\"objects\"},coffin:{keywords:[\"vampire\",\"dead\",\"die\",\"death\",\"rip\",\"graveyard\",\"cemetery\",\"casket\",\"funeral\",\"box\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚰\" src=\"26b0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},funeral_urn:{keywords:[\"dead\",\"die\",\"death\",\"rip\",\"ashes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚱\" src=\"26b1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},amphora:{keywords:[\"vase\",\"jar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏺\" src=\"1f3fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crystal_ball:{keywords:[\"disco\",\"party\",\"magic\",\"circus\",\"fortune_teller\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔮\" src=\"1f52e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},prayer_beads:{keywords:[\"dhikr\",\"religious\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📿\" src=\"1f4ff.png\"/>',fitzpatrick_scale:false,category:\"objects\"},nazar_amulet:{keywords:[\"bead\",\"charm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧿\" src=\"1f9ff.png\"/>',fitzpatrick_scale:false,category:\"objects\"},barber:{keywords:[\"hair\",\"salon\",\"style\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💈\" src=\"1f488.png\"/>',fitzpatrick_scale:false,category:\"objects\"},alembic:{keywords:[\"distilling\",\"science\",\"experiment\",\"chemistry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚗\" src=\"2697.png\"/>',fitzpatrick_scale:false,category:\"objects\"},telescope:{keywords:[\"stars\",\"space\",\"zoom\",\"science\",\"astronomy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔭\" src=\"1f52d.png\"/>',fitzpatrick_scale:false,category:\"objects\"},microscope:{keywords:[\"laboratory\",\"experiment\",\"zoomin\",\"science\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔬\" src=\"1f52c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hole:{keywords:[\"embarrassing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕳\" src=\"1f573.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pill:{keywords:[\"health\",\"medicine\",\"doctor\",\"pharmacy\",\"drug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💊\" src=\"1f48a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},syringe:{keywords:[\"health\",\"hospital\",\"drugs\",\"blood\",\"medicine\",\"needle\",\"doctor\",\"nurse\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💉\" src=\"1f489.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dna:{keywords:[\"biologist\",\"genetics\",\"life\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧬\" src=\"1f9ec.png\"/>',fitzpatrick_scale:false,category:\"objects\"},microbe:{keywords:[\"amoeba\",\"bacteria\",\"germs\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦠\" src=\"1f9a0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},petri_dish:{keywords:[\"bacteria\",\"biology\",\"culture\",\"lab\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧫\" src=\"1f9eb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},test_tube:{keywords:[\"chemistry\",\"experiment\",\"lab\",\"science\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧪\" src=\"1f9ea.png\"/>',fitzpatrick_scale:false,category:\"objects\"},thermometer:{keywords:[\"weather\",\"temperature\",\"hot\",\"cold\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌡\" src=\"1f321.png\"/>',fitzpatrick_scale:false,category:\"objects\"},broom:{keywords:[\"cleaning\",\"sweeping\",\"witch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧹\" src=\"1f9f9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},basket:{keywords:[\"laundry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧺\" src=\"1f9fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},toilet_paper:{keywords:[\"roll\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧻\" src=\"1f9fb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},label:{keywords:[\"sale\",\"tag\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏷\" src=\"1f3f7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bookmark:{keywords:[\"favorite\",\"label\",\"save\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔖\" src=\"1f516.png\"/>',fitzpatrick_scale:false,category:\"objects\"},toilet:{keywords:[\"restroom\",\"wc\",\"washroom\",\"bathroom\",\"potty\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚽\" src=\"1f6bd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shower:{keywords:[\"clean\",\"water\",\"bathroom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚿\" src=\"1f6bf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bathtub:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛁\" src=\"1f6c1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},soap:{keywords:[\"bar\",\"bathing\",\"cleaning\",\"lather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧼\" src=\"1f9fc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},sponge:{keywords:[\"absorbing\",\"cleaning\",\"porous\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧽\" src=\"1f9fd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},lotion_bottle:{keywords:[\"moisturizer\",\"sunscreen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧴\" src=\"1f9f4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},key:{keywords:[\"lock\",\"door\",\"password\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔑\" src=\"1f511.png\"/>',fitzpatrick_scale:false,category:\"objects\"},old_key:{keywords:[\"lock\",\"door\",\"password\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗝\" src=\"1f5dd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},couch_and_lamp:{keywords:[\"read\",\"chill\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛋\" src=\"1f6cb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},sleeping_bed:{keywords:[\"bed\",\"rest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛌\" src=\"1f6cc.png\"/>',fitzpatrick_scale:true,category:\"objects\"},bed:{keywords:[\"sleep\",\"rest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛏\" src=\"1f6cf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},door:{keywords:[\"house\",\"entry\",\"exit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚪\" src=\"1f6aa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bellhop_bell:{keywords:[\"service\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛎\" src=\"1f6ce.png\"/>',fitzpatrick_scale:false,category:\"objects\"},teddy_bear:{keywords:[\"plush\",\"stuffed\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧸\" src=\"1f9f8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},framed_picture:{keywords:[\"photography\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖼\" src=\"1f5bc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},world_map:{keywords:[\"location\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗺\" src=\"1f5fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},parasol_on_ground:{keywords:[\"weather\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛱\" src=\"26f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},moyai:{keywords:[\"rock\",\"easter island\",\"moai\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗿\" src=\"1f5ff.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shopping:{keywords:[\"mall\",\"buy\",\"purchase\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛍\" src=\"1f6cd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shopping_cart:{keywords:[\"trolley\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛒\" src=\"1f6d2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},balloon:{keywords:[\"party\",\"celebration\",\"birthday\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎈\" src=\"1f388.png\"/>',fitzpatrick_scale:false,category:\"objects\"},flags:{keywords:[\"fish\",\"japanese\",\"koinobori\",\"carp\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎏\" src=\"1f38f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},ribbon:{keywords:[\"decoration\",\"pink\",\"girl\",\"bowtie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎀\" src=\"1f380.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gift:{keywords:[\"present\",\"birthday\",\"christmas\",\"xmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎁\" src=\"1f381.png\"/>',fitzpatrick_scale:false,category:\"objects\"},confetti_ball:{keywords:[\"festival\",\"party\",\"birthday\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎊\" src=\"1f38a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},tada:{keywords:[\"party\",\"congratulations\",\"birthday\",\"magic\",\"circus\",\"celebration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎉\" src=\"1f389.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dolls:{keywords:[\"japanese\",\"toy\",\"kimono\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎎\" src=\"1f38e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},wind_chime:{keywords:[\"nature\",\"ding\",\"spring\",\"bell\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎐\" src=\"1f390.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crossed_flags:{keywords:[\"japanese\",\"nation\",\"country\",\"border\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎌\" src=\"1f38c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},izakaya_lantern:{keywords:[\"light\",\"paper\",\"halloween\",\"spooky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏮\" src=\"1f3ee.png\"/>',fitzpatrick_scale:false,category:\"objects\"},red_envelope:{keywords:[\"gift\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧧\" src=\"1f9e7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},email:{keywords:[\"letter\",\"postal\",\"inbox\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✉️\" src=\"2709.png\"/>',fitzpatrick_scale:false,category:\"objects\"},envelope_with_arrow:{keywords:[\"email\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📩\" src=\"1f4e9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},incoming_envelope:{keywords:[\"email\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📨\" src=\"1f4e8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},\"e-mail\":{keywords:[\"communication\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📧\" src=\"1f4e7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},love_letter:{keywords:[\"email\",\"like\",\"affection\",\"envelope\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💌\" src=\"1f48c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},postbox:{keywords:[\"email\",\"letter\",\"envelope\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📮\" src=\"1f4ee.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox_closed:{keywords:[\"email\",\"communication\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📪\" src=\"1f4ea.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox:{keywords:[\"email\",\"inbox\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📫\" src=\"1f4eb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox_with_mail:{keywords:[\"email\",\"inbox\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📬\" src=\"1f4ec.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox_with_no_mail:{keywords:[\"email\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📭\" src=\"1f4ed.png\"/>',fitzpatrick_scale:false,category:\"objects\"},package:{keywords:[\"mail\",\"gift\",\"cardboard\",\"box\",\"moving\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📦\" src=\"1f4e6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},postal_horn:{keywords:[\"instrument\",\"music\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📯\" src=\"1f4ef.png\"/>',fitzpatrick_scale:false,category:\"objects\"},inbox_tray:{keywords:[\"email\",\"documents\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📥\" src=\"1f4e5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},outbox_tray:{keywords:[\"inbox\",\"email\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📤\" src=\"1f4e4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},scroll:{keywords:[\"documents\",\"ancient\",\"history\",\"paper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📜\" src=\"1f4dc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},page_with_curl:{keywords:[\"documents\",\"office\",\"paper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📃\" src=\"1f4c3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bookmark_tabs:{keywords:[\"favorite\",\"save\",\"order\",\"tidy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📑\" src=\"1f4d1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},receipt:{keywords:[\"accounting\",\"expenses\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧾\" src=\"1f9fe.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bar_chart:{keywords:[\"graph\",\"presentation\",\"stats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📊\" src=\"1f4ca.png\"/>',fitzpatrick_scale:false,category:\"objects\"},chart_with_upwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recovery\",\"business\",\"economics\",\"money\",\"sales\",\"good\",\"success\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📈\" src=\"1f4c8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},chart_with_downwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recession\",\"business\",\"economics\",\"money\",\"sales\",\"bad\",\"failure\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📉\" src=\"1f4c9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},page_facing_up:{keywords:[\"documents\",\"office\",\"paper\",\"information\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📄\" src=\"1f4c4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},date:{keywords:[\"calendar\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📅\" src=\"1f4c5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},calendar:{keywords:[\"schedule\",\"date\",\"planning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📆\" src=\"1f4c6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},spiral_calendar:{keywords:[\"date\",\"schedule\",\"planning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗓\" src=\"1f5d3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},card_index:{keywords:[\"business\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📇\" src=\"1f4c7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},card_file_box:{keywords:[\"business\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗃\" src=\"1f5c3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},ballot_box:{keywords:[\"election\",\"vote\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗳\" src=\"1f5f3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},file_cabinet:{keywords:[\"filing\",\"organizing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗄\" src=\"1f5c4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},clipboard:{keywords:[\"stationery\",\"documents\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📋\" src=\"1f4cb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},spiral_notepad:{keywords:[\"memo\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗒\" src=\"1f5d2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},file_folder:{keywords:[\"documents\",\"business\",\"office\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📁\" src=\"1f4c1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},open_file_folder:{keywords:[\"documents\",\"load\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📂\" src=\"1f4c2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},card_index_dividers:{keywords:[\"organizing\",\"business\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗂\" src=\"1f5c2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},newspaper_roll:{keywords:[\"press\",\"headline\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗞\" src=\"1f5de.png\"/>',fitzpatrick_scale:false,category:\"objects\"},newspaper:{keywords:[\"press\",\"headline\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📰\" src=\"1f4f0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},notebook:{keywords:[\"stationery\",\"record\",\"notes\",\"paper\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📓\" src=\"1f4d3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},closed_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"learn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📕\" src=\"1f4d5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},green_book:{keywords:[\"read\",\"library\",\"knowledge\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📗\" src=\"1f4d7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},blue_book:{keywords:[\"read\",\"library\",\"knowledge\",\"learn\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📘\" src=\"1f4d8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},orange_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📙\" src=\"1f4d9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},notebook_with_decorative_cover:{keywords:[\"classroom\",\"notes\",\"record\",\"paper\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📔\" src=\"1f4d4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},ledger:{keywords:[\"notes\",\"paper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📒\" src=\"1f4d2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},books:{keywords:[\"literature\",\"library\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📚\" src=\"1f4da.png\"/>',fitzpatrick_scale:false,category:\"objects\"},open_book:{keywords:[\"book\",\"read\",\"library\",\"knowledge\",\"literature\",\"learn\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📖\" src=\"1f4d6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},safety_pin:{keywords:[\"diaper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧷\" src=\"1f9f7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},link:{keywords:[\"rings\",\"url\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔗\" src=\"1f517.png\"/>',fitzpatrick_scale:false,category:\"objects\"},paperclip:{keywords:[\"documents\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📎\" src=\"1f4ce.png\"/>',fitzpatrick_scale:false,category:\"objects\"},paperclips:{keywords:[\"documents\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖇\" src=\"1f587.png\"/>',fitzpatrick_scale:false,category:\"objects\"},scissors:{keywords:[\"stationery\",\"cut\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✂️\" src=\"2702.png\"/>',fitzpatrick_scale:false,category:\"objects\"},triangular_ruler:{keywords:[\"stationery\",\"math\",\"architect\",\"sketch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📐\" src=\"1f4d0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},straight_ruler:{keywords:[\"stationery\",\"calculate\",\"length\",\"math\",\"school\",\"drawing\",\"architect\",\"sketch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📏\" src=\"1f4cf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},abacus:{keywords:[\"calculation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧮\" src=\"1f9ee.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pushpin:{keywords:[\"stationery\",\"mark\",\"here\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📌\" src=\"1f4cc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},round_pushpin:{keywords:[\"stationery\",\"location\",\"map\",\"here\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📍\" src=\"1f4cd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},triangular_flag_on_post:{keywords:[\"mark\",\"milestone\",\"place\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚩\" src=\"1f6a9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},white_flag:{keywords:[\"losing\",\"loser\",\"lost\",\"surrender\",\"give up\",\"fail\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏳\" src=\"1f3f3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},black_flag:{keywords:[\"pirate\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴\" src=\"1f3f4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},rainbow_flag:{keywords:[\"flag\",\"rainbow\",\"pride\",\"gay\",\"lgbt\",\"glbt\",\"queer\",\"homosexual\",\"lesbian\",\"bisexual\",\"transgender\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏳️‍🌈\" src=\"1f3f3-fe0f-200d-1f308.png\"/>',fitzpatrick_scale:false,category:\"objects\"},closed_lock_with_key:{keywords:[\"security\",\"privacy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔐\" src=\"1f510.png\"/>',fitzpatrick_scale:false,category:\"objects\"},lock:{keywords:[\"security\",\"password\",\"padlock\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔒\" src=\"1f512.png\"/>',fitzpatrick_scale:false,category:\"objects\"},unlock:{keywords:[\"privacy\",\"security\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔓\" src=\"1f513.png\"/>',fitzpatrick_scale:false,category:\"objects\"},lock_with_ink_pen:{keywords:[\"security\",\"secret\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔏\" src=\"1f50f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖊\" src=\"1f58a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},fountain_pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖋\" src=\"1f58b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},black_nib:{keywords:[\"pen\",\"stationery\",\"writing\",\"write\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✒️\" src=\"2712.png\"/>',fitzpatrick_scale:false,category:\"objects\"},memo:{keywords:[\"write\",\"documents\",\"stationery\",\"pencil\",\"paper\",\"writing\",\"legal\",\"exam\",\"quiz\",\"test\",\"study\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📝\" src=\"1f4dd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pencil2:{keywords:[\"stationery\",\"write\",\"paper\",\"writing\",\"school\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✏️\" src=\"270f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crayon:{keywords:[\"drawing\",\"creativity\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖍\" src=\"1f58d.png\"/>',fitzpatrick_scale:false,category:\"objects\"},paintbrush:{keywords:[\"drawing\",\"creativity\",\"art\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖌\" src=\"1f58c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mag:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔍\" src=\"1f50d.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mag_right:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔎\" src=\"1f50e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},heart:{keywords:[\"love\",\"like\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❤️\" src=\"2764.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},orange_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧡\" src=\"1f9e1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},yellow_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💛\" src=\"1f49b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},green_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💚\" src=\"1f49a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},blue_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💙\" src=\"1f499.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},purple_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💜\" src=\"1f49c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_heart:{keywords:[\"evil\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖤\" src=\"1f5a4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},broken_heart:{keywords:[\"sad\",\"sorry\",\"break\",\"heart\",\"heartbreak\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💔\" src=\"1f494.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_heart_exclamation:{keywords:[\"decoration\",\"love\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❣\" src=\"2763.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},two_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💕\" src=\"1f495.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},revolving_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💞\" src=\"1f49e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heartbeat:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"pink\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💓\" src=\"1f493.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heartpulse:{keywords:[\"like\",\"love\",\"affection\",\"valentines\",\"pink\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💗\" src=\"1f497.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sparkling_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💖\" src=\"1f496.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cupid:{keywords:[\"love\",\"like\",\"heart\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💘\" src=\"1f498.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},gift_heart:{keywords:[\"love\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💝\" src=\"1f49d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heart_decoration:{keywords:[\"purple-square\",\"love\",\"like\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💟\" src=\"1f49f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},peace_symbol:{keywords:[\"hippie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☮\" src=\"262e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},latin_cross:{keywords:[\"christianity\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✝\" src=\"271d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},star_and_crescent:{keywords:[\"islam\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☪\" src=\"262a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},om:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕉\" src=\"1f549.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wheel_of_dharma:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☸\" src=\"2638.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},star_of_david:{keywords:[\"judaism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✡\" src=\"2721.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},six_pointed_star:{keywords:[\"purple-square\",\"religion\",\"jewish\",\"hexagram\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔯\" src=\"1f52f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},menorah:{keywords:[\"hanukkah\",\"candles\",\"jewish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕎\" src=\"1f54e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},yin_yang:{keywords:[\"balance\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☯\" src=\"262f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},orthodox_cross:{keywords:[\"suppedaneum\",\"religion\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☦\" src=\"2626.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},place_of_worship:{keywords:[\"religion\",\"church\",\"temple\",\"prayer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛐\" src=\"1f6d0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ophiuchus:{keywords:[\"sign\",\"purple-square\",\"constellation\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛎\" src=\"26ce.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},aries:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♈\" src=\"2648.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},taurus:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♉\" src=\"2649.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},gemini:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♊\" src=\"264a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cancer:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♋\" src=\"264b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},leo:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♌\" src=\"264c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},virgo:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♍\" src=\"264d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},libra:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♎\" src=\"264e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},scorpius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\",\"scorpio\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♏\" src=\"264f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sagittarius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♐\" src=\"2650.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},capricorn:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♑\" src=\"2651.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},aquarius:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♒\" src=\"2652.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},pisces:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♓\" src=\"2653.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},id:{keywords:[\"purple-square\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆔\" src=\"1f194.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},atom_symbol:{keywords:[\"science\",\"physics\",\"chemistry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚛\" src=\"269b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7a7a:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"empty\",\"sky\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈳\" src=\"1f233.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u5272:{keywords:[\"cut\",\"divide\",\"chinese\",\"kanji\",\"pink-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈹\" src=\"1f239.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},radioactive:{keywords:[\"nuclear\",\"danger\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☢\" src=\"2622.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},biohazard:{keywords:[\"danger\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☣\" src=\"2623.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mobile_phone_off:{keywords:[\"mute\",\"orange-square\",\"silence\",\"quiet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📴\" src=\"1f4f4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},vibration_mode:{keywords:[\"orange-square\",\"phone\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📳\" src=\"1f4f3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6709:{keywords:[\"orange-square\",\"chinese\",\"have\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈶\" src=\"1f236.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7121:{keywords:[\"nothing\",\"chinese\",\"kanji\",\"japanese\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈚\" src=\"1f21a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7533:{keywords:[\"chinese\",\"japanese\",\"kanji\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈸\" src=\"1f238.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u55b6:{keywords:[\"japanese\",\"opening hours\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈺\" src=\"1f23a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6708:{keywords:[\"chinese\",\"month\",\"moon\",\"japanese\",\"orange-square\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈷️\" src=\"1f237.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eight_pointed_black_star:{keywords:[\"orange-square\",\"shape\",\"polygon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✴️\" src=\"2734.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},vs:{keywords:[\"words\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆚\" src=\"1f19a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},accept:{keywords:[\"ok\",\"good\",\"chinese\",\"kanji\",\"agree\",\"yes\",\"orange-circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🉑\" src=\"1f251.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_flower:{keywords:[\"japanese\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💮\" src=\"1f4ae.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ideograph_advantage:{keywords:[\"chinese\",\"kanji\",\"obtain\",\"get\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🉐\" src=\"1f250.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},secret:{keywords:[\"privacy\",\"chinese\",\"sshh\",\"kanji\",\"red-circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"㊙️\" src=\"3299.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},congratulations:{keywords:[\"chinese\",\"kanji\",\"japanese\",\"red-circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"㊗️\" src=\"3297.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u5408:{keywords:[\"japanese\",\"chinese\",\"join\",\"kanji\",\"red-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈴\" src=\"1f234.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6e80:{keywords:[\"full\",\"chinese\",\"japanese\",\"red-square\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈵\" src=\"1f235.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7981:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"forbidden\",\"limit\",\"restricted\",\"red-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈲\" src=\"1f232.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},a:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅰️\" src=\"1f170.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},b:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅱️\" src=\"1f171.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ab:{keywords:[\"red-square\",\"alphabet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆎\" src=\"1f18e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cl:{keywords:[\"alphabet\",\"words\",\"red-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆑\" src=\"1f191.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},o2:{keywords:[\"alphabet\",\"red-square\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅾️\" src=\"1f17e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sos:{keywords:[\"help\",\"red-square\",\"words\",\"emergency\",\"911\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆘\" src=\"1f198.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_entry:{keywords:[\"limit\",\"security\",\"privacy\",\"bad\",\"denied\",\"stop\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛔\" src=\"26d4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},name_badge:{keywords:[\"fire\",\"forbid\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📛\" src=\"1f4db.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_entry_sign:{keywords:[\"forbid\",\"stop\",\"limit\",\"denied\",\"disallow\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚫\" src=\"1f6ab.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},x:{keywords:[\"no\",\"delete\",\"remove\",\"cancel\",\"red\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❌\" src=\"274c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},o:{keywords:[\"circle\",\"round\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⭕\" src=\"2b55.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},stop_sign:{keywords:[\"stop\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛑\" src=\"1f6d1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},anger:{keywords:[\"angry\",\"mad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💢\" src=\"1f4a2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},hotsprings:{keywords:[\"bath\",\"warm\",\"relax\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♨️\" src=\"2668.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_pedestrians:{keywords:[\"rules\",\"crossing\",\"walking\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚷\" src=\"1f6b7.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},do_not_litter:{keywords:[\"trash\",\"bin\",\"garbage\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚯\" src=\"1f6af.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_bicycles:{keywords:[\"cyclist\",\"prohibited\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚳\" src=\"1f6b3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},\"non-potable_water\":{keywords:[\"drink\",\"faucet\",\"tap\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚱\" src=\"1f6b1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},underage:{keywords:[\"18\",\"drink\",\"pub\",\"night\",\"minor\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔞\" src=\"1f51e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_mobile_phones:{keywords:[\"iphone\",\"mute\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📵\" src=\"1f4f5.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},exclamation:{keywords:[\"heavy_exclamation_mark\",\"danger\",\"surprise\",\"punctuation\",\"wow\",\"warning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❗\" src=\"2757.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},grey_exclamation:{keywords:[\"surprise\",\"punctuation\",\"gray\",\"wow\",\"warning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❕\" src=\"2755.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},question:{keywords:[\"doubt\",\"confused\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❓\" src=\"2753.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},grey_question:{keywords:[\"doubts\",\"gray\",\"huh\",\"confused\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❔\" src=\"2754.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},bangbang:{keywords:[\"exclamation\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"‼️\" src=\"203c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},interrobang:{keywords:[\"wat\",\"punctuation\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⁉️\" src=\"2049.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},low_brightness:{keywords:[\"sun\",\"afternoon\",\"warm\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔅\" src=\"1f505.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},high_brightness:{keywords:[\"sun\",\"light\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔆\" src=\"1f506.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},trident:{keywords:[\"weapon\",\"spear\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔱\" src=\"1f531.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},fleur_de_lis:{keywords:[\"decorative\",\"scout\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚜\" src=\"269c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},part_alternation_mark:{keywords:[\"graph\",\"presentation\",\"stats\",\"business\",\"economics\",\"bad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"〽️\" src=\"303d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},warning:{keywords:[\"exclamation\",\"wip\",\"alert\",\"error\",\"problem\",\"issue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚠️\" src=\"26a0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},children_crossing:{keywords:[\"school\",\"warning\",\"danger\",\"sign\",\"driving\",\"yellow-diamond\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚸\" src=\"1f6b8.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},beginner:{keywords:[\"badge\",\"shield\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔰\" src=\"1f530.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},recycle:{keywords:[\"arrow\",\"environment\",\"garbage\",\"trash\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♻️\" src=\"267b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6307:{keywords:[\"chinese\",\"point\",\"green-square\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈯\" src=\"1f22f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},chart:{keywords:[\"green-square\",\"graph\",\"presentation\",\"stats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💹\" src=\"1f4b9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sparkle:{keywords:[\"stars\",\"green-square\",\"awesome\",\"good\",\"fireworks\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❇️\" src=\"2747.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eight_spoked_asterisk:{keywords:[\"star\",\"sparkle\",\"green-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✳️\" src=\"2733.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},negative_squared_cross_mark:{keywords:[\"x\",\"green-square\",\"no\",\"deny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❎\" src=\"274e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_check_mark:{keywords:[\"green-square\",\"ok\",\"agree\",\"vote\",\"election\",\"answer\",\"tick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✅\" src=\"2705.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},diamond_shape_with_a_dot_inside:{keywords:[\"jewel\",\"blue\",\"gem\",\"crystal\",\"fancy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💠\" src=\"1f4a0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cyclone:{keywords:[\"weather\",\"swirl\",\"blue\",\"cloud\",\"vortex\",\"spiral\",\"whirlpool\",\"spin\",\"tornado\",\"hurricane\",\"typhoon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌀\" src=\"1f300.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},loop:{keywords:[\"tape\",\"cassette\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➿\" src=\"27bf.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},globe_with_meridians:{keywords:[\"earth\",\"international\",\"world\",\"internet\",\"interweb\",\"i18n\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌐\" src=\"1f310.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},m:{keywords:[\"alphabet\",\"blue-circle\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"Ⓜ️\" src=\"24c2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},atm:{keywords:[\"money\",\"sales\",\"cash\",\"blue-square\",\"payment\",\"bank\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏧\" src=\"1f3e7.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sa:{keywords:[\"japanese\",\"blue-square\",\"katakana\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈂️\" src=\"1f202.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},passport_control:{keywords:[\"custom\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛂\" src=\"1f6c2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},customs:{keywords:[\"passport\",\"border\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛃\" src=\"1f6c3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},baggage_claim:{keywords:[\"blue-square\",\"airport\",\"transport\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛄\" src=\"1f6c4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},left_luggage:{keywords:[\"blue-square\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛅\" src=\"1f6c5.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wheelchair:{keywords:[\"blue-square\",\"disabled\",\"a11y\",\"accessibility\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♿\" src=\"267f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_smoking:{keywords:[\"cigarette\",\"blue-square\",\"smell\",\"smoke\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚭\" src=\"1f6ad.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wc:{keywords:[\"toilet\",\"restroom\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚾\" src=\"1f6be.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},parking:{keywords:[\"cars\",\"blue-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅿️\" src=\"1f17f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},potable_water:{keywords:[\"blue-square\",\"liquid\",\"restroom\",\"cleaning\",\"faucet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚰\" src=\"1f6b0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mens:{keywords:[\"toilet\",\"restroom\",\"wc\",\"blue-square\",\"gender\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚹\" src=\"1f6b9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},womens:{keywords:[\"purple-square\",\"woman\",\"female\",\"toilet\",\"loo\",\"restroom\",\"gender\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚺\" src=\"1f6ba.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},baby_symbol:{keywords:[\"orange-square\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚼\" src=\"1f6bc.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},restroom:{keywords:[\"blue-square\",\"toilet\",\"refresh\",\"wc\",\"gender\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚻\" src=\"1f6bb.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},put_litter_in_its_place:{keywords:[\"blue-square\",\"sign\",\"human\",\"info\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚮\" src=\"1f6ae.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cinema:{keywords:[\"blue-square\",\"record\",\"film\",\"movie\",\"curtain\",\"stage\",\"theater\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎦\" src=\"1f3a6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},signal_strength:{keywords:[\"blue-square\",\"reception\",\"phone\",\"internet\",\"connection\",\"wifi\",\"bluetooth\",\"bars\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📶\" src=\"1f4f6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},koko:{keywords:[\"blue-square\",\"here\",\"katakana\",\"japanese\",\"destination\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈁\" src=\"1f201.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ng:{keywords:[\"blue-square\",\"words\",\"shape\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆖\" src=\"1f196.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ok:{keywords:[\"good\",\"agree\",\"yes\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆗\" src=\"1f197.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},up:{keywords:[\"blue-square\",\"above\",\"high\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆙\" src=\"1f199.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cool:{keywords:[\"words\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆒\" src=\"1f192.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},new:{keywords:[\"blue-square\",\"words\",\"start\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆕\" src=\"1f195.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},free:{keywords:[\"blue-square\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆓\" src=\"1f193.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},zero:{keywords:[\"0\",\"numbers\",\"blue-square\",\"null\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"0️⃣\" src=\"30-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},one:{keywords:[\"blue-square\",\"numbers\",\"1\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"1️⃣\" src=\"31-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},two:{keywords:[\"numbers\",\"2\",\"prime\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"2️⃣\" src=\"32-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},three:{keywords:[\"3\",\"numbers\",\"prime\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"3️⃣\" src=\"33-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},four:{keywords:[\"4\",\"numbers\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"4️⃣\" src=\"34-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},five:{keywords:[\"5\",\"numbers\",\"blue-square\",\"prime\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"5️⃣\" src=\"35-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},six:{keywords:[\"6\",\"numbers\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"6️⃣\" src=\"36-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},seven:{keywords:[\"7\",\"numbers\",\"blue-square\",\"prime\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"7️⃣\" src=\"37-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eight:{keywords:[\"8\",\"blue-square\",\"numbers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"8️⃣\" src=\"38-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},nine:{keywords:[\"blue-square\",\"numbers\",\"9\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"9️⃣\" src=\"39-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},keycap_ten:{keywords:[\"numbers\",\"10\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔟\" src=\"1f51f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},asterisk:{keywords:[\"star\",\"keycap\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"*⃣\" src=\"2a-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eject_button:{keywords:[\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏏️\" src=\"23cf.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_forward:{keywords:[\"blue-square\",\"right\",\"direction\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"▶️\" src=\"25b6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},pause_button:{keywords:[\"pause\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏸\" src=\"23f8.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},next_track_button:{keywords:[\"forward\",\"next\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏭\" src=\"23ed.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},stop_button:{keywords:[\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏹\" src=\"23f9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},record_button:{keywords:[\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏺\" src=\"23fa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},play_or_pause_button:{keywords:[\"blue-square\",\"play\",\"pause\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏯\" src=\"23ef.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},previous_track_button:{keywords:[\"backward\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏮\" src=\"23ee.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},fast_forward:{keywords:[\"blue-square\",\"play\",\"speed\",\"continue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏩\" src=\"23e9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},rewind:{keywords:[\"play\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏪\" src=\"23ea.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},twisted_rightwards_arrows:{keywords:[\"blue-square\",\"shuffle\",\"music\",\"random\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔀\" src=\"1f500.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},repeat:{keywords:[\"loop\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔁\" src=\"1f501.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},repeat_one:{keywords:[\"blue-square\",\"loop\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔂\" src=\"1f502.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_backward:{keywords:[\"blue-square\",\"left\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◀️\" src=\"25c0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_up_small:{keywords:[\"blue-square\",\"triangle\",\"direction\",\"point\",\"forward\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔼\" src=\"1f53c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_down_small:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔽\" src=\"1f53d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_double_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏫\" src=\"23eb.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_double_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏬\" src=\"23ec.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_right:{keywords:[\"blue-square\",\"next\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➡️\" src=\"27a1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_left:{keywords:[\"blue-square\",\"previous\",\"back\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬅️\" src=\"2b05.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_up:{keywords:[\"blue-square\",\"continue\",\"top\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬆️\" src=\"2b06.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬇️\" src=\"2b07.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_right:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northeast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↗️\" src=\"2197.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_right:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southeast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↘️\" src=\"2198.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_left:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southwest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↙️\" src=\"2199.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_left:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northwest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↖️\" src=\"2196.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_up_down:{keywords:[\"blue-square\",\"direction\",\"way\",\"vertical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↕️\" src=\"2195.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},left_right_arrow:{keywords:[\"shape\",\"direction\",\"horizontal\",\"sideways\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↔️\" src=\"2194.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrows_counterclockwise:{keywords:[\"blue-square\",\"sync\",\"cycle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔄\" src=\"1f504.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_right_hook:{keywords:[\"blue-square\",\"return\",\"rotate\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↪️\" src=\"21aa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},leftwards_arrow_with_hook:{keywords:[\"back\",\"return\",\"blue-square\",\"undo\",\"enter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↩️\" src=\"21a9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⤴️\" src=\"2934.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⤵️\" src=\"2935.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},hash:{keywords:[\"symbol\",\"blue-square\",\"twitter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"#️⃣\" src=\"23-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},information_source:{keywords:[\"blue-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"ℹ️\" src=\"2139.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},abc:{keywords:[\"blue-square\",\"alphabet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔤\" src=\"1f524.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},abcd:{keywords:[\"blue-square\",\"alphabet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔡\" src=\"1f521.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},capital_abcd:{keywords:[\"alphabet\",\"words\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔠\" src=\"1f520.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},symbols:{keywords:[\"blue-square\",\"music\",\"note\",\"ampersand\",\"percent\",\"glyphs\",\"characters\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔣\" src=\"1f523.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},musical_note:{keywords:[\"score\",\"tone\",\"sound\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎵\" src=\"1f3b5.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},notes:{keywords:[\"music\",\"score\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎶\" src=\"1f3b6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wavy_dash:{keywords:[\"draw\",\"line\",\"moustache\",\"mustache\",\"squiggle\",\"scribble\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"〰️\" src=\"3030.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},curly_loop:{keywords:[\"scribble\",\"draw\",\"shape\",\"squiggle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➰\" src=\"27b0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_check_mark:{keywords:[\"ok\",\"nike\",\"answer\",\"yes\",\"tick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✔️\" src=\"2714.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrows_clockwise:{keywords:[\"sync\",\"cycle\",\"round\",\"repeat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔃\" src=\"1f503.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_plus_sign:{keywords:[\"math\",\"calculation\",\"addition\",\"more\",\"increase\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➕\" src=\"2795.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_minus_sign:{keywords:[\"math\",\"calculation\",\"subtract\",\"less\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➖\" src=\"2796.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_division_sign:{keywords:[\"divide\",\"math\",\"calculation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➗\" src=\"2797.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_multiplication_x:{keywords:[\"math\",\"calculation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✖️\" src=\"2716.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},infinity:{keywords:[\"forever\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♾\" src=\"267e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_dollar_sign:{keywords:[\"money\",\"sales\",\"payment\",\"currency\",\"buck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💲\" src=\"1f4b2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},currency_exchange:{keywords:[\"money\",\"sales\",\"dollar\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💱\" src=\"1f4b1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},copyright:{keywords:[\"ip\",\"license\",\"circle\",\"law\",\"legal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"©️\" src=\"a9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},registered:{keywords:[\"alphabet\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"®️\" src=\"ae.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},tm:{keywords:[\"trademark\",\"brand\",\"law\",\"legal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"™️\" src=\"2122.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},end:{keywords:[\"words\",\"arrow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔚\" src=\"1f51a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},back:{keywords:[\"arrow\",\"words\",\"return\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔙\" src=\"1f519.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},on:{keywords:[\"arrow\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔛\" src=\"1f51b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},top:{keywords:[\"words\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔝\" src=\"1f51d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},soon:{keywords:[\"arrow\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔜\" src=\"1f51c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ballot_box_with_check:{keywords:[\"ok\",\"agree\",\"confirm\",\"black-square\",\"vote\",\"election\",\"yes\",\"tick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☑️\" src=\"2611.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},radio_button:{keywords:[\"input\",\"old\",\"music\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔘\" src=\"1f518.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_circle:{keywords:[\"shape\",\"round\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚪\" src=\"26aa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_circle:{keywords:[\"shape\",\"button\",\"round\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚫\" src=\"26ab.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},red_circle:{keywords:[\"shape\",\"error\",\"danger\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔴\" src=\"1f534.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},large_blue_circle:{keywords:[\"shape\",\"icon\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔵\" src=\"1f535.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔸\" src=\"1f538.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔹\" src=\"1f539.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},large_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔶\" src=\"1f536.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},large_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔷\" src=\"1f537.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle:{keywords:[\"shape\",\"direction\",\"up\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔺\" src=\"1f53a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_small_square:{keywords:[\"shape\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"▪️\" src=\"25aa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_small_square:{keywords:[\"shape\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"▫️\" src=\"25ab.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_large_square:{keywords:[\"shape\",\"icon\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬛\" src=\"2b1b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_large_square:{keywords:[\"shape\",\"icon\",\"stone\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬜\" src=\"2b1c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle_down:{keywords:[\"shape\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔻\" src=\"1f53b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_medium_square:{keywords:[\"shape\",\"button\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◼️\" src=\"25fc.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_medium_square:{keywords:[\"shape\",\"stone\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◻️\" src=\"25fb.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_medium_small_square:{keywords:[\"icon\",\"shape\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◾\" src=\"25fe.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_medium_small_square:{keywords:[\"shape\",\"stone\",\"icon\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◽\" src=\"25fd.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_square_button:{keywords:[\"shape\",\"input\",\"frame\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔲\" src=\"1f532.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_square_button:{keywords:[\"shape\",\"input\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔳\" src=\"1f533.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},speaker:{keywords:[\"sound\",\"volume\",\"silence\",\"broadcast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔈\" src=\"1f508.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sound:{keywords:[\"volume\",\"speaker\",\"broadcast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔉\" src=\"1f509.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},loud_sound:{keywords:[\"volume\",\"noise\",\"noisy\",\"speaker\",\"broadcast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔊\" src=\"1f50a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mute:{keywords:[\"sound\",\"volume\",\"silence\",\"quiet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔇\" src=\"1f507.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mega:{keywords:[\"sound\",\"speaker\",\"volume\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📣\" src=\"1f4e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},loudspeaker:{keywords:[\"volume\",\"sound\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📢\" src=\"1f4e2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},bell:{keywords:[\"sound\",\"notification\",\"christmas\",\"xmas\",\"chime\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔔\" src=\"1f514.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_bell:{keywords:[\"sound\",\"volume\",\"mute\",\"quiet\",\"silent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔕\" src=\"1f515.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_joker:{keywords:[\"poker\",\"cards\",\"game\",\"play\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🃏\" src=\"1f0cf.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mahjong:{keywords:[\"game\",\"play\",\"chinese\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🀄\" src=\"1f004.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},spades:{keywords:[\"poker\",\"cards\",\"suits\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♠️\" src=\"2660.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clubs:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♣️\" src=\"2663.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},hearts:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♥️\" src=\"2665.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},diamonds:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♦️\" src=\"2666.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},flower_playing_cards:{keywords:[\"game\",\"sunset\",\"red\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎴\" src=\"1f3b4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},thought_balloon:{keywords:[\"bubble\",\"cloud\",\"speech\",\"thinking\",\"dream\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💭\" src=\"1f4ad.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},right_anger_bubble:{keywords:[\"caption\",\"speech\",\"thinking\",\"mad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗯\" src=\"1f5ef.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},speech_balloon:{keywords:[\"bubble\",\"words\",\"message\",\"talk\",\"chatting\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💬\" src=\"1f4ac.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},left_speech_bubble:{keywords:[\"words\",\"message\",\"talk\",\"chatting\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗨\" src=\"1f5e8.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕐\" src=\"1f550.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock2:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕑\" src=\"1f551.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock3:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕒\" src=\"1f552.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock4:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕓\" src=\"1f553.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock5:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕔\" src=\"1f554.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock6:{keywords:[\"time\",\"late\",\"early\",\"schedule\",\"dawn\",\"dusk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕕\" src=\"1f555.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock7:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕖\" src=\"1f556.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock8:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕗\" src=\"1f557.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock9:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕘\" src=\"1f558.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock10:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕙\" src=\"1f559.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock11:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕚\" src=\"1f55a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock12:{keywords:[\"time\",\"noon\",\"midnight\",\"midday\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕛\" src=\"1f55b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕜\" src=\"1f55c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕝\" src=\"1f55d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock330:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕞\" src=\"1f55e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock430:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕟\" src=\"1f55f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock530:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕠\" src=\"1f560.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock630:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕡\" src=\"1f561.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock730:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕢\" src=\"1f562.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock830:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕣\" src=\"1f563.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock930:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕤\" src=\"1f564.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1030:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕥\" src=\"1f565.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕦\" src=\"1f566.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕧\" src=\"1f567.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},afghanistan:{keywords:[\"af\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇫\" src=\"1f1e6-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},aland_islands:{keywords:[\"Åland\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇽\" src=\"1f1e6-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},albania:{keywords:[\"al\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇱\" src=\"1f1e6-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},algeria:{keywords:[\"dz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇿\" src=\"1f1e9-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},american_samoa:{keywords:[\"american\",\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇸\" src=\"1f1e6-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},andorra:{keywords:[\"ad\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇩\" src=\"1f1e6-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},angola:{keywords:[\"ao\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇴\" src=\"1f1e6-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},anguilla:{keywords:[\"ai\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇮\" src=\"1f1e6-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},antarctica:{keywords:[\"aq\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇶\" src=\"1f1e6-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},antigua_barbuda:{keywords:[\"antigua\",\"barbuda\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇬\" src=\"1f1e6-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},argentina:{keywords:[\"ar\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇷\" src=\"1f1e6-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},armenia:{keywords:[\"am\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇲\" src=\"1f1e6-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},aruba:{keywords:[\"aw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇼\" src=\"1f1e6-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},australia:{keywords:[\"au\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇺\" src=\"1f1e6-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},austria:{keywords:[\"at\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇹\" src=\"1f1e6-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},azerbaijan:{keywords:[\"az\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇿\" src=\"1f1e6-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bahamas:{keywords:[\"bs\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇸\" src=\"1f1e7-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bahrain:{keywords:[\"bh\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇭\" src=\"1f1e7-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bangladesh:{keywords:[\"bd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇩\" src=\"1f1e7-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},barbados:{keywords:[\"bb\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇧\" src=\"1f1e7-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},belarus:{keywords:[\"by\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇾\" src=\"1f1e7-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},belgium:{keywords:[\"be\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇪\" src=\"1f1e7-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},belize:{keywords:[\"bz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇿\" src=\"1f1e7-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},benin:{keywords:[\"bj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇯\" src=\"1f1e7-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bermuda:{keywords:[\"bm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇲\" src=\"1f1e7-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bhutan:{keywords:[\"bt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇹\" src=\"1f1e7-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bolivia:{keywords:[\"bo\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇴\" src=\"1f1e7-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},caribbean_netherlands:{keywords:[\"bonaire\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇶\" src=\"1f1e7-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bosnia_herzegovina:{keywords:[\"bosnia\",\"herzegovina\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇦\" src=\"1f1e7-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},botswana:{keywords:[\"bw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇼\" src=\"1f1e7-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},brazil:{keywords:[\"br\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇷\" src=\"1f1e7-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},british_indian_ocean_territory:{keywords:[\"british\",\"indian\",\"ocean\",\"territory\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇴\" src=\"1f1ee-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},british_virgin_islands:{keywords:[\"british\",\"virgin\",\"islands\",\"bvi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇬\" src=\"1f1fb-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},brunei:{keywords:[\"bn\",\"darussalam\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇳\" src=\"1f1e7-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bulgaria:{keywords:[\"bg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇬\" src=\"1f1e7-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},burkina_faso:{keywords:[\"burkina\",\"faso\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇫\" src=\"1f1e7-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},burundi:{keywords:[\"bi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇮\" src=\"1f1e7-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cape_verde:{keywords:[\"cabo\",\"verde\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇻\" src=\"1f1e8-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cambodia:{keywords:[\"kh\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇭\" src=\"1f1f0-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cameroon:{keywords:[\"cm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇲\" src=\"1f1e8-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},canada:{keywords:[\"ca\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇦\" src=\"1f1e8-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},canary_islands:{keywords:[\"canary\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇨\" src=\"1f1ee-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cayman_islands:{keywords:[\"cayman\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇾\" src=\"1f1f0-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},central_african_republic:{keywords:[\"central\",\"african\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇫\" src=\"1f1e8-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},chad:{keywords:[\"td\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇩\" src=\"1f1f9-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},chile:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇱\" src=\"1f1e8-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cn:{keywords:[\"china\",\"chinese\",\"prc\",\"flag\",\"country\",\"nation\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇳\" src=\"1f1e8-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},christmas_island:{keywords:[\"christmas\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇽\" src=\"1f1e8-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cocos_islands:{keywords:[\"cocos\",\"keeling\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇨\" src=\"1f1e8-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},colombia:{keywords:[\"co\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇴\" src=\"1f1e8-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},comoros:{keywords:[\"km\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇲\" src=\"1f1f0-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},congo_brazzaville:{keywords:[\"congo\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇬\" src=\"1f1e8-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},congo_kinshasa:{keywords:[\"congo\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇩\" src=\"1f1e8-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cook_islands:{keywords:[\"cook\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇰\" src=\"1f1e8-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},costa_rica:{keywords:[\"costa\",\"rica\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇷\" src=\"1f1e8-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},croatia:{keywords:[\"hr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇷\" src=\"1f1ed-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cuba:{keywords:[\"cu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇺\" src=\"1f1e8-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},curacao:{keywords:[\"curaçao\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇼\" src=\"1f1e8-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cyprus:{keywords:[\"cy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇾\" src=\"1f1e8-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},czech_republic:{keywords:[\"cz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇿\" src=\"1f1e8-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},denmark:{keywords:[\"dk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇰\" src=\"1f1e9-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},djibouti:{keywords:[\"dj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇯\" src=\"1f1e9-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},dominica:{keywords:[\"dm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇲\" src=\"1f1e9-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},dominican_republic:{keywords:[\"dominican\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇴\" src=\"1f1e9-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ecuador:{keywords:[\"ec\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇨\" src=\"1f1ea-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},egypt:{keywords:[\"eg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇬\" src=\"1f1ea-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},el_salvador:{keywords:[\"el\",\"salvador\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇻\" src=\"1f1f8-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},equatorial_guinea:{keywords:[\"equatorial\",\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇶\" src=\"1f1ec-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},eritrea:{keywords:[\"er\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇷\" src=\"1f1ea-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},estonia:{keywords:[\"ee\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇪\" src=\"1f1ea-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ethiopia:{keywords:[\"et\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇹\" src=\"1f1ea-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},eu:{keywords:[\"european\",\"union\",\"flag\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇺\" src=\"1f1ea-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},falkland_islands:{keywords:[\"falkland\",\"islands\",\"malvinas\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇰\" src=\"1f1eb-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},faroe_islands:{keywords:[\"faroe\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇴\" src=\"1f1eb-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},fiji:{keywords:[\"fj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇯\" src=\"1f1eb-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},finland:{keywords:[\"fi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇮\" src=\"1f1eb-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},fr:{keywords:[\"banner\",\"flag\",\"nation\",\"france\",\"french\",\"country\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇷\" src=\"1f1eb-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},french_guiana:{keywords:[\"french\",\"guiana\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇫\" src=\"1f1ec-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},french_polynesia:{keywords:[\"french\",\"polynesia\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇫\" src=\"1f1f5-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},french_southern_territories:{keywords:[\"french\",\"southern\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇫\" src=\"1f1f9-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},gabon:{keywords:[\"ga\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇦\" src=\"1f1ec-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},gambia:{keywords:[\"gm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇲\" src=\"1f1ec-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},georgia:{keywords:[\"ge\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇪\" src=\"1f1ec-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},de:{keywords:[\"german\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇪\" src=\"1f1e9-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ghana:{keywords:[\"gh\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇭\" src=\"1f1ec-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},gibraltar:{keywords:[\"gi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇮\" src=\"1f1ec-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},greece:{keywords:[\"gr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇷\" src=\"1f1ec-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},greenland:{keywords:[\"gl\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇱\" src=\"1f1ec-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},grenada:{keywords:[\"gd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇩\" src=\"1f1ec-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guadeloupe:{keywords:[\"gp\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇵\" src=\"1f1ec-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guam:{keywords:[\"gu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇺\" src=\"1f1ec-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guatemala:{keywords:[\"gt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇹\" src=\"1f1ec-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guernsey:{keywords:[\"gg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇬\" src=\"1f1ec-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guinea:{keywords:[\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇳\" src=\"1f1ec-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guinea_bissau:{keywords:[\"gw\",\"bissau\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇼\" src=\"1f1ec-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guyana:{keywords:[\"gy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇾\" src=\"1f1ec-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},haiti:{keywords:[\"ht\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇹\" src=\"1f1ed-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},honduras:{keywords:[\"hn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇳\" src=\"1f1ed-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},hong_kong:{keywords:[\"hong\",\"kong\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇰\" src=\"1f1ed-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},hungary:{keywords:[\"hu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇺\" src=\"1f1ed-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},iceland:{keywords:[\"is\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇸\" src=\"1f1ee-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},india:{keywords:[\"in\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇳\" src=\"1f1ee-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},indonesia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇩\" src=\"1f1ee-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},iran:{keywords:[\"iran,\",\"islamic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇷\" src=\"1f1ee-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},iraq:{keywords:[\"iq\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇶\" src=\"1f1ee-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ireland:{keywords:[\"ie\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇪\" src=\"1f1ee-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},isle_of_man:{keywords:[\"isle\",\"man\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇲\" src=\"1f1ee-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},israel:{keywords:[\"il\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇱\" src=\"1f1ee-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},it:{keywords:[\"italy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇹\" src=\"1f1ee-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cote_divoire:{keywords:[\"ivory\",\"coast\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇮\" src=\"1f1e8-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jamaica:{keywords:[\"jm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇲\" src=\"1f1ef-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jp:{keywords:[\"japanese\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇵\" src=\"1f1ef-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jersey:{keywords:[\"je\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇪\" src=\"1f1ef-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jordan:{keywords:[\"jo\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇴\" src=\"1f1ef-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kazakhstan:{keywords:[\"kz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇿\" src=\"1f1f0-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kenya:{keywords:[\"ke\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇪\" src=\"1f1f0-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kiribati:{keywords:[\"ki\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇮\" src=\"1f1f0-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kosovo:{keywords:[\"xk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇽🇰\" src=\"1f1fd-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kuwait:{keywords:[\"kw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇼\" src=\"1f1f0-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kyrgyzstan:{keywords:[\"kg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇬\" src=\"1f1f0-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},laos:{keywords:[\"lao\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇦\" src=\"1f1f1-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},latvia:{keywords:[\"lv\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇻\" src=\"1f1f1-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},lebanon:{keywords:[\"lb\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇧\" src=\"1f1f1-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},lesotho:{keywords:[\"ls\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇸\" src=\"1f1f1-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},liberia:{keywords:[\"lr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇷\" src=\"1f1f1-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},libya:{keywords:[\"ly\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇾\" src=\"1f1f1-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},liechtenstein:{keywords:[\"li\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇮\" src=\"1f1f1-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},lithuania:{keywords:[\"lt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇹\" src=\"1f1f1-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},luxembourg:{keywords:[\"lu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇺\" src=\"1f1f1-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},macau:{keywords:[\"macao\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇴\" src=\"1f1f2-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},macedonia:{keywords:[\"macedonia,\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇰\" src=\"1f1f2-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},madagascar:{keywords:[\"mg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇬\" src=\"1f1f2-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},malawi:{keywords:[\"mw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇼\" src=\"1f1f2-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},malaysia:{keywords:[\"my\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇾\" src=\"1f1f2-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},maldives:{keywords:[\"mv\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇻\" src=\"1f1f2-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mali:{keywords:[\"ml\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇱\" src=\"1f1f2-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},malta:{keywords:[\"mt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇹\" src=\"1f1f2-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},marshall_islands:{keywords:[\"marshall\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇭\" src=\"1f1f2-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},martinique:{keywords:[\"mq\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇶\" src=\"1f1f2-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mauritania:{keywords:[\"mr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇷\" src=\"1f1f2-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mauritius:{keywords:[\"mu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇺\" src=\"1f1f2-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mayotte:{keywords:[\"yt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇾🇹\" src=\"1f1fe-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mexico:{keywords:[\"mx\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇽\" src=\"1f1f2-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},micronesia:{keywords:[\"micronesia,\",\"federated\",\"states\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇲\" src=\"1f1eb-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},moldova:{keywords:[\"moldova,\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇩\" src=\"1f1f2-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},monaco:{keywords:[\"mc\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇨\" src=\"1f1f2-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mongolia:{keywords:[\"mn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇳\" src=\"1f1f2-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},montenegro:{keywords:[\"me\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇪\" src=\"1f1f2-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},montserrat:{keywords:[\"ms\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇸\" src=\"1f1f2-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},morocco:{keywords:[\"ma\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇦\" src=\"1f1f2-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mozambique:{keywords:[\"mz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇿\" src=\"1f1f2-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},myanmar:{keywords:[\"mm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇲\" src=\"1f1f2-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},namibia:{keywords:[\"na\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇦\" src=\"1f1f3-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nauru:{keywords:[\"nr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇷\" src=\"1f1f3-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nepal:{keywords:[\"np\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇵\" src=\"1f1f3-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},netherlands:{keywords:[\"nl\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇱\" src=\"1f1f3-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},new_caledonia:{keywords:[\"new\",\"caledonia\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇨\" src=\"1f1f3-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},new_zealand:{keywords:[\"new\",\"zealand\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇿\" src=\"1f1f3-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nicaragua:{keywords:[\"ni\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇮\" src=\"1f1f3-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},niger:{keywords:[\"ne\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇪\" src=\"1f1f3-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nigeria:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇬\" src=\"1f1f3-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},niue:{keywords:[\"nu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇺\" src=\"1f1f3-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},norfolk_island:{keywords:[\"norfolk\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇫\" src=\"1f1f3-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},northern_mariana_islands:{keywords:[\"northern\",\"mariana\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇵\" src=\"1f1f2-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},north_korea:{keywords:[\"north\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇵\" src=\"1f1f0-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},norway:{keywords:[\"no\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇴\" src=\"1f1f3-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},oman:{keywords:[\"om_symbol\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇴🇲\" src=\"1f1f4-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},pakistan:{keywords:[\"pk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇰\" src=\"1f1f5-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},palau:{keywords:[\"pw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇼\" src=\"1f1f5-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},palestinian_territories:{keywords:[\"palestine\",\"palestinian\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇸\" src=\"1f1f5-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},panama:{keywords:[\"pa\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇦\" src=\"1f1f5-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},papua_new_guinea:{keywords:[\"papua\",\"new\",\"guinea\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇬\" src=\"1f1f5-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},paraguay:{keywords:[\"py\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇾\" src=\"1f1f5-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},peru:{keywords:[\"pe\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇪\" src=\"1f1f5-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},philippines:{keywords:[\"ph\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇭\" src=\"1f1f5-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},pitcairn_islands:{keywords:[\"pitcairn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇳\" src=\"1f1f5-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},poland:{keywords:[\"pl\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇱\" src=\"1f1f5-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},portugal:{keywords:[\"pt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇹\" src=\"1f1f5-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},puerto_rico:{keywords:[\"puerto\",\"rico\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇷\" src=\"1f1f5-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},qatar:{keywords:[\"qa\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇶🇦\" src=\"1f1f6-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},reunion:{keywords:[\"réunion\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇪\" src=\"1f1f7-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},romania:{keywords:[\"ro\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇴\" src=\"1f1f7-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ru:{keywords:[\"russian\",\"federation\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇺\" src=\"1f1f7-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},rwanda:{keywords:[\"rw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇼\" src=\"1f1f7-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_barthelemy:{keywords:[\"saint\",\"barthélemy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇱\" src=\"1f1e7-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_helena:{keywords:[\"saint\",\"helena\",\"ascension\",\"tristan\",\"cunha\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇭\" src=\"1f1f8-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_kitts_nevis:{keywords:[\"saint\",\"kitts\",\"nevis\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇳\" src=\"1f1f0-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_lucia:{keywords:[\"saint\",\"lucia\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇨\" src=\"1f1f1-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_pierre_miquelon:{keywords:[\"saint\",\"pierre\",\"miquelon\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇲\" src=\"1f1f5-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_vincent_grenadines:{keywords:[\"saint\",\"vincent\",\"grenadines\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇨\" src=\"1f1fb-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},samoa:{keywords:[\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇼🇸\" src=\"1f1fc-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},san_marino:{keywords:[\"san\",\"marino\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇲\" src=\"1f1f8-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sao_tome_principe:{keywords:[\"sao\",\"tome\",\"principe\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇹\" src=\"1f1f8-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},saudi_arabia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇦\" src=\"1f1f8-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},senegal:{keywords:[\"sn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇳\" src=\"1f1f8-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},serbia:{keywords:[\"rs\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇸\" src=\"1f1f7-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},seychelles:{keywords:[\"sc\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇨\" src=\"1f1f8-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sierra_leone:{keywords:[\"sierra\",\"leone\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇱\" src=\"1f1f8-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},singapore:{keywords:[\"sg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇬\" src=\"1f1f8-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sint_maarten:{keywords:[\"sint\",\"maarten\",\"dutch\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇽\" src=\"1f1f8-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},slovakia:{keywords:[\"sk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇰\" src=\"1f1f8-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},slovenia:{keywords:[\"si\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇮\" src=\"1f1f8-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},solomon_islands:{keywords:[\"solomon\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇧\" src=\"1f1f8-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},somalia:{keywords:[\"so\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇴\" src=\"1f1f8-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},south_africa:{keywords:[\"south\",\"africa\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇿🇦\" src=\"1f1ff-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},south_georgia_south_sandwich_islands:{keywords:[\"south\",\"georgia\",\"sandwich\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇸\" src=\"1f1ec-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kr:{keywords:[\"south\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇷\" src=\"1f1f0-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},south_sudan:{keywords:[\"south\",\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇸\" src=\"1f1f8-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},es:{keywords:[\"spain\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇸\" src=\"1f1ea-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sri_lanka:{keywords:[\"sri\",\"lanka\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇰\" src=\"1f1f1-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sudan:{keywords:[\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇩\" src=\"1f1f8-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},suriname:{keywords:[\"sr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇷\" src=\"1f1f8-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},swaziland:{keywords:[\"sz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇿\" src=\"1f1f8-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sweden:{keywords:[\"se\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇪\" src=\"1f1f8-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},switzerland:{keywords:[\"ch\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇭\" src=\"1f1e8-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},syria:{keywords:[\"syrian\",\"arab\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇾\" src=\"1f1f8-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},taiwan:{keywords:[\"tw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇼\" src=\"1f1f9-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tajikistan:{keywords:[\"tj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇯\" src=\"1f1f9-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tanzania:{keywords:[\"tanzania,\",\"united\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇿\" src=\"1f1f9-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},thailand:{keywords:[\"th\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇭\" src=\"1f1f9-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},timor_leste:{keywords:[\"timor\",\"leste\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇱\" src=\"1f1f9-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},togo:{keywords:[\"tg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇬\" src=\"1f1f9-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tokelau:{keywords:[\"tk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇰\" src=\"1f1f9-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tonga:{keywords:[\"to\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇴\" src=\"1f1f9-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},trinidad_tobago:{keywords:[\"trinidad\",\"tobago\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇹\" src=\"1f1f9-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tunisia:{keywords:[\"tn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇳\" src=\"1f1f9-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tr:{keywords:[\"turkey\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇷\" src=\"1f1f9-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},turkmenistan:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇲\" src=\"1f1f9-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},turks_caicos_islands:{keywords:[\"turks\",\"caicos\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇨\" src=\"1f1f9-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tuvalu:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇻\" src=\"1f1f9-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uganda:{keywords:[\"ug\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇬\" src=\"1f1fa-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ukraine:{keywords:[\"ua\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇦\" src=\"1f1fa-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},united_arab_emirates:{keywords:[\"united\",\"arab\",\"emirates\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇪\" src=\"1f1e6-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uk:{keywords:[\"united\",\"kingdom\",\"great\",\"britain\",\"northern\",\"ireland\",\"flag\",\"nation\",\"country\",\"banner\",\"british\",\"UK\",\"english\",\"england\",\"union jack\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇧\" src=\"1f1ec-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},england:{keywords:[\"flag\",\"english\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴󠁧󠁢󠁥󠁮󠁧󠁿\" src=\"1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png\"/>',fitzpatrick_scale:false,category:\"flags\"},scotland:{keywords:[\"flag\",\"scottish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴󠁧󠁢󠁳󠁣󠁴󠁿\" src=\"1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png\"/>',fitzpatrick_scale:false,category:\"flags\"},wales:{keywords:[\"flag\",\"welsh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴󠁧󠁢󠁷󠁬󠁳󠁿\" src=\"1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png\"/>',fitzpatrick_scale:false,category:\"flags\"},us:{keywords:[\"united\",\"states\",\"america\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇸\" src=\"1f1fa-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},us_virgin_islands:{keywords:[\"virgin\",\"islands\",\"us\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇮\" src=\"1f1fb-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uruguay:{keywords:[\"uy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇾\" src=\"1f1fa-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uzbekistan:{keywords:[\"uz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇿\" src=\"1f1fa-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},vanuatu:{keywords:[\"vu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇺\" src=\"1f1fb-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},vatican_city:{keywords:[\"vatican\",\"city\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇦\" src=\"1f1fb-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},venezuela:{keywords:[\"ve\",\"bolivarian\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇪\" src=\"1f1fb-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},vietnam:{keywords:[\"viet\",\"nam\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇳\" src=\"1f1fb-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},wallis_futuna:{keywords:[\"wallis\",\"futuna\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇼🇫\" src=\"1f1fc-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},western_sahara:{keywords:[\"western\",\"sahara\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇭\" src=\"1f1ea-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},yemen:{keywords:[\"ye\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇾🇪\" src=\"1f1fe-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},zambia:{keywords:[\"zm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇿🇲\" src=\"1f1ff-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},zimbabwe:{keywords:[\"zw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇿🇼\" src=\"1f1ff-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},united_nations:{keywords:[\"un\",\"flag\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇳\" src=\"1f1fa-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},pirate_flag:{keywords:[\"skull\",\"crossbones\",\"flag\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴‍☠️\" src=\"1f3f4-200d-2620-fe0f.png\"/>',fitzpatrick_scale:false,category:\"flags\"}});"
  },
  {
    "path": "vue_acimage_web/public/tinymce/plugins/emoticons/js/emojis.js",
    "content": "window.tinymce.Resource.add(\"tinymce.plugins.emoticons\",{grinning:{keywords:[\"face\",\"smile\",\"happy\",\"joy\",\":D\",\"grin\"],char:\"😀\",fitzpatrick_scale:false,category:\"people\"},grimacing:{keywords:[\"face\",\"grimace\",\"teeth\"],char:\"😬\",fitzpatrick_scale:false,category:\"people\"},grin:{keywords:[\"face\",\"happy\",\"smile\",\"joy\",\"kawaii\"],char:\"😁\",fitzpatrick_scale:false,category:\"people\"},joy:{keywords:[\"face\",\"cry\",\"tears\",\"weep\",\"happy\",\"happytears\",\"haha\"],char:\"😂\",fitzpatrick_scale:false,category:\"people\"},rofl:{keywords:[\"face\",\"rolling\",\"floor\",\"laughing\",\"lol\",\"haha\"],char:\"🤣\",fitzpatrick_scale:false,category:\"people\"},partying:{keywords:[\"face\",\"celebration\",\"woohoo\"],char:\"🥳\",fitzpatrick_scale:false,category:\"people\"},smiley:{keywords:[\"face\",\"happy\",\"joy\",\"haha\",\":D\",\":)\",\"smile\",\"funny\"],char:\"😃\",fitzpatrick_scale:false,category:\"people\"},smile:{keywords:[\"face\",\"happy\",\"joy\",\"funny\",\"haha\",\"laugh\",\"like\",\":D\",\":)\"],char:\"😄\",fitzpatrick_scale:false,category:\"people\"},sweat_smile:{keywords:[\"face\",\"hot\",\"happy\",\"laugh\",\"sweat\",\"smile\",\"relief\"],char:\"😅\",fitzpatrick_scale:false,category:\"people\"},laughing:{keywords:[\"happy\",\"joy\",\"lol\",\"satisfied\",\"haha\",\"face\",\"glad\",\"XD\",\"laugh\"],char:\"😆\",fitzpatrick_scale:false,category:\"people\"},innocent:{keywords:[\"face\",\"angel\",\"heaven\",\"halo\"],char:\"😇\",fitzpatrick_scale:false,category:\"people\"},wink:{keywords:[\"face\",\"happy\",\"mischievous\",\"secret\",\";)\",\"smile\",\"eye\"],char:\"😉\",fitzpatrick_scale:false,category:\"people\"},blush:{keywords:[\"face\",\"smile\",\"happy\",\"flushed\",\"crush\",\"embarrassed\",\"shy\",\"joy\"],char:\"😊\",fitzpatrick_scale:false,category:\"people\"},slightly_smiling_face:{keywords:[\"face\",\"smile\"],char:\"🙂\",fitzpatrick_scale:false,category:\"people\"},upside_down_face:{keywords:[\"face\",\"flipped\",\"silly\",\"smile\"],char:\"🙃\",fitzpatrick_scale:false,category:\"people\"},relaxed:{keywords:[\"face\",\"blush\",\"massage\",\"happiness\"],char:\"☺️\",fitzpatrick_scale:false,category:\"people\"},yum:{keywords:[\"happy\",\"joy\",\"tongue\",\"smile\",\"face\",\"silly\",\"yummy\",\"nom\",\"delicious\",\"savouring\"],char:\"😋\",fitzpatrick_scale:false,category:\"people\"},relieved:{keywords:[\"face\",\"relaxed\",\"phew\",\"massage\",\"happiness\"],char:\"😌\",fitzpatrick_scale:false,category:\"people\"},heart_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"heart\"],char:\"😍\",fitzpatrick_scale:false,category:\"people\"},smiling_face_with_three_hearts:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"hearts\",\"adore\"],char:\"🥰\",fitzpatrick_scale:false,category:\"people\"},kissing_heart:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😘\",fitzpatrick_scale:false,category:\"people\"},kissing:{keywords:[\"love\",\"like\",\"face\",\"3\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😗\",fitzpatrick_scale:false,category:\"people\"},kissing_smiling_eyes:{keywords:[\"face\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😙\",fitzpatrick_scale:false,category:\"people\"},kissing_closed_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😚\",fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_winking_eye:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"wink\",\"tongue\"],char:\"😜\",fitzpatrick_scale:false,category:\"people\"},zany:{keywords:[\"face\",\"goofy\",\"crazy\"],char:\"🤪\",fitzpatrick_scale:false,category:\"people\"},raised_eyebrow:{keywords:[\"face\",\"distrust\",\"scepticism\",\"disapproval\",\"disbelief\",\"surprise\"],char:\"🤨\",fitzpatrick_scale:false,category:\"people\"},monocle:{keywords:[\"face\",\"stuffy\",\"wealthy\"],char:\"🧐\",fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_closed_eyes:{keywords:[\"face\",\"prank\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:\"😝\",fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:\"😛\",fitzpatrick_scale:false,category:\"people\"},money_mouth_face:{keywords:[\"face\",\"rich\",\"dollar\",\"money\"],char:\"🤑\",fitzpatrick_scale:false,category:\"people\"},nerd_face:{keywords:[\"face\",\"nerdy\",\"geek\",\"dork\"],char:\"🤓\",fitzpatrick_scale:false,category:\"people\"},sunglasses:{keywords:[\"face\",\"cool\",\"smile\",\"summer\",\"beach\",\"sunglass\"],char:\"😎\",fitzpatrick_scale:false,category:\"people\"},star_struck:{keywords:[\"face\",\"smile\",\"starry\",\"eyes\",\"grinning\"],char:\"🤩\",fitzpatrick_scale:false,category:\"people\"},clown_face:{keywords:[\"face\"],char:\"🤡\",fitzpatrick_scale:false,category:\"people\"},cowboy_hat_face:{keywords:[\"face\",\"cowgirl\",\"hat\"],char:\"🤠\",fitzpatrick_scale:false,category:\"people\"},hugs:{keywords:[\"face\",\"smile\",\"hug\"],char:\"🤗\",fitzpatrick_scale:false,category:\"people\"},smirk:{keywords:[\"face\",\"smile\",\"mean\",\"prank\",\"smug\",\"sarcasm\"],char:\"😏\",fitzpatrick_scale:false,category:\"people\"},no_mouth:{keywords:[\"face\",\"hellokitty\"],char:\"😶\",fitzpatrick_scale:false,category:\"people\"},neutral_face:{keywords:[\"indifference\",\"meh\",\":|\",\"neutral\"],char:\"😐\",fitzpatrick_scale:false,category:\"people\"},expressionless:{keywords:[\"face\",\"indifferent\",\"-_-\",\"meh\",\"deadpan\"],char:\"😑\",fitzpatrick_scale:false,category:\"people\"},unamused:{keywords:[\"indifference\",\"bored\",\"straight face\",\"serious\",\"sarcasm\",\"unimpressed\",\"skeptical\",\"dubious\",\"side_eye\"],char:\"😒\",fitzpatrick_scale:false,category:\"people\"},roll_eyes:{keywords:[\"face\",\"eyeroll\",\"frustrated\"],char:\"🙄\",fitzpatrick_scale:false,category:\"people\"},thinking:{keywords:[\"face\",\"hmmm\",\"think\",\"consider\"],char:\"🤔\",fitzpatrick_scale:false,category:\"people\"},lying_face:{keywords:[\"face\",\"lie\",\"pinocchio\"],char:\"🤥\",fitzpatrick_scale:false,category:\"people\"},hand_over_mouth:{keywords:[\"face\",\"whoops\",\"shock\",\"surprise\"],char:\"🤭\",fitzpatrick_scale:false,category:\"people\"},shushing:{keywords:[\"face\",\"quiet\",\"shhh\"],char:\"🤫\",fitzpatrick_scale:false,category:\"people\"},symbols_over_mouth:{keywords:[\"face\",\"swearing\",\"cursing\",\"cussing\",\"profanity\",\"expletive\"],char:\"🤬\",fitzpatrick_scale:false,category:\"people\"},exploding_head:{keywords:[\"face\",\"shocked\",\"mind\",\"blown\"],char:\"🤯\",fitzpatrick_scale:false,category:\"people\"},flushed:{keywords:[\"face\",\"blush\",\"shy\",\"flattered\"],char:\"😳\",fitzpatrick_scale:false,category:\"people\"},disappointed:{keywords:[\"face\",\"sad\",\"upset\",\"depressed\",\":(\"],char:\"😞\",fitzpatrick_scale:false,category:\"people\"},worried:{keywords:[\"face\",\"concern\",\"nervous\",\":(\"],char:\"😟\",fitzpatrick_scale:false,category:\"people\"},angry:{keywords:[\"mad\",\"face\",\"annoyed\",\"frustrated\"],char:\"😠\",fitzpatrick_scale:false,category:\"people\"},rage:{keywords:[\"angry\",\"mad\",\"hate\",\"despise\"],char:\"😡\",fitzpatrick_scale:false,category:\"people\"},pensive:{keywords:[\"face\",\"sad\",\"depressed\",\"upset\"],char:\"😔\",fitzpatrick_scale:false,category:\"people\"},confused:{keywords:[\"face\",\"indifference\",\"huh\",\"weird\",\"hmmm\",\":/\"],char:\"😕\",fitzpatrick_scale:false,category:\"people\"},slightly_frowning_face:{keywords:[\"face\",\"frowning\",\"disappointed\",\"sad\",\"upset\"],char:\"🙁\",fitzpatrick_scale:false,category:\"people\"},frowning_face:{keywords:[\"face\",\"sad\",\"upset\",\"frown\"],char:\"☹\",fitzpatrick_scale:false,category:\"people\"},persevere:{keywords:[\"face\",\"sick\",\"no\",\"upset\",\"oops\"],char:\"😣\",fitzpatrick_scale:false,category:\"people\"},confounded:{keywords:[\"face\",\"confused\",\"sick\",\"unwell\",\"oops\",\":S\"],char:\"😖\",fitzpatrick_scale:false,category:\"people\"},tired_face:{keywords:[\"sick\",\"whine\",\"upset\",\"frustrated\"],char:\"😫\",fitzpatrick_scale:false,category:\"people\"},weary:{keywords:[\"face\",\"tired\",\"sleepy\",\"sad\",\"frustrated\",\"upset\"],char:\"😩\",fitzpatrick_scale:false,category:\"people\"},pleading:{keywords:[\"face\",\"begging\",\"mercy\"],char:\"🥺\",fitzpatrick_scale:false,category:\"people\"},triumph:{keywords:[\"face\",\"gas\",\"phew\",\"proud\",\"pride\"],char:\"😤\",fitzpatrick_scale:false,category:\"people\"},open_mouth:{keywords:[\"face\",\"surprise\",\"impressed\",\"wow\",\"whoa\",\":O\"],char:\"😮\",fitzpatrick_scale:false,category:\"people\"},scream:{keywords:[\"face\",\"munch\",\"scared\",\"omg\"],char:\"😱\",fitzpatrick_scale:false,category:\"people\"},fearful:{keywords:[\"face\",\"scared\",\"terrified\",\"nervous\",\"oops\",\"huh\"],char:\"😨\",fitzpatrick_scale:false,category:\"people\"},cold_sweat:{keywords:[\"face\",\"nervous\",\"sweat\"],char:\"😰\",fitzpatrick_scale:false,category:\"people\"},hushed:{keywords:[\"face\",\"woo\",\"shh\"],char:\"😯\",fitzpatrick_scale:false,category:\"people\"},frowning:{keywords:[\"face\",\"aw\",\"what\"],char:\"😦\",fitzpatrick_scale:false,category:\"people\"},anguished:{keywords:[\"face\",\"stunned\",\"nervous\"],char:\"😧\",fitzpatrick_scale:false,category:\"people\"},cry:{keywords:[\"face\",\"tears\",\"sad\",\"depressed\",\"upset\",\":'(\"],char:\"😢\",fitzpatrick_scale:false,category:\"people\"},disappointed_relieved:{keywords:[\"face\",\"phew\",\"sweat\",\"nervous\"],char:\"😥\",fitzpatrick_scale:false,category:\"people\"},drooling_face:{keywords:[\"face\"],char:\"🤤\",fitzpatrick_scale:false,category:\"people\"},sleepy:{keywords:[\"face\",\"tired\",\"rest\",\"nap\"],char:\"😪\",fitzpatrick_scale:false,category:\"people\"},sweat:{keywords:[\"face\",\"hot\",\"sad\",\"tired\",\"exercise\"],char:\"😓\",fitzpatrick_scale:false,category:\"people\"},hot:{keywords:[\"face\",\"feverish\",\"heat\",\"red\",\"sweating\"],char:\"🥵\",fitzpatrick_scale:false,category:\"people\"},cold:{keywords:[\"face\",\"blue\",\"freezing\",\"frozen\",\"frostbite\",\"icicles\"],char:\"🥶\",fitzpatrick_scale:false,category:\"people\"},sob:{keywords:[\"face\",\"cry\",\"tears\",\"sad\",\"upset\",\"depressed\"],char:\"😭\",fitzpatrick_scale:false,category:\"people\"},dizzy_face:{keywords:[\"spent\",\"unconscious\",\"xox\",\"dizzy\"],char:\"😵\",fitzpatrick_scale:false,category:\"people\"},astonished:{keywords:[\"face\",\"xox\",\"surprised\",\"poisoned\"],char:\"😲\",fitzpatrick_scale:false,category:\"people\"},zipper_mouth_face:{keywords:[\"face\",\"sealed\",\"zipper\",\"secret\"],char:\"🤐\",fitzpatrick_scale:false,category:\"people\"},nauseated_face:{keywords:[\"face\",\"vomit\",\"gross\",\"green\",\"sick\",\"throw up\",\"ill\"],char:\"🤢\",fitzpatrick_scale:false,category:\"people\"},sneezing_face:{keywords:[\"face\",\"gesundheit\",\"sneeze\",\"sick\",\"allergy\"],char:\"🤧\",fitzpatrick_scale:false,category:\"people\"},vomiting:{keywords:[\"face\",\"sick\"],char:\"🤮\",fitzpatrick_scale:false,category:\"people\"},mask:{keywords:[\"face\",\"sick\",\"ill\",\"disease\"],char:\"😷\",fitzpatrick_scale:false,category:\"people\"},face_with_thermometer:{keywords:[\"sick\",\"temperature\",\"thermometer\",\"cold\",\"fever\"],char:\"🤒\",fitzpatrick_scale:false,category:\"people\"},face_with_head_bandage:{keywords:[\"injured\",\"clumsy\",\"bandage\",\"hurt\"],char:\"🤕\",fitzpatrick_scale:false,category:\"people\"},woozy:{keywords:[\"face\",\"dizzy\",\"intoxicated\",\"tipsy\",\"wavy\"],char:\"🥴\",fitzpatrick_scale:false,category:\"people\"},sleeping:{keywords:[\"face\",\"tired\",\"sleepy\",\"night\",\"zzz\"],char:\"😴\",fitzpatrick_scale:false,category:\"people\"},zzz:{keywords:[\"sleepy\",\"tired\",\"dream\"],char:\"💤\",fitzpatrick_scale:false,category:\"people\"},poop:{keywords:[\"hankey\",\"shitface\",\"fail\",\"turd\",\"shit\"],char:\"💩\",fitzpatrick_scale:false,category:\"people\"},smiling_imp:{keywords:[\"devil\",\"horns\"],char:\"😈\",fitzpatrick_scale:false,category:\"people\"},imp:{keywords:[\"devil\",\"angry\",\"horns\"],char:\"👿\",fitzpatrick_scale:false,category:\"people\"},japanese_ogre:{keywords:[\"monster\",\"red\",\"mask\",\"halloween\",\"scary\",\"creepy\",\"devil\",\"demon\",\"japanese\",\"ogre\"],char:\"👹\",fitzpatrick_scale:false,category:\"people\"},japanese_goblin:{keywords:[\"red\",\"evil\",\"mask\",\"monster\",\"scary\",\"creepy\",\"japanese\",\"goblin\"],char:\"👺\",fitzpatrick_scale:false,category:\"people\"},skull:{keywords:[\"dead\",\"skeleton\",\"creepy\",\"death\"],char:\"💀\",fitzpatrick_scale:false,category:\"people\"},ghost:{keywords:[\"halloween\",\"spooky\",\"scary\"],char:\"👻\",fitzpatrick_scale:false,category:\"people\"},alien:{keywords:[\"UFO\",\"paul\",\"weird\",\"outer_space\"],char:\"👽\",fitzpatrick_scale:false,category:\"people\"},robot:{keywords:[\"computer\",\"machine\",\"bot\"],char:\"🤖\",fitzpatrick_scale:false,category:\"people\"},smiley_cat:{keywords:[\"animal\",\"cats\",\"happy\",\"smile\"],char:\"😺\",fitzpatrick_scale:false,category:\"people\"},smile_cat:{keywords:[\"animal\",\"cats\",\"smile\"],char:\"😸\",fitzpatrick_scale:false,category:\"people\"},joy_cat:{keywords:[\"animal\",\"cats\",\"haha\",\"happy\",\"tears\"],char:\"😹\",fitzpatrick_scale:false,category:\"people\"},heart_eyes_cat:{keywords:[\"animal\",\"love\",\"like\",\"affection\",\"cats\",\"valentines\",\"heart\"],char:\"😻\",fitzpatrick_scale:false,category:\"people\"},smirk_cat:{keywords:[\"animal\",\"cats\",\"smirk\"],char:\"😼\",fitzpatrick_scale:false,category:\"people\"},kissing_cat:{keywords:[\"animal\",\"cats\",\"kiss\"],char:\"😽\",fitzpatrick_scale:false,category:\"people\"},scream_cat:{keywords:[\"animal\",\"cats\",\"munch\",\"scared\",\"scream\"],char:\"🙀\",fitzpatrick_scale:false,category:\"people\"},crying_cat_face:{keywords:[\"animal\",\"tears\",\"weep\",\"sad\",\"cats\",\"upset\",\"cry\"],char:\"😿\",fitzpatrick_scale:false,category:\"people\"},pouting_cat:{keywords:[\"animal\",\"cats\"],char:\"😾\",fitzpatrick_scale:false,category:\"people\"},palms_up:{keywords:[\"hands\",\"gesture\",\"cupped\",\"prayer\"],char:\"🤲\",fitzpatrick_scale:true,category:\"people\"},raised_hands:{keywords:[\"gesture\",\"hooray\",\"yea\",\"celebration\",\"hands\"],char:\"🙌\",fitzpatrick_scale:true,category:\"people\"},clap:{keywords:[\"hands\",\"praise\",\"applause\",\"congrats\",\"yay\"],char:\"👏\",fitzpatrick_scale:true,category:\"people\"},wave:{keywords:[\"hands\",\"gesture\",\"goodbye\",\"solong\",\"farewell\",\"hello\",\"hi\",\"palm\"],char:\"👋\",fitzpatrick_scale:true,category:\"people\"},call_me_hand:{keywords:[\"hands\",\"gesture\"],char:\"🤙\",fitzpatrick_scale:true,category:\"people\"},\"+1\":{keywords:[\"thumbsup\",\"yes\",\"awesome\",\"good\",\"agree\",\"accept\",\"cool\",\"hand\",\"like\"],char:\"👍\",fitzpatrick_scale:true,category:\"people\"},\"-1\":{keywords:[\"thumbsdown\",\"no\",\"dislike\",\"hand\"],char:\"👎\",fitzpatrick_scale:true,category:\"people\"},facepunch:{keywords:[\"angry\",\"violence\",\"fist\",\"hit\",\"attack\",\"hand\"],char:\"👊\",fitzpatrick_scale:true,category:\"people\"},fist:{keywords:[\"fingers\",\"hand\",\"grasp\"],char:\"✊\",fitzpatrick_scale:true,category:\"people\"},fist_left:{keywords:[\"hand\",\"fistbump\"],char:\"🤛\",fitzpatrick_scale:true,category:\"people\"},fist_right:{keywords:[\"hand\",\"fistbump\"],char:\"🤜\",fitzpatrick_scale:true,category:\"people\"},v:{keywords:[\"fingers\",\"ohyeah\",\"hand\",\"peace\",\"victory\",\"two\"],char:\"✌\",fitzpatrick_scale:true,category:\"people\"},ok_hand:{keywords:[\"fingers\",\"limbs\",\"perfect\",\"ok\",\"okay\"],char:\"👌\",fitzpatrick_scale:true,category:\"people\"},raised_hand:{keywords:[\"fingers\",\"stop\",\"highfive\",\"palm\",\"ban\"],char:\"✋\",fitzpatrick_scale:true,category:\"people\"},raised_back_of_hand:{keywords:[\"fingers\",\"raised\",\"backhand\"],char:\"🤚\",fitzpatrick_scale:true,category:\"people\"},open_hands:{keywords:[\"fingers\",\"butterfly\",\"hands\",\"open\"],char:\"👐\",fitzpatrick_scale:true,category:\"people\"},muscle:{keywords:[\"arm\",\"flex\",\"hand\",\"summer\",\"strong\",\"biceps\"],char:\"💪\",fitzpatrick_scale:true,category:\"people\"},pray:{keywords:[\"please\",\"hope\",\"wish\",\"namaste\",\"highfive\"],char:\"🙏\",fitzpatrick_scale:true,category:\"people\"},foot:{keywords:[\"kick\",\"stomp\"],char:\"🦶\",fitzpatrick_scale:true,category:\"people\"},leg:{keywords:[\"kick\",\"limb\"],char:\"🦵\",fitzpatrick_scale:true,category:\"people\"},handshake:{keywords:[\"agreement\",\"shake\"],char:\"🤝\",fitzpatrick_scale:false,category:\"people\"},point_up:{keywords:[\"hand\",\"fingers\",\"direction\",\"up\"],char:\"☝\",fitzpatrick_scale:true,category:\"people\"},point_up_2:{keywords:[\"fingers\",\"hand\",\"direction\",\"up\"],char:\"👆\",fitzpatrick_scale:true,category:\"people\"},point_down:{keywords:[\"fingers\",\"hand\",\"direction\",\"down\"],char:\"👇\",fitzpatrick_scale:true,category:\"people\"},point_left:{keywords:[\"direction\",\"fingers\",\"hand\",\"left\"],char:\"👈\",fitzpatrick_scale:true,category:\"people\"},point_right:{keywords:[\"fingers\",\"hand\",\"direction\",\"right\"],char:\"👉\",fitzpatrick_scale:true,category:\"people\"},fu:{keywords:[\"hand\",\"fingers\",\"rude\",\"middle\",\"flipping\"],char:\"🖕\",fitzpatrick_scale:true,category:\"people\"},raised_hand_with_fingers_splayed:{keywords:[\"hand\",\"fingers\",\"palm\"],char:\"🖐\",fitzpatrick_scale:true,category:\"people\"},love_you:{keywords:[\"hand\",\"fingers\",\"gesture\"],char:\"🤟\",fitzpatrick_scale:true,category:\"people\"},metal:{keywords:[\"hand\",\"fingers\",\"evil_eye\",\"sign_of_horns\",\"rock_on\"],char:\"🤘\",fitzpatrick_scale:true,category:\"people\"},crossed_fingers:{keywords:[\"good\",\"lucky\"],char:\"🤞\",fitzpatrick_scale:true,category:\"people\"},vulcan_salute:{keywords:[\"hand\",\"fingers\",\"spock\",\"star trek\"],char:\"🖖\",fitzpatrick_scale:true,category:\"people\"},writing_hand:{keywords:[\"lower_left_ballpoint_pen\",\"stationery\",\"write\",\"compose\"],char:\"✍\",fitzpatrick_scale:true,category:\"people\"},selfie:{keywords:[\"camera\",\"phone\"],char:\"🤳\",fitzpatrick_scale:true,category:\"people\"},nail_care:{keywords:[\"beauty\",\"manicure\",\"finger\",\"fashion\",\"nail\"],char:\"💅\",fitzpatrick_scale:true,category:\"people\"},lips:{keywords:[\"mouth\",\"kiss\"],char:\"👄\",fitzpatrick_scale:false,category:\"people\"},tooth:{keywords:[\"teeth\",\"dentist\"],char:\"🦷\",fitzpatrick_scale:false,category:\"people\"},tongue:{keywords:[\"mouth\",\"playful\"],char:\"👅\",fitzpatrick_scale:false,category:\"people\"},ear:{keywords:[\"face\",\"hear\",\"sound\",\"listen\"],char:\"👂\",fitzpatrick_scale:true,category:\"people\"},nose:{keywords:[\"smell\",\"sniff\"],char:\"👃\",fitzpatrick_scale:true,category:\"people\"},eye:{keywords:[\"face\",\"look\",\"see\",\"watch\",\"stare\"],char:\"👁\",fitzpatrick_scale:false,category:\"people\"},eyes:{keywords:[\"look\",\"watch\",\"stalk\",\"peek\",\"see\"],char:\"👀\",fitzpatrick_scale:false,category:\"people\"},brain:{keywords:[\"smart\",\"intelligent\"],char:\"🧠\",fitzpatrick_scale:false,category:\"people\"},bust_in_silhouette:{keywords:[\"user\",\"person\",\"human\"],char:\"👤\",fitzpatrick_scale:false,category:\"people\"},busts_in_silhouette:{keywords:[\"user\",\"person\",\"human\",\"group\",\"team\"],char:\"👥\",fitzpatrick_scale:false,category:\"people\"},speaking_head:{keywords:[\"user\",\"person\",\"human\",\"sing\",\"say\",\"talk\"],char:\"🗣\",fitzpatrick_scale:false,category:\"people\"},baby:{keywords:[\"child\",\"boy\",\"girl\",\"toddler\"],char:\"👶\",fitzpatrick_scale:true,category:\"people\"},child:{keywords:[\"gender-neutral\",\"young\"],char:\"🧒\",fitzpatrick_scale:true,category:\"people\"},boy:{keywords:[\"man\",\"male\",\"guy\",\"teenager\"],char:\"👦\",fitzpatrick_scale:true,category:\"people\"},girl:{keywords:[\"female\",\"woman\",\"teenager\"],char:\"👧\",fitzpatrick_scale:true,category:\"people\"},adult:{keywords:[\"gender-neutral\",\"person\"],char:\"🧑\",fitzpatrick_scale:true,category:\"people\"},man:{keywords:[\"mustache\",\"father\",\"dad\",\"guy\",\"classy\",\"sir\",\"moustache\"],char:\"👨\",fitzpatrick_scale:true,category:\"people\"},woman:{keywords:[\"female\",\"girls\",\"lady\"],char:\"👩\",fitzpatrick_scale:true,category:\"people\"},blonde_woman:{keywords:[\"woman\",\"female\",\"girl\",\"blonde\",\"person\"],char:\"👱‍♀️\",fitzpatrick_scale:true,category:\"people\"},blonde_man:{keywords:[\"man\",\"male\",\"boy\",\"blonde\",\"guy\",\"person\"],char:\"👱\",fitzpatrick_scale:true,category:\"people\"},bearded_person:{keywords:[\"person\",\"bewhiskered\"],char:\"🧔\",fitzpatrick_scale:true,category:\"people\"},older_adult:{keywords:[\"human\",\"elder\",\"senior\",\"gender-neutral\"],char:\"🧓\",fitzpatrick_scale:true,category:\"people\"},older_man:{keywords:[\"human\",\"male\",\"men\",\"old\",\"elder\",\"senior\"],char:\"👴\",fitzpatrick_scale:true,category:\"people\"},older_woman:{keywords:[\"human\",\"female\",\"women\",\"lady\",\"old\",\"elder\",\"senior\"],char:\"👵\",fitzpatrick_scale:true,category:\"people\"},man_with_gua_pi_mao:{keywords:[\"male\",\"boy\",\"chinese\"],char:\"👲\",fitzpatrick_scale:true,category:\"people\"},woman_with_headscarf:{keywords:[\"female\",\"hijab\",\"mantilla\",\"tichel\"],char:\"🧕\",fitzpatrick_scale:true,category:\"people\"},woman_with_turban:{keywords:[\"female\",\"indian\",\"hinduism\",\"arabs\",\"woman\"],char:\"👳‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_with_turban:{keywords:[\"male\",\"indian\",\"hinduism\",\"arabs\"],char:\"👳\",fitzpatrick_scale:true,category:\"people\"},policewoman:{keywords:[\"woman\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\",\"female\"],char:\"👮‍♀️\",fitzpatrick_scale:true,category:\"people\"},policeman:{keywords:[\"man\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\"],char:\"👮\",fitzpatrick_scale:true,category:\"people\"},construction_worker_woman:{keywords:[\"female\",\"human\",\"wip\",\"build\",\"construction\",\"worker\",\"labor\",\"woman\"],char:\"👷‍♀️\",fitzpatrick_scale:true,category:\"people\"},construction_worker_man:{keywords:[\"male\",\"human\",\"wip\",\"guy\",\"build\",\"construction\",\"worker\",\"labor\"],char:\"👷\",fitzpatrick_scale:true,category:\"people\"},guardswoman:{keywords:[\"uk\",\"gb\",\"british\",\"female\",\"royal\",\"woman\"],char:\"💂‍♀️\",fitzpatrick_scale:true,category:\"people\"},guardsman:{keywords:[\"uk\",\"gb\",\"british\",\"male\",\"guy\",\"royal\"],char:\"💂\",fitzpatrick_scale:true,category:\"people\"},female_detective:{keywords:[\"human\",\"spy\",\"detective\",\"female\",\"woman\"],char:\"🕵️‍♀️\",fitzpatrick_scale:true,category:\"people\"},male_detective:{keywords:[\"human\",\"spy\",\"detective\"],char:\"🕵\",fitzpatrick_scale:true,category:\"people\"},woman_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"woman\",\"human\"],char:\"👩‍⚕️\",fitzpatrick_scale:true,category:\"people\"},man_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"man\",\"human\"],char:\"👨‍⚕️\",fitzpatrick_scale:true,category:\"people\"},woman_farmer:{keywords:[\"rancher\",\"gardener\",\"woman\",\"human\"],char:\"👩‍🌾\",fitzpatrick_scale:true,category:\"people\"},man_farmer:{keywords:[\"rancher\",\"gardener\",\"man\",\"human\"],char:\"👨‍🌾\",fitzpatrick_scale:true,category:\"people\"},woman_cook:{keywords:[\"chef\",\"woman\",\"human\"],char:\"👩‍🍳\",fitzpatrick_scale:true,category:\"people\"},man_cook:{keywords:[\"chef\",\"man\",\"human\"],char:\"👨‍🍳\",fitzpatrick_scale:true,category:\"people\"},woman_student:{keywords:[\"graduate\",\"woman\",\"human\"],char:\"👩‍🎓\",fitzpatrick_scale:true,category:\"people\"},man_student:{keywords:[\"graduate\",\"man\",\"human\"],char:\"👨‍🎓\",fitzpatrick_scale:true,category:\"people\"},woman_singer:{keywords:[\"rockstar\",\"entertainer\",\"woman\",\"human\"],char:\"👩‍🎤\",fitzpatrick_scale:true,category:\"people\"},man_singer:{keywords:[\"rockstar\",\"entertainer\",\"man\",\"human\"],char:\"👨‍🎤\",fitzpatrick_scale:true,category:\"people\"},woman_teacher:{keywords:[\"instructor\",\"professor\",\"woman\",\"human\"],char:\"👩‍🏫\",fitzpatrick_scale:true,category:\"people\"},man_teacher:{keywords:[\"instructor\",\"professor\",\"man\",\"human\"],char:\"👨‍🏫\",fitzpatrick_scale:true,category:\"people\"},woman_factory_worker:{keywords:[\"assembly\",\"industrial\",\"woman\",\"human\"],char:\"👩‍🏭\",fitzpatrick_scale:true,category:\"people\"},man_factory_worker:{keywords:[\"assembly\",\"industrial\",\"man\",\"human\"],char:\"👨‍🏭\",fitzpatrick_scale:true,category:\"people\"},woman_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"woman\",\"human\",\"laptop\",\"computer\"],char:\"👩‍💻\",fitzpatrick_scale:true,category:\"people\"},man_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"man\",\"human\",\"laptop\",\"computer\"],char:\"👨‍💻\",fitzpatrick_scale:true,category:\"people\"},woman_office_worker:{keywords:[\"business\",\"manager\",\"woman\",\"human\"],char:\"👩‍💼\",fitzpatrick_scale:true,category:\"people\"},man_office_worker:{keywords:[\"business\",\"manager\",\"man\",\"human\"],char:\"👨‍💼\",fitzpatrick_scale:true,category:\"people\"},woman_mechanic:{keywords:[\"plumber\",\"woman\",\"human\",\"wrench\"],char:\"👩‍🔧\",fitzpatrick_scale:true,category:\"people\"},man_mechanic:{keywords:[\"plumber\",\"man\",\"human\",\"wrench\"],char:\"👨‍🔧\",fitzpatrick_scale:true,category:\"people\"},woman_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"woman\",\"human\"],char:\"👩‍🔬\",fitzpatrick_scale:true,category:\"people\"},man_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"man\",\"human\"],char:\"👨‍🔬\",fitzpatrick_scale:true,category:\"people\"},woman_artist:{keywords:[\"painter\",\"woman\",\"human\"],char:\"👩‍🎨\",fitzpatrick_scale:true,category:\"people\"},man_artist:{keywords:[\"painter\",\"man\",\"human\"],char:\"👨‍🎨\",fitzpatrick_scale:true,category:\"people\"},woman_firefighter:{keywords:[\"fireman\",\"woman\",\"human\"],char:\"👩‍🚒\",fitzpatrick_scale:true,category:\"people\"},man_firefighter:{keywords:[\"fireman\",\"man\",\"human\"],char:\"👨‍🚒\",fitzpatrick_scale:true,category:\"people\"},woman_pilot:{keywords:[\"aviator\",\"plane\",\"woman\",\"human\"],char:\"👩‍✈️\",fitzpatrick_scale:true,category:\"people\"},man_pilot:{keywords:[\"aviator\",\"plane\",\"man\",\"human\"],char:\"👨‍✈️\",fitzpatrick_scale:true,category:\"people\"},woman_astronaut:{keywords:[\"space\",\"rocket\",\"woman\",\"human\"],char:\"👩‍🚀\",fitzpatrick_scale:true,category:\"people\"},man_astronaut:{keywords:[\"space\",\"rocket\",\"man\",\"human\"],char:\"👨‍🚀\",fitzpatrick_scale:true,category:\"people\"},woman_judge:{keywords:[\"justice\",\"court\",\"woman\",\"human\"],char:\"👩‍⚖️\",fitzpatrick_scale:true,category:\"people\"},man_judge:{keywords:[\"justice\",\"court\",\"man\",\"human\"],char:\"👨‍⚖️\",fitzpatrick_scale:true,category:\"people\"},woman_superhero:{keywords:[\"woman\",\"female\",\"good\",\"heroine\",\"superpowers\"],char:\"🦸‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_superhero:{keywords:[\"man\",\"male\",\"good\",\"hero\",\"superpowers\"],char:\"🦸‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_supervillain:{keywords:[\"woman\",\"female\",\"evil\",\"bad\",\"criminal\",\"heroine\",\"superpowers\"],char:\"🦹‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_supervillain:{keywords:[\"man\",\"male\",\"evil\",\"bad\",\"criminal\",\"hero\",\"superpowers\"],char:\"🦹‍♂️\",fitzpatrick_scale:true,category:\"people\"},mrs_claus:{keywords:[\"woman\",\"female\",\"xmas\",\"mother christmas\"],char:\"🤶\",fitzpatrick_scale:true,category:\"people\"},santa:{keywords:[\"festival\",\"man\",\"male\",\"xmas\",\"father christmas\"],char:\"🎅\",fitzpatrick_scale:true,category:\"people\"},sorceress:{keywords:[\"woman\",\"female\",\"mage\",\"witch\"],char:\"🧙‍♀️\",fitzpatrick_scale:true,category:\"people\"},wizard:{keywords:[\"man\",\"male\",\"mage\",\"sorcerer\"],char:\"🧙‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_elf:{keywords:[\"woman\",\"female\"],char:\"🧝‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_elf:{keywords:[\"man\",\"male\"],char:\"🧝‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_vampire:{keywords:[\"woman\",\"female\"],char:\"🧛‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_vampire:{keywords:[\"man\",\"male\",\"dracula\"],char:\"🧛‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_zombie:{keywords:[\"woman\",\"female\",\"undead\",\"walking dead\"],char:\"🧟‍♀️\",fitzpatrick_scale:false,category:\"people\"},man_zombie:{keywords:[\"man\",\"male\",\"dracula\",\"undead\",\"walking dead\"],char:\"🧟‍♂️\",fitzpatrick_scale:false,category:\"people\"},woman_genie:{keywords:[\"woman\",\"female\"],char:\"🧞‍♀️\",fitzpatrick_scale:false,category:\"people\"},man_genie:{keywords:[\"man\",\"male\"],char:\"🧞‍♂️\",fitzpatrick_scale:false,category:\"people\"},mermaid:{keywords:[\"woman\",\"female\",\"merwoman\",\"ariel\"],char:\"🧜‍♀️\",fitzpatrick_scale:true,category:\"people\"},merman:{keywords:[\"man\",\"male\",\"triton\"],char:\"🧜‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_fairy:{keywords:[\"woman\",\"female\"],char:\"🧚‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_fairy:{keywords:[\"man\",\"male\"],char:\"🧚‍♂️\",fitzpatrick_scale:true,category:\"people\"},angel:{keywords:[\"heaven\",\"wings\",\"halo\"],char:\"👼\",fitzpatrick_scale:true,category:\"people\"},pregnant_woman:{keywords:[\"baby\"],char:\"🤰\",fitzpatrick_scale:true,category:\"people\"},breastfeeding:{keywords:[\"nursing\",\"baby\"],char:\"🤱\",fitzpatrick_scale:true,category:\"people\"},princess:{keywords:[\"girl\",\"woman\",\"female\",\"blond\",\"crown\",\"royal\",\"queen\"],char:\"👸\",fitzpatrick_scale:true,category:\"people\"},prince:{keywords:[\"boy\",\"man\",\"male\",\"crown\",\"royal\",\"king\"],char:\"🤴\",fitzpatrick_scale:true,category:\"people\"},bride_with_veil:{keywords:[\"couple\",\"marriage\",\"wedding\",\"woman\",\"bride\"],char:\"👰\",fitzpatrick_scale:true,category:\"people\"},man_in_tuxedo:{keywords:[\"couple\",\"marriage\",\"wedding\",\"groom\"],char:\"🤵\",fitzpatrick_scale:true,category:\"people\"},running_woman:{keywords:[\"woman\",\"walking\",\"exercise\",\"race\",\"running\",\"female\"],char:\"🏃‍♀️\",fitzpatrick_scale:true,category:\"people\"},running_man:{keywords:[\"man\",\"walking\",\"exercise\",\"race\",\"running\"],char:\"🏃\",fitzpatrick_scale:true,category:\"people\"},walking_woman:{keywords:[\"human\",\"feet\",\"steps\",\"woman\",\"female\"],char:\"🚶‍♀️\",fitzpatrick_scale:true,category:\"people\"},walking_man:{keywords:[\"human\",\"feet\",\"steps\"],char:\"🚶\",fitzpatrick_scale:true,category:\"people\"},dancer:{keywords:[\"female\",\"girl\",\"woman\",\"fun\"],char:\"💃\",fitzpatrick_scale:true,category:\"people\"},man_dancing:{keywords:[\"male\",\"boy\",\"fun\",\"dancer\"],char:\"🕺\",fitzpatrick_scale:true,category:\"people\"},dancing_women:{keywords:[\"female\",\"bunny\",\"women\",\"girls\"],char:\"👯\",fitzpatrick_scale:false,category:\"people\"},dancing_men:{keywords:[\"male\",\"bunny\",\"men\",\"boys\"],char:\"👯‍♂️\",fitzpatrick_scale:false,category:\"people\"},couple:{keywords:[\"pair\",\"people\",\"human\",\"love\",\"date\",\"dating\",\"like\",\"affection\",\"valentines\",\"marriage\"],char:\"👫\",fitzpatrick_scale:false,category:\"people\"},two_men_holding_hands:{keywords:[\"pair\",\"couple\",\"love\",\"like\",\"bromance\",\"friendship\",\"people\",\"human\"],char:\"👬\",fitzpatrick_scale:false,category:\"people\"},two_women_holding_hands:{keywords:[\"pair\",\"friendship\",\"couple\",\"love\",\"like\",\"female\",\"people\",\"human\"],char:\"👭\",fitzpatrick_scale:false,category:\"people\"},bowing_woman:{keywords:[\"woman\",\"female\",\"girl\"],char:\"🙇‍♀️\",fitzpatrick_scale:true,category:\"people\"},bowing_man:{keywords:[\"man\",\"male\",\"boy\"],char:\"🙇\",fitzpatrick_scale:true,category:\"people\"},man_facepalming:{keywords:[\"man\",\"male\",\"boy\",\"disbelief\"],char:\"🤦‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_facepalming:{keywords:[\"woman\",\"female\",\"girl\",\"disbelief\"],char:\"🤦‍♀️\",fitzpatrick_scale:true,category:\"people\"},woman_shrugging:{keywords:[\"woman\",\"female\",\"girl\",\"confused\",\"indifferent\",\"doubt\"],char:\"🤷\",fitzpatrick_scale:true,category:\"people\"},man_shrugging:{keywords:[\"man\",\"male\",\"boy\",\"confused\",\"indifferent\",\"doubt\"],char:\"🤷‍♂️\",fitzpatrick_scale:true,category:\"people\"},tipping_hand_woman:{keywords:[\"female\",\"girl\",\"woman\",\"human\",\"information\"],char:\"💁\",fitzpatrick_scale:true,category:\"people\"},tipping_hand_man:{keywords:[\"male\",\"boy\",\"man\",\"human\",\"information\"],char:\"💁‍♂️\",fitzpatrick_scale:true,category:\"people\"},no_good_woman:{keywords:[\"female\",\"girl\",\"woman\",\"nope\"],char:\"🙅\",fitzpatrick_scale:true,category:\"people\"},no_good_man:{keywords:[\"male\",\"boy\",\"man\",\"nope\"],char:\"🙅‍♂️\",fitzpatrick_scale:true,category:\"people\"},ok_woman:{keywords:[\"women\",\"girl\",\"female\",\"pink\",\"human\",\"woman\"],char:\"🙆\",fitzpatrick_scale:true,category:\"people\"},ok_man:{keywords:[\"men\",\"boy\",\"male\",\"blue\",\"human\",\"man\"],char:\"🙆‍♂️\",fitzpatrick_scale:true,category:\"people\"},raising_hand_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:\"🙋\",fitzpatrick_scale:true,category:\"people\"},raising_hand_man:{keywords:[\"male\",\"boy\",\"man\"],char:\"🙋‍♂️\",fitzpatrick_scale:true,category:\"people\"},pouting_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:\"🙎\",fitzpatrick_scale:true,category:\"people\"},pouting_man:{keywords:[\"male\",\"boy\",\"man\"],char:\"🙎‍♂️\",fitzpatrick_scale:true,category:\"people\"},frowning_woman:{keywords:[\"female\",\"girl\",\"woman\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:\"🙍\",fitzpatrick_scale:true,category:\"people\"},frowning_man:{keywords:[\"male\",\"boy\",\"man\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:\"🙍‍♂️\",fitzpatrick_scale:true,category:\"people\"},haircut_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:\"💇\",fitzpatrick_scale:true,category:\"people\"},haircut_man:{keywords:[\"male\",\"boy\",\"man\"],char:\"💇‍♂️\",fitzpatrick_scale:true,category:\"people\"},massage_woman:{keywords:[\"female\",\"girl\",\"woman\",\"head\"],char:\"💆\",fitzpatrick_scale:true,category:\"people\"},massage_man:{keywords:[\"male\",\"boy\",\"man\",\"head\"],char:\"💆‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_in_steamy_room:{keywords:[\"female\",\"woman\",\"spa\",\"steamroom\",\"sauna\"],char:\"🧖‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_in_steamy_room:{keywords:[\"male\",\"man\",\"spa\",\"steamroom\",\"sauna\"],char:\"🧖‍♂️\",fitzpatrick_scale:true,category:\"people\"},couple_with_heart_woman_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:\"💑\",fitzpatrick_scale:false,category:\"people\"},couple_with_heart_woman_woman:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:\"👩‍❤️‍👩\",fitzpatrick_scale:false,category:\"people\"},couple_with_heart_man_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:\"👨‍❤️‍👨\",fitzpatrick_scale:false,category:\"people\"},couplekiss_man_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:\"💏\",fitzpatrick_scale:false,category:\"people\"},couplekiss_woman_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:\"👩‍❤️‍💋‍👩\",fitzpatrick_scale:false,category:\"people\"},couplekiss_man_man:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:\"👨‍❤️‍💋‍👨\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy:{keywords:[\"home\",\"parents\",\"child\",\"mom\",\"dad\",\"father\",\"mother\",\"people\",\"human\"],char:\"👪\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"child\"],char:\"👨‍👩‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👩‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👩‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👩‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_man_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_man_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_man_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👩‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👩‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👩‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👩‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👩‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👨‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👨‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👨‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👨‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👨‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},yarn:{keywords:[\"ball\",\"crochet\",\"knit\"],char:\"🧶\",fitzpatrick_scale:false,category:\"people\"},thread:{keywords:[\"needle\",\"sewing\",\"spool\",\"string\"],char:\"🧵\",fitzpatrick_scale:false,category:\"people\"},coat:{keywords:[\"jacket\"],char:\"🧥\",fitzpatrick_scale:false,category:\"people\"},labcoat:{keywords:[\"doctor\",\"experiment\",\"scientist\",\"chemist\"],char:\"🥼\",fitzpatrick_scale:false,category:\"people\"},womans_clothes:{keywords:[\"fashion\",\"shopping_bags\",\"female\"],char:\"👚\",fitzpatrick_scale:false,category:\"people\"},tshirt:{keywords:[\"fashion\",\"cloth\",\"casual\",\"shirt\",\"tee\"],char:\"👕\",fitzpatrick_scale:false,category:\"people\"},jeans:{keywords:[\"fashion\",\"shopping\"],char:\"👖\",fitzpatrick_scale:false,category:\"people\"},necktie:{keywords:[\"shirt\",\"suitup\",\"formal\",\"fashion\",\"cloth\",\"business\"],char:\"👔\",fitzpatrick_scale:false,category:\"people\"},dress:{keywords:[\"clothes\",\"fashion\",\"shopping\"],char:\"👗\",fitzpatrick_scale:false,category:\"people\"},bikini:{keywords:[\"swimming\",\"female\",\"woman\",\"girl\",\"fashion\",\"beach\",\"summer\"],char:\"👙\",fitzpatrick_scale:false,category:\"people\"},kimono:{keywords:[\"dress\",\"fashion\",\"women\",\"female\",\"japanese\"],char:\"👘\",fitzpatrick_scale:false,category:\"people\"},lipstick:{keywords:[\"female\",\"girl\",\"fashion\",\"woman\"],char:\"💄\",fitzpatrick_scale:false,category:\"people\"},kiss:{keywords:[\"face\",\"lips\",\"love\",\"like\",\"affection\",\"valentines\"],char:\"💋\",fitzpatrick_scale:false,category:\"people\"},footprints:{keywords:[\"feet\",\"tracking\",\"walking\",\"beach\"],char:\"👣\",fitzpatrick_scale:false,category:\"people\"},flat_shoe:{keywords:[\"ballet\",\"slip-on\",\"slipper\"],char:\"🥿\",fitzpatrick_scale:false,category:\"people\"},high_heel:{keywords:[\"fashion\",\"shoes\",\"female\",\"pumps\",\"stiletto\"],char:\"👠\",fitzpatrick_scale:false,category:\"people\"},sandal:{keywords:[\"shoes\",\"fashion\",\"flip flops\"],char:\"👡\",fitzpatrick_scale:false,category:\"people\"},boot:{keywords:[\"shoes\",\"fashion\"],char:\"👢\",fitzpatrick_scale:false,category:\"people\"},mans_shoe:{keywords:[\"fashion\",\"male\"],char:\"👞\",fitzpatrick_scale:false,category:\"people\"},athletic_shoe:{keywords:[\"shoes\",\"sports\",\"sneakers\"],char:\"👟\",fitzpatrick_scale:false,category:\"people\"},hiking_boot:{keywords:[\"backpacking\",\"camping\",\"hiking\"],char:\"🥾\",fitzpatrick_scale:false,category:\"people\"},socks:{keywords:[\"stockings\",\"clothes\"],char:\"🧦\",fitzpatrick_scale:false,category:\"people\"},gloves:{keywords:[\"hands\",\"winter\",\"clothes\"],char:\"🧤\",fitzpatrick_scale:false,category:\"people\"},scarf:{keywords:[\"neck\",\"winter\",\"clothes\"],char:\"🧣\",fitzpatrick_scale:false,category:\"people\"},womans_hat:{keywords:[\"fashion\",\"accessories\",\"female\",\"lady\",\"spring\"],char:\"👒\",fitzpatrick_scale:false,category:\"people\"},tophat:{keywords:[\"magic\",\"gentleman\",\"classy\",\"circus\"],char:\"🎩\",fitzpatrick_scale:false,category:\"people\"},billed_hat:{keywords:[\"cap\",\"baseball\"],char:\"🧢\",fitzpatrick_scale:false,category:\"people\"},rescue_worker_helmet:{keywords:[\"construction\",\"build\"],char:\"⛑\",fitzpatrick_scale:false,category:\"people\"},mortar_board:{keywords:[\"school\",\"college\",\"degree\",\"university\",\"graduation\",\"cap\",\"hat\",\"legal\",\"learn\",\"education\"],char:\"🎓\",fitzpatrick_scale:false,category:\"people\"},crown:{keywords:[\"king\",\"kod\",\"leader\",\"royalty\",\"lord\"],char:\"👑\",fitzpatrick_scale:false,category:\"people\"},school_satchel:{keywords:[\"student\",\"education\",\"bag\",\"backpack\"],char:\"🎒\",fitzpatrick_scale:false,category:\"people\"},luggage:{keywords:[\"packing\",\"travel\"],char:\"🧳\",fitzpatrick_scale:false,category:\"people\"},pouch:{keywords:[\"bag\",\"accessories\",\"shopping\"],char:\"👝\",fitzpatrick_scale:false,category:\"people\"},purse:{keywords:[\"fashion\",\"accessories\",\"money\",\"sales\",\"shopping\"],char:\"👛\",fitzpatrick_scale:false,category:\"people\"},handbag:{keywords:[\"fashion\",\"accessory\",\"accessories\",\"shopping\"],char:\"👜\",fitzpatrick_scale:false,category:\"people\"},briefcase:{keywords:[\"business\",\"documents\",\"work\",\"law\",\"legal\",\"job\",\"career\"],char:\"💼\",fitzpatrick_scale:false,category:\"people\"},eyeglasses:{keywords:[\"fashion\",\"accessories\",\"eyesight\",\"nerdy\",\"dork\",\"geek\"],char:\"👓\",fitzpatrick_scale:false,category:\"people\"},dark_sunglasses:{keywords:[\"face\",\"cool\",\"accessories\"],char:\"🕶\",fitzpatrick_scale:false,category:\"people\"},goggles:{keywords:[\"eyes\",\"protection\",\"safety\"],char:\"🥽\",fitzpatrick_scale:false,category:\"people\"},ring:{keywords:[\"wedding\",\"propose\",\"marriage\",\"valentines\",\"diamond\",\"fashion\",\"jewelry\",\"gem\",\"engagement\"],char:\"💍\",fitzpatrick_scale:false,category:\"people\"},closed_umbrella:{keywords:[\"weather\",\"rain\",\"drizzle\"],char:\"🌂\",fitzpatrick_scale:false,category:\"people\"},dog:{keywords:[\"animal\",\"friend\",\"nature\",\"woof\",\"puppy\",\"pet\",\"faithful\"],char:\"🐶\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cat:{keywords:[\"animal\",\"meow\",\"nature\",\"pet\",\"kitten\"],char:\"🐱\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse:{keywords:[\"animal\",\"nature\",\"cheese_wedge\",\"rodent\"],char:\"🐭\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hamster:{keywords:[\"animal\",\"nature\"],char:\"🐹\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit:{keywords:[\"animal\",\"nature\",\"pet\",\"spring\",\"magic\",\"bunny\"],char:\"🐰\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fox_face:{keywords:[\"animal\",\"nature\",\"face\"],char:\"🦊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bear:{keywords:[\"animal\",\"nature\",\"wild\"],char:\"🐻\",fitzpatrick_scale:false,category:\"animals_and_nature\"},panda_face:{keywords:[\"animal\",\"nature\",\"panda\"],char:\"🐼\",fitzpatrick_scale:false,category:\"animals_and_nature\"},koala:{keywords:[\"animal\",\"nature\"],char:\"🐨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger:{keywords:[\"animal\",\"cat\",\"danger\",\"wild\",\"nature\",\"roar\"],char:\"🐯\",fitzpatrick_scale:false,category:\"animals_and_nature\"},lion:{keywords:[\"animal\",\"nature\"],char:\"🦁\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cow:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:\"🐮\",fitzpatrick_scale:false,category:\"animals_and_nature\"},pig:{keywords:[\"animal\",\"oink\",\"nature\"],char:\"🐷\",fitzpatrick_scale:false,category:\"animals_and_nature\"},pig_nose:{keywords:[\"animal\",\"oink\"],char:\"🐽\",fitzpatrick_scale:false,category:\"animals_and_nature\"},frog:{keywords:[\"animal\",\"nature\",\"croak\",\"toad\"],char:\"🐸\",fitzpatrick_scale:false,category:\"animals_and_nature\"},squid:{keywords:[\"animal\",\"nature\",\"ocean\",\"sea\"],char:\"🦑\",fitzpatrick_scale:false,category:\"animals_and_nature\"},octopus:{keywords:[\"animal\",\"creature\",\"ocean\",\"sea\",\"nature\",\"beach\"],char:\"🐙\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shrimp:{keywords:[\"animal\",\"ocean\",\"nature\",\"seafood\"],char:\"🦐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey_face:{keywords:[\"animal\",\"nature\",\"circus\"],char:\"🐵\",fitzpatrick_scale:false,category:\"animals_and_nature\"},gorilla:{keywords:[\"animal\",\"nature\",\"circus\"],char:\"🦍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},see_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"haha\"],char:\"🙈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hear_no_evil:{keywords:[\"animal\",\"monkey\",\"nature\"],char:\"🙉\",fitzpatrick_scale:false,category:\"animals_and_nature\"},speak_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"omg\"],char:\"🙊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey:{keywords:[\"animal\",\"nature\",\"banana\",\"circus\"],char:\"🐒\",fitzpatrick_scale:false,category:\"animals_and_nature\"},chicken:{keywords:[\"animal\",\"cluck\",\"nature\",\"bird\"],char:\"🐔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},penguin:{keywords:[\"animal\",\"nature\"],char:\"🐧\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bird:{keywords:[\"animal\",\"nature\",\"fly\",\"tweet\",\"spring\"],char:\"🐦\",fitzpatrick_scale:false,category:\"animals_and_nature\"},baby_chick:{keywords:[\"animal\",\"chicken\",\"bird\"],char:\"🐤\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hatching_chick:{keywords:[\"animal\",\"chicken\",\"egg\",\"born\",\"baby\",\"bird\"],char:\"🐣\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hatched_chick:{keywords:[\"animal\",\"chicken\",\"baby\",\"bird\"],char:\"🐥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},duck:{keywords:[\"animal\",\"nature\",\"bird\",\"mallard\"],char:\"🦆\",fitzpatrick_scale:false,category:\"animals_and_nature\"},eagle:{keywords:[\"animal\",\"nature\",\"bird\"],char:\"🦅\",fitzpatrick_scale:false,category:\"animals_and_nature\"},owl:{keywords:[\"animal\",\"nature\",\"bird\",\"hoot\"],char:\"🦉\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bat:{keywords:[\"animal\",\"nature\",\"blind\",\"vampire\"],char:\"🦇\",fitzpatrick_scale:false,category:\"animals_and_nature\"},wolf:{keywords:[\"animal\",\"nature\",\"wild\"],char:\"🐺\",fitzpatrick_scale:false,category:\"animals_and_nature\"},boar:{keywords:[\"animal\",\"nature\"],char:\"🐗\",fitzpatrick_scale:false,category:\"animals_and_nature\"},horse:{keywords:[\"animal\",\"brown\",\"nature\"],char:\"🐴\",fitzpatrick_scale:false,category:\"animals_and_nature\"},unicorn:{keywords:[\"animal\",\"nature\",\"mystical\"],char:\"🦄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},honeybee:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\",\"spring\",\"honey\"],char:\"🐝\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bug:{keywords:[\"animal\",\"insect\",\"nature\",\"worm\"],char:\"🐛\",fitzpatrick_scale:false,category:\"animals_and_nature\"},butterfly:{keywords:[\"animal\",\"insect\",\"nature\",\"caterpillar\"],char:\"🦋\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snail:{keywords:[\"slow\",\"animal\",\"shell\"],char:\"🐌\",fitzpatrick_scale:false,category:\"animals_and_nature\"},beetle:{keywords:[\"animal\",\"insect\",\"nature\",\"ladybug\"],char:\"🐞\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ant:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\"],char:\"🐜\",fitzpatrick_scale:false,category:\"animals_and_nature\"},grasshopper:{keywords:[\"animal\",\"cricket\",\"chirp\"],char:\"🦗\",fitzpatrick_scale:false,category:\"animals_and_nature\"},spider:{keywords:[\"animal\",\"arachnid\"],char:\"🕷\",fitzpatrick_scale:false,category:\"animals_and_nature\"},scorpion:{keywords:[\"animal\",\"arachnid\"],char:\"🦂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},crab:{keywords:[\"animal\",\"crustacean\"],char:\"🦀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snake:{keywords:[\"animal\",\"evil\",\"nature\",\"hiss\",\"python\"],char:\"🐍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},lizard:{keywords:[\"animal\",\"nature\",\"reptile\"],char:\"🦎\",fitzpatrick_scale:false,category:\"animals_and_nature\"},\"t-rex\":{keywords:[\"animal\",\"nature\",\"dinosaur\",\"tyrannosaurus\",\"extinct\"],char:\"🦖\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sauropod:{keywords:[\"animal\",\"nature\",\"dinosaur\",\"brachiosaurus\",\"brontosaurus\",\"diplodocus\",\"extinct\"],char:\"🦕\",fitzpatrick_scale:false,category:\"animals_and_nature\"},turtle:{keywords:[\"animal\",\"slow\",\"nature\",\"tortoise\"],char:\"🐢\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tropical_fish:{keywords:[\"animal\",\"swim\",\"ocean\",\"beach\",\"nemo\"],char:\"🐠\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fish:{keywords:[\"animal\",\"food\",\"nature\"],char:\"🐟\",fitzpatrick_scale:false,category:\"animals_and_nature\"},blowfish:{keywords:[\"animal\",\"nature\",\"food\",\"sea\",\"ocean\"],char:\"🐡\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dolphin:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"flipper\",\"fins\",\"beach\"],char:\"🐬\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shark:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"jaws\",\"fins\",\"beach\"],char:\"🦈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},whale:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:\"🐳\",fitzpatrick_scale:false,category:\"animals_and_nature\"},whale2:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:\"🐋\",fitzpatrick_scale:false,category:\"animals_and_nature\"},crocodile:{keywords:[\"animal\",\"nature\",\"reptile\",\"lizard\",\"alligator\"],char:\"🐊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},leopard:{keywords:[\"animal\",\"nature\"],char:\"🐆\",fitzpatrick_scale:false,category:\"animals_and_nature\"},zebra:{keywords:[\"animal\",\"nature\",\"stripes\",\"safari\"],char:\"🦓\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger2:{keywords:[\"animal\",\"nature\",\"roar\"],char:\"🐅\",fitzpatrick_scale:false,category:\"animals_and_nature\"},water_buffalo:{keywords:[\"animal\",\"nature\",\"ox\",\"cow\"],char:\"🐃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ox:{keywords:[\"animal\",\"cow\",\"beef\"],char:\"🐂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cow2:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:\"🐄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},deer:{keywords:[\"animal\",\"nature\",\"horns\",\"venison\"],char:\"🦌\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dromedary_camel:{keywords:[\"animal\",\"hot\",\"desert\",\"hump\"],char:\"🐪\",fitzpatrick_scale:false,category:\"animals_and_nature\"},camel:{keywords:[\"animal\",\"nature\",\"hot\",\"desert\",\"hump\"],char:\"🐫\",fitzpatrick_scale:false,category:\"animals_and_nature\"},giraffe:{keywords:[\"animal\",\"nature\",\"spots\",\"safari\"],char:\"🦒\",fitzpatrick_scale:false,category:\"animals_and_nature\"},elephant:{keywords:[\"animal\",\"nature\",\"nose\",\"th\",\"circus\"],char:\"🐘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rhinoceros:{keywords:[\"animal\",\"nature\",\"horn\"],char:\"🦏\",fitzpatrick_scale:false,category:\"animals_and_nature\"},goat:{keywords:[\"animal\",\"nature\"],char:\"🐐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ram:{keywords:[\"animal\",\"sheep\",\"nature\"],char:\"🐏\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sheep:{keywords:[\"animal\",\"nature\",\"wool\",\"shipit\"],char:\"🐑\",fitzpatrick_scale:false,category:\"animals_and_nature\"},racehorse:{keywords:[\"animal\",\"gamble\",\"luck\"],char:\"🐎\",fitzpatrick_scale:false,category:\"animals_and_nature\"},pig2:{keywords:[\"animal\",\"nature\"],char:\"🐖\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rat:{keywords:[\"animal\",\"mouse\",\"rodent\"],char:\"🐀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse2:{keywords:[\"animal\",\"nature\",\"rodent\"],char:\"🐁\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rooster:{keywords:[\"animal\",\"nature\",\"chicken\"],char:\"🐓\",fitzpatrick_scale:false,category:\"animals_and_nature\"},turkey:{keywords:[\"animal\",\"bird\"],char:\"🦃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dove:{keywords:[\"animal\",\"bird\"],char:\"🕊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dog2:{keywords:[\"animal\",\"nature\",\"friend\",\"doge\",\"pet\",\"faithful\"],char:\"🐕\",fitzpatrick_scale:false,category:\"animals_and_nature\"},poodle:{keywords:[\"dog\",\"animal\",\"101\",\"nature\",\"pet\"],char:\"🐩\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cat2:{keywords:[\"animal\",\"meow\",\"pet\",\"cats\"],char:\"🐈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit2:{keywords:[\"animal\",\"nature\",\"pet\",\"magic\",\"spring\"],char:\"🐇\",fitzpatrick_scale:false,category:\"animals_and_nature\"},chipmunk:{keywords:[\"animal\",\"nature\",\"rodent\",\"squirrel\"],char:\"🐿\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hedgehog:{keywords:[\"animal\",\"nature\",\"spiny\"],char:\"🦔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},raccoon:{keywords:[\"animal\",\"nature\"],char:\"🦝\",fitzpatrick_scale:false,category:\"animals_and_nature\"},llama:{keywords:[\"animal\",\"nature\",\"alpaca\"],char:\"🦙\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hippopotamus:{keywords:[\"animal\",\"nature\"],char:\"🦛\",fitzpatrick_scale:false,category:\"animals_and_nature\"},kangaroo:{keywords:[\"animal\",\"nature\",\"australia\",\"joey\",\"hop\",\"marsupial\"],char:\"🦘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},badger:{keywords:[\"animal\",\"nature\",\"honey\"],char:\"🦡\",fitzpatrick_scale:false,category:\"animals_and_nature\"},swan:{keywords:[\"animal\",\"nature\",\"bird\"],char:\"🦢\",fitzpatrick_scale:false,category:\"animals_and_nature\"},peacock:{keywords:[\"animal\",\"nature\",\"peahen\",\"bird\"],char:\"🦚\",fitzpatrick_scale:false,category:\"animals_and_nature\"},parrot:{keywords:[\"animal\",\"nature\",\"bird\",\"pirate\",\"talk\"],char:\"🦜\",fitzpatrick_scale:false,category:\"animals_and_nature\"},lobster:{keywords:[\"animal\",\"nature\",\"bisque\",\"claws\",\"seafood\"],char:\"🦞\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mosquito:{keywords:[\"animal\",\"nature\",\"insect\",\"malaria\"],char:\"🦟\",fitzpatrick_scale:false,category:\"animals_and_nature\"},paw_prints:{keywords:[\"animal\",\"tracking\",\"footprints\",\"dog\",\"cat\",\"pet\",\"feet\"],char:\"🐾\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:\"🐉\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon_face:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:\"🐲\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cactus:{keywords:[\"vegetable\",\"plant\",\"nature\"],char:\"🌵\",fitzpatrick_scale:false,category:\"animals_and_nature\"},christmas_tree:{keywords:[\"festival\",\"vacation\",\"december\",\"xmas\",\"celebration\"],char:\"🎄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},evergreen_tree:{keywords:[\"plant\",\"nature\"],char:\"🌲\",fitzpatrick_scale:false,category:\"animals_and_nature\"},deciduous_tree:{keywords:[\"plant\",\"nature\"],char:\"🌳\",fitzpatrick_scale:false,category:\"animals_and_nature\"},palm_tree:{keywords:[\"plant\",\"vegetable\",\"nature\",\"summer\",\"beach\",\"mojito\",\"tropical\"],char:\"🌴\",fitzpatrick_scale:false,category:\"animals_and_nature\"},seedling:{keywords:[\"plant\",\"nature\",\"grass\",\"lawn\",\"spring\"],char:\"🌱\",fitzpatrick_scale:false,category:\"animals_and_nature\"},herb:{keywords:[\"vegetable\",\"plant\",\"medicine\",\"weed\",\"grass\",\"lawn\"],char:\"🌿\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shamrock:{keywords:[\"vegetable\",\"plant\",\"nature\",\"irish\",\"clover\"],char:\"☘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},four_leaf_clover:{keywords:[\"vegetable\",\"plant\",\"nature\",\"lucky\",\"irish\"],char:\"🍀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bamboo:{keywords:[\"plant\",\"nature\",\"vegetable\",\"panda\",\"pine_decoration\"],char:\"🎍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tanabata_tree:{keywords:[\"plant\",\"nature\",\"branch\",\"summer\"],char:\"🎋\",fitzpatrick_scale:false,category:\"animals_and_nature\"},leaves:{keywords:[\"nature\",\"plant\",\"tree\",\"vegetable\",\"grass\",\"lawn\",\"spring\"],char:\"🍃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fallen_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"leaves\"],char:\"🍂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},maple_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"ca\",\"fall\"],char:\"🍁\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ear_of_rice:{keywords:[\"nature\",\"plant\"],char:\"🌾\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hibiscus:{keywords:[\"plant\",\"vegetable\",\"flowers\",\"beach\"],char:\"🌺\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sunflower:{keywords:[\"nature\",\"plant\",\"fall\"],char:\"🌻\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rose:{keywords:[\"flowers\",\"valentines\",\"love\",\"spring\"],char:\"🌹\",fitzpatrick_scale:false,category:\"animals_and_nature\"},wilted_flower:{keywords:[\"plant\",\"nature\",\"flower\"],char:\"🥀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tulip:{keywords:[\"flowers\",\"plant\",\"nature\",\"summer\",\"spring\"],char:\"🌷\",fitzpatrick_scale:false,category:\"animals_and_nature\"},blossom:{keywords:[\"nature\",\"flowers\",\"yellow\"],char:\"🌼\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cherry_blossom:{keywords:[\"nature\",\"plant\",\"spring\",\"flower\"],char:\"🌸\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bouquet:{keywords:[\"flowers\",\"nature\",\"spring\"],char:\"💐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mushroom:{keywords:[\"plant\",\"vegetable\"],char:\"🍄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},chestnut:{keywords:[\"food\",\"squirrel\"],char:\"🌰\",fitzpatrick_scale:false,category:\"animals_and_nature\"},jack_o_lantern:{keywords:[\"halloween\",\"light\",\"pumpkin\",\"creepy\",\"fall\"],char:\"🎃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shell:{keywords:[\"nature\",\"sea\",\"beach\"],char:\"🐚\",fitzpatrick_scale:false,category:\"animals_and_nature\"},spider_web:{keywords:[\"animal\",\"insect\",\"arachnid\",\"silk\"],char:\"🕸\",fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_americas:{keywords:[\"globe\",\"world\",\"USA\",\"international\"],char:\"🌎\",fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_africa:{keywords:[\"globe\",\"world\",\"international\"],char:\"🌍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_asia:{keywords:[\"globe\",\"world\",\"east\",\"international\"],char:\"🌏\",fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon:{keywords:[\"nature\",\"yellow\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌕\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_gibbous_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\",\"waxing_gibbous_moon\"],char:\"🌖\",fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌗\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌑\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌒\",fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌓\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_gibbous_moon:{keywords:[\"nature\",\"night\",\"sky\",\"gray\",\"twilight\",\"planet\",\"space\",\"evening\",\"sleep\"],char:\"🌔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌚\",fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌝\",fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌛\",fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌜\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_with_face:{keywords:[\"nature\",\"morning\",\"sky\"],char:\"🌞\",fitzpatrick_scale:false,category:\"animals_and_nature\"},crescent_moon:{keywords:[\"night\",\"sleep\",\"sky\",\"evening\",\"magic\"],char:\"🌙\",fitzpatrick_scale:false,category:\"animals_and_nature\"},star:{keywords:[\"night\",\"yellow\"],char:\"⭐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},star2:{keywords:[\"night\",\"sparkle\",\"awesome\",\"good\",\"magic\"],char:\"🌟\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dizzy:{keywords:[\"star\",\"sparkle\",\"shoot\",\"magic\"],char:\"💫\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sparkles:{keywords:[\"stars\",\"shine\",\"shiny\",\"cool\",\"awesome\",\"good\",\"magic\"],char:\"✨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},comet:{keywords:[\"space\"],char:\"☄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sunny:{keywords:[\"weather\",\"nature\",\"brightness\",\"summer\",\"beach\",\"spring\"],char:\"☀️\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_small_cloud:{keywords:[\"weather\"],char:\"🌤\",fitzpatrick_scale:false,category:\"animals_and_nature\"},partly_sunny:{keywords:[\"weather\",\"nature\",\"cloudy\",\"morning\",\"fall\",\"spring\"],char:\"⛅\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_large_cloud:{keywords:[\"weather\"],char:\"🌥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_rain_cloud:{keywords:[\"weather\"],char:\"🌦\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud:{keywords:[\"weather\",\"sky\"],char:\"☁️\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_rain:{keywords:[\"weather\"],char:\"🌧\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning_and_rain:{keywords:[\"weather\",\"lightning\"],char:\"⛈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning:{keywords:[\"weather\",\"thunder\"],char:\"🌩\",fitzpatrick_scale:false,category:\"animals_and_nature\"},zap:{keywords:[\"thunder\",\"weather\",\"lightning bolt\",\"fast\"],char:\"⚡\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fire:{keywords:[\"hot\",\"cook\",\"flame\"],char:\"🔥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},boom:{keywords:[\"bomb\",\"explode\",\"explosion\",\"collision\",\"blown\"],char:\"💥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snowflake:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\"],char:\"❄️\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_snow:{keywords:[\"weather\"],char:\"🌨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\",\"without_snow\"],char:\"⛄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman_with_snow:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\"],char:\"☃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},wind_face:{keywords:[\"gust\",\"air\"],char:\"🌬\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dash:{keywords:[\"wind\",\"air\",\"fast\",\"shoo\",\"fart\",\"smoke\",\"puff\"],char:\"💨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tornado:{keywords:[\"weather\",\"cyclone\",\"twister\"],char:\"🌪\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fog:{keywords:[\"weather\"],char:\"🌫\",fitzpatrick_scale:false,category:\"animals_and_nature\"},open_umbrella:{keywords:[\"weather\",\"spring\"],char:\"☂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},umbrella:{keywords:[\"rainy\",\"weather\",\"spring\"],char:\"☔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},droplet:{keywords:[\"water\",\"drip\",\"faucet\",\"spring\"],char:\"💧\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sweat_drops:{keywords:[\"water\",\"drip\",\"oops\"],char:\"💦\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ocean:{keywords:[\"sea\",\"water\",\"wave\",\"nature\",\"tsunami\",\"disaster\"],char:\"🌊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},green_apple:{keywords:[\"fruit\",\"nature\"],char:\"🍏\",fitzpatrick_scale:false,category:\"food_and_drink\"},apple:{keywords:[\"fruit\",\"mac\",\"school\"],char:\"🍎\",fitzpatrick_scale:false,category:\"food_and_drink\"},pear:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍐\",fitzpatrick_scale:false,category:\"food_and_drink\"},tangerine:{keywords:[\"food\",\"fruit\",\"nature\",\"orange\"],char:\"🍊\",fitzpatrick_scale:false,category:\"food_and_drink\"},lemon:{keywords:[\"fruit\",\"nature\"],char:\"🍋\",fitzpatrick_scale:false,category:\"food_and_drink\"},banana:{keywords:[\"fruit\",\"food\",\"monkey\"],char:\"🍌\",fitzpatrick_scale:false,category:\"food_and_drink\"},watermelon:{keywords:[\"fruit\",\"food\",\"picnic\",\"summer\"],char:\"🍉\",fitzpatrick_scale:false,category:\"food_and_drink\"},grapes:{keywords:[\"fruit\",\"food\",\"wine\"],char:\"🍇\",fitzpatrick_scale:false,category:\"food_and_drink\"},strawberry:{keywords:[\"fruit\",\"food\",\"nature\"],char:\"🍓\",fitzpatrick_scale:false,category:\"food_and_drink\"},melon:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍈\",fitzpatrick_scale:false,category:\"food_and_drink\"},cherries:{keywords:[\"food\",\"fruit\"],char:\"🍒\",fitzpatrick_scale:false,category:\"food_and_drink\"},peach:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍑\",fitzpatrick_scale:false,category:\"food_and_drink\"},pineapple:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍍\",fitzpatrick_scale:false,category:\"food_and_drink\"},coconut:{keywords:[\"fruit\",\"nature\",\"food\",\"palm\"],char:\"🥥\",fitzpatrick_scale:false,category:\"food_and_drink\"},kiwi_fruit:{keywords:[\"fruit\",\"food\"],char:\"🥝\",fitzpatrick_scale:false,category:\"food_and_drink\"},mango:{keywords:[\"fruit\",\"food\",\"tropical\"],char:\"🥭\",fitzpatrick_scale:false,category:\"food_and_drink\"},avocado:{keywords:[\"fruit\",\"food\"],char:\"🥑\",fitzpatrick_scale:false,category:\"food_and_drink\"},broccoli:{keywords:[\"fruit\",\"food\",\"vegetable\"],char:\"🥦\",fitzpatrick_scale:false,category:\"food_and_drink\"},tomato:{keywords:[\"fruit\",\"vegetable\",\"nature\",\"food\"],char:\"🍅\",fitzpatrick_scale:false,category:\"food_and_drink\"},eggplant:{keywords:[\"vegetable\",\"nature\",\"food\",\"aubergine\"],char:\"🍆\",fitzpatrick_scale:false,category:\"food_and_drink\"},cucumber:{keywords:[\"fruit\",\"food\",\"pickle\"],char:\"🥒\",fitzpatrick_scale:false,category:\"food_and_drink\"},carrot:{keywords:[\"vegetable\",\"food\",\"orange\"],char:\"🥕\",fitzpatrick_scale:false,category:\"food_and_drink\"},hot_pepper:{keywords:[\"food\",\"spicy\",\"chilli\",\"chili\"],char:\"🌶\",fitzpatrick_scale:false,category:\"food_and_drink\"},potato:{keywords:[\"food\",\"tuber\",\"vegatable\",\"starch\"],char:\"🥔\",fitzpatrick_scale:false,category:\"food_and_drink\"},corn:{keywords:[\"food\",\"vegetable\",\"plant\"],char:\"🌽\",fitzpatrick_scale:false,category:\"food_and_drink\"},leafy_greens:{keywords:[\"food\",\"vegetable\",\"plant\",\"bok choy\",\"cabbage\",\"kale\",\"lettuce\"],char:\"🥬\",fitzpatrick_scale:false,category:\"food_and_drink\"},sweet_potato:{keywords:[\"food\",\"nature\"],char:\"🍠\",fitzpatrick_scale:false,category:\"food_and_drink\"},peanuts:{keywords:[\"food\",\"nut\"],char:\"🥜\",fitzpatrick_scale:false,category:\"food_and_drink\"},honey_pot:{keywords:[\"bees\",\"sweet\",\"kitchen\"],char:\"🍯\",fitzpatrick_scale:false,category:\"food_and_drink\"},croissant:{keywords:[\"food\",\"bread\",\"french\"],char:\"🥐\",fitzpatrick_scale:false,category:\"food_and_drink\"},bread:{keywords:[\"food\",\"wheat\",\"breakfast\",\"toast\"],char:\"🍞\",fitzpatrick_scale:false,category:\"food_and_drink\"},baguette_bread:{keywords:[\"food\",\"bread\",\"french\"],char:\"🥖\",fitzpatrick_scale:false,category:\"food_and_drink\"},bagel:{keywords:[\"food\",\"bread\",\"bakery\",\"schmear\"],char:\"🥯\",fitzpatrick_scale:false,category:\"food_and_drink\"},pretzel:{keywords:[\"food\",\"bread\",\"twisted\"],char:\"🥨\",fitzpatrick_scale:false,category:\"food_and_drink\"},cheese:{keywords:[\"food\",\"chadder\"],char:\"🧀\",fitzpatrick_scale:false,category:\"food_and_drink\"},egg:{keywords:[\"food\",\"chicken\",\"breakfast\"],char:\"🥚\",fitzpatrick_scale:false,category:\"food_and_drink\"},bacon:{keywords:[\"food\",\"breakfast\",\"pork\",\"pig\",\"meat\"],char:\"🥓\",fitzpatrick_scale:false,category:\"food_and_drink\"},steak:{keywords:[\"food\",\"cow\",\"meat\",\"cut\",\"chop\",\"lambchop\",\"porkchop\"],char:\"🥩\",fitzpatrick_scale:false,category:\"food_and_drink\"},pancakes:{keywords:[\"food\",\"breakfast\",\"flapjacks\",\"hotcakes\"],char:\"🥞\",fitzpatrick_scale:false,category:\"food_and_drink\"},poultry_leg:{keywords:[\"food\",\"meat\",\"drumstick\",\"bird\",\"chicken\",\"turkey\"],char:\"🍗\",fitzpatrick_scale:false,category:\"food_and_drink\"},meat_on_bone:{keywords:[\"good\",\"food\",\"drumstick\"],char:\"🍖\",fitzpatrick_scale:false,category:\"food_and_drink\"},bone:{keywords:[\"skeleton\"],char:\"🦴\",fitzpatrick_scale:false,category:\"food_and_drink\"},fried_shrimp:{keywords:[\"food\",\"animal\",\"appetizer\",\"summer\"],char:\"🍤\",fitzpatrick_scale:false,category:\"food_and_drink\"},fried_egg:{keywords:[\"food\",\"breakfast\",\"kitchen\",\"egg\"],char:\"🍳\",fitzpatrick_scale:false,category:\"food_and_drink\"},hamburger:{keywords:[\"meat\",\"fast food\",\"beef\",\"cheeseburger\",\"mcdonalds\",\"burger king\"],char:\"🍔\",fitzpatrick_scale:false,category:\"food_and_drink\"},fries:{keywords:[\"chips\",\"snack\",\"fast food\"],char:\"🍟\",fitzpatrick_scale:false,category:\"food_and_drink\"},stuffed_flatbread:{keywords:[\"food\",\"flatbread\",\"stuffed\",\"gyro\"],char:\"🥙\",fitzpatrick_scale:false,category:\"food_and_drink\"},hotdog:{keywords:[\"food\",\"frankfurter\"],char:\"🌭\",fitzpatrick_scale:false,category:\"food_and_drink\"},pizza:{keywords:[\"food\",\"party\"],char:\"🍕\",fitzpatrick_scale:false,category:\"food_and_drink\"},sandwich:{keywords:[\"food\",\"lunch\",\"bread\"],char:\"🥪\",fitzpatrick_scale:false,category:\"food_and_drink\"},canned_food:{keywords:[\"food\",\"soup\"],char:\"🥫\",fitzpatrick_scale:false,category:\"food_and_drink\"},spaghetti:{keywords:[\"food\",\"italian\",\"noodle\"],char:\"🍝\",fitzpatrick_scale:false,category:\"food_and_drink\"},taco:{keywords:[\"food\",\"mexican\"],char:\"🌮\",fitzpatrick_scale:false,category:\"food_and_drink\"},burrito:{keywords:[\"food\",\"mexican\"],char:\"🌯\",fitzpatrick_scale:false,category:\"food_and_drink\"},green_salad:{keywords:[\"food\",\"healthy\",\"lettuce\"],char:\"🥗\",fitzpatrick_scale:false,category:\"food_and_drink\"},shallow_pan_of_food:{keywords:[\"food\",\"cooking\",\"casserole\",\"paella\"],char:\"🥘\",fitzpatrick_scale:false,category:\"food_and_drink\"},ramen:{keywords:[\"food\",\"japanese\",\"noodle\",\"chopsticks\"],char:\"🍜\",fitzpatrick_scale:false,category:\"food_and_drink\"},stew:{keywords:[\"food\",\"meat\",\"soup\"],char:\"🍲\",fitzpatrick_scale:false,category:\"food_and_drink\"},fish_cake:{keywords:[\"food\",\"japan\",\"sea\",\"beach\",\"narutomaki\",\"pink\",\"swirl\",\"kamaboko\",\"surimi\",\"ramen\"],char:\"🍥\",fitzpatrick_scale:false,category:\"food_and_drink\"},fortune_cookie:{keywords:[\"food\",\"prophecy\"],char:\"🥠\",fitzpatrick_scale:false,category:\"food_and_drink\"},sushi:{keywords:[\"food\",\"fish\",\"japanese\",\"rice\"],char:\"🍣\",fitzpatrick_scale:false,category:\"food_and_drink\"},bento:{keywords:[\"food\",\"japanese\",\"box\"],char:\"🍱\",fitzpatrick_scale:false,category:\"food_and_drink\"},curry:{keywords:[\"food\",\"spicy\",\"hot\",\"indian\"],char:\"🍛\",fitzpatrick_scale:false,category:\"food_and_drink\"},rice_ball:{keywords:[\"food\",\"japanese\"],char:\"🍙\",fitzpatrick_scale:false,category:\"food_and_drink\"},rice:{keywords:[\"food\",\"china\",\"asian\"],char:\"🍚\",fitzpatrick_scale:false,category:\"food_and_drink\"},rice_cracker:{keywords:[\"food\",\"japanese\"],char:\"🍘\",fitzpatrick_scale:false,category:\"food_and_drink\"},oden:{keywords:[\"food\",\"japanese\"],char:\"🍢\",fitzpatrick_scale:false,category:\"food_and_drink\"},dango:{keywords:[\"food\",\"dessert\",\"sweet\",\"japanese\",\"barbecue\",\"meat\"],char:\"🍡\",fitzpatrick_scale:false,category:\"food_and_drink\"},shaved_ice:{keywords:[\"hot\",\"dessert\",\"summer\"],char:\"🍧\",fitzpatrick_scale:false,category:\"food_and_drink\"},ice_cream:{keywords:[\"food\",\"hot\",\"dessert\"],char:\"🍨\",fitzpatrick_scale:false,category:\"food_and_drink\"},icecream:{keywords:[\"food\",\"hot\",\"dessert\",\"summer\"],char:\"🍦\",fitzpatrick_scale:false,category:\"food_and_drink\"},pie:{keywords:[\"food\",\"dessert\",\"pastry\"],char:\"🥧\",fitzpatrick_scale:false,category:\"food_and_drink\"},cake:{keywords:[\"food\",\"dessert\"],char:\"🍰\",fitzpatrick_scale:false,category:\"food_and_drink\"},cupcake:{keywords:[\"food\",\"dessert\",\"bakery\",\"sweet\"],char:\"🧁\",fitzpatrick_scale:false,category:\"food_and_drink\"},moon_cake:{keywords:[\"food\",\"autumn\"],char:\"🥮\",fitzpatrick_scale:false,category:\"food_and_drink\"},birthday:{keywords:[\"food\",\"dessert\",\"cake\"],char:\"🎂\",fitzpatrick_scale:false,category:\"food_and_drink\"},custard:{keywords:[\"dessert\",\"food\"],char:\"🍮\",fitzpatrick_scale:false,category:\"food_and_drink\"},candy:{keywords:[\"snack\",\"dessert\",\"sweet\",\"lolly\"],char:\"🍬\",fitzpatrick_scale:false,category:\"food_and_drink\"},lollipop:{keywords:[\"food\",\"snack\",\"candy\",\"sweet\"],char:\"🍭\",fitzpatrick_scale:false,category:\"food_and_drink\"},chocolate_bar:{keywords:[\"food\",\"snack\",\"dessert\",\"sweet\"],char:\"🍫\",fitzpatrick_scale:false,category:\"food_and_drink\"},popcorn:{keywords:[\"food\",\"movie theater\",\"films\",\"snack\"],char:\"🍿\",fitzpatrick_scale:false,category:\"food_and_drink\"},dumpling:{keywords:[\"food\",\"empanada\",\"pierogi\",\"potsticker\"],char:\"🥟\",fitzpatrick_scale:false,category:\"food_and_drink\"},doughnut:{keywords:[\"food\",\"dessert\",\"snack\",\"sweet\",\"donut\"],char:\"🍩\",fitzpatrick_scale:false,category:\"food_and_drink\"},cookie:{keywords:[\"food\",\"snack\",\"oreo\",\"chocolate\",\"sweet\",\"dessert\"],char:\"🍪\",fitzpatrick_scale:false,category:\"food_and_drink\"},milk_glass:{keywords:[\"beverage\",\"drink\",\"cow\"],char:\"🥛\",fitzpatrick_scale:false,category:\"food_and_drink\"},beer:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:\"🍺\",fitzpatrick_scale:false,category:\"food_and_drink\"},beers:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:\"🍻\",fitzpatrick_scale:false,category:\"food_and_drink\"},clinking_glasses:{keywords:[\"beverage\",\"drink\",\"party\",\"alcohol\",\"celebrate\",\"cheers\",\"wine\",\"champagne\",\"toast\"],char:\"🥂\",fitzpatrick_scale:false,category:\"food_and_drink\"},wine_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"booze\"],char:\"🍷\",fitzpatrick_scale:false,category:\"food_and_drink\"},tumbler_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"liquor\",\"booze\",\"bourbon\",\"scotch\",\"whisky\",\"glass\",\"shot\"],char:\"🥃\",fitzpatrick_scale:false,category:\"food_and_drink\"},cocktail:{keywords:[\"drink\",\"drunk\",\"alcohol\",\"beverage\",\"booze\",\"mojito\"],char:\"🍸\",fitzpatrick_scale:false,category:\"food_and_drink\"},tropical_drink:{keywords:[\"beverage\",\"cocktail\",\"summer\",\"beach\",\"alcohol\",\"booze\",\"mojito\"],char:\"🍹\",fitzpatrick_scale:false,category:\"food_and_drink\"},champagne:{keywords:[\"drink\",\"wine\",\"bottle\",\"celebration\"],char:\"🍾\",fitzpatrick_scale:false,category:\"food_and_drink\"},sake:{keywords:[\"wine\",\"drink\",\"drunk\",\"beverage\",\"japanese\",\"alcohol\",\"booze\"],char:\"🍶\",fitzpatrick_scale:false,category:\"food_and_drink\"},tea:{keywords:[\"drink\",\"bowl\",\"breakfast\",\"green\",\"british\"],char:\"🍵\",fitzpatrick_scale:false,category:\"food_and_drink\"},cup_with_straw:{keywords:[\"drink\",\"soda\"],char:\"🥤\",fitzpatrick_scale:false,category:\"food_and_drink\"},coffee:{keywords:[\"beverage\",\"caffeine\",\"latte\",\"espresso\"],char:\"☕\",fitzpatrick_scale:false,category:\"food_and_drink\"},baby_bottle:{keywords:[\"food\",\"container\",\"milk\"],char:\"🍼\",fitzpatrick_scale:false,category:\"food_and_drink\"},salt:{keywords:[\"condiment\",\"shaker\"],char:\"🧂\",fitzpatrick_scale:false,category:\"food_and_drink\"},spoon:{keywords:[\"cutlery\",\"kitchen\",\"tableware\"],char:\"🥄\",fitzpatrick_scale:false,category:\"food_and_drink\"},fork_and_knife:{keywords:[\"cutlery\",\"kitchen\"],char:\"🍴\",fitzpatrick_scale:false,category:\"food_and_drink\"},plate_with_cutlery:{keywords:[\"food\",\"eat\",\"meal\",\"lunch\",\"dinner\",\"restaurant\"],char:\"🍽\",fitzpatrick_scale:false,category:\"food_and_drink\"},bowl_with_spoon:{keywords:[\"food\",\"breakfast\",\"cereal\",\"oatmeal\",\"porridge\"],char:\"🥣\",fitzpatrick_scale:false,category:\"food_and_drink\"},takeout_box:{keywords:[\"food\",\"leftovers\"],char:\"🥡\",fitzpatrick_scale:false,category:\"food_and_drink\"},chopsticks:{keywords:[\"food\"],char:\"🥢\",fitzpatrick_scale:false,category:\"food_and_drink\"},soccer:{keywords:[\"sports\",\"football\"],char:\"⚽\",fitzpatrick_scale:false,category:\"activity\"},basketball:{keywords:[\"sports\",\"balls\",\"NBA\"],char:\"🏀\",fitzpatrick_scale:false,category:\"activity\"},football:{keywords:[\"sports\",\"balls\",\"NFL\"],char:\"🏈\",fitzpatrick_scale:false,category:\"activity\"},baseball:{keywords:[\"sports\",\"balls\"],char:\"⚾\",fitzpatrick_scale:false,category:\"activity\"},softball:{keywords:[\"sports\",\"balls\"],char:\"🥎\",fitzpatrick_scale:false,category:\"activity\"},tennis:{keywords:[\"sports\",\"balls\",\"green\"],char:\"🎾\",fitzpatrick_scale:false,category:\"activity\"},volleyball:{keywords:[\"sports\",\"balls\"],char:\"🏐\",fitzpatrick_scale:false,category:\"activity\"},rugby_football:{keywords:[\"sports\",\"team\"],char:\"🏉\",fitzpatrick_scale:false,category:\"activity\"},flying_disc:{keywords:[\"sports\",\"frisbee\",\"ultimate\"],char:\"🥏\",fitzpatrick_scale:false,category:\"activity\"},\"8ball\":{keywords:[\"pool\",\"hobby\",\"game\",\"luck\",\"magic\"],char:\"🎱\",fitzpatrick_scale:false,category:\"activity\"},golf:{keywords:[\"sports\",\"business\",\"flag\",\"hole\",\"summer\"],char:\"⛳\",fitzpatrick_scale:false,category:\"activity\"},golfing_woman:{keywords:[\"sports\",\"business\",\"woman\",\"female\"],char:\"🏌️‍♀️\",fitzpatrick_scale:false,category:\"activity\"},golfing_man:{keywords:[\"sports\",\"business\"],char:\"🏌\",fitzpatrick_scale:true,category:\"activity\"},ping_pong:{keywords:[\"sports\",\"pingpong\"],char:\"🏓\",fitzpatrick_scale:false,category:\"activity\"},badminton:{keywords:[\"sports\"],char:\"🏸\",fitzpatrick_scale:false,category:\"activity\"},goal_net:{keywords:[\"sports\"],char:\"🥅\",fitzpatrick_scale:false,category:\"activity\"},ice_hockey:{keywords:[\"sports\"],char:\"🏒\",fitzpatrick_scale:false,category:\"activity\"},field_hockey:{keywords:[\"sports\"],char:\"🏑\",fitzpatrick_scale:false,category:\"activity\"},lacrosse:{keywords:[\"sports\",\"ball\",\"stick\"],char:\"🥍\",fitzpatrick_scale:false,category:\"activity\"},cricket:{keywords:[\"sports\"],char:\"🏏\",fitzpatrick_scale:false,category:\"activity\"},ski:{keywords:[\"sports\",\"winter\",\"cold\",\"snow\"],char:\"🎿\",fitzpatrick_scale:false,category:\"activity\"},skier:{keywords:[\"sports\",\"winter\",\"snow\"],char:\"⛷\",fitzpatrick_scale:false,category:\"activity\"},snowboarder:{keywords:[\"sports\",\"winter\"],char:\"🏂\",fitzpatrick_scale:true,category:\"activity\"},person_fencing:{keywords:[\"sports\",\"fencing\",\"sword\"],char:\"🤺\",fitzpatrick_scale:false,category:\"activity\"},women_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:\"🤼‍♀️\",fitzpatrick_scale:false,category:\"activity\"},men_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:\"🤼‍♂️\",fitzpatrick_scale:false,category:\"activity\"},woman_cartwheeling:{keywords:[\"gymnastics\"],char:\"🤸‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_cartwheeling:{keywords:[\"gymnastics\"],char:\"🤸‍♂️\",fitzpatrick_scale:true,category:\"activity\"},woman_playing_handball:{keywords:[\"sports\"],char:\"🤾‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_playing_handball:{keywords:[\"sports\"],char:\"🤾‍♂️\",fitzpatrick_scale:true,category:\"activity\"},ice_skate:{keywords:[\"sports\"],char:\"⛸\",fitzpatrick_scale:false,category:\"activity\"},curling_stone:{keywords:[\"sports\"],char:\"🥌\",fitzpatrick_scale:false,category:\"activity\"},skateboard:{keywords:[\"board\"],char:\"🛹\",fitzpatrick_scale:false,category:\"activity\"},sled:{keywords:[\"sleigh\",\"luge\",\"toboggan\"],char:\"🛷\",fitzpatrick_scale:false,category:\"activity\"},bow_and_arrow:{keywords:[\"sports\"],char:\"🏹\",fitzpatrick_scale:false,category:\"activity\"},fishing_pole_and_fish:{keywords:[\"food\",\"hobby\",\"summer\"],char:\"🎣\",fitzpatrick_scale:false,category:\"activity\"},boxing_glove:{keywords:[\"sports\",\"fighting\"],char:\"🥊\",fitzpatrick_scale:false,category:\"activity\"},martial_arts_uniform:{keywords:[\"judo\",\"karate\",\"taekwondo\"],char:\"🥋\",fitzpatrick_scale:false,category:\"activity\"},rowing_woman:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\",\"woman\",\"female\"],char:\"🚣‍♀️\",fitzpatrick_scale:true,category:\"activity\"},rowing_man:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\"],char:\"🚣\",fitzpatrick_scale:true,category:\"activity\"},climbing_woman:{keywords:[\"sports\",\"hobby\",\"woman\",\"female\",\"rock\"],char:\"🧗‍♀️\",fitzpatrick_scale:true,category:\"activity\"},climbing_man:{keywords:[\"sports\",\"hobby\",\"man\",\"male\",\"rock\"],char:\"🧗‍♂️\",fitzpatrick_scale:true,category:\"activity\"},swimming_woman:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\",\"woman\",\"female\"],char:\"🏊‍♀️\",fitzpatrick_scale:true,category:\"activity\"},swimming_man:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\"],char:\"🏊\",fitzpatrick_scale:true,category:\"activity\"},woman_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:\"🤽‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:\"🤽‍♂️\",fitzpatrick_scale:true,category:\"activity\"},woman_in_lotus_position:{keywords:[\"woman\",\"female\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:\"🧘‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_in_lotus_position:{keywords:[\"man\",\"male\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:\"🧘‍♂️\",fitzpatrick_scale:true,category:\"activity\"},surfing_woman:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\",\"woman\",\"female\"],char:\"🏄‍♀️\",fitzpatrick_scale:true,category:\"activity\"},surfing_man:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\"],char:\"🏄\",fitzpatrick_scale:true,category:\"activity\"},bath:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:\"🛀\",fitzpatrick_scale:true,category:\"activity\"},basketball_woman:{keywords:[\"sports\",\"human\",\"woman\",\"female\"],char:\"⛹️‍♀️\",fitzpatrick_scale:true,category:\"activity\"},basketball_man:{keywords:[\"sports\",\"human\"],char:\"⛹\",fitzpatrick_scale:true,category:\"activity\"},weight_lifting_woman:{keywords:[\"sports\",\"training\",\"exercise\",\"woman\",\"female\"],char:\"🏋️‍♀️\",fitzpatrick_scale:true,category:\"activity\"},weight_lifting_man:{keywords:[\"sports\",\"training\",\"exercise\"],char:\"🏋\",fitzpatrick_scale:true,category:\"activity\"},biking_woman:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\",\"woman\",\"female\"],char:\"🚴‍♀️\",fitzpatrick_scale:true,category:\"activity\"},biking_man:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\"],char:\"🚴\",fitzpatrick_scale:true,category:\"activity\"},mountain_biking_woman:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\",\"woman\",\"female\"],char:\"🚵‍♀️\",fitzpatrick_scale:true,category:\"activity\"},mountain_biking_man:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\"],char:\"🚵\",fitzpatrick_scale:true,category:\"activity\"},horse_racing:{keywords:[\"animal\",\"betting\",\"competition\",\"gambling\",\"luck\"],char:\"🏇\",fitzpatrick_scale:true,category:\"activity\"},business_suit_levitating:{keywords:[\"suit\",\"business\",\"levitate\",\"hover\",\"jump\"],char:\"🕴\",fitzpatrick_scale:true,category:\"activity\"},trophy:{keywords:[\"win\",\"award\",\"contest\",\"place\",\"ftw\",\"ceremony\"],char:\"🏆\",fitzpatrick_scale:false,category:\"activity\"},running_shirt_with_sash:{keywords:[\"play\",\"pageant\"],char:\"🎽\",fitzpatrick_scale:false,category:\"activity\"},medal_sports:{keywords:[\"award\",\"winning\"],char:\"🏅\",fitzpatrick_scale:false,category:\"activity\"},medal_military:{keywords:[\"award\",\"winning\",\"army\"],char:\"🎖\",fitzpatrick_scale:false,category:\"activity\"},\"1st_place_medal\":{keywords:[\"award\",\"winning\",\"first\"],char:\"🥇\",fitzpatrick_scale:false,category:\"activity\"},\"2nd_place_medal\":{keywords:[\"award\",\"second\"],char:\"🥈\",fitzpatrick_scale:false,category:\"activity\"},\"3rd_place_medal\":{keywords:[\"award\",\"third\"],char:\"🥉\",fitzpatrick_scale:false,category:\"activity\"},reminder_ribbon:{keywords:[\"sports\",\"cause\",\"support\",\"awareness\"],char:\"🎗\",fitzpatrick_scale:false,category:\"activity\"},rosette:{keywords:[\"flower\",\"decoration\",\"military\"],char:\"🏵\",fitzpatrick_scale:false,category:\"activity\"},ticket:{keywords:[\"event\",\"concert\",\"pass\"],char:\"🎫\",fitzpatrick_scale:false,category:\"activity\"},tickets:{keywords:[\"sports\",\"concert\",\"entrance\"],char:\"🎟\",fitzpatrick_scale:false,category:\"activity\"},performing_arts:{keywords:[\"acting\",\"theater\",\"drama\"],char:\"🎭\",fitzpatrick_scale:false,category:\"activity\"},art:{keywords:[\"design\",\"paint\",\"draw\",\"colors\"],char:\"🎨\",fitzpatrick_scale:false,category:\"activity\"},circus_tent:{keywords:[\"festival\",\"carnival\",\"party\"],char:\"🎪\",fitzpatrick_scale:false,category:\"activity\"},woman_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:\"🤹‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:\"🤹‍♂️\",fitzpatrick_scale:true,category:\"activity\"},microphone:{keywords:[\"sound\",\"music\",\"PA\",\"sing\",\"talkshow\"],char:\"🎤\",fitzpatrick_scale:false,category:\"activity\"},headphones:{keywords:[\"music\",\"score\",\"gadgets\"],char:\"🎧\",fitzpatrick_scale:false,category:\"activity\"},musical_score:{keywords:[\"treble\",\"clef\",\"compose\"],char:\"🎼\",fitzpatrick_scale:false,category:\"activity\"},musical_keyboard:{keywords:[\"piano\",\"instrument\",\"compose\"],char:\"🎹\",fitzpatrick_scale:false,category:\"activity\"},drum:{keywords:[\"music\",\"instrument\",\"drumsticks\",\"snare\"],char:\"🥁\",fitzpatrick_scale:false,category:\"activity\"},saxophone:{keywords:[\"music\",\"instrument\",\"jazz\",\"blues\"],char:\"🎷\",fitzpatrick_scale:false,category:\"activity\"},trumpet:{keywords:[\"music\",\"brass\"],char:\"🎺\",fitzpatrick_scale:false,category:\"activity\"},guitar:{keywords:[\"music\",\"instrument\"],char:\"🎸\",fitzpatrick_scale:false,category:\"activity\"},violin:{keywords:[\"music\",\"instrument\",\"orchestra\",\"symphony\"],char:\"🎻\",fitzpatrick_scale:false,category:\"activity\"},clapper:{keywords:[\"movie\",\"film\",\"record\"],char:\"🎬\",fitzpatrick_scale:false,category:\"activity\"},video_game:{keywords:[\"play\",\"console\",\"PS4\",\"controller\"],char:\"🎮\",fitzpatrick_scale:false,category:\"activity\"},space_invader:{keywords:[\"game\",\"arcade\",\"play\"],char:\"👾\",fitzpatrick_scale:false,category:\"activity\"},dart:{keywords:[\"game\",\"play\",\"bar\",\"target\",\"bullseye\"],char:\"🎯\",fitzpatrick_scale:false,category:\"activity\"},game_die:{keywords:[\"dice\",\"random\",\"tabletop\",\"play\",\"luck\"],char:\"🎲\",fitzpatrick_scale:false,category:\"activity\"},chess_pawn:{keywords:[\"expendable\"],char:\"♟\",fitzpatrick_scale:false,category:\"activity\"},slot_machine:{keywords:[\"bet\",\"gamble\",\"vegas\",\"fruit machine\",\"luck\",\"casino\"],char:\"🎰\",fitzpatrick_scale:false,category:\"activity\"},jigsaw:{keywords:[\"interlocking\",\"puzzle\",\"piece\"],char:\"🧩\",fitzpatrick_scale:false,category:\"activity\"},bowling:{keywords:[\"sports\",\"fun\",\"play\"],char:\"🎳\",fitzpatrick_scale:false,category:\"activity\"},red_car:{keywords:[\"red\",\"transportation\",\"vehicle\"],char:\"🚗\",fitzpatrick_scale:false,category:\"travel_and_places\"},taxi:{keywords:[\"uber\",\"vehicle\",\"cars\",\"transportation\"],char:\"🚕\",fitzpatrick_scale:false,category:\"travel_and_places\"},blue_car:{keywords:[\"transportation\",\"vehicle\"],char:\"🚙\",fitzpatrick_scale:false,category:\"travel_and_places\"},bus:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:\"🚌\",fitzpatrick_scale:false,category:\"travel_and_places\"},trolleybus:{keywords:[\"bart\",\"transportation\",\"vehicle\"],char:\"🚎\",fitzpatrick_scale:false,category:\"travel_and_places\"},racing_car:{keywords:[\"sports\",\"race\",\"fast\",\"formula\",\"f1\"],char:\"🏎\",fitzpatrick_scale:false,category:\"travel_and_places\"},police_car:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"law\",\"legal\",\"enforcement\"],char:\"🚓\",fitzpatrick_scale:false,category:\"travel_and_places\"},ambulance:{keywords:[\"health\",\"911\",\"hospital\"],char:\"🚑\",fitzpatrick_scale:false,category:\"travel_and_places\"},fire_engine:{keywords:[\"transportation\",\"cars\",\"vehicle\"],char:\"🚒\",fitzpatrick_scale:false,category:\"travel_and_places\"},minibus:{keywords:[\"vehicle\",\"car\",\"transportation\"],char:\"🚐\",fitzpatrick_scale:false,category:\"travel_and_places\"},truck:{keywords:[\"cars\",\"transportation\"],char:\"🚚\",fitzpatrick_scale:false,category:\"travel_and_places\"},articulated_lorry:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"express\"],char:\"🚛\",fitzpatrick_scale:false,category:\"travel_and_places\"},tractor:{keywords:[\"vehicle\",\"car\",\"farming\",\"agriculture\"],char:\"🚜\",fitzpatrick_scale:false,category:\"travel_and_places\"},kick_scooter:{keywords:[\"vehicle\",\"kick\",\"razor\"],char:\"🛴\",fitzpatrick_scale:false,category:\"travel_and_places\"},motorcycle:{keywords:[\"race\",\"sports\",\"fast\"],char:\"🏍\",fitzpatrick_scale:false,category:\"travel_and_places\"},bike:{keywords:[\"sports\",\"bicycle\",\"exercise\",\"hipster\"],char:\"🚲\",fitzpatrick_scale:false,category:\"travel_and_places\"},motor_scooter:{keywords:[\"vehicle\",\"vespa\",\"sasha\"],char:\"🛵\",fitzpatrick_scale:false,category:\"travel_and_places\"},rotating_light:{keywords:[\"police\",\"ambulance\",\"911\",\"emergency\",\"alert\",\"error\",\"pinged\",\"law\",\"legal\"],char:\"🚨\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_police_car:{keywords:[\"vehicle\",\"law\",\"legal\",\"enforcement\",\"911\"],char:\"🚔\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_bus:{keywords:[\"vehicle\",\"transportation\"],char:\"🚍\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_automobile:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:\"🚘\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_taxi:{keywords:[\"vehicle\",\"cars\",\"uber\"],char:\"🚖\",fitzpatrick_scale:false,category:\"travel_and_places\"},aerial_tramway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:\"🚡\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_cableway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:\"🚠\",fitzpatrick_scale:false,category:\"travel_and_places\"},suspension_railway:{keywords:[\"vehicle\",\"transportation\"],char:\"🚟\",fitzpatrick_scale:false,category:\"travel_and_places\"},railway_car:{keywords:[\"transportation\",\"vehicle\"],char:\"🚃\",fitzpatrick_scale:false,category:\"travel_and_places\"},train:{keywords:[\"transportation\",\"vehicle\",\"carriage\",\"public\",\"travel\"],char:\"🚋\",fitzpatrick_scale:false,category:\"travel_and_places\"},monorail:{keywords:[\"transportation\",\"vehicle\"],char:\"🚝\",fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_side:{keywords:[\"transportation\",\"vehicle\"],char:\"🚄\",fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_front:{keywords:[\"transportation\",\"vehicle\",\"speed\",\"fast\",\"public\",\"travel\"],char:\"🚅\",fitzpatrick_scale:false,category:\"travel_and_places\"},light_rail:{keywords:[\"transportation\",\"vehicle\"],char:\"🚈\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_railway:{keywords:[\"transportation\",\"vehicle\"],char:\"🚞\",fitzpatrick_scale:false,category:\"travel_and_places\"},steam_locomotive:{keywords:[\"transportation\",\"vehicle\",\"train\"],char:\"🚂\",fitzpatrick_scale:false,category:\"travel_and_places\"},train2:{keywords:[\"transportation\",\"vehicle\"],char:\"🚆\",fitzpatrick_scale:false,category:\"travel_and_places\"},metro:{keywords:[\"transportation\",\"blue-square\",\"mrt\",\"underground\",\"tube\"],char:\"🚇\",fitzpatrick_scale:false,category:\"travel_and_places\"},tram:{keywords:[\"transportation\",\"vehicle\"],char:\"🚊\",fitzpatrick_scale:false,category:\"travel_and_places\"},station:{keywords:[\"transportation\",\"vehicle\",\"public\"],char:\"🚉\",fitzpatrick_scale:false,category:\"travel_and_places\"},flying_saucer:{keywords:[\"transportation\",\"vehicle\",\"ufo\"],char:\"🛸\",fitzpatrick_scale:false,category:\"travel_and_places\"},helicopter:{keywords:[\"transportation\",\"vehicle\",\"fly\"],char:\"🚁\",fitzpatrick_scale:false,category:\"travel_and_places\"},small_airplane:{keywords:[\"flight\",\"transportation\",\"fly\",\"vehicle\"],char:\"🛩\",fitzpatrick_scale:false,category:\"travel_and_places\"},airplane:{keywords:[\"vehicle\",\"transportation\",\"flight\",\"fly\"],char:\"✈️\",fitzpatrick_scale:false,category:\"travel_and_places\"},flight_departure:{keywords:[\"airport\",\"flight\",\"landing\"],char:\"🛫\",fitzpatrick_scale:false,category:\"travel_and_places\"},flight_arrival:{keywords:[\"airport\",\"flight\",\"boarding\"],char:\"🛬\",fitzpatrick_scale:false,category:\"travel_and_places\"},sailboat:{keywords:[\"ship\",\"summer\",\"transportation\",\"water\",\"sailing\"],char:\"⛵\",fitzpatrick_scale:false,category:\"travel_and_places\"},motor_boat:{keywords:[\"ship\"],char:\"🛥\",fitzpatrick_scale:false,category:\"travel_and_places\"},speedboat:{keywords:[\"ship\",\"transportation\",\"vehicle\",\"summer\"],char:\"🚤\",fitzpatrick_scale:false,category:\"travel_and_places\"},ferry:{keywords:[\"boat\",\"ship\",\"yacht\"],char:\"⛴\",fitzpatrick_scale:false,category:\"travel_and_places\"},passenger_ship:{keywords:[\"yacht\",\"cruise\",\"ferry\"],char:\"🛳\",fitzpatrick_scale:false,category:\"travel_and_places\"},rocket:{keywords:[\"launch\",\"ship\",\"staffmode\",\"NASA\",\"outer space\",\"outer_space\",\"fly\"],char:\"🚀\",fitzpatrick_scale:false,category:\"travel_and_places\"},artificial_satellite:{keywords:[\"communication\",\"gps\",\"orbit\",\"spaceflight\",\"NASA\",\"ISS\"],char:\"🛰\",fitzpatrick_scale:false,category:\"travel_and_places\"},seat:{keywords:[\"sit\",\"airplane\",\"transport\",\"bus\",\"flight\",\"fly\"],char:\"💺\",fitzpatrick_scale:false,category:\"travel_and_places\"},canoe:{keywords:[\"boat\",\"paddle\",\"water\",\"ship\"],char:\"🛶\",fitzpatrick_scale:false,category:\"travel_and_places\"},anchor:{keywords:[\"ship\",\"ferry\",\"sea\",\"boat\"],char:\"⚓\",fitzpatrick_scale:false,category:\"travel_and_places\"},construction:{keywords:[\"wip\",\"progress\",\"caution\",\"warning\"],char:\"🚧\",fitzpatrick_scale:false,category:\"travel_and_places\"},fuelpump:{keywords:[\"gas station\",\"petroleum\"],char:\"⛽\",fitzpatrick_scale:false,category:\"travel_and_places\"},busstop:{keywords:[\"transportation\",\"wait\"],char:\"🚏\",fitzpatrick_scale:false,category:\"travel_and_places\"},vertical_traffic_light:{keywords:[\"transportation\",\"driving\"],char:\"🚦\",fitzpatrick_scale:false,category:\"travel_and_places\"},traffic_light:{keywords:[\"transportation\",\"signal\"],char:\"🚥\",fitzpatrick_scale:false,category:\"travel_and_places\"},checkered_flag:{keywords:[\"contest\",\"finishline\",\"race\",\"gokart\"],char:\"🏁\",fitzpatrick_scale:false,category:\"travel_and_places\"},ship:{keywords:[\"transportation\",\"titanic\",\"deploy\"],char:\"🚢\",fitzpatrick_scale:false,category:\"travel_and_places\"},ferris_wheel:{keywords:[\"photo\",\"carnival\",\"londoneye\"],char:\"🎡\",fitzpatrick_scale:false,category:\"travel_and_places\"},roller_coaster:{keywords:[\"carnival\",\"playground\",\"photo\",\"fun\"],char:\"🎢\",fitzpatrick_scale:false,category:\"travel_and_places\"},carousel_horse:{keywords:[\"photo\",\"carnival\"],char:\"🎠\",fitzpatrick_scale:false,category:\"travel_and_places\"},building_construction:{keywords:[\"wip\",\"working\",\"progress\"],char:\"🏗\",fitzpatrick_scale:false,category:\"travel_and_places\"},foggy:{keywords:[\"photo\",\"mountain\"],char:\"🌁\",fitzpatrick_scale:false,category:\"travel_and_places\"},tokyo_tower:{keywords:[\"photo\",\"japanese\"],char:\"🗼\",fitzpatrick_scale:false,category:\"travel_and_places\"},factory:{keywords:[\"building\",\"industry\",\"pollution\",\"smoke\"],char:\"🏭\",fitzpatrick_scale:false,category:\"travel_and_places\"},fountain:{keywords:[\"photo\",\"summer\",\"water\",\"fresh\"],char:\"⛲\",fitzpatrick_scale:false,category:\"travel_and_places\"},rice_scene:{keywords:[\"photo\",\"japan\",\"asia\",\"tsukimi\"],char:\"🎑\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain:{keywords:[\"photo\",\"nature\",\"environment\"],char:\"⛰\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_snow:{keywords:[\"photo\",\"nature\",\"environment\",\"winter\",\"cold\"],char:\"🏔\",fitzpatrick_scale:false,category:\"travel_and_places\"},mount_fuji:{keywords:[\"photo\",\"mountain\",\"nature\",\"japanese\"],char:\"🗻\",fitzpatrick_scale:false,category:\"travel_and_places\"},volcano:{keywords:[\"photo\",\"nature\",\"disaster\"],char:\"🌋\",fitzpatrick_scale:false,category:\"travel_and_places\"},japan:{keywords:[\"nation\",\"country\",\"japanese\",\"asia\"],char:\"🗾\",fitzpatrick_scale:false,category:\"travel_and_places\"},camping:{keywords:[\"photo\",\"outdoors\",\"tent\"],char:\"🏕\",fitzpatrick_scale:false,category:\"travel_and_places\"},tent:{keywords:[\"photo\",\"camping\",\"outdoors\"],char:\"⛺\",fitzpatrick_scale:false,category:\"travel_and_places\"},national_park:{keywords:[\"photo\",\"environment\",\"nature\"],char:\"🏞\",fitzpatrick_scale:false,category:\"travel_and_places\"},motorway:{keywords:[\"road\",\"cupertino\",\"interstate\",\"highway\"],char:\"🛣\",fitzpatrick_scale:false,category:\"travel_and_places\"},railway_track:{keywords:[\"train\",\"transportation\"],char:\"🛤\",fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise:{keywords:[\"morning\",\"view\",\"vacation\",\"photo\"],char:\"🌅\",fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise_over_mountains:{keywords:[\"view\",\"vacation\",\"photo\"],char:\"🌄\",fitzpatrick_scale:false,category:\"travel_and_places\"},desert:{keywords:[\"photo\",\"warm\",\"saharah\"],char:\"🏜\",fitzpatrick_scale:false,category:\"travel_and_places\"},beach_umbrella:{keywords:[\"weather\",\"summer\",\"sunny\",\"sand\",\"mojito\"],char:\"🏖\",fitzpatrick_scale:false,category:\"travel_and_places\"},desert_island:{keywords:[\"photo\",\"tropical\",\"mojito\"],char:\"🏝\",fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunrise:{keywords:[\"photo\",\"good morning\",\"dawn\"],char:\"🌇\",fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunset:{keywords:[\"photo\",\"evening\",\"sky\",\"buildings\"],char:\"🌆\",fitzpatrick_scale:false,category:\"travel_and_places\"},cityscape:{keywords:[\"photo\",\"night life\",\"urban\"],char:\"🏙\",fitzpatrick_scale:false,category:\"travel_and_places\"},night_with_stars:{keywords:[\"evening\",\"city\",\"downtown\"],char:\"🌃\",fitzpatrick_scale:false,category:\"travel_and_places\"},bridge_at_night:{keywords:[\"photo\",\"sanfrancisco\"],char:\"🌉\",fitzpatrick_scale:false,category:\"travel_and_places\"},milky_way:{keywords:[\"photo\",\"space\",\"stars\"],char:\"🌌\",fitzpatrick_scale:false,category:\"travel_and_places\"},stars:{keywords:[\"night\",\"photo\"],char:\"🌠\",fitzpatrick_scale:false,category:\"travel_and_places\"},sparkler:{keywords:[\"stars\",\"night\",\"shine\"],char:\"🎇\",fitzpatrick_scale:false,category:\"travel_and_places\"},fireworks:{keywords:[\"photo\",\"festival\",\"carnival\",\"congratulations\"],char:\"🎆\",fitzpatrick_scale:false,category:\"travel_and_places\"},rainbow:{keywords:[\"nature\",\"happy\",\"unicorn_face\",\"photo\",\"sky\",\"spring\"],char:\"🌈\",fitzpatrick_scale:false,category:\"travel_and_places\"},houses:{keywords:[\"buildings\",\"photo\"],char:\"🏘\",fitzpatrick_scale:false,category:\"travel_and_places\"},european_castle:{keywords:[\"building\",\"royalty\",\"history\"],char:\"🏰\",fitzpatrick_scale:false,category:\"travel_and_places\"},japanese_castle:{keywords:[\"photo\",\"building\"],char:\"🏯\",fitzpatrick_scale:false,category:\"travel_and_places\"},stadium:{keywords:[\"photo\",\"place\",\"sports\",\"concert\",\"venue\"],char:\"🏟\",fitzpatrick_scale:false,category:\"travel_and_places\"},statue_of_liberty:{keywords:[\"american\",\"newyork\"],char:\"🗽\",fitzpatrick_scale:false,category:\"travel_and_places\"},house:{keywords:[\"building\",\"home\"],char:\"🏠\",fitzpatrick_scale:false,category:\"travel_and_places\"},house_with_garden:{keywords:[\"home\",\"plant\",\"nature\"],char:\"🏡\",fitzpatrick_scale:false,category:\"travel_and_places\"},derelict_house:{keywords:[\"abandon\",\"evict\",\"broken\",\"building\"],char:\"🏚\",fitzpatrick_scale:false,category:\"travel_and_places\"},office:{keywords:[\"building\",\"bureau\",\"work\"],char:\"🏢\",fitzpatrick_scale:false,category:\"travel_and_places\"},department_store:{keywords:[\"building\",\"shopping\",\"mall\"],char:\"🏬\",fitzpatrick_scale:false,category:\"travel_and_places\"},post_office:{keywords:[\"building\",\"envelope\",\"communication\"],char:\"🏣\",fitzpatrick_scale:false,category:\"travel_and_places\"},european_post_office:{keywords:[\"building\",\"email\"],char:\"🏤\",fitzpatrick_scale:false,category:\"travel_and_places\"},hospital:{keywords:[\"building\",\"health\",\"surgery\",\"doctor\"],char:\"🏥\",fitzpatrick_scale:false,category:\"travel_and_places\"},bank:{keywords:[\"building\",\"money\",\"sales\",\"cash\",\"business\",\"enterprise\"],char:\"🏦\",fitzpatrick_scale:false,category:\"travel_and_places\"},hotel:{keywords:[\"building\",\"accomodation\",\"checkin\"],char:\"🏨\",fitzpatrick_scale:false,category:\"travel_and_places\"},convenience_store:{keywords:[\"building\",\"shopping\",\"groceries\"],char:\"🏪\",fitzpatrick_scale:false,category:\"travel_and_places\"},school:{keywords:[\"building\",\"student\",\"education\",\"learn\",\"teach\"],char:\"🏫\",fitzpatrick_scale:false,category:\"travel_and_places\"},love_hotel:{keywords:[\"like\",\"affection\",\"dating\"],char:\"🏩\",fitzpatrick_scale:false,category:\"travel_and_places\"},wedding:{keywords:[\"love\",\"like\",\"affection\",\"couple\",\"marriage\",\"bride\",\"groom\"],char:\"💒\",fitzpatrick_scale:false,category:\"travel_and_places\"},classical_building:{keywords:[\"art\",\"culture\",\"history\"],char:\"🏛\",fitzpatrick_scale:false,category:\"travel_and_places\"},church:{keywords:[\"building\",\"religion\",\"christ\"],char:\"⛪\",fitzpatrick_scale:false,category:\"travel_and_places\"},mosque:{keywords:[\"islam\",\"worship\",\"minaret\"],char:\"🕌\",fitzpatrick_scale:false,category:\"travel_and_places\"},synagogue:{keywords:[\"judaism\",\"worship\",\"temple\",\"jewish\"],char:\"🕍\",fitzpatrick_scale:false,category:\"travel_and_places\"},kaaba:{keywords:[\"mecca\",\"mosque\",\"islam\"],char:\"🕋\",fitzpatrick_scale:false,category:\"travel_and_places\"},shinto_shrine:{keywords:[\"temple\",\"japan\",\"kyoto\"],char:\"⛩\",fitzpatrick_scale:false,category:\"travel_and_places\"},watch:{keywords:[\"time\",\"accessories\"],char:\"⌚\",fitzpatrick_scale:false,category:\"objects\"},iphone:{keywords:[\"technology\",\"apple\",\"gadgets\",\"dial\"],char:\"📱\",fitzpatrick_scale:false,category:\"objects\"},calling:{keywords:[\"iphone\",\"incoming\"],char:\"📲\",fitzpatrick_scale:false,category:\"objects\"},computer:{keywords:[\"technology\",\"laptop\",\"screen\",\"display\",\"monitor\"],char:\"💻\",fitzpatrick_scale:false,category:\"objects\"},keyboard:{keywords:[\"technology\",\"computer\",\"type\",\"input\",\"text\"],char:\"⌨\",fitzpatrick_scale:false,category:\"objects\"},desktop_computer:{keywords:[\"technology\",\"computing\",\"screen\"],char:\"🖥\",fitzpatrick_scale:false,category:\"objects\"},printer:{keywords:[\"paper\",\"ink\"],char:\"🖨\",fitzpatrick_scale:false,category:\"objects\"},computer_mouse:{keywords:[\"click\"],char:\"🖱\",fitzpatrick_scale:false,category:\"objects\"},trackball:{keywords:[\"technology\",\"trackpad\"],char:\"🖲\",fitzpatrick_scale:false,category:\"objects\"},joystick:{keywords:[\"game\",\"play\"],char:\"🕹\",fitzpatrick_scale:false,category:\"objects\"},clamp:{keywords:[\"tool\"],char:\"🗜\",fitzpatrick_scale:false,category:\"objects\"},minidisc:{keywords:[\"technology\",\"record\",\"data\",\"disk\",\"90s\"],char:\"💽\",fitzpatrick_scale:false,category:\"objects\"},floppy_disk:{keywords:[\"oldschool\",\"technology\",\"save\",\"90s\",\"80s\"],char:\"💾\",fitzpatrick_scale:false,category:\"objects\"},cd:{keywords:[\"technology\",\"dvd\",\"disk\",\"disc\",\"90s\"],char:\"💿\",fitzpatrick_scale:false,category:\"objects\"},dvd:{keywords:[\"cd\",\"disk\",\"disc\"],char:\"📀\",fitzpatrick_scale:false,category:\"objects\"},vhs:{keywords:[\"record\",\"video\",\"oldschool\",\"90s\",\"80s\"],char:\"📼\",fitzpatrick_scale:false,category:\"objects\"},camera:{keywords:[\"gadgets\",\"photography\"],char:\"📷\",fitzpatrick_scale:false,category:\"objects\"},camera_flash:{keywords:[\"photography\",\"gadgets\"],char:\"📸\",fitzpatrick_scale:false,category:\"objects\"},video_camera:{keywords:[\"film\",\"record\"],char:\"📹\",fitzpatrick_scale:false,category:\"objects\"},movie_camera:{keywords:[\"film\",\"record\"],char:\"🎥\",fitzpatrick_scale:false,category:\"objects\"},film_projector:{keywords:[\"video\",\"tape\",\"record\",\"movie\"],char:\"📽\",fitzpatrick_scale:false,category:\"objects\"},film_strip:{keywords:[\"movie\"],char:\"🎞\",fitzpatrick_scale:false,category:\"objects\"},telephone_receiver:{keywords:[\"technology\",\"communication\",\"dial\"],char:\"📞\",fitzpatrick_scale:false,category:\"objects\"},phone:{keywords:[\"technology\",\"communication\",\"dial\",\"telephone\"],char:\"☎️\",fitzpatrick_scale:false,category:\"objects\"},pager:{keywords:[\"bbcall\",\"oldschool\",\"90s\"],char:\"📟\",fitzpatrick_scale:false,category:\"objects\"},fax:{keywords:[\"communication\",\"technology\"],char:\"📠\",fitzpatrick_scale:false,category:\"objects\"},tv:{keywords:[\"technology\",\"program\",\"oldschool\",\"show\",\"television\"],char:\"📺\",fitzpatrick_scale:false,category:\"objects\"},radio:{keywords:[\"communication\",\"music\",\"podcast\",\"program\"],char:\"📻\",fitzpatrick_scale:false,category:\"objects\"},studio_microphone:{keywords:[\"sing\",\"recording\",\"artist\",\"talkshow\"],char:\"🎙\",fitzpatrick_scale:false,category:\"objects\"},level_slider:{keywords:[\"scale\"],char:\"🎚\",fitzpatrick_scale:false,category:\"objects\"},control_knobs:{keywords:[\"dial\"],char:\"🎛\",fitzpatrick_scale:false,category:\"objects\"},compass:{keywords:[\"magnetic\",\"navigation\",\"orienteering\"],char:\"🧭\",fitzpatrick_scale:false,category:\"objects\"},stopwatch:{keywords:[\"time\",\"deadline\"],char:\"⏱\",fitzpatrick_scale:false,category:\"objects\"},timer_clock:{keywords:[\"alarm\"],char:\"⏲\",fitzpatrick_scale:false,category:\"objects\"},alarm_clock:{keywords:[\"time\",\"wake\"],char:\"⏰\",fitzpatrick_scale:false,category:\"objects\"},mantelpiece_clock:{keywords:[\"time\"],char:\"🕰\",fitzpatrick_scale:false,category:\"objects\"},hourglass_flowing_sand:{keywords:[\"oldschool\",\"time\",\"countdown\"],char:\"⏳\",fitzpatrick_scale:false,category:\"objects\"},hourglass:{keywords:[\"time\",\"clock\",\"oldschool\",\"limit\",\"exam\",\"quiz\",\"test\"],char:\"⌛\",fitzpatrick_scale:false,category:\"objects\"},satellite:{keywords:[\"communication\",\"future\",\"radio\",\"space\"],char:\"📡\",fitzpatrick_scale:false,category:\"objects\"},battery:{keywords:[\"power\",\"energy\",\"sustain\"],char:\"🔋\",fitzpatrick_scale:false,category:\"objects\"},electric_plug:{keywords:[\"charger\",\"power\"],char:\"🔌\",fitzpatrick_scale:false,category:\"objects\"},bulb:{keywords:[\"light\",\"electricity\",\"idea\"],char:\"💡\",fitzpatrick_scale:false,category:\"objects\"},flashlight:{keywords:[\"dark\",\"camping\",\"sight\",\"night\"],char:\"🔦\",fitzpatrick_scale:false,category:\"objects\"},candle:{keywords:[\"fire\",\"wax\"],char:\"🕯\",fitzpatrick_scale:false,category:\"objects\"},fire_extinguisher:{keywords:[\"quench\"],char:\"🧯\",fitzpatrick_scale:false,category:\"objects\"},wastebasket:{keywords:[\"bin\",\"trash\",\"rubbish\",\"garbage\",\"toss\"],char:\"🗑\",fitzpatrick_scale:false,category:\"objects\"},oil_drum:{keywords:[\"barrell\"],char:\"🛢\",fitzpatrick_scale:false,category:\"objects\"},money_with_wings:{keywords:[\"dollar\",\"bills\",\"payment\",\"sale\"],char:\"💸\",fitzpatrick_scale:false,category:\"objects\"},dollar:{keywords:[\"money\",\"sales\",\"bill\",\"currency\"],char:\"💵\",fitzpatrick_scale:false,category:\"objects\"},yen:{keywords:[\"money\",\"sales\",\"japanese\",\"dollar\",\"currency\"],char:\"💴\",fitzpatrick_scale:false,category:\"objects\"},euro:{keywords:[\"money\",\"sales\",\"dollar\",\"currency\"],char:\"💶\",fitzpatrick_scale:false,category:\"objects\"},pound:{keywords:[\"british\",\"sterling\",\"money\",\"sales\",\"bills\",\"uk\",\"england\",\"currency\"],char:\"💷\",fitzpatrick_scale:false,category:\"objects\"},moneybag:{keywords:[\"dollar\",\"payment\",\"coins\",\"sale\"],char:\"💰\",fitzpatrick_scale:false,category:\"objects\"},credit_card:{keywords:[\"money\",\"sales\",\"dollar\",\"bill\",\"payment\",\"shopping\"],char:\"💳\",fitzpatrick_scale:false,category:\"objects\"},gem:{keywords:[\"blue\",\"ruby\",\"diamond\",\"jewelry\"],char:\"💎\",fitzpatrick_scale:false,category:\"objects\"},balance_scale:{keywords:[\"law\",\"fairness\",\"weight\"],char:\"⚖\",fitzpatrick_scale:false,category:\"objects\"},toolbox:{keywords:[\"tools\",\"diy\",\"fix\",\"maintainer\",\"mechanic\"],char:\"🧰\",fitzpatrick_scale:false,category:\"objects\"},wrench:{keywords:[\"tools\",\"diy\",\"ikea\",\"fix\",\"maintainer\"],char:\"🔧\",fitzpatrick_scale:false,category:\"objects\"},hammer:{keywords:[\"tools\",\"build\",\"create\"],char:\"🔨\",fitzpatrick_scale:false,category:\"objects\"},hammer_and_pick:{keywords:[\"tools\",\"build\",\"create\"],char:\"⚒\",fitzpatrick_scale:false,category:\"objects\"},hammer_and_wrench:{keywords:[\"tools\",\"build\",\"create\"],char:\"🛠\",fitzpatrick_scale:false,category:\"objects\"},pick:{keywords:[\"tools\",\"dig\"],char:\"⛏\",fitzpatrick_scale:false,category:\"objects\"},nut_and_bolt:{keywords:[\"handy\",\"tools\",\"fix\"],char:\"🔩\",fitzpatrick_scale:false,category:\"objects\"},gear:{keywords:[\"cog\"],char:\"⚙\",fitzpatrick_scale:false,category:\"objects\"},brick:{keywords:[\"bricks\"],char:\"🧱\",fitzpatrick_scale:false,category:\"objects\"},chains:{keywords:[\"lock\",\"arrest\"],char:\"⛓\",fitzpatrick_scale:false,category:\"objects\"},magnet:{keywords:[\"attraction\",\"magnetic\"],char:\"🧲\",fitzpatrick_scale:false,category:\"objects\"},gun:{keywords:[\"violence\",\"weapon\",\"pistol\",\"revolver\"],char:\"🔫\",fitzpatrick_scale:false,category:\"objects\"},bomb:{keywords:[\"boom\",\"explode\",\"explosion\",\"terrorism\"],char:\"💣\",fitzpatrick_scale:false,category:\"objects\"},firecracker:{keywords:[\"dynamite\",\"boom\",\"explode\",\"explosion\",\"explosive\"],char:\"🧨\",fitzpatrick_scale:false,category:\"objects\"},hocho:{keywords:[\"knife\",\"blade\",\"cutlery\",\"kitchen\",\"weapon\"],char:\"🔪\",fitzpatrick_scale:false,category:\"objects\"},dagger:{keywords:[\"weapon\"],char:\"🗡\",fitzpatrick_scale:false,category:\"objects\"},crossed_swords:{keywords:[\"weapon\"],char:\"⚔\",fitzpatrick_scale:false,category:\"objects\"},shield:{keywords:[\"protection\",\"security\"],char:\"🛡\",fitzpatrick_scale:false,category:\"objects\"},smoking:{keywords:[\"kills\",\"tobacco\",\"cigarette\",\"joint\",\"smoke\"],char:\"🚬\",fitzpatrick_scale:false,category:\"objects\"},skull_and_crossbones:{keywords:[\"poison\",\"danger\",\"deadly\",\"scary\",\"death\",\"pirate\",\"evil\"],char:\"☠\",fitzpatrick_scale:false,category:\"objects\"},coffin:{keywords:[\"vampire\",\"dead\",\"die\",\"death\",\"rip\",\"graveyard\",\"cemetery\",\"casket\",\"funeral\",\"box\"],char:\"⚰\",fitzpatrick_scale:false,category:\"objects\"},funeral_urn:{keywords:[\"dead\",\"die\",\"death\",\"rip\",\"ashes\"],char:\"⚱\",fitzpatrick_scale:false,category:\"objects\"},amphora:{keywords:[\"vase\",\"jar\"],char:\"🏺\",fitzpatrick_scale:false,category:\"objects\"},crystal_ball:{keywords:[\"disco\",\"party\",\"magic\",\"circus\",\"fortune_teller\"],char:\"🔮\",fitzpatrick_scale:false,category:\"objects\"},prayer_beads:{keywords:[\"dhikr\",\"religious\"],char:\"📿\",fitzpatrick_scale:false,category:\"objects\"},nazar_amulet:{keywords:[\"bead\",\"charm\"],char:\"🧿\",fitzpatrick_scale:false,category:\"objects\"},barber:{keywords:[\"hair\",\"salon\",\"style\"],char:\"💈\",fitzpatrick_scale:false,category:\"objects\"},alembic:{keywords:[\"distilling\",\"science\",\"experiment\",\"chemistry\"],char:\"⚗\",fitzpatrick_scale:false,category:\"objects\"},telescope:{keywords:[\"stars\",\"space\",\"zoom\",\"science\",\"astronomy\"],char:\"🔭\",fitzpatrick_scale:false,category:\"objects\"},microscope:{keywords:[\"laboratory\",\"experiment\",\"zoomin\",\"science\",\"study\"],char:\"🔬\",fitzpatrick_scale:false,category:\"objects\"},hole:{keywords:[\"embarrassing\"],char:\"🕳\",fitzpatrick_scale:false,category:\"objects\"},pill:{keywords:[\"health\",\"medicine\",\"doctor\",\"pharmacy\",\"drug\"],char:\"💊\",fitzpatrick_scale:false,category:\"objects\"},syringe:{keywords:[\"health\",\"hospital\",\"drugs\",\"blood\",\"medicine\",\"needle\",\"doctor\",\"nurse\"],char:\"💉\",fitzpatrick_scale:false,category:\"objects\"},dna:{keywords:[\"biologist\",\"genetics\",\"life\"],char:\"🧬\",fitzpatrick_scale:false,category:\"objects\"},microbe:{keywords:[\"amoeba\",\"bacteria\",\"germs\"],char:\"🦠\",fitzpatrick_scale:false,category:\"objects\"},petri_dish:{keywords:[\"bacteria\",\"biology\",\"culture\",\"lab\"],char:\"🧫\",fitzpatrick_scale:false,category:\"objects\"},test_tube:{keywords:[\"chemistry\",\"experiment\",\"lab\",\"science\"],char:\"🧪\",fitzpatrick_scale:false,category:\"objects\"},thermometer:{keywords:[\"weather\",\"temperature\",\"hot\",\"cold\"],char:\"🌡\",fitzpatrick_scale:false,category:\"objects\"},broom:{keywords:[\"cleaning\",\"sweeping\",\"witch\"],char:\"🧹\",fitzpatrick_scale:false,category:\"objects\"},basket:{keywords:[\"laundry\"],char:\"🧺\",fitzpatrick_scale:false,category:\"objects\"},toilet_paper:{keywords:[\"roll\"],char:\"🧻\",fitzpatrick_scale:false,category:\"objects\"},label:{keywords:[\"sale\",\"tag\"],char:\"🏷\",fitzpatrick_scale:false,category:\"objects\"},bookmark:{keywords:[\"favorite\",\"label\",\"save\"],char:\"🔖\",fitzpatrick_scale:false,category:\"objects\"},toilet:{keywords:[\"restroom\",\"wc\",\"washroom\",\"bathroom\",\"potty\"],char:\"🚽\",fitzpatrick_scale:false,category:\"objects\"},shower:{keywords:[\"clean\",\"water\",\"bathroom\"],char:\"🚿\",fitzpatrick_scale:false,category:\"objects\"},bathtub:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:\"🛁\",fitzpatrick_scale:false,category:\"objects\"},soap:{keywords:[\"bar\",\"bathing\",\"cleaning\",\"lather\"],char:\"🧼\",fitzpatrick_scale:false,category:\"objects\"},sponge:{keywords:[\"absorbing\",\"cleaning\",\"porous\"],char:\"🧽\",fitzpatrick_scale:false,category:\"objects\"},lotion_bottle:{keywords:[\"moisturizer\",\"sunscreen\"],char:\"🧴\",fitzpatrick_scale:false,category:\"objects\"},key:{keywords:[\"lock\",\"door\",\"password\"],char:\"🔑\",fitzpatrick_scale:false,category:\"objects\"},old_key:{keywords:[\"lock\",\"door\",\"password\"],char:\"🗝\",fitzpatrick_scale:false,category:\"objects\"},couch_and_lamp:{keywords:[\"read\",\"chill\"],char:\"🛋\",fitzpatrick_scale:false,category:\"objects\"},sleeping_bed:{keywords:[\"bed\",\"rest\"],char:\"🛌\",fitzpatrick_scale:true,category:\"objects\"},bed:{keywords:[\"sleep\",\"rest\"],char:\"🛏\",fitzpatrick_scale:false,category:\"objects\"},door:{keywords:[\"house\",\"entry\",\"exit\"],char:\"🚪\",fitzpatrick_scale:false,category:\"objects\"},bellhop_bell:{keywords:[\"service\"],char:\"🛎\",fitzpatrick_scale:false,category:\"objects\"},teddy_bear:{keywords:[\"plush\",\"stuffed\"],char:\"🧸\",fitzpatrick_scale:false,category:\"objects\"},framed_picture:{keywords:[\"photography\"],char:\"🖼\",fitzpatrick_scale:false,category:\"objects\"},world_map:{keywords:[\"location\",\"direction\"],char:\"🗺\",fitzpatrick_scale:false,category:\"objects\"},parasol_on_ground:{keywords:[\"weather\",\"summer\"],char:\"⛱\",fitzpatrick_scale:false,category:\"objects\"},moyai:{keywords:[\"rock\",\"easter island\",\"moai\"],char:\"🗿\",fitzpatrick_scale:false,category:\"objects\"},shopping:{keywords:[\"mall\",\"buy\",\"purchase\"],char:\"🛍\",fitzpatrick_scale:false,category:\"objects\"},shopping_cart:{keywords:[\"trolley\"],char:\"🛒\",fitzpatrick_scale:false,category:\"objects\"},balloon:{keywords:[\"party\",\"celebration\",\"birthday\",\"circus\"],char:\"🎈\",fitzpatrick_scale:false,category:\"objects\"},flags:{keywords:[\"fish\",\"japanese\",\"koinobori\",\"carp\",\"banner\"],char:\"🎏\",fitzpatrick_scale:false,category:\"objects\"},ribbon:{keywords:[\"decoration\",\"pink\",\"girl\",\"bowtie\"],char:\"🎀\",fitzpatrick_scale:false,category:\"objects\"},gift:{keywords:[\"present\",\"birthday\",\"christmas\",\"xmas\"],char:\"🎁\",fitzpatrick_scale:false,category:\"objects\"},confetti_ball:{keywords:[\"festival\",\"party\",\"birthday\",\"circus\"],char:\"🎊\",fitzpatrick_scale:false,category:\"objects\"},tada:{keywords:[\"party\",\"congratulations\",\"birthday\",\"magic\",\"circus\",\"celebration\"],char:\"🎉\",fitzpatrick_scale:false,category:\"objects\"},dolls:{keywords:[\"japanese\",\"toy\",\"kimono\"],char:\"🎎\",fitzpatrick_scale:false,category:\"objects\"},wind_chime:{keywords:[\"nature\",\"ding\",\"spring\",\"bell\"],char:\"🎐\",fitzpatrick_scale:false,category:\"objects\"},crossed_flags:{keywords:[\"japanese\",\"nation\",\"country\",\"border\"],char:\"🎌\",fitzpatrick_scale:false,category:\"objects\"},izakaya_lantern:{keywords:[\"light\",\"paper\",\"halloween\",\"spooky\"],char:\"🏮\",fitzpatrick_scale:false,category:\"objects\"},red_envelope:{keywords:[\"gift\"],char:\"🧧\",fitzpatrick_scale:false,category:\"objects\"},email:{keywords:[\"letter\",\"postal\",\"inbox\",\"communication\"],char:\"✉️\",fitzpatrick_scale:false,category:\"objects\"},envelope_with_arrow:{keywords:[\"email\",\"communication\"],char:\"📩\",fitzpatrick_scale:false,category:\"objects\"},incoming_envelope:{keywords:[\"email\",\"inbox\"],char:\"📨\",fitzpatrick_scale:false,category:\"objects\"},\"e-mail\":{keywords:[\"communication\",\"inbox\"],char:\"📧\",fitzpatrick_scale:false,category:\"objects\"},love_letter:{keywords:[\"email\",\"like\",\"affection\",\"envelope\",\"valentines\"],char:\"💌\",fitzpatrick_scale:false,category:\"objects\"},postbox:{keywords:[\"email\",\"letter\",\"envelope\"],char:\"📮\",fitzpatrick_scale:false,category:\"objects\"},mailbox_closed:{keywords:[\"email\",\"communication\",\"inbox\"],char:\"📪\",fitzpatrick_scale:false,category:\"objects\"},mailbox:{keywords:[\"email\",\"inbox\",\"communication\"],char:\"📫\",fitzpatrick_scale:false,category:\"objects\"},mailbox_with_mail:{keywords:[\"email\",\"inbox\",\"communication\"],char:\"📬\",fitzpatrick_scale:false,category:\"objects\"},mailbox_with_no_mail:{keywords:[\"email\",\"inbox\"],char:\"📭\",fitzpatrick_scale:false,category:\"objects\"},package:{keywords:[\"mail\",\"gift\",\"cardboard\",\"box\",\"moving\"],char:\"📦\",fitzpatrick_scale:false,category:\"objects\"},postal_horn:{keywords:[\"instrument\",\"music\"],char:\"📯\",fitzpatrick_scale:false,category:\"objects\"},inbox_tray:{keywords:[\"email\",\"documents\"],char:\"📥\",fitzpatrick_scale:false,category:\"objects\"},outbox_tray:{keywords:[\"inbox\",\"email\"],char:\"📤\",fitzpatrick_scale:false,category:\"objects\"},scroll:{keywords:[\"documents\",\"ancient\",\"history\",\"paper\"],char:\"📜\",fitzpatrick_scale:false,category:\"objects\"},page_with_curl:{keywords:[\"documents\",\"office\",\"paper\"],char:\"📃\",fitzpatrick_scale:false,category:\"objects\"},bookmark_tabs:{keywords:[\"favorite\",\"save\",\"order\",\"tidy\"],char:\"📑\",fitzpatrick_scale:false,category:\"objects\"},receipt:{keywords:[\"accounting\",\"expenses\"],char:\"🧾\",fitzpatrick_scale:false,category:\"objects\"},bar_chart:{keywords:[\"graph\",\"presentation\",\"stats\"],char:\"📊\",fitzpatrick_scale:false,category:\"objects\"},chart_with_upwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recovery\",\"business\",\"economics\",\"money\",\"sales\",\"good\",\"success\"],char:\"📈\",fitzpatrick_scale:false,category:\"objects\"},chart_with_downwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recession\",\"business\",\"economics\",\"money\",\"sales\",\"bad\",\"failure\"],char:\"📉\",fitzpatrick_scale:false,category:\"objects\"},page_facing_up:{keywords:[\"documents\",\"office\",\"paper\",\"information\"],char:\"📄\",fitzpatrick_scale:false,category:\"objects\"},date:{keywords:[\"calendar\",\"schedule\"],char:\"📅\",fitzpatrick_scale:false,category:\"objects\"},calendar:{keywords:[\"schedule\",\"date\",\"planning\"],char:\"📆\",fitzpatrick_scale:false,category:\"objects\"},spiral_calendar:{keywords:[\"date\",\"schedule\",\"planning\"],char:\"🗓\",fitzpatrick_scale:false,category:\"objects\"},card_index:{keywords:[\"business\",\"stationery\"],char:\"📇\",fitzpatrick_scale:false,category:\"objects\"},card_file_box:{keywords:[\"business\",\"stationery\"],char:\"🗃\",fitzpatrick_scale:false,category:\"objects\"},ballot_box:{keywords:[\"election\",\"vote\"],char:\"🗳\",fitzpatrick_scale:false,category:\"objects\"},file_cabinet:{keywords:[\"filing\",\"organizing\"],char:\"🗄\",fitzpatrick_scale:false,category:\"objects\"},clipboard:{keywords:[\"stationery\",\"documents\"],char:\"📋\",fitzpatrick_scale:false,category:\"objects\"},spiral_notepad:{keywords:[\"memo\",\"stationery\"],char:\"🗒\",fitzpatrick_scale:false,category:\"objects\"},file_folder:{keywords:[\"documents\",\"business\",\"office\"],char:\"📁\",fitzpatrick_scale:false,category:\"objects\"},open_file_folder:{keywords:[\"documents\",\"load\"],char:\"📂\",fitzpatrick_scale:false,category:\"objects\"},card_index_dividers:{keywords:[\"organizing\",\"business\",\"stationery\"],char:\"🗂\",fitzpatrick_scale:false,category:\"objects\"},newspaper_roll:{keywords:[\"press\",\"headline\"],char:\"🗞\",fitzpatrick_scale:false,category:\"objects\"},newspaper:{keywords:[\"press\",\"headline\"],char:\"📰\",fitzpatrick_scale:false,category:\"objects\"},notebook:{keywords:[\"stationery\",\"record\",\"notes\",\"paper\",\"study\"],char:\"📓\",fitzpatrick_scale:false,category:\"objects\"},closed_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"learn\"],char:\"📕\",fitzpatrick_scale:false,category:\"objects\"},green_book:{keywords:[\"read\",\"library\",\"knowledge\",\"study\"],char:\"📗\",fitzpatrick_scale:false,category:\"objects\"},blue_book:{keywords:[\"read\",\"library\",\"knowledge\",\"learn\",\"study\"],char:\"📘\",fitzpatrick_scale:false,category:\"objects\"},orange_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"study\"],char:\"📙\",fitzpatrick_scale:false,category:\"objects\"},notebook_with_decorative_cover:{keywords:[\"classroom\",\"notes\",\"record\",\"paper\",\"study\"],char:\"📔\",fitzpatrick_scale:false,category:\"objects\"},ledger:{keywords:[\"notes\",\"paper\"],char:\"📒\",fitzpatrick_scale:false,category:\"objects\"},books:{keywords:[\"literature\",\"library\",\"study\"],char:\"📚\",fitzpatrick_scale:false,category:\"objects\"},open_book:{keywords:[\"book\",\"read\",\"library\",\"knowledge\",\"literature\",\"learn\",\"study\"],char:\"📖\",fitzpatrick_scale:false,category:\"objects\"},safety_pin:{keywords:[\"diaper\"],char:\"🧷\",fitzpatrick_scale:false,category:\"objects\"},link:{keywords:[\"rings\",\"url\"],char:\"🔗\",fitzpatrick_scale:false,category:\"objects\"},paperclip:{keywords:[\"documents\",\"stationery\"],char:\"📎\",fitzpatrick_scale:false,category:\"objects\"},paperclips:{keywords:[\"documents\",\"stationery\"],char:\"🖇\",fitzpatrick_scale:false,category:\"objects\"},scissors:{keywords:[\"stationery\",\"cut\"],char:\"✂️\",fitzpatrick_scale:false,category:\"objects\"},triangular_ruler:{keywords:[\"stationery\",\"math\",\"architect\",\"sketch\"],char:\"📐\",fitzpatrick_scale:false,category:\"objects\"},straight_ruler:{keywords:[\"stationery\",\"calculate\",\"length\",\"math\",\"school\",\"drawing\",\"architect\",\"sketch\"],char:\"📏\",fitzpatrick_scale:false,category:\"objects\"},abacus:{keywords:[\"calculation\"],char:\"🧮\",fitzpatrick_scale:false,category:\"objects\"},pushpin:{keywords:[\"stationery\",\"mark\",\"here\"],char:\"📌\",fitzpatrick_scale:false,category:\"objects\"},round_pushpin:{keywords:[\"stationery\",\"location\",\"map\",\"here\"],char:\"📍\",fitzpatrick_scale:false,category:\"objects\"},triangular_flag_on_post:{keywords:[\"mark\",\"milestone\",\"place\"],char:\"🚩\",fitzpatrick_scale:false,category:\"objects\"},white_flag:{keywords:[\"losing\",\"loser\",\"lost\",\"surrender\",\"give up\",\"fail\"],char:\"🏳\",fitzpatrick_scale:false,category:\"objects\"},black_flag:{keywords:[\"pirate\"],char:\"🏴\",fitzpatrick_scale:false,category:\"objects\"},rainbow_flag:{keywords:[\"flag\",\"rainbow\",\"pride\",\"gay\",\"lgbt\",\"glbt\",\"queer\",\"homosexual\",\"lesbian\",\"bisexual\",\"transgender\"],char:\"🏳️‍🌈\",fitzpatrick_scale:false,category:\"objects\"},closed_lock_with_key:{keywords:[\"security\",\"privacy\"],char:\"🔐\",fitzpatrick_scale:false,category:\"objects\"},lock:{keywords:[\"security\",\"password\",\"padlock\"],char:\"🔒\",fitzpatrick_scale:false,category:\"objects\"},unlock:{keywords:[\"privacy\",\"security\"],char:\"🔓\",fitzpatrick_scale:false,category:\"objects\"},lock_with_ink_pen:{keywords:[\"security\",\"secret\"],char:\"🔏\",fitzpatrick_scale:false,category:\"objects\"},pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:\"🖊\",fitzpatrick_scale:false,category:\"objects\"},fountain_pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:\"🖋\",fitzpatrick_scale:false,category:\"objects\"},black_nib:{keywords:[\"pen\",\"stationery\",\"writing\",\"write\"],char:\"✒️\",fitzpatrick_scale:false,category:\"objects\"},memo:{keywords:[\"write\",\"documents\",\"stationery\",\"pencil\",\"paper\",\"writing\",\"legal\",\"exam\",\"quiz\",\"test\",\"study\",\"compose\"],char:\"📝\",fitzpatrick_scale:false,category:\"objects\"},pencil2:{keywords:[\"stationery\",\"write\",\"paper\",\"writing\",\"school\",\"study\"],char:\"✏️\",fitzpatrick_scale:false,category:\"objects\"},crayon:{keywords:[\"drawing\",\"creativity\"],char:\"🖍\",fitzpatrick_scale:false,category:\"objects\"},paintbrush:{keywords:[\"drawing\",\"creativity\",\"art\"],char:\"🖌\",fitzpatrick_scale:false,category:\"objects\"},mag:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:\"🔍\",fitzpatrick_scale:false,category:\"objects\"},mag_right:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:\"🔎\",fitzpatrick_scale:false,category:\"objects\"},heart:{keywords:[\"love\",\"like\",\"valentines\"],char:\"❤️\",fitzpatrick_scale:false,category:\"symbols\"},orange_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"🧡\",fitzpatrick_scale:false,category:\"symbols\"},yellow_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💛\",fitzpatrick_scale:false,category:\"symbols\"},green_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💚\",fitzpatrick_scale:false,category:\"symbols\"},blue_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💙\",fitzpatrick_scale:false,category:\"symbols\"},purple_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💜\",fitzpatrick_scale:false,category:\"symbols\"},black_heart:{keywords:[\"evil\"],char:\"🖤\",fitzpatrick_scale:false,category:\"symbols\"},broken_heart:{keywords:[\"sad\",\"sorry\",\"break\",\"heart\",\"heartbreak\"],char:\"💔\",fitzpatrick_scale:false,category:\"symbols\"},heavy_heart_exclamation:{keywords:[\"decoration\",\"love\"],char:\"❣\",fitzpatrick_scale:false,category:\"symbols\"},two_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"heart\"],char:\"💕\",fitzpatrick_scale:false,category:\"symbols\"},revolving_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💞\",fitzpatrick_scale:false,category:\"symbols\"},heartbeat:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"pink\",\"heart\"],char:\"💓\",fitzpatrick_scale:false,category:\"symbols\"},heartpulse:{keywords:[\"like\",\"love\",\"affection\",\"valentines\",\"pink\"],char:\"💗\",fitzpatrick_scale:false,category:\"symbols\"},sparkling_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💖\",fitzpatrick_scale:false,category:\"symbols\"},cupid:{keywords:[\"love\",\"like\",\"heart\",\"affection\",\"valentines\"],char:\"💘\",fitzpatrick_scale:false,category:\"symbols\"},gift_heart:{keywords:[\"love\",\"valentines\"],char:\"💝\",fitzpatrick_scale:false,category:\"symbols\"},heart_decoration:{keywords:[\"purple-square\",\"love\",\"like\"],char:\"💟\",fitzpatrick_scale:false,category:\"symbols\"},peace_symbol:{keywords:[\"hippie\"],char:\"☮\",fitzpatrick_scale:false,category:\"symbols\"},latin_cross:{keywords:[\"christianity\"],char:\"✝\",fitzpatrick_scale:false,category:\"symbols\"},star_and_crescent:{keywords:[\"islam\"],char:\"☪\",fitzpatrick_scale:false,category:\"symbols\"},om:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:\"🕉\",fitzpatrick_scale:false,category:\"symbols\"},wheel_of_dharma:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:\"☸\",fitzpatrick_scale:false,category:\"symbols\"},star_of_david:{keywords:[\"judaism\"],char:\"✡\",fitzpatrick_scale:false,category:\"symbols\"},six_pointed_star:{keywords:[\"purple-square\",\"religion\",\"jewish\",\"hexagram\"],char:\"🔯\",fitzpatrick_scale:false,category:\"symbols\"},menorah:{keywords:[\"hanukkah\",\"candles\",\"jewish\"],char:\"🕎\",fitzpatrick_scale:false,category:\"symbols\"},yin_yang:{keywords:[\"balance\"],char:\"☯\",fitzpatrick_scale:false,category:\"symbols\"},orthodox_cross:{keywords:[\"suppedaneum\",\"religion\"],char:\"☦\",fitzpatrick_scale:false,category:\"symbols\"},place_of_worship:{keywords:[\"religion\",\"church\",\"temple\",\"prayer\"],char:\"🛐\",fitzpatrick_scale:false,category:\"symbols\"},ophiuchus:{keywords:[\"sign\",\"purple-square\",\"constellation\",\"astrology\"],char:\"⛎\",fitzpatrick_scale:false,category:\"symbols\"},aries:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♈\",fitzpatrick_scale:false,category:\"symbols\"},taurus:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:\"♉\",fitzpatrick_scale:false,category:\"symbols\"},gemini:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♊\",fitzpatrick_scale:false,category:\"symbols\"},cancer:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♋\",fitzpatrick_scale:false,category:\"symbols\"},leo:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♌\",fitzpatrick_scale:false,category:\"symbols\"},virgo:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♍\",fitzpatrick_scale:false,category:\"symbols\"},libra:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♎\",fitzpatrick_scale:false,category:\"symbols\"},scorpius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\",\"scorpio\"],char:\"♏\",fitzpatrick_scale:false,category:\"symbols\"},sagittarius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♐\",fitzpatrick_scale:false,category:\"symbols\"},capricorn:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♑\",fitzpatrick_scale:false,category:\"symbols\"},aquarius:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♒\",fitzpatrick_scale:false,category:\"symbols\"},pisces:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:\"♓\",fitzpatrick_scale:false,category:\"symbols\"},id:{keywords:[\"purple-square\",\"words\"],char:\"🆔\",fitzpatrick_scale:false,category:\"symbols\"},atom_symbol:{keywords:[\"science\",\"physics\",\"chemistry\"],char:\"⚛\",fitzpatrick_scale:false,category:\"symbols\"},u7a7a:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"empty\",\"sky\",\"blue-square\"],char:\"🈳\",fitzpatrick_scale:false,category:\"symbols\"},u5272:{keywords:[\"cut\",\"divide\",\"chinese\",\"kanji\",\"pink-square\"],char:\"🈹\",fitzpatrick_scale:false,category:\"symbols\"},radioactive:{keywords:[\"nuclear\",\"danger\"],char:\"☢\",fitzpatrick_scale:false,category:\"symbols\"},biohazard:{keywords:[\"danger\"],char:\"☣\",fitzpatrick_scale:false,category:\"symbols\"},mobile_phone_off:{keywords:[\"mute\",\"orange-square\",\"silence\",\"quiet\"],char:\"📴\",fitzpatrick_scale:false,category:\"symbols\"},vibration_mode:{keywords:[\"orange-square\",\"phone\"],char:\"📳\",fitzpatrick_scale:false,category:\"symbols\"},u6709:{keywords:[\"orange-square\",\"chinese\",\"have\",\"kanji\"],char:\"🈶\",fitzpatrick_scale:false,category:\"symbols\"},u7121:{keywords:[\"nothing\",\"chinese\",\"kanji\",\"japanese\",\"orange-square\"],char:\"🈚\",fitzpatrick_scale:false,category:\"symbols\"},u7533:{keywords:[\"chinese\",\"japanese\",\"kanji\",\"orange-square\"],char:\"🈸\",fitzpatrick_scale:false,category:\"symbols\"},u55b6:{keywords:[\"japanese\",\"opening hours\",\"orange-square\"],char:\"🈺\",fitzpatrick_scale:false,category:\"symbols\"},u6708:{keywords:[\"chinese\",\"month\",\"moon\",\"japanese\",\"orange-square\",\"kanji\"],char:\"🈷️\",fitzpatrick_scale:false,category:\"symbols\"},eight_pointed_black_star:{keywords:[\"orange-square\",\"shape\",\"polygon\"],char:\"✴️\",fitzpatrick_scale:false,category:\"symbols\"},vs:{keywords:[\"words\",\"orange-square\"],char:\"🆚\",fitzpatrick_scale:false,category:\"symbols\"},accept:{keywords:[\"ok\",\"good\",\"chinese\",\"kanji\",\"agree\",\"yes\",\"orange-circle\"],char:\"🉑\",fitzpatrick_scale:false,category:\"symbols\"},white_flower:{keywords:[\"japanese\",\"spring\"],char:\"💮\",fitzpatrick_scale:false,category:\"symbols\"},ideograph_advantage:{keywords:[\"chinese\",\"kanji\",\"obtain\",\"get\",\"circle\"],char:\"🉐\",fitzpatrick_scale:false,category:\"symbols\"},secret:{keywords:[\"privacy\",\"chinese\",\"sshh\",\"kanji\",\"red-circle\"],char:\"㊙️\",fitzpatrick_scale:false,category:\"symbols\"},congratulations:{keywords:[\"chinese\",\"kanji\",\"japanese\",\"red-circle\"],char:\"㊗️\",fitzpatrick_scale:false,category:\"symbols\"},u5408:{keywords:[\"japanese\",\"chinese\",\"join\",\"kanji\",\"red-square\"],char:\"🈴\",fitzpatrick_scale:false,category:\"symbols\"},u6e80:{keywords:[\"full\",\"chinese\",\"japanese\",\"red-square\",\"kanji\"],char:\"🈵\",fitzpatrick_scale:false,category:\"symbols\"},u7981:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"forbidden\",\"limit\",\"restricted\",\"red-square\"],char:\"🈲\",fitzpatrick_scale:false,category:\"symbols\"},a:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:\"🅰️\",fitzpatrick_scale:false,category:\"symbols\"},b:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:\"🅱️\",fitzpatrick_scale:false,category:\"symbols\"},ab:{keywords:[\"red-square\",\"alphabet\"],char:\"🆎\",fitzpatrick_scale:false,category:\"symbols\"},cl:{keywords:[\"alphabet\",\"words\",\"red-square\"],char:\"🆑\",fitzpatrick_scale:false,category:\"symbols\"},o2:{keywords:[\"alphabet\",\"red-square\",\"letter\"],char:\"🅾️\",fitzpatrick_scale:false,category:\"symbols\"},sos:{keywords:[\"help\",\"red-square\",\"words\",\"emergency\",\"911\"],char:\"🆘\",fitzpatrick_scale:false,category:\"symbols\"},no_entry:{keywords:[\"limit\",\"security\",\"privacy\",\"bad\",\"denied\",\"stop\",\"circle\"],char:\"⛔\",fitzpatrick_scale:false,category:\"symbols\"},name_badge:{keywords:[\"fire\",\"forbid\"],char:\"📛\",fitzpatrick_scale:false,category:\"symbols\"},no_entry_sign:{keywords:[\"forbid\",\"stop\",\"limit\",\"denied\",\"disallow\",\"circle\"],char:\"🚫\",fitzpatrick_scale:false,category:\"symbols\"},x:{keywords:[\"no\",\"delete\",\"remove\",\"cancel\",\"red\"],char:\"❌\",fitzpatrick_scale:false,category:\"symbols\"},o:{keywords:[\"circle\",\"round\"],char:\"⭕\",fitzpatrick_scale:false,category:\"symbols\"},stop_sign:{keywords:[\"stop\"],char:\"🛑\",fitzpatrick_scale:false,category:\"symbols\"},anger:{keywords:[\"angry\",\"mad\"],char:\"💢\",fitzpatrick_scale:false,category:\"symbols\"},hotsprings:{keywords:[\"bath\",\"warm\",\"relax\"],char:\"♨️\",fitzpatrick_scale:false,category:\"symbols\"},no_pedestrians:{keywords:[\"rules\",\"crossing\",\"walking\",\"circle\"],char:\"🚷\",fitzpatrick_scale:false,category:\"symbols\"},do_not_litter:{keywords:[\"trash\",\"bin\",\"garbage\",\"circle\"],char:\"🚯\",fitzpatrick_scale:false,category:\"symbols\"},no_bicycles:{keywords:[\"cyclist\",\"prohibited\",\"circle\"],char:\"🚳\",fitzpatrick_scale:false,category:\"symbols\"},\"non-potable_water\":{keywords:[\"drink\",\"faucet\",\"tap\",\"circle\"],char:\"🚱\",fitzpatrick_scale:false,category:\"symbols\"},underage:{keywords:[\"18\",\"drink\",\"pub\",\"night\",\"minor\",\"circle\"],char:\"🔞\",fitzpatrick_scale:false,category:\"symbols\"},no_mobile_phones:{keywords:[\"iphone\",\"mute\",\"circle\"],char:\"📵\",fitzpatrick_scale:false,category:\"symbols\"},exclamation:{keywords:[\"heavy_exclamation_mark\",\"danger\",\"surprise\",\"punctuation\",\"wow\",\"warning\"],char:\"❗\",fitzpatrick_scale:false,category:\"symbols\"},grey_exclamation:{keywords:[\"surprise\",\"punctuation\",\"gray\",\"wow\",\"warning\"],char:\"❕\",fitzpatrick_scale:false,category:\"symbols\"},question:{keywords:[\"doubt\",\"confused\"],char:\"❓\",fitzpatrick_scale:false,category:\"symbols\"},grey_question:{keywords:[\"doubts\",\"gray\",\"huh\",\"confused\"],char:\"❔\",fitzpatrick_scale:false,category:\"symbols\"},bangbang:{keywords:[\"exclamation\",\"surprise\"],char:\"‼️\",fitzpatrick_scale:false,category:\"symbols\"},interrobang:{keywords:[\"wat\",\"punctuation\",\"surprise\"],char:\"⁉️\",fitzpatrick_scale:false,category:\"symbols\"},100:{keywords:[\"score\",\"perfect\",\"numbers\",\"century\",\"exam\",\"quiz\",\"test\",\"pass\",\"hundred\"],char:\"💯\",fitzpatrick_scale:false,category:\"symbols\"},low_brightness:{keywords:[\"sun\",\"afternoon\",\"warm\",\"summer\"],char:\"🔅\",fitzpatrick_scale:false,category:\"symbols\"},high_brightness:{keywords:[\"sun\",\"light\"],char:\"🔆\",fitzpatrick_scale:false,category:\"symbols\"},trident:{keywords:[\"weapon\",\"spear\"],char:\"🔱\",fitzpatrick_scale:false,category:\"symbols\"},fleur_de_lis:{keywords:[\"decorative\",\"scout\"],char:\"⚜\",fitzpatrick_scale:false,category:\"symbols\"},part_alternation_mark:{keywords:[\"graph\",\"presentation\",\"stats\",\"business\",\"economics\",\"bad\"],char:\"〽️\",fitzpatrick_scale:false,category:\"symbols\"},warning:{keywords:[\"exclamation\",\"wip\",\"alert\",\"error\",\"problem\",\"issue\"],char:\"⚠️\",fitzpatrick_scale:false,category:\"symbols\"},children_crossing:{keywords:[\"school\",\"warning\",\"danger\",\"sign\",\"driving\",\"yellow-diamond\"],char:\"🚸\",fitzpatrick_scale:false,category:\"symbols\"},beginner:{keywords:[\"badge\",\"shield\"],char:\"🔰\",fitzpatrick_scale:false,category:\"symbols\"},recycle:{keywords:[\"arrow\",\"environment\",\"garbage\",\"trash\"],char:\"♻️\",fitzpatrick_scale:false,category:\"symbols\"},u6307:{keywords:[\"chinese\",\"point\",\"green-square\",\"kanji\"],char:\"🈯\",fitzpatrick_scale:false,category:\"symbols\"},chart:{keywords:[\"green-square\",\"graph\",\"presentation\",\"stats\"],char:\"💹\",fitzpatrick_scale:false,category:\"symbols\"},sparkle:{keywords:[\"stars\",\"green-square\",\"awesome\",\"good\",\"fireworks\"],char:\"❇️\",fitzpatrick_scale:false,category:\"symbols\"},eight_spoked_asterisk:{keywords:[\"star\",\"sparkle\",\"green-square\"],char:\"✳️\",fitzpatrick_scale:false,category:\"symbols\"},negative_squared_cross_mark:{keywords:[\"x\",\"green-square\",\"no\",\"deny\"],char:\"❎\",fitzpatrick_scale:false,category:\"symbols\"},white_check_mark:{keywords:[\"green-square\",\"ok\",\"agree\",\"vote\",\"election\",\"answer\",\"tick\"],char:\"✅\",fitzpatrick_scale:false,category:\"symbols\"},diamond_shape_with_a_dot_inside:{keywords:[\"jewel\",\"blue\",\"gem\",\"crystal\",\"fancy\"],char:\"💠\",fitzpatrick_scale:false,category:\"symbols\"},cyclone:{keywords:[\"weather\",\"swirl\",\"blue\",\"cloud\",\"vortex\",\"spiral\",\"whirlpool\",\"spin\",\"tornado\",\"hurricane\",\"typhoon\"],char:\"🌀\",fitzpatrick_scale:false,category:\"symbols\"},loop:{keywords:[\"tape\",\"cassette\"],char:\"➿\",fitzpatrick_scale:false,category:\"symbols\"},globe_with_meridians:{keywords:[\"earth\",\"international\",\"world\",\"internet\",\"interweb\",\"i18n\"],char:\"🌐\",fitzpatrick_scale:false,category:\"symbols\"},m:{keywords:[\"alphabet\",\"blue-circle\",\"letter\"],char:\"Ⓜ️\",fitzpatrick_scale:false,category:\"symbols\"},atm:{keywords:[\"money\",\"sales\",\"cash\",\"blue-square\",\"payment\",\"bank\"],char:\"🏧\",fitzpatrick_scale:false,category:\"symbols\"},sa:{keywords:[\"japanese\",\"blue-square\",\"katakana\"],char:\"🈂️\",fitzpatrick_scale:false,category:\"symbols\"},passport_control:{keywords:[\"custom\",\"blue-square\"],char:\"🛂\",fitzpatrick_scale:false,category:\"symbols\"},customs:{keywords:[\"passport\",\"border\",\"blue-square\"],char:\"🛃\",fitzpatrick_scale:false,category:\"symbols\"},baggage_claim:{keywords:[\"blue-square\",\"airport\",\"transport\"],char:\"🛄\",fitzpatrick_scale:false,category:\"symbols\"},left_luggage:{keywords:[\"blue-square\",\"travel\"],char:\"🛅\",fitzpatrick_scale:false,category:\"symbols\"},wheelchair:{keywords:[\"blue-square\",\"disabled\",\"a11y\",\"accessibility\"],char:\"♿\",fitzpatrick_scale:false,category:\"symbols\"},no_smoking:{keywords:[\"cigarette\",\"blue-square\",\"smell\",\"smoke\"],char:\"🚭\",fitzpatrick_scale:false,category:\"symbols\"},wc:{keywords:[\"toilet\",\"restroom\",\"blue-square\"],char:\"🚾\",fitzpatrick_scale:false,category:\"symbols\"},parking:{keywords:[\"cars\",\"blue-square\",\"alphabet\",\"letter\"],char:\"🅿️\",fitzpatrick_scale:false,category:\"symbols\"},potable_water:{keywords:[\"blue-square\",\"liquid\",\"restroom\",\"cleaning\",\"faucet\"],char:\"🚰\",fitzpatrick_scale:false,category:\"symbols\"},mens:{keywords:[\"toilet\",\"restroom\",\"wc\",\"blue-square\",\"gender\",\"male\"],char:\"🚹\",fitzpatrick_scale:false,category:\"symbols\"},womens:{keywords:[\"purple-square\",\"woman\",\"female\",\"toilet\",\"loo\",\"restroom\",\"gender\"],char:\"🚺\",fitzpatrick_scale:false,category:\"symbols\"},baby_symbol:{keywords:[\"orange-square\",\"child\"],char:\"🚼\",fitzpatrick_scale:false,category:\"symbols\"},restroom:{keywords:[\"blue-square\",\"toilet\",\"refresh\",\"wc\",\"gender\"],char:\"🚻\",fitzpatrick_scale:false,category:\"symbols\"},put_litter_in_its_place:{keywords:[\"blue-square\",\"sign\",\"human\",\"info\"],char:\"🚮\",fitzpatrick_scale:false,category:\"symbols\"},cinema:{keywords:[\"blue-square\",\"record\",\"film\",\"movie\",\"curtain\",\"stage\",\"theater\"],char:\"🎦\",fitzpatrick_scale:false,category:\"symbols\"},signal_strength:{keywords:[\"blue-square\",\"reception\",\"phone\",\"internet\",\"connection\",\"wifi\",\"bluetooth\",\"bars\"],char:\"📶\",fitzpatrick_scale:false,category:\"symbols\"},koko:{keywords:[\"blue-square\",\"here\",\"katakana\",\"japanese\",\"destination\"],char:\"🈁\",fitzpatrick_scale:false,category:\"symbols\"},ng:{keywords:[\"blue-square\",\"words\",\"shape\",\"icon\"],char:\"🆖\",fitzpatrick_scale:false,category:\"symbols\"},ok:{keywords:[\"good\",\"agree\",\"yes\",\"blue-square\"],char:\"🆗\",fitzpatrick_scale:false,category:\"symbols\"},up:{keywords:[\"blue-square\",\"above\",\"high\"],char:\"🆙\",fitzpatrick_scale:false,category:\"symbols\"},cool:{keywords:[\"words\",\"blue-square\"],char:\"🆒\",fitzpatrick_scale:false,category:\"symbols\"},new:{keywords:[\"blue-square\",\"words\",\"start\"],char:\"🆕\",fitzpatrick_scale:false,category:\"symbols\"},free:{keywords:[\"blue-square\",\"words\"],char:\"🆓\",fitzpatrick_scale:false,category:\"symbols\"},zero:{keywords:[\"0\",\"numbers\",\"blue-square\",\"null\"],char:\"0️⃣\",fitzpatrick_scale:false,category:\"symbols\"},one:{keywords:[\"blue-square\",\"numbers\",\"1\"],char:\"1️⃣\",fitzpatrick_scale:false,category:\"symbols\"},two:{keywords:[\"numbers\",\"2\",\"prime\",\"blue-square\"],char:\"2️⃣\",fitzpatrick_scale:false,category:\"symbols\"},three:{keywords:[\"3\",\"numbers\",\"prime\",\"blue-square\"],char:\"3️⃣\",fitzpatrick_scale:false,category:\"symbols\"},four:{keywords:[\"4\",\"numbers\",\"blue-square\"],char:\"4️⃣\",fitzpatrick_scale:false,category:\"symbols\"},five:{keywords:[\"5\",\"numbers\",\"blue-square\",\"prime\"],char:\"5️⃣\",fitzpatrick_scale:false,category:\"symbols\"},six:{keywords:[\"6\",\"numbers\",\"blue-square\"],char:\"6️⃣\",fitzpatrick_scale:false,category:\"symbols\"},seven:{keywords:[\"7\",\"numbers\",\"blue-square\",\"prime\"],char:\"7️⃣\",fitzpatrick_scale:false,category:\"symbols\"},eight:{keywords:[\"8\",\"blue-square\",\"numbers\"],char:\"8️⃣\",fitzpatrick_scale:false,category:\"symbols\"},nine:{keywords:[\"blue-square\",\"numbers\",\"9\"],char:\"9️⃣\",fitzpatrick_scale:false,category:\"symbols\"},keycap_ten:{keywords:[\"numbers\",\"10\",\"blue-square\"],char:\"🔟\",fitzpatrick_scale:false,category:\"symbols\"},asterisk:{keywords:[\"star\",\"keycap\"],char:\"*⃣\",fitzpatrick_scale:false,category:\"symbols\"},1234:{keywords:[\"numbers\",\"blue-square\"],char:\"🔢\",fitzpatrick_scale:false,category:\"symbols\"},eject_button:{keywords:[\"blue-square\"],char:\"⏏️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_forward:{keywords:[\"blue-square\",\"right\",\"direction\",\"play\"],char:\"▶️\",fitzpatrick_scale:false,category:\"symbols\"},pause_button:{keywords:[\"pause\",\"blue-square\"],char:\"⏸\",fitzpatrick_scale:false,category:\"symbols\"},next_track_button:{keywords:[\"forward\",\"next\",\"blue-square\"],char:\"⏭\",fitzpatrick_scale:false,category:\"symbols\"},stop_button:{keywords:[\"blue-square\"],char:\"⏹\",fitzpatrick_scale:false,category:\"symbols\"},record_button:{keywords:[\"blue-square\"],char:\"⏺\",fitzpatrick_scale:false,category:\"symbols\"},play_or_pause_button:{keywords:[\"blue-square\",\"play\",\"pause\"],char:\"⏯\",fitzpatrick_scale:false,category:\"symbols\"},previous_track_button:{keywords:[\"backward\"],char:\"⏮\",fitzpatrick_scale:false,category:\"symbols\"},fast_forward:{keywords:[\"blue-square\",\"play\",\"speed\",\"continue\"],char:\"⏩\",fitzpatrick_scale:false,category:\"symbols\"},rewind:{keywords:[\"play\",\"blue-square\"],char:\"⏪\",fitzpatrick_scale:false,category:\"symbols\"},twisted_rightwards_arrows:{keywords:[\"blue-square\",\"shuffle\",\"music\",\"random\"],char:\"🔀\",fitzpatrick_scale:false,category:\"symbols\"},repeat:{keywords:[\"loop\",\"record\"],char:\"🔁\",fitzpatrick_scale:false,category:\"symbols\"},repeat_one:{keywords:[\"blue-square\",\"loop\"],char:\"🔂\",fitzpatrick_scale:false,category:\"symbols\"},arrow_backward:{keywords:[\"blue-square\",\"left\",\"direction\"],char:\"◀️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_up_small:{keywords:[\"blue-square\",\"triangle\",\"direction\",\"point\",\"forward\",\"top\"],char:\"🔼\",fitzpatrick_scale:false,category:\"symbols\"},arrow_down_small:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"🔽\",fitzpatrick_scale:false,category:\"symbols\"},arrow_double_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:\"⏫\",fitzpatrick_scale:false,category:\"symbols\"},arrow_double_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"⏬\",fitzpatrick_scale:false,category:\"symbols\"},arrow_right:{keywords:[\"blue-square\",\"next\"],char:\"➡️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_left:{keywords:[\"blue-square\",\"previous\",\"back\"],char:\"⬅️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_up:{keywords:[\"blue-square\",\"continue\",\"top\",\"direction\"],char:\"⬆️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"⬇️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_right:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northeast\"],char:\"↗️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_right:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southeast\"],char:\"↘️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_left:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southwest\"],char:\"↙️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_left:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northwest\"],char:\"↖️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_up_down:{keywords:[\"blue-square\",\"direction\",\"way\",\"vertical\"],char:\"↕️\",fitzpatrick_scale:false,category:\"symbols\"},left_right_arrow:{keywords:[\"shape\",\"direction\",\"horizontal\",\"sideways\"],char:\"↔️\",fitzpatrick_scale:false,category:\"symbols\"},arrows_counterclockwise:{keywords:[\"blue-square\",\"sync\",\"cycle\"],char:\"🔄\",fitzpatrick_scale:false,category:\"symbols\"},arrow_right_hook:{keywords:[\"blue-square\",\"return\",\"rotate\",\"direction\"],char:\"↪️\",fitzpatrick_scale:false,category:\"symbols\"},leftwards_arrow_with_hook:{keywords:[\"back\",\"return\",\"blue-square\",\"undo\",\"enter\"],char:\"↩️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:\"⤴️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"⤵️\",fitzpatrick_scale:false,category:\"symbols\"},hash:{keywords:[\"symbol\",\"blue-square\",\"twitter\"],char:\"#️⃣\",fitzpatrick_scale:false,category:\"symbols\"},information_source:{keywords:[\"blue-square\",\"alphabet\",\"letter\"],char:\"ℹ️\",fitzpatrick_scale:false,category:\"symbols\"},abc:{keywords:[\"blue-square\",\"alphabet\"],char:\"🔤\",fitzpatrick_scale:false,category:\"symbols\"},abcd:{keywords:[\"blue-square\",\"alphabet\"],char:\"🔡\",fitzpatrick_scale:false,category:\"symbols\"},capital_abcd:{keywords:[\"alphabet\",\"words\",\"blue-square\"],char:\"🔠\",fitzpatrick_scale:false,category:\"symbols\"},symbols:{keywords:[\"blue-square\",\"music\",\"note\",\"ampersand\",\"percent\",\"glyphs\",\"characters\"],char:\"🔣\",fitzpatrick_scale:false,category:\"symbols\"},musical_note:{keywords:[\"score\",\"tone\",\"sound\"],char:\"🎵\",fitzpatrick_scale:false,category:\"symbols\"},notes:{keywords:[\"music\",\"score\"],char:\"🎶\",fitzpatrick_scale:false,category:\"symbols\"},wavy_dash:{keywords:[\"draw\",\"line\",\"moustache\",\"mustache\",\"squiggle\",\"scribble\"],char:\"〰️\",fitzpatrick_scale:false,category:\"symbols\"},curly_loop:{keywords:[\"scribble\",\"draw\",\"shape\",\"squiggle\"],char:\"➰\",fitzpatrick_scale:false,category:\"symbols\"},heavy_check_mark:{keywords:[\"ok\",\"nike\",\"answer\",\"yes\",\"tick\"],char:\"✔️\",fitzpatrick_scale:false,category:\"symbols\"},arrows_clockwise:{keywords:[\"sync\",\"cycle\",\"round\",\"repeat\"],char:\"🔃\",fitzpatrick_scale:false,category:\"symbols\"},heavy_plus_sign:{keywords:[\"math\",\"calculation\",\"addition\",\"more\",\"increase\"],char:\"➕\",fitzpatrick_scale:false,category:\"symbols\"},heavy_minus_sign:{keywords:[\"math\",\"calculation\",\"subtract\",\"less\"],char:\"➖\",fitzpatrick_scale:false,category:\"symbols\"},heavy_division_sign:{keywords:[\"divide\",\"math\",\"calculation\"],char:\"➗\",fitzpatrick_scale:false,category:\"symbols\"},heavy_multiplication_x:{keywords:[\"math\",\"calculation\"],char:\"✖️\",fitzpatrick_scale:false,category:\"symbols\"},infinity:{keywords:[\"forever\"],char:\"♾\",fitzpatrick_scale:false,category:\"symbols\"},heavy_dollar_sign:{keywords:[\"money\",\"sales\",\"payment\",\"currency\",\"buck\"],char:\"💲\",fitzpatrick_scale:false,category:\"symbols\"},currency_exchange:{keywords:[\"money\",\"sales\",\"dollar\",\"travel\"],char:\"💱\",fitzpatrick_scale:false,category:\"symbols\"},copyright:{keywords:[\"ip\",\"license\",\"circle\",\"law\",\"legal\"],char:\"©️\",fitzpatrick_scale:false,category:\"symbols\"},registered:{keywords:[\"alphabet\",\"circle\"],char:\"®️\",fitzpatrick_scale:false,category:\"symbols\"},tm:{keywords:[\"trademark\",\"brand\",\"law\",\"legal\"],char:\"™️\",fitzpatrick_scale:false,category:\"symbols\"},end:{keywords:[\"words\",\"arrow\"],char:\"🔚\",fitzpatrick_scale:false,category:\"symbols\"},back:{keywords:[\"arrow\",\"words\",\"return\"],char:\"🔙\",fitzpatrick_scale:false,category:\"symbols\"},on:{keywords:[\"arrow\",\"words\"],char:\"🔛\",fitzpatrick_scale:false,category:\"symbols\"},top:{keywords:[\"words\",\"blue-square\"],char:\"🔝\",fitzpatrick_scale:false,category:\"symbols\"},soon:{keywords:[\"arrow\",\"words\"],char:\"🔜\",fitzpatrick_scale:false,category:\"symbols\"},ballot_box_with_check:{keywords:[\"ok\",\"agree\",\"confirm\",\"black-square\",\"vote\",\"election\",\"yes\",\"tick\"],char:\"☑️\",fitzpatrick_scale:false,category:\"symbols\"},radio_button:{keywords:[\"input\",\"old\",\"music\",\"circle\"],char:\"🔘\",fitzpatrick_scale:false,category:\"symbols\"},white_circle:{keywords:[\"shape\",\"round\"],char:\"⚪\",fitzpatrick_scale:false,category:\"symbols\"},black_circle:{keywords:[\"shape\",\"button\",\"round\"],char:\"⚫\",fitzpatrick_scale:false,category:\"symbols\"},red_circle:{keywords:[\"shape\",\"error\",\"danger\"],char:\"🔴\",fitzpatrick_scale:false,category:\"symbols\"},large_blue_circle:{keywords:[\"shape\",\"icon\",\"button\"],char:\"🔵\",fitzpatrick_scale:false,category:\"symbols\"},small_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔸\",fitzpatrick_scale:false,category:\"symbols\"},small_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔹\",fitzpatrick_scale:false,category:\"symbols\"},large_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔶\",fitzpatrick_scale:false,category:\"symbols\"},large_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔷\",fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle:{keywords:[\"shape\",\"direction\",\"up\",\"top\"],char:\"🔺\",fitzpatrick_scale:false,category:\"symbols\"},black_small_square:{keywords:[\"shape\",\"icon\"],char:\"▪️\",fitzpatrick_scale:false,category:\"symbols\"},white_small_square:{keywords:[\"shape\",\"icon\"],char:\"▫️\",fitzpatrick_scale:false,category:\"symbols\"},black_large_square:{keywords:[\"shape\",\"icon\",\"button\"],char:\"⬛\",fitzpatrick_scale:false,category:\"symbols\"},white_large_square:{keywords:[\"shape\",\"icon\",\"stone\",\"button\"],char:\"⬜\",fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle_down:{keywords:[\"shape\",\"direction\",\"bottom\"],char:\"🔻\",fitzpatrick_scale:false,category:\"symbols\"},black_medium_square:{keywords:[\"shape\",\"button\",\"icon\"],char:\"◼️\",fitzpatrick_scale:false,category:\"symbols\"},white_medium_square:{keywords:[\"shape\",\"stone\",\"icon\"],char:\"◻️\",fitzpatrick_scale:false,category:\"symbols\"},black_medium_small_square:{keywords:[\"icon\",\"shape\",\"button\"],char:\"◾\",fitzpatrick_scale:false,category:\"symbols\"},white_medium_small_square:{keywords:[\"shape\",\"stone\",\"icon\",\"button\"],char:\"◽\",fitzpatrick_scale:false,category:\"symbols\"},black_square_button:{keywords:[\"shape\",\"input\",\"frame\"],char:\"🔲\",fitzpatrick_scale:false,category:\"symbols\"},white_square_button:{keywords:[\"shape\",\"input\"],char:\"🔳\",fitzpatrick_scale:false,category:\"symbols\"},speaker:{keywords:[\"sound\",\"volume\",\"silence\",\"broadcast\"],char:\"🔈\",fitzpatrick_scale:false,category:\"symbols\"},sound:{keywords:[\"volume\",\"speaker\",\"broadcast\"],char:\"🔉\",fitzpatrick_scale:false,category:\"symbols\"},loud_sound:{keywords:[\"volume\",\"noise\",\"noisy\",\"speaker\",\"broadcast\"],char:\"🔊\",fitzpatrick_scale:false,category:\"symbols\"},mute:{keywords:[\"sound\",\"volume\",\"silence\",\"quiet\"],char:\"🔇\",fitzpatrick_scale:false,category:\"symbols\"},mega:{keywords:[\"sound\",\"speaker\",\"volume\"],char:\"📣\",fitzpatrick_scale:false,category:\"symbols\"},loudspeaker:{keywords:[\"volume\",\"sound\"],char:\"📢\",fitzpatrick_scale:false,category:\"symbols\"},bell:{keywords:[\"sound\",\"notification\",\"christmas\",\"xmas\",\"chime\"],char:\"🔔\",fitzpatrick_scale:false,category:\"symbols\"},no_bell:{keywords:[\"sound\",\"volume\",\"mute\",\"quiet\",\"silent\"],char:\"🔕\",fitzpatrick_scale:false,category:\"symbols\"},black_joker:{keywords:[\"poker\",\"cards\",\"game\",\"play\",\"magic\"],char:\"🃏\",fitzpatrick_scale:false,category:\"symbols\"},mahjong:{keywords:[\"game\",\"play\",\"chinese\",\"kanji\"],char:\"🀄\",fitzpatrick_scale:false,category:\"symbols\"},spades:{keywords:[\"poker\",\"cards\",\"suits\",\"magic\"],char:\"♠️\",fitzpatrick_scale:false,category:\"symbols\"},clubs:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:\"♣️\",fitzpatrick_scale:false,category:\"symbols\"},hearts:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:\"♥️\",fitzpatrick_scale:false,category:\"symbols\"},diamonds:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:\"♦️\",fitzpatrick_scale:false,category:\"symbols\"},flower_playing_cards:{keywords:[\"game\",\"sunset\",\"red\"],char:\"🎴\",fitzpatrick_scale:false,category:\"symbols\"},thought_balloon:{keywords:[\"bubble\",\"cloud\",\"speech\",\"thinking\",\"dream\"],char:\"💭\",fitzpatrick_scale:false,category:\"symbols\"},right_anger_bubble:{keywords:[\"caption\",\"speech\",\"thinking\",\"mad\"],char:\"🗯\",fitzpatrick_scale:false,category:\"symbols\"},speech_balloon:{keywords:[\"bubble\",\"words\",\"message\",\"talk\",\"chatting\"],char:\"💬\",fitzpatrick_scale:false,category:\"symbols\"},left_speech_bubble:{keywords:[\"words\",\"message\",\"talk\",\"chatting\"],char:\"🗨\",fitzpatrick_scale:false,category:\"symbols\"},clock1:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕐\",fitzpatrick_scale:false,category:\"symbols\"},clock2:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕑\",fitzpatrick_scale:false,category:\"symbols\"},clock3:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕒\",fitzpatrick_scale:false,category:\"symbols\"},clock4:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕓\",fitzpatrick_scale:false,category:\"symbols\"},clock5:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕔\",fitzpatrick_scale:false,category:\"symbols\"},clock6:{keywords:[\"time\",\"late\",\"early\",\"schedule\",\"dawn\",\"dusk\"],char:\"🕕\",fitzpatrick_scale:false,category:\"symbols\"},clock7:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕖\",fitzpatrick_scale:false,category:\"symbols\"},clock8:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕗\",fitzpatrick_scale:false,category:\"symbols\"},clock9:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕘\",fitzpatrick_scale:false,category:\"symbols\"},clock10:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕙\",fitzpatrick_scale:false,category:\"symbols\"},clock11:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕚\",fitzpatrick_scale:false,category:\"symbols\"},clock12:{keywords:[\"time\",\"noon\",\"midnight\",\"midday\",\"late\",\"early\",\"schedule\"],char:\"🕛\",fitzpatrick_scale:false,category:\"symbols\"},clock130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕜\",fitzpatrick_scale:false,category:\"symbols\"},clock230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕝\",fitzpatrick_scale:false,category:\"symbols\"},clock330:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕞\",fitzpatrick_scale:false,category:\"symbols\"},clock430:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕟\",fitzpatrick_scale:false,category:\"symbols\"},clock530:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕠\",fitzpatrick_scale:false,category:\"symbols\"},clock630:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕡\",fitzpatrick_scale:false,category:\"symbols\"},clock730:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕢\",fitzpatrick_scale:false,category:\"symbols\"},clock830:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕣\",fitzpatrick_scale:false,category:\"symbols\"},clock930:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕤\",fitzpatrick_scale:false,category:\"symbols\"},clock1030:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕥\",fitzpatrick_scale:false,category:\"symbols\"},clock1130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕦\",fitzpatrick_scale:false,category:\"symbols\"},clock1230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕧\",fitzpatrick_scale:false,category:\"symbols\"},afghanistan:{keywords:[\"af\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇫\",fitzpatrick_scale:false,category:\"flags\"},aland_islands:{keywords:[\"Åland\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇽\",fitzpatrick_scale:false,category:\"flags\"},albania:{keywords:[\"al\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇱\",fitzpatrick_scale:false,category:\"flags\"},algeria:{keywords:[\"dz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇿\",fitzpatrick_scale:false,category:\"flags\"},american_samoa:{keywords:[\"american\",\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇸\",fitzpatrick_scale:false,category:\"flags\"},andorra:{keywords:[\"ad\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇩\",fitzpatrick_scale:false,category:\"flags\"},angola:{keywords:[\"ao\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇴\",fitzpatrick_scale:false,category:\"flags\"},anguilla:{keywords:[\"ai\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇮\",fitzpatrick_scale:false,category:\"flags\"},antarctica:{keywords:[\"aq\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇶\",fitzpatrick_scale:false,category:\"flags\"},antigua_barbuda:{keywords:[\"antigua\",\"barbuda\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇬\",fitzpatrick_scale:false,category:\"flags\"},argentina:{keywords:[\"ar\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇷\",fitzpatrick_scale:false,category:\"flags\"},armenia:{keywords:[\"am\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇲\",fitzpatrick_scale:false,category:\"flags\"},aruba:{keywords:[\"aw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇼\",fitzpatrick_scale:false,category:\"flags\"},australia:{keywords:[\"au\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇺\",fitzpatrick_scale:false,category:\"flags\"},austria:{keywords:[\"at\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇹\",fitzpatrick_scale:false,category:\"flags\"},azerbaijan:{keywords:[\"az\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇿\",fitzpatrick_scale:false,category:\"flags\"},bahamas:{keywords:[\"bs\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇸\",fitzpatrick_scale:false,category:\"flags\"},bahrain:{keywords:[\"bh\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇭\",fitzpatrick_scale:false,category:\"flags\"},bangladesh:{keywords:[\"bd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇩\",fitzpatrick_scale:false,category:\"flags\"},barbados:{keywords:[\"bb\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇧\",fitzpatrick_scale:false,category:\"flags\"},belarus:{keywords:[\"by\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇾\",fitzpatrick_scale:false,category:\"flags\"},belgium:{keywords:[\"be\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇪\",fitzpatrick_scale:false,category:\"flags\"},belize:{keywords:[\"bz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇿\",fitzpatrick_scale:false,category:\"flags\"},benin:{keywords:[\"bj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇯\",fitzpatrick_scale:false,category:\"flags\"},bermuda:{keywords:[\"bm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇲\",fitzpatrick_scale:false,category:\"flags\"},bhutan:{keywords:[\"bt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇹\",fitzpatrick_scale:false,category:\"flags\"},bolivia:{keywords:[\"bo\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇴\",fitzpatrick_scale:false,category:\"flags\"},caribbean_netherlands:{keywords:[\"bonaire\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇶\",fitzpatrick_scale:false,category:\"flags\"},bosnia_herzegovina:{keywords:[\"bosnia\",\"herzegovina\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇦\",fitzpatrick_scale:false,category:\"flags\"},botswana:{keywords:[\"bw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇼\",fitzpatrick_scale:false,category:\"flags\"},brazil:{keywords:[\"br\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇷\",fitzpatrick_scale:false,category:\"flags\"},british_indian_ocean_territory:{keywords:[\"british\",\"indian\",\"ocean\",\"territory\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇴\",fitzpatrick_scale:false,category:\"flags\"},british_virgin_islands:{keywords:[\"british\",\"virgin\",\"islands\",\"bvi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇬\",fitzpatrick_scale:false,category:\"flags\"},brunei:{keywords:[\"bn\",\"darussalam\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇳\",fitzpatrick_scale:false,category:\"flags\"},bulgaria:{keywords:[\"bg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇬\",fitzpatrick_scale:false,category:\"flags\"},burkina_faso:{keywords:[\"burkina\",\"faso\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇫\",fitzpatrick_scale:false,category:\"flags\"},burundi:{keywords:[\"bi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇮\",fitzpatrick_scale:false,category:\"flags\"},cape_verde:{keywords:[\"cabo\",\"verde\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇻\",fitzpatrick_scale:false,category:\"flags\"},cambodia:{keywords:[\"kh\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇭\",fitzpatrick_scale:false,category:\"flags\"},cameroon:{keywords:[\"cm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇲\",fitzpatrick_scale:false,category:\"flags\"},canada:{keywords:[\"ca\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇦\",fitzpatrick_scale:false,category:\"flags\"},canary_islands:{keywords:[\"canary\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇨\",fitzpatrick_scale:false,category:\"flags\"},cayman_islands:{keywords:[\"cayman\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇾\",fitzpatrick_scale:false,category:\"flags\"},central_african_republic:{keywords:[\"central\",\"african\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇫\",fitzpatrick_scale:false,category:\"flags\"},chad:{keywords:[\"td\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇩\",fitzpatrick_scale:false,category:\"flags\"},chile:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇱\",fitzpatrick_scale:false,category:\"flags\"},cn:{keywords:[\"china\",\"chinese\",\"prc\",\"flag\",\"country\",\"nation\",\"banner\"],char:\"🇨🇳\",fitzpatrick_scale:false,category:\"flags\"},christmas_island:{keywords:[\"christmas\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇽\",fitzpatrick_scale:false,category:\"flags\"},cocos_islands:{keywords:[\"cocos\",\"keeling\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇨\",fitzpatrick_scale:false,category:\"flags\"},colombia:{keywords:[\"co\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇴\",fitzpatrick_scale:false,category:\"flags\"},comoros:{keywords:[\"km\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇲\",fitzpatrick_scale:false,category:\"flags\"},congo_brazzaville:{keywords:[\"congo\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇬\",fitzpatrick_scale:false,category:\"flags\"},congo_kinshasa:{keywords:[\"congo\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇩\",fitzpatrick_scale:false,category:\"flags\"},cook_islands:{keywords:[\"cook\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇰\",fitzpatrick_scale:false,category:\"flags\"},costa_rica:{keywords:[\"costa\",\"rica\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇷\",fitzpatrick_scale:false,category:\"flags\"},croatia:{keywords:[\"hr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇷\",fitzpatrick_scale:false,category:\"flags\"},cuba:{keywords:[\"cu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇺\",fitzpatrick_scale:false,category:\"flags\"},curacao:{keywords:[\"curaçao\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇼\",fitzpatrick_scale:false,category:\"flags\"},cyprus:{keywords:[\"cy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇾\",fitzpatrick_scale:false,category:\"flags\"},czech_republic:{keywords:[\"cz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇿\",fitzpatrick_scale:false,category:\"flags\"},denmark:{keywords:[\"dk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇰\",fitzpatrick_scale:false,category:\"flags\"},djibouti:{keywords:[\"dj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇯\",fitzpatrick_scale:false,category:\"flags\"},dominica:{keywords:[\"dm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇲\",fitzpatrick_scale:false,category:\"flags\"},dominican_republic:{keywords:[\"dominican\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇴\",fitzpatrick_scale:false,category:\"flags\"},ecuador:{keywords:[\"ec\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇨\",fitzpatrick_scale:false,category:\"flags\"},egypt:{keywords:[\"eg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇬\",fitzpatrick_scale:false,category:\"flags\"},el_salvador:{keywords:[\"el\",\"salvador\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇻\",fitzpatrick_scale:false,category:\"flags\"},equatorial_guinea:{keywords:[\"equatorial\",\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇶\",fitzpatrick_scale:false,category:\"flags\"},eritrea:{keywords:[\"er\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇷\",fitzpatrick_scale:false,category:\"flags\"},estonia:{keywords:[\"ee\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇪\",fitzpatrick_scale:false,category:\"flags\"},ethiopia:{keywords:[\"et\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇹\",fitzpatrick_scale:false,category:\"flags\"},eu:{keywords:[\"european\",\"union\",\"flag\",\"banner\"],char:\"🇪🇺\",fitzpatrick_scale:false,category:\"flags\"},falkland_islands:{keywords:[\"falkland\",\"islands\",\"malvinas\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇰\",fitzpatrick_scale:false,category:\"flags\"},faroe_islands:{keywords:[\"faroe\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇴\",fitzpatrick_scale:false,category:\"flags\"},fiji:{keywords:[\"fj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇯\",fitzpatrick_scale:false,category:\"flags\"},finland:{keywords:[\"fi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇮\",fitzpatrick_scale:false,category:\"flags\"},fr:{keywords:[\"banner\",\"flag\",\"nation\",\"france\",\"french\",\"country\"],char:\"🇫🇷\",fitzpatrick_scale:false,category:\"flags\"},french_guiana:{keywords:[\"french\",\"guiana\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇫\",fitzpatrick_scale:false,category:\"flags\"},french_polynesia:{keywords:[\"french\",\"polynesia\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇫\",fitzpatrick_scale:false,category:\"flags\"},french_southern_territories:{keywords:[\"french\",\"southern\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇫\",fitzpatrick_scale:false,category:\"flags\"},gabon:{keywords:[\"ga\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇦\",fitzpatrick_scale:false,category:\"flags\"},gambia:{keywords:[\"gm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇲\",fitzpatrick_scale:false,category:\"flags\"},georgia:{keywords:[\"ge\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇪\",fitzpatrick_scale:false,category:\"flags\"},de:{keywords:[\"german\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇩🇪\",fitzpatrick_scale:false,category:\"flags\"},ghana:{keywords:[\"gh\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇭\",fitzpatrick_scale:false,category:\"flags\"},gibraltar:{keywords:[\"gi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇮\",fitzpatrick_scale:false,category:\"flags\"},greece:{keywords:[\"gr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇷\",fitzpatrick_scale:false,category:\"flags\"},greenland:{keywords:[\"gl\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇱\",fitzpatrick_scale:false,category:\"flags\"},grenada:{keywords:[\"gd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇩\",fitzpatrick_scale:false,category:\"flags\"},guadeloupe:{keywords:[\"gp\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇵\",fitzpatrick_scale:false,category:\"flags\"},guam:{keywords:[\"gu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇺\",fitzpatrick_scale:false,category:\"flags\"},guatemala:{keywords:[\"gt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇹\",fitzpatrick_scale:false,category:\"flags\"},guernsey:{keywords:[\"gg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇬\",fitzpatrick_scale:false,category:\"flags\"},guinea:{keywords:[\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇳\",fitzpatrick_scale:false,category:\"flags\"},guinea_bissau:{keywords:[\"gw\",\"bissau\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇼\",fitzpatrick_scale:false,category:\"flags\"},guyana:{keywords:[\"gy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇾\",fitzpatrick_scale:false,category:\"flags\"},haiti:{keywords:[\"ht\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇹\",fitzpatrick_scale:false,category:\"flags\"},honduras:{keywords:[\"hn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇳\",fitzpatrick_scale:false,category:\"flags\"},hong_kong:{keywords:[\"hong\",\"kong\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇰\",fitzpatrick_scale:false,category:\"flags\"},hungary:{keywords:[\"hu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇺\",fitzpatrick_scale:false,category:\"flags\"},iceland:{keywords:[\"is\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇸\",fitzpatrick_scale:false,category:\"flags\"},india:{keywords:[\"in\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇳\",fitzpatrick_scale:false,category:\"flags\"},indonesia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇩\",fitzpatrick_scale:false,category:\"flags\"},iran:{keywords:[\"iran,\",\"islamic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇷\",fitzpatrick_scale:false,category:\"flags\"},iraq:{keywords:[\"iq\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇶\",fitzpatrick_scale:false,category:\"flags\"},ireland:{keywords:[\"ie\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇪\",fitzpatrick_scale:false,category:\"flags\"},isle_of_man:{keywords:[\"isle\",\"man\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇲\",fitzpatrick_scale:false,category:\"flags\"},israel:{keywords:[\"il\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇱\",fitzpatrick_scale:false,category:\"flags\"},it:{keywords:[\"italy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇹\",fitzpatrick_scale:false,category:\"flags\"},cote_divoire:{keywords:[\"ivory\",\"coast\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇮\",fitzpatrick_scale:false,category:\"flags\"},jamaica:{keywords:[\"jm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇯🇲\",fitzpatrick_scale:false,category:\"flags\"},jp:{keywords:[\"japanese\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇯🇵\",fitzpatrick_scale:false,category:\"flags\"},jersey:{keywords:[\"je\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇯🇪\",fitzpatrick_scale:false,category:\"flags\"},jordan:{keywords:[\"jo\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇯🇴\",fitzpatrick_scale:false,category:\"flags\"},kazakhstan:{keywords:[\"kz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇿\",fitzpatrick_scale:false,category:\"flags\"},kenya:{keywords:[\"ke\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇪\",fitzpatrick_scale:false,category:\"flags\"},kiribati:{keywords:[\"ki\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇮\",fitzpatrick_scale:false,category:\"flags\"},kosovo:{keywords:[\"xk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇽🇰\",fitzpatrick_scale:false,category:\"flags\"},kuwait:{keywords:[\"kw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇼\",fitzpatrick_scale:false,category:\"flags\"},kyrgyzstan:{keywords:[\"kg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇬\",fitzpatrick_scale:false,category:\"flags\"},laos:{keywords:[\"lao\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇦\",fitzpatrick_scale:false,category:\"flags\"},latvia:{keywords:[\"lv\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇻\",fitzpatrick_scale:false,category:\"flags\"},lebanon:{keywords:[\"lb\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇧\",fitzpatrick_scale:false,category:\"flags\"},lesotho:{keywords:[\"ls\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇸\",fitzpatrick_scale:false,category:\"flags\"},liberia:{keywords:[\"lr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇷\",fitzpatrick_scale:false,category:\"flags\"},libya:{keywords:[\"ly\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇾\",fitzpatrick_scale:false,category:\"flags\"},liechtenstein:{keywords:[\"li\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇮\",fitzpatrick_scale:false,category:\"flags\"},lithuania:{keywords:[\"lt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇹\",fitzpatrick_scale:false,category:\"flags\"},luxembourg:{keywords:[\"lu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇺\",fitzpatrick_scale:false,category:\"flags\"},macau:{keywords:[\"macao\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇴\",fitzpatrick_scale:false,category:\"flags\"},macedonia:{keywords:[\"macedonia,\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇰\",fitzpatrick_scale:false,category:\"flags\"},madagascar:{keywords:[\"mg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇬\",fitzpatrick_scale:false,category:\"flags\"},malawi:{keywords:[\"mw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇼\",fitzpatrick_scale:false,category:\"flags\"},malaysia:{keywords:[\"my\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇾\",fitzpatrick_scale:false,category:\"flags\"},maldives:{keywords:[\"mv\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇻\",fitzpatrick_scale:false,category:\"flags\"},mali:{keywords:[\"ml\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇱\",fitzpatrick_scale:false,category:\"flags\"},malta:{keywords:[\"mt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇹\",fitzpatrick_scale:false,category:\"flags\"},marshall_islands:{keywords:[\"marshall\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇭\",fitzpatrick_scale:false,category:\"flags\"},martinique:{keywords:[\"mq\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇶\",fitzpatrick_scale:false,category:\"flags\"},mauritania:{keywords:[\"mr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇷\",fitzpatrick_scale:false,category:\"flags\"},mauritius:{keywords:[\"mu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇺\",fitzpatrick_scale:false,category:\"flags\"},mayotte:{keywords:[\"yt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇾🇹\",fitzpatrick_scale:false,category:\"flags\"},mexico:{keywords:[\"mx\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇽\",fitzpatrick_scale:false,category:\"flags\"},micronesia:{keywords:[\"micronesia,\",\"federated\",\"states\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇲\",fitzpatrick_scale:false,category:\"flags\"},moldova:{keywords:[\"moldova,\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇩\",fitzpatrick_scale:false,category:\"flags\"},monaco:{keywords:[\"mc\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇨\",fitzpatrick_scale:false,category:\"flags\"},mongolia:{keywords:[\"mn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇳\",fitzpatrick_scale:false,category:\"flags\"},montenegro:{keywords:[\"me\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇪\",fitzpatrick_scale:false,category:\"flags\"},montserrat:{keywords:[\"ms\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇸\",fitzpatrick_scale:false,category:\"flags\"},morocco:{keywords:[\"ma\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇦\",fitzpatrick_scale:false,category:\"flags\"},mozambique:{keywords:[\"mz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇿\",fitzpatrick_scale:false,category:\"flags\"},myanmar:{keywords:[\"mm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇲\",fitzpatrick_scale:false,category:\"flags\"},namibia:{keywords:[\"na\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇦\",fitzpatrick_scale:false,category:\"flags\"},nauru:{keywords:[\"nr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇷\",fitzpatrick_scale:false,category:\"flags\"},nepal:{keywords:[\"np\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇵\",fitzpatrick_scale:false,category:\"flags\"},netherlands:{keywords:[\"nl\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇱\",fitzpatrick_scale:false,category:\"flags\"},new_caledonia:{keywords:[\"new\",\"caledonia\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇨\",fitzpatrick_scale:false,category:\"flags\"},new_zealand:{keywords:[\"new\",\"zealand\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇿\",fitzpatrick_scale:false,category:\"flags\"},nicaragua:{keywords:[\"ni\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇮\",fitzpatrick_scale:false,category:\"flags\"},niger:{keywords:[\"ne\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇪\",fitzpatrick_scale:false,category:\"flags\"},nigeria:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇬\",fitzpatrick_scale:false,category:\"flags\"},niue:{keywords:[\"nu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇺\",fitzpatrick_scale:false,category:\"flags\"},norfolk_island:{keywords:[\"norfolk\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇫\",fitzpatrick_scale:false,category:\"flags\"},northern_mariana_islands:{keywords:[\"northern\",\"mariana\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇵\",fitzpatrick_scale:false,category:\"flags\"},north_korea:{keywords:[\"north\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇰🇵\",fitzpatrick_scale:false,category:\"flags\"},norway:{keywords:[\"no\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇴\",fitzpatrick_scale:false,category:\"flags\"},oman:{keywords:[\"om_symbol\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇴🇲\",fitzpatrick_scale:false,category:\"flags\"},pakistan:{keywords:[\"pk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇰\",fitzpatrick_scale:false,category:\"flags\"},palau:{keywords:[\"pw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇼\",fitzpatrick_scale:false,category:\"flags\"},palestinian_territories:{keywords:[\"palestine\",\"palestinian\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇸\",fitzpatrick_scale:false,category:\"flags\"},panama:{keywords:[\"pa\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇦\",fitzpatrick_scale:false,category:\"flags\"},papua_new_guinea:{keywords:[\"papua\",\"new\",\"guinea\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇬\",fitzpatrick_scale:false,category:\"flags\"},paraguay:{keywords:[\"py\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇾\",fitzpatrick_scale:false,category:\"flags\"},peru:{keywords:[\"pe\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇪\",fitzpatrick_scale:false,category:\"flags\"},philippines:{keywords:[\"ph\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇭\",fitzpatrick_scale:false,category:\"flags\"},pitcairn_islands:{keywords:[\"pitcairn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇳\",fitzpatrick_scale:false,category:\"flags\"},poland:{keywords:[\"pl\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇱\",fitzpatrick_scale:false,category:\"flags\"},portugal:{keywords:[\"pt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇹\",fitzpatrick_scale:false,category:\"flags\"},puerto_rico:{keywords:[\"puerto\",\"rico\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇷\",fitzpatrick_scale:false,category:\"flags\"},qatar:{keywords:[\"qa\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇶🇦\",fitzpatrick_scale:false,category:\"flags\"},reunion:{keywords:[\"réunion\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇪\",fitzpatrick_scale:false,category:\"flags\"},romania:{keywords:[\"ro\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇴\",fitzpatrick_scale:false,category:\"flags\"},ru:{keywords:[\"russian\",\"federation\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇺\",fitzpatrick_scale:false,category:\"flags\"},rwanda:{keywords:[\"rw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇼\",fitzpatrick_scale:false,category:\"flags\"},st_barthelemy:{keywords:[\"saint\",\"barthélemy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇱\",fitzpatrick_scale:false,category:\"flags\"},st_helena:{keywords:[\"saint\",\"helena\",\"ascension\",\"tristan\",\"cunha\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇭\",fitzpatrick_scale:false,category:\"flags\"},st_kitts_nevis:{keywords:[\"saint\",\"kitts\",\"nevis\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇳\",fitzpatrick_scale:false,category:\"flags\"},st_lucia:{keywords:[\"saint\",\"lucia\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇨\",fitzpatrick_scale:false,category:\"flags\"},st_pierre_miquelon:{keywords:[\"saint\",\"pierre\",\"miquelon\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇲\",fitzpatrick_scale:false,category:\"flags\"},st_vincent_grenadines:{keywords:[\"saint\",\"vincent\",\"grenadines\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇨\",fitzpatrick_scale:false,category:\"flags\"},samoa:{keywords:[\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇼🇸\",fitzpatrick_scale:false,category:\"flags\"},san_marino:{keywords:[\"san\",\"marino\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇲\",fitzpatrick_scale:false,category:\"flags\"},sao_tome_principe:{keywords:[\"sao\",\"tome\",\"principe\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇹\",fitzpatrick_scale:false,category:\"flags\"},saudi_arabia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇦\",fitzpatrick_scale:false,category:\"flags\"},senegal:{keywords:[\"sn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇳\",fitzpatrick_scale:false,category:\"flags\"},serbia:{keywords:[\"rs\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇸\",fitzpatrick_scale:false,category:\"flags\"},seychelles:{keywords:[\"sc\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇨\",fitzpatrick_scale:false,category:\"flags\"},sierra_leone:{keywords:[\"sierra\",\"leone\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇱\",fitzpatrick_scale:false,category:\"flags\"},singapore:{keywords:[\"sg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇬\",fitzpatrick_scale:false,category:\"flags\"},sint_maarten:{keywords:[\"sint\",\"maarten\",\"dutch\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇽\",fitzpatrick_scale:false,category:\"flags\"},slovakia:{keywords:[\"sk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇰\",fitzpatrick_scale:false,category:\"flags\"},slovenia:{keywords:[\"si\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇮\",fitzpatrick_scale:false,category:\"flags\"},solomon_islands:{keywords:[\"solomon\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇧\",fitzpatrick_scale:false,category:\"flags\"},somalia:{keywords:[\"so\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇴\",fitzpatrick_scale:false,category:\"flags\"},south_africa:{keywords:[\"south\",\"africa\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇿🇦\",fitzpatrick_scale:false,category:\"flags\"},south_georgia_south_sandwich_islands:{keywords:[\"south\",\"georgia\",\"sandwich\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇸\",fitzpatrick_scale:false,category:\"flags\"},kr:{keywords:[\"south\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇰🇷\",fitzpatrick_scale:false,category:\"flags\"},south_sudan:{keywords:[\"south\",\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇸\",fitzpatrick_scale:false,category:\"flags\"},es:{keywords:[\"spain\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇸\",fitzpatrick_scale:false,category:\"flags\"},sri_lanka:{keywords:[\"sri\",\"lanka\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇰\",fitzpatrick_scale:false,category:\"flags\"},sudan:{keywords:[\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇩\",fitzpatrick_scale:false,category:\"flags\"},suriname:{keywords:[\"sr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇷\",fitzpatrick_scale:false,category:\"flags\"},swaziland:{keywords:[\"sz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇿\",fitzpatrick_scale:false,category:\"flags\"},sweden:{keywords:[\"se\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇪\",fitzpatrick_scale:false,category:\"flags\"},switzerland:{keywords:[\"ch\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇭\",fitzpatrick_scale:false,category:\"flags\"},syria:{keywords:[\"syrian\",\"arab\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇾\",fitzpatrick_scale:false,category:\"flags\"},taiwan:{keywords:[\"tw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇼\",fitzpatrick_scale:false,category:\"flags\"},tajikistan:{keywords:[\"tj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇯\",fitzpatrick_scale:false,category:\"flags\"},tanzania:{keywords:[\"tanzania,\",\"united\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇿\",fitzpatrick_scale:false,category:\"flags\"},thailand:{keywords:[\"th\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇭\",fitzpatrick_scale:false,category:\"flags\"},timor_leste:{keywords:[\"timor\",\"leste\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇱\",fitzpatrick_scale:false,category:\"flags\"},togo:{keywords:[\"tg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇬\",fitzpatrick_scale:false,category:\"flags\"},tokelau:{keywords:[\"tk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇰\",fitzpatrick_scale:false,category:\"flags\"},tonga:{keywords:[\"to\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇴\",fitzpatrick_scale:false,category:\"flags\"},trinidad_tobago:{keywords:[\"trinidad\",\"tobago\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇹\",fitzpatrick_scale:false,category:\"flags\"},tunisia:{keywords:[\"tn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇳\",fitzpatrick_scale:false,category:\"flags\"},tr:{keywords:[\"turkey\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇷\",fitzpatrick_scale:false,category:\"flags\"},turkmenistan:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇲\",fitzpatrick_scale:false,category:\"flags\"},turks_caicos_islands:{keywords:[\"turks\",\"caicos\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇨\",fitzpatrick_scale:false,category:\"flags\"},tuvalu:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇻\",fitzpatrick_scale:false,category:\"flags\"},uganda:{keywords:[\"ug\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇬\",fitzpatrick_scale:false,category:\"flags\"},ukraine:{keywords:[\"ua\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇦\",fitzpatrick_scale:false,category:\"flags\"},united_arab_emirates:{keywords:[\"united\",\"arab\",\"emirates\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇪\",fitzpatrick_scale:false,category:\"flags\"},uk:{keywords:[\"united\",\"kingdom\",\"great\",\"britain\",\"northern\",\"ireland\",\"flag\",\"nation\",\"country\",\"banner\",\"british\",\"UK\",\"english\",\"england\",\"union jack\"],char:\"🇬🇧\",fitzpatrick_scale:false,category:\"flags\"},england:{keywords:[\"flag\",\"english\"],char:\"🏴󠁧󠁢󠁥󠁮󠁧󠁿\",fitzpatrick_scale:false,category:\"flags\"},scotland:{keywords:[\"flag\",\"scottish\"],char:\"🏴󠁧󠁢󠁳󠁣󠁴󠁿\",fitzpatrick_scale:false,category:\"flags\"},wales:{keywords:[\"flag\",\"welsh\"],char:\"🏴󠁧󠁢󠁷󠁬󠁳󠁿\",fitzpatrick_scale:false,category:\"flags\"},us:{keywords:[\"united\",\"states\",\"america\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇸\",fitzpatrick_scale:false,category:\"flags\"},us_virgin_islands:{keywords:[\"virgin\",\"islands\",\"us\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇮\",fitzpatrick_scale:false,category:\"flags\"},uruguay:{keywords:[\"uy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇾\",fitzpatrick_scale:false,category:\"flags\"},uzbekistan:{keywords:[\"uz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇿\",fitzpatrick_scale:false,category:\"flags\"},vanuatu:{keywords:[\"vu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇺\",fitzpatrick_scale:false,category:\"flags\"},vatican_city:{keywords:[\"vatican\",\"city\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇦\",fitzpatrick_scale:false,category:\"flags\"},venezuela:{keywords:[\"ve\",\"bolivarian\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇪\",fitzpatrick_scale:false,category:\"flags\"},vietnam:{keywords:[\"viet\",\"nam\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇳\",fitzpatrick_scale:false,category:\"flags\"},wallis_futuna:{keywords:[\"wallis\",\"futuna\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇼🇫\",fitzpatrick_scale:false,category:\"flags\"},western_sahara:{keywords:[\"western\",\"sahara\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇭\",fitzpatrick_scale:false,category:\"flags\"},yemen:{keywords:[\"ye\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇾🇪\",fitzpatrick_scale:false,category:\"flags\"},zambia:{keywords:[\"zm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇿🇲\",fitzpatrick_scale:false,category:\"flags\"},zimbabwe:{keywords:[\"zw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇿🇼\",fitzpatrick_scale:false,category:\"flags\"},united_nations:{keywords:[\"un\",\"flag\",\"banner\"],char:\"🇺🇳\",fitzpatrick_scale:false,category:\"flags\"},pirate_flag:{keywords:[\"skull\",\"crossbones\",\"flag\",\"banner\"],char:\"🏴‍☠️\",fitzpatrick_scale:false,category:\"flags\"}});"
  },
  {
    "path": "vue_acimage_web/public/tinymce/tinymce.d.ts",
    "content": "interface StringPathBookmark {\n    start: string;\n    end?: string;\n    forward?: boolean;\n}\ninterface RangeBookmark {\n    rng: Range;\n    forward?: boolean;\n}\ninterface IdBookmark {\n    id: string;\n    keep?: boolean;\n    forward?: boolean;\n}\ninterface IndexBookmark {\n    name: string;\n    index: number;\n}\ninterface PathBookmark {\n    start: number[];\n    end?: number[];\n    isFakeCaret?: boolean;\n    forward?: boolean;\n}\ndeclare type Bookmark = StringPathBookmark | RangeBookmark | IdBookmark | IndexBookmark | PathBookmark;\ndeclare type NormalizedEvent<E, T = any> = E & {\n    readonly type: string;\n    readonly target: T;\n    readonly isDefaultPrevented: () => boolean;\n    readonly preventDefault: () => void;\n    readonly isPropagationStopped: () => boolean;\n    readonly stopPropagation: () => void;\n    readonly isImmediatePropagationStopped: () => boolean;\n    readonly stopImmediatePropagation: () => void;\n};\ndeclare type MappedEvent<T extends {}, K extends string> = K extends keyof T ? T[K] : any;\ninterface NativeEventMap {\n    'beforepaste': Event;\n    'blur': FocusEvent;\n    'beforeinput': InputEvent;\n    'click': MouseEvent;\n    'compositionend': Event;\n    'compositionstart': Event;\n    'compositionupdate': Event;\n    'contextmenu': PointerEvent;\n    'copy': ClipboardEvent;\n    'cut': ClipboardEvent;\n    'dblclick': MouseEvent;\n    'drag': DragEvent;\n    'dragdrop': DragEvent;\n    'dragend': DragEvent;\n    'draggesture': DragEvent;\n    'dragover': DragEvent;\n    'dragstart': DragEvent;\n    'drop': DragEvent;\n    'focus': FocusEvent;\n    'focusin': FocusEvent;\n    'focusout': FocusEvent;\n    'input': InputEvent;\n    'keydown': KeyboardEvent;\n    'keypress': KeyboardEvent;\n    'keyup': KeyboardEvent;\n    'mousedown': MouseEvent;\n    'mouseenter': MouseEvent;\n    'mouseleave': MouseEvent;\n    'mousemove': MouseEvent;\n    'mouseout': MouseEvent;\n    'mouseover': MouseEvent;\n    'mouseup': MouseEvent;\n    'paste': ClipboardEvent;\n    'selectionchange': Event;\n    'submit': Event;\n    'touchend': TouchEvent;\n    'touchmove': TouchEvent;\n    'touchstart': TouchEvent;\n    'touchcancel': TouchEvent;\n    'wheel': WheelEvent;\n}\ndeclare type EditorEvent<T> = NormalizedEvent<T>;\ninterface EventDispatcherSettings {\n    scope?: any;\n    toggleEvent?: (name: string, state: boolean) => void | boolean;\n    beforeFire?: <T>(args: EditorEvent<T>) => void;\n}\ninterface EventDispatcherConstructor<T extends {}> {\n    readonly prototype: EventDispatcher<T>;\n    new (settings?: EventDispatcherSettings): EventDispatcher<T>;\n    isNative: (name: string) => boolean;\n}\ndeclare class EventDispatcher<T extends {}> {\n    static isNative(name: string): boolean;\n    private readonly settings;\n    private readonly scope;\n    private readonly toggleEvent;\n    private bindings;\n    constructor(settings?: EventDispatcherSettings);\n    fire<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U): EditorEvent<U>;\n    dispatch<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U): EditorEvent<U>;\n    on<K extends string>(name: K, callback: false | ((event: EditorEvent<MappedEvent<T, K>>) => void | boolean), prepend?: boolean, extra?: {}): this;\n    off<K extends string>(name?: K, callback?: (event: EditorEvent<MappedEvent<T, K>>) => void): this;\n    once<K extends string>(name: K, callback: (event: EditorEvent<MappedEvent<T, K>>) => void, prepend?: boolean): this;\n    has(name: string): boolean;\n}\ndeclare const enum UndoLevelType {\n    Fragmented = \"fragmented\",\n    Complete = \"complete\"\n}\ninterface BaseUndoLevel {\n    type: UndoLevelType;\n    bookmark: Bookmark | null;\n    beforeBookmark: Bookmark | null;\n}\ninterface FragmentedUndoLevel extends BaseUndoLevel {\n    type: UndoLevelType.Fragmented;\n    fragments: string[];\n    content: '';\n}\ninterface CompleteUndoLevel extends BaseUndoLevel {\n    type: UndoLevelType.Complete;\n    fragments: null;\n    content: string;\n}\ndeclare type NewUndoLevel = CompleteUndoLevel | FragmentedUndoLevel;\ndeclare type UndoLevel = NewUndoLevel & {\n    bookmark: Bookmark;\n};\ninterface UndoManager {\n    data: UndoLevel[];\n    typing: boolean;\n    add: (level?: Partial<UndoLevel>, event?: EditorEvent<any>) => UndoLevel | null;\n    dispatchChange: () => void;\n    beforeChange: () => void;\n    undo: () => UndoLevel | undefined;\n    redo: () => UndoLevel | undefined;\n    clear: () => void;\n    reset: () => void;\n    hasUndo: () => boolean;\n    hasRedo: () => boolean;\n    transact: (callback: () => void) => UndoLevel | null;\n    ignore: (callback: () => void) => void;\n    extra: (callback1: () => void, callback2: () => void) => void;\n}\ndeclare type SchemaType = 'html4' | 'html5' | 'html5-strict';\ninterface ElementSettings {\n    block_elements?: string;\n    boolean_attributes?: string;\n    move_caret_before_on_enter_elements?: string;\n    non_empty_elements?: string;\n    self_closing_elements?: string;\n    text_block_elements?: string;\n    text_inline_elements?: string;\n    void_elements?: string;\n    whitespace_elements?: string;\n    transparent_elements?: string;\n}\ninterface SchemaSettings extends ElementSettings {\n    custom_elements?: string;\n    extended_valid_elements?: string;\n    invalid_elements?: string;\n    invalid_styles?: string | Record<string, string>;\n    schema?: SchemaType;\n    valid_children?: string;\n    valid_classes?: string | Record<string, string>;\n    valid_elements?: string;\n    valid_styles?: string | Record<string, string>;\n    verify_html?: boolean;\n    padd_empty_block_inline_children?: boolean;\n}\ninterface Attribute {\n    required?: boolean;\n    defaultValue?: string;\n    forcedValue?: string;\n    validValues?: Record<string, {}>;\n}\ninterface DefaultAttribute {\n    name: string;\n    value: string;\n}\ninterface AttributePattern extends Attribute {\n    pattern: RegExp;\n}\ninterface ElementRule {\n    attributes: Record<string, Attribute>;\n    attributesDefault?: DefaultAttribute[];\n    attributesForced?: DefaultAttribute[];\n    attributesOrder: string[];\n    attributePatterns?: AttributePattern[];\n    attributesRequired?: string[];\n    paddEmpty?: boolean;\n    removeEmpty?: boolean;\n    removeEmptyAttrs?: boolean;\n    paddInEmptyBlock?: boolean;\n}\ninterface SchemaElement extends ElementRule {\n    outputName?: string;\n    parentsRequired?: string[];\n    pattern?: RegExp;\n}\ninterface SchemaMap {\n    [name: string]: {};\n}\ninterface SchemaRegExpMap {\n    [name: string]: RegExp;\n}\ninterface Schema {\n    type: SchemaType;\n    children: Record<string, SchemaMap>;\n    elements: Record<string, SchemaElement>;\n    getValidStyles: () => Record<string, string[]> | undefined;\n    getValidClasses: () => Record<string, SchemaMap> | undefined;\n    getBlockElements: () => SchemaMap;\n    getInvalidStyles: () => Record<string, SchemaMap> | undefined;\n    getVoidElements: () => SchemaMap;\n    getTextBlockElements: () => SchemaMap;\n    getTextInlineElements: () => SchemaMap;\n    getBoolAttrs: () => SchemaMap;\n    getElementRule: (name: string) => SchemaElement | undefined;\n    getSelfClosingElements: () => SchemaMap;\n    getNonEmptyElements: () => SchemaMap;\n    getMoveCaretBeforeOnEnterElements: () => SchemaMap;\n    getWhitespaceElements: () => SchemaMap;\n    getTransparentElements: () => SchemaMap;\n    getSpecialElements: () => SchemaRegExpMap;\n    isValidChild: (name: string, child: string) => boolean;\n    isValid: (name: string, attr?: string) => boolean;\n    getCustomElements: () => SchemaMap;\n    addValidElements: (validElements: string) => void;\n    setValidElements: (validElements: string) => void;\n    addCustomElements: (customElements: string) => void;\n    addValidChildren: (validChildren: any) => void;\n}\ndeclare type Attributes$1 = Array<{\n    name: string;\n    value: string;\n}> & {\n    map: Record<string, string>;\n};\ninterface AstNodeConstructor {\n    readonly prototype: AstNode;\n    new (name: string, type: number): AstNode;\n    create(name: string, attrs?: Record<string, string>): AstNode;\n}\ndeclare class AstNode {\n    static create(name: string, attrs?: Record<string, string>): AstNode;\n    name: string;\n    type: number;\n    attributes?: Attributes$1;\n    value?: string;\n    parent?: AstNode | null;\n    firstChild?: AstNode | null;\n    lastChild?: AstNode | null;\n    next?: AstNode | null;\n    prev?: AstNode | null;\n    raw?: boolean;\n    constructor(name: string, type: number);\n    replace(node: AstNode): AstNode;\n    attr(name: string, value: string | null | undefined): AstNode | undefined;\n    attr(name: Record<string, string | null | undefined> | undefined): AstNode | undefined;\n    attr(name: string): string | undefined;\n    clone(): AstNode;\n    wrap(wrapper: AstNode): AstNode;\n    unwrap(): void;\n    remove(): AstNode;\n    append(node: AstNode): AstNode;\n    insert(node: AstNode, refNode: AstNode, before?: boolean): AstNode;\n    getAll(name: string): AstNode[];\n    children(): AstNode[];\n    empty(): AstNode;\n    isEmpty(elements: SchemaMap, whitespace?: SchemaMap, predicate?: (node: AstNode) => boolean): boolean;\n    walk(prev?: boolean): AstNode | null | undefined;\n}\ndeclare type Content = string | AstNode;\ndeclare type ContentFormat = 'raw' | 'text' | 'html' | 'tree';\ninterface GetContentArgs {\n    format: ContentFormat;\n    get: boolean;\n    getInner: boolean;\n    no_events?: boolean;\n    save?: boolean;\n    source_view?: boolean;\n    [key: string]: any;\n}\ninterface SetContentArgs {\n    format: string;\n    set: boolean;\n    content: Content;\n    no_events?: boolean;\n    no_selection?: boolean;\n    paste?: boolean;\n    load?: boolean;\n    initial?: boolean;\n    [key: string]: any;\n}\ninterface GetSelectionContentArgs extends GetContentArgs {\n    selection?: boolean;\n    contextual?: boolean;\n}\ninterface SetSelectionContentArgs extends SetContentArgs {\n    content: string;\n    selection?: boolean;\n}\ninterface BlobInfoData {\n    id?: string;\n    name?: string;\n    filename?: string;\n    blob: Blob;\n    base64: string;\n    blobUri?: string;\n    uri?: string;\n}\ninterface BlobInfo {\n    id: () => string;\n    name: () => string;\n    filename: () => string;\n    blob: () => Blob;\n    base64: () => string;\n    blobUri: () => string;\n    uri: () => string | undefined;\n}\ninterface BlobCache {\n    create: {\n        (o: BlobInfoData): BlobInfo;\n        (id: string, blob: Blob, base64: string, name?: string, filename?: string): BlobInfo;\n    };\n    add: (blobInfo: BlobInfo) => void;\n    get: (id: string) => BlobInfo | undefined;\n    getByUri: (blobUri: string) => BlobInfo | undefined;\n    getByData: (base64: string, type: string) => BlobInfo | undefined;\n    findFirst: (predicate: (blobInfo: BlobInfo) => boolean) => BlobInfo | undefined;\n    removeByUri: (blobUri: string) => void;\n    destroy: () => void;\n}\ninterface BlobInfoImagePair {\n    image: HTMLImageElement;\n    blobInfo: BlobInfo;\n}\ndeclare class NodeChange {\n    private readonly editor;\n    private lastPath;\n    constructor(editor: Editor);\n    nodeChanged(args?: Record<string, any>): void;\n    private isSameElementPath;\n}\ninterface SelectionOverrides {\n    showCaret: (direction: number, node: HTMLElement, before: boolean, scrollIntoView?: boolean) => Range | null;\n    showBlockCaretContainer: (blockCaretContainer: HTMLElement) => void;\n    hideFakeCaret: () => void;\n    destroy: () => void;\n}\ninterface Quirks {\n    refreshContentEditable(): void;\n    isHidden(): boolean;\n}\ndeclare type DecoratorData = Record<string, any>;\ndeclare type Decorator = (uid: string, data: DecoratorData) => {\n    attributes?: {};\n    classes?: string[];\n};\ndeclare type AnnotationListener = (state: boolean, name: string, data?: {\n    uid: string;\n    nodes: any[];\n}) => void;\ndeclare type AnnotationListenerApi = AnnotationListener;\ninterface AnnotatorSettings {\n    decorate: Decorator;\n    persistent?: boolean;\n}\ninterface Annotator {\n    register: (name: string, settings: AnnotatorSettings) => void;\n    annotate: (name: string, data: DecoratorData) => void;\n    annotationChanged: (name: string, f: AnnotationListenerApi) => void;\n    remove: (name: string) => void;\n    removeAll: (name: string) => void;\n    getAll: (name: string) => Record<string, Element[]>;\n}\ninterface GeomRect {\n    readonly x: number;\n    readonly y: number;\n    readonly w: number;\n    readonly h: number;\n}\ninterface Rect {\n    inflate: (rect: GeomRect, w: number, h: number) => GeomRect;\n    relativePosition: (rect: GeomRect, targetRect: GeomRect, rel: string) => GeomRect;\n    findBestRelativePosition: (rect: GeomRect, targetRect: GeomRect, constrainRect: GeomRect, rels: string[]) => string | null;\n    intersect: (rect: GeomRect, cropRect: GeomRect) => GeomRect | null;\n    clamp: (rect: GeomRect, clampRect: GeomRect, fixedSize?: boolean) => GeomRect;\n    create: (x: number, y: number, w: number, h: number) => GeomRect;\n    fromClientRect: (clientRect: DOMRect) => GeomRect;\n}\ninterface NotificationManagerImpl {\n    open: (spec: NotificationSpec, closeCallback: () => void) => NotificationApi;\n    close: <T extends NotificationApi>(notification: T) => void;\n    getArgs: <T extends NotificationApi>(notification: T) => NotificationSpec;\n}\ninterface NotificationSpec {\n    type?: 'info' | 'warning' | 'error' | 'success';\n    text: string;\n    icon?: string;\n    progressBar?: boolean;\n    timeout?: number;\n    closeButton?: boolean;\n}\ninterface NotificationApi {\n    close: () => void;\n    progressBar: {\n        value: (percent: number) => void;\n    };\n    text: (text: string) => void;\n    reposition: () => void;\n    getEl: () => HTMLElement;\n    settings: NotificationSpec;\n}\ninterface NotificationManager {\n    open: (spec: NotificationSpec) => NotificationApi;\n    close: () => void;\n    getNotifications: () => NotificationApi[];\n}\ninterface UploadFailure {\n    message: string;\n    remove?: boolean;\n}\ndeclare type ProgressFn = (percent: number) => void;\ndeclare type UploadHandler = (blobInfo: BlobInfo, progress: ProgressFn) => Promise<string>;\ninterface UploadResult$2 {\n    url: string;\n    blobInfo: BlobInfo;\n    status: boolean;\n    error?: UploadFailure;\n}\ninterface RawPattern {\n    start?: any;\n    end?: any;\n    format?: any;\n    cmd?: any;\n    value?: any;\n    replacement?: any;\n}\ninterface InlineBasePattern {\n    readonly start: string;\n    readonly end: string;\n}\ninterface InlineFormatPattern extends InlineBasePattern {\n    readonly type: 'inline-format';\n    readonly format: string[];\n}\ninterface InlineCmdPattern extends InlineBasePattern {\n    readonly type: 'inline-command';\n    readonly cmd: string;\n    readonly value?: any;\n}\ndeclare type InlinePattern = InlineFormatPattern | InlineCmdPattern;\ninterface BlockBasePattern {\n    readonly start: string;\n}\ninterface BlockFormatPattern extends BlockBasePattern {\n    readonly type: 'block-format';\n    readonly format: string;\n}\ninterface BlockCmdPattern extends BlockBasePattern {\n    readonly type: 'block-command';\n    readonly cmd: string;\n    readonly value?: any;\n}\ndeclare type BlockPattern = BlockFormatPattern | BlockCmdPattern;\ndeclare type Pattern = InlinePattern | BlockPattern;\ninterface DynamicPatternContext {\n    readonly text: string;\n    readonly block: Element;\n}\ndeclare type DynamicPatternsLookup = (ctx: DynamicPatternContext) => Pattern[];\ndeclare type RawDynamicPatternsLookup = (ctx: DynamicPatternContext) => RawPattern[];\ninterface AlertBannerSpec {\n    type: 'alertbanner';\n    level: 'info' | 'warn' | 'error' | 'success';\n    text: string;\n    icon: string;\n    url?: string;\n}\ninterface ButtonSpec {\n    type: 'button';\n    text: string;\n    enabled?: boolean;\n    primary?: boolean;\n    name?: string;\n    icon?: string;\n    borderless?: boolean;\n    buttonType?: 'primary' | 'secondary' | 'toolbar';\n}\ninterface FormComponentSpec {\n    type: string;\n    name: string;\n}\ninterface FormComponentWithLabelSpec extends FormComponentSpec {\n    label?: string;\n}\ninterface CheckboxSpec extends FormComponentSpec {\n    type: 'checkbox';\n    label: string;\n    enabled?: boolean;\n}\ninterface CollectionSpec extends FormComponentWithLabelSpec {\n    type: 'collection';\n}\ninterface CollectionItem {\n    value: string;\n    text: string;\n    icon: string;\n}\ninterface ColorInputSpec extends FormComponentWithLabelSpec {\n    type: 'colorinput';\n    storageKey?: string;\n}\ninterface ColorPickerSpec extends FormComponentWithLabelSpec {\n    type: 'colorpicker';\n}\ninterface CustomEditorInit {\n    setValue: (value: string) => void;\n    getValue: () => string;\n    destroy: () => void;\n}\ndeclare type CustomEditorInitFn = (elm: HTMLElement, settings: any) => Promise<CustomEditorInit>;\ninterface CustomEditorOldSpec extends FormComponentSpec {\n    type: 'customeditor';\n    tag?: string;\n    init: (e: HTMLElement) => Promise<CustomEditorInit>;\n}\ninterface CustomEditorNewSpec extends FormComponentSpec {\n    type: 'customeditor';\n    tag?: string;\n    scriptId: string;\n    scriptUrl: string;\n    settings?: any;\n}\ndeclare type CustomEditorSpec = CustomEditorOldSpec | CustomEditorNewSpec;\ninterface DropZoneSpec extends FormComponentWithLabelSpec {\n    type: 'dropzone';\n}\ninterface GridSpec {\n    type: 'grid';\n    columns: number;\n    items: BodyComponentSpec[];\n}\ninterface HtmlPanelSpec {\n    type: 'htmlpanel';\n    html: string;\n    presets?: 'presentation' | 'document';\n}\ninterface IframeSpec extends FormComponentWithLabelSpec {\n    type: 'iframe';\n    sandboxed?: boolean;\n    transparent?: boolean;\n}\ninterface ImagePreviewSpec extends FormComponentSpec {\n    type: 'imagepreview';\n    height?: string;\n}\ninterface InputSpec extends FormComponentWithLabelSpec {\n    type: 'input';\n    inputMode?: string;\n    placeholder?: string;\n    maximized?: boolean;\n    enabled?: boolean;\n}\ninterface LabelSpec {\n    type: 'label';\n    label: string;\n    items: BodyComponentSpec[];\n}\ninterface ListBoxSingleItemSpec {\n    text: string;\n    value: string;\n}\ninterface ListBoxNestedItemSpec {\n    text: string;\n    items: ListBoxItemSpec[];\n}\ndeclare type ListBoxItemSpec = ListBoxNestedItemSpec | ListBoxSingleItemSpec;\ninterface ListBoxSpec extends FormComponentWithLabelSpec {\n    type: 'listbox';\n    items: ListBoxItemSpec[];\n    disabled?: boolean;\n}\ninterface PanelSpec {\n    type: 'panel';\n    classes?: string[];\n    items: BodyComponentSpec[];\n}\ninterface SelectBoxItemSpec {\n    text: string;\n    value: string;\n}\ninterface SelectBoxSpec extends FormComponentWithLabelSpec {\n    type: 'selectbox';\n    items: SelectBoxItemSpec[];\n    size?: number;\n    enabled?: boolean;\n}\ninterface SizeInputSpec extends FormComponentWithLabelSpec {\n    type: 'sizeinput';\n    constrain?: boolean;\n    enabled?: boolean;\n}\ninterface SliderSpec extends FormComponentSpec {\n    type: 'slider';\n    label: string;\n    min?: number;\n    max?: number;\n}\ninterface TableSpec {\n    type: 'table';\n    header: string[];\n    cells: string[][];\n}\ninterface TextAreaSpec extends FormComponentWithLabelSpec {\n    type: 'textarea';\n    placeholder?: string;\n    maximized?: boolean;\n    enabled?: boolean;\n}\ninterface UrlInputSpec extends FormComponentWithLabelSpec {\n    type: 'urlinput';\n    filetype?: 'image' | 'media' | 'file';\n    enabled?: boolean;\n}\ninterface UrlInputData {\n    value: string;\n    meta: {\n        text?: string;\n    };\n}\ndeclare type BodyComponentSpec = BarSpec | ButtonSpec | CheckboxSpec | TextAreaSpec | InputSpec | ListBoxSpec | SelectBoxSpec | SizeInputSpec | SliderSpec | IframeSpec | HtmlPanelSpec | UrlInputSpec | DropZoneSpec | ColorInputSpec | GridSpec | ColorPickerSpec | ImagePreviewSpec | AlertBannerSpec | CollectionSpec | LabelSpec | TableSpec | PanelSpec | CustomEditorSpec;\ninterface BarSpec {\n    type: 'bar';\n    items: BodyComponentSpec[];\n}\ninterface CommonMenuItemSpec {\n    enabled?: boolean;\n    text?: string;\n    value?: string;\n    meta?: Record<string, any>;\n    shortcut?: string;\n}\ninterface CommonMenuItemInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n}\ninterface DialogToggleMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'togglemenuitem';\n    name: string;\n}\ndeclare type DialogFooterMenuButtonItemSpec = DialogToggleMenuItemSpec;\ninterface BaseDialogFooterButtonSpec {\n    name?: string;\n    align?: 'start' | 'end';\n    primary?: boolean;\n    enabled?: boolean;\n    icon?: string;\n    buttonType?: 'primary' | 'secondary';\n}\ninterface DialogFooterNormalButtonSpec extends BaseDialogFooterButtonSpec {\n    type: 'submit' | 'cancel' | 'custom';\n    text: string;\n}\ninterface DialogFooterMenuButtonSpec extends BaseDialogFooterButtonSpec {\n    type: 'menu';\n    text?: string;\n    tooltip?: string;\n    icon?: string;\n    items: DialogFooterMenuButtonItemSpec[];\n}\ndeclare type DialogFooterButtonSpec = DialogFooterNormalButtonSpec | DialogFooterMenuButtonSpec;\ninterface TabSpec {\n    name?: string;\n    title: string;\n    items: BodyComponentSpec[];\n}\ninterface TabPanelSpec {\n    type: 'tabpanel';\n    tabs: TabSpec[];\n}\ndeclare type DialogDataItem = any;\ndeclare type DialogData = Record<string, DialogDataItem>;\ninterface DialogInstanceApi<T extends DialogData> {\n    getData: () => T;\n    setData: (data: Partial<T>) => void;\n    setEnabled: (name: string, state: boolean) => void;\n    focus: (name: string) => void;\n    showTab: (name: string) => void;\n    redial: (nu: DialogSpec<T>) => void;\n    block: (msg: string) => void;\n    unblock: () => void;\n    close: () => void;\n}\ninterface DialogActionDetails {\n    name: string;\n    value?: any;\n}\ninterface DialogChangeDetails<T> {\n    name: keyof T;\n}\ninterface DialogTabChangeDetails {\n    newTabName: string;\n    oldTabName: string;\n}\ndeclare type DialogActionHandler<T extends DialogData> = (api: DialogInstanceApi<T>, details: DialogActionDetails) => void;\ndeclare type DialogChangeHandler<T extends DialogData> = (api: DialogInstanceApi<T>, details: DialogChangeDetails<T>) => void;\ndeclare type DialogSubmitHandler<T extends DialogData> = (api: DialogInstanceApi<T>) => void;\ndeclare type DialogCloseHandler = () => void;\ndeclare type DialogCancelHandler<T extends DialogData> = (api: DialogInstanceApi<T>) => void;\ndeclare type DialogTabChangeHandler<T extends DialogData> = (api: DialogInstanceApi<T>, details: DialogTabChangeDetails) => void;\ndeclare type DialogSize = 'normal' | 'medium' | 'large';\ninterface DialogSpec<T extends DialogData> {\n    title: string;\n    size?: DialogSize;\n    body: TabPanelSpec | PanelSpec;\n    buttons: DialogFooterButtonSpec[];\n    initialData?: Partial<T>;\n    onAction?: DialogActionHandler<T>;\n    onChange?: DialogChangeHandler<T>;\n    onSubmit?: DialogSubmitHandler<T>;\n    onClose?: DialogCloseHandler;\n    onCancel?: DialogCancelHandler<T>;\n    onTabChange?: DialogTabChangeHandler<T>;\n}\ninterface UrlDialogInstanceApi {\n    block: (msg: string) => void;\n    unblock: () => void;\n    close: () => void;\n    sendMessage: (msg: any) => void;\n}\ninterface UrlDialogActionDetails {\n    name: string;\n    value?: any;\n}\ninterface UrlDialogMessage {\n    mceAction: string;\n    [key: string]: any;\n}\ndeclare type UrlDialogActionHandler = (api: UrlDialogInstanceApi, actions: UrlDialogActionDetails) => void;\ndeclare type UrlDialogCloseHandler = () => void;\ndeclare type UrlDialogCancelHandler = (api: UrlDialogInstanceApi) => void;\ndeclare type UrlDialogMessageHandler = (api: UrlDialogInstanceApi, message: UrlDialogMessage) => void;\ninterface UrlDialogFooterButtonSpec extends DialogFooterNormalButtonSpec {\n    type: 'cancel' | 'custom';\n}\ninterface UrlDialogSpec {\n    title: string;\n    url: string;\n    height?: number;\n    width?: number;\n    buttons?: UrlDialogFooterButtonSpec[];\n    onAction?: UrlDialogActionHandler;\n    onClose?: UrlDialogCloseHandler;\n    onCancel?: UrlDialogCancelHandler;\n    onMessage?: UrlDialogMessageHandler;\n}\ndeclare type CardContainerDirection = 'vertical' | 'horizontal';\ndeclare type CardContainerAlign = 'left' | 'right';\ndeclare type CardContainerValign = 'top' | 'middle' | 'bottom';\ninterface CardContainerSpec {\n    type: 'cardcontainer';\n    items: CardItemSpec[];\n    direction?: CardContainerDirection;\n    align?: CardContainerAlign;\n    valign?: CardContainerValign;\n}\ninterface CardImageSpec {\n    type: 'cardimage';\n    src: string;\n    alt?: string;\n    classes?: string[];\n}\ninterface CardTextSpec {\n    type: 'cardtext';\n    text: string;\n    name?: string;\n    classes?: string[];\n}\ndeclare type CardItemSpec = CardContainerSpec | CardImageSpec | CardTextSpec;\ninterface CardMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n}\ninterface CardMenuItemSpec extends Omit<CommonMenuItemSpec, 'text' | 'shortcut'> {\n    type: 'cardmenuitem';\n    label?: string;\n    items: CardItemSpec[];\n    onSetup?: (api: CardMenuItemInstanceApi) => (api: CardMenuItemInstanceApi) => void;\n    onAction?: (api: CardMenuItemInstanceApi) => void;\n}\ninterface SeparatorMenuItemSpec {\n    type?: 'separator';\n    text?: string;\n}\ndeclare type ColumnTypes$1 = number | 'auto';\ndeclare type SeparatorItemSpec = SeparatorMenuItemSpec;\ninterface AutocompleterItemSpec {\n    type?: 'autocompleteitem';\n    value: string;\n    text?: string;\n    icon?: string;\n    meta?: Record<string, any>;\n}\ndeclare type AutocompleterContents = SeparatorItemSpec | AutocompleterItemSpec | CardMenuItemSpec;\ninterface AutocompleterSpec {\n    type?: 'autocompleter';\n    ch?: string;\n    trigger?: string;\n    minChars?: number;\n    columns?: ColumnTypes$1;\n    matches?: (rng: Range, text: string, pattern: string) => boolean;\n    fetch: (pattern: string, maxResults: number, fetchOptions: Record<string, any>) => Promise<AutocompleterContents[]>;\n    onAction: (autocompleterApi: AutocompleterInstanceApi, rng: Range, value: string, meta: Record<string, any>) => void;\n    maxResults?: number;\n    highlightOn?: string[];\n}\ninterface AutocompleterInstanceApi {\n    hide: () => void;\n    reload: (fetchOptions: Record<string, any>) => void;\n}\ndeclare type ContextPosition = 'node' | 'selection' | 'line';\ndeclare type ContextScope = 'node' | 'editor';\ninterface ContextBarSpec {\n    predicate?: (elem: Element) => boolean;\n    position?: ContextPosition;\n    scope?: ContextScope;\n}\ninterface BaseToolbarButtonSpec<I extends BaseToolbarButtonInstanceApi> {\n    enabled?: boolean;\n    tooltip?: string;\n    icon?: string;\n    text?: string;\n    onSetup?: (api: I) => (api: I) => void;\n}\ninterface BaseToolbarButtonInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n}\ninterface ToolbarButtonSpec extends BaseToolbarButtonSpec<ToolbarButtonInstanceApi> {\n    type?: 'button';\n    onAction: (api: ToolbarButtonInstanceApi) => void;\n}\ninterface ToolbarButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n}\ninterface BaseToolbarToggleButtonSpec<I extends BaseToolbarButtonInstanceApi> extends BaseToolbarButtonSpec<I> {\n    active?: boolean;\n}\ninterface BaseToolbarToggleButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ninterface ToolbarToggleButtonSpec extends BaseToolbarToggleButtonSpec<ToolbarToggleButtonInstanceApi> {\n    type?: 'togglebutton';\n    onAction: (api: ToolbarToggleButtonInstanceApi) => void;\n}\ninterface ToolbarToggleButtonInstanceApi extends BaseToolbarToggleButtonInstanceApi {\n}\ninterface ContextFormLaunchButtonApi extends BaseToolbarButtonSpec<BaseToolbarButtonInstanceApi> {\n    type: 'contextformbutton';\n}\ninterface ContextFormLaunchToggleButtonSpec extends BaseToolbarToggleButtonSpec<BaseToolbarToggleButtonInstanceApi> {\n    type: 'contextformtogglebutton';\n}\ninterface ContextFormButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n}\ninterface ContextFormToggleButtonInstanceApi extends BaseToolbarToggleButtonInstanceApi {\n}\ninterface ContextFormButtonSpec extends BaseToolbarButtonSpec<ContextFormButtonInstanceApi> {\n    type?: 'contextformbutton';\n    primary?: boolean;\n    onAction: (formApi: ContextFormInstanceApi, api: ContextFormButtonInstanceApi) => void;\n}\ninterface ContextFormToggleButtonSpec extends BaseToolbarToggleButtonSpec<ContextFormToggleButtonInstanceApi> {\n    type?: 'contextformtogglebutton';\n    onAction: (formApi: ContextFormInstanceApi, buttonApi: ContextFormToggleButtonInstanceApi) => void;\n    primary?: boolean;\n}\ninterface ContextFormInstanceApi {\n    hide: () => void;\n    getValue: () => string;\n}\ninterface ContextFormSpec extends ContextBarSpec {\n    type?: 'contextform';\n    initValue?: () => string;\n    label?: string;\n    launch?: ContextFormLaunchButtonApi | ContextFormLaunchToggleButtonSpec;\n    commands: Array<ContextFormToggleButtonSpec | ContextFormButtonSpec>;\n}\ninterface ContextToolbarSpec extends ContextBarSpec {\n    type?: 'contexttoolbar';\n    items: string;\n}\ninterface ChoiceMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'choiceitem';\n    icon?: string;\n}\ninterface ChoiceMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ninterface ContextMenuItem extends CommonMenuItemSpec {\n    text: string;\n    icon?: string;\n    type?: 'item';\n    onAction: () => void;\n}\ninterface ContextSubMenu extends CommonMenuItemSpec {\n    type: 'submenu';\n    text: string;\n    icon?: string;\n    getSubmenuItems: () => string | Array<ContextMenuContents>;\n}\ndeclare type ContextMenuContents = string | ContextMenuItem | SeparatorMenuItemSpec | ContextSubMenu;\ninterface ContextMenuApi {\n    update: (element: Element) => string | Array<ContextMenuContents>;\n}\ninterface FancyActionArgsMap {\n    'inserttable': {\n        numRows: number;\n        numColumns: number;\n    };\n    'colorswatch': {\n        value: string;\n    };\n}\ninterface BaseFancyMenuItemSpec<T extends keyof FancyActionArgsMap> {\n    type: 'fancymenuitem';\n    fancytype: T;\n    initData?: Record<string, unknown>;\n    onAction?: (data: FancyActionArgsMap[T]) => void;\n}\ninterface InsertTableMenuItemSpec extends BaseFancyMenuItemSpec<'inserttable'> {\n    fancytype: 'inserttable';\n    initData?: {};\n}\ninterface ColorSwatchMenuItemSpec extends BaseFancyMenuItemSpec<'colorswatch'> {\n    fancytype: 'colorswatch';\n    initData?: {\n        allowCustomColors?: boolean;\n        colors?: ChoiceMenuItemSpec[];\n        storageKey?: string;\n    };\n}\ndeclare type FancyMenuItemSpec = InsertTableMenuItemSpec | ColorSwatchMenuItemSpec;\ninterface MenuItemSpec extends CommonMenuItemSpec {\n    type?: 'menuitem';\n    icon?: string;\n    onSetup?: (api: MenuItemInstanceApi) => (api: MenuItemInstanceApi) => void;\n    onAction?: (api: MenuItemInstanceApi) => void;\n}\ninterface MenuItemInstanceApi extends CommonMenuItemInstanceApi {\n}\ndeclare type NestedMenuItemContents = string | MenuItemSpec | NestedMenuItemSpec | ToggleMenuItemSpec | SeparatorMenuItemSpec | FancyMenuItemSpec;\ninterface NestedMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'nestedmenuitem';\n    icon?: string;\n    getSubmenuItems: () => string | Array<NestedMenuItemContents>;\n    onSetup?: (api: NestedMenuItemInstanceApi) => (api: NestedMenuItemInstanceApi) => void;\n}\ninterface NestedMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n}\ninterface ToggleMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'togglemenuitem';\n    icon?: string;\n    active?: boolean;\n    onSetup?: (api: ToggleMenuItemInstanceApi) => void;\n    onAction: (api: ToggleMenuItemInstanceApi) => void;\n}\ninterface ToggleMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ntype PublicDialog_d_AlertBannerSpec = AlertBannerSpec;\ntype PublicDialog_d_BarSpec = BarSpec;\ntype PublicDialog_d_BodyComponentSpec = BodyComponentSpec;\ntype PublicDialog_d_ButtonSpec = ButtonSpec;\ntype PublicDialog_d_CheckboxSpec = CheckboxSpec;\ntype PublicDialog_d_CollectionItem = CollectionItem;\ntype PublicDialog_d_CollectionSpec = CollectionSpec;\ntype PublicDialog_d_ColorInputSpec = ColorInputSpec;\ntype PublicDialog_d_ColorPickerSpec = ColorPickerSpec;\ntype PublicDialog_d_CustomEditorSpec = CustomEditorSpec;\ntype PublicDialog_d_CustomEditorInit = CustomEditorInit;\ntype PublicDialog_d_CustomEditorInitFn = CustomEditorInitFn;\ntype PublicDialog_d_DialogData = DialogData;\ntype PublicDialog_d_DialogSize = DialogSize;\ntype PublicDialog_d_DialogSpec<T extends DialogData> = DialogSpec<T>;\ntype PublicDialog_d_DialogInstanceApi<T extends DialogData> = DialogInstanceApi<T>;\ntype PublicDialog_d_DialogFooterButtonSpec = DialogFooterButtonSpec;\ntype PublicDialog_d_DialogActionDetails = DialogActionDetails;\ntype PublicDialog_d_DialogChangeDetails<T> = DialogChangeDetails<T>;\ntype PublicDialog_d_DialogTabChangeDetails = DialogTabChangeDetails;\ntype PublicDialog_d_DropZoneSpec = DropZoneSpec;\ntype PublicDialog_d_GridSpec = GridSpec;\ntype PublicDialog_d_HtmlPanelSpec = HtmlPanelSpec;\ntype PublicDialog_d_IframeSpec = IframeSpec;\ntype PublicDialog_d_ImagePreviewSpec = ImagePreviewSpec;\ntype PublicDialog_d_InputSpec = InputSpec;\ntype PublicDialog_d_LabelSpec = LabelSpec;\ntype PublicDialog_d_ListBoxSpec = ListBoxSpec;\ntype PublicDialog_d_ListBoxItemSpec = ListBoxItemSpec;\ntype PublicDialog_d_ListBoxNestedItemSpec = ListBoxNestedItemSpec;\ntype PublicDialog_d_ListBoxSingleItemSpec = ListBoxSingleItemSpec;\ntype PublicDialog_d_PanelSpec = PanelSpec;\ntype PublicDialog_d_SelectBoxSpec = SelectBoxSpec;\ntype PublicDialog_d_SelectBoxItemSpec = SelectBoxItemSpec;\ntype PublicDialog_d_SizeInputSpec = SizeInputSpec;\ntype PublicDialog_d_SliderSpec = SliderSpec;\ntype PublicDialog_d_TableSpec = TableSpec;\ntype PublicDialog_d_TabSpec = TabSpec;\ntype PublicDialog_d_TabPanelSpec = TabPanelSpec;\ntype PublicDialog_d_TextAreaSpec = TextAreaSpec;\ntype PublicDialog_d_UrlInputData = UrlInputData;\ntype PublicDialog_d_UrlInputSpec = UrlInputSpec;\ntype PublicDialog_d_UrlDialogSpec = UrlDialogSpec;\ntype PublicDialog_d_UrlDialogFooterButtonSpec = UrlDialogFooterButtonSpec;\ntype PublicDialog_d_UrlDialogInstanceApi = UrlDialogInstanceApi;\ntype PublicDialog_d_UrlDialogActionDetails = UrlDialogActionDetails;\ntype PublicDialog_d_UrlDialogMessage = UrlDialogMessage;\ndeclare namespace PublicDialog_d {\n    export { PublicDialog_d_AlertBannerSpec as AlertBannerSpec, PublicDialog_d_BarSpec as BarSpec, PublicDialog_d_BodyComponentSpec as BodyComponentSpec, PublicDialog_d_ButtonSpec as ButtonSpec, PublicDialog_d_CheckboxSpec as CheckboxSpec, PublicDialog_d_CollectionItem as CollectionItem, PublicDialog_d_CollectionSpec as CollectionSpec, PublicDialog_d_ColorInputSpec as ColorInputSpec, PublicDialog_d_ColorPickerSpec as ColorPickerSpec, PublicDialog_d_CustomEditorSpec as CustomEditorSpec, PublicDialog_d_CustomEditorInit as CustomEditorInit, PublicDialog_d_CustomEditorInitFn as CustomEditorInitFn, PublicDialog_d_DialogData as DialogData, PublicDialog_d_DialogSize as DialogSize, PublicDialog_d_DialogSpec as DialogSpec, PublicDialog_d_DialogInstanceApi as DialogInstanceApi, PublicDialog_d_DialogFooterButtonSpec as DialogFooterButtonSpec, PublicDialog_d_DialogActionDetails as DialogActionDetails, PublicDialog_d_DialogChangeDetails as DialogChangeDetails, PublicDialog_d_DialogTabChangeDetails as DialogTabChangeDetails, PublicDialog_d_DropZoneSpec as DropZoneSpec, PublicDialog_d_GridSpec as GridSpec, PublicDialog_d_HtmlPanelSpec as HtmlPanelSpec, PublicDialog_d_IframeSpec as IframeSpec, PublicDialog_d_ImagePreviewSpec as ImagePreviewSpec, PublicDialog_d_InputSpec as InputSpec, PublicDialog_d_LabelSpec as LabelSpec, PublicDialog_d_ListBoxSpec as ListBoxSpec, PublicDialog_d_ListBoxItemSpec as ListBoxItemSpec, PublicDialog_d_ListBoxNestedItemSpec as ListBoxNestedItemSpec, PublicDialog_d_ListBoxSingleItemSpec as ListBoxSingleItemSpec, PublicDialog_d_PanelSpec as PanelSpec, PublicDialog_d_SelectBoxSpec as SelectBoxSpec, PublicDialog_d_SelectBoxItemSpec as SelectBoxItemSpec, PublicDialog_d_SizeInputSpec as SizeInputSpec, PublicDialog_d_SliderSpec as SliderSpec, PublicDialog_d_TableSpec as TableSpec, PublicDialog_d_TabSpec as TabSpec, PublicDialog_d_TabPanelSpec as TabPanelSpec, PublicDialog_d_TextAreaSpec as TextAreaSpec, PublicDialog_d_UrlInputData as UrlInputData, PublicDialog_d_UrlInputSpec as UrlInputSpec, PublicDialog_d_UrlDialogSpec as UrlDialogSpec, PublicDialog_d_UrlDialogFooterButtonSpec as UrlDialogFooterButtonSpec, PublicDialog_d_UrlDialogInstanceApi as UrlDialogInstanceApi, PublicDialog_d_UrlDialogActionDetails as UrlDialogActionDetails, PublicDialog_d_UrlDialogMessage as UrlDialogMessage, };\n}\ntype PublicInlineContent_d_AutocompleterSpec = AutocompleterSpec;\ntype PublicInlineContent_d_AutocompleterItemSpec = AutocompleterItemSpec;\ntype PublicInlineContent_d_AutocompleterContents = AutocompleterContents;\ntype PublicInlineContent_d_AutocompleterInstanceApi = AutocompleterInstanceApi;\ntype PublicInlineContent_d_ContextPosition = ContextPosition;\ntype PublicInlineContent_d_ContextScope = ContextScope;\ntype PublicInlineContent_d_ContextFormSpec = ContextFormSpec;\ntype PublicInlineContent_d_ContextFormInstanceApi = ContextFormInstanceApi;\ntype PublicInlineContent_d_ContextFormButtonSpec = ContextFormButtonSpec;\ntype PublicInlineContent_d_ContextFormButtonInstanceApi = ContextFormButtonInstanceApi;\ntype PublicInlineContent_d_ContextFormToggleButtonSpec = ContextFormToggleButtonSpec;\ntype PublicInlineContent_d_ContextFormToggleButtonInstanceApi = ContextFormToggleButtonInstanceApi;\ntype PublicInlineContent_d_ContextToolbarSpec = ContextToolbarSpec;\ntype PublicInlineContent_d_SeparatorItemSpec = SeparatorItemSpec;\ndeclare namespace PublicInlineContent_d {\n    export { PublicInlineContent_d_AutocompleterSpec as AutocompleterSpec, PublicInlineContent_d_AutocompleterItemSpec as AutocompleterItemSpec, PublicInlineContent_d_AutocompleterContents as AutocompleterContents, PublicInlineContent_d_AutocompleterInstanceApi as AutocompleterInstanceApi, PublicInlineContent_d_ContextPosition as ContextPosition, PublicInlineContent_d_ContextScope as ContextScope, PublicInlineContent_d_ContextFormSpec as ContextFormSpec, PublicInlineContent_d_ContextFormInstanceApi as ContextFormInstanceApi, PublicInlineContent_d_ContextFormButtonSpec as ContextFormButtonSpec, PublicInlineContent_d_ContextFormButtonInstanceApi as ContextFormButtonInstanceApi, PublicInlineContent_d_ContextFormToggleButtonSpec as ContextFormToggleButtonSpec, PublicInlineContent_d_ContextFormToggleButtonInstanceApi as ContextFormToggleButtonInstanceApi, PublicInlineContent_d_ContextToolbarSpec as ContextToolbarSpec, PublicInlineContent_d_SeparatorItemSpec as SeparatorItemSpec, };\n}\ntype PublicMenu_d_MenuItemSpec = MenuItemSpec;\ntype PublicMenu_d_MenuItemInstanceApi = MenuItemInstanceApi;\ntype PublicMenu_d_NestedMenuItemContents = NestedMenuItemContents;\ntype PublicMenu_d_NestedMenuItemSpec = NestedMenuItemSpec;\ntype PublicMenu_d_NestedMenuItemInstanceApi = NestedMenuItemInstanceApi;\ntype PublicMenu_d_FancyMenuItemSpec = FancyMenuItemSpec;\ntype PublicMenu_d_ColorSwatchMenuItemSpec = ColorSwatchMenuItemSpec;\ntype PublicMenu_d_InsertTableMenuItemSpec = InsertTableMenuItemSpec;\ntype PublicMenu_d_ToggleMenuItemSpec = ToggleMenuItemSpec;\ntype PublicMenu_d_ToggleMenuItemInstanceApi = ToggleMenuItemInstanceApi;\ntype PublicMenu_d_ChoiceMenuItemSpec = ChoiceMenuItemSpec;\ntype PublicMenu_d_ChoiceMenuItemInstanceApi = ChoiceMenuItemInstanceApi;\ntype PublicMenu_d_SeparatorMenuItemSpec = SeparatorMenuItemSpec;\ntype PublicMenu_d_ContextMenuApi = ContextMenuApi;\ntype PublicMenu_d_ContextMenuContents = ContextMenuContents;\ntype PublicMenu_d_ContextMenuItem = ContextMenuItem;\ntype PublicMenu_d_ContextSubMenu = ContextSubMenu;\ntype PublicMenu_d_CardMenuItemSpec = CardMenuItemSpec;\ntype PublicMenu_d_CardMenuItemInstanceApi = CardMenuItemInstanceApi;\ntype PublicMenu_d_CardItemSpec = CardItemSpec;\ntype PublicMenu_d_CardContainerSpec = CardContainerSpec;\ntype PublicMenu_d_CardImageSpec = CardImageSpec;\ntype PublicMenu_d_CardTextSpec = CardTextSpec;\ndeclare namespace PublicMenu_d {\n    export { PublicMenu_d_MenuItemSpec as MenuItemSpec, PublicMenu_d_MenuItemInstanceApi as MenuItemInstanceApi, PublicMenu_d_NestedMenuItemContents as NestedMenuItemContents, PublicMenu_d_NestedMenuItemSpec as NestedMenuItemSpec, PublicMenu_d_NestedMenuItemInstanceApi as NestedMenuItemInstanceApi, PublicMenu_d_FancyMenuItemSpec as FancyMenuItemSpec, PublicMenu_d_ColorSwatchMenuItemSpec as ColorSwatchMenuItemSpec, PublicMenu_d_InsertTableMenuItemSpec as InsertTableMenuItemSpec, PublicMenu_d_ToggleMenuItemSpec as ToggleMenuItemSpec, PublicMenu_d_ToggleMenuItemInstanceApi as ToggleMenuItemInstanceApi, PublicMenu_d_ChoiceMenuItemSpec as ChoiceMenuItemSpec, PublicMenu_d_ChoiceMenuItemInstanceApi as ChoiceMenuItemInstanceApi, PublicMenu_d_SeparatorMenuItemSpec as SeparatorMenuItemSpec, PublicMenu_d_ContextMenuApi as ContextMenuApi, PublicMenu_d_ContextMenuContents as ContextMenuContents, PublicMenu_d_ContextMenuItem as ContextMenuItem, PublicMenu_d_ContextSubMenu as ContextSubMenu, PublicMenu_d_CardMenuItemSpec as CardMenuItemSpec, PublicMenu_d_CardMenuItemInstanceApi as CardMenuItemInstanceApi, PublicMenu_d_CardItemSpec as CardItemSpec, PublicMenu_d_CardContainerSpec as CardContainerSpec, PublicMenu_d_CardImageSpec as CardImageSpec, PublicMenu_d_CardTextSpec as CardTextSpec, };\n}\ninterface SidebarInstanceApi {\n    element: () => HTMLElement;\n}\ninterface SidebarSpec {\n    icon?: string;\n    tooltip?: string;\n    onShow?: (api: SidebarInstanceApi) => void;\n    onSetup?: (api: SidebarInstanceApi) => (api: SidebarInstanceApi) => void;\n    onHide?: (api: SidebarInstanceApi) => void;\n}\ntype PublicSidebar_d_SidebarSpec = SidebarSpec;\ntype PublicSidebar_d_SidebarInstanceApi = SidebarInstanceApi;\ndeclare namespace PublicSidebar_d {\n    export { PublicSidebar_d_SidebarSpec as SidebarSpec, PublicSidebar_d_SidebarInstanceApi as SidebarInstanceApi, };\n}\ninterface ToolbarGroupSetting {\n    name: string;\n    items: string[];\n}\ndeclare type ToolbarConfig = string | ToolbarGroupSetting[];\ninterface GroupToolbarButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n}\ninterface GroupToolbarButtonSpec extends BaseToolbarButtonSpec<GroupToolbarButtonInstanceApi> {\n    type?: 'grouptoolbarbutton';\n    items?: ToolbarConfig;\n}\ndeclare type MenuButtonItemTypes = NestedMenuItemContents;\ndeclare type SuccessCallback$1 = (menu: string | MenuButtonItemTypes[]) => void;\ninterface MenuButtonFetchContext {\n    pattern: string;\n}\ninterface BaseMenuButtonSpec {\n    text?: string;\n    tooltip?: string;\n    icon?: string;\n    search?: boolean | {\n        placeholder?: string;\n    };\n    fetch: (success: SuccessCallback$1, fetchContext: MenuButtonFetchContext) => void;\n    onSetup?: (api: BaseMenuButtonInstanceApi) => (api: BaseMenuButtonInstanceApi) => void;\n}\ninterface BaseMenuButtonInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ninterface ToolbarMenuButtonSpec extends BaseMenuButtonSpec {\n    type?: 'menubutton';\n    onSetup?: (api: ToolbarMenuButtonInstanceApi) => (api: ToolbarMenuButtonInstanceApi) => void;\n}\ninterface ToolbarMenuButtonInstanceApi extends BaseMenuButtonInstanceApi {\n}\ndeclare type ToolbarSplitButtonItemTypes = ChoiceMenuItemSpec | SeparatorMenuItemSpec;\ndeclare type SuccessCallback = (menu: ToolbarSplitButtonItemTypes[]) => void;\ndeclare type SelectPredicate = (value: string) => boolean;\ndeclare type PresetTypes = 'color' | 'normal' | 'listpreview';\ndeclare type ColumnTypes = number | 'auto';\ninterface ToolbarSplitButtonSpec {\n    type?: 'splitbutton';\n    tooltip?: string;\n    icon?: string;\n    text?: string;\n    select?: SelectPredicate;\n    presets?: PresetTypes;\n    columns?: ColumnTypes;\n    fetch: (success: SuccessCallback) => void;\n    onSetup?: (api: ToolbarSplitButtonInstanceApi) => (api: ToolbarSplitButtonInstanceApi) => void;\n    onAction: (api: ToolbarSplitButtonInstanceApi) => void;\n    onItemAction: (api: ToolbarSplitButtonInstanceApi, value: string) => void;\n}\ninterface ToolbarSplitButtonInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n    setIconFill: (id: string, value: string) => void;\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ntype PublicToolbar_d_ToolbarButtonSpec = ToolbarButtonSpec;\ntype PublicToolbar_d_ToolbarButtonInstanceApi = ToolbarButtonInstanceApi;\ntype PublicToolbar_d_ToolbarSplitButtonSpec = ToolbarSplitButtonSpec;\ntype PublicToolbar_d_ToolbarSplitButtonInstanceApi = ToolbarSplitButtonInstanceApi;\ntype PublicToolbar_d_ToolbarMenuButtonSpec = ToolbarMenuButtonSpec;\ntype PublicToolbar_d_ToolbarMenuButtonInstanceApi = ToolbarMenuButtonInstanceApi;\ntype PublicToolbar_d_ToolbarToggleButtonSpec = ToolbarToggleButtonSpec;\ntype PublicToolbar_d_ToolbarToggleButtonInstanceApi = ToolbarToggleButtonInstanceApi;\ntype PublicToolbar_d_GroupToolbarButtonSpec = GroupToolbarButtonSpec;\ntype PublicToolbar_d_GroupToolbarButtonInstanceApi = GroupToolbarButtonInstanceApi;\ndeclare namespace PublicToolbar_d {\n    export { PublicToolbar_d_ToolbarButtonSpec as ToolbarButtonSpec, PublicToolbar_d_ToolbarButtonInstanceApi as ToolbarButtonInstanceApi, PublicToolbar_d_ToolbarSplitButtonSpec as ToolbarSplitButtonSpec, PublicToolbar_d_ToolbarSplitButtonInstanceApi as ToolbarSplitButtonInstanceApi, PublicToolbar_d_ToolbarMenuButtonSpec as ToolbarMenuButtonSpec, PublicToolbar_d_ToolbarMenuButtonInstanceApi as ToolbarMenuButtonInstanceApi, PublicToolbar_d_ToolbarToggleButtonSpec as ToolbarToggleButtonSpec, PublicToolbar_d_ToolbarToggleButtonInstanceApi as ToolbarToggleButtonInstanceApi, PublicToolbar_d_GroupToolbarButtonSpec as GroupToolbarButtonSpec, PublicToolbar_d_GroupToolbarButtonInstanceApi as GroupToolbarButtonInstanceApi, };\n}\ninterface ViewNormalButtonSpec {\n    type: 'button';\n    text: string;\n    buttonType?: 'primary' | 'secondary';\n    onAction: () => void;\n}\ndeclare type ViewButtonSpec = ViewNormalButtonSpec;\ninterface ViewInstanceApi {\n    getContainer: () => HTMLElement;\n}\ninterface ViewSpec {\n    buttons?: ViewButtonSpec[];\n    onShow: (api: ViewInstanceApi) => void;\n    onHide: (api: ViewInstanceApi) => void;\n}\ntype PublicView_d_ViewSpec = ViewSpec;\ntype PublicView_d_ViewInstanceApi = ViewInstanceApi;\ndeclare namespace PublicView_d {\n    export { PublicView_d_ViewSpec as ViewSpec, PublicView_d_ViewInstanceApi as ViewInstanceApi, };\n}\ninterface Registry$1 {\n    addButton: (name: string, spec: ToolbarButtonSpec) => void;\n    addGroupToolbarButton: (name: string, spec: GroupToolbarButtonSpec) => void;\n    addToggleButton: (name: string, spec: ToolbarToggleButtonSpec) => void;\n    addMenuButton: (name: string, spec: ToolbarMenuButtonSpec) => void;\n    addSplitButton: (name: string, spec: ToolbarSplitButtonSpec) => void;\n    addMenuItem: (name: string, spec: MenuItemSpec) => void;\n    addNestedMenuItem: (name: string, spec: NestedMenuItemSpec) => void;\n    addToggleMenuItem: (name: string, spec: ToggleMenuItemSpec) => void;\n    addContextMenu: (name: string, spec: ContextMenuApi) => void;\n    addContextToolbar: (name: string, spec: ContextToolbarSpec) => void;\n    addContextForm: (name: string, spec: ContextFormSpec) => void;\n    addIcon: (name: string, svgData: string) => void;\n    addAutocompleter: (name: string, spec: AutocompleterSpec) => void;\n    addSidebar: (name: string, spec: SidebarSpec) => void;\n    addView: (name: string, spec: ViewSpec) => void;\n    getAll: () => {\n        buttons: Record<string, ToolbarButtonSpec | GroupToolbarButtonSpec | ToolbarMenuButtonSpec | ToolbarSplitButtonSpec | ToolbarToggleButtonSpec>;\n        menuItems: Record<string, MenuItemSpec | NestedMenuItemSpec | ToggleMenuItemSpec>;\n        popups: Record<string, AutocompleterSpec>;\n        contextMenus: Record<string, ContextMenuApi>;\n        contextToolbars: Record<string, ContextToolbarSpec | ContextFormSpec>;\n        icons: Record<string, string>;\n        sidebars: Record<string, SidebarSpec>;\n        views: Record<string, ViewSpec>;\n    };\n}\ninterface AutocompleteLookupData {\n    readonly matchText: string;\n    readonly items: AutocompleterContents[];\n    readonly columns: ColumnTypes$1;\n    readonly onAction: (autoApi: AutocompleterInstanceApi, rng: Range, value: string, meta: Record<string, any>) => void;\n    readonly highlightOn: string[];\n}\ninterface AutocompleterEventArgs {\n    readonly lookupData: AutocompleteLookupData[];\n}\ninterface RangeLikeObject {\n    startContainer: Node;\n    startOffset: number;\n    endContainer: Node;\n    endOffset: number;\n}\ndeclare type ApplyFormat = BlockFormat | InlineFormat | SelectorFormat;\ndeclare type RemoveFormat = RemoveBlockFormat | RemoveInlineFormat | RemoveSelectorFormat;\ndeclare type Format = ApplyFormat | RemoveFormat;\ndeclare type Formats = Record<string, Format | Format[]>;\ndeclare type FormatAttrOrStyleValue = string | ((vars?: FormatVars) => string | null);\ndeclare type FormatVars = Record<string, string | null>;\ninterface BaseFormat<T> {\n    ceFalseOverride?: boolean;\n    classes?: string | string[];\n    collapsed?: boolean;\n    exact?: boolean;\n    expand?: boolean;\n    links?: boolean;\n    mixed?: boolean;\n    block_expand?: boolean;\n    onmatch?: (node: Element, fmt: T, itemName: string) => boolean;\n    remove?: 'none' | 'empty' | 'all';\n    remove_similar?: boolean;\n    split?: boolean;\n    deep?: boolean;\n    preserve_attributes?: string[];\n}\ninterface Block {\n    block: string;\n    list_block?: string;\n    wrapper?: boolean;\n}\ninterface Inline {\n    inline: string;\n}\ninterface Selector {\n    selector: string;\n    inherit?: boolean;\n}\ninterface CommonFormat<T> extends BaseFormat<T> {\n    attributes?: Record<string, FormatAttrOrStyleValue>;\n    styles?: Record<string, FormatAttrOrStyleValue>;\n    toggle?: boolean;\n    preview?: string | false;\n    onformat?: (elm: Element, fmt: T, vars?: FormatVars, node?: Node | RangeLikeObject | null) => void;\n    clear_child_styles?: boolean;\n    merge_siblings?: boolean;\n    merge_with_parents?: boolean;\n}\ninterface BlockFormat extends Block, CommonFormat<BlockFormat> {\n}\ninterface InlineFormat extends Inline, CommonFormat<InlineFormat> {\n}\ninterface SelectorFormat extends Selector, CommonFormat<SelectorFormat> {\n}\ninterface CommonRemoveFormat<T> extends BaseFormat<T> {\n    attributes?: string[] | Record<string, FormatAttrOrStyleValue>;\n    styles?: string[] | Record<string, FormatAttrOrStyleValue>;\n}\ninterface RemoveBlockFormat extends Block, CommonRemoveFormat<RemoveBlockFormat> {\n}\ninterface RemoveInlineFormat extends Inline, CommonRemoveFormat<RemoveInlineFormat> {\n}\ninterface RemoveSelectorFormat extends Selector, CommonRemoveFormat<RemoveSelectorFormat> {\n}\ninterface Filter<C extends Function> {\n    name: string;\n    callbacks: C[];\n}\ninterface ParserArgs {\n    getInner?: boolean | number;\n    forced_root_block?: boolean | string;\n    context?: string;\n    isRootContent?: boolean;\n    format?: string;\n    invalid?: boolean;\n    no_events?: boolean;\n    [key: string]: any;\n}\ndeclare type ParserFilterCallback = (nodes: AstNode[], name: string, args: ParserArgs) => void;\ninterface ParserFilter extends Filter<ParserFilterCallback> {\n}\ninterface DomParserSettings {\n    allow_html_data_urls?: boolean;\n    allow_svg_data_urls?: boolean;\n    allow_conditional_comments?: boolean;\n    allow_html_in_named_anchor?: boolean;\n    allow_script_urls?: boolean;\n    allow_unsafe_link_target?: boolean;\n    convert_fonts_to_spans?: boolean;\n    fix_list_elements?: boolean;\n    font_size_legacy_values?: string;\n    forced_root_block?: boolean | string;\n    forced_root_block_attrs?: Record<string, string>;\n    preserve_cdata?: boolean;\n    remove_trailing_brs?: boolean;\n    root_name?: string;\n    validate?: boolean;\n    inline_styles?: boolean;\n    blob_cache?: BlobCache;\n    document?: Document;\n}\ninterface DomParser {\n    schema: Schema;\n    addAttributeFilter: (name: string, callback: ParserFilterCallback) => void;\n    getAttributeFilters: () => ParserFilter[];\n    removeAttributeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    addNodeFilter: (name: string, callback: ParserFilterCallback) => void;\n    getNodeFilters: () => ParserFilter[];\n    removeNodeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    parse: (html: string, args?: ParserArgs) => AstNode;\n}\ninterface StyleSheetLoaderSettings {\n    maxLoadTime?: number;\n    contentCssCors?: boolean;\n    referrerPolicy?: ReferrerPolicy;\n}\ninterface StyleSheetLoader {\n    load: (url: string) => Promise<void>;\n    loadAll: (urls: string[]) => Promise<string[]>;\n    unload: (url: string) => void;\n    unloadAll: (urls: string[]) => void;\n    _setReferrerPolicy: (referrerPolicy: ReferrerPolicy) => void;\n    _setContentCssCors: (contentCssCors: boolean) => void;\n}\ndeclare type Registry = Registry$1;\ninterface EditorUiApi {\n    show: () => void;\n    hide: () => void;\n    setEnabled: (state: boolean) => void;\n    isEnabled: () => boolean;\n}\ninterface EditorUi extends EditorUiApi {\n    registry: Registry;\n    styleSheetLoader: StyleSheetLoader;\n}\ntype Ui_d_Registry = Registry;\ntype Ui_d_EditorUiApi = EditorUiApi;\ntype Ui_d_EditorUi = EditorUi;\ndeclare namespace Ui_d {\n    export { Ui_d_Registry as Registry, PublicDialog_d as Dialog, PublicInlineContent_d as InlineContent, PublicMenu_d as Menu, PublicView_d as View, PublicSidebar_d as Sidebar, PublicToolbar_d as Toolbar, Ui_d_EditorUiApi as EditorUiApi, Ui_d_EditorUi as EditorUi, };\n}\ninterface WindowParams {\n    readonly inline?: 'cursor' | 'toolbar';\n    readonly ariaAttrs?: boolean;\n}\ndeclare type InstanceApi<T extends DialogData> = UrlDialogInstanceApi | DialogInstanceApi<T>;\ninterface WindowManagerImpl {\n    open: <T extends DialogData>(config: DialogSpec<T>, params: WindowParams | undefined, closeWindow: (dialog: DialogInstanceApi<T>) => void) => DialogInstanceApi<T>;\n    openUrl: (config: UrlDialogSpec, closeWindow: (dialog: UrlDialogInstanceApi) => void) => UrlDialogInstanceApi;\n    alert: (message: string, callback: () => void) => void;\n    confirm: (message: string, callback: (state: boolean) => void) => void;\n    close: (dialog: InstanceApi<any>) => void;\n}\ninterface WindowManager {\n    open: <T extends DialogData>(config: DialogSpec<T>, params?: WindowParams) => DialogInstanceApi<T>;\n    openUrl: (config: UrlDialogSpec) => UrlDialogInstanceApi;\n    alert: (message: string, callback?: () => void, scope?: any) => void;\n    confirm: (message: string, callback?: (state: boolean) => void, scope?: any) => void;\n    close: () => void;\n}\ninterface ExecCommandEvent {\n    command: string;\n    ui: boolean;\n    value?: any;\n}\ninterface BeforeGetContentEvent extends GetContentArgs {\n    selection?: boolean;\n}\ninterface GetContentEvent extends BeforeGetContentEvent {\n    content: string;\n}\ninterface BeforeSetContentEvent extends SetContentArgs {\n    content: string;\n    selection?: boolean;\n}\ninterface SetContentEvent extends BeforeSetContentEvent {\n    content: string;\n}\ninterface SaveContentEvent extends GetContentEvent {\n    save: boolean;\n}\ninterface NewBlockEvent {\n    newBlock: Element;\n}\ninterface NodeChangeEvent {\n    element: Element;\n    parents: Node[];\n    selectionChange?: boolean;\n    initial?: boolean;\n}\ninterface FormatEvent {\n    format: string;\n    vars?: FormatVars;\n    node?: Node | RangeLikeObject | null;\n}\ninterface ObjectResizeEvent {\n    target: HTMLElement;\n    width: number;\n    height: number;\n    origin: string;\n}\ninterface ObjectSelectedEvent {\n    target: Node;\n    targetClone?: Node;\n}\ninterface ScrollIntoViewEvent {\n    elm: HTMLElement;\n    alignToTop: boolean | undefined;\n}\ninterface SetSelectionRangeEvent {\n    range: Range;\n    forward: boolean | undefined;\n}\ninterface ShowCaretEvent {\n    target: Node;\n    direction: number;\n    before: boolean;\n}\ninterface SwitchModeEvent {\n    mode: string;\n}\ninterface ChangeEvent {\n    level: UndoLevel;\n    lastLevel: UndoLevel | undefined;\n}\ninterface AddUndoEvent extends ChangeEvent {\n    originalEvent: Event | undefined;\n}\ninterface UndoRedoEvent {\n    level: UndoLevel;\n}\ninterface WindowEvent<T extends DialogData> {\n    dialog: InstanceApi<T>;\n}\ninterface ProgressStateEvent {\n    state: boolean;\n    time?: number;\n}\ninterface AfterProgressStateEvent {\n    state: boolean;\n}\ninterface PlaceholderToggleEvent {\n    state: boolean;\n}\ninterface LoadErrorEvent {\n    message: string;\n}\ninterface PreProcessEvent extends ParserArgs {\n    node: Element;\n}\ninterface PostProcessEvent extends ParserArgs {\n    content: string;\n}\ninterface PastePlainTextToggleEvent {\n    state: boolean;\n}\ninterface PastePreProcessEvent {\n    content: string;\n    readonly internal: boolean;\n}\ninterface PastePostProcessEvent {\n    node: HTMLElement;\n    readonly internal: boolean;\n}\ninterface NewTableRowEvent {\n    node: HTMLTableRowElement;\n}\ninterface NewTableCellEvent {\n    node: HTMLTableCellElement;\n}\ninterface TableEventData {\n    readonly structure: boolean;\n    readonly style: boolean;\n}\ninterface TableModifiedEvent extends TableEventData {\n    readonly table: HTMLTableElement;\n}\ninterface BeforeOpenNotificationEvent {\n    notification: NotificationSpec;\n}\ninterface OpenNotificationEvent {\n    notification: NotificationApi;\n}\ninterface EditorEventMap extends Omit<NativeEventMap, 'blur' | 'focus'> {\n    'activate': {\n        relatedTarget: Editor | null;\n    };\n    'deactivate': {\n        relatedTarget: Editor;\n    };\n    'focus': {\n        blurredEditor: Editor | null;\n    };\n    'blur': {\n        focusedEditor: Editor | null;\n    };\n    'resize': UIEvent;\n    'scroll': UIEvent;\n    'detach': {};\n    'remove': {};\n    'init': {};\n    'ScrollIntoView': ScrollIntoViewEvent;\n    'AfterScrollIntoView': ScrollIntoViewEvent;\n    'ObjectResized': ObjectResizeEvent;\n    'ObjectResizeStart': ObjectResizeEvent;\n    'SwitchMode': SwitchModeEvent;\n    'ScrollWindow': Event;\n    'ResizeWindow': UIEvent;\n    'SkinLoaded': {};\n    'SkinLoadError': LoadErrorEvent;\n    'PluginLoadError': LoadErrorEvent;\n    'ModelLoadError': LoadErrorEvent;\n    'IconsLoadError': LoadErrorEvent;\n    'ThemeLoadError': LoadErrorEvent;\n    'LanguageLoadError': LoadErrorEvent;\n    'BeforeExecCommand': ExecCommandEvent;\n    'ExecCommand': ExecCommandEvent;\n    'NodeChange': NodeChangeEvent;\n    'FormatApply': FormatEvent;\n    'FormatRemove': FormatEvent;\n    'ShowCaret': ShowCaretEvent;\n    'SelectionChange': {};\n    'ObjectSelected': ObjectSelectedEvent;\n    'BeforeObjectSelected': ObjectSelectedEvent;\n    'GetSelectionRange': {\n        range: Range;\n    };\n    'SetSelectionRange': SetSelectionRangeEvent;\n    'AfterSetSelectionRange': SetSelectionRangeEvent;\n    'BeforeGetContent': BeforeGetContentEvent;\n    'GetContent': GetContentEvent;\n    'BeforeSetContent': BeforeSetContentEvent;\n    'SetContent': SetContentEvent;\n    'SaveContent': SaveContentEvent;\n    'RawSaveContent': SaveContentEvent;\n    'LoadContent': {\n        load: boolean;\n        element: HTMLElement;\n    };\n    'PreviewFormats': {};\n    'AfterPreviewFormats': {};\n    'ScriptsLoaded': {};\n    'PreInit': {};\n    'PostRender': {};\n    'NewBlock': NewBlockEvent;\n    'ClearUndos': {};\n    'TypingUndo': {};\n    'Redo': UndoRedoEvent;\n    'Undo': UndoRedoEvent;\n    'BeforeAddUndo': AddUndoEvent;\n    'AddUndo': AddUndoEvent;\n    'change': ChangeEvent;\n    'CloseWindow': WindowEvent<any>;\n    'OpenWindow': WindowEvent<any>;\n    'ProgressState': ProgressStateEvent;\n    'AfterProgressState': AfterProgressStateEvent;\n    'PlaceholderToggle': PlaceholderToggleEvent;\n    'tap': TouchEvent;\n    'longpress': TouchEvent;\n    'longpresscancel': {};\n    'PreProcess': PreProcessEvent;\n    'PostProcess': PostProcessEvent;\n    'AutocompleterStart': AutocompleterEventArgs;\n    'AutocompleterUpdate': AutocompleterEventArgs;\n    'AutocompleterEnd': {};\n    'PastePlainTextToggle': PastePlainTextToggleEvent;\n    'PastePreProcess': PastePreProcessEvent;\n    'PastePostProcess': PastePostProcessEvent;\n    'TableModified': TableModifiedEvent;\n    'NewRow': NewTableRowEvent;\n    'NewCell': NewTableCellEvent;\n    'SetAttrib': SetAttribEvent;\n    'hide': {};\n    'show': {};\n    'dirty': {};\n    'BeforeOpenNotification': BeforeOpenNotificationEvent;\n    'OpenNotification': OpenNotificationEvent;\n}\ninterface EditorManagerEventMap {\n    'AddEditor': {\n        editor: Editor;\n    };\n    'RemoveEditor': {\n        editor: Editor;\n    };\n    'BeforeUnload': {\n        returnValue: any;\n    };\n}\ntype EventTypes_d_ExecCommandEvent = ExecCommandEvent;\ntype EventTypes_d_BeforeGetContentEvent = BeforeGetContentEvent;\ntype EventTypes_d_GetContentEvent = GetContentEvent;\ntype EventTypes_d_BeforeSetContentEvent = BeforeSetContentEvent;\ntype EventTypes_d_SetContentEvent = SetContentEvent;\ntype EventTypes_d_SaveContentEvent = SaveContentEvent;\ntype EventTypes_d_NewBlockEvent = NewBlockEvent;\ntype EventTypes_d_NodeChangeEvent = NodeChangeEvent;\ntype EventTypes_d_FormatEvent = FormatEvent;\ntype EventTypes_d_ObjectResizeEvent = ObjectResizeEvent;\ntype EventTypes_d_ObjectSelectedEvent = ObjectSelectedEvent;\ntype EventTypes_d_ScrollIntoViewEvent = ScrollIntoViewEvent;\ntype EventTypes_d_SetSelectionRangeEvent = SetSelectionRangeEvent;\ntype EventTypes_d_ShowCaretEvent = ShowCaretEvent;\ntype EventTypes_d_SwitchModeEvent = SwitchModeEvent;\ntype EventTypes_d_ChangeEvent = ChangeEvent;\ntype EventTypes_d_AddUndoEvent = AddUndoEvent;\ntype EventTypes_d_UndoRedoEvent = UndoRedoEvent;\ntype EventTypes_d_WindowEvent<T extends DialogData> = WindowEvent<T>;\ntype EventTypes_d_ProgressStateEvent = ProgressStateEvent;\ntype EventTypes_d_AfterProgressStateEvent = AfterProgressStateEvent;\ntype EventTypes_d_PlaceholderToggleEvent = PlaceholderToggleEvent;\ntype EventTypes_d_LoadErrorEvent = LoadErrorEvent;\ntype EventTypes_d_PreProcessEvent = PreProcessEvent;\ntype EventTypes_d_PostProcessEvent = PostProcessEvent;\ntype EventTypes_d_PastePlainTextToggleEvent = PastePlainTextToggleEvent;\ntype EventTypes_d_PastePreProcessEvent = PastePreProcessEvent;\ntype EventTypes_d_PastePostProcessEvent = PastePostProcessEvent;\ntype EventTypes_d_NewTableRowEvent = NewTableRowEvent;\ntype EventTypes_d_NewTableCellEvent = NewTableCellEvent;\ntype EventTypes_d_TableEventData = TableEventData;\ntype EventTypes_d_TableModifiedEvent = TableModifiedEvent;\ntype EventTypes_d_BeforeOpenNotificationEvent = BeforeOpenNotificationEvent;\ntype EventTypes_d_OpenNotificationEvent = OpenNotificationEvent;\ntype EventTypes_d_EditorEventMap = EditorEventMap;\ntype EventTypes_d_EditorManagerEventMap = EditorManagerEventMap;\ndeclare namespace EventTypes_d {\n    export { EventTypes_d_ExecCommandEvent as ExecCommandEvent, EventTypes_d_BeforeGetContentEvent as BeforeGetContentEvent, EventTypes_d_GetContentEvent as GetContentEvent, EventTypes_d_BeforeSetContentEvent as BeforeSetContentEvent, EventTypes_d_SetContentEvent as SetContentEvent, EventTypes_d_SaveContentEvent as SaveContentEvent, EventTypes_d_NewBlockEvent as NewBlockEvent, EventTypes_d_NodeChangeEvent as NodeChangeEvent, EventTypes_d_FormatEvent as FormatEvent, EventTypes_d_ObjectResizeEvent as ObjectResizeEvent, EventTypes_d_ObjectSelectedEvent as ObjectSelectedEvent, EventTypes_d_ScrollIntoViewEvent as ScrollIntoViewEvent, EventTypes_d_SetSelectionRangeEvent as SetSelectionRangeEvent, EventTypes_d_ShowCaretEvent as ShowCaretEvent, EventTypes_d_SwitchModeEvent as SwitchModeEvent, EventTypes_d_ChangeEvent as ChangeEvent, EventTypes_d_AddUndoEvent as AddUndoEvent, EventTypes_d_UndoRedoEvent as UndoRedoEvent, EventTypes_d_WindowEvent as WindowEvent, EventTypes_d_ProgressStateEvent as ProgressStateEvent, EventTypes_d_AfterProgressStateEvent as AfterProgressStateEvent, EventTypes_d_PlaceholderToggleEvent as PlaceholderToggleEvent, EventTypes_d_LoadErrorEvent as LoadErrorEvent, EventTypes_d_PreProcessEvent as PreProcessEvent, EventTypes_d_PostProcessEvent as PostProcessEvent, EventTypes_d_PastePlainTextToggleEvent as PastePlainTextToggleEvent, EventTypes_d_PastePreProcessEvent as PastePreProcessEvent, EventTypes_d_PastePostProcessEvent as PastePostProcessEvent, EventTypes_d_NewTableRowEvent as NewTableRowEvent, EventTypes_d_NewTableCellEvent as NewTableCellEvent, EventTypes_d_TableEventData as TableEventData, EventTypes_d_TableModifiedEvent as TableModifiedEvent, EventTypes_d_BeforeOpenNotificationEvent as BeforeOpenNotificationEvent, EventTypes_d_OpenNotificationEvent as OpenNotificationEvent, EventTypes_d_EditorEventMap as EditorEventMap, EventTypes_d_EditorManagerEventMap as EditorManagerEventMap, };\n}\ntype Format_d_Formats = Formats;\ntype Format_d_Format = Format;\ntype Format_d_ApplyFormat = ApplyFormat;\ntype Format_d_BlockFormat = BlockFormat;\ntype Format_d_InlineFormat = InlineFormat;\ntype Format_d_SelectorFormat = SelectorFormat;\ntype Format_d_RemoveFormat = RemoveFormat;\ntype Format_d_RemoveBlockFormat = RemoveBlockFormat;\ntype Format_d_RemoveInlineFormat = RemoveInlineFormat;\ntype Format_d_RemoveSelectorFormat = RemoveSelectorFormat;\ndeclare namespace Format_d {\n    export { Format_d_Formats as Formats, Format_d_Format as Format, Format_d_ApplyFormat as ApplyFormat, Format_d_BlockFormat as BlockFormat, Format_d_InlineFormat as InlineFormat, Format_d_SelectorFormat as SelectorFormat, Format_d_RemoveFormat as RemoveFormat, Format_d_RemoveBlockFormat as RemoveBlockFormat, Format_d_RemoveInlineFormat as RemoveInlineFormat, Format_d_RemoveSelectorFormat as RemoveSelectorFormat, };\n}\ndeclare type StyleFormat = BlockStyleFormat | InlineStyleFormat | SelectorStyleFormat;\ndeclare type AllowedFormat = Separator | FormatReference | StyleFormat | NestedFormatting;\ninterface Separator {\n    title: string;\n}\ninterface FormatReference {\n    title: string;\n    format: string;\n    icon?: string;\n}\ninterface NestedFormatting {\n    title: string;\n    items: Array<FormatReference | StyleFormat>;\n}\ninterface CommonStyleFormat {\n    name?: string;\n    title: string;\n    icon?: string;\n}\ninterface BlockStyleFormat extends BlockFormat, CommonStyleFormat {\n}\ninterface InlineStyleFormat extends InlineFormat, CommonStyleFormat {\n}\ninterface SelectorStyleFormat extends SelectorFormat, CommonStyleFormat {\n}\ndeclare type EntityEncoding = 'named' | 'numeric' | 'raw' | 'named,numeric' | 'named+numeric' | 'numeric,named' | 'numeric+named';\ninterface ContentLanguage {\n    readonly title: string;\n    readonly code: string;\n    readonly customCode?: string;\n}\ndeclare type ThemeInitFunc = (editor: Editor, elm: HTMLElement) => {\n    editorContainer: HTMLElement;\n    iframeContainer: HTMLElement;\n    height?: number;\n    iframeHeight?: number;\n    api?: EditorUiApi;\n};\ndeclare type SetupCallback = (editor: Editor) => void;\ndeclare type FilePickerCallback = (callback: (value: string, meta?: Record<string, any>) => void, value: string, meta: Record<string, any>) => void;\ndeclare type FilePickerValidationStatus = 'valid' | 'unknown' | 'invalid' | 'none';\ndeclare type FilePickerValidationCallback = (info: {\n    type: string;\n    url: string;\n}, callback: (validation: {\n    status: FilePickerValidationStatus;\n    message: string;\n}) => void) => void;\ndeclare type PastePreProcessFn = (editor: Editor, args: PastePreProcessEvent) => void;\ndeclare type PastePostProcessFn = (editor: Editor, args: PastePostProcessEvent) => void;\ndeclare type URLConverter = (url: string, name: string, elm?: string | Element) => string;\ndeclare type URLConverterCallback = (url: string, node: Node | string | undefined, on_save: boolean, name: string) => string;\ninterface ToolbarGroup {\n    name?: string;\n    items: string[];\n}\ndeclare type ToolbarMode = 'floating' | 'sliding' | 'scrolling' | 'wrap';\ndeclare type ToolbarLocation = 'top' | 'bottom' | 'auto';\ninterface BaseEditorOptions {\n    a11y_advanced_options?: boolean;\n    add_form_submit_trigger?: boolean;\n    add_unload_trigger?: boolean;\n    allow_conditional_comments?: boolean;\n    allow_html_data_urls?: boolean;\n    allow_html_in_named_anchor?: boolean;\n    allow_script_urls?: boolean;\n    allow_svg_data_urls?: boolean;\n    allow_unsafe_link_target?: boolean;\n    anchor_bottom?: false | string;\n    anchor_top?: false | string;\n    auto_focus?: string | true;\n    automatic_uploads?: boolean;\n    base_url?: string;\n    block_formats?: string;\n    block_unsupported_drop?: boolean;\n    body_id?: string;\n    body_class?: string;\n    br_in_pre?: boolean;\n    br_newline_selector?: string;\n    browser_spellcheck?: boolean;\n    branding?: boolean;\n    cache_suffix?: string;\n    color_cols?: number;\n    color_cols_foreground?: number;\n    color_cols_background?: number;\n    color_map?: string[];\n    color_map_foreground?: string[];\n    color_map_background?: string[];\n    color_default_foreground?: string;\n    color_default_background?: string;\n    content_css?: boolean | string | string[];\n    content_css_cors?: boolean;\n    content_security_policy?: string;\n    content_style?: string;\n    content_langs?: ContentLanguage[];\n    contextmenu?: string | string[] | false;\n    contextmenu_never_use_native?: boolean;\n    convert_fonts_to_spans?: boolean;\n    convert_urls?: boolean;\n    custom_colors?: boolean;\n    custom_elements?: string;\n    custom_ui_selector?: string;\n    custom_undo_redo_levels?: number;\n    deprecation_warnings?: boolean;\n    directionality?: 'ltr' | 'rtl';\n    doctype?: string;\n    document_base_url?: string;\n    draggable_modal?: boolean;\n    editable_class?: string;\n    element_format?: 'xhtml' | 'html';\n    elementpath?: boolean;\n    encoding?: string;\n    end_container_on_empty_block?: boolean | string;\n    entities?: string;\n    entity_encoding?: EntityEncoding;\n    extended_valid_elements?: string;\n    event_root?: string;\n    file_picker_callback?: FilePickerCallback;\n    file_picker_types?: string;\n    file_picker_validator_handler?: FilePickerValidationCallback;\n    fix_list_elements?: boolean;\n    fixed_toolbar_container?: string;\n    fixed_toolbar_container_target?: HTMLElement;\n    font_css?: string | string[];\n    font_family_formats?: string;\n    font_size_classes?: string;\n    font_size_legacy_values?: string;\n    font_size_style_values?: string;\n    font_size_formats?: string;\n    forced_root_block?: string;\n    forced_root_block_attrs?: Record<string, string>;\n    formats?: Formats;\n    format_noneditable_selector?: string;\n    height?: number | string;\n    hidden_input?: boolean;\n    icons?: string;\n    icons_url?: string;\n    id?: string;\n    iframe_aria_text?: string;\n    iframe_attrs?: Record<string, string>;\n    images_file_types?: string;\n    images_replace_blob_uris?: boolean;\n    images_reuse_filename?: boolean;\n    images_upload_base_path?: string;\n    images_upload_credentials?: boolean;\n    images_upload_handler?: UploadHandler;\n    images_upload_url?: string;\n    indent?: boolean;\n    indent_after?: string;\n    indent_before?: string;\n    indent_use_margin?: boolean;\n    indentation?: string;\n    init_instance_callback?: SetupCallback;\n    inline?: boolean;\n    inline_boundaries?: boolean;\n    inline_boundaries_selector?: string;\n    inline_styles?: boolean;\n    invalid_elements?: string;\n    invalid_styles?: string | Record<string, string>;\n    keep_styles?: boolean;\n    language?: string;\n    language_load?: boolean;\n    language_url?: string;\n    line_height_formats?: string;\n    max_height?: number;\n    max_width?: number;\n    menu?: Record<string, {\n        title: string;\n        items: string;\n    }>;\n    menubar?: boolean | string;\n    min_height?: number;\n    min_width?: number;\n    model?: string;\n    model_url?: string;\n    newline_behavior?: 'block' | 'linebreak' | 'invert' | 'default';\n    no_newline_selector?: string;\n    noneditable_class?: string;\n    noneditable_regexp?: RegExp | RegExp[];\n    nowrap?: boolean;\n    object_resizing?: boolean | string;\n    paste_as_text?: boolean;\n    paste_block_drop?: boolean;\n    paste_data_images?: boolean;\n    paste_merge_formats?: boolean;\n    paste_postprocess?: PastePostProcessFn;\n    paste_preprocess?: PastePreProcessFn;\n    paste_remove_styles_if_webkit?: boolean;\n    paste_tab_spaces?: number;\n    paste_webkit_styles?: string;\n    placeholder?: string;\n    preserve_cdata?: boolean;\n    preview_styles?: false | string;\n    promotion?: boolean;\n    protect?: RegExp[];\n    readonly?: boolean;\n    referrer_policy?: ReferrerPolicy;\n    relative_urls?: boolean;\n    remove_script_host?: boolean;\n    remove_trailing_brs?: boolean;\n    removed_menuitems?: string;\n    resize?: boolean | 'both';\n    resize_img_proportional?: boolean;\n    root_name?: string;\n    schema?: SchemaType;\n    selector?: string;\n    setup?: SetupCallback;\n    sidebar_show?: string;\n    skin?: boolean | string;\n    skin_url?: string;\n    smart_paste?: boolean;\n    statusbar?: boolean;\n    style_formats?: AllowedFormat[];\n    style_formats_autohide?: boolean;\n    style_formats_merge?: boolean;\n    submit_patch?: boolean;\n    suffix?: string;\n    table_tab_navigation?: boolean;\n    target?: HTMLElement;\n    text_patterns?: RawPattern[] | false;\n    text_patterns_lookup?: RawDynamicPatternsLookup;\n    theme?: string | ThemeInitFunc | false;\n    theme_url?: string;\n    toolbar?: boolean | string | string[] | Array<ToolbarGroup>;\n    toolbar1?: string;\n    toolbar2?: string;\n    toolbar3?: string;\n    toolbar4?: string;\n    toolbar5?: string;\n    toolbar6?: string;\n    toolbar7?: string;\n    toolbar8?: string;\n    toolbar9?: string;\n    toolbar_groups?: Record<string, GroupToolbarButtonSpec>;\n    toolbar_location?: ToolbarLocation;\n    toolbar_mode?: ToolbarMode;\n    toolbar_sticky?: boolean;\n    toolbar_sticky_offset?: number;\n    typeahead_urls?: boolean;\n    url_converter?: URLConverter;\n    url_converter_scope?: any;\n    urlconverter_callback?: URLConverterCallback;\n    valid_children?: string;\n    valid_classes?: string | Record<string, string>;\n    valid_elements?: string;\n    valid_styles?: string | Record<string, string>;\n    verify_html?: boolean;\n    visual?: boolean;\n    visual_anchor_class?: string;\n    visual_table_class?: string;\n    width?: number | string;\n    disable_nodechange?: boolean;\n    forced_plugins?: string | string[];\n    plugin_base_urls?: Record<string, string>;\n    service_message?: string;\n    [key: string]: any;\n}\ninterface RawEditorOptions extends BaseEditorOptions {\n    external_plugins?: Record<string, string>;\n    mobile?: RawEditorOptions;\n    plugins?: string | string[];\n}\ninterface NormalizedEditorOptions extends BaseEditorOptions {\n    external_plugins: Record<string, string>;\n    forced_plugins: string[];\n    plugins: string[];\n}\ninterface EditorOptions extends NormalizedEditorOptions {\n    a11y_advanced_options: boolean;\n    allow_unsafe_link_target: boolean;\n    anchor_bottom: string;\n    anchor_top: string;\n    automatic_uploads: boolean;\n    block_formats: string;\n    body_class: string;\n    body_id: string;\n    br_newline_selector: string;\n    color_map: string[];\n    color_cols: number;\n    color_cols_foreground: number;\n    color_cols_background: number;\n    color_default_background: string;\n    color_default_foreground: string;\n    content_css: string[];\n    contextmenu: string[];\n    custom_colors: boolean;\n    document_base_url: string;\n    draggable_modal: boolean;\n    editable_class: string;\n    font_css: string[];\n    font_family_formats: string;\n    font_size_classes: string;\n    font_size_formats: string;\n    font_size_legacy_values: string;\n    font_size_style_values: string;\n    forced_root_block: string;\n    forced_root_block_attrs: Record<string, string>;\n    format_noneditable_selector: string;\n    height: number | string;\n    iframe_attrs: Record<string, string>;\n    images_file_types: string;\n    images_upload_base_path: string;\n    images_upload_credentials: boolean;\n    images_upload_url: string;\n    indent_use_margin: boolean;\n    indentation: string;\n    inline: boolean;\n    inline_boundaries_selector: string;\n    language: string;\n    language_load: boolean;\n    language_url: string;\n    line_height_formats: string;\n    menu: Record<string, {\n        title: string;\n        items: string;\n    }>;\n    menubar: boolean | string;\n    model: string;\n    no_newline_selector: string;\n    noneditable_class: string;\n    noneditable_regexp: RegExp[];\n    object_resizing: string;\n    paste_as_text: boolean;\n    preview_styles: string;\n    promotion: boolean;\n    readonly: boolean;\n    removed_menuitems: string;\n    toolbar: boolean | string | string[] | Array<ToolbarGroup>;\n    toolbar_groups: Record<string, GroupToolbarButtonSpec>;\n    toolbar_location: ToolbarLocation;\n    toolbar_mode: ToolbarMode;\n    toolbar_persist: boolean;\n    toolbar_sticky: boolean;\n    toolbar_sticky_offset: number;\n    text_patterns: Pattern[];\n    text_patterns_lookup: DynamicPatternsLookup;\n    visual: boolean;\n    visual_anchor_class: string;\n    visual_table_class: string;\n    width: number | string;\n}\ndeclare type StyleMap = Record<string, string | number>;\ninterface StylesSettings {\n    allow_script_urls?: boolean;\n    allow_svg_data_urls?: boolean;\n    url_converter?: URLConverter;\n    url_converter_scope?: any;\n}\ninterface Styles {\n    parse: (css: string | undefined) => Record<string, string>;\n    serialize: (styles: StyleMap, elementName?: string) => string;\n}\ndeclare type EventUtilsCallback<T> = (event: EventUtilsEvent<T>) => void | boolean;\ndeclare type EventUtilsEvent<T> = NormalizedEvent<T> & {\n    metaKey: boolean;\n};\ninterface Callback$1<T> {\n    func: EventUtilsCallback<T>;\n    scope: any;\n}\ninterface CallbackList<T> extends Array<Callback$1<T>> {\n    fakeName: string | false;\n    capture: boolean;\n    nativeHandler: EventListener;\n}\ninterface EventUtilsConstructor {\n    readonly prototype: EventUtils;\n    new (): EventUtils;\n    Event: EventUtils;\n}\ndeclare class EventUtils {\n    static Event: EventUtils;\n    domLoaded: boolean;\n    events: Record<number, Record<string, CallbackList<any>>>;\n    private readonly expando;\n    private hasFocusIn;\n    private count;\n    constructor();\n    bind<K extends keyof HTMLElementEventMap>(target: any, name: K, callback: EventUtilsCallback<HTMLElementEventMap[K]>, scope?: any): EventUtilsCallback<HTMLElementEventMap[K]>;\n    bind<T = any>(target: any, names: string, callback: EventUtilsCallback<T>, scope?: any): EventUtilsCallback<T>;\n    unbind<K extends keyof HTMLElementEventMap>(target: any, name: K, callback?: EventUtilsCallback<HTMLElementEventMap[K]>): this;\n    unbind<T = any>(target: any, names: string, callback?: EventUtilsCallback<T>): this;\n    unbind(target: any): this;\n    fire(target: any, name: string, args?: {}): this;\n    dispatch(target: any, name: string, args?: {}): this;\n    clean(target: any): this;\n    destroy(): void;\n    cancel<T>(e: EventUtilsEvent<T>): boolean;\n    private executeHandlers;\n}\ninterface SetAttribEvent {\n    attrElm: HTMLElement;\n    attrName: string;\n    attrValue: string | boolean | number | null;\n}\ninterface DOMUtilsSettings {\n    schema: Schema;\n    url_converter: URLConverter;\n    url_converter_scope: any;\n    ownEvents: boolean;\n    keep_values: boolean;\n    update_styles: boolean;\n    root_element: HTMLElement | null;\n    collect: boolean;\n    onSetAttrib: (event: SetAttribEvent) => void;\n    contentCssCors: boolean;\n    referrerPolicy: ReferrerPolicy;\n}\ndeclare type Target = Node | Window;\ndeclare type RunArguments<T extends Node = Node> = string | T | Array<string | T> | null;\ndeclare type BoundEvent = [\n    Target,\n    string,\n    EventUtilsCallback<any>,\n    any\n];\ndeclare type Callback<K extends string> = EventUtilsCallback<MappedEvent<HTMLElementEventMap, K>>;\ndeclare type RunResult<T, R> = T extends Array<any> ? R[] : false | R;\ninterface DOMUtils {\n    doc: Document;\n    settings: Partial<DOMUtilsSettings>;\n    win: Window;\n    files: Record<string, boolean>;\n    stdMode: boolean;\n    boxModel: boolean;\n    styleSheetLoader: StyleSheetLoader;\n    boundEvents: BoundEvent[];\n    styles: Styles;\n    schema: Schema;\n    events: EventUtils;\n    root: Node | null;\n    isBlock: {\n        (node: Node | null): node is HTMLElement;\n        (node: string): boolean;\n    };\n    clone: (node: Node, deep: boolean) => Node;\n    getRoot: () => HTMLElement;\n    getViewPort: (argWin?: Window) => GeomRect;\n    getRect: (elm: string | HTMLElement) => GeomRect;\n    getSize: (elm: string | HTMLElement) => {\n        w: number;\n        h: number;\n    };\n    getParent: {\n        <K extends keyof HTMLElementTagNameMap>(node: string | Node | null, selector: K, root?: Node): HTMLElementTagNameMap[K] | null;\n        <T extends Element>(node: string | Node | null, selector: string | ((node: Node) => node is T), root?: Node): T | null;\n        (node: string | Node | null, selector?: string | ((node: Node) => boolean | void), root?: Node): Node | null;\n    };\n    getParents: {\n        <K extends keyof HTMLElementTagNameMap>(elm: string | HTMLElementTagNameMap[K] | null, selector: K, root?: Node, collect?: boolean): Array<HTMLElementTagNameMap[K]>;\n        <T extends Element>(node: string | Node | null, selector: string | ((node: Node) => node is T), root?: Node, collect?: boolean): T[];\n        (elm: string | Node | null, selector?: string | ((node: Node) => boolean | void), root?: Node, collect?: boolean): Node[];\n    };\n    get: {\n        <T extends Node>(elm: T): T;\n        (elm: string): HTMLElement | null;\n    };\n    getNext: (node: Node | null, selector: string | ((node: Node) => boolean)) => Node | null;\n    getPrev: (node: Node | null, selector: string | ((node: Node) => boolean)) => Node | null;\n    select: {\n        <K extends keyof HTMLElementTagNameMap>(selector: K, scope?: string | Node): Array<HTMLElementTagNameMap[K]>;\n        <T extends HTMLElement = HTMLElement>(selector: string, scope?: string | Node): T[];\n    };\n    is: {\n        <T extends Element>(elm: Node | Node[] | null, selector: string): elm is T;\n        (elm: Node | Node[] | null, selector: string): boolean;\n    };\n    add: (parentElm: RunArguments, name: string | Element, attrs?: Record<string, string | boolean | number | null>, html?: string | Node | null, create?: boolean) => HTMLElement;\n    create: {\n        <K extends keyof HTMLElementTagNameMap>(name: K, attrs?: Record<string, string | boolean | number | null>, html?: string | Node | null): HTMLElementTagNameMap[K];\n        (name: string, attrs?: Record<string, string | boolean | number | null>, html?: string | Node | null): HTMLElement;\n    };\n    createHTML: (name: string, attrs?: Record<string, string | null>, html?: string) => string;\n    createFragment: (html?: string) => DocumentFragment;\n    remove: {\n        <T extends Node>(node: T | T[], keepChildren?: boolean): typeof node extends Array<any> ? T[] : T;\n        <T extends Node>(node: string, keepChildren?: boolean): T | false;\n    };\n    getStyle: {\n        (elm: Element, name: string, computed: true): string;\n        (elm: string | Element | null, name: string, computed?: boolean): string | undefined;\n    };\n    setStyle: (elm: string | Element | Element[], name: string, value: string | number | null) => void;\n    setStyles: (elm: string | Element | Element[], stylesArg: StyleMap) => void;\n    removeAllAttribs: (e: RunArguments<Element>) => void;\n    setAttrib: (elm: RunArguments<Element>, name: string, value: string | boolean | number | null) => void;\n    setAttribs: (elm: RunArguments<Element>, attrs: Record<string, string | boolean | number | null>) => void;\n    getAttrib: (elm: string | Element | null, name: string, defaultVal?: string) => string;\n    getAttribs: (elm: string | Element) => NamedNodeMap | Attr[];\n    getPos: (elm: string | Element, rootElm?: Node) => {\n        x: number;\n        y: number;\n    };\n    parseStyle: (cssText: string) => Record<string, string>;\n    serializeStyle: (stylesArg: StyleMap, name?: string) => string;\n    addStyle: (cssText: string) => void;\n    loadCSS: (url: string) => void;\n    hasClass: (elm: string | Element, cls: string) => boolean;\n    addClass: (elm: RunArguments<Element>, cls: string) => void;\n    removeClass: (elm: RunArguments<Element>, cls: string) => void;\n    toggleClass: (elm: RunArguments<Element>, cls: string, state?: boolean) => void;\n    show: (elm: string | Node | Node[]) => void;\n    hide: (elm: string | Node | Node[]) => void;\n    isHidden: (elm: string | Node) => boolean;\n    uniqueId: (prefix?: string) => string;\n    setHTML: (elm: RunArguments<Element>, html: string) => void;\n    getOuterHTML: (elm: string | Node) => string;\n    setOuterHTML: (elm: string | Node | Node[], html: string) => void;\n    decode: (text: string) => string;\n    encode: (text: string) => string;\n    insertAfter: {\n        <T extends Node>(node: T | T[], reference: string | Node): T;\n        <T extends Node>(node: RunArguments<T>, reference: string | Node): RunResult<typeof node, T>;\n    };\n    replace: {\n        <T extends Node>(newElm: Node, oldElm: T | T[], keepChildren?: boolean): T;\n        <T extends Node>(newElm: Node, oldElm: RunArguments<T>, keepChildren?: boolean): false | T;\n    };\n    rename: {\n        <K extends keyof HTMLElementTagNameMap>(elm: Element, name: K): HTMLElementTagNameMap[K];\n        (elm: Element, name: string): Element;\n    };\n    findCommonAncestor: (a: Node, b: Node) => Node | null;\n    run<R, T extends Node>(this: DOMUtils, elm: T | T[], func: (node: T) => R, scope?: any): typeof elm extends Array<any> ? R[] : R;\n    run<R, T extends Node>(this: DOMUtils, elm: RunArguments<T>, func: (node: T) => R, scope?: any): RunResult<typeof elm, R>;\n    isEmpty: (node: Node, elements?: Record<string, any>) => boolean;\n    createRng: () => Range;\n    nodeIndex: (node: Node, normalized?: boolean) => number;\n    split: {\n        <T extends Node>(parentElm: Node, splitElm: Node, replacementElm: T): T | undefined;\n        <T extends Node>(parentElm: Node, splitElm: T): T | undefined;\n    };\n    bind: {\n        <K extends string>(target: Target, name: K, func: Callback<K>, scope?: any): Callback<K>;\n        <K extends string>(target: Target[], name: K, func: Callback<K>, scope?: any): Callback<K>[];\n    };\n    unbind: {\n        <K extends string>(target: Target, name?: K, func?: EventUtilsCallback<MappedEvent<HTMLElementEventMap, K>>): EventUtils;\n        <K extends string>(target: Target[], name?: K, func?: EventUtilsCallback<MappedEvent<HTMLElementEventMap, K>>): EventUtils[];\n    };\n    fire: (target: Node | Window, name: string, evt?: {}) => EventUtils;\n    dispatch: (target: Node | Window, name: string, evt?: {}) => EventUtils;\n    getContentEditable: (node: Node) => string | null;\n    getContentEditableParent: (node: Node) => string | null;\n    destroy: () => void;\n    isChildOf: (node: Node, parent: Node) => boolean;\n    dumpRng: (r: Range) => string;\n}\ninterface ClientRect {\n    left: number;\n    top: number;\n    bottom: number;\n    right: number;\n    width: number;\n    height: number;\n}\ninterface BookmarkManager {\n    getBookmark: (type?: number, normalized?: boolean) => Bookmark;\n    moveToBookmark: (bookmark: Bookmark) => void;\n}\ninterface ControlSelection {\n    isResizable: (elm: Element) => boolean;\n    showResizeRect: (elm: HTMLElement) => void;\n    hideResizeRect: () => void;\n    updateResizeRect: (evt: EditorEvent<any>) => void;\n    destroy: () => void;\n}\ninterface WriterSettings {\n    element_format?: 'xhtml' | 'html';\n    entities?: string;\n    entity_encoding?: EntityEncoding;\n    indent?: boolean;\n    indent_after?: string;\n    indent_before?: string;\n}\ndeclare type Attributes = Array<{\n    name: string;\n    value: string;\n}>;\ninterface Writer {\n    cdata: (text: string) => void;\n    comment: (text: string) => void;\n    doctype: (text: string) => void;\n    end: (name: string) => void;\n    getContent: () => string;\n    pi: (name: string, text?: string) => void;\n    reset: () => void;\n    start: (name: string, attrs?: Attributes | null, empty?: boolean) => void;\n    text: (text: string, raw?: boolean) => void;\n}\ninterface HtmlSerializerSettings extends WriterSettings {\n    inner?: boolean;\n    validate?: boolean;\n}\ninterface HtmlSerializer {\n    serialize: (node: AstNode) => string;\n}\ninterface DomSerializerSettings extends DomParserSettings, WriterSettings, SchemaSettings, HtmlSerializerSettings {\n    url_converter?: URLConverter;\n    url_converter_scope?: {};\n}\ninterface DomSerializerImpl {\n    schema: Schema;\n    addNodeFilter: (name: string, callback: ParserFilterCallback) => void;\n    addAttributeFilter: (name: string, callback: ParserFilterCallback) => void;\n    getNodeFilters: () => ParserFilter[];\n    getAttributeFilters: () => ParserFilter[];\n    removeNodeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    removeAttributeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    serialize: {\n        (node: Element, parserArgs: {\n            format: 'tree';\n        } & ParserArgs): AstNode;\n        (node: Element, parserArgs?: ParserArgs): string;\n    };\n    addRules: (rules: string) => void;\n    setRules: (rules: string) => void;\n    addTempAttr: (name: string) => void;\n    getTempAttrs: () => string[];\n}\ninterface DomSerializer extends DomSerializerImpl {\n}\ninterface EditorSelection {\n    bookmarkManager: BookmarkManager;\n    controlSelection: ControlSelection;\n    dom: DOMUtils;\n    win: Window;\n    serializer: DomSerializer;\n    editor: Editor;\n    collapse: (toStart?: boolean) => void;\n    setCursorLocation: {\n        (node: Node, offset: number): void;\n        (): void;\n    };\n    getContent: {\n        (args: {\n            format: 'tree';\n        } & Partial<GetSelectionContentArgs>): AstNode;\n        (args?: Partial<GetSelectionContentArgs>): string;\n    };\n    setContent: (content: string, args?: Partial<SetSelectionContentArgs>) => void;\n    getBookmark: (type?: number, normalized?: boolean) => Bookmark;\n    moveToBookmark: (bookmark: Bookmark) => void;\n    select: (node: Node, content?: boolean) => Node;\n    isCollapsed: () => boolean;\n    isForward: () => boolean;\n    setNode: (elm: Element) => Element;\n    getNode: () => HTMLElement;\n    getSel: () => Selection | null;\n    setRng: (rng: Range, forward?: boolean) => void;\n    getRng: () => Range;\n    getStart: (real?: boolean) => Element;\n    getEnd: (real?: boolean) => Element;\n    getSelectedBlocks: (startElm?: Element, endElm?: Element) => Element[];\n    normalize: () => Range;\n    selectorChanged: (selector: string, callback: (active: boolean, args: {\n        node: Node;\n        selector: String;\n        parents: Node[];\n    }) => void) => EditorSelection;\n    selectorChangedWithUnbind: (selector: string, callback: (active: boolean, args: {\n        node: Node;\n        selector: String;\n        parents: Node[];\n    }) => void) => {\n        unbind: () => void;\n    };\n    getScrollContainer: () => HTMLElement | undefined;\n    scrollIntoView: (elm?: HTMLElement, alignToTop?: boolean) => void;\n    placeCaretAt: (clientX: number, clientY: number) => void;\n    getBoundingClientRect: () => ClientRect | DOMRect;\n    destroy: () => void;\n    expand: (options?: {\n        type: 'word';\n    }) => void;\n}\ndeclare type EditorCommandCallback<S> = (this: S, ui: boolean, value: any) => void;\ndeclare type EditorCommandsCallback = (command: string, ui: boolean, value?: any) => void;\ninterface Commands {\n    state: Record<string, (command: string) => boolean>;\n    exec: Record<string, EditorCommandsCallback>;\n    value: Record<string, (command: string) => string>;\n}\ninterface ExecCommandArgs {\n    skip_focus?: boolean;\n}\ninterface EditorCommandsConstructor {\n    readonly prototype: EditorCommands;\n    new (editor: Editor): EditorCommands;\n}\ndeclare class EditorCommands {\n    private readonly editor;\n    private commands;\n    constructor(editor: Editor);\n    execCommand(command: string, ui?: boolean, value?: any, args?: ExecCommandArgs): boolean;\n    queryCommandState(command: string): boolean;\n    queryCommandValue(command: string): string;\n    addCommands<K extends keyof Commands>(commandList: Commands[K], type: K): void;\n    addCommands(commandList: Record<string, EditorCommandsCallback>): void;\n    addCommand<S>(command: string, callback: EditorCommandCallback<S>, scope: S): void;\n    addCommand(command: string, callback: EditorCommandCallback<Editor>): void;\n    queryCommandSupported(command: string): boolean;\n    addQueryStateHandler<S>(command: string, callback: (this: S) => boolean, scope: S): void;\n    addQueryStateHandler(command: string, callback: (this: Editor) => boolean): void;\n    addQueryValueHandler<S>(command: string, callback: (this: S) => string, scope: S): void;\n    addQueryValueHandler(command: string, callback: (this: Editor) => string): void;\n}\ninterface RawString {\n    raw: string;\n}\ndeclare type Primitive = string | number | boolean | Record<string | number, any> | Function;\ndeclare type TokenisedString = [\n    string,\n    ...Primitive[]\n];\ndeclare type Untranslated = Primitive | TokenisedString | RawString | null | undefined;\ndeclare type TranslatedString = string;\ninterface I18n {\n    getData: () => Record<string, Record<string, string>>;\n    setCode: (newCode: string) => void;\n    getCode: () => string;\n    add: (code: string, items: Record<string, string>) => void;\n    translate: (text: Untranslated) => TranslatedString;\n    isRtl: () => boolean;\n    hasCode: (code: string) => boolean;\n}\ninterface Observable<T extends {}> {\n    fire<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U, bubble?: boolean): EditorEvent<U>;\n    dispatch<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U, bubble?: boolean): EditorEvent<U>;\n    on<K extends string>(name: K, callback: (event: EditorEvent<MappedEvent<T, K>>) => void, prepend?: boolean): EventDispatcher<T>;\n    off<K extends string>(name?: K, callback?: (event: EditorEvent<MappedEvent<T, K>>) => void): EventDispatcher<T>;\n    once<K extends string>(name: K, callback: (event: EditorEvent<MappedEvent<T, K>>) => void): EventDispatcher<T>;\n    hasEventListeners(name: string): boolean;\n}\ninterface URISettings {\n    base_uri?: URI;\n}\ninterface URIConstructor {\n    readonly prototype: URI;\n    new (url: string, settings?: URISettings): URI;\n    getDocumentBaseUrl: (loc: {\n        protocol: string;\n        host?: string;\n        href?: string;\n        pathname?: string;\n    }) => string;\n    parseDataUri: (uri: string) => {\n        type: string;\n        data: string;\n    };\n}\ninterface SafeUriOptions {\n    readonly allow_html_data_urls?: boolean;\n    readonly allow_script_urls?: boolean;\n    readonly allow_svg_data_urls?: boolean;\n}\ndeclare class URI {\n    static parseDataUri(uri: string): {\n        type: string | undefined;\n        data: string;\n    };\n    static isDomSafe(uri: string, context?: string, options?: SafeUriOptions): boolean;\n    static getDocumentBaseUrl(loc: {\n        protocol: string;\n        host?: string;\n        href?: string;\n        pathname?: string;\n    }): string;\n    source: string;\n    protocol: string | undefined;\n    authority: string | undefined;\n    userInfo: string | undefined;\n    user: string | undefined;\n    password: string | undefined;\n    host: string | undefined;\n    port: string | undefined;\n    relative: string | undefined;\n    path: string;\n    directory: string;\n    file: string | undefined;\n    query: string | undefined;\n    anchor: string | undefined;\n    settings: URISettings;\n    constructor(url: string, settings?: URISettings);\n    setPath(path: string): void;\n    toRelative(uri: string): string;\n    toAbsolute(uri: string, noHost?: boolean): string;\n    isSameOrigin(uri: URI): boolean;\n    toRelPath(base: string, path: string): string;\n    toAbsPath(base: string, path: string): string;\n    getURI(noProtoHost?: boolean): string;\n}\ninterface EditorManager extends Observable<EditorManagerEventMap> {\n    defaultOptions: RawEditorOptions;\n    majorVersion: string;\n    minorVersion: string;\n    releaseDate: string;\n    activeEditor: Editor | null;\n    focusedEditor: Editor | null;\n    baseURI: URI;\n    baseURL: string;\n    documentBaseURL: string;\n    i18n: I18n;\n    suffix: string;\n    add(this: EditorManager, editor: Editor): Editor;\n    addI18n: (code: string, item: Record<string, string>) => void;\n    createEditor(this: EditorManager, id: string, options: RawEditorOptions): Editor;\n    execCommand(this: EditorManager, cmd: string, ui: boolean, value: any): boolean;\n    get(this: EditorManager): Editor[];\n    get(this: EditorManager, id: number | string): Editor | null;\n    init(this: EditorManager, options: RawEditorOptions): Promise<Editor[]>;\n    overrideDefaults(this: EditorManager, defaultOptions: Partial<RawEditorOptions>): void;\n    remove(this: EditorManager): void;\n    remove(this: EditorManager, selector: string): void;\n    remove(this: EditorManager, editor: Editor): Editor | null;\n    setActive(this: EditorManager, editor: Editor): void;\n    setup(this: EditorManager): void;\n    translate: (text: Untranslated) => TranslatedString;\n    triggerSave: () => void;\n    _setBaseUrl(this: EditorManager, baseUrl: string): void;\n}\ninterface EditorObservable extends Observable<EditorEventMap> {\n    bindPendingEventDelegates(this: Editor): void;\n    toggleNativeEvent(this: Editor, name: string, state: boolean): void;\n    unbindAllNativeEvents(this: Editor): void;\n}\ninterface ProcessorSuccess<T> {\n    valid: true;\n    value: T;\n}\ninterface ProcessorError {\n    valid: false;\n    message: string;\n}\ndeclare type SimpleProcessor = (value: unknown) => boolean;\ndeclare type Processor<T> = (value: unknown) => ProcessorSuccess<T> | ProcessorError;\ninterface BuiltInOptionTypeMap {\n    'string': string;\n    'number': number;\n    'boolean': boolean;\n    'array': any[];\n    'function': Function;\n    'object': any;\n    'string[]': string[];\n    'object[]': any[];\n    'regexp': RegExp;\n}\ndeclare type BuiltInOptionType = keyof BuiltInOptionTypeMap;\ninterface BaseOptionSpec {\n    immutable?: boolean;\n    deprecated?: boolean;\n    docsUrl?: string;\n}\ninterface BuiltInOptionSpec<K extends BuiltInOptionType> extends BaseOptionSpec {\n    processor: K;\n    default?: BuiltInOptionTypeMap[K];\n}\ninterface SimpleOptionSpec<T> extends BaseOptionSpec {\n    processor: SimpleProcessor;\n    default?: T;\n}\ninterface OptionSpec<T, U> extends BaseOptionSpec {\n    processor: Processor<U>;\n    default?: T;\n}\ninterface Options {\n    register: {\n        <K extends BuiltInOptionType>(name: string, spec: BuiltInOptionSpec<K>): void;\n        <K extends keyof NormalizedEditorOptions>(name: K, spec: OptionSpec<NormalizedEditorOptions[K], EditorOptions[K]> | SimpleOptionSpec<NormalizedEditorOptions[K]>): void;\n        <T, U>(name: string, spec: OptionSpec<T, U>): void;\n        <T>(name: string, spec: SimpleOptionSpec<T>): void;\n    };\n    isRegistered: (name: string) => boolean;\n    get: {\n        <K extends keyof EditorOptions>(name: K): EditorOptions[K];\n        <T>(name: string): T | undefined;\n    };\n    set: <K extends string, T>(name: K, value: K extends keyof NormalizedEditorOptions ? NormalizedEditorOptions[K] : T) => boolean;\n    unset: (name: string) => boolean;\n    isSet: (name: string) => boolean;\n}\ninterface UploadResult$1 {\n    element: HTMLImageElement;\n    status: boolean;\n    blobInfo: BlobInfo;\n    uploadUri: string;\n    removed: boolean;\n}\ninterface EditorUpload {\n    blobCache: BlobCache;\n    addFilter: (filter: (img: HTMLImageElement) => boolean) => void;\n    uploadImages: () => Promise<UploadResult$1[]>;\n    uploadImagesAuto: () => Promise<UploadResult$1[]>;\n    scanForImages: () => Promise<BlobInfoImagePair[]>;\n    destroy: () => void;\n}\ndeclare type FormatChangeCallback = (state: boolean, data: {\n    node: Node;\n    format: string;\n    parents: Element[];\n}) => void;\ninterface FormatRegistry {\n    get: {\n        (name: string): Format[] | undefined;\n        (): Record<string, Format[]>;\n    };\n    has: (name: string) => boolean;\n    register: (name: string | Formats, format?: Format[] | Format) => void;\n    unregister: (name: string) => Formats;\n}\ninterface Formatter extends FormatRegistry {\n    apply: (name: string, vars?: FormatVars, node?: Node | RangeLikeObject | null) => void;\n    remove: (name: string, vars?: FormatVars, node?: Node | Range, similar?: boolean) => void;\n    toggle: (name: string, vars?: FormatVars, node?: Node) => void;\n    match: (name: string, vars?: FormatVars, node?: Node, similar?: boolean) => boolean;\n    closest: (names: string[]) => string | null;\n    matchAll: (names: string[], vars?: FormatVars) => string[];\n    matchNode: (node: Node | null, name: string, vars?: FormatVars, similar?: boolean) => Format | undefined;\n    canApply: (name: string) => boolean;\n    formatChanged: (names: string, callback: FormatChangeCallback, similar?: boolean, vars?: FormatVars) => {\n        unbind: () => void;\n    };\n    getCssText: (format: string | ApplyFormat) => string;\n}\ninterface EditorMode {\n    isReadOnly: () => boolean;\n    set: (mode: string) => void;\n    get: () => string;\n    register: (mode: string, api: EditorModeApi) => void;\n}\ninterface EditorModeApi {\n    activate: () => void;\n    deactivate: () => void;\n    editorReadOnly: boolean;\n}\ninterface Model {\n    readonly table: {\n        readonly getSelectedCells: () => HTMLTableCellElement[];\n        readonly clearSelectedCells: (container: Node) => void;\n    };\n}\ndeclare type ModelManager = AddOnManager<Model>;\ninterface Plugin {\n    getMetadata?: () => {\n        name: string;\n        url: string;\n    };\n    init?: (editor: Editor, url: string) => void;\n    [key: string]: any;\n}\ndeclare type PluginManager = AddOnManager<void | Plugin>;\ninterface ShortcutsConstructor {\n    readonly prototype: Shortcuts;\n    new (editor: Editor): Shortcuts;\n}\ndeclare type CommandFunc = string | [\n    string,\n    boolean,\n    any\n] | (() => void);\ndeclare class Shortcuts {\n    private readonly editor;\n    private readonly shortcuts;\n    private pendingPatterns;\n    constructor(editor: Editor);\n    add(pattern: string, desc: string | null, cmdFunc: CommandFunc, scope?: any): boolean;\n    remove(pattern: string): boolean;\n    private normalizeCommandFunc;\n    private createShortcut;\n    private hasModifier;\n    private isFunctionKey;\n    private matchShortcut;\n    private executeShortcutAction;\n}\ninterface RenderResult {\n    iframeContainer?: HTMLElement;\n    editorContainer: HTMLElement;\n    api?: Partial<EditorUiApi>;\n}\ninterface Theme {\n    ui?: any;\n    inline?: any;\n    execCommand?: (command: string, ui?: boolean, value?: any) => boolean;\n    destroy?: () => void;\n    init?: (editor: Editor, url: string) => void;\n    renderUI?: () => RenderResult;\n    getNotificationManagerImpl?: () => NotificationManagerImpl;\n    getWindowManagerImpl?: () => WindowManagerImpl;\n}\ndeclare type ThemeManager = AddOnManager<void | Theme>;\ninterface EditorConstructor {\n    readonly prototype: Editor;\n    new (id: string, options: RawEditorOptions, editorManager: EditorManager): Editor;\n}\ndeclare class Editor implements EditorObservable {\n    documentBaseUrl: string;\n    baseUri: URI;\n    id: string;\n    plugins: Record<string, Plugin>;\n    documentBaseURI: URI;\n    baseURI: URI;\n    contentCSS: string[];\n    contentStyles: string[];\n    ui: EditorUi;\n    mode: EditorMode;\n    options: Options;\n    shortcuts: Shortcuts;\n    loadedCSS: Record<string, any>;\n    editorCommands: EditorCommands;\n    suffix: string;\n    editorManager: EditorManager;\n    hidden: boolean;\n    inline: boolean;\n    hasVisual: boolean;\n    isNotDirty: boolean;\n    annotator: Annotator;\n    bodyElement: HTMLElement | undefined;\n    bookmark: any;\n    composing: boolean;\n    container: HTMLElement;\n    contentAreaContainer: HTMLElement;\n    contentDocument: Document;\n    contentWindow: Window;\n    delegates: Record<string, EventUtilsCallback<any>> | undefined;\n    destroyed: boolean;\n    dom: DOMUtils;\n    editorContainer: HTMLElement;\n    editorUpload: EditorUpload;\n    eventRoot: Element | undefined;\n    formatter: Formatter;\n    formElement: HTMLElement | undefined;\n    formEventDelegate: ((e: Event) => void) | undefined;\n    hasHiddenInput: boolean;\n    iframeElement: HTMLIFrameElement | null;\n    iframeHTML: string | undefined;\n    initialized: boolean;\n    notificationManager: NotificationManager;\n    orgDisplay: string;\n    orgVisibility: string | undefined;\n    parser: DomParser;\n    quirks: Quirks;\n    readonly: boolean;\n    removed: boolean;\n    schema: Schema;\n    selection: EditorSelection;\n    serializer: DomSerializer;\n    startContent: string;\n    targetElm: HTMLElement;\n    theme: Theme;\n    model: Model;\n    undoManager: UndoManager;\n    windowManager: WindowManager;\n    _beforeUnload: (() => void) | undefined;\n    _eventDispatcher: EventDispatcher<NativeEventMap> | undefined;\n    _nodeChangeDispatcher: NodeChange;\n    _pendingNativeEvents: string[];\n    _selectionOverrides: SelectionOverrides;\n    _skinLoaded: boolean;\n    bindPendingEventDelegates: EditorObservable['bindPendingEventDelegates'];\n    toggleNativeEvent: EditorObservable['toggleNativeEvent'];\n    unbindAllNativeEvents: EditorObservable['unbindAllNativeEvents'];\n    fire: EditorObservable['fire'];\n    dispatch: EditorObservable['dispatch'];\n    on: EditorObservable['on'];\n    off: EditorObservable['off'];\n    once: EditorObservable['once'];\n    hasEventListeners: EditorObservable['hasEventListeners'];\n    constructor(id: string, options: RawEditorOptions, editorManager: EditorManager);\n    render(): void;\n    focus(skipFocus?: boolean): void;\n    hasFocus(): boolean;\n    translate(text: Untranslated): TranslatedString;\n    getParam<K extends BuiltInOptionType>(name: string, defaultVal: BuiltInOptionTypeMap[K], type: K): BuiltInOptionTypeMap[K];\n    getParam<K extends keyof NormalizedEditorOptions>(name: K, defaultVal?: NormalizedEditorOptions[K], type?: BuiltInOptionType): NormalizedEditorOptions[K];\n    getParam<T>(name: string, defaultVal: T, type?: BuiltInOptionType): T;\n    hasPlugin(name: string, loaded?: boolean): boolean;\n    nodeChanged(args?: any): void;\n    addCommand<S>(name: string, callback: EditorCommandCallback<S>, scope: S): void;\n    addCommand(name: string, callback: EditorCommandCallback<Editor>): void;\n    addQueryStateHandler<S>(name: string, callback: (this: S) => boolean, scope?: S): void;\n    addQueryStateHandler(name: string, callback: (this: Editor) => boolean): void;\n    addQueryValueHandler<S>(name: string, callback: (this: S) => string, scope: S): void;\n    addQueryValueHandler(name: string, callback: (this: Editor) => string): void;\n    addShortcut(pattern: string, desc: string, cmdFunc: string | [\n        string,\n        boolean,\n        any\n    ] | (() => void), scope?: any): void;\n    execCommand(cmd: string, ui?: boolean, value?: any, args?: ExecCommandArgs): boolean;\n    queryCommandState(cmd: string): boolean;\n    queryCommandValue(cmd: string): string;\n    queryCommandSupported(cmd: string): boolean;\n    show(): void;\n    hide(): void;\n    isHidden(): boolean;\n    setProgressState(state: boolean, time?: number): void;\n    load(args?: Partial<SetContentArgs>): string;\n    save(args?: Partial<GetContentArgs>): string;\n    setContent(content: string, args?: Partial<SetContentArgs>): string;\n    setContent(content: AstNode, args?: Partial<SetContentArgs>): AstNode;\n    setContent(content: Content, args?: Partial<SetContentArgs>): Content;\n    getContent(args: {\n        format: 'tree';\n    } & Partial<GetContentArgs>): AstNode;\n    getContent(args?: Partial<GetContentArgs>): string;\n    insertContent(content: string, args?: any): void;\n    resetContent(initialContent?: string): void;\n    isDirty(): boolean;\n    setDirty(state: boolean): void;\n    getContainer(): HTMLElement;\n    getContentAreaContainer(): HTMLElement;\n    getElement(): HTMLElement;\n    getWin(): Window;\n    getDoc(): Document;\n    getBody(): HTMLElement;\n    convertURL(url: string, name: string, elm?: string | Element): string;\n    addVisual(elm?: HTMLElement): void;\n    remove(): void;\n    destroy(automatic?: boolean): void;\n    uploadImages(): Promise<UploadResult$1[]>;\n    _scanForImages(): Promise<BlobInfoImagePair[]>;\n}\ninterface UrlObject {\n    prefix: string;\n    resource: string;\n    suffix: string;\n}\ndeclare type WaitState = 'added' | 'loaded';\ndeclare type AddOnConstructor<T> = (editor: Editor, url: string) => T;\ninterface AddOnManager<T> {\n    items: AddOnConstructor<T>[];\n    urls: Record<string, string>;\n    lookup: Record<string, {\n        instance: AddOnConstructor<T>;\n    }>;\n    get: (name: string) => AddOnConstructor<T> | undefined;\n    requireLangPack: (name: string, languages?: string) => void;\n    add: (id: string, addOn: AddOnConstructor<T>) => AddOnConstructor<T>;\n    remove: (name: string) => void;\n    createUrl: (baseUrl: UrlObject, dep: string | UrlObject) => UrlObject;\n    load: (name: string, addOnUrl: string | UrlObject) => Promise<void>;\n    waitFor: (name: string, state?: WaitState) => Promise<void>;\n}\ninterface RangeUtils {\n    walk: (rng: Range, callback: (nodes: Node[]) => void) => void;\n    split: (rng: Range) => RangeLikeObject;\n    normalize: (rng: Range) => boolean;\n    expand: (rng: Range, options?: {\n        type: 'word';\n    }) => Range;\n}\ninterface ScriptLoaderSettings {\n    referrerPolicy?: ReferrerPolicy;\n}\ninterface ScriptLoaderConstructor {\n    readonly prototype: ScriptLoader;\n    new (): ScriptLoader;\n    ScriptLoader: ScriptLoader;\n}\ndeclare class ScriptLoader {\n    static ScriptLoader: ScriptLoader;\n    private settings;\n    private states;\n    private queue;\n    private scriptLoadedCallbacks;\n    private queueLoadedCallbacks;\n    private loading;\n    constructor(settings?: ScriptLoaderSettings);\n    _setReferrerPolicy(referrerPolicy: ReferrerPolicy): void;\n    loadScript(url: string): Promise<void>;\n    isDone(url: string): boolean;\n    markDone(url: string): void;\n    add(url: string): Promise<void>;\n    load(url: string): Promise<void>;\n    remove(url: string): void;\n    loadQueue(): Promise<void>;\n    loadScripts(scripts: string[]): Promise<void>;\n}\ndeclare type TextProcessCallback = (node: Text, offset: number, text: string) => number;\ninterface Spot {\n    container: Text;\n    offset: number;\n}\ninterface TextSeeker {\n    backwards: (node: Node, offset: number, process: TextProcessCallback, root?: Node) => Spot | null;\n    forwards: (node: Node, offset: number, process: TextProcessCallback, root?: Node) => Spot | null;\n}\ninterface DomTreeWalkerConstructor {\n    readonly prototype: DomTreeWalker;\n    new (startNode: Node, rootNode: Node): DomTreeWalker;\n}\ndeclare class DomTreeWalker {\n    private readonly rootNode;\n    private node;\n    constructor(startNode: Node, rootNode: Node);\n    current(): Node | null | undefined;\n    next(shallow?: boolean): Node | null | undefined;\n    prev(shallow?: boolean): Node | null | undefined;\n    prev2(shallow?: boolean): Node | null | undefined;\n    private findSibling;\n    private findPreviousNode;\n}\ninterface Version {\n    major: number;\n    minor: number;\n}\ninterface Env {\n    transparentSrc: string;\n    documentMode: number;\n    cacheSuffix: any;\n    container: any;\n    canHaveCSP: boolean;\n    windowsPhone: boolean;\n    browser: {\n        current: string | undefined;\n        version: Version;\n        isEdge: () => boolean;\n        isChromium: () => boolean;\n        isIE: () => boolean;\n        isOpera: () => boolean;\n        isFirefox: () => boolean;\n        isSafari: () => boolean;\n    };\n    os: {\n        current: string | undefined;\n        version: Version;\n        isWindows: () => boolean;\n        isiOS: () => boolean;\n        isAndroid: () => boolean;\n        isMacOS: () => boolean;\n        isLinux: () => boolean;\n        isSolaris: () => boolean;\n        isFreeBSD: () => boolean;\n        isChromeOS: () => boolean;\n    };\n    deviceType: {\n        isiPad: () => boolean;\n        isiPhone: () => boolean;\n        isTablet: () => boolean;\n        isPhone: () => boolean;\n        isTouch: () => boolean;\n        isWebView: () => boolean;\n        isDesktop: () => boolean;\n    };\n}\ninterface FakeClipboardItem {\n    readonly items: Record<string, any>;\n    readonly types: ReadonlyArray<string>;\n    readonly getType: <D = any>(type: string) => D | undefined;\n}\ninterface FakeClipboard {\n    readonly FakeClipboardItem: (items: Record<string, any>) => FakeClipboardItem;\n    readonly write: (data: FakeClipboardItem[]) => void;\n    readonly read: () => FakeClipboardItem[] | undefined;\n    readonly clear: () => void;\n}\ninterface FocusManager {\n    isEditorUIElement: (elm: Element) => boolean;\n}\ninterface EntitiesMap {\n    [name: string]: string;\n}\ninterface Entities {\n    encodeRaw: (text: string, attr?: boolean) => string;\n    encodeAllRaw: (text: string) => string;\n    encodeNumeric: (text: string, attr?: boolean) => string;\n    encodeNamed: (text: string, attr?: boolean, entities?: EntitiesMap) => string;\n    getEncodeFunc: (name: string, entities?: string) => (text: string, attr?: boolean) => string;\n    decode: (text: string) => string;\n}\ninterface IconPack {\n    icons: Record<string, string>;\n}\ninterface IconManager {\n    add: (id: string, iconPack: IconPack) => void;\n    get: (id: string) => IconPack;\n    has: (id: string) => boolean;\n}\ninterface Resource {\n    load: <T = any>(id: string, url: string) => Promise<T>;\n    add: (id: string, data: any) => void;\n    unload: (id: string) => void;\n}\ntype TextPatterns_d_Pattern = Pattern;\ntype TextPatterns_d_RawPattern = RawPattern;\ntype TextPatterns_d_DynamicPatternsLookup = DynamicPatternsLookup;\ntype TextPatterns_d_RawDynamicPatternsLookup = RawDynamicPatternsLookup;\ntype TextPatterns_d_DynamicPatternContext = DynamicPatternContext;\ntype TextPatterns_d_BlockCmdPattern = BlockCmdPattern;\ntype TextPatterns_d_BlockPattern = BlockPattern;\ntype TextPatterns_d_BlockFormatPattern = BlockFormatPattern;\ntype TextPatterns_d_InlineCmdPattern = InlineCmdPattern;\ntype TextPatterns_d_InlinePattern = InlinePattern;\ntype TextPatterns_d_InlineFormatPattern = InlineFormatPattern;\ndeclare namespace TextPatterns_d {\n    export { TextPatterns_d_Pattern as Pattern, TextPatterns_d_RawPattern as RawPattern, TextPatterns_d_DynamicPatternsLookup as DynamicPatternsLookup, TextPatterns_d_RawDynamicPatternsLookup as RawDynamicPatternsLookup, TextPatterns_d_DynamicPatternContext as DynamicPatternContext, TextPatterns_d_BlockCmdPattern as BlockCmdPattern, TextPatterns_d_BlockPattern as BlockPattern, TextPatterns_d_BlockFormatPattern as BlockFormatPattern, TextPatterns_d_InlineCmdPattern as InlineCmdPattern, TextPatterns_d_InlinePattern as InlinePattern, TextPatterns_d_InlineFormatPattern as InlineFormatPattern, };\n}\ninterface Delay {\n    setEditorInterval: (editor: Editor, callback: () => void, time?: number) => number;\n    setEditorTimeout: (editor: Editor, callback: () => void, time?: number) => number;\n}\ndeclare type UploadResult = UploadResult$2;\ninterface ImageUploader {\n    upload: (blobInfos: BlobInfo[], showNotification?: boolean) => Promise<UploadResult[]>;\n}\ndeclare type ArrayCallback$1<T, R> = (this: any, x: T, i: number, xs: ArrayLike<T>) => R;\ndeclare type ObjCallback$1<T, R> = (this: any, value: T, key: string, obj: Record<string, T>) => R;\ndeclare type ArrayCallback<T, R> = ArrayCallback$1<T, R>;\ndeclare type ObjCallback<T, R> = ObjCallback$1<T, R>;\ndeclare type WalkCallback<T> = (this: any, o: T, i: string, n: keyof T | undefined) => boolean | void;\ninterface Tools {\n    is: (obj: any, type?: string) => boolean;\n    isArray: <T>(arr: any) => arr is Array<T>;\n    inArray: <T>(arr: ArrayLike<T>, value: T) => number;\n    grep: {\n        <T>(arr: ArrayLike<T> | null | undefined, pred?: ArrayCallback<T, boolean>): T[];\n        <T>(arr: Record<string, T> | null | undefined, pred?: ObjCallback<T, boolean>): T[];\n    };\n    trim: (str: string | null | undefined) => string;\n    toArray: <T>(obj: ArrayLike<T>) => T[];\n    hasOwn: (obj: any, name: string) => boolean;\n    makeMap: (items: ArrayLike<string> | string | undefined, delim?: string | RegExp, map?: Record<string, {}>) => Record<string, {}>;\n    each: {\n        <T>(arr: ArrayLike<T> | null | undefined, cb: ArrayCallback<T, void | boolean>, scope?: any): boolean;\n        <T>(obj: Record<string, T> | null | undefined, cb: ObjCallback<T, void | boolean>, scope?: any): boolean;\n    };\n    map: {\n        <T, R>(arr: ArrayLike<T> | null | undefined, cb: ArrayCallback<T, R>): R[];\n        <T, R>(obj: Record<string, T> | null | undefined, cb: ObjCallback<T, R>): R[];\n    };\n    extend: (obj: Object, ext: Object, ...objs: Object[]) => any;\n    walk: <T extends Record<string, any>>(obj: T, f: WalkCallback<T>, n?: keyof T, scope?: any) => void;\n    resolve: (path: string, o?: Object) => any;\n    explode: (s: string | string[], d?: string | RegExp) => string[];\n    _addCacheSuffix: (url: string) => string;\n}\ninterface KeyboardLikeEvent {\n    shiftKey: boolean;\n    ctrlKey: boolean;\n    altKey: boolean;\n    metaKey: boolean;\n}\ninterface VK {\n    BACKSPACE: number;\n    DELETE: number;\n    DOWN: number;\n    ENTER: number;\n    ESC: number;\n    LEFT: number;\n    RIGHT: number;\n    SPACEBAR: number;\n    TAB: number;\n    UP: number;\n    PAGE_UP: number;\n    PAGE_DOWN: number;\n    END: number;\n    HOME: number;\n    modifierPressed: (e: KeyboardLikeEvent) => boolean;\n    metaKeyPressed: (e: KeyboardLikeEvent) => boolean;\n}\ninterface DOMUtilsNamespace {\n    (doc: Document, settings: Partial<DOMUtilsSettings>): DOMUtils;\n    DOM: DOMUtils;\n    nodeIndex: (node: Node, normalized?: boolean) => number;\n}\ninterface RangeUtilsNamespace {\n    (dom: DOMUtils): RangeUtils;\n    compareRanges: (rng1: RangeLikeObject, rng2: RangeLikeObject) => boolean;\n    getCaretRangeFromPoint: (clientX: number, clientY: number, doc: Document) => Range;\n    getSelectedNode: (range: Range) => Node;\n    getNode: (container: Node, offset: number) => Node;\n}\ninterface AddOnManagerNamespace {\n    <T>(): AddOnManager<T>;\n    language: string | undefined;\n    languageLoad: boolean;\n    baseURL: string;\n    PluginManager: PluginManager;\n    ThemeManager: ThemeManager;\n    ModelManager: ModelManager;\n}\ninterface BookmarkManagerNamespace {\n    (selection: EditorSelection): BookmarkManager;\n    isBookmarkNode: (node: Node) => boolean;\n}\ninterface TinyMCE extends EditorManager {\n    geom: {\n        Rect: Rect;\n    };\n    util: {\n        Delay: Delay;\n        Tools: Tools;\n        VK: VK;\n        URI: URIConstructor;\n        EventDispatcher: EventDispatcherConstructor<any>;\n        Observable: Observable<any>;\n        I18n: I18n;\n        LocalStorage: Storage;\n        ImageUploader: ImageUploader;\n    };\n    dom: {\n        EventUtils: EventUtilsConstructor;\n        TreeWalker: DomTreeWalkerConstructor;\n        TextSeeker: (dom: DOMUtils, isBlockBoundary?: (node: Node) => boolean) => TextSeeker;\n        DOMUtils: DOMUtilsNamespace;\n        ScriptLoader: ScriptLoaderConstructor;\n        RangeUtils: RangeUtilsNamespace;\n        Serializer: (settings: DomSerializerSettings, editor?: Editor) => DomSerializer;\n        ControlSelection: (selection: EditorSelection, editor: Editor) => ControlSelection;\n        BookmarkManager: BookmarkManagerNamespace;\n        Selection: (dom: DOMUtils, win: Window, serializer: DomSerializer, editor: Editor) => EditorSelection;\n        StyleSheetLoader: (documentOrShadowRoot: Document | ShadowRoot, settings: StyleSheetLoaderSettings) => StyleSheetLoader;\n        Event: EventUtils;\n    };\n    html: {\n        Styles: (settings?: StylesSettings, schema?: Schema) => Styles;\n        Entities: Entities;\n        Node: AstNodeConstructor;\n        Schema: (settings?: SchemaSettings) => Schema;\n        DomParser: (settings?: DomParserSettings, schema?: Schema) => DomParser;\n        Writer: (settings?: WriterSettings) => Writer;\n        Serializer: (settings?: HtmlSerializerSettings, schema?: Schema) => HtmlSerializer;\n    };\n    AddOnManager: AddOnManagerNamespace;\n    Annotator: (editor: Editor) => Annotator;\n    Editor: EditorConstructor;\n    EditorCommands: EditorCommandsConstructor;\n    EditorManager: EditorManager;\n    EditorObservable: EditorObservable;\n    Env: Env;\n    FocusManager: FocusManager;\n    Formatter: (editor: Editor) => Formatter;\n    NotificationManager: (editor: Editor) => NotificationManager;\n    Shortcuts: ShortcutsConstructor;\n    UndoManager: (editor: Editor) => UndoManager;\n    WindowManager: (editor: Editor) => WindowManager;\n    DOM: DOMUtils;\n    ScriptLoader: ScriptLoader;\n    PluginManager: PluginManager;\n    ThemeManager: ThemeManager;\n    ModelManager: ModelManager;\n    IconManager: IconManager;\n    Resource: Resource;\n    FakeClipboard: FakeClipboard;\n    trim: Tools['trim'];\n    isArray: Tools['isArray'];\n    is: Tools['is'];\n    toArray: Tools['toArray'];\n    makeMap: Tools['makeMap'];\n    each: Tools['each'];\n    map: Tools['map'];\n    grep: Tools['grep'];\n    inArray: Tools['inArray'];\n    extend: Tools['extend'];\n    walk: Tools['walk'];\n    resolve: Tools['resolve'];\n    explode: Tools['explode'];\n    _addCacheSuffix: Tools['_addCacheSuffix'];\n}\ndeclare const tinymce: TinyMCE;\nexport { AddOnManager, Annotator, AstNode, Bookmark, BookmarkManager, ControlSelection, DOMUtils, Delay, DomParser, DomParserSettings, DomSerializer, DomSerializerSettings, DomTreeWalker, Editor, EditorCommands, EditorEvent, EditorManager, EditorModeApi, EditorObservable, EditorOptions, EditorSelection, Entities, Env, EventDispatcher, EventUtils, EventTypes_d as Events, FakeClipboard, FocusManager, Format_d as Formats, Formatter, GeomRect, HtmlSerializer, HtmlSerializerSettings, I18n, IconManager, Model, ModelManager, NotificationApi, NotificationManager, NotificationSpec, Observable, Plugin, PluginManager, RangeUtils, RawEditorOptions, Rect, Resource, Schema, SchemaSettings, ScriptLoader, Shortcuts, StyleSheetLoader, Styles, TextPatterns_d as TextPatterns, TextSeeker, Theme, ThemeManager, TinyMCE, Tools, URI, Ui_d as Ui, UndoManager, VK, WindowManager, Writer, WriterSettings, tinymce as default };\n"
  },
  {
    "path": "vue_acimage_web/src/App.vue",
    "content": "<template>\n\t<div id=\"app\">\n\t\t<router-view></router-view>\n\t</div>\n</template>\n\n<script>\n\texport default {\n\t\tname: 'App',\n\t\tcreated() {\n\t\t\tthis.$store.commit(\"init\");\n\t\t}\n\t}\n</script>\n\n<style>\n\tbody {\n\t\tmargin: 0px;\n\t}\n\n\t/* 解决tinymce弹出框在el-dialog中层级太低被遮盖 */\n\t.tox-tinymce-aux {\n\t\tz-index: 9999 !important;\n\t}\n\n\t/* #app {\n  font-family: Avenir, Helvetica, Arial, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  text-align: center;\n  color: #2c3e50;\n  margin-top: 60px;\n} */\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/api/HomeCarousel.js",
    "content": "import request from '@/utils/request.js'\n\n\nexport let queryHomeCarousel = function() {\n\treturn request.get('/api/community/homeCarousels/list');\n}\n"
  },
  {
    "path": "vue_acimage_web/src/api/TopicSearch.js",
    "content": "import request from '@/utils/request.js'\n\nexport let searchTopics = function(formData) {\n\tlet config = { params: formData }\n\treturn request.get('/api/community/topics/search/multiSearch', config)\n}\n"
  },
  {
    "path": "vue_acimage_web/src/api/UserRank.js",
    "content": "import request from '@/utils/request.js'\nimport { Code } from '@/utils/result.js'\n\nexport let pageUserRankBy = function(params) {\n\treturn request.get('/api/community/users/rank/byColumn', { params: params });\n}\n"
  },
  {
    "path": "vue_acimage_web/src/api/category.js",
    "content": "import request from '@/utils/request.js'\nimport axios from 'axios'\nimport MessageUtils from '@/utils/MessageUtils'\nimport global from '@/utils/global.js'\n\nexport let queryAllCategories = function() {\n\treturn request.get('/api/community/categories/query/all')\n}"
  },
  {
    "path": "vue_acimage_web/src/api/comment.js",
    "content": "import request from '@/utils/request.js'\n\n\n//操作\nexport let addComment = function(data) {\n\treturn request.post('/api/community/comments/operate', data);\n}\n\nexport let deleteComment = function(commentId) {\n\treturn request.delete('/api/community/comments/operate/' + commentId);\n}\n\nexport let modifyComment = function(data) {\n\treturn request.put('/api/community/comments/operate', data);\n}\n\n\n//查询\nexport let queryCommentPage = function(topicId, pageNo) {\n\tlet reqParams = {\n\t\ttopicId: topicId,\n\t\tpageNo: pageNo\n\t};\n\tlet config = { params: reqParams };\n\treturn request.get('/api/community/comments/query/topicComments', config)\n}\n\nexport let pageMyComments = function(pageNo) {\n\treturn request.get('/api/community/comments/query/mine/' + pageNo)\n}\n"
  },
  {
    "path": "vue_acimage_web/src/api/image.js",
    "content": "import request from '@/utils/request.js'\nimport axios from 'axios'\nimport MessageUtils from '@/utils/MessageUtils'\nimport global from '@/utils/global.js'\n\nexport let downloadImages = function(imageIds, fileName) {\n\n\tlet urlParams = new URLSearchParams();\n\tfor (let imageId of imageIds) {\n\t\turlParams.append(\"imageIds\", imageId);\n\t}\n\t// let url='/api/files/download/imageFiles?'+urlParams;\n\tlet config = { responseType: 'blob', params: urlParams, headers: { 'authorization': global.TOKEN() } }\n\taxios.post('/api/image/download/imageFiles', {}, config)\n\t\t.then(resp => {\n\t\t\tif (resp.data.type == 'application/json') {\n\t\t\t\tconst reader = new FileReader(); //创建一个FileReader实例\n\t\t\t\treader.readAsText(resp.data, 'utf-8'); //读取文件,结果用字符串形式表示\n\t\t\t\treader.onload = function() { //读取完成后,**获取reader.result**\n\t\t\t\t\tconst result = JSON.parse(reader.result);\n\t\t\t\t\tMessageUtils.notice(result.msg);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet url = window.URL.createObjectURL(new Blob([resp.data]));\n\t\t\tlet link = document.createElement('a');\n\t\t\tlink.style.display = 'none';\n\t\t\tlink.href = url;\n\t\t\tlink.setAttribute('download', fileName + \".zip\");\n\t\t\tdocument.body.appendChild(link);\n\t\t\tlink.click();\n\t\t\t// 释放URL对象所占资源\n\t\t\twindow.URL.revokeObjectURL(url);\n\t\t\t// 用完即删\n\t\t\tdocument.body.removeChild(link);\n\t\t})\n}\n\nexport let searchImagesByImage = function(reqData) {\n\tlet config = { 'Content-type': 'multipart/form-data' };\n\treturn request.post('/api/image/images/searchByImage', reqData, config);\n}\n\nexport let uploadTopicImage = function(reqData) {\n\tlet config = { 'Content-type': 'multipart/form-data' };\n\treturn request.post('/api/image/images/operate/upload/topicImage', reqData, config);\n}\n\n\n"
  },
  {
    "path": "vue_acimage_web/src/api/login.js",
    "content": "import request from '@/utils/request.js'\nimport axios from 'axios'\nimport { Code } from '@/utils/result.js'\n\nexport let sendCodeToEmail = function(email) {\n\tlet param = { email: email }\n\treturn request.post('/api/user/logins/sendCode',{},{ params: param })\n}\n\nexport let getPublicKey = function() {\n\treturn request.get('/api/user/logins/getPublicKey')\n}\nexport let queryIsUsernameExist = function(username) {\n\treturn axios.get('/api/user/users/isExist/' + username).then(resp => {\n\t\tlet res = resp.data;\n\t\tif (res.code == Code.OK) {\n\t\t\treturn res;\n\t\t}\n\t});\n}\n\n\n\n\nexport let doLogin = function(data) {\n\treturn request.post('/api/user/logins/doLogin', data)\n}\n\nexport let doRegister = function(data) {\n\treturn request.post('/api/user/logins/doRegister', data)\n}\n\nexport let doLogout = function() {\n\treturn request.post('/api/user/logins/logout');\n}\n\n\n"
  },
  {
    "path": "vue_acimage_web/src/api/message.js",
    "content": "import request from '@/utils/request.js'\n\n\n\n//查询\nexport let pageCommentMessages = function(pageNo,pageSize) {\n\treturn request.get('/api/user/messages/query/comments/page/'+pageNo+'/'+pageSize)\n}\n\n\n"
  },
  {
    "path": "vue_acimage_web/src/api/photo.js",
    "content": "import request from '@/utils/request.js'\nimport axios from 'axios'\nimport MessageUtils from '@/utils/MessageUtils'\nimport global from '@/utils/global.js'\n\n\nexport let uploadPhoto = function(reqData) {\n\tlet config = { 'Content-type': 'multipart/form-data' };\n\treturn request.post('/api/image/photos/operate/upload', reqData, config);\n}\n\n\n"
  },
  {
    "path": "vue_acimage_web/src/api/star.js",
    "content": "import request from '@/utils/request.js'\n\n//操作\nexport let deleteStar=function(topicId){\n\treturn request.delete('/api/community/stars/operate/'+topicId);\n}\n\nexport let addStar=function(topicId){\n\treturn request.post('/api/community/stars/operate/'+topicId);\n}\n\n\n//查询\nexport let queryIsStar=function(topicId){\n\treturn request.get('/api/community/stars/query/isStar/'+topicId)\n}\n\nexport let pageMyStar=function(pageNo){\n\treturn request.get('/api/community/stars/query/mine/' + pageNo);\n}"
  },
  {
    "path": "vue_acimage_web/src/api/tag.js",
    "content": "import request from '@/utils/request.js'\nimport axios from 'axios'\nimport MessageUtils from '@/utils/MessageUtils'\nimport global from '@/utils/global.js'\n\nexport let queryAllTags = function() {\n\treturn request.get('/api/community/tags/query/all')\n}"
  },
  {
    "path": "vue_acimage_web/src/api/topic.js",
    "content": "import request from '@/utils/request.js'\n\n\n\n//查询话题\nexport let queryRecentHotTopics = function() {\n\treturn request.get('/api/community/topics/query/recentHot');\n}\n\nexport let queryRecommendTopics = function() {\n\treturn request.get('/api/community/topics/query/recommend');\n}\n\nexport let queryMostCommentCountTopics = function() {\n\treturn request.get('/api/community/topics/query/most/commentCount');\n}\n\nexport let queryTopicAndFirstCommentPage = function(topicId) {\n\treturn request.get('/api/community/topics/query/info/' + topicId);\n}\n\nexport let pageMyPublishTopic = function(pageNo) {\n\treturn request.get('/api/community/topics/query/mine/' + pageNo);\n}\n\nexport let pageActiveTopics = function(pageNo,pageSize) {\n\treturn request.get('/api/community/topics/query/most/active/' + pageNo+'/'+pageSize)\n}\n\nexport let get3HotTopicLists = function() {\n\treturn request.get('/api/community/topics/query/hot/3attrs')\n}\n\n\n//\nexport let pageByCategoryId = function(formData) {\n\treturn request.get('/api/community/topics/query/byCategoryId',{params:formData})\n}\n\nexport let pageByTagId = function(formData) {\n\treturn request.get('/api/community/topics/query/byTagId',{params:formData})\n}\n\nexport let pageBySort = function(formData) {\n\treturn request.get('/api/community/topics/query/bySort',{params:formData})\n}\n\n\n\n\n\n//操作话题\nexport let addTopic = function(formData) {\n\tlet config = { 'Content-type': 'multipart/form-data' };\n\treturn request.post('/api/community/topics/operate', formData, config);\n}\n\nexport let modifyTitle = function(id, title) {\n\treturn request.put('/api/community/topics/operate/title/' + id + '/' + title);\n}\n\nexport let modifyHtml= function(data) {\n\treturn request.put('/api/community/topics/operate/html', data);\n}\n\nexport let deleteTopic = function(topicId) {\n\treturn request.delete('/api/community/topics/operate/' + topicId)\n}\n"
  },
  {
    "path": "vue_acimage_web/src/api/user.js",
    "content": "import request from '@/utils/request.js'\nimport axios from 'axios'\nimport { Code } from '@/utils/result.js'\n\n\n//查询\nexport let queryProfile = function() {\n\treturn request.get('/api/user/users/query/me');\n}\n\n\n//操作\nexport let modifyUsername = function(newUsername) {\n\treturn request.put('/api/user/users/operate/username/' + newUsername)\n}\n\n// export let uploadPhoto = function(reqData) {\n// \tlet config = { 'Content-type': 'multipart/form-data' };\n// \treturn request.post('/api/user/users/uploadPhoto', reqData, config);\n// }\n\n\n\n\n"
  },
  {
    "path": "vue_acimage_web/src/components/BaseContainer/BaseContainer.css",
    "content": ".container{\n\tdisplay:inline-block;\n\twidth: 100%;\n}\n\n.header{\n\tposition:relative;\n\theight:60px;\n}\n\n.header-icon{\n\tfont-size:40px;\n\tcolor:#E5E544;\n}\n\n.header-title{\n\tposition:relative;\n\ttop:-6px;\n\tdisplay: inline-block;\n\tfont-size:25px;\n\tcolor:#999999;\n}"
  },
  {
    "path": "vue_acimage_web/src/components/BaseContainer/BaseContainer.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"container\">\n\t\t\t<div class=\"header\">\n\t\t\t\t<i :class=\"iconClass\" :style=\"iconStyle\"></i>\n\t\t\t\t<div class=\"header-title\">{{title}}</div>\n\t\t\t\t<div style=\"margin-top:-20px;\">\n\t\t\t\t\t<el-divider direction=\"horizontal\"></el-divider>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<slot></slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\texport default {\n\t\tname: \"BaseContainer\",\n\t\tprops: {\n\t\t\ticon: {\n\t\t\t\tdefault: 'el-icon-discover'\n\t\t\t},\n\t\t\ttitle: {},\n\t\t\ticonColor: {\n\t\t\t\tdefault: \"#E5E544\"\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\ticonClass() {\n\t\t\t\treturn this.icon + ' header-icon'\n\t\t\t},\n\t\t\ticonStyle() {\n\t\t\t\treturn {color:this.iconColor}\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./BaseContainer.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/CategoryCard/CategoryCard.css",
    "content": ".categories-container>>>.el-button {\n\tdisplay: inline-block;\n\tmargin-top: 6px;\n\tmargin-right: 4px;\n\tmargin-left: auto;\n}\n\n.category-card-div>>> .el-card{\n\tborder-width:2px;\n}\n\n.categories-container {\n\tpadding-bottom: 10px;\n}"
  },
  {
    "path": "vue_acimage_web/src/components/CategoryCard/CategoryCard.vue",
    "content": "<template>\n\t<div class=\"category-card-div\">\n\t\t<el-card shadow=\"hover\">\n\t\t\t<div slot=\"header\">\n\t\t\t\t<span style=\"color:#FF9300\">\n\t\t\t\t\t<i class=\"el-icon-c-scale-to-original\" />分类\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div class=\"categories-container\">\n\t\t\t\t<el-button :id=\"'btn'+item.id\" v-for=\"item in $store.state.categoryList\" size=\"small\"\n\t\t\t\t\t:type=\"$global.buttonType(item.id)\" plain @click=\"handleClick(item.id)\" :key=\"item.id\">\n\t\t\t\t\t{{item.label}}\n\t\t\t\t</el-button>\n\t\t\t</div>\n\t\t</el-card>\n\t</div>\n\n</template>\n\n<script>\n\texport default {\n\t\tname: 'CategoryCard',\n\t\tprops: {\n\t\t\tclickCategory: {\n\t\t\t\ttype: Function\n\t\t\t},\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tactiveId: null\n\t\t\t}\n\t\t},\n\t\tmethods: {\n\t\t\thandleClick(categoryId) {\n\t\t\t\tif (this.activeId == categoryId) {\n\t\t\t\t\tthis.activeId = null;\n\t\t\t\t\t// document.querySelector('#btn' + categoryId).blur()\n\t\t\t\t\t// document.querySelector('#btn'+categoryId).mouseleave()\n\t\t\t\t} else {\n\t\t\t\t\tthis.activeId = categoryId\n\t\t\t\t}\n\n\n\t\t\t\tthis.clickCategory(categoryId);\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style scoped src=\"./CategoryCard.css\">\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/EditBoard/EditBoard.css",
    "content": ".wrapper{\n\t\n}"
  },
  {
    "path": "vue_acimage_web/src/components/EditBoard/EditBoard.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"wrapper\" :style=\"myStyle\">\n\t\t\t<editor api-key=\"no-api-key\" :init=\"options\" v-model=\"Html\" />\n\t\t\t<!-- <div style=\"width: 200px;\" v-dompurify-html=\"Html\"></div> -->\n\t\t</div>\n\t</div>\n\n</template>\n\n<script>\n\timport Editor from '@tinymce/tinymce-vue'\n\timport { uploadTopicImage } from '@/api/image.js'\n\timport { Code } from '@/utils/result.js'\n\n\texport default {\n\t\tname: 'EditBoard',\n\t\tcomponents: {\n\t\t\t'editor': Editor\n\t\t},\n\t\tprops: {\n\t\t\twidth: {\n\t\t\t\ttype: String,\n\t\t\t\tdefault: '720px'\n\t\t\t},\n\t\t\tmargin: {\n\t\t\t\ttype: String,\n\t\t\t\tdefault: '0px'\n\t\t\t},\n\t\t\thtml: {\n\t\t\t\ttype: String,\n\t\t\t\tdefault: ''\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\tmyStyle() {\n\t\t\t\treturn {\n\t\t\t\t\twidth: this.width,\n\t\t\t\t\tmarginLeft: this.margin,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\toptions: {\n\t\t\t\t\t// plugins: 'lists link image table code help wordcount',\n\t\t\t\t\tplugins: 'lists link code wordcount emoticons image table',\n\t\t\t\t\theight: 600,\n\t\t\t\t\tlanguage: 'zh-Hans',\n\t\t\t\t\temoticons_database_url: '../../../tinymce/plugins/emoticons/js/emojis.js',\n\t\t\t\t\ttoolbar: [\n\t\t\t\t\t\t`code undo redo blocks\n\t\t\t\t\t\t| cut copy paste pastetext\n\t\t\t\t\t\t| forecolor backcolor bold italic underline strikethrough link anchor \n\t\t\t\t\t\t| alignleft aligncenter alignright alignjustify outdent indent \n\t\t\t\t\t\t| styleselect formatselect fontselect fontsizeselect | bullist numlist \n\t\t\t\t\t\t| blockquote subscript superscript removeformat\n\t\t\t\t\t\t| table image media charmap emoticons hr pagebreak insertdatetime print preview \n\t\t\t\t\t\t| fullscreen | bdmap indent2em lineheight axupimgs`\n\t\t\t\t\t],\n\t\t\t\t\timages_file_types: 'jpeg,jpg,png',\n\t\t\t\t\trelative_urls: true, //是否相对地址\n\t\t\t\t\tconvert_urls: false, //是否转换地址\n\t\t\t\t\t// images_upload_url: \"/manage/upload\", //指定上传图片的后端处理程序的URL。\n\t\t\t\t\t// images_upload_base_path: \"/demo\",\n\t\t\t\t\timages_upload_handler: (blobInfo, success, failure) => new Promise((resolve, reject) => {\n\t\t\t\t\t\tconst formData = new FormData();\n\t\t\t\t\t\tformData.append(\"imageFile\", blobInfo.blob());\n\t\t\t\t\t\tuploadTopicImage(formData).then((res) => {\n\t\t\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t\t\tconsole.log(res);\n\t\t\t\t\t\t\t\tresolve(res.data); //将图片展示到编辑器中\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treject(res.msg)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}).catch(() => {\n\t\t\t\t\t\t\t// resolve(\"static/image/user-rank-header.jpg\");\n\t\t\t\t\t\t\treject(\"上传失败\");\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t\t// content_style: `\n\t\t\t\t\t//   body {font-size: 14pt;}\n\t\t\t\t\t//   html, body                { height:100%; }\n\t\t\t\t\t//   a                         { text-decoration: none; }\n\t\t\t\t\t//   p                         { line-height:1.6; margin: 0px; }\n\t\t\t\t\t//   table                     { word-wrap:break-word; word-break:break-all;max-width:100%; border:none; border-color:#999; }\n\t\t\t\t\t//   .mce-object-iframe        { width:100%; box-sizing:border-box; margin:0; padding:0; }\n\t\t\t\t\t//   ul,ol                     { list-style-position:inside; }\n\t\t\t\t\t//   `,\n\t\t\t\t\t// img                       { max-width:100%; display:block;height:auto; }\n\t\t\t\t\tfontsize_formats: \"8pt 10pt 12pt 14pt 18pt 24pt 36pt\",\n\n\t\t\t\t},\n\t\t\t\tHtml: '内容',\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tthis.Html = this.html;\n\t\t}\n\t}\n</script>\n\n<style scoped>\n\t.wrapper {\n\t\tdisplay: block;\n\t}\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/FloatImage/FloatImage.css",
    "content": "\n.container{\n\tposition:relative;\n\twidth:100%;\n\theight:250px;\n\tmargin-bottom: 15px;\n\tdisplay: inline-block;\n\tbackground-color:white;\n\tborder-radius:5px 5px;\n\tbox-shadow:1px 1px 1px #EDEDE4;\n\tcolor:#7D7D70;\n}\n\n.container:hover{\n\tbox-shadow:0px 0px 6px  #626258 !important;\n\ttransform: translate(0, -3px);\n\ttransition:transform 0.2s;\t\n}\n\n.title{\n\twidth:100%;\n\tword-wrap: break-word;\n\tfont-size: 14px;\n\tposition:absolute;\n\tz-index:11;\n\tbottom:50px;\n\tcolor:white;\n\tbackground-image: linear-gradient(to top, rgba(0, 0, 0, 0.7), rgba(99, 99, 99, 0.01));\n\t/* rgba(99,99,99,1); */\n\t\n}\n\n/* .scan-in-title{\n\tdisplay:inline;\n\tfont-size:8px;\n\tmargin-lef:5px;\n\t\n} */\n\n.image{\n\twidth:100%;\n\theight:200px;\n\tborder-radius:5px 5px 0px 0px;\n\t\n}\n\n.info{\n\tposition:absolute;\n\tleft:10px;\n\ttop:205px;\n\twidth:95%;\n}\n\n.username{\n\tposition: absolute;\n\tfont-size: 10px;\n\ttop:2px;\n\tleft:24px;\n}\n\n.time{\n\tfont-size:10px;\n\tmargin-left:3px;\n\tdisplay: inline-block;\n\twidth:50%\n}\n\n.time-icon-color{\n\tcolor:red;\n\tfont-weight:bold !important;\n}\n\n.info-star{\n\tmargin-top:-4px;\n\tdisplay: inline-block;\n}\n\n.info-star-icon{\n\tposition: relative;\n\ttop:0.8px;\n\tdisplay: inline-block;\n}\n\n.info-star-num{\n\tdisplay: inline-block;\n}\n\n"
  },
  {
    "path": "vue_acimage_web/src/components/FloatImage/FloatImage.vue",
    "content": "<template>\n\t<div class=\"container\">\n\t\t<el-image :src=\"imageUrl\" fit=\"cover\" class=\"image\" lazy>\n\t\t</el-image>\n\t\t<div class=\"title\">\n\t\t\t{{$global.omitStr(title,20)}}\n\t\t</div>\n\t\t<div class=\"info\">\n\t\t\t<el-avatar :src=\"photoUrl\" :size=\"20\"></el-avatar>\n\n\t\t\t<div class=\"username\">{{$global.omitStr(username,8)}}</div>\n\t\t\t<div style=\"margin-top:-5px;\">\n\t\t\t\t<div class=\"time\">\n\t\t\t\t\t<i class=\"el-icon-time time-icon-color\"></i>\n\t\t\t\t\t<span>{{$global.timeView(createTime)}}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"info-star\">\n\t\t\t\t\t<div class=\"info-star-icon\">\n\t\t\t\t\t\t<i class=\"el-icon-star-on\" style=\"font-size:16px;color:orange;\"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"info-star-num\">\n\t\t\t\t\t\t<span style=\"font-size:10px;\">{{star}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\texport default {\n\t\tname: 'FloatImage',\n\t\tprops: {\n\t\t\ttitle: {\n\t\t\t\tdefault: '加载中'\n\t\t\t},\n\t\t\tusername: {\n\t\t\t\tdefault: '加载中'\n\t\t\t},\n\t\t\tcreateTime: {\n\t\t\t\tdefault: '2022-9-26 20:22:22'\n\t\t\t},\n\t\t\tstar: {\n\t\t\t\tdefault: 888\n\t\t\t},\n\t\t\tpageView: {\n\t\t\t\tdefaut: 88888\n\t\t\t},\n\t\t\tphotoUrl: {\n\t\t\t\tdefault: 'static/image/0.jpeg'\n\t\t\t},\n\t\t\timageUrl: {\n\t\t\t\tdefault: 'static/image/0.jpeg'\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\ttimeFromNow() {\n\t\t\t\tlet d1 = new Date(this.createTime);\n\t\t\t\tlet d2 = new Date();\n\t\t\t\tlet days = d2.getTime() - d1.getTime();\n\t\t\t\tlet time = parseInt(days / (1000 * 60 * 60 * 24));\n\t\t\t\tif (time >= 1) {\n\t\t\t\t\treturn time + '天前';\n\t\t\t\t}\n\n\t\t\t\ttime = parseInt(days / (1000 * 60 * 60));\n\t\t\t\tif (time >= 1) {\n\t\t\t\t\treturn time + '小时前'\n\t\t\t\t}\n\n\t\t\t\ttime = parseInt(days / (1000 * 60));\n\t\t\t\tif (time >= 1) {\n\t\t\t\t\treturn time + '分钟前'\n\t\t\t\t}\n\n\t\t\t\ttime = parseInt(days / (1000));\n\t\t\t\tif (time >= 1) {\n\t\t\t\t\treturn time + '秒前'\n\t\t\t\t}\n\n\t\t\t\treturn '刚刚'\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./FloatImage.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/HelloWorld.vue",
    "content": "<template>\n  <div class=\"hello\">\n    <h1>{{ msg }}</h1>\n    <p>\n      For a guide and recipes on how to configure / customize this project,<br>\n      check out the\n      <a href=\"https://cli.vuejs.org\" target=\"_blank\" rel=\"noopener\">vue-cli documentation</a>.\n    </p>\n    <h3>Installed CLI Plugins</h3>\n    <ul>\n      <li><a href=\"https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel\" target=\"_blank\" rel=\"noopener\">babel</a></li>\n      <li><a href=\"https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint\" target=\"_blank\" rel=\"noopener\">eslint</a></li>\n    </ul>\n    <h3>Essential Links</h3>\n    <ul>\n      <li><a href=\"https://vuejs.org\" target=\"_blank\" rel=\"noopener\">Core Docs</a></li>\n      <li><a href=\"https://forum.vuejs.org\" target=\"_blank\" rel=\"noopener\">Forum</a></li>\n      <li><a href=\"https://chat.vuejs.org\" target=\"_blank\" rel=\"noopener\">Community Chat</a></li>\n      <li><a href=\"https://twitter.com/vuejs\" target=\"_blank\" rel=\"noopener\">Twitter</a></li>\n      <li><a href=\"https://news.vuejs.org\" target=\"_blank\" rel=\"noopener\">News</a></li>\n    </ul>\n    <h3>Ecosystem</h3>\n    <ul>\n      <li><a href=\"https://router.vuejs.org\" target=\"_blank\" rel=\"noopener\">vue-router</a></li>\n      <li><a href=\"https://vuex.vuejs.org\" target=\"_blank\" rel=\"noopener\">vuex</a></li>\n      <li><a href=\"https://github.com/vuejs/vue-devtools#vue-devtools\" target=\"_blank\" rel=\"noopener\">vue-devtools</a></li>\n      <li><a href=\"https://vue-loader.vuejs.org\" target=\"_blank\" rel=\"noopener\">vue-loader</a></li>\n      <li><a href=\"https://github.com/vuejs/awesome-vue\" target=\"_blank\" rel=\"noopener\">awesome-vue</a></li>\n    </ul>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'HelloWorld',\n  props: {\n    msg: String\n  }\n}\n</script>\n\n<!-- Add \"scoped\" attribute to limit CSS to this component only -->\n<style scoped>\nh3 {\n  margin: 40px 0 0;\n}\nul {\n  list-style-type: none;\n  padding: 0;\n}\nli {\n  display: inline-block;\n  margin: 0 10px;\n}\na {\n  color: #42b983;\n}\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/HomeCarousel/HomeCarousel.vue",
    "content": "<template>\n\t<div class=\"carousel-container\" @click=\"toLink\">\n\t\t<el-carousel trigger=\"click\" height=\"350px\" @change=\"onCarouselChange\">\n\t\t\t<el-carousel-item v-for=\"image in images\" :key=\"image.id\" class=\"hover-pointer\">\n\t\t\t\t<el-image :src=\"image.url\" fit=\"cover\">\n\t\t\t\t</el-image>\n\t\t\t</el-carousel-item>\n\t\t</el-carousel>\n\t\t<div class=\"carousel-description\">\n\t\t\t<span style=\"margin-left:10px;\">{{currentDescription}}</span>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { queryHomeCarousel } from '@/api/HomeCarousel.js'\n\timport CommonUtils from '@/utils/CommonUtils';\n\timport { Code } from '@/utils/result.js'\n\n\texport default {\n\t\tname: 'HomeCarousel',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tindex: 0,\n\t\t\t\timages: []\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\tcurrentDescription() {\n\t\t\t\tif (this.images.length == 0) {\n\t\t\t\t\treturn '';\n\t\t\t\t}\n\t\t\t\treturn this.$global.omitStr(this.images[this.index].description, 30);\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tlet _this = this;\n\t\t\tqueryHomeCarousel().then(result => {\n\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t_this.images = result.data;\n\t\t\t\t\t_this.index = 0;\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t\tmethods: {\n\t\t\tonCarouselChange() {\n\t\t\t\tthis.index = (this.index + 1) % this.images.length;\n\t\t\t},\n\t\t\ttoLink() {\n\t\t\t\tlet link = this.images[this.index].link;\n\t\t\t\tif (!CommonUtils.isEmpty(link) && !CommonUtils.isEmpty(link.trim())) {\n\t\t\t\t\twindow.open(link)\n\t\t\t\t\t// window.location.href = link;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style scoped>\n\t.carousel-container {\n\t\twidth: 100%;\n\t\tposition: relative;\n\t\toverflow: hidden;\n\t}\n\n\t.carousel-container>>>.el-carousel {\n\t\tborder-radius: 5px;\n\t\twidth: 100%;\n\t}\n\n\t.carousel-container>>>.el-image {\n\t\twidth: 100%;\n\t\theight: 350px;\n\t\tborder-radius: 5px;\n\t}\n\n\t.carousel-description {\n\t\tdisplay: inline-block;\n\t\tposition: relative;\n\t\twidth: 100%;\n\t\theight: 60px;\n\t\tborder-radius: 5px;\n\t\ttop: -60px;\n\t\tz-index: 11;\n\t\tcolor: white;\n\t\tfont-size: 20px;\n\t\tbackground-image: linear-gradient(to top, rgba(0, 0, 0, 0.7), rgba(99, 99, 99, 0));\n\t}\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/MaskImage/MaskImage.css",
    "content": ".mask-image-container {\n\twidth: 250px;\n\theight: 175px;\n\tmargin-left: 10px;\n\tposition: relative;\n\tdisplay: inline-block;\n\tborder-radius: 5px;\n\toverflow: hidden;\n}\n\n.image-container {\n\twidth: 250px;\n\theight: 150px;\n\tborder-radius: 5px;\n\toverflow: hidden;\n}\n\n.image-mask {\n\twidth: 250px;\n\theight: 150px;\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\tborder-radius: 5px;\n\toverflow: hidden;\n\tbackground-color: rgba(169, 165, 164, 0.4);\n\tz-index: 11;\n\tcolor: white;\n\topacity: 0;\n}\n\n.image-mask:hover {\n\topacity: 0.9;\n\ttransition: opacity 0.4s;\n}\n\n/* .opacity-gradient{\n\topacity: 0;\n} */\n\n/* .opacity-gradient:hover {\n\topacity: 0.9;\n\ttransition: opacity 0.4s;\n} */\n\n.image-info {\n\twidth: 230px;\n\tmargin-left: 10px;\n\tmargin-right: 10px;\n\tmargin-top: 10px;\n}\n\n.image-info-bottom {\n\n\tposition: absolute;\n\tbottom: 10px;\n\n\tfont-family: 'Times New Roman', Times, serif;\n\tfont-size: 16px;\n}\n\n\n.bottom-title{\n\tcolor: #555555;\n\tfont-size: 14px;\n\tdisplay:inline-block;\n\tvertical-align: top;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/components/MaskImage/MaskImage.vue",
    "content": "<template>\n\t<div class=\"mask-image-container\">\n\t\t<div class=\"image-container\">\n\t\t\t<img :src=\"imageUrl\" width=\"250px\" height=\"150px\" style=\"object-fit:cover;object-position:center\" />\n\t\t</div>\n\t\t<div class=\"image-mask\">\n\t\t\t<div class=\"image-info\">\n\t\t\t\t{{$global.omitStr(title,18)}}\n\t\t\t</div>\n\t\t\t<div class=\"image-info image-info-bottom opacity-gradient\">\n\t\t\t\t<el-row style=\"margin-bottom: 5px;\">\n\t\t\t\t\t<el-col :span=\"16\">\n\t\t\t\t\t\t作者：{{$global.omitStr(username,8)}}\n\t\t\t\t\t</el-col>\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<div style=\"margin-top:2px;\">\n\t\t\t\t\t\t\t<i class=\"el-icon-chat-dot-square\"></i>{{commentCount}}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\n\t\t\t\t\t</el-col>\n\t\t\t\t\t\n\t\t\t\t</el-row>\n\t\t\t\t<el-row>\n\t\t\t\t\t<el-col :span=\"16\">\n\t\t\t\t\t\t<i class=\"el-icon-star-off\"></i>{{starCount}}\n\t\t\t\t\t</el-col>\n\n\t\t\t\t\t<el-col :span=\"8\">\n\t\t\t\t\t\t<i class=\"el-icon-view\"></i>{{pageView}}\n\t\t\t\t\t</el-col>\n\t\t\t\t</el-row>\n\t\t\t</div>\n\n\t\t</div>\n\t\t<span class=\"bottom-title\">\n\t\t\t{{$global.omitStr(title,17)}}\n\t\t</span>\n\n\t</div>\n</template>\n\n<script>\n\texport default {\n\t\tname: 'MaskImage',\n\t\tprops: {\n\t\t\ttitle: {\n\t\t\t\tdefault: '加载中...'\n\t\t\t},\n\t\t\tusername: {\n\t\t\t\tdefault: '加载中...'\n\t\t\t},\n\t\t\tcreateTime: {\n\t\t\t\tdefault: '2022-9-26 20:22:22'\n\t\t\t},\n\t\t\tstarCount: {\n\t\t\t\tdefault: 888\n\t\t\t},\n\t\t\tpageView: {\n\t\t\t\tdefaut: 88888\n\t\t\t},\n\t\t\tcommentCount: {\n\t\t\t\tdefaut: 88888\n\t\t\t},\n\t\t\timageUrl: {\n\t\t\t\tdefault: 'static/image/0.jpeg'\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./MaskImage.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/MyHeader/MyHeader.css",
    "content": "/* tabs */\n.tabs-wrapper {\n\theight: 60px;\n\tfont-size: 22px;\n\tbackground-color: white;\n\tbox-shadow: 0px 0px 2px #A7A796;\n\ttext-align: center;\n\tposition: relative;\n}\n\n.tabs-wrapper>img:last-child {\n\theight: 55px;\n\tposition: absolute;\n\tright: 10px;\n}\n\n.tabs-wrapper>img:first-child {\n\theight: 55px;\n\tposition: absolute;\n\tleft: 10px;\n}\n\n.tabs-container {\n\tdisplay: inline-block;\n\tmargin-top: 16px;\n\tfont-family: serif;\n}\n\n.tabs-container>div {\n\tdisplay: inline-block;\n}\n\n.tabs-item-link {\n\tcolor: #666666;\n\tfont-size: 22px;\n}\n\n.tabs-item-link:hover {\n\tcolor: #0074A0;\n}\n\n/* End tabs */\n"
  },
  {
    "path": "vue_acimage_web/src/components/MyHeader/MyHeader.vue",
    "content": "<template>\n\t<div>\n\t\t<my-navigation></my-navigation>\n\t\t<!-- header背景图 -->\n\t\t<div style=\"height:100px;background-image: url(static/image/header-bg.webp);background-position: center;\">\n\t\t</div>\n\t\t<div class=\"tabs-wrapper\">\n\t\t\t<img src=\"static/image/happy.jpeg\" />\n\t\t\t<div class=\"tabs-container\">\n\t\t\t\t<router-link to=\"/\" class=\"no-underline tabs-item-link\">\n\t\t\t\t\t<i class=\"el-icon-s-home\" style=\"color: red;\"></i>首页\n\t\t\t\t</router-link>\n\t\t\t\t<el-divider direction=\"vertical\"></el-divider>\n\t\t\t\t<router-link to=\"/forum\" class=\"no-underline tabs-item-link\">\n\t\t\t\t\t<i class=\"el-icon-chat-line-square\" style=\"color: red;\"></i>论坛\n\t\t\t\t</router-link>\n\t\t\t\t<el-divider direction=\"vertical\"></el-divider>\n\t\t\t\t<router-link to=\"/publish\" class=\"no-underline tabs-item-link\">\n\t\t\t\t\t<i class=\"el-icon-s-promotion\" style=\"color: red;\"></i>分享\n\t\t\t\t</router-link>\n\t\t\t\t<el-divider direction=\"vertical\"></el-divider>\n\t\t\t\t<router-link to=\"/SearchTopic\" class=\"no-underline tabs-item-link\">\n\t\t\t\t\t<i class=\"el-icon-search\" style=\"color: red;\"></i>搜索\n\t\t\t\t</router-link>\n\t\t\t\t<el-divider direction=\"vertical\"></el-divider>\n\t\t\t\t<router-link to=\"/SearchImage\" class=\"no-underline tabs-item-link\">\n\t\t\t\t\t<i class=\"el-icon-picture\" style=\"color: red;\"></i>识图\n\t\t\t\t</router-link>\n\t\t\t\t<el-divider direction=\"vertical\"></el-divider>\n\t\t\t\t<router-link to=\"/about\" class=\"no-underline tabs-item-link\">\n\t\t\t\t\t<i class=\"el-icon-info\" style=\"color: red;\"></i>关于\n\t\t\t\t</router-link>\n\t\t\t</div>\n\t\t\t<!-- <span style=\"color:red;\">HOT</span> -->\n\t\t\t<img src=\"static/image/sad.jpeg\" />\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport MyNavigation from './MyNavigation/MyNavigation.vue'\n\texport default {\n\t\tname: 'MyHeader',\n\t\tcomponents: {\n\t\t\tMyNavigation\n\t\t},\n\t}\n</script>\n\n<style src=\"./MyHeader.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/MyHeader/MyNavigation/MyNavigation.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"wrapper\">\n\t\t\t<el-row>\n\t\t\t\t<el-menu router mode=\"horizontal\" style=\"border: 0px;height:52px;margin-top:-12px;opacity: 0.85;\">\n\t\t\t\t\t<el-menu-item index=\"/\">\n\t\t\t\t\t\t<i class=\"el-icon-house\" style=\"color:orange\"></i>主页\n\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t<el-menu-item index=\"/MyActivity\">\n\t\t\t\t\t\t<i class=\"el-icon-magic-stick\" style=\"color:orange\"></i>我的动态\n\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t<el-menu-item index=\"/MyMessage\">\n\t\t\t\t\t\t<i class=\"el-icon-message\" style=\"color:red\"></i>消息\n\t\t\t\t\t\t<el-badge :value=\"$store.state.messageNum\" :max=\"99\" style=\"position: fixed;top: -20px;\">\n\t\t\t\t\t\t</el-badge>\n\t\t\t\t\t</el-menu-item>\n\n\t\t\t\t\t<el-submenu index=\"3\">\n\t\t\t\t\t\t<template slot=\"title\">\n\t\t\t\t\t\t\t<i class=\"el-icon-user-solid\" style=\"color:orange\"></i>个人中心\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<el-menu-item index=\"/profile\">\n\t\t\t\t\t\t\t<i class=\"el-icon-info\" style=\"color:orange\"></i>我的信息\n\t\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t\t<el-menu-item @click=\"logout\">\n\t\t\t\t\t\t\t<i class=\"el-icon-upload2\" style=\"color:orange\"></i>登出\n\t\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t</el-submenu>\n\n\t\t\t\t\t<el-menu-item>\n\t\t\t\t\t\t<el-button @click=\"onClickLogin\" type=\"danger\" style=\"position:fixed;right:80px;top: 0px;\">登录\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</el-menu-item>\n\t\t\t\t</el-menu>\n\t\t\t\t<el-input v-model=\"search\" @keyup.enter.native=\"enterSearch\" placeholder=\" ( ﹁ ﹁ ) ~→搜什么呢...\"\n\t\t\t\t\tmaxlength=\"15\" prefix-icon=\"el-icon-edit\" size=\"small\" clearable>\n\t\t\t\t</el-input>\n\t\t\t\t<el-avatar :src=\"$store.getters.getPhotoUrl\" :size=\"50\"\n\t\t\t\t\tstyle=\"margin-top:15px;position:fixed;right:10px;top:-10px;\">\n\t\t\t\t</el-avatar>\n\n\t\t\t</el-row>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { doLogout } from '@/api/login.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport MessageUtils from '@/utils/MessageUtils.js'\n\timport CommonUtils from '@/utils/CommonUtils.js'\n\texport default {\n\t\tname: 'MyNavigation',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tsearch: '',\n\t\t\t\tmessageNum: null,\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tlet _this = this;\n\t\t\tif (!this.$store.getters.isLogin) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (this.$store.state.hasWebSocket) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.$store.state.hasWebSocket = true;\n\t\t\t\n\t\t\tlet loc = window.location;\n\t\t\tlet webSocketUri = null;\n\t\t\tif (loc.protocol === \"https:\") {\n\t\t\t\twebSocketUri = \"wss:\";\n\t\t\t} else {\n\t\t\t\twebSocketUri = \"ws:\";\n\t\t\t}\n\t\t\twebSocketUri += \"//\" + loc.host + \"/websocket\";\n\t\t\tlet ws = new WebSocket(webSocketUri, this.$store.state.token);\n\t\t\t// let ws = new WebSocket(\"ws:localhost:81/websocket\", this.$store.state.token);\n\t\t\t//监听是否连接成功\n\t\t\tws.onopen = function() {\n\t\t\t\tconsole.log('连接成功');\n\t\t\t\t// ws.send('test1');\n\t\t\t}\n\t\t\t// 接听服务器发回的信息并处理展示\n\t\t\tws.onmessage = function(messageEvent) {\n\t\t\t\tconsole.log('接收到来自服务器的消息：' + messageEvent.data);\n\t\t\t\tif (messageEvent.data != 0) {\n\t\t\t\t\t_this.$store.state.messageNum = messageEvent.data;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 监听连接关闭事件\n\t\t\tws.onclose = function() {\n\t\t\t\tconsole.log('关闭成功');\n\t\t\t\t// ws.close()\n\t\t\t}\n\t\t\t// 监听并处理error事件\n\t\t\tws.onerror = function(error) {\n\t\t\t\tconsole.log(error);\n\t\t\t}\n\n\t\t},\n\t\tmethods: {\n\t\t\tlogout() {\n\t\t\t\tlet _this = this;\n\t\t\t\tdoLogout().then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.$store.commit('removeToken');\n\t\t\t\t\t\t_this.$router.push({ path: '/login' });\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\tonClickLogin() {\n\t\t\t\tlet _this = this;\n\t\t\t\tMessageUtils.confirm('确定跳转到登录界面？').then(() => {\n\t\t\t\t\t_this.$router.push({ path: '/login' });\n\t\t\t\t})\n\t\t\t},\n\t\t\tenterSearch() {\n\t\t\t\tif (this.$router.path != '/SearchTopic') {\n\t\t\t\t\tthis.$router.push({\n\t\t\t\t\t\tpath: '/SearchTopic',\n\t\t\t\t\t\tquery: { search: this.search }\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n</script>\n\n<style scoped>\n\t.wrapper {\n\t\twidth: 100%;\n\t\theight: 40px;\n\t\tposition: fixed;\n\t\ttop: 0px;\n\t\tleft: 0px;\n\t\toverflow: hidden;\n\t\tz-index: 100;\n\t\tbackground-color: raba(255, 255, 255, 0.85);\n\t}\n\n\t.wrapper>>>.el-input {\n\t\twidth: 200px;\n\t\tposition: fixed;\n\t\tright: 200px;\n\t\ttop: 4px;\n\t\theight: 30px;\n\t\topacity: 0.9;\n\t\ttransition: 0.7s;\n\t}\n\n\t.wrapper>>>.el-input:hover {\n\t\twidth: 280px;\n\t\ttransition: 0.7s;\n\t}\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/ShowTinymce/ShowTinymce.vue",
    "content": "<template>\n\n\t<div class=\"wrapper\" :style=\"myStyle\">\n\t\t<editor api-key=\"no-api-key\" :init=\"options\" v-model=\"Html\" />\n\t</div>\n</template>\n\n<script>\n\timport Editor from '@tinymce/tinymce-vue'\n\n\texport default {\n\t\tname: 'ShowTinymce',\n\t\tcomponents: {\n\t\t\t'editor': Editor\n\t\t},\n\t\tprops: {\n\t\t\twidth: {\n\t\t\t\ttype: String,\n\t\t\t\tdefault: '600px'\n\t\t\t},\n\t\t\tmargin: {\n\t\t\t\ttype: String,\n\t\t\t\tdefault: '0px'\n\t\t\t},\n\t\t},\n\t\tcomputed:{\n\t\t\tmyStyle(){\n\t\t\t\treturn {\n\t\t\t\t\twidth:this.width,\n\t\t\t\t\tmarginLeft:this.margin\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\toptions: {\n\t\t\t\t\t// plugins: 'lists link image table code help wordcount',\n\t\t\t\t\tplugins: '',\n\t\t\t\t\tlanguage: 'zh-Hans',\n\t\t\t\t\t// emoticons_database_url: '../../../tinymce/plugins/emoticons/js/emojis.js',\n\t\t\t\t\ttoolbar: [],\n\t\t\t\t\tmenubar:'',\n\t\t\t\t\theader:''\n\n\t\t\t\t},\n\t\t\t\tHtml: ''\n\t\t\t}\n\t\t},\n\t\tmethods:{\n\t\t\tgetHtml(){\n\t\t\t\t\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/TagCard/TagCard.css",
    "content": ".tags-container>>>.el-tag:hover {\n\tborder: 1px solid skyblue;\n}\n\n.tags-container>>>.el-tag:active {\n\tborder: 1px solid blue;\n}\n\n.tags-container>>>.el-tag:focus {\n\tborder: 1px solid blue !important;\n}\n\n.tags-container>>>.el-tag {\n\tdisplay: inline-block;\n\tmargin-top: 6px;\n\tmargin-right: 5px;\n\tmargin-left: auto;\n}\n\n"
  },
  {
    "path": "vue_acimage_web/src/components/TagCard/TagCard.vue",
    "content": "<template>\n\t<div>\n\t\t<el-card shadow=\"hover\" style=\"border-width:2px;\">\n\t\t\t<div slot=\"header\">\n\t\t\t\t<span class=\"c6\" style=\"color: rgb(250, 100, 180);\">\n\t\t\t\t\t<i class=\"el-icon-collection-tag\"></i>标签\n\t\t\t\t</span>\n\t\t\t\t<!-- <img src=\"static/image/user-rank-header.jpg\" style=\"width:100%;height: 80px;\"/> -->\n\t\t\t</div>\n\t\t\t<div class=\"tags-container\">\n\t\t\t\t<el-tag v-for=\"item in $store.state.tagList\" :id=\"'tag-btn'+item.id\" @click=\"handleClick(item.id)\"\n\t\t\t\t\tclass=\"hover-pointer\" :type=\"$global.buttonType(item.id)\" :key=\"item.id\">{{item.label}}</el-tag>\n\t\t\t</div>\n\t\t</el-card>\n\t</div>\n\n</template>\n\n<script>\n\texport default {\n\t\tname: 'TagCard',\n\t\tprops: {\n\t\t\tclickTag: {\n\t\t\t\ttype: Function\n\t\t\t}\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tactiveId: null\n\t\t\t}\n\t\t},\n\t\tmethods: {\n\t\t\thandleClick(tagId) {\n\t\t\t\t// this.clickTag(tagId);\n\t\t\t\tfor(let tag of this.$store.state.tagList){\n\t\t\t\t\tdocument.querySelector('#tag-btn' + tag.id).style.border = \"\"\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (this.activeId == tagId) {\n\t\t\t\t\tthis.activeId = null;\n\t\t\t\t} else {\n\t\t\t\t\tthis.activeId = tagId\n\t\t\t\t\tdocument.querySelector('#tag-btn' + this.activeId).style.border = \"1px solid blue\"\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tthis.clickTag(tagId)\n\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style scoped src=\"./TagCard.css\">\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/TopicCard/TopicCard.css",
    "content": ".wrapper {\n\twidth: 760px;\n\theight: 130px;\n\tdisplay: inline-block;\n\tposition: relative;\n\tborder-radius: 3px;\n\tbox-shadow: 1px 1px 6px #CCCCCC;\n\tbackground-color: white;\n}\n\n.wrapper:hover {\n\tbox-shadow: 1px 1px 6px #AAAAAA;\n\ttransform: translate(0, -2px);\n\ttransition: transform 0.2s;\n}\n\n.wrapper-left {\n\theight: 100%;\n\twidth: 135px;\n\tdisplay: inline-block;\n}\n\n/* .wrapper-left>img {\n\theight: 100%;\n\twidth: 120px;\n\tobject-fit: cover;\n\tborder-radius: 3px;\n\tdisplay: inline-block;\n} */\n.wrapper-left>>>.el-image {\n\theight: 100%;\n\twidth: 120px;\n\tobject-fit: cover;\n\tborder-radius: 3px;\n\tdisplay: inline-block;\n}\n\n.white-gradient {\n\tposition: absolute;\n\tleft: 61px;\n\ttop: 0px;\n\theight: 100%;\n\twidth: 60px;\n\tbackground: linear-gradient(to left, #fff, rgba(255, 255, 255, 0));\n}\n\n.wrapper-medium {\n\tposition: relative;\n\tdisplay: inline-block;\n\tvertical-align: top;\n\twidth: 475px;\n\theight: 100%;\n}\n\n.title {\n\tfont-size: 16px;\n\tmargin-top: 5px;\n\twidth: 475px;\n\theight: 20px;\n\toverflow: hidden;\n\tcolor: #333333;\n}\n\n.content {\n\tfont-size: 14px;\n\tcolor: #999999;\n\theight: 58px;\n\toverflow: hidden;\n\tmargin-top: 8px;\n}\n\n.info {\n\tposition: absolute;\n\tbottom: 5px;\n\tleft: 10px;\n}\n\n.username {\n\tdisplay: inline-block;\n\tposition: relative;\n\tbottom: 5px;\n\t/* \tmargin-bottom: auto; */\n\tcolor: #666666;\n\tmargin-left: 5px;\n\tfont-size: 14px;\n}\n\n.data {\n\tdisplay: inline-block;\n\tposition: relative;\n\tbottom: 5px;\n\t/* \tmargin-bottom: auto; */\n\tmargin-left: 5px;\n\tfont-size: 12px;\n\tcolor: #999999;\n}\n\n.wrapper-right {\n\tposition: relative;\n\tdisplay: inline-block;\n\tvertical-align: top;\n\theight: 100%;\n\twidth: 140px;\n\tpadding-left: 10px;\n}\n\n.category-container {\n\tmargin-top: 10px;\n\theight: 30px;\n}\n\n.tags-container {\n\tmargin-top: 10px;\n\n}\n\n.tags-container>>>.el-tag {\n\tmargin-top: 3px;\n\tmargin-right: 3px;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/components/TopicCard/TopicCard.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"wrapper hover-pointer\" @click=\"toTopic\">\n\t\t\t<div class=\"wrapper-left\">\n\t\t\t\t<el-image :src=\"coverImageUrl\" fit=\"cover\" lazy></el-image>\n\t\t\t\t<div class=\"white-gradient\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"wrapper-medium\">\n\t\t\t\t<div class=\"title\">\n\t\t\t\t\t<div v-if=\"titleHtml\" v-dompurify-html=\"title\"></div>\n\t\t\t\t\t<span v-else>{{title}}</span>\n\t\t\t\t\t<!-- {{$global.omitStr(title)}} -->\n\t\t\t\t</div>\n\t\t\t\t<div class=\"content\" v-if=\"contentHtml\" v-dompurify-html=\"$global.omitStr(html,contentLimit)\"> </div>\n\t\t\t\t<div class=\"content\" v-else>\n\t\t\t\t\t{{$global.omitStr(html,contentLimit)}}\n\t\t\t\t</div>\n\t\t\t\t<div class=\"info\">\n\t\t\t\t\t<div style=\"display: inline-block;\">\n\t\t\t\t\t\t<el-avatar :size=\"20\" :src=\"photoUrl\">\n\t\t\t\t\t\t</el-avatar>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"username\">\n\t\t\t\t\t\t{{username}}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"data\">\n\t\t\t\t\t\t{{timeLabel}} {{$global.timeView(updateTime)}}\n\t\t\t\t\t\t<template v-if=\"starCount != null\">\n\t\t\t\t\t\t\t·\n\t\t\t\t\t\t\t<i class=\"el-icon-star-off\"></i>\n\t\t\t\t\t\t\t{{starCount}}\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"commentCount != null\">\n\t\t\t\t\t\t\t·\n\t\t\t\t\t\t\t<i class=\"el-icon-chat-dot-square\"></i>\n\t\t\t\t\t\t\t{{commentCount}}\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"pageView != null\">\n\t\t\t\t\t\t\t·\n\t\t\t\t\t\t\t<i class=\"el-icon-view\"></i>\n\t\t\t\t\t\t\t{{pageView}}\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"wrapper-right\">\n\t\t\t\t<div class=\"category-container\">\n\t\t\t\t\t<el-tag effect=\"plain\" :type=\"$global.buttonType(categoryId)\" v-if=\"!$global.isEmpty(categoryId)\">\n\t\t\t\t\t\t{{$store.getters.categoryLabel(categoryId)}}\n\t\t\t\t\t</el-tag>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"tags-container\">\n\t\t\t\t\t<template v-for=\"id in tagIds\">\n\t\t\t\t\t\t<el-tag v-if=\"!$global.isEmpty($store.getters.tagLabel(id))\" effect=\"plain\"\n\t\t\t\t\t\t\t:type=\"$global.buttonType(id)\" :key=\"id\">\n\t\t\t\t\t\t\t{{$store.getters.tagLabel(id)}}\n\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\n</template>\n\n<script>\n\texport default {\n\t\tname: \"TopicCard\",\n\t\tprops: {\n\t\t\ttitle: {\n\t\t\t\ttype: String,\n\t\t\t\tdefault: 'sadhasdj'\n\t\t\t},\n\t\t\tcontent: {\n\t\t\t\ttype: String,\n\t\t\t},\n\t\t\thtml: {\n\t\t\t\ttype: String,\n\t\t\t},\n\t\t\tcontentLimit: {\n\t\t\t\ttype: Number,\n\t\t\t\tdefault: 500\n\t\t\t},\n\t\t\tusername: {},\n\t\t\tupdateTime: {},\n\t\t\ttimeLabel:{\n\t\t\t\tdefault:'更新于'\n\t\t\t},\n\t\t\tstarCount: {},\n\t\t\tcommentCount: {},\n\t\t\tpageView: {},\n\t\t\tphotoUrl: {},\n\t\t\tcoverImageUrl: {},\n\t\t\ttagIds: {\n\t\t\t\ttype: Array\n\t\t\t},\n\t\t\tcategoryId: {\n\t\t\t\ttype: Number\n\t\t\t},\n\t\t\tto: {},\n\t\t\tshowImage: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdefault: true\n\t\t\t},\n\t\t\ttitleHtml: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdefault: false\n\t\t\t},\n\t\t\tcontentHtml: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdefault: false\n\t\t\t}\n\n\t\t},\n\t\tmethods: {\n\t\t\ttoTopic() {\n\t\t\t\tthis.$router.push({ path: this.to });\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./TopicCard.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/TopicList/TopicList.css",
    "content": ".categories-container>>>.el-button {\n\tdisplay: inline-block;\n\tmargin-top: 6px;\n\tmargin-right: 4px;\n\tmargin-left: auto;\n}\n\n.category-card-div>>> .el-card{\n\tborder-width:2px;\n}\n\n.categories-container {\n\tpadding-bottom: 10px;\n}\n\n.icon-medal{\n\tposition: relative;\n\tfont-size: 20px;\n\ttop:4px;\n}\n\n.data-container{\n\tmargin-left: 20px;\n\tmargin-bottom: -5px;\n\tfont-size: 12px;\n}\n\n.item-container{\n\tfont-size: 14px;\n\tposition: relative;\n\tmargin-bottom: 5px;\n}\n\n.ml7{\n\tmargin-left: 7px;\n}"
  },
  {
    "path": "vue_acimage_web/src/components/TopicList/TopicList.vue",
    "content": "<template>\n\t<div class=\"topic-list-div\">\n\t\t<el-card shadow=\"hover\" style=\"border-width: 2px;\">\n\t\t\t<div slot=\"header\">\n\t\t\t\t<span :style=\"'color:'+labelColor\">{{label}}</span>\n\t\t\t</div>\n\t\t\t<div>\n\t\t\t\t<div v-for=\"(item,index) in topics\" :key=\"item.id\" class=\"item-container\">\n\t\t\t\t\t<i v-if=\"medalMode&&index<3\" :class=\"medalClass(index)\" :style=\"medalColor(index)\"></i>\n\t\t\t\t\t<el-link :href=\"'/#/topic/'+item.id\">\n\t\t\t\t\t\t<span v-if=\"!medalMode||index>=3\" class=\"ml7\">\n\t\t\t\t\t\t\t{{index+1}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t{{$global.omitStr(item.title,titleLimit)}}\n\t\t\t\t\t</el-link>\n\t\t\t\t\t<div v-if=\"showData\" class=\"data-container\">\n\t\t\t\t\t\t<i class=\"el-icon-star-off\"></i>{{item.starCount}}\n\t\t\t\t\t\t·<i class=\"el-icon-chat-dot-square\"></i>{{item.commentCount}}\n\t\t\t\t\t\t· <i class=\"el-icon-view\"></i>{{item.pageView}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</el-card>\n\t</div>\n\n</template>\n\n<script>\n\texport default {\n\t\tname: 'TopicList',\n\t\tprops: {\n\t\t\tshowData: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdefault: true\n\t\t\t},\n\t\t\tmedalMode: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdefault: false\n\t\t\t},\n\t\t\tlabel: {\n\t\t\t\ttype: String,\n\t\t\t\tdefault: '热门话题'\n\t\t\t},\n\t\t\tlabelColor:{\n\t\t\t\tdefault:'#666666'\n\t\t\t},\n\t\t\ttitleLimit: {\n\t\t\t\ttype: Number,\n\t\t\t\tdefault: 21\n\t\t\t},\n\t\t\ttopics: {\n\t\t\t\ttype: Array,\n\t\t\t\tdefault: () => {\n\t\t\t\t\treturn [{\n\t\t\t\t\t\t\tid: 1,\n\t\t\t\t\t\t\ttitle: '发家史客户端卡技术大家看爱打架深三等奖安徽的',\n\t\t\t\t\t\t\tcommentCount: 55,\n\t\t\t\t\t\t\tstarCount: 66,\n\t\t\t\t\t\t\tpageView: 77\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: 2,\n\t\t\t\t\t\t\ttitle: '发家史客户端卡技术大家看爱打架深',\n\t\t\t\t\t\t\tcommentCount: 55,\n\t\t\t\t\t\t\tstarCount: 66,\n\t\t\t\t\t\t\tpageView: 77\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: 3,\n\t\t\t\t\t\t\ttitle: '发家史客户端卡技术大家看爱打架深',\n\t\t\t\t\t\t\tcommentCount: 55,\n\t\t\t\t\t\t\tstarCount: 66,\n\t\t\t\t\t\t\tpageView: 77\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: 4,\n\t\t\t\t\t\t\ttitle: '发家史客户端卡技术大家看爱打架深',\n\t\t\t\t\t\t\tcommentCount: 55,\n\t\t\t\t\t\t\tstarCount: 66,\n\t\t\t\t\t\t\tpageView: 77\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\tmedalColor() {\n\t\t\t\treturn (index) => {\n\t\t\t\t\tif (index == 0) {\n\t\t\t\t\t\treturn { color: 'gold' };\n\t\t\t\t\t} else if (index == 1) {\n\t\t\t\t\t\treturn { color: 'silver' };\n\t\t\t\t\t} else if (index == 2) {\n\t\t\t\t\t\treturn { color: '#D38B0D' }\n\t\t\t\t\t}\n\t\t\t\t\treturn {};\n\t\t\t\t};\n\t\t\t},\n\t\t\tmedalClass() {\n\t\t\t\treturn (index) => {\n\t\t\t\t\tconst commonClass = 'icon-medal';\n\t\t\t\t\tif (index == 0) {\n\t\t\t\t\t\treturn 'el-icon-medal-1' + ' ' + commonClass;\n\t\t\t\t\t} else if (index == 1 || index == 2) {\n\t\t\t\t\t\treturn 'el-icon-medal' + ' ' + commonClass;\n\t\t\t\t\t}\n\t\t\t\t\treturn {};\n\t\t\t\t}\n\t\t\t}\n\n\t\t},\n\t\tmethods: {\n\t\t\tclickTopicLink(id) {\n\t\t\t\tthis.$router.push({ path: '/topic/' + id });\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style scoped src=\"./TopicList.css\">\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/components/TopicRank/TopicRank.css",
    "content": ".wrapper{\n\tbox-shadow:0px 0px 5px #CCCCCC;\n\tborder-radius: 3px;\n}\n\n.header{\n\tposition: relative;\n\ttext-align: center;\n\tborder-radius: 4px 4px 0px 0px;\n\toverflow: hidden;\n\theight:134px;\n}\n\n.header img {\n\twidth: 100%;\n\theight: 100px;\n\tborder-radius: 3px;\n\tobject-fit: cover;\n\tz-index: 5;\n}\n\n.white-gradient {\n\tposition: absolute;\n\ttop: 20px;\n\theight: 80px;\n\twidth: 100%;\n\tz-index:8;\n\tbackground: linear-gradient(to top, rgba(255, 255, 255, 0.92),rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0));\n}\n\n.header-item{\n\theight: 34px;\n\tbackground-color: #CCCCCC;\n\tdisplay: inline-block;\n\ttext-align: center;\n\tcolor:white;\n}\n\n.label{\n\tmargin-top:5px;\n\tfont-size: 16px;\n}\n\n.body{\n\tpadding-left:20px;\n\tpadding-bottom: 10px;\n\tpadding-top:15px;\n\tbackground-color: white;\n}\n\n.image-container{\n\twidth:90px;\n\theight:60px;\n\tborder-radius: 5px;\n\tposition:relative;\n\tdisplay:inline-block;\n}\n\n.image-container>>>.el-image{\n\tborder-radius: 4px;\n\twidth:90px;\n\theight:60px\n}\n\n.image-index{\n\tbackground-color:#FF6C94;\n\tposition: absolute;\n\ttop:0px;\n}\n\n.index{\n\twidth:18px;\n\theight:19px;\n\tcolor:white;\n\ttext-align: center;\n\tborder-radius: 2px;\n}\n\n.front-title{\n\tdisplay: inline-block;\n\toverflow: hidden;\n\tvertical-align: top;\n\tmargin-left: 5px;\n\twidth: calc(100% - 100px)\n}\n\n.hover-bg-red:hover{\n\tbackground-color:#FF566A !important;\n}\n\n"
  },
  {
    "path": "vue_acimage_web/src/components/TopicRank/TopicRank.vue",
    "content": "<template>\n\t<div>\n\t\t<!-- <el-card shadow=\"hover\" style=\"border-width: 2px;\"> -->\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"header\">\n\t\t\t\t<div style=\"height: 100px;\">\n\t\t\t\t\t<img :src=\"headerSrc\" />\n\t\t\t\t\t<div class=\"white-gradient\"></div>\n\t\t\t\t</div>\n\n\t\t\t\t<div :id=\"'header-item'+index\" v-for=\"(label,index) in labels\" :key=\"index\" :style=\"headerItemStyle\"\n\t\t\t\t\t@mouseenter=\"activeIndex=index\" class=\"header-item hover-pointer hover-bg-red\">\n\t\t\t\t\t<div class=\"label\">{{label}}</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"body\">\n\t\t\t\t<div v-for=\"(topic,index) in topics\" :key=\"topic.id\" class=\"item-container\">\n\t\t\t\t\t<template v-if=\"index<endIndex\">\n\t\t\t\t\t\t<div class=\"image-container\">\n\t\t\t\t\t\t\t<el-image fit=\"cover\" lazy :src=\"topic.coverImageUrl\"></el-image>\n\t\t\t\t\t\t\t<div class=\"index image-index\">\n\t\t\t\t\t\t\t\t{{index+1}}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"front-title\">\n\t\t\t\t\t\t\t<el-link :href=\"'/#/topic/'+topic.id\" :underline=\"false\">\n\t\t\t\t\t\t\t\t{{$global.omitStr(topic.title,titleLimit+6)}}\n\t\t\t\t\t\t\t</el-link>\n\t\t\t\t\t\t\t<br />\n\t\t\t\t\t\t\t<span style=\"color:#666666;font-size: 12px;\">\n\t\t\t\t\t\t\t\t<i class=\"el-icon-star-off\"></i>{{topic.starCount}}\n\t\t\t\t\t\t\t\t· <i class=\"el-icon-chat-dot-square\"></i>{{topic.commentCount}}\n\t\t\t\t\t\t\t\t· <i class=\"el-icon-view\"></i>{{topic.pageView}}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t<el-link :href=\"'/#/topic/'+topic.id\" :underline=\"false\">\n\t\t\t\t\t\t\t<div class=\"index\" style=\"background-color:#CCCCCC;display:inline-block;\">\n\t\t\t\t\t\t\t\t{{index+1}}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{{$global.omitStr(topic.title,titleLimit)}}\n\t\t\t\t\t\t</el-link>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- </el-card> -->\n\t\t</div>\n\t</div>\n\n</template>\n\n<script>\n\texport default {\n\t\tname: 'TopicList',\n\t\tprops: {\n\t\t\thandleTabHover: {\n\t\t\t\ttype: Function,\n\t\t\t\tdefault: null\n\t\t\t},\n\t\t\tendIndex: {\n\t\t\t\tdefault: 3\n\t\t\t},\n\t\t\theaderSrc: {\n\t\t\t\tdefault: 'static/image/topic-rank.webp'\n\t\t\t},\n\t\t\tshowData: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdefault: true\n\t\t\t},\n\t\t\tlabels: {\n\t\t\t\ttype: Array,\n\t\t\t},\n\t\t\ttitleLimit: {\n\t\t\t\ttype: Number,\n\t\t\t\tdefault: 17\n\t\t\t},\n\t\t\ttopics: {\n\t\t\t\ttype: Array,\n\t\t\t\tdefault: () => {\n\t\t\t\t\treturn [\n\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tactiveIndex: null\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tthis.activeIndex = 0\n\t\t\tdocument.querySelector(\"#header-item0\").style.backgroundColor = \"#FF566A\"\n\t\t},\n\t\twatch: {\n\t\t\tactiveIndex: {\n\t\t\t\thandler(newVal, oldVal) {\n\t\t\t\t\tfor (let i = 0; i < this.labels.length; i++) {\n\t\t\t\t\t\tdocument.querySelector(\"#header-item\" + i).style.backgroundColor = \"#CCCCCC\";\n\t\t\t\t\t}\n\t\t\t\t\tdocument.querySelector(\"#header-item\" + newVal).style.backgroundColor = \"#FF566A\";\n\t\t\t\t\tif (this.handleTabHover !== null) {\n\t\t\t\t\t\tthis.handleTabHover(this.activeIndex);\n\t\t\t\t\t}\n\n\t\t\t\t},\n\t\t\t\timmediate: false\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\theaderItemStyle() {\n\t\t\t\tlet width = 100 / this.labels.length;\n\t\t\t\treturn { width: width + '%' };\n\t\t\t},\n\t\t\tmedalColor() {\n\t\t\t\treturn (index) => {\n\t\t\t\t\tif (index == 0) {\n\t\t\t\t\t\treturn { color: 'gold' };\n\t\t\t\t\t} else if (index == 1) {\n\t\t\t\t\t\treturn { color: 'silver' };\n\t\t\t\t\t} else if (index == 2) {\n\t\t\t\t\t\treturn { color: '#D38B0D' }\n\t\t\t\t\t}\n\t\t\t\t\treturn {};\n\t\t\t\t};\n\t\t\t},\n\t\t\tmedalClass() {\n\t\t\t\treturn (index) => {\n\t\t\t\t\tconst commonClass = 'icon-medal';\n\t\t\t\t\tif (index == 0) {\n\t\t\t\t\t\treturn 'el-icon-medal-1' + ' ' + commonClass;\n\t\t\t\t\t} else if (index == 1 || index == 2) {\n\t\t\t\t\t\treturn 'el-icon-medal' + ' ' + commonClass;\n\t\t\t\t\t}\n\t\t\t\t\treturn {};\n\t\t\t\t}\n\t\t\t}\n\n\t\t},\n\t\tmethods: {\n\t\t\tclickTopicLink(id) {\n\t\t\t\tthis.$router.push({ path: '/topic/' + id });\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style scoped src=\"./TopicRank.css\">\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/config.js",
    "content": "export default{\n\tdomainOfImages:'http://rof8epeiz.hn-bkt.clouddn.com/',\n}"
  },
  {
    "path": "vue_acimage_web/src/main.js",
    "content": "import Vue from 'vue'\nimport App from './App.vue'\nimport router from '@/router'\nimport store from '@/store'\n\nimport axios from 'axios'\nVue.prototype.axios = axios\n\nimport ElementUI from 'element-ui';\nimport 'element-ui/lib/theme-chalk/index.css';\nVue.use(ElementUI);\n\nimport {Notification} from 'element-ui';\nVue.prototype.$notify = Notification;\n\nimport global from '@/utils/global.js';\nVue.prototype.$global=global\n\n//防xss攻击\nimport VueDOMPurifyHTML from 'vue-dompurify-html'\nVue.use(VueDOMPurifyHTML)\n\nVue.config.productionTip = false\nif(process.env.VUE_APP_MOCK){\n\trequire('../mock')\n}\n\n\nnew Vue({\n  render: h => h(App),\n  router,\n  store\n}).$mount('#app')\n"
  },
  {
    "path": "vue_acimage_web/src/router/index.js",
    "content": "import Vue from 'vue'\nimport VueRouter from 'vue-router'\nVue.use(VueRouter)\n\n\nimport Home from '@/views/Home/Home.vue'\nimport Login from '@/views/Login/Login.vue'\nimport PublishTopic from '@/views/PublishTopic/PublishTopic.vue'\nimport MyProfile from '@/views/MyProfile/MyProfile.vue'\nimport MyActivity from '@/views/MyActivity/MyActivity.vue'\nimport MyMessage from '@/views/MyMessage/MyMessage.vue'\nimport Forum from '@/views/Forum/Forum.vue'\nimport About from '@/views/About/About.vue'\nimport SearchImage from '@/views/SearchImage/SearchImage.vue'\nimport SearchTopic from '@/views/SearchTopic/SearchTopic.vue'\n\n\nconst router = new VueRouter({\n\troutes: [\n\t\t{\n\t\t\tpath: '/',\n\t\t\tcomponent: Home\n\t\t},\n\t\t{\n\t\t\tpath: '/login',\n\t\t\tcomponent: Login\n\t\t},\n\t\t{\n\t\t\tpath: '/publish',\n\t\t\tcomponent: PublishTopic\n\t\t},\n\t\t{\n\t\t\tpath: '/topic/:id',\n\t\t\tcomponent: ()=>import('@/views/TopicInfo/TopicInfo.vue')\n\t\t},\n\t\t{\n\t\t\tpath: '/profile',\n\t\t\tcomponent: MyProfile\n\t\t},\n\t\t{\n\t\t\tpath: '/MyActivity',\n\t\t\tcomponent: MyActivity\n\t\t},\n\t\t{\n\t\t\tpath: '/MyMessage',\n\t\t\tcomponent: MyMessage\n\t\t},\n\t\t{\n\t\t\tpath: '/forum',\n\t\t\tcomponent: Forum\n\t\t},\n\t\t{\n\t\t\tpath: '/SearchTopic',\n\t\t\tcomponent: SearchTopic\n\t\t},\n\t\t{\n\t\t\tpath: '/SearchImage',\n\t\t\tcomponent: SearchImage\n\t\t},\n\t\t{\n\t\t\tpath: '/about',\n\t\t\tcomponent: About\n\t\t},\n\n\t\t\n\t]\n})\n\nexport default router\n"
  },
  {
    "path": "vue_acimage_web/src/store/index.js",
    "content": "import Vuex from 'vuex'\nimport Vue from 'vue'\nimport Config from '@/config.js'\nimport CommonUtils from '@/utils/CommonUtils.js'\n\nimport jwtDecode from \"jwt-decode\";\nimport { queryAllCategories } from \"@/api/category.js\"\nimport { queryAllTags } from \"@/api/tag.js\"\nimport { Code } from '@/utils/result.js'\n\n//应用vuex插件\nVue.use(Vuex)\n\nexport default new Vuex.Store({\n\t//数据，相当于data\n\tstate: {\n\t\tuserId: '',\n\t\tusername: '',\n\t\tphotoUrl: '',\n\t\ttoken: '',\n\t\tcategoryList: [],\n\t\ttagList: [],\n\t\tmessageNum: null,\n\t\thasWebSocket:false,\n\t\t//elementui五种按钮类型\n\t\t// types: ['success', 'primary', 'info', 'warning', 'danger']\n\t},\n\tgetters: {\n\t\tgetPhotoUrl(state) {\n\t\t\tif (!CommonUtils.isEmpty(state.photoUrl)) {\n\t\t\t\treturn state.photoUrl;\n\t\t\t}\n\t\t\treturn 'static/image/default-photo.webp';\n\t\t},\n\t\tcategoryLabel(state) {\n\t\t\treturn (id) => {\n\t\t\t\tfor (let item of state.categoryList) {\n\t\t\t\t\tif (item.id == id) {\n\t\t\t\t\t\treturn item.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\ttagLabel(state) {\n\t\t\treturn (id) => {\n\t\t\t\tfor (let item of state.tagList) {\n\t\t\t\t\tif (item.id == id) {\n\t\t\t\t\t\treturn item.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tisLogin(state) {\n\t\t\tlet token = localStorage.getItem(\"token\");\n\t\t\treturn !CommonUtils.isEmpty(token);\n\t\t}\n\n\t},\n\t//里面定义方法，操作state方发\n\tmutations: {\n\t\tinit(state) {\n\t\t\tstate.token = localStorage.getItem(\"token\");\n\t\t\ttry {\n\t\t\t\tstate.userId = jwtDecode(state.token).userId;\n\t\t\t\tstate.userName = jwtDecode(state.token).userName;\n\t\t\t\tstate.photoUrl = jwtDecode(state.token).photoUrl;\n\t\t\t} catch (err) {\n\t\t\t\tstate.userId = '';\n\t\t\t\tstate.username = '';\n\t\t\t\tstate.photoUrl = '';\n\t\t\t}\n\t\t\t//查询分类\n\t\t\tqueryAllCategories().then(res => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\tstate.categoryList = res.data;\n\t\t\t\t}\n\t\t\t});\n\t\t\t//查询标签\n\t\t\tqueryAllTags().then(res => {\n\t\t\t\tstate.tagList = res.data;\n\t\t\t});\n\t\t},\n\t\tsetToken(state, token) {\n\t\t\tlocalStorage.setItem(\"token\", token);\n\t\t\tthis.commit('init')\n\t\t},\n\t\tremoveToken(state) {\n\t\t\tlocalStorage.removeItem(\"token\");\n\t\t\tthis.commit('init')\n\t\t}\n\t},\n\t// 操作异步操作mutation\n\tactions: {\n\n\t},\n\tmodules: {\n\n\t},\n})\n"
  },
  {
    "path": "vue_acimage_web/src/utils/CommonUtils.js",
    "content": "import MessageUtils from '@/utils/MessageUtils'\nimport { Code } from \"@/utils/result.js\"\nexport default {\n\tisEmpty(object) {\n\t\tif (object == undefined || object == '' || object == null || object == {} || object == []) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tpopMsgAndRefreshIfOk(request, msg, delaySeconds) {\n\t\trequest.then(result => {\n\t\t\tconsole.log(result);\n\t\t\tif (result.code == Code.OK) {\n\t\t\t\tMessageUtils.success(msg);\n\t\t\t\tif (!this.isEmpty(delaySeconds)) {\n\t\t\t\t\tthis.delayRefresh(delaySeconds);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t},\n\tpopMsgIfOk(request, msg) {\n\t\tthis.popMsgAndRefreshIfOk(request, msg);\n\t},\n\n\tdelayRefresh(delaySeconds) {\n\t\tsetTimeout(() => {\n\t\t\tlocation.reload();\n\t\t}, delaySeconds * 1000);\n\t},\n\n\tgetUrlParams(url) {\n\t\tlet firstIndex = url.indexOf('?');\n\t\tif (firstIndex == -1) return;\n\t\tlet argStr = url.slice(firstIndex + 1, url.length);\n\n\t\tlet args = argStr.split(\"&\");\n\t\tlet params = {};\n\t\tfor (let i = 0; i < args.length; i++) {\n\t\t\tlet keyAndValues = args[i].split(\"=\");\n\t\t\tparams[keyAndValues[0]] = keyAndValues[1];\n\t\t}\n\t\treturn params;\n\t},\n\n\n\ttoFormData(object) {\n\t\tconst formData = new FormData()\n\t\tObject.keys(object).forEach(key => {\n\t\t\tconst value = object[key]\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tvalue.forEach((subValue, i) =>\n\t\t\t\t\tformData.append(key + `[${i}]`, subValue)\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tformData.append(key, object[key])\n\t\t\t}\n\t\t})\n\t\treturn formData\n\t},\n\t\n\tcopyPropertiesTo(source,target){\n\t\tObject.keys(source).forEach(key => {\n\t\t\tconst value = source[key]\n\t\t\tif(target[key]!=undefined){\n\t\t\t\ttarget[key]=value\n\t\t\t}\t\t\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "vue_acimage_web/src/utils/MessageUtils.js",
    "content": "import {\n\tMessage,\n\tMessageBox,\n\tLoading,\n\tNotification\n} from 'element-ui';\nexport default{\n\tnotice(msg,durationSeconds,type){\n\t\tlet newType=type||'warning';\n\t\tlet newDuration=durationSeconds||4;\n\t\tNotification({\n\t\t\ttitle: '提示',\n\t\t\tmessage: msg,\n\t\t\ttype: newType,\n\t\t\tduration: newDuration*1000\n\t\t});\n\t},\n\tsuccess(msg,durationSeconds){\n\t\tlet newDuration=durationSeconds||4;\n\t\tNotification({\n\t\t\ttitle: '成功',\n\t\t\tmessage: msg,\n\t\t\ttype: 'success',\n\t\t\tduration: newDuration*1000\n\t\t});\n\t},\n\tconfirm(msg){\n\t\treturn MessageBox.confirm(msg, '提示', {\n\t\t\tconfirmButtonText: '确定',\n\t\t\tcancelButtonText: '取消',\n\t\t\ttype: 'warning'\n\t\t})\n\t}\n\n}"
  },
  {
    "path": "vue_acimage_web/src/utils/StringUtils.js",
    "content": "export default {\n\thtml2Text(htmlString) {\n\t\treturn htmlString.toString().replace(/<(style|script|iframe)[^>]*?>[\\s\\S]+?<\\/\\1\\s*>/gi, '')\n\t\t\t.replace(/<[^>]+?>/g, '')\n\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t.replace(/ /g, ' ')\n\t\t\t.replace(/>/g, ' ')\n\t\t\t.replace(/&nbsp;/gm,' ');\n\t}\n}\n"
  },
  {
    "path": "vue_acimage_web/src/utils/global.js",
    "content": "import Vue from 'vue'\nimport cookie from 'vue-cookie'\nimport CommonUtils from '@/utils/CommonUtils.js'\nimport MessageUtils from '@/utils/MessageUtils.js'\nimport StringUtls from '@/utils/StringUtils.js'\n\nimport Config from '@/config.js'\nimport { Code } from '@/utils/result.js'\nimport { MessageBox } from 'element-ui'\nimport jwtDecode from \"jwt-decode\";\nimport StringUtils from '@/utils/StringUtils.js'\n\nlet global = new Vue();\n\n\n\n// global.trueImageUrl = function(url) {\n// \tif (CommonUtils.isEmpty(url) || url == '#') {\n// \t\treturn '#';\n// \t}\n// \treturn global.baseImageUrl + url;\n// }\n\n//获取头像路径\nglobal.getPhotoUrl = function(url) {\n\tif (CommonUtils.isEmpty(url) || url == '#') {\n\t\treturn 'static/image/default-photo.webp';\n\t}\n\treturn url;\n}\n\n//获取分享链接\nglobal.getTopicUrl = function(topicId) {\n\treturn '/topic/' + topicId;\n}\n\nglobal.isEmpty = function(object) {\n\treturn CommonUtils.isEmpty(object);\n}\n\nglobal.buttonType = function(id) {\n\tlet types = ['success', 'primary', 'warning', 'danger'];\n\treturn types[id % 4];\n}\n\n//限制显示长度\nglobal.omitStr = function(content, totalLength) {\n\tif (content.length > totalLength) {\n\t\treturn content.slice(0, totalLength - 2) + '...'\n\t} else {\n\t\treturn content;\n\t}\n}\n\nglobal.timeView = function(datetime) {\n\tlet d1 = new Date(datetime);\n\tlet d2 = new Date();\n\tlet duration = d2.getTime() - d1.getTime();\n\n\tlet time = parseInt(Math.floor(duration / (1000 * 60 * 60 * 24 * 30)));\n\tif (time >= 1) {\n\t\treturn d1.getFullYear()+'-'+(d1.getMonth()+1)+'-'+d1.getDate();\n\t}\n\n\ttime = parseInt(Math.floor(duration / (1000 * 60 * 60 * 24)));\n\tif (time == 1) {\n\t\treturn '昨天';\n\t} else if (time > 1) {\n\t\treturn time + '天前';\n\t}\n\n\ttime = parseInt(Math.floor(duration / (1000 * 60 * 60)));\n\tif (time >= 1) {\n\t\treturn time + '小时前'\n\t}\n\n\ttime = parseInt(Math.floor(duration / (1000 * 60)));\n\tif (time >= 1) {\n\t\treturn time + '分钟前'\n\t}\n\n\ttime = parseInt(Math.floor(duration / (1000)));\n\tif (time >= 1) {\n\t\treturn time + '秒前'\n\t}\n\n\treturn '刚刚'\n}\n\nglobal.html2Text = function(contentHtml, limitLength) {\n\tlet content = StringUtils.html2Text(contentHtml);\n\tif (limitLength == undefined) {\n\t\treturn content;\n\t}\n\tif (content.length > limitLength) {\n\t\treturn content.slice(0, limitLength - 3) + '...'\n\t} else {\n\t\treturn content;\n\t}\n}\n\nexport default global;\n"
  },
  {
    "path": "vue_acimage_web/src/utils/request.js",
    "content": "import axios from 'axios';\nimport { Message, MessageBox, Loading, Notification } from 'element-ui';\nimport MessageUtils from '@/utils/MessageUtils'\nimport { Code } from '@/utils/result.js';\nimport store from '@/store'\n\n// const service = axios.create({\n// \tbaseURL: \"http://127.0.0.1:8080/projectName\",//请求地址前缀\n// \ttimeout: 0\n// });\nconst service = axios.create();\n\nvar requestNum = 0;\nvar loading = null;\n\n// 请求拦截器\nservice.interceptors.request.use(\n\tconfig => {\n\t\t//添加请求头部参数\n\t\t// config.headers['arg1'] = \"arg1Value\";\n\n\t\t//开始loading\n\t\tconfig.headers['authorization'] = store.state.token;\n\t\trequestNum++;\n\t\tif (loading == null) {\n\t\t\tloading = Loading.service({ fullscreen: true, text: '正在努力加载中~', background: 'rgba(250, 250, 250, 0.5)' });\n\t\t\t// setTimeout(() => { loading.close(); }, 1);\n\t\t} else if (loading != null && requestNum > 0) {\n\t\t\tloading = Loading.service({ fullscreen: true, text: '正在努力加载中~',background: 'rgba(250, 250, 250, 0.5)'  });\n\t\t}\n\t\treturn config;\n\t},\n\terror => {\n\t\trequestNum = 0;\n\t\tif (loading) {\n\t\t\tloading.close();\n\t\t}\n\t\treturn Promise.reject(error);\n\t}\n);\n\n// 响应拦截器\nservice.interceptors.response.use(\n\tresponse => {\n\t\t//拦截到成功的数据\n\t\trequestNum--;\n\t\tif (loading == null || requestNum <= 0) {\n\t\t\tloading.close()\n\t\t}\n\t\tconst result = response.data;\n\t\tif (result.code == Code.ERR) {\n\t\t\tMessageUtils.notice(result.msg, 2)\n\t\t} else if (result.code == Code.OK) {\n\t\t\treturn response.data;\n\t\t} else {\n\t\t\t// 出错了直接关闭loading\n\t\t\trequestNum = 0\n\t\t\tloading.close();\n\t\t}\n\t},\n\terror => {\n\t\t//拦截到失败的数据\n\t\tconsole.log('错误码', error)\n\t\tif (error.response.status == 401) {\n\t\t\tMessageUtils.notice('(￣_,￣ )请登录或成为认证用户后再操作')\n\t\t} else if(error.response.status == 429){\n\t\t\tMessageUtils.notice('系统繁忙┗( T﹏T )┛')\n\t\t} else {\n\t\t\tMessageUtils.notice(\"出错了o(≧口≦)o，状态码\"+error.response.status)\n\t\t}\n\t\t// 出错了直接关闭loading\n\t\trequestNum = 0;\n\t\tloading.close();\n\t\treturn Promise.reject(error);\n\t}\n);\n\nexport default service;\n"
  },
  {
    "path": "vue_acimage_web/src/utils/result.js",
    "content": "// 状态码\nexport let Code = {\n\tOK: 20000,\n\tERR: 20001,\n\tDATA_NOT_EXIST: 20011\n}\n"
  },
  {
    "path": "vue_acimage_web/src/utils/utils.js",
    "content": "\n"
  },
  {
    "path": "vue_acimage_web/src/views/About/About.css",
    "content": "body{\n\tbackground-color: #F5F5F5;\n\tmargin:0px;\n\tpadding:0px;\n}\n\n.upload-plus-disabled .el-upload--picture-card {\n\tdisplay: none;\n}\t\t\t\t\t\t\n\n.main{\n\twidth:1040px;\n\tmargin-left: calc( (100% - 510px - 520px) / 2 );\n\tmargin-top: 15px;\n\tdisplay: block;\n\tbackground-color: white;\n\tborder-radius: 5px;\n\tbox-shadow:0px 0px 5px #CCCCCC;\n\tpadding-bottom: 200px;\n}\n\n.header{\n\ttop:5px;\n\tposition:relative;\n\theight:60px;\n\ttext-align: center;\n}\n\n.header-title{\n\tposition:relative;\n\ttop:-6px;\n\tdisplay: inline-block;\n\tfont-size:25px;\n\tcolor:#999999;\n}\n\n.header-icon{\n\tfont-size:40px;\n\tcolor:#E5E544;\n}\n\n.upload-container{\n\twidth:400px;\n\tmargin-left: calc(50% - 400px / 2);\n}\n\n.upload-hint{\n\tcolor:red;\n\tfont-size:15px;\n\tfont-family:'Times New Roman', Times, serif;\n\tmargin-bottom:5px;\n}\n\n.search-result-container{\n\tmargin-top:20px;\n\tmargin-left:100px;\n\tmargin-right:100px;\n}\n\n.result-image-container{\n\tmargin-right:10px;\n\theight:250px;\n\twidth:150px;\n\tdisplay:inline-block;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/About/About.vue",
    "content": "<template>\n\t<div id=\"app\">\n\t\t<my-header></my-header>\n\t\t<div class=\"main\">\n\t\t\t<div class=\"header\">\n\t\t\t\t<i class=\"el-icon-info header-icon\" style=\"\"></i>\n\t\t\t\t<div class=\"header-title\">关于本站</div>\n\t\t\t\t<div style=\"margin-top:-20px;\">\n\t\t\t\t\t<el-divider direction=\"horizontal\"></el-divider>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div style=\"padding:80px;color:#666666\">\n\t\t\t\t一. 本站GitHub地址\n\t\t\t\t<br />\n\t\t\t\t\n\t\t\t\t<el-link href=\"https://github.com/ggggborn/SpringCloud-acimage\" target=\"_blank\">\n\t\t\t\t\t<el-image src=\"static/image/github.webp\" style=\"width: 20px; height: 20px \" fit=\"cover\"></el-image>\n\t\t\t\t\thttps://github.com/ggggborn/SpringCloud-acimage\n\t\t\t\t</el-link>\n\t\t\t\t<br />\n\t\t\t\t喜欢的话进去来个<i class=\"el-icon-star-on\" style=\"color:orange\"></i>吧 o(￣▽￣)ｄ\n\t\t\t\t<br />\n\t\t\t\t欢迎大家加群692992463（进群备注：ac）交流本项目✧(≖ ◡ ≖✿)\n\t\t\t\t<br />\n\t\t\t\t<br />\n\t\t\t\t\n\t\t\t\t二. 网站规则\n\t\t\t\t<br />\n\t\t\t\t为了防止乱发言和被攻击，网站随时会调整访客和用户的权限，有可能导致某些接口不可用┑(￣Д ￣)┍\n\t\t\t\t<br />\n\t\t\t\t为了防止恶意刷屏，本站对每天发表话题/评论和修改话题/评论次数都有限制（10-20）次之间\n\t\t\t\t<br />\n\t\t\t\t如果想进一步得到更多权限，请加qq群692992463（进群备注：ac），找群主索要\n\t\t\t\t<br />\n\t\t\t\t<br />\n\t\t\t\t\n\t\t\t\t三. 请大家文明发言，谢谢。\n\t\t\t\t<br />\n\t\t\t\t<br />\n\t\t\t\t\n\t\t\t\t四. 目前功能比较少，而且由于个人精力有限，之后我会不断完善优化。\n\t\t\t\t\n\t\t\t\t\n\t\t\t</div>\n\t\t</div>\n\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'AboutA',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\n\t\t\t};\n\t\t},\n\t\tmounted() {\n\n\t\t},\n\t\tmethods: {\n\n\t\t}\n\n\n\t}\n</script>\n\n<style src=\"./About.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/Forum/Forum.css",
    "content": ".forum-div {\n\t/* background-color: #FBFBFB; */\n\tbackground-color: #F9FBFB;\n\tmargin: 0px;\n\tpadding: 0px;\n}\n\n.upload-plus-disabled .el-upload--picture-card {\n\tdisplay: none;\n}\n\n.wrapper {\n\twidth: 1130px;\n\tmargin-left: calc((100% - 1120px) / 2);\n\tmargin-top: 15px;\n\tdisplay: block;\n\tpadding-bottom: 100px;\n\tborder-radius: 5px;\n}\n\n.wrapper-left {\n\twidth: 760px;\n\tdisplay: inline-block;\n\t/* \tbackground-color: white; */\n\tbackground-color: rgba(0, 0, 0, 0);\n\t/* \tborder-radius: 5px;\n\tbox-shadow:0px 0px 5px #CCCCCC; */\n\tpadding-bottom: 10px;\n}\n\n\n\n.wrapper-header {\n\ttop: 5px;\n\tposition: relative;\n\tborder-radius: 5px;\n\t/* height: 44px; */\n\theight: 350px;\n\t/* text-align: center; */\n\tmargin-bottom: 20px;\n}\n\n.mask-images-container{\n\twidth:520px;\n\tmargin-left:10px;\n\tdisplay: inline-block;\n\tvertical-align: top;\n}\n\n.mask-images-container>div{\n\tdisplay: inline-block;\n}\n\n.wrapper-header-icon {\n\tfont-size: 40px;\n\tcolor: #E5E544;\n}\n\n.wrapper-header-title {\n\tposition: relative;\n\ttop: -6px;\n\tdisplay: inline-block;\n\tfont-size: 25px;\n\tcolor: #999999;\n}\n\n.wrapper-right {\n\twidth: 340px;\n\tdisplay: inline-block;\n\tvertical-align: top;\n\t/* \tborder-radius: 5px;\n\tbox-shadow: 0px 0px 5px #CCCCCC; */\n\tbackground-color: rgba(0, 0, 0, 0);\n\t/* \t\tpadding-bottom: 200px; */\n\tmargin-left: 30px;\n}\n\n\n.mt10 {\n\tmargin-top: 10px;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/* \n.user-rank-header {\n\tfont-size: 22px;\n\tcolor: skyblue;\n\ttext-align: center;\n\tmargin-top: 10px;\n}\n\n.topic-item {\n\tmargin-bottom: -20px;\n\tmargin-top: -20px;\n\tpadding-left: 10px;\n\tpadding-right: 10px;\n}\n\n.item-medium {\n\tdisplay: inline-block;\n\tvertical-align: top;\n\tmargin-left: 10px;\n\twidth: 500px;\n}\n\n.topic-title {\n\tcolor: #666666;\n\tfont-size: 18px;\n}\n\n.topic-data {\n\tcolor: #999999;\n\tfont-size: 14px;\n}\n\n.topic-content {\n\tmargin-top: 2px;\n\tcolor: #999999;\n\tfont-size: 16px;\n}\n\n.item-right {\n\tdisplay: inline-block;\n\tvertical-align: top;\n}\n\n.item-right-image {\n\theight: 100px;\n\twidth: 100px;\n\tborder-radius: 3px;\n\tmargin-top: 4px;\n} */\n"
  },
  {
    "path": "vue_acimage_web/src/views/Forum/Forum.vue",
    "content": "<template>\n\t<div class=\"forum-div\">\n\t\t<my-header></my-header>\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"wrapper-header\">\n\t\t\t\t<div style=\"display: inline-block;width:598px\">\n\t\t\t\t\t<home-carousel></home-carousel>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"mask-images-container\">\n\t\t\t\t\t<div v-for=\"topic in recommendTopics\" :key=\"topic.id\">\n\t\t\t\t\t\t<router-link :key=\"topic.id\" :to=\"$global.getTopicUrl(topic.id)\" :underline=\"false\">\n\t\t\t\t\t\t\t<mask-image :image-url=\"topic.coverImageUrl\" :title=\"topic.title\"\n\t\t\t\t\t\t\t\t:star-count=\"topic.starCount\" :page-view=\"topic.pageView\"\n\t\t\t\t\t\t\t\t:commentCount=\"topic.commentCount\" :username=\"topic.user.username\">\n\t\t\t\t\t\t\t</mask-image>\n\t\t\t\t\t\t</router-link>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"wrapper-left\">\n\t\t\t\t<el-skeleton v-if=\"loading\" :rows=\"6\" animated />\n\t\t\t\t<template v-else>\n\t\t\t\t\t<div v-for=\"topic in topics\" style=\"margin-bottom:12px;\" :key=\"topic.id\">\n\t\t\t\t\t\t<topic-card :title=\"topic.title\" :html=\"topic.content\" :updateTime=\"topic.activityTime\"\n\t\t\t\t\t\t\t:username=\"topic.user.username\" :starCount=\"topic.starCount\"\n\t\t\t\t\t\t\t:commentCount=\"topic.commentCount\" :pageView=\"topic.pageView\"\n\t\t\t\t\t\t\t:photoUrl=\"$global.getPhotoUrl(topic.user.photoUrl)\" :to=\"$global.getTopicUrl(topic.id)\"\n\t\t\t\t\t\t\t:categoryId=\"topic.categoryId\" :tagIds=\"topic.tagIds\" :coverImageUrl=\"topic.coverImageUrl\"\n\t\t\t\t\t\t\ttimeLabel=\"话题活跃于\">\n\t\t\t\t\t\t</topic-card>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div style=\"text-align: center;\">\n\t\t\t\t\t\t<el-pagination v-if=\"topics.length>0\" background layout=\"prev, pager, next\" :total=\"totalCount\"\n\t\t\t\t\t\t\t:current-page.sync=\"curPage\" @current-change=\"handlePageNoChange\"\n\t\t\t\t\t\t\t:page-size=\"query.pageSize\">\n\t\t\t\t\t\t</el-pagination>\n\t\t\t\t\t\t<el-empty v-else image=\"static/image/sad.jpeg\" description=\"没有你想要的结果 ┑(￣Д ￣)┍\"></el-empty>\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t\t<div class=\"wrapper-right\">\n\t\t\t\t<category-card :click-category=\"clickCategory\"></category-card>\n\t\t\t\t<div class=\"mt10\">\n\t\t\t\t\t<tag-card :click-tag=\"clickTag\"></tag-card>\n\t\t\t\t</div>\n\t\t\t\t<div style=\"margin-top:20px;\">\n\t\t\t\t\t<topic-rank :labels=\"['火热讨论','抢先收藏','引人注目']\" :topics=\"topicsInTab\"\n\t\t\t\t\t:handleTabHover=\"handleTabHover\">\n\t\t\t\t\t</topic-rank>\n\t\t\t\t</div>\n\t\t\t\t<div style=\"margin-top:20px\">\n\t\t\t\t\t<user-rank></user-rank>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport TopicCard from '@/components/TopicCard/TopicCard.vue'\n\timport CategoryCard from '@/components/CategoryCard/CategoryCard.vue'\n\timport TagCard from '@/components/TagCard/TagCard.vue'\n\timport TopicList from '@/components/TopicList/TopicList.vue'\n\timport TopicRank from '@/components/TopicRank/TopicRank.vue'\n\timport HomeCarousel from '@/components/HomeCarousel/HomeCarousel.vue'\n\timport MaskImage from '@/components/MaskImage/MaskImage.vue'\n\timport UserRank from '@/views/Home/UserRank/UserRank.vue'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\timport { pageActiveTopics } from '@/api/topic.js'\n\timport {\n\t\tqueryRecentHotTopics,\n\t\tqueryRecommendTopics,\n\t\tqueryMostCommentCountTopics,\n\t\tget3HotTopicLists,\n\t\tpageByCategoryId,\n\t\tpageByTagId,\n\t\tpageBySort\n\t} from '@/api/topic.js'\n\n\texport default {\n\t\tname: 'ForumA',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tTopicCard,\n\t\t\tCategoryCard,\n\t\t\tTagCard,\n\t\t\tTopicRank,\n\n\t\t\tHomeCarousel,\n\t\t\tMaskImage,\n\t\t\tUserRank\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tloading: true,\n\t\t\t\ttotalCount: 100,\n\t\t\t\trecommendTopics: [],\n\t\t\t\trecentHotTopics: [],\n\t\t\t\ttopicsInTab: [],\n\t\t\t\thotTopicLists:[[],[],[]],\n\t\t\t\ttopics: [],\n\t\t\t\ttabIndex:0,\n\t\t\t\tcurPage: 1,\n\t\t\t\tquery: {\n\t\t\t\t\ttagId: null,\n\t\t\t\t\tcategoryId: null,\n\t\t\t\t\tpageNo: 1,\n\t\t\t\t\tpageSize: 12,\n\t\t\t\t\tsortMode: 'ACTIVITY_TIME'\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\twatch: {\n\t\t\tquery: {\n\t\t\t\thandler(newval, oldval) {\n\t\t\t\t\tconsole.log('新+' + newval);\n\t\t\t\t\tconsole.log('旧+' + oldval);\n\t\t\t\t},\n\t\t\t\tdeep: true\n\t\t\t}\n\t\t},\n\t\tcreated() {\n\t\t\tthis.getTopics();\n\t\t\tlet _this = this;\n\t\t\tqueryRecommendTopics().then(result => {\n\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t_this.recommendTopics = result.data;\n\t\t\t\t}\n\t\t\t});\n\t\t\tqueryRecentHotTopics().then(result => {\n\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t_this.recentHotTopics = result.data;\n\t\t\t\t}\n\t\t\t});\n\t\t\tget3HotTopicLists().then(res=>{\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t_this.hotTopicLists = res.data;\n\t\t\t\t\tthis.topicsInTab=this.hotTopicLists[this.tabIndex];\n\t\t\t\t}\n\t\t\t});\n\t\t\tqueryMostCommentCountTopics().then(res => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t_this.mostCommentCountTopics = res.data;\n\t\t\t\t}\n\t\t\t})\n\n\t\t},\n\t\tmethods: {\n\t\t\tclickCategory(categoryId) {\n\t\t\t\tthis.query.tagId = null;\n\t\t\t\tif (this.query.categoryId == null) {\n\t\t\t\t\tthis.query.pageNo = 1;\n\t\t\t\t\tthis.curPage = 1;\n\t\t\t\t}\n\t\t\t\tthis.query.categoryId = categoryId;\n\t\t\t\tlet _this = this;\n\t\t\t\tthis.getTopics();\n\t\t\t},\n\t\t\tclickTag(tagId) {\n\t\t\t\tthis.query.categoryId = null;\n\t\t\t\tif (this.query.tagId == null) {\n\t\t\t\t\tthis.query.pageNo = 1;\n\t\t\t\t\tthis.curPage = 1;\n\t\t\t\t}\n\t\t\t\tthis.query.tagId = tagId;\n\t\t\t\tthis.getTopics();\n\t\t\t},\n\t\t\thandlePageNoChange() {\n\t\t\t\tthis.query.pageNo = this.curPage;\n\t\t\t\tthis.getTopics();\n\t\t\t},\n\t\t\tgetTopics() {\n\t\t\t\tlet _this = this;\n\t\t\t\tif (this.query.categoryId != null) {\n\t\t\t\t\t//按分类查\n\t\t\t\t\t_this.loading = true;\n\t\t\t\t\tpageByCategoryId(this.query).then(res => {\n\t\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t\t_this.topics = res.data.dataList;\n\t\t\t\t\t\t\t_this.totalCount = res.data.totalCount;\n\t\t\t\t\t\t\t_this.loading = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t} else if (this.query.tagId != null) {\n\t\t\t\t\t//按标签查\n\t\t\t\t\t_this.loading = true;\n\t\t\t\t\tpageByTagId(this.query).then(res => {\n\t\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t\t_this.topics = res.data.dataList;\n\t\t\t\t\t\t\t_this.totalCount = res.data.totalCount;\n\t\t\t\t\t\t\t_this.loading = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\t_this.loading = true;\n\t\t\t\t\t// pageBySort(this.query).then(res => {\n\t\t\t\t\t// \tif (res.code == Code.OK) {\n\t\t\t\t\t// \t\t_this.topics = res.data.dataList;\n\t\t\t\t\t// \t\t_this.totalCount = res.data.totalCount;\n\t\t\t\t\t// \t\t_this.loading = false;\n\t\t\t\t\t// \t}\n\t\t\t\t\t// });\n\t\t\t\t\tpageActiveTopics(this.query.pageNo, this.query.pageSize)\n\t\t\t\t\t\t.then(result => {\n\t\t\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t\t\t_this.topics = result.data.dataList;\n\t\t\t\t\t\t\t\t_this.totalCount = result.data.totalCount;\n\t\t\t\t\t\t\t\t_this.loading = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t},\n\t\t\thandleTabHover(tabIndex){\n\t\t\t\tthis.topicsInTab=this.hotTopicLists[tabIndex];\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./Forum.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/Home/Home.css",
    "content": "\n.home-wrapper{\n\tbackground-color: white;\n\tpadding-bottom: 200px;\n}\n\n.home-main{\n\twidth:1130px;\n\tmargin-left: calc( (100% - 1120px) / 2 );\n\tmargin-top: 20px;\n\tdisplay: block;\n}\n\n.mask-images-container{\n\twidth:520px;\n\tmargin-left:10px;\n\tdisplay: inline-block;\n\tvertical-align: top;\n}\n\n.mask-images-container>div{\n\tdisplay: inline-block;\n}\n\n.float-image-container{\n\tdisplay: inline-block;\n\tmargin-left: 18px;\n\tmargin-right:18px;\n\twidth: 190px;\n}\n\n"
  },
  {
    "path": "vue_acimage_web/src/views/Home/Home.vue",
    "content": "<template>\n\t<div class=\"home-wrapper\">\n\t\t<my-header></my-header>\n\t\t<div class=\"home-main\">\n\t\t\t<div style=\"display: inline-block;width:598px;\">\n\t\t\t\t<home-carousel></home-carousel>\n\t\t\t</div>\n\n\t\t\t<div class=\"mask-images-container\">\n\t\t\t\t<div v-for=\"topic in recommendTopics\" :key=\"topic.id\">\n\t\t\t\t\t<router-link :key=\"topic.id\" :to=\"$global.getTopicUrl(topic.id)\" :underline=\"false\">\n\t\t\t\t\t\t<mask-image :image-url=\"topic.coverImageUrl\"\n\t\t\t\t\t\t\t:title=\"labelWithTitle(topic.categoryId,topic.title)\" :star-count=\"topic.starCount\"\n\t\t\t\t\t\t\t:page-view=\"topic.pageView\" :commentCount=\"topic.commentCount\"\n\t\t\t\t\t\t\t:username=\"topic.user.username\">\n\t\t\t\t\t\t</mask-image>\n\t\t\t\t\t</router-link>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<base-container title=\"美图分享\" icon=\"el-icon-picture\" style=\"margin-top:-40px;\">\n\t\t\t\t<el-skeleton v-if=\"loading\" :rows=\"8\" animated />\n\t\t\t\t<template v-else>\n\t\t\t\t\t<router-link v-for=\"topic in pictureTopics\" :key=\"topic.id\" :to=\"$global.getTopicUrl(topic.id)\">\n\t\t\t\t\t\t<div class=\"float-image-container\">\n\t\t\t\t\t\t\t<float-image :title=\"topic.title\" :star=\"topic.starCount\" :page-view=\"topic.pageView\"\n\t\t\t\t\t\t\t\t:username=\"topic.user.username\" :create-time=\"topic.createTime\"\n\t\t\t\t\t\t\t\t:photo-url=\"$global.getPhotoUrl(topic.user.photoUrl)\" :image-url=\"topic.coverImageUrl\">\n\t\t\t\t\t\t\t</float-image>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</router-link>\n\t\t\t\t</template>\n\t\t\t</base-container>\n\t\t\t\n\t\t\t<base-container title=\"番剧茶馆\" icon=\"el-icon-milk-tea\" iconColor=\"#FFA131\" style=\"margin-top: 20px;\">\n\t\t\t\t<el-skeleton v-if=\"loading\" :rows=\"8\" animated />\n\t\t\t\t<template v-else>\n\t\t\t\t\t<router-link v-for=\"topic in animationTopis\" :key=\"topic.id\" :to=\"$global.getTopicUrl(topic.id)\">\n\t\t\t\t\t\t<div class=\"float-image-container\">\n\t\t\t\t\t\t\t<float-image :title=\"topic.title\" :star=\"topic.starCount\" :page-view=\"topic.pageView\"\n\t\t\t\t\t\t\t\t:username=\"topic.user.username\" :create-time=\"topic.createTime\"\n\t\t\t\t\t\t\t\t:photo-url=\"$global.getPhotoUrl(topic.user.photoUrl)\" :image-url=\"topic.coverImageUrl\">\n\t\t\t\t\t\t\t</float-image>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</router-link>\n\t\t\t\t</template>\n\t\t\t</base-container>\n\n\t\t\t<base-container title=\"最新发表\" style=\"margin-top: 20px;\">\n\t\t\t\t<el-skeleton v-if=\"loading\" :rows=\"8\" animated />\n\t\t\t\t<template v-else>\n\t\t\t\t\t<router-link v-for=\"topic in latestPublicTopics\" :key=\"topic.id\" :to=\"$global.getTopicUrl(topic.id)\">\n\t\t\t\t\t\t<div class=\"float-image-container\">\n\t\t\t\t\t\t\t<float-image :title=\"topic.title\" :star=\"topic.starCount\" :page-view=\"topic.pageView\"\n\t\t\t\t\t\t\t\t:username=\"topic.user.username\" :create-time=\"topic.createTime\"\n\t\t\t\t\t\t\t\t:photo-url=\"$global.getPhotoUrl(topic.user.photoUrl)\" :image-url=\"topic.coverImageUrl\">\n\t\t\t\t\t\t\t</float-image>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</router-link>\n\t\t\t\t</template>\n\t\t\t</base-container>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\t//组件\n\timport HomeCarousel from '@/components/HomeCarousel/HomeCarousel.vue'\n\t// import UserRank from './UserRank/UserRank.vue'\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport MaskImage from '@/components/MaskImage/MaskImage.vue'\n\timport FloatImage from '@/components/FloatImage/FloatImage.vue'\n\timport BaseContainer from '@/components/BaseContainer/BaseContainer.vue'\n\n\timport {\n\t\tqueryRecentHotTopics,\n\t\tqueryRecommendTopics,\n\t\tpageByCategoryId,\n\t\tpageBySort\n\t} from '@/api/topic.js'\n\timport { pageActiveTopics } from '@/api/topic.js'\n\timport axios from 'axios'\n\n\timport { Code } from '@/utils/result.js'\n\n\texport default {\n\t\tname: 'HomeA',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tHomeCarousel,\n\t\t\tMaskImage,\n\t\t\tFloatImage,\n\t\t\tBaseContainer\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tloading: true,\n\t\t\t\tqueryCategory: {\n\t\t\t\t\tpageNo: 1,\n\t\t\t\t\tpageSize: 10,\n\t\t\t\t\tcategoryId: null\n\t\t\t\t},\n\t\t\t\t//美图分享话题\n\t\t\t\tpictureTopics: [],\n\t\t\t\t//番剧茶馆话题\n\t\t\t\tanimationTopis:[],\n\t\t\t\t//最新发表话题\n\t\t\t\tlatestPublicTopics:[],\n\t\t\t\trecommendTopics: []\n\t\t\t};\n\n\t\t},\n\t\tcomputed: {\n\t\t\tlabelWithTitle() {\n\t\t\t\treturn (categoryId, title) => {\n\t\t\t\t\treturn '【' + this.$store.getters.categoryLabel(categoryId) + '】' + title;\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tlet _this = this;\n\t\t\t//美图分享id\n\t\t\tthis.queryCategory.categoryId = 6;\n\t\t\tpageByCategoryId(this.queryCategory).then(res => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t_this.pictureTopics = res.data.dataList;\n\t\t\t\t\t_this.loading = false;\n\t\t\t\t}\n\t\t\t})\n\t\t\t//番剧茶馆id\n\t\t\tthis.queryCategory.categoryId = 1;\n\t\t\tpageByCategoryId(this.queryCategory).then(res => {\n\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t_this.animationTopis = res.data.dataList;\n\t\t\t\t\t_this.loading = false;\n\t\t\t\t}\n\t\t\t})\n\t\t\t\n\t\t\tpageBySort({pageNo:1,pageSize:10,sortMode:'CREATE_TIME'}).then(res=>{\n\t\t\t\tif(res.code==Code.OK){\n\t\t\t\t\t_this.latestPublicTopics=res.data.dataList\n\t\t\t\t}\n\t\t\t});\n\t\t\t\n\t\t\tqueryRecommendTopics().then(result => {\n\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t_this.recommendTopics = result.data;\n\t\t\t\t\t_this.loading = false;\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t}\n</script>\n\n<style src=\"./Home.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/Home/UserRank/UserRank.css",
    "content": ".wrapper {\n\twidth: 100%;\n\tbackground-color: white;\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 5px #CCCCCC;\n\tpadding-bottom: 40px;\n\tposition: relative;\n\ttransition: 0.4s;\n}\n\n.wrapper:hover {\n\tbox-shadow: 0px 0px 5px #AAAAAA;\n\ttransition: 0.5s;\n}\n\n.wrapper-header>img {\n\twidth: 100%;\n\theight: 100px;\n\tborder-radius: 3px;\n\tobject-fit: cover;\n\tz-index: 5;\n}\n\n.white-gradient {\n\tposition: absolute;\n\ttop: 45px;\n\theight: 60px;\n\twidth: 100%;\n\tbackground: linear-gradient(to top, #fff, rgba(255, 255, 255, 0));\n\t/* background-image: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.01)); */\n}\n\n.header-title {\n\tmargin-left: 120px;\n\tposition: absolute;\n\tbottom: 12px;\n\tfont-style: italic;\n\ttext-shadow: 2px 5px 4px pink;\n\tcolor: rgb(255, 0, 153);\n}\n\n.user-rank-header {\n\tfont-size: 22px;\n\tcolor: #C5A40F;\n\ttext-align: center;\n\tpadding-top: 20px;\n}\n\n.sort-choice-container {\n\tmargin-top: 10px;\n\tmargin-bottom: 15px;\n\tmargin-left: 20px;\n}\n\n.sort-hint {\n\tfont-size: 14px;\n\tmargin-right: 5px;\n\tmargin-left: 5px;\n\tcolor: #333333\n}\n\n.user-item-container {\n\tmargin-top: 5px;\n\tmargin-left: 20px;\n}\n\n.user-item-left {\n\tdisplay: inline-block;\n\tmargin-left: 10px;\n}\n\n.user-item-right {\n\tdisplay: inline-block;\n\tmargin-left: 10px;\n\tmargin-top: -2px;\n\tvertical-align: top;\n}\n\n.user-item-right-header {\n\tfont-size: 14px;\n\tcolor: #666666;\n}\n\n.user-item-right-bottom>div {\n\tcolor: #12BADF;\n\tfont-size: 12px;\n\tdisplay: inline-block;\n}\n\n.user-item-right-bottom>span {\n\tfont-size: 12px;\n\tcolor: #666666;\n\tmargin-left: -3px;\n\tmargin-right: 5px;\n}\n\n.gray-color {\n\tcolor: #666666;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/views/Home/UserRank/UserRank.vue",
    "content": "<template>\n\t<div>\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"wrapper-header\">\n\t\t\t\t<img src=\"static/image/user-rank-header.webp\" />\n\t\t\t\t<div class=\"white-gradient\">\n\t\t\t\t\t<div class=\"header-title\">用户排行</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<el-tabs type=\"card\" style=\"margin-top:-5px;\" v-model=\"query.column\">\n\t\t\t\t<el-tab-pane label=\"星星收割者\" name=\"starCount\"></el-tab-pane>\n\t\t\t\t<el-tab-pane label=\"话题达人\" name=\"topicCount\"></el-tab-pane>\n\t\t\t</el-tabs>\n\n\t\t\t<el-skeleton v-if=\"loading\" :rows=\"6\" animated style=\"width: 90%;margin-left:5%\" />\n\t\t\t<div v-else v-for=\"user in users\" class=\"user-item-container\" :key=\"user.id\">\n\t\t\t\t<div class=\"user-item-left\">\n\t\t\t\t\t<el-avatar :size=\"40\" :src=\"$global.getPhotoUrl(user.photoUrl)\"></el-avatar>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"user-item-right\">\n\t\t\t\t\t<div class=\"user-item-right-header\">\n\t\t\t\t\t\t{{user.username}}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"user-item-right-bottom\">\n\t\t\t\t\t\t<div>{{user.starCount}}</div> <span>star</span>\n\t\t\t\t\t\t<div>{{user.topicCount}}</div> <span>话题</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div style=\"text-align: center;\">\n\t\t\t\t<el-pagination v-if=\"users.length>0\" layout=\"prev, pager, next\" :total=\"totalCount\"\n\t\t\t\t\t:current-page.sync=\"curPage\" @current-change=\"handlePageNoChange\" :page-size=\"10\">\n\t\t\t\t</el-pagination>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { pageUserRankBy } from '@/api/UserRank.js'\n\timport { Code } from '@/utils/result.js'\n\n\texport default {\n\t\tname: 'UserRank',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tcurPage: 1,\n\t\t\t\tquery: {\n\t\t\t\t\tpageNo: 1,\n\t\t\t\t\tcolumn: 'starCount'\n\t\t\t\t},\n\t\t\t\ttotalCount: 0,\n\t\t\t\tloading: true,\n\t\t\t\tusers: []\n\t\t\t}\n\t\t},\n\t\twatch: {\n\t\t\tquery: {\n\t\t\t\thandler(newVal, oldVal) {\n\t\t\t\t\tthis.getUserRankPage();\n\t\t\t\t},\n\t\t\t\tdeep: true\n\t\t\t}\n\t\t},\n\t\tcreated() {\n\t\t\tthis.getUserRankPage();\n\t\t},\n\t\tmethods: {\n\t\t\tgetUserRankPage() {\n\t\t\t\tlet _this = this;\n\t\t\t\tpageUserRankBy(this.query).then(res => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t_this.users = res.data.dataList;\n\t\t\t\t\t\t_this.totalCount = res.data.totalCount;\n\t\t\t\t\t\t_this.loading = false;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t},\n\t\t\thandlePageNoChange() {\n\t\t\t\tthis.query.pageNo = this.curPage;\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./UserRank.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/Login/Login.css",
    "content": "body{\n\tmargin:0px;\n\tpadding:0px;\n\tbackground-size: cover;\n}\n\n.login-wrapper{\n\tbackground-size:cover; \n\theight:100vh;\n\twidth: 100vw;\n}\n\n.login-main{\n\ttext-align: center;\n}\n\n.login-container {\n\twidth: 400px;\n\tdisplay: inline-block;\n\t/* border:1px solid #409EFF; */\n\tborder-radius: 10px;\n\tbackground-color: rgba(255,255,255,0.8);\n\tposition: fixed;\n\tleft:51vw;\n\ttop:20vh;\n}\n  \n.login-container:hover {\n\tbackground-color: rgba(255,255,255,0.9);\n}\n\n.login-tabs-container{\n\tmargin-top: -20px;\n\tmargin-bottom: -10px;\n}\n.login-tabs-content{\n\tfont-size:18px !important; \n\tbackground-color: rgba(0,0,0,0) !important;\n}\n   \n.hint-container{\n\theight:22px;\n\tdisplay: block;\n\tmargin-top:10px ;\n\ttext-align: left;\n}\n   \n.hint-content{\n\tcolor:red;\n\tfont-size:10px;\n\tfont-family:'Times New Roman', Times, serif; \n}\n\n.content{\n\tmargin-top: -10px;\n}\n\n.content>>>.el-row{\n\tmargin-top:10px;\n}\n\n.verify-code{\n\twidth: 100px;\n\tmargin-left:20px;\n\theight:40px;\n\tdisplay:inline-block;\n\tvertical-align: top;\n}\n\n\n  \n"
  },
  {
    "path": "vue_acimage_web/src/views/Login/Login.vue",
    "content": "<template>\n\t<div class=\"login-wrapper\" style=\"background-image: url(static/image/login-bg5.jpeg); \">\n\t\t<div class=\"login-main\">\n\t\t\t<div class=\"login-container\" :style=\"{height:loginBoxHeight+'px'}\">\n\t\t\t\t<div style=\"margin-top: 0px;display: inline-block;\">\n\t\t\t\t\t<!-- logo -->\n\t\t\t\t\t<img src=\"static/image/logo.png\" />\n\t\t\t\t\t<!-- 登录框tabs -->\n\t\t\t\t\t<div class=\"login-tabs-container\">\n\t\t\t\t\t\t<el-menu mode=\"horizontal\" style=\"background-color: rgba(0,0,0,0);\">\n\t\t\t\t\t\t\t<el-menu-item index=\"1\" @click=\"isRegisterMode=false\" class=\"login-tabs-content\">登录\n\t\t\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t\t\t<el-menu-item index=\"2\" @click=\"isRegisterMode=true\" class=\"login-tabs-content\">注册\n\t\t\t\t\t\t\t</el-menu-item>\n\t\t\t\t\t\t</el-menu>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"hint-container\">\n\t\t\t\t\t\t<span class=\"hint-content\">{{hint}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- 注册内容 -->\n\t\t\t\t\t<div class=\"content\" v-if=\"isRegisterMode\">\n\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"请输入邮箱\" v-model=\"email\" maxlength=\"32\" prefix-icon=\"el-icon-message\"\n\t\t\t\t\t\t\t\tstyle=\"width:250px;\" clearable>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"请输入密码\" v-model=\"password\" maxlength=\"16\" prefix-icon=\"el-icon-lock\"\n\t\t\t\t\t\t\t\tstyle=\"width:250px;\" show-password>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"请输入昵称\" v-model=\"username\" @change=\"onUsernameChange\" maxlength=\"12\"\n\t\t\t\t\t\t\t\tprefix-icon=\"el-icon-user\" size=\"samll\" style=\"width:250px;\" clearable>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"验证码(点击下方到邮箱获取)\" v-model=\"verifyCode\" maxlength=\"8\"\n\t\t\t\t\t\t\t\tstyle=\"width:250px;\" clearable>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-button type=\"primary\" @click=\"sendCode\" style=\"width: 250px;\">\n\t\t\t\t\t\t\t\t点我发送验证码至邮箱\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-button type=\"danger\" @click=\"register\" style=\"width: 120px;\">\n\t\t\t\t\t\t\t\t注册\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t\t<el-button @click=\"toHome\" type=\"primary\" style=\"width: 120px;\" plain>\n\t\t\t\t\t\t\t\t返回主页\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t</el-row>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!--登录内容 -->\n\t\t\t\t\t<div class=\"content\" v-else>\n\t\t\t\t\t\t<!-- <el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"请输入账号\" v-model=\"username\" @change=\"onUsernameChange\" maxlength=\"12\"\n\t\t\t\t\t\t\t\tprefix-icon=\"el-icon-user\" size=\"samll\" style=\"width:250px;\" clearable>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t</el-row>-->\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"请输入邮箱\" v-model=\"email\" maxlength=\"32\" prefix-icon=\"el-icon-message\"\n\t\t\t\t\t\t\t\tstyle=\"width:250px;\" clearable>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"请输入密码\" v-model=\"password\" maxlength=\"16\" prefix-icon=\"el-icon-lock\"\n\t\t\t\t\t\t\t\tstyle=\"width:250px;\" show-password>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-input placeholder=\"验证码\" v-model=\"verifyCode\" maxlength=\"8\" style=\"width:130px;\"\n\t\t\t\t\t\t\t\tclearable>\n\t\t\t\t\t\t\t</el-input>\n\t\t\t\t\t\t\t<div class=\"verify-code\">\n\t\t\t\t\t\t\t\t<img alt=\"单击图片刷新!\" class=\"pointer\" src=\"/api/user/verifies/commonCode\" width=\"100px\"\n\t\t\t\t\t\t\t\t\theight=\"40px\" onclick=\"this.src='/api/user/verifies/commonCode?d='+new Date()*1\">\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-row>\n\n\t\t\t\t\t\t<el-row>\n\t\t\t\t\t\t\t<el-button type=\"danger\" @click=\"login\" style=\"width: 120px;\">\n\t\t\t\t\t\t\t\t登录\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t\t<el-button @click=\"toHome\" type=\"primary\" style=\"width: 120px;\" plain>\n\t\t\t\t\t\t\t\t返回主页\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t</el-row>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { doLogin, doRegister, getPublicKey, sendCodeToEmail, queryIsUsernameExist } from '@/api/login.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\timport { JSEncrypt } from 'jsencrypt'\n\texport default {\n\t\tname: 'LoginA',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tpassword: '',\n\t\t\t\temail: '',\n\t\t\t\tusername: '',\n\t\t\t\tverifyCode: '',\n\t\t\t\tisRegisterMode: false,\n\t\t\t\tisUsernameExist: false,\n\t\t\t};\n\t\t},\n\t\tcomputed: {\n\t\t\thint() {\n\t\t\t\t// return 'asdasd'\n\t\t\t\tconsole.log(this.isUsernameExist)\n\t\t\t\tif (this.isRegisterMode && this.isUsernameExist) {\n\t\t\t\t\treturn '*用户名已存在';\n\t\t\t\t}\n\t\t\t\treturn '';\n\t\t\t},\n\t\t\tloginBoxHeight() {\n\t\t\t\treturn this.isRegisterMode ? '450' : '350';\n\t\t\t}\n\n\t\t},\n\t\tmethods: {\n\t\t\thasNull() {\n\t\t\t\tfor (let item of arguments) {\n\t\t\t\t\tif (CommonUtils.isEmpty(item)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\tsendCode() {\n\t\t\t\tsendCodeToEmail(this.email).then(res => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"发送成功，请到邮箱查看\");\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\tregister() {\n\t\t\t\tif (this.hasNull(this.username, this.password, this.email) == true) {\n\t\t\t\t\tMessageUtils.notice(\"有选项为空\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tlet _this = this;\n\t\t\t\tgetPublicKey().then(result => {\n\t\t\t\t\t// 向服务器获取公钥\n\t\t\t\t\treturn result.data;\n\t\t\t\t}).then(publicKey => {\n\t\t\t\t\tlet encryptor = new JSEncrypt();\n\t\t\t\t\tencryptor.setPublicKey(publicKey);\n\t\t\t\t\t//加密\n\t\t\t\t\tlet passwordEncrypt = encryptor.encrypt(_this.password);\n\t\t\t\t\tlet user = {\n\t\t\t\t\t\t\"username\": this.username,\n\t\t\t\t\t\t\"password\": passwordEncrypt,\n\t\t\t\t\t\t\"email\": this.email,\n\t\t\t\t\t\t\"verifyCode\": this.verifyCode\n\t\t\t\t\t};\n\t\t\t\t\tdoRegister(user).then(result => {\n\t\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t\t_this.$store.commit('setToken', result.data);\n\t\t\t\t\t\t\tMessageUtils.success(\"注册成功\", 1);\n\t\t\t\t\t\t\tsetTimeout(() => { _this.$router.push({ path: '/' }) }, 500);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t},\n\t\t\tlogin() {\n\t\t\t\tif (this.hasNull(this.password, this.email) == true) {\n\t\t\t\t\tMessageUtils.notice('有选项为空');\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tlet _this = this;\n\t\t\t\tgetPublicKey().then(result => {\n\t\t\t\t\t//获取公钥\n\t\t\t\t\treturn result.data;\n\t\t\t\t}).then(publicKey => {\n\t\t\t\t\tlet encryptor = new JSEncrypt();\n\t\t\t\t\tencryptor.setPublicKey(publicKey);\n\t\t\t\t\t//加密\n\t\t\t\t\tlet passwordEncrypt = encryptor.encrypt(_this.password);\n\t\t\t\t\tlet user = {\n\t\t\t\t\t\temail: this.email,\n\t\t\t\t\t\tpassword: passwordEncrypt,\n\t\t\t\t\t\tverifyCode: this.verifyCode,\n\t\t\t\t\t};\n\t\t\t\t\tdoLogin(user)\n\t\t\t\t\t\t.then(result => {\n\t\t\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t\t\t_this.$store.commit('setToken', result.data);\n\t\t\t\t\t\t\t\tMessageUtils.success('登录成功', 1);\n\t\t\t\t\t\t\t\tsetTimeout(() => { _this.$router.push({ path: '/' }); }, 500);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t},\n\t\t\tonUsernameChange() {\n\t\t\t\t//检查用户名是否存在\n\t\t\t\tif (this.isRegisterMode && this.username.trim() != '') {\n\t\t\t\t\tlet _this = this;\n\t\t\t\t\tqueryIsUsernameExist(_this.username)\n\t\t\t\t\t\t.then(res => {\n\t\t\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t\t\t_this.isUsernameExist = res.data;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t},\n\t\t\ttoHome(){\n\t\t\t\tthis.$router.push({ path: '/' })\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./Login.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyActivity.css",
    "content": ".outer-div {\n\tbackground-color: #F5F5F5;\n}\n\n.wrapper {\n\twidth: 800px;\n\tmargin-left: calc((100% - 800px) / 2);\n\tmargin-top: 15px;\n\tdisplay: block;\n}\n\n.wrapper-left {\n\tposition: relative;\n\twidth: 100px;\n\tdisplay: inline-block;\n\tbackground-color: rgba(255, 255, 255, 0.8);\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 5px #CCCCCC;\n\tpadding-bottom: 525px;\n\ttext-align: center;\n}\n\n.wrapper-left div {\n\tfont-size: 16px;\n\tmargin-top: 20px;\n\tcolor: #666666;\n}\n\n.wrapper-left div:nth-child(1) {\n\tcolor: #666666 !important;\n\tcursor: auto !important;\n}\n\n.wrapper-left div:hover {\n\tcolor: #0B8FFF;\n\tcursor: pointer;\n}\n\n.wrapper-right {\n\twidth: 680px;\n\tvertical-align: top;\n\tmargin-left: 10px;\n\tdisplay: inline-block;\n}\n\n.wrapper-right>div {\n\tborder-radius: 5px;\n\tbackground-color: rgba(255, 255, 255, 0.9);\n\tbox-shadow: 0px 0px 5px #CCCCCC;\n}\n\n.activity-header {\n\tpadding: 15px;\n\tfont-size: 22px;\n\ttext-align: center;\n}\n\n.activity-content {\n\tposition: relative;\n\tpadding: 10px;\n\tmargin-top: 10px;\n\tpadding-bottom: 20px;\n}\n\n\n.activity-medium {\n\tdisplay: inline-block;\n\tvertical-align: top;\n\tmargin-left: 10px;\n\twidth: 300px;\n}\n\n.activity-time {\n\tmargin-top: 5px;\n\tcolor: #999999;\n\tfont-size: 14px;\n}\n\n.activity-right {\n\tdisplay: inline-block;\n\tvertical-align: top;\n\tfloat: right;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyActivity.vue",
    "content": "<template>\n\t<div class=\"outer-div\">\n\t\t<my-header></my-header>\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"wrapper-left\">\n\t\t\t\t<div>\n\t\t\t\t\t我的动态\n\t\t\t\t</div>\n\t\t\t\t<template v-for=\"(tab,index) in tabs\">\n\t\t\t\t\t<div @click=\"tabIndex=index\" :style=\"tabIndex==index?'color:#0B8FFF':''\" :key=\"tab\">\n\t\t\t\t\t\t{{tab}}\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t\t<div class=\"wrapper-right\">\n\t\t\t\t<div class=\"activity-header\">\n\t\t\t\t\t{{tabs[tabIndex]}}\n\t\t\t\t</div>\n\t\t\t\t<template v-if=\"tabIndex==0\">\n\t\t\t\t\t<my-topic-activity></my-topic-activity>\n\t\t\t\t</template>\n\t\t\t\t<template v-else-if=\"tabIndex==1\">\n\t\t\t\t\t<my-comment-activity></my-comment-activity>\n\t\t\t\t</template>\n\t\t\t\t<template v-else-if=\"tabIndex==2\">\n\t\t\t\t\t<my-star-activity></my-star-activity>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport MyTopicActivity from './MyTopicActivity/MyTopicActivity.vue'\n\timport MyCommentActivity from './MyCommentActivity/MyCommentActivity.vue'\n\timport MyStarActivity from './MyStarActivity/MyStarActivity.vue'\n\n\timport { pageMyPublishTopic } from '@/api/topic.js'\n\timport { pageMyComment } from '@/api/comment.js'\n\timport { pageMyStar } from '@/api/star.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'MyActivity',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tMyTopicActivity,\n\t\t\tMyCommentActivity,\n\t\t\tMyStarActivity\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\ttabIndex: 0,\n\t\t\t\ttabs: ['我的话题', '我的评论', '我的收藏'],\n\t\t\t}\n\t\t}\n\t}\n\t// \tactivityType: ['topic', 'comment', 'star'],\n\t// \tactivityLabel: ['分享了', '评论了', '收藏了'],\n\t// \tpageNo: [1, 1, 1],\n\t// \ttopicPageNo: 1,\n\t// \ttotalCount: [10, 10, 10],\n\t// \tt: 10,\n\t// \ttopics: [{\n\t// \t\t\tid: 0,\n\t// \t\t\ttitle: '随便起个名字',\n\t// \t\t\tcreateTime: '2022-2-22 22:22:22',\n\t// \t\t\tstarCount: 888,\n\t// \t\t\tfirstImageUrl: '',\n\t// \t\t},\n\t// \t\t{\n\t// \t\t\ttitle: '随便起个名字',\n\t// \t\t\tcreateTime: '2022-2-22 22:22:22',\n\t// \t\t\tstarCount: 888,\n\t// \t\t\tfirstImageUrl: '',\n\t// \t\t}\n\t// \t],\n\t// \tcomments: [{\n\t// \t\tid: 1,\n\t// \t\tcontent: '这是瞎写的评论',\n\t// \t\tcreateTime: '2020-2-22 22:22:22',\n\t// \t\ttopic: {\n\t// \t\t\ttitle: '评论标题',\n\t// \t\t\tcreateTime: '2021-2-22 22:22:22',\n\t// \t\t\tstarCount: 888,\n\t// \t\t\tfirstImageUrl: '',\n\t// \t\t}\n\t// \t}],\n\t// \tstars: [{\n\t// \t\t\ttopicId: 2,\n\t// \t\t\tcreateTime: '2022-2-22 22:22:22',\n\t// \t\t\ttopic: {\n\t// \t\t\t\ttitle: '评论标题',\n\t// \t\t\t\tcreateTime: '2022-2-22 22:22:22',\n\t// \t\t\t\tstarCount: 888,\n\t// \t\t\t\tfirstImageUrl: '',\n\t// \t\t\t}\n\t// \t\t}\n\n\t// \t]\n\t// }\n\t// mounted() {\n\t// \tthis.getTopicPage(1);\n\t// \tthis.getCommentPage(1);\n\t// \tthis.getStarPage(1);\n\t// },\n\t// computed: {\n\t// \tactivities() {\n\t// \t\tlet activitiesInfo = [];\n\t// \t\tif (this.activityType[this.tabIndex] == 'topic') {\n\t// \t\t\tfor (let item of this.topics) {\n\t// \t\t\t\tlet activity = item;\n\t// \t\t\t\tactivity['time'] = item.createTime;\n\t// \t\t\t\tactivitiesInfo.push(activity);\n\t// \t\t\t}\n\t// \t\t} else if (this.activityType[this.tabIndex] == 'comment') {\n\t// \t\t\tfor (let item of this.comments) {\n\t// \t\t\t\tlet activity = item.topic;\n\t// \t\t\t\tactivity['content'] = item.content;\n\t// \t\t\t\tactivity['time'] = item.createTime;\n\t// \t\t\t\tactivity['id'] = item.id;\n\t// \t\t\t\tactivitiesInfo.push(activity);\n\t// \t\t\t}\n\t// \t\t} else if (this.activityType[this.tabIndex] == 'star') {\n\t// \t\t\tfor (let item of this.stars) {\n\t// \t\t\t\tlet activity = item.topic;\n\n\t// \t\t\t\tactivity['time'] = item.createTime;\n\t// \t\t\t\tactivity['id'] = item.topicId;\n\t// \t\t\t\tconsole.log(activity['id'])\n\t// \t\t\t\tactivitiesInfo.push(activity);\n\t// \t\t\t}\n\t// \t\t}\n\t// \t\treturn activitiesInfo;\n\t// \t}\n\t// },\n\t// \tmethods: {\n\t// \t\tgetTopicPage(pageNo) {\n\t// \t\t\tlet _this = this;\n\t// \t\t\tpageMyPublishTopic(pageNo).then(result => {\n\t// \t\t\t\tif (result.code == Code.OK) {\n\t// \t\t\t\t\t_this.topics = result.data.dataList;\n\t// \t\t\t\t\t_this.totalCount.splice(0, 1, result.data.totalCount);\n\t// \t\t\t\t}\n\t// \t\t\t})\n\t// \t\t},\n\t// \t\tgetCommentPage(pageNo) {\n\t// \t\t\tlet _this = this;\n\t// \t\t\tpageMyComment(pageNo).then(result => {\n\t// \t\t\t\tif (result.code == Code.OK) {\n\t// \t\t\t\t\t_this.comments = result.data.dataList;\n\t// \t\t\t\t\t_this.totalCount.splice(1, 1, result.data.totalCount);\n\t// \t\t\t\t}\n\t// \t\t\t})\n\t// \t\t},\n\t// \t\tgetStarPage(pageNo) {\n\t// \t\t\tlet _this = this;\n\t// \t\t\tpageMyStar(pageNo).then(result => {\n\t// \t\t\t\tif (result.code == Code.OK) {\n\t// \t\t\t\t\t_this.stars = result.data.dataList;\n\t// \t\t\t\t\t_this.totalCount.splice(2, 1, result.data.totalCount);\n\t// \t\t\t\t}\n\t// \t\t\t})\n\t// \t\t},\n\t// \t\t//更新对应页\n\t// \t\tonPageNoChange() {\n\t// \t\t\tlet type = this.activityType[this.tabIndex];\n\t// \t\t\tlet pageNo = this.pageNo[this.tabIndex];\n\t// \t\t\tif (type == 'topic') {\n\t// \t\t\t\tthis.getTopicPage(pageNo);\n\t// \t\t\t} else if (type == 'comment') {\n\t// \t\t\t\tthis.getCommentPage(pageNo);\n\t// \t\t\t} else if (type == 'star') {\n\t// \t\t\t\tthis.getStarPage(pageNo);\n\t// \t\t\t}\n\t// \t\t},\n\t// \t}\n\t// }\n</script>\n\n<style scoped src=\"./MyActivity.css\" >\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyCommentActivity/MyCommentActivity.css",
    "content": "\n.activity-content{\n\tposition: relative;\n\tpadding:10px;\n\tmargin-top: 10px;\n\tpadding-bottom: 20px;\n}\n\n.activity-title{\n\tfont-size: 16px;\n\tmargin-top:-5px;\n}\n\n.activity-title-label{\n\tcolor:#AAAAAA;\n\tfont-size:15px;\n}\n\n.activity-medium{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tmargin-left:10px;\n\twidth:500px;\n}\n\n.activity-time{\n\tmargin-top:5px;\n\tcolor:#999999;\n\tfont-size:16px;\n}\n\n.activity-right{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tfloat:right;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyCommentActivity/MyCommentActivity.vue",
    "content": "<template>\n\t<div class=\"activity-content\">\n\t\t<template v-for=\"(comment,index) in comments\">\n\t\t\t<el-divider v-if=\"index>0\" :key=\"comment.topic.id\"></el-divider>\n\t\t\t<div class=\"activity-content-item\" :key=\"comment.id\">\n\t\t\t\t<el-avatar :src=\"$store.getters.getPhotoUrl\" :size=\"60\"></el-avatar>\n\t\t\t\t<div class=\"activity-medium\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<span class=\"activity-title-label\">评论了</span>\n\t\t\t\t\t\t<router-link :to=\"$global.getTopicUrl(comment.topic.id)\"\n\t\t\t\t\t\t\tclass=\"no-underline router-link-item activity-title\">\n\t\t\t\t\t\t\t{{$global.omitStr(comment.topic.title,20)}}\n\t\t\t\t\t\t</router-link>\n\t\t\t\t\t\t<br />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span style=\"color: #666666;\">{{comment.content}}</span>\n\t\t\t\t\t\n\t\t\t\t\t<div class=\"activity-time\">\n\t\t\t\t\t\t{{comment.createTime}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"activity-right\">\n\t\t\t\t\t<el-image :src=\"comment.topic.coverImageUrl\" fit=\"cover\"\n\t\t\t\t\t\tstyle=\"height:70px;border-radius: 3px\">\n\t\t\t\t\t</el-image>\n\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- 分页 -->\n\t\t<div style=\"text-align: center;margin-top:20px;\">\n\t\t\t<el-pagination background layout=\"prev, pager, next\" :total=\"totalCount\" :current-page.sync=\"pageNo\"\n\t\t\t\t@current-change=\"onPageNoChange\" :page-size=\"5\">\n\t\t\t</el-pagination>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { pageMyComments } from '@/api/comment.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'MyTopicActivity',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tpageNo: 1,\n\t\t\t\ttotalCount: 2,\n\t\t\t\tcomments: [],\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tthis.getCommentPage(1);\n\t\t},\n\t\tmethods: {\n\t\t\tgetCommentPage(pageNo) {\n\t\t\t\tlet _this = this;\n\t\t\t\tpageMyComments(pageNo).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.comments = result.data.dataList;\n\t\t\t\t\t\t_this.totalCount=result.data.totalCount;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\t//更新对应页\n\t\t\tonPageNoChange() {\n\t\t\t\tthis.getCommentPage(this.pageNo);\n\t\t\t},\n\t\t}\n\t}\n</script>\n\n<style src=\"./MyCommentActivity.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyStarActivity/MyStarActivity.css",
    "content": "\n.activity-content{\n\tposition: relative;\n\tpadding:10px;\n\tmargin-top: 10px;\n\tpadding-bottom: 20px;\n}\n\n.activity-title{\n\tfont-size: 16px;\n\tmargin-top:-5px;\n}\n\n.activity-title-label{\n\tcolor:#AAAAAA;\n\tfont-size:15px;\n}\n\n.activity-medium{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tmargin-left:10px;\n\twidth:300px;\n}\n\n.activity-time{\n\tmargin-top:5px;\n\tcolor:#999999;\n\tfont-size:16px;\n}\n\n.activity-right{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tfloat:right;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyStarActivity/MyStarActivity.vue",
    "content": "<template>\n\t<div class=\"activity-content\">\n\t\t<template v-for=\"(star,index) in stars\">\n\t\t\t<el-divider v-if=\"index>0\" :key=\"star.topic.id\"></el-divider>\n\t\t\t<div class=\"activity-content-item\" :key=\"star.id\">\n\t\t\t\t<el-avatar :src=\"$store.getters.getPhotoUrl\" :size=\"60\"></el-avatar>\n\t\t\t\t<div class=\"activity-medium\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<span class=\"activity-title-label\">收藏了</span>\n\t\t\t\t\t\t<router-link :to=\"$global.getTopicUrl(star.topic.id)\"\n\t\t\t\t\t\t\tclass=\"no-underline router-link-item activity-title\">\n\t\t\t\t\t\t\t{{$global.omitStr(star.topic.title,20)}}\n\t\t\t\t\t\t</router-link>\n\t\t\t\t\t\t<br />\n\t\t\t\t\t</div>\n\t\t\t\t\t<br />\n\t\t\t\t\t<div class=\"activity-time\">\n\t\t\t\t\t\t{{star.createTime}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"activity-right\">\n\t\t\t\t\t<el-image :src=\"star.topic.coverImageUrl\" fit=\"cover\"\n\t\t\t\t\t\tstyle=\"height:70px;border-radius: 3px\">\n\t\t\t\t\t</el-image>\n\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- 分页 -->\n\t\t<div style=\"text-align: center;margin-top:20px;\">\n\t\t\t<el-pagination background layout=\"prev, pager, next\" :total=\"totalCount\" :current-page.sync=\"pageNo\"\n\t\t\t\t@current-change=\"onPageNoChange\" :page-size=\"5\">\n\t\t\t</el-pagination>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { pageMyStar } from '@/api/star.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'MyTopicActivity',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tpageNo: 1,\n\t\t\t\ttotalCount: 2,\n\t\t\t\tstars: []\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tthis.getStarPage(1);\n\t\t},\n\t\tmethods: {\n\t\t\tgetStarPage(pageNo) {\n\t\t\t\tlet _this = this;\n\t\t\t\tpageMyStar(pageNo).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.stars = result.data.dataList;\n\t\t\t\t\t\t_this.totalCount= result.data.totalCount;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\t//更新对应页\n\t\t\tonPageNoChange() {\n\t\t\t\tthis.getStarPage(this.pageNo);\n\t\t\t},\n\t\t}\n\t}\n</script>\n\n<style src=\"./MyStarActivity.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyTopicActivity/MyTopicActivity.css",
    "content": "\n.activity-content{\n\tposition: relative;\n\tpadding:10px;\n\tmargin-top: 10px;\n\tpadding-bottom: 20px;\n}\n\n.activity-title{\n\tfont-size: 16px;\n\tmargin-top:-5px;\n}\n\n.topic-data{\n\tcolor:#999999;\n}\n\n.activity-title-label{\n\tcolor:#AAAAAA;\n\tfont-size:15px;\n}\n\n.activity-medium{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tmargin-left:10px;\n\twidth:300px;\n}\n\n.activity-time{\n\tmargin-top:5px;\n\tcolor:#999999;\n\tfont-size:16px;\n}\n\n.activity-right{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tfloat:right;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/MyActivity/MyTopicActivity/MyTopicActivity.vue",
    "content": "<template>\n\t<div class=\"activity-content\">\n\t\t<template v-for=\"(topic,index) in topics\">\n\t\t\t<el-divider v-if=\"index>0\" :key=\"topic.id\"></el-divider>\n\t\t\t<div class=\"activity-content-item\" :key=\"topic.id\">\n\t\t\t\t<el-avatar :src=\"$store.getters.getPhotoUrl\" :size=\"60\"></el-avatar>\n\t\t\t\t<div class=\"activity-medium\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<span class=\"activity-title-label\">分享了</span>\n\t\t\t\t\t\t<router-link :to=\"$global.getTopicUrl(topic.id)\"\n\t\t\t\t\t\t\tclass=\"no-underline router-link-item activity-title\">\n\t\t\t\t\t\t\t{{$global.omitStr(topic.title,20)}}\n\t\t\t\t\t\t</router-link>\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\t<div class=\"topic-data\">\n\t\t\t\t\t\t\t<i class=\"el-icon-star-off\">{{topic.starCount}}</i>\n\t\t\t\t\t\t\t<el-divider direction=\"vertical\"></el-divider>\n\t\t\t\t\t\t\t<i class=\"el-icon-chat-dot-square\">{{topic.commentCount}}</i>\n\t\t\t\t\t\t\t<el-divider direction=\"vertical\"></el-divider>\n\t\t\t\t\t\t\t<i class=\"el-icon-view\">{{topic.pageView}}</i>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"activity-time\">\n\t\t\t\t\t\t{{topic.createTime}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"activity-right\">\n\t\t\t\t\t<el-image :src=\"topic.coverImageUrl\" fit=\"cover\"\n\t\t\t\t\t\tstyle=\"height:70px;border-radius: 3px\">\n\t\t\t\t\t</el-image>\n\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- 分页 -->\n\t\t<div style=\"text-align: center;margin-top:20px;\">\n\t\t\t<el-pagination background layout=\"prev, pager, next\" :total=\"totalCount\" :current-page.sync=\"pageNo\"\n\t\t\t\t@current-change=\"onPageNoChange\" :page-size=\"5\">\n\t\t\t</el-pagination>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { pageMyPublishTopic } from '@/api/topic.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'MyTopicActivity',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tpageNo: 1,\n\t\t\t\ttotalCount: 2,\n\t\t\t\ttopics: []\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tthis.getTopicPage(1);\n\t\t},\n\t\tmethods: {\n\t\t\tgetTopicPage(pageNo) {\n\t\t\t\tlet _this = this;\n\t\t\t\tpageMyPublishTopic(pageNo).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.topics = result.data.dataList;\n\t\t\t\t\t\t_this.totalCount = result.data.totalCount;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\t//更新对应页\n\t\t\tonPageNoChange() {\n\t\t\t\tthis.getTopicPage(this.pageNo);\n\t\t\t},\n\t\t}\n\t}\n</script>\n\n<style src=\"./MyTopicActivity.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyMessage/CommentMessage/CommentMessage.css",
    "content": "\n.activity-content{\n\tposition: relative;\n\tpadding:10px;\n\tmargin-top: 10px;\n\tpadding-bottom: 20px;\n}\n\n.activity-title{\n\tfont-size: 16px;\n\tmargin-top:-5px;\n}\n\n.activity-title-label{\n\tcolor:#AAAAAA;\n\tfont-size:15px;\n}\n\n.activity-medium{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tmargin-left:10px;\n\twidth:500px;\n}\n\n.activity-time{\n\tmargin-top:5px;\n\tcolor:#999999;\n\tfont-size:16px;\n}\n\n.activity-right{\n\tdisplay:inline-block;\n\tvertical-align: top;\n\tfloat:right;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/MyMessage/CommentMessage/CommentMessage.vue",
    "content": "<template>\n\t<div class=\"activity-content\">\n\t\t<template v-for=\"(commentMsg,index) in commentMsgs\">\n\t\t\t<el-divider v-if=\"index>0\" :key=\"commentMsg.topicId\"></el-divider>\n\t\t\t<div class=\"activity-content-item\" :key=\"commentMsg.commentId\">\n\t\t\t\t<!-- <el-avatar :src=\"$store.getters.getPhotoUrl\" :size=\"60\"></el-avatar> -->\n\t\t\t\t<div class=\"activity-medium\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t{{commentMsg.user.username}}\n\t\t\t\t\t\t<span class=\"activity-title-label\">评论了你的话题 </span>\n\t\t\t\t\t\t<router-link :to=\"$global.getTopicUrl(commentMsg.topicId)\"\n\t\t\t\t\t\t\tclass=\"no-underline router-link-item activity-title\">\n\t\t\t\t\t\t\t{{$global.omitStr(commentMsg.topicTitle,20)}}\n\t\t\t\t\t\t</router-link>\n\t\t\t\t\t\t<br />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span style=\"color: #666666;\">{{commentMsg.content}}</span>\n\n\t\t\t\t\t<div class=\"activity-time\">\n\t\t\t\t\t\t{{commentMsg.createTime}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"activity-right\">\n\t\t\t\t\t<!-- \t\t\t\t\t<el-image :src=\"comment.topic.coverImageUrl\" fit=\"cover\"\n\t\t\t\t\t\tstyle=\"height:70px;border-radius: 3px\">\n\t\t\t\t\t</el-image> -->\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- 分页 -->\n\t\t<div style=\"text-align: center;margin-top:20px;\">\n\t\t\t<el-pagination background layout=\"prev, pager, next\" :total=\"totalCount\" :current-page.sync=\"pageNo\"\n\t\t\t\t@current-change=\"onPageNoChange\" :page-size=\"5\">\n\t\t\t</el-pagination>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport { pageCommentMessages } from '@/api/message.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'MyTopicActivity',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tpageNo: 1,\n\t\t\t\ttotalCount: 2,\n\t\t\t\tcommentMsgs: [],\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tthis.getCommentMsgPage(1);\n\t\t},\n\t\tmethods: {\n\t\t\tgetCommentMsgPage(pageNo) {\n\t\t\t\tlet _this = this;\n\t\t\t\tpageCommentMessages(pageNo, 5).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.commentMsgs = result.data.dataList;\n\t\t\t\t\t\t_this.totalCount = result.data.totalCount;\n\t\t\t\t\t\tif (_this.pageNo == 1) {\n\t\t\t\t\t\t\t_this.$store.state.messageNum = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\t//更新对应页\n\t\t\tonPageNoChange() {\n\t\t\t\tthis.getCommentMsgPage(this.pageNo);\n\t\t\t},\n\t\t}\n\t}\n</script>\n\n<style src=\"./CommentMessage.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyMessage/MyMessage.css",
    "content": ".outer-div {\n\tbackground-color: #F5F5F5;\n}\n\n.wrapper {\n\twidth: 800px;\n\tmargin-left: calc((100% - 800px) / 2);\n\tmargin-top: 15px;\n\tdisplay: block;\n}\n\n.wrapper-left {\n\tposition: relative;\n\twidth: 100px;\n\tdisplay: inline-block;\n\tbackground-color: rgba(255, 255, 255, 0.8);\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 5px #CCCCCC;\n\tpadding-bottom: 525px;\n\ttext-align: center;\n}\n\n.wrapper-left div {\n\tfont-size: 16px;\n\tmargin-top: 20px;\n\tcolor: #666666;\n}\n\n.wrapper-left div:nth-child(1) {\n\tcolor: #666666 !important;\n\tcursor: auto !important;\n}\n\n.wrapper-left div:hover {\n\tcolor: #0B8FFF;\n\tcursor: pointer;\n}\n\n.wrapper-right {\n\twidth: 680px;\n\tvertical-align: top;\n\tmargin-left: 10px;\n\tdisplay: inline-block;\n}\n\n.wrapper-right>div {\n\tborder-radius: 5px;\n\tbackground-color: rgba(255, 255, 255, 0.9);\n\tbox-shadow: 0px 0px 5px #CCCCCC;\n}\n\n.activity-header {\n\tpadding: 15px;\n\tfont-size: 22px;\n\ttext-align: center;\n}\n\n.activity-content {\n\tposition: relative;\n\tpadding: 10px;\n\tmargin-top: 10px;\n\tpadding-bottom: 20px;\n}\n\n\n.activity-medium {\n\tdisplay: inline-block;\n\tvertical-align: top;\n\tmargin-left: 10px;\n\twidth: 300px;\n}\n\n.activity-time {\n\tmargin-top: 5px;\n\tcolor: #999999;\n\tfont-size: 14px;\n}\n\n.activity-right {\n\tdisplay: inline-block;\n\tvertical-align: top;\n\tfloat: right;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyMessage/MyMessage.vue",
    "content": "<template>\n\t<div class=\"outer-div\">\n\t\t<my-header></my-header>\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"wrapper-left\">\n\t\t\t\t<div>\n\t\t\t\t\t我的消息\n\t\t\t\t</div>\n\t\t\t\t<template v-for=\"(tab,index) in tabs\">\n\t\t\t\t\t<div @click=\"tabIndex=index\" :style=\"tabIndex==index?'color:#0B8FFF':''\" :key=\"tab\">\n\t\t\t\t\t\t{{tab}}\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t\t<div class=\"wrapper-right\">\n\t\t\t\t<div class=\"activity-header\">\n\t\t\t\t\t{{tabs[tabIndex]}}\n\t\t\t\t</div>\n\t\t\t\t<comment-message></comment-message>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport CommentMessage from './CommentMessage/CommentMessage.vue'\n\n\timport { pageMyPublishTopic } from '@/api/topic.js'\n\timport { pageMyComment } from '@/api/comment.js'\n\timport { pageMyStar } from '@/api/star.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'MyMessage',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tCommentMessage,\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\ttabIndex: 0,\n\t\t\t\ttabs: ['评论我的'],\n\t\t\t}\n\t\t}\n\t}\n\n</script>\n\n<style scoped src=\"./MyMessage.css\">\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/MyProfile/MyProfile.css",
    "content": ".wrapper{\n\twidth:800px;\n\tmargin-left: calc( (100% - 800px) / 2 );\n\tmargin-top: 15px;\n\tdisplay: block;\n}\n\n.main{\n\tposition:relative;\n\tdisplay: block;\n\tbackground-color: white;\n\tborder-radius: 5px;\n\tbox-shadow:0px 0px 5px #CCCCCC;\n\tpadding-bottom: 100px;\n}\n\n.profile-header{\n\ttop:5px;\n\tposition:relative;\n\theight:60px;\n\tpadding-left: 10px;\n}\n\n.profile-header-title{\n\tposition:relative;\n\ttop:-6px;\n\tdisplay: inline-block;\n\tfont-size:25px;\n\tcolor:#86DAF3;\n}\n\n.profile-header-icon{\n\tfont-size:40px;\n\tcolor:#86DAF3;\n}\n\n.profile-content{\n\tposition: relative;\n\tmargin-left:15%;\n\tmargin-top:15px;\n}\n\n.profile-content-left{\n\twidth:120px;\n\ttext-align: center;\n\tdisplay: inline-block;\n}\n\n.profile-content-right{\n\twidth:400px;\n\tposition: relative;\n\tmargin-left:40px;\n\tmargin-top:20px;\n\tvertical-align: top;\n\tdisplay: inline-block;\n\tcolor:#666666;\n}\n\n/* .profile-content-right>div{\n\theight:20px;\n\tmargin-bottom:10px;\n} */\n\n.item-edit-btn{\n\tdisplay: inline;\n\tposition: absolute;\n\tz-index: 10;\n\tright:0px;\n\tmargin-top:3px;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/MyProfile/MyProfile.vue",
    "content": "<template>\n\t<div>\n\t\t<my-header></my-header>\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"main\">\n\t\t\t\t<div class=\"profile-header\">\n\t\t\t\t\t<i class=\"el-icon-user profile-header-icon\"></i>\n\t\t\t\t\t<div class=\"profile-header-title\">我的信息</div>\n\t\t\t\t\t<div style=\"margin-top:-20px;\">\n\t\t\t\t\t\t<el-divider direction=\"horizontal\"></el-divider>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"profile-content\">\n\t\t\t\t\t<div class=\"profile-content-left\">\n\t\t\t\t\t\t<div style=\"display: inline-block;text-align: right: 10px;\">\n\t\t\t\t\t\t\t<el-avatar :src=\"$store.getters.getPhotoUrl\" :size=\"120\"></el-avatar>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\t<el-button type=\"primary\" @click=\"uploadPhotoVisible=true\" size=\"mini\">更换头像</el-button>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"profile-content-right\">\n\t\t\t\t\t\t<div class=\"item-edit-btn\">\n\t\t\t\t\t\t\t<el-button type=\"primary\" size=\"mini\" @click=\"usernameModifyVisible=true\">修改\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<el-form label-position=\"left\" label-width=\"70px\">\n\n\t\t\t\t\t\t\t<div style=\"margin-bottom:-20px;\">\n\n\t\t\t\t\t\t\t\t<el-form-item label=\"用户名\">\n\t\t\t\t\t\t\t\t\t{{user.username}}\n\t\t\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div style=\"margin-bottom:-20px;\">\n\t\t\t\t\t\t\t\t<el-form-item label=\"邮箱\">\n\t\t\t\t\t\t\t\t\t{{user.email}}\n\t\t\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div style=\"margin-bottom:-20px;\">\n\t\t\t\t\t\t\t\t<el-form-item label=\"注册时间\">\n\t\t\t\t\t\t\t\t\t{{user.registerTime}}\n\t\t\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div style=\"margin-bottom:-20px;\">\n\t\t\t\t\t\t\t\t<el-form-item label=\"话题\">\n\t\t\t\t\t\t\t\t\t{{user.topicCount}}\n\t\t\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div style=\"margin-bottom:-20px;\">\n\t\t\t\t\t\t\t\t<el-form-item label=\"star\">\n\t\t\t\t\t\t\t\t\t{{user.starCount}}\n\t\t\t\t\t\t\t\t</el-form-item>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</el-form>\n\t\t\t\t\t\t<div style=\"text-align: center;\">\n\t\t\t\t\t\t\t<el-button type=\"primary\" size=\"mini\" @click=\"passwordModifyVisible=true\">修改密码\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- <div>\n\t\t\t\t\t\t\t用户名：{{user.username}}\n\t\t\t\t\t\t\t<div class=\"item-edit-btn\">\n\t\t\t\t\t\t\t\t<el-button type=\"primary\" size=\"mini\" @click=\"usernameModifyVisible=true\">修改\n\t\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t\t</div>\n\t\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t邮箱：{{user.email}}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t分享数：996\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\tstar:9\n\t\t\t\t\t\t</div> -->\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t</div>\n\t\t</div>\n\t\t<!-- 修改用户名对话框 -->\n\t\t<el-dialog title=\"修改用户名\" :visible.sync=\"usernameModifyVisible\">\n\t\t\t<el-form>\n\t\t\t\t<el-form-item label=\"新的用户名\">\n\t\t\t\t\t<el-input autocomplete=\"off\" v-model=\"usernameModify\" maxlength=\"12\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<div slot=\"footer\" class=\"dialog-footer\">\n\t\t\t\t<el-button @click=\"usernameModifyVisible = false\">取 消</el-button>\n\t\t\t\t<el-button type=\"primary\" @click=\"onConfirmModifyUsername\">确 定</el-button>\n\t\t\t</div>\n\t\t</el-dialog>\n\n\t\t<!-- 上传头像对话框 -->\n\t\t<el-dialog title=\"上传头像\" :visible.sync=\"uploadPhotoVisible\">\n\t\t\t<el-form>\n\t\t\t\t<el-form-item>\n\t\t\t\t\t<el-upload action=\"https://jsonplaceholder.typicode.com/posts/\" ref=\"upload\"\n\t\t\t\t\t\t:class=\"{'upload-plus-disabled':imageList.length>=1}\" list-type=\"picture-card\"\n\t\t\t\t\t\t:on-remove=\"handleRemove\" :limit=\"1\" :auto-upload=\"false\" accept=\"image/*\"\n\t\t\t\t\t\t:on-exceed=\"exceedLimit\" :on-change=\"handleChange\">\n\t\t\t\t\t\t<i class=\"el-icon-plus\"></i>\n\t\t\t\t\t</el-upload>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<div slot=\"footer\" class=\"dialog-footer\">\n\t\t\t\t<el-button @click=\"uploadPhotoVisible = false\">取 消</el-button>\n\t\t\t\t<el-button type=\"primary\" @click=\"onConfirmUploadPhoto\">确 定</el-button>\n\t\t\t</div>\n\t\t</el-dialog>\n\n\t\t<!-- 修改密码对话框 -->\n\t\t<el-dialog title=\"修改密码\" :visible.sync=\"passwordModifyVisible\">\n\t\t\t<el-form>\n\t\t\t\t<el-form-item label=\"旧密码\">\n\t\t\t\t\t<el-input autocomplete=\"off\" v-model=\"oldPassword\" maxlength=\"16\" show-password></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"新密码\">\n\t\t\t\t\t<el-input autocomplete=\"off\" v-model=\"newPassword\" maxlength=\"16\" show-password></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"确认新密码\">\n\t\t\t\t\t<el-input autocomplete=\"off\" v-model=\"passwordEnsure\" maxlength=\"16\" show-password></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<div slot=\"footer\" class=\"dialog-footer\">\n\t\t\t\t<el-button @click=\"passwordModifyVisible = false\">取 消</el-button>\n\t\t\t\t<el-button type=\"primary\" @click=\"passwordModifyVisible = false;\">确 定</el-button>\n\t\t\t</div>\n\t\t</el-dialog>\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\n\timport { modifyUsername, queryProfile } from '@/api/user.js'\n\timport { uploadPhoto} from '@/api/photo.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'MyProfile',\n\t\tcomponents: {\n\t\t\tMyHeader\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\t//修改用户名\n\t\t\t\tusernameModifyVisible: false,\n\t\t\t\tusernameModify: '',\n\t\t\t\t//修改密码\n\t\t\t\tpasswordModifyVisible: false,\n\t\t\t\toldPassword: '',\n\t\t\t\tnewPassword: '',\n\t\t\t\tpasswordEnsure: '',\n\t\t\t\t//上传头像\n\t\t\t\tuploadPhotoVisible: false,\n\t\t\t\timageList: [],\n\t\t\t\t//用户个人信息\n\t\t\t\tuser: {}\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\tlet _this = this;\n\t\t\tqueryProfile().then(result => {\n\t\t\t\t_this.user = result.data;\n\t\t\t\t_this.usernameModify = _this.user.username;\n\t\t\t})\n\n\t\t},\n\t\tmethods: {\n\t\t\thandleRemove(file, fileList) {\n\t\t\t\tconsole.log(file, fileList);\n\t\t\t},\n\t\t\thandleChange(file, fileList) {\n\t\t\t\tthis.imageList = fileList;\n\t\t\t\tconsole.log(this.imageList);\n\t\t\t},\n\t\t\texceedLimit() {\n\t\t\t\tMessageUtils.notice('只能选择1张图');\n\t\t\t},\n\t\t\t//修改用户名\n\t\t\tonConfirmModifyUsername() {\n\t\t\t\tlet _this = this;\n\t\t\t\tmodifyUsername(this.usernameModify).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.$store.commit('setToken',result.data);\n\t\t\t\t\t\tMessageUtils.success(\"修改成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\tonConfirmUploadPhoto() {\n\t\t\t\tif (this.imageList.length == 0) {\n\t\t\t\t\tMessageUtils.notice(\"请选择至少一张图片\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//将photo加入到表单数据中\n\t\t\t\tlet reqData = new FormData();\n\t\t\t\tlet photo = this.imageList[0];\n\t\t\t\treqData.append(\"photoFile\", photo.raw);\n\n\t\t\t\tlet _this = this;\n\t\t\t\tuploadPhoto(reqData).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.$store.commit('setToken',result.data);\n\t\t\t\t\t\tMessageUtils.success(\"上传成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(1);\n\t\t\t\t\t}\n\t\t\t\t})\n\n\n\t\t\t\t// $.ajax({\n\t\t\t\t// \turl: \"/api/users/uploadPhoto\",\n\t\t\t\t// \ttype: \"post\", //请求方式\n\t\t\t\t// \tdata: reqData, //请求数据\n\t\t\t\t// \tprocessData: false, //是否将请求数据转换为对象\n\t\t\t\t// \tcontentType: false, //发送数据到服务器时所使用的内容类型，false表示不需要ajax处理\n\t\t\t\t// \tdataType: 'json',\n\t\t\t\t// \tsuccess: function(result) {\n\t\t\t\t// \t\tif (result.code == Code.OK) {\n\t\t\t\t// \t\t\t_this.passwordModifyVisible = false;\n\t\t\t\t// \t\t\t_this.popupSuccess(\"上传成功\");\n\t\t\t\t// \t\t\tsetTimeout(function() {\n\t\t\t\t// \t\t\t\twindow.location.reload();\n\t\t\t\t// \t\t\t}, 1000);\n\t\t\t\t// \t\t} else {\n\t\t\t\t// \t\t\t_this.popupHint(result.msg);\n\t\t\t\t// \t\t}\n\t\t\t\t// \t},\n\t\t\t\t// \terror: function() {\n\t\t\t\t// \t\t_this.popupHint(\"提交失败\");\n\t\t\t\t// \t}\n\t\t\t\t// });\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./MyProfile.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/PublishTopic/PublishTopic.css",
    "content": ".wrapper {\n\twidth: 1200px;\n\tmargin-left: calc((100% - 1200px) / 2);\n\tmargin-top: 15px;\n\tdisplay: block;\n\tbackground-color: white;\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 5px #CCCCCC;\n\tpadding-bottom: 200px;\n}\n\n.wrapper-header {\n\ttop: 5px;\n\tposition: relative;\n\theight: 60px;\n\ttext-align: center;\n\tmargin-bottom: 30px;\n}\n\n.header-title {\n\tposition: relative;\n\ttop: -6px;\n\tdisplay: inline-block;\n\tfont-size: 25px;\n\tcolor: #999999;\n}\n\n.header-icon {\n\tfont-size: 40px;\n\tcolor: #E5E544;\n}\n\n.upload-container {\n\twidth: 320px;\n\theight: 148px;\n\tborder: 2px solid skyblue;\n\tborder-radius: 5px;\n\tmargin-top: 20px;\n\ttext-align: center;\n}\n\n.upload-hint {\n\tcolor: red;\n\tfont-size: 15px;\n\tfont-family: 'Times New Roman', Times, serif;\n}\n\n.cropper-container{\n\ttext-align:center;\n\twidth: 300px;\n\theight: 300px;\n\tmargin-left: 100px;\n}\n\n.mr5 {\n\tmargin-right: 5px;\n}\n\n\n\n.active-blue:hover {\n\tbackground-color: blue !important;\n\ttransition: 1s;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/views/PublishTopic/PublishTopic.vue",
    "content": "<template>\n\t<div>\n\t\t<my-header></my-header>\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"wrapper-header\">\n\t\t\t\t<i class=\"el-icon-s-promotion header-icon\" style=\"\"></i>\n\t\t\t\t<div class=\"header-title\">快来分享吧</div>\n\t\t\t\t<div style=\"margin-top:-20px;\">\n\t\t\t\t\t<el-divider direction=\"horizontal\"></el-divider>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<el-form ref=\"form\" label-width=\"160px\" style=\"width: 600px;\" :model=\"addForm\">\n\t\t\t\t<el-form-item label=\"标题\">\n\t\t\t\t\t<el-input v-model=\"addForm.title\" prefix-icon=\"el-icon-edit-outline\" maxlength=\"30\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"分类\">\n\t\t\t\t\t<el-select v-model=\"addForm.categoryId\" placeholder=\"请选择分类\" clearable>\n\t\t\t\t\t\t<el-option v-for=\"item in $store.state.categoryList\" :label=\"item.label\" :value=\"item.id\"\n\t\t\t\t\t\t\t:key=\"item.id\">\n\t\t\t\t\t\t</el-option>\n\t\t\t\t\t</el-select>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"标签(1-3个)\">\n\t\t\t\t\t<el-tag v-for=\"item in chosenTagList\" :key=\"item.id\" class=\"hover-pointer mr5\"\n\t\t\t\t\t\t@close=\"removeTag(item)\" :type=\"$global.buttonType(item.id)\" closable>\n\t\t\t\t\t\t{{item.label}}\n\t\t\t\t\t</el-tag>\n\t\t\t\t\t<br />\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<el-tag v-for=\"item in $store.state.tagList\" :key=\"item.id\" :type=\"$global.buttonType(item.id)\"\n\t\t\t\t\t\t\t@click=\"clickTag(item)\" class=\"hover-pointer mr5\">\n\t\t\t\t\t\t\t{{item.label}}\n\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t</div>\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-form-item label=\"封面\">\n\t\t\t\t\t<div class=\"upload-container\">\n\t\t\t\t\t\t<el-upload action=\"#\" ref=\"upload\" list-type=\"picture-card\"\n\t\t\t\t\t\t\t:class=\"{'upload-plus-disabled':imageList.length>=1}\" :on-preview=\"handlePictureCardPreview\"\n\t\t\t\t\t\t\t:on-remove=\"handleRemove\" :limit=\"1\" :auto-upload=\"false\" accept=\"image/*\"\n\t\t\t\t\t\t\t:on-exceed=\"exceedLimit\" :on-change=\"handleChange\">\n\t\t\t\t\t\t\t<i class=\"el-icon-plus\"></i>\n\t\t\t\t\t\t</el-upload>\n\t\t\t\t\t\t<el-dialog :visible.sync=\"dialogVisible\">\n\t\t\t\t\t\t\t<img width=\"100%\" :src=\"dialogImageUrl\" alt=\"\">\n\t\t\t\t\t\t</el-dialog>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"upload-hint\">\n\t\t\t\t\t\t图片大小上限1MB (支持格式：jpg,png,jpeg,webp)\n\t\t\t\t\t</div>\n\n\t\t\t\t</el-form-item>\n\t\t\t\t<el-dialog :visible.sync=\"cropperVisible\">\n\t\t\t\t\t<div  class=\"cropper-container\">\n\t\t\t\t\t\t<vueCropper ref=\"cropper\" :img=\"imageUrl\" :outputSize=\"option.size\"\n\t\t\t\t\t\t\t:outputType=\"option.outputType\" :info=\"true\" :full=\"option.full\" :canMove=\"option.canMove\"\n\t\t\t\t\t\t\t:canMoveBox=\"option.canMoveBox\" :original=\"option.original\" :autoCrop=\"option.autoCrop\"\n\t\t\t\t\t\t\t:fixed=\"option.fixed\" :fixedNumber=\"option.fixedNumber\" :centerBox=\"option.centerBox\"\n\t\t\t\t\t\t\t:infoTrue=\"option.infoTrue\" :fixedBox=\"option.fixedBox\">\n\t\t\t\t\t\t</vueCropper>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div slot=\"footer\" class=\"dialog-footer\">\n\t\t\t\t\t\t<el-button size=\"mini\" @click=\"handleCancelCropper\">取 消</el-button>\n\t\t\t\t\t\t<el-button size=\"mini\" type=\"primary\" @click=\"confirmCoverImage\">确认</el-button>\n\t\t\t\t\t</div>\n\t\t\t\t</el-dialog>\n\t\t\t</el-form>\n\n\t\t\t<div style=\"text-align: center;\">\n\t\t\t\t<edit-board ref=\"editBoard\" margin=\"160px\" width=\"820px\"></edit-board>\n\t\t\t\t<el-button @click=\"submitTopic\" type=\"primary\" style=\"margin-top: 20px;\">提交</el-button>\n\t\t\t</div>\n\t\t</div>\n\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport EditBoard from '@/components/EditBoard/EditBoard.vue'\n\timport { VueCropper } from 'vue-cropper'\n\n\n\timport { addTopic } from '@/api/topic.js'\n\timport { uploadTopicImages } from '@/api/image.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport MessageUtils from '@/utils/MessageUtils'\n\timport StringUtils from '@/utils/StringUtils'\n\n\timport { queryAllCategories } from \"@/api/category.js\"\n\timport { queryAllTags } from \"@/api/tag.js\"\n\n\texport default {\n\t\tname: 'PublishTopic',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tEditBoard,\n\t\t\tVueCropper\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tdialogImageUrl: '',\n\t\t\t\tdialogVisible: false,\n\t\t\t\tcropperVisible: false,\n\t\t\t\taddForm: {\n\t\t\t\t\ttitle: '',\n\t\t\t\t\tcategoryId: null,\n\t\t\t\t\tcoverImage: {},\n\t\t\t\t},\n\t\t\t\timageUrl: '',\n\t\t\t\timageBlob: null,\n\t\t\t\timageList: [],\n\t\t\t\ttempFile: null,\n\t\t\t\timageType: null,\n\t\t\t\tchosenTagList: [],\n\n\t\t\t\toption: {\n\t\t\t\t\timg: '', // 裁剪图片地址，这里可以本地图片或者链接，链接不用require\n\t\t\t\t\toutputSize: 1, // 裁剪生成图片质量\n\t\t\t\t\toutputType: 'png', // 裁剪生成图片格式\n\t\t\t\t\tcanScale: true, // 图片是否允许滚轮播放\n\t\t\t\t\tautoCrop: true, // 是否默认生成截图框 false\n\t\t\t\t\tinfo: false, // 是否展示截图框信息\n\t\t\t\t\tcanMoveBox: true, // 截图框是否可以拖动\n\t\t\t\t\tfixedNumber: [135, 130],\n\t\t\t\t\tfixedBox: false, // 固定截图框的大小\n\t\t\t\t\tfixed: true,\n\t\t\t\t\tcanMove: false, // 上传图片是否可拖动\n\t\t\t\t\tcenterBox: true, // 截图框限制在图片里面\n\t\t\t\t},\n\n\t\t\t};\n\t\t},\n\t\tmethods: {\n\t\t\ttest() {\n\t\t\t\talert(this.$refs['editBoard'].Html);\n\t\t\t},\n\t\t\thandleRemove(file, fileList) {\n\t\t\t\tthis.imageUrl = '';\n\t\t\t\tconsole.log(file, fileList);\n\t\t\t},\n\t\t\thandleCancelCropper() {\n\t\t\t\tthis.cropperVisible = false;\n\t\t\t\tthis.imageUrl = '';\n\t\t\t},\n\t\t\tconfirmCoverImage() {\n\t\t\t\tthis.$refs['cropper'].getCropBlob((blob) => {\n\t\t\t\t\tthis.imageBlob = blob;\n\t\t\t\t})\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tthis.tempFile.url = window.URL.createObjectURL(this.imageBlob);\n\t\t\t\t\tthis.imageList.push(this.tempFile);\n\t\t\t\t}, 100)\n\t\t\t\tthis.cropperVisible = false;\n\n\t\t\t},\n\t\t\thandleChange(file, fileList) {\n\t\t\t\tthis.cropperVisible = true;\n\t\t\t\tthis.imageList = fileList;\n\t\t\t\tlet raw = file.raw;\n\t\t\t\tthis.imageUrl = window.URL.createObjectURL(raw);\n\t\t\t\tthis.imageType = raw.type;\n\t\t\t\tthis.tempFile = file;\n\t\t\t\tconsole.log(file)\n\t\t\t\tfileList.splice(0, 1);\n\t\t\t},\n\t\t\thandlePictureCardPreview(file) {\n\t\t\t\tthis.dialogImageUrl = file.url;\n\t\t\t\tthis.dialogVisible = true;\n\t\t\t},\n\t\t\texceedLimit() {\n\t\t\t\tMessageUtils.notice('最多上传1张图');\n\t\t\t},\n\t\t\tclickTag(tag) {\n\t\t\t\t//长度限制，超过三个停止\n\t\t\t\tif(this.chosenTagList.length>=3){\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//判断重复\n\t\t\t\tfor (let item of this.chosenTagList) {\n\t\t\t\t\tif (tag.id == item.id) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.chosenTagList.push(tag);\n\t\t\t},\n\t\t\tremoveTag(tag) {\n\t\t\t\tfor (let i = 0; i < this.chosenTagList.length; i++) {\n\t\t\t\t\tlet item = this.chosenTagList[i];\n\t\t\t\t\tif (tag.id == item.id) {\n\t\t\t\t\t\tthis.chosenTagList.splice(i, 1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tvalidateForm() {\n\t\t\t\tif (this.imageList.length == 0) {\n\t\t\t\t\tMessageUtils.notice(\"请选择封面\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (this.addForm.title.length < 4) {\n\t\t\t\t\tMessageUtils.notice(\"标题字数4以上\");\n\t\t\t\t\treturn false;\n\t\t\t\t} else if (this.addForm.title.length > 30) {\n\t\t\t\t\tMessageUtils.notice(\"标题字数30以内\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tlet text = this.$refs['editBoard'].Html.toString();\n\t\t\t\tlet pureText = StringUtils.html2Text(text);\n\t\t\t\tconsole.log(pureText)\n\t\t\t\tif (pureText.trim().length < 4) {\n\t\t\t\t\tMessageUtils.notice(\"内容至少要四个字符\");\n\t\t\t\t\treturn false;\n\t\t\t\t} \n\t\t\t\telse if (text.length > 4500) {\n\t\t\t\t\tMessageUtils.notice(\"文本过长，请缩减\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tsubmitTopic() {\n\t\t\t\tif (this.validateForm() == false) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet addTopicForm = new FormData();\n\t\t\t\t//加图片加入到表单数据中\n\n\t\t\t\t// addTopicForm.append(\"coverImage\", this.imageList[0].raw);\n\t\t\t\taddTopicForm.append(\"html\", this.$refs['editBoard'].Html);\n\t\t\t\taddTopicForm.append(\"title\", this.addForm.title);\n\t\t\t\taddTopicForm.append(\"categoryId\", this.addForm.categoryId);\n\t\t\t\tfor (let tag of this.chosenTagList) {\n\t\t\t\t\taddTopicForm.append(\"tagIds\", tag.id);\n\t\t\t\t}\n\t\t\t\tlet _this = this;\n\t\t\t\t//新的文件名\n\t\t\t\tlet filename = this.tempFile.name;\n\t\t\t\tlet index = filename.indexOf(\".\");\n\t\t\t\tfilename = filename.substring(0, index) + \".png\";\n\t\t\t\taddTopicForm.append(\"coverImage\", new File([this.imageBlob], filename, { type: \"image/png\" }));\n\t\t\t\taddTopic(addTopicForm).then(res => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"发表成功\", 1);\n\t\t\t\t\t\tlet url = '/topic/' + res.data;\n\t\t\t\t\t\tsetTimeout(() => { _this.$router.push({ path: url }); }, 500);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t}\n\t}\n</script>\n\n<style src=\"./PublishTopic.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/SearchImage/SearchImage.css",
    "content": "body{\n\tbackground-color: #F5F5F5;\n\tmargin:0px;\n\tpadding:0px;\n}\n\n.upload-plus-disabled .el-upload--picture-card {\n\tdisplay: none;\n}\t\t\t\t\t\t\n\n.search-main{\n\twidth:1040px;\n\tmargin-left: calc( (100% - 510px - 520px) / 2 );\n\tmargin-top: 15px;\n\tdisplay: block;\n\tbackground-color: white;\n\tborder-radius: 5px;\n\tbox-shadow:0px 0px 5px #CCCCCC;\n\tpadding-bottom: 200px;\n}\n\n.search-header{\n\ttop:5px;\n\tposition:relative;\n\theight:60px;\n\ttext-align: center;\n}\n\n.search-header-title{\n\tposition:relative;\n\ttop:-6px;\n\tdisplay: inline-block;\n\tfont-size:25px;\n\tcolor:#999999;\n}\n\n.search-header-icon{\n\tfont-size:40px;\n\tcolor:#E5E544;\n}\n\n.upload-container{\n\twidth:400px;\n\tmargin-left: calc(50% - 400px / 2);\n}\n\n.upload-hint{\n\tcolor:red;\n\tfont-size:15px;\n\tfont-family:'Times New Roman', Times, serif;\n\tmargin-bottom:5px;\n}\n\n.search-result-container{\n\tmargin-top:20px;\n\tmargin-left:100px;\n\tmargin-right:100px;\n}\n\n.result-image-container{\n\tmargin-right:10px;\n\theight:250px;\n\twidth:150px;\n\tdisplay:inline-block;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/SearchImage/SearchImage.vue",
    "content": "<template>\n\t<div id=\"app\">\n\t\t<my-header></my-header>\n\t\t<div class=\"search-main\">\n\t\t\t<div class=\"search-header\">\n\t\t\t\t<i class=\"el-icon-search search-header-icon\" style=\"\"></i>\n\t\t\t\t<div class=\"search-header-title\">以图搜图</div>\n\t\t\t\t<div style=\"margin-top:-20px;\">\n\t\t\t\t\t<el-divider direction=\"horizontal\"></el-divider>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div style=\"text-align: center;\">\n\t\t\t\t<div class=\"upload-container\">\n\t\t\t\t\t<el-upload action=\"#\" ref=\"upload\" list-type=\"picture-card\"\n\t\t\t\t\t\t:class=\"{'upload-plus-disabled':imageList.length>=1}\" :on-preview=\"handlePictureCardPreview\"\n\t\t\t\t\t\t:on-remove=\"handleRemove\" :limit=\"1\" :multiple=\"true\" :auto-upload=\"false\" accept=\"image/*\"\n\t\t\t\t\t\t:on-exceed=\"exceedLimit\" :on-change=\"handleChange\">\n\t\t\t\t\t\t<i class=\"el-icon-plus\"></i>\n\t\t\t\t\t</el-upload>\n\t\t\t\t\t<el-dialog :visible.sync=\"dialogVisible\">\n\t\t\t\t\t\t<img width=\"100%\" :src=\"dialogImageUrl\" alt=\"\">\n\t\t\t\t\t</el-dialog>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"upload-hint\">\n\t\t\t\t\t*选择你要识别的图片（2MB以内）\n\t\t\t\t</div>\n\t\t\t\t<el-button @click=\"submitImage\" type=\"primary\">提交</el-button>\n\t\t\t</div>\n\n\n\t\t\t<el-empty v-if=\"!haveSearched\" description=\"快来搜索吧\"></el-empty>\n\t\t\t<el-empty v-else-if=\"$global.isEmpty(resultImages)\"></el-empty>\n\t\t\t<div v-else class=\" search-result-container\">\n\t\t\t\t<template v-for=\"image in resultImages\">\n\t\t\t\t\t<router-link class=\"no-underline\" :to=\"$global.getTopicUrl(image.topic.id)\" :key=\"image.id\">\n\t\t\t\t\t\t<div class=\"result-image-container\">\n\t\t\t\t\t\t\t<float-image :title=\"image.topic.title\" :star=\"image.topic.starCount\"\n\t\t\t\t\t\t\t\t:page-view=\"image.topic.pageView\" :username=\"image.topic.user.username\"\n\t\t\t\t\t\t\t\t:create-time=\"image.topic.createTime\"\n\t\t\t\t\t\t\t\t:photo-url=\"$global.getPhotoUrl(image.topic.user.photoUrl)\" :image-url=\"image.url\">\n\t\t\t\t\t\t\t</float-image>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t</router-link>\n\t\t\t\t</template>\n\t\t</div>\n\t</div>\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport FloatImage from '@/components/FloatImage/FloatImage.vue'\n\n\timport { searchImagesByImage } from '@/api/image.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'SearchImage',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tFloatImage\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tdialogImageUrl: '',\n\t\t\t\tdialogVisible: false,\n\t\t\t\timageList: [],\n\n\t\t\t\tisShowResult: false,\n\t\t\t\thaveSearched: false,\n\t\t\t\thint: '快来搜索吧',\n\t\t\t\tresultImages: [{\n\t\t\t\t\tid: 0,\n\t\t\t\t\tdescription: '快来搜索吧',\n\t\t\t\t\turl: '',\n\t\t\t\t\ttopic: {\n\t\t\t\t\t\tid: 404,\n\t\t\t\t\t\ttitle: '快来搜索吧',\n\t\t\t\t\t\tcreateTime: '2022-2-22 22:22:22',\n\t\t\t\t\t\tstarCount: 888,\n\t\t\t\t\t\tpageView: 888,\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tusername: '快来搜索吧',\n\t\t\t\t\t\t\tphotoUrl: ''\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}]\n\t\t\t};\n\t\t},\n\t\tmounted() {\n\n\t\t},\n\t\tmethods: {\n\t\t\thandleRemove(file, fileList) {\n\t\t\t\tconsole.log(file, fileList);\n\t\t\t},\n\t\t\thandleChange(file, fileList) {\n\t\t\t\tthis.imageList = fileList;\n\t\t\t\tconsole.log(this.imageList);\n\t\t\t},\n\t\t\thandlePictureCardPreview(file) {\n\t\t\t\tthis.dialogImageUrl = file.url;\n\t\t\t\tthis.dialogVisible = true;\n\t\t\t},\n\t\t\texceedLimit() {\n\t\t\t\tMessageUtils.notice('只能选择1张图');\n\t\t\t},\n\t\t\tsubmitImage() {\n\t\t\t\tif (this.imageList.length == 0) {\n\t\t\t\t\tMessageUtils.notice(\"请选择一张图片\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet reqData = new FormData();\n\t\t\t\tlet image = this.imageList[0];\n\t\t\t\treqData.append(\"imageFile\", image.raw);\n\n\t\t\t\tlet _this = this;\n\t\t\t\tsearchImagesByImage(reqData).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.resultImages = result.data;\n\t\t\t\t\t\t_this.haveSearched = true;\n\t\t\t\t\t\tMessageUtils.success(\"搜索成功\");\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t// $.ajax({\n\t\t\t\t// \turl: \"/api/files/images/searchByImage\",\n\t\t\t\t// \ttype: \"post\", //请求方式\n\t\t\t\t// \tdata: reqData, //请求数据\n\t\t\t\t// \tprocessData: false, //是否将请求数据转换为对象\n\t\t\t\t// \tcontentType: false, //发送数据到服务器时所使用的内容类型，false表示不需要ajax处理\n\t\t\t\t// \tdataType: 'json',\n\t\t\t\t// \tsuccess: function(result) {\n\t\t\t\t// \t\tif (result.code == Code.OK) {\n\t\t\t\t// \t\t\t_this.resultImages = result.data;\n\t\t\t\t// \t\t\t_this.popupSuccess(\"搜索成功\");\n\t\t\t\t// \t\t} else {\n\t\t\t\t// \t\t\t_this.popupHint(result.msg);\n\t\t\t\t// \t\t}\n\t\t\t\t// \t},\n\t\t\t\t// \terror: function() {\n\t\t\t\t// \t\t_this.popupHint(\"网络异常，提交失败\");\n\t\t\t\t// \t}\n\t\t\t\t// });\n\t\t\t}\n\n\t\t}\n\t}\n</script>\n\n<style src=\"./SearchImage.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/SearchTopic/SearchTopic.css",
    "content": "\n.wrapper{\n\twidth:1040px;\n\tmargin-left: calc( (100% - 510px - 520px) / 2 );\n\tmargin-top: 15px;\n\t\n\tdisplay: block;\n\tbackground-color: white;\n\tborder-radius: 5px;\n\t/* box-shadow:0px 0px 5px #CCCCCC; */\n\tpadding-bottom: 200px;\n}\n\n.wrapper-top{\n\tpadding-top: 20px;\n\tpadding-bottom: 20px;\n\twidth:100%;\n\tborder-radius: 5px;\n\tbox-shadow:0px 0px 5px #CCCCCC;\n}\n\n.wrapper-header{\n\ttop:5px;\n\tposition:relative;\n\theight:60px;\n\ttext-align: center;\n\tmargin-bottom: 30px;\n}\n\n.header-title{\n\tposition:relative;\n\ttop:-6px;\n\tdisplay: inline-block;\n\tfont-size:25px;\n\tcolor:#999999;\n}\n\n.header-icon{\n\tfont-size:40px;\n\tcolor:#E5E544;\n}\n\n\n.mr5{\n\tmargin-right: 5px;\n}\n\n.active-blue:hover{\n\tbackground-color:blue !important;\n\ttransition: 1s;\n}"
  },
  {
    "path": "vue_acimage_web/src/views/SearchTopic/SearchTopic.vue",
    "content": "<template>\n\t<div>\n\t\t<my-header></my-header>\n\t\t<div class=\"wrapper\">\n\t\t\t<div class=\"wrapper-top\">\n\t\t\t\t<el-form ref=\"form\" label-width=\"120px\" style=\"width: 900px;\" :model=\"query\">\n\t\t\t\t\t<el-form-item label=\"关键词\">\n\t\t\t\t\t\t<el-input @keydown.native.enter=\"submitSearch\" style=\"width:300px;\" v-model=\"query.search\"\n\t\t\t\t\t\t\tprefix-icon=\"el-icon-edit-outline\" maxlength=\"15\" clearable></el-input>\n\t\t\t\t\t\t<el-button @click=\"submitSearch\" style=\"margin-left:30px;\" type=\"primary\" plain>提交搜索</el-button>\n\t\t\t\t\t</el-form-item>\n\t\t\t\t\t<el-form-item label=\"分类\" prop=\"categoryId\">\n\t\t\t\t\t\t<el-select v-model=\"query.categoryId\" placeholder=\"选择分类\" clearable>\n\t\t\t\t\t\t\t<el-option v-for=\"item in $store.state.categoryList\" :label=\"item.label\" :value=\"item.id\"\n\t\t\t\t\t\t\t\t:key=\"item.id\">\n\t\t\t\t\t\t\t</el-option>\n\t\t\t\t\t\t</el-select>\n\t\t\t\t\t</el-form-item>\n\t\t\t\t\t<el-form-item label=\"标签\">\n\t\t\t\t\t\t<el-tag v-if=\"query.tagId!=null\" class=\"hover-pointer mr5\" @close=\"query.tagId=null\"\n\t\t\t\t\t\t\t:type=\"$global.buttonType(query.tagId)\" closable>\n\t\t\t\t\t\t\t{{$store.getters.tagLabel(query.tagId)}}\n\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<el-tag v-for=\"item in $store.state.tagList\" :key=\"item.id\"\n\t\t\t\t\t\t\t\t:type=\"$global.buttonType(item.id)\" @click=\"query.tagId=item.id\"\n\t\t\t\t\t\t\t\tclass=\"hover-pointer mr5\">\n\t\t\t\t\t\t\t\t{{item.label}}\n\t\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</el-form-item>\n\t\t\t\t\t<el-form-item label=\"排序\">\n\t\t\t\t\t\t<el-tabs v-model=\"query.sortMode\" tab-position=\"top\" style=\"width:380px;\"\n\t\t\t\t\t\t\t@tab-click=\"handleTabClick\">\n\t\t\t\t\t\t\t<el-tab-pane v-for=\"(label,index) in sortLabels\" :label=\"label\" :name=\"sortKeys[index]\"\n\t\t\t\t\t\t\t\t:key=\"label\">\n\t\t\t\t\t\t\t</el-tab-pane>\n\t\t\t\t\t\t</el-tabs>\n\t\t\t\t\t</el-form-item>\n\t\t\t\t</el-form>\n\n\t\t\t\t<div style=\"margin-left: 120px;\">\n\t\t\t\t\t<el-skeleton v-if=\"loading\" :rows=\"6\" animated />\n\t\t\t\t\t<el-empty v-else-if=\"$global.isEmpty(topics)\" image=\"static/image/sad.jpeg\"\n\t\t\t\t\t\tdescription=\"没有你想要的结果 ┑(￣Д ￣)┍\"></el-empty>\n\t\t\t\t\t<div v-else v-for=\"topic in topics\" :key=\"topic.id\" style=\"margin-bottom: 5px;\">\n\t\t\t\t\t\t<topic-card :title=\"topic.title\" :html=\"topic.content\" :updateTime=\"topic.activityTime\"\n\t\t\t\t\t\t\t:starCount=\"topic.starCount\" :commentCount=\"topic.commentCount\" :pageView=\"topic.pageView\"\n\t\t\t\t\t\t\t:username=\"topic.user.username\" :photoUrl=\"$global.getPhotoUrl(topic.user.photoUrl)\"\n\t\t\t\t\t\t\t:to=\"$global.getTopicUrl(topic.id)\" :categoryId=\"topic.categoryId\" :tagIds=\"topic.tagIds\"\n\t\t\t\t\t\t\t:coverImageUrl=\"topic.coverImageUrl\" titleHtml contentHtml>\n\t\t\t\t\t\t</topic-card>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div style=\"text-align: center;\">\n\t\t\t\t\t<el-pagination background layout=\"prev, pager, next\" :total=\"totalCount\"\n\t\t\t\t\t\t:current-page.sync=\"curPage\" @current-change=\"handlePageChange\" :page-size=\"10\">\n\t\t\t\t\t</el-pagination>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport TopicCard from '@/components/TopicCard/TopicCard.vue'\n\n\timport { Code } from '@/utils/result.js'\n\timport MessageUtils from '@/utils/MessageUtils'\n\timport StringUtils from '@/utils/StringUtils'\n\timport CommonUtils from '@/utils/CommonUtils'\n\n\timport { pageRencentTopic } from '@/api/topic.js'\n\timport { searchTopics } from '@/api/TopicSearch'\n\n\n\tlet sortModes = {\n\t\tNORMAL: '相关性',\n\t\tCREATE_TIME: '创建时间',\n\t\tSTAR_COUNT: 'star',\n\t\tCOMMENT_COUNT: '评论数',\n\t\tPAGE_VIEW: '浏览量',\n\t};\n\n\texport default {\n\t\tname: 'SearchTopic',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tTopicCard\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tcurPage: 1,\n\t\t\t\ttotalCount: 1,\n\t\t\t\tloading: false,\n\t\t\t\tquery: {\n\t\t\t\t\tsearch: '',\n\t\t\t\t\tpageNo: 1,\n\t\t\t\t\tcategoryId: null,\n\t\t\t\t\ttagId: null,\n\t\t\t\t\tsortMode: 'NORMAL'\n\t\t\t\t},\n\t\t\t\ttopics: []\n\t\t\t};\n\t\t},\n\t\tcomputed: {\n\t\t\tsortKeys() {\n\t\t\t\treturn Object.keys(sortModes);\n\t\t\t},\n\t\t\tsortLabels() {\n\t\t\t\tlet keys = this.sortKeys;\n\t\t\t\tlet labels = [];\n\t\t\t\tfor (let key of keys) {\n\t\t\t\t\tlabels.push(sortModes[key])\n\t\t\t\t}\n\t\t\t\treturn labels;\n\t\t\t},\n\t\t},\n\t\twatch: {\n\t\t\t'$route'(to, from) {\n\t\t\t\tCommonUtils.copyPropertiesTo(this.$route.query, this.query)\n\t\t\t\tthis.toSearch();\n\t\t\t\t// CommonUtils.copyPropertiesTo(this.$route.query, this.query)\n\t\t\t\t// if (!CommonUtils.isEmpty(this.query.search.trim()) ||\n\t\t\t\t// \tthis.query.pageNo != 1 ||\n\t\t\t\t// \tthis.query.categoryId != null ||\n\t\t\t\t// \tthis.query.tagId != null ||\n\t\t\t\t// \tthis.query.sortMode != 'NORMAL') {\n\t\t\t\t// \tif (to.path == from.path) {\n\t\t\t\t// \t\tthis.toSearch();\n\t\t\t\t// \t}\n\t\t\t\t// }\n\t\t\t},\n\t\t},\n\t\tcreated() {\n\t\t\tCommonUtils.copyPropertiesTo(this.$route.query, this.query)\n\t\t\tif (!CommonUtils.isEmpty(this.query.search.trim()) ||\n\t\t\t\tthis.query.pageNo != 1 ||\n\t\t\t\tthis.query.categoryId != null ||\n\t\t\t\tthis.query.tagId != null ||\n\t\t\t\tthis.query.sortMode != 'NORMAL') {\n\n\t\t\t\tthis.toSearch();\n\t\t\t}\n\t\t},\n\t\tmethods: {\n\t\t\thandleTabClick(tab) {\n\t\t\t\tthis.query.sortMode = this.sortKeys[tab.index];\n\t\t\t},\n\t\t\thandlePageChange() {\n\t\t\t\tthis.query.pageNo = this.curPage;\n\t\t\t\tthis.toSearch();\n\t\t\t},\n\t\t\tsubmitSearch() {\n\t\t\t\tif (JSON.stringify(this.query) != JSON.stringify(this.$route.query)) {\n\t\t\t\t\t// this.$router.replace({ query: this.query })\n\t\t\t\t\tthis.toSearch();\n\t\t\t\t} else {\n\t\t\t\t\tMessageUtils.notice(\"请改变搜索选项\")\n\t\t\t\t}\n\n\t\t\t},\n\t\t\ttoSearch() {\n\t\t\t\tlet _this = this;\n\t\t\t\tif (CommonUtils.isEmpty(this.query.categoryId)) {\n\t\t\t\t\tthis.query.categoryId = null;\n\t\t\t\t}\n\t\t\t\tthis.loading = true;\n\t\t\t\tsearchTopics(_this.query).then(res => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"搜索成功\", 1);\n\t\t\t\t\t\t_this.topics = res.data.dataList;\n\t\t\t\t\t\t_this.totalCount = res.data.totalCount;\n\t\t\t\t\t}\n\t\t\t\t\tthis.loading = false;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./SearchTopic.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/TopicInfo/PublishComment/PublishComment.css",
    "content": "\n.user-comment-container{\n\ttext-align: center;\n\twidth:100%;\n\tmargin-bottom: 20px;\n}\n\n.user-comment-container>div{\n\tdisplay: inline-block;\n\tvertical-align: top;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/views/TopicInfo/PublishComment/PublishComment.vue",
    "content": "<template>\n\t<div class=\"user-comment-container\">\n\t\t<div>\n\t\t\t<el-avatar :src=\"$store.getters.getPhotoUrl\" :size=\"60\"></el-avatar>\n\t\t</div>\n\t\t<div>\n\t\t\t<el-input type=\"textarea\" :rows=\"3\" style=\"width:400px;\" maxlength=\"200\" placeholder=\"快来发表友好的评论吧~\"\n\t\t\t\tv-model=\"content\">\n\t\t\t</el-input>\n\t\t</div>\n\t\t<el-button type=\"primary\" style=\"height:74px;margin-left: 5px;\" @click=\"submitComment\">评论</el-button>\n\t</div>\n</template>\n\n<script>\n\timport { addComment } from '@/api/comment.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils.js'\n\timport MessageUtils from '@/utils/MessageUtils.js'\n\n\texport default {\n\t\tname: 'PublishComment',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tcontent: ''\n\t\t\t}\n\t\t},\n\t\tprops: {\n\t\t\ttopicId: {},\n\t\t},\n\t\tmethods: {\n\t\t\tsubmitComment() {\n\t\t\t\tlet data = {\n\t\t\t\t\t'topicId': this.topicId,\n\t\t\t\t\t'content': this.content\n\t\t\t\t};\n\t\t\t\taddComment(data).then(res => {\n\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"发表成功\");\n\t\t\t\t\t\tCommonUtils.delayRefresh(0.5)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./PublishComment.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/TopicInfo/TopicInfo.css",
    "content": "body {\n\tbackground-color: #F5F5F5;\n\tmargin: 0px;\n\tpadding: 0px;\n}\n\n.wrapper {\n\twidth: 1230px;\n\tmargin-left: calc((100% - 1200px) / 2);\n\tmargin-top: 15px;\n\tdisplay: block;\n\tpadding-bottom:200px;\n}\n\n.wrapper-left {\n\tposition: relative;\n\twidth: 900px;\n\tdisplay: inline-block;\n\tbackground-color: white;\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 5px #CCCCCC;\n\tpadding-bottom: 200px;\n\toverflow: hidden;\n}\n\n.title {\n\ttext-align: center;\n\tfont-size: 25px;\n\tfont-family: 'Times New Roman', Times, serif;\n\tfont-weight: bold;\n\tmargin-top: 20px;\n\tcolor: #666666;\n}\n\n.title-edit-button {\n\tdisplay: inline-block;\n\tfloat: right;\n\tmargin-right: 40px;\n}\n\n.topic-info {\n\tmargin-left: 5%;\n\tmargin-top: 10px;\n\tcolor: #666666;\n}\n\n.tags-container{\n\tmargin-left:10px;\n}\n\n.tags-container>>>.el-tag{\n\tmargin-right:3px;\n}\n\n.time-hint-container {\n\tposition: relative;\n\twidth: 100%;\n\theight: 60px;\n}\n\n.time-hint {\n\tmargin-top: 30px;\n\tmargin-left: 40px;\n\tcolor: #999999;\n}\n\n.content-edit-button {\n\tdisplay: inline-block;\n\tmargin-right: 40px;\n\tfloat: right;\n}\n\n.topic-html-container{\n\twidth:820px;\n\tdisplay: block;\n\tmargin-left:40px;\n\toverflow: hidden;\n}\n\n.comments-container {\n\tmargin-left: 10%;\n\twidth: 80%;\n}\n\n.comments-count {\n\theight: 40px;\n\tcolor: #333333;\n\tmargin-left: -3%;\n\tfont-size: 14px;\n}\n\n.gray-color {\n\tcolor: #AAAAAA;\n}\n\n.wrapper-right {\n\twidth: 300px;\n\tvertical-align: top;\n\tmargin-left: 20px;\n\tdisplay: inline-block;\n\tborder-radius: 5px;\n}\n\n.user-container{\n\ttext-align: center;\n\tpadding-bottom: 20px;\n\tborder:2px solid #EEEEEE;\n\tborder-radius: 5px;\n\ttransition: 0.3s;\n\t/* box-shadow: 0px 0px 5px #CCCCCC; */\n}\n\n\n\n"
  },
  {
    "path": "vue_acimage_web/src/views/TopicInfo/TopicInfo.vue",
    "content": "<template>\n\t<div>\n\t\t<my-header></my-header>\n\t\t<div class=\"wrapper\">\n\t\t\t<el-skeleton class=\"wrapper-left\" v-if=\"loading\" :rows=\"10\" animated />\n\t\t\t<div v-else class=\"wrapper-left\">\n\t\t\t\t<!-- 当前话题的相关信息 -->\n\t\t\t\t<div class=\"title\">\n\t\t\t\t\t{{topic.title}}\n\t\t\t\t</div>\n\t\t\t\t<div class=\"topic-info\">\n\t\t\t\t\t<i class=\"el-icon-user\"></i>\n\t\t\t\t\t{{topic.user.username}}\n\t\t\t\t\t<el-divider direction=\"vertical\"></el-divider> {{topic.createTime}}\n\t\t\t\t\t<el-divider direction=\"vertical\"></el-divider> {{topic.pageView}}浏览\n\t\t\t\t\t<el-divider direction=\"vertical\"></el-divider> {{topic.starCount}}收藏\n\t\t\t\t\t<el-divider direction=\"vertical\"></el-divider> {{topic.commentCount}}评论\n\t\t\t\t\t<!-- 编辑标题按钮 -->\n\t\t\t\t\t<div v-if=\"$store.state.userId == topic.userId\" class=\"title-edit-button\">\n\t\t\t\t\t\t<el-button @click=\"titleModifyVisible=true\" class=\"gray-color\" type=\"text\" size=\"mini\">编辑标题\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div style=\"margin-top: 8px;\">\n\t\t\t\t\t\t<el-tag :type=\"$global.buttonType(topic.categoryId)\" effect=\"plain\" size=\"small\" :key=\"-1\">\n\t\t\t\t\t\t\t{{$store.getters.categoryLabel(topic.categoryId)}}\n\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t\t<span class=\"tags-container\">\n\t\t\t\t\t\t\t<el-tag v-for=\"tagId in topic.tagIds\" :type=\"$global.buttonType(tagId)\" effect=\"plain\"\n\t\t\t\t\t\t\t\tsize=\"mini\" :key=\"tagId\">\n\t\t\t\t\t\t\t\t{{$store.getters.tagLabel(tagId)}}\n\t\t\t\t\t\t\t</el-tag>\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!--End 当前话题的相关信息 -->\n\t\t\t\t<div style=\"margin-top:-20px;width:90%;margin-left:5%\">\n\t\t\t\t\t<el-divider direction=\"horizontal\"></el-divider>\n\t\t\t\t</div>\n\t\t\t\t<!--话题内容 -->\n\t\t\t\t<div class=\"topic-html-container\">\n\t\t\t\t\t<div v-dompurify-html=\"topic.html\"></div>\n\t\t\t\t</div>\n\n\t\t\t\t<div style=\"text-align: center;margin-top:20px;\">\n\t\t\t\t\t<el-button @click=\"onClickStar\" :type=\" isStar?'info':'danger' \" :disabled=\"isLoadingStar\"\n\t\t\t\t\t\tstyle=\"border:none\" :icon=\"isStar?'':'el-icon-star-off'\">\n\t\t\t\t\t\t{{isStar?'取消星星':'给TA星星'}}\n\t\t\t\t\t</el-button>\n\t\t\t\t\t<!-- \t\t\t\t\t<el-button type=\"danger\" style=\"border:none\" icon=\"el-icon-download\" @click=\"onClickDownloadImages\">\n\t\t\t\t\t\t下载\n\t\t\t\t\t</el-button> -->\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"time-hint\">\n\t\t\t\t\t编辑于：{{topic.updateTime}}\n\t\t\t\t\t<template v-if=\"$store.state.userId==topic.userId\">\n\t\t\t\t\t\t<div class=\"content-edit-button\">\n\t\t\t\t\t\t\t<el-button @click=\"contentModifyVisible=true\" type=\"text\" class=\"gray-color\" size=\"mini\">\n\t\t\t\t\t\t\t\t编辑内容\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t\t<el-button @click=\"onClickDeleteTopic\" type=\"text\" class=\"gray-color\" size=\"mini\">删除话题\n\t\t\t\t\t\t\t</el-button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- 分割线 -->\n\t\t\t\t<div style=\"width:90%;margin-left:5%\">\n\t\t\t\t\t<el-divider direction=\"horizontal\"></el-divider>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- 当前用户评论框 -->\n\t\t\t\t<publish-comment :topic-id=\"topic.id\"></publish-comment>\n\n\t\t\t\t<!-- 当前话题的评论 -->\n\t\t\t\t<div class=\"comments-container\">\n\t\t\t\t\t<div class=\"comments-count\">\n\t\t\t\t\t\t共 {{topic.commentCount}} 条评论\n\t\t\t\t\t</div>\n\t\t\t\t\t<user-comment v-for=\"comment in topic.comments\" :key=\"comment.id\"\n\t\t\t\t\t\t:photo-src=\"$global.getPhotoUrl(comment.user.photoUrl)\" :username=\"comment.user.username\"\n\t\t\t\t\t\t:content=\"comment.content\" :createTime=\"comment.createTime\" :comment-id=\"comment.id\"\n\t\t\t\t\t\t:editable=\"$store.state.userId==comment.user.id\">\n\t\t\t\t\t</user-comment>\n\t\t\t\t\t<!-- 分页 -->\n\t\t\t\t\t<div v-if=\"topic.comments.length!=0\" style=\"text-align: center;\">\n\t\t\t\t\t\t<el-pagination background layout=\"prev, pager, next\" :total=\"topic.commentCount\"\n\t\t\t\t\t\t\t:current-page.sync=\"pageNo\" @current-change=\"onPageNoChange\" :page-size=\"10\">\n\t\t\t\t\t\t</el-pagination>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- End 当前话题的评论 -->\n\t\t\t</div>\n\n\t\t\t<div class=\"wrapper-right\">\n\t\t\t\t<!-- 右侧话题主人信息 -->\n\t\t\t\t<div class=\"user-container hover-shadow\">\n\t\t\t\t\t<el-avatar :src=\"$global.getPhotoUrl(topic.user.photoUrl)\" :size=\"90\" style=\"margin-top:20px;\">\n\t\t\t\t\t</el-avatar>\n\t\t\t\t\t<br />\n\t\t\t\t\t<span style=\"color:#F67B7B;\">{{topic.user.username}}</span>\n\t\t\t\t\t<div style=\"font-size:14px;margin-top:5px;color:#999999;\">\n\t\t\t\t\t\t<i class=\"el-icon-star-on\" style=\"color:orange;\"></i>{{topic.user.starCount}}\n\t\t\t\t\t\t<i class=\"el-icon-edit\" style=\"color:skyblue;\"></i>{{topic.user.topicCount}}\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\t这是我的个性签名\n\t\t\t\t\t</div>\n\t\t\t\t\t<div style=\"margin-top:10px;\">\n\t\t\t\t\t\t<el-button type=\"primary\" icon=\"el-icon-plus\" size=\"mini\"\n\t\t\t\t\t\t\tstyle=\"background-color:#EC5555;border:none;\">\n\t\t\t\t\t\t\t关注\n\t\t\t\t\t\t</el-button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!--End 右侧话题主人信息-->\n\n\t\t\t\t<div v-if=\"!$global.isEmpty(topic.similarTopics)\" style=\"margin-top: 10px;\">\n\t\t\t\t\t<topic-rank :labels=\"['相关推荐']\" :topics=\"topic.similarTopics\" headerSrc=\"static/image/similar-topics-header.webp\"></topic-rank>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- 修改标题对话框 -->\n\t\t<el-dialog title=\"修改标题\" :visible.sync=\"titleModifyVisible\">\n\t\t\t<el-form>\n\t\t\t\t<el-form-item label=\"新的标题(2-30字)\">\n\t\t\t\t\t<el-input v-model=\"titleModify\" maxlength=\"30\" clearable=\"\"></el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<div slot=\"footer\" class=\"dialog-footer\">\n\t\t\t\t<el-button @click=\"titleModifyVisible = false\">取 消</el-button>\n\t\t\t\t<el-button type=\"primary\" @click=\"onConfirmModifyTitle\">确 定</el-button>\n\t\t\t</div>\n\t\t</el-dialog>\n\n\t\t<!-- 修改内容对话框 -->\n\t\t<el-dialog title=\"修改内容\" :visible.sync=\"contentModifyVisible\" width=\"900px\">\n\t\t\t<edit-board ref=\"editBoard\" width=\"820px\" :html=\"topic.html\"></edit-board>\n\t\t\t<div slot=\"footer\" class=\"dialog-footer\">\n\t\t\t\t<el-button @click=\"contentModifyVisible = false\">取 消</el-button>\n\t\t\t\t<el-button type=\"primary\" @click=\"onConfirmModifyHtml\">确 定</el-button>\n\t\t\t</div>\n\t\t</el-dialog>\n\n\t</div>\n</template>\n\n<script>\n\timport MyHeader from '@/components/MyHeader/MyHeader.vue'\n\timport UserComment from './UserComment/UserComment.vue'\n\timport PublishComment from './PublishComment/PublishComment.vue'\n\timport EditBoard from '@/components/EditBoard/EditBoard.vue'\n\timport TopicRank from '@/components/TopicRank/TopicRank.vue'\n\n\n\timport {\n\t\tdeleteTopic,\n\t\tqueryTopicAndFirstCommentPage,\n\t\tmodifyHtml,\n\t\tmodifyTitle\n\t} from '@/api/topic.js'\n\timport { queryCommentPage } from '@/api/comment.js'\n\timport { deleteStar, addStar, queryIsStar } from '@/api/star.js'\n\timport { downloadImages, modifyDescription } from '@/api/image.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\texport default {\n\t\tname: 'TopicInfo',\n\t\tcomponents: {\n\t\t\tMyHeader,\n\t\t\tUserComment,\n\t\t\tPublishComment,\n\t\t\tEditBoard,\n\t\t\tTopicRank\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tloading: true,\n\t\t\t\tisLoadingStar: false,\n\t\t\t\ttitleModifyVisible: false,\n\t\t\t\ttitleModify: '',\n\t\t\t\tcontentModifyVisible: false,\n\t\t\t\t// 当前用户的评论\n\t\t\t\tcomment: {\n\t\t\t\t\tcontent: ''\n\t\t\t\t},\n\t\t\t\t//记录修改后的数据\n\t\t\t\tisModifyTopic: false,\n\t\t\t\tdescriptionsModify: [],\n\t\t\t\tpageNo: 1,\n\t\t\t\t//展示话题的相关数据\n\t\t\t\tisStar: false,\n\t\t\t\ttopic: {\n\t\t\t\t\tid: 0,\n\t\t\t\t\ttitle: '这是我的title',\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tuserId: 996,\n\t\t\t\t\t\tusername: 'TA996',\n\t\t\t\t\t\tstarCount: 996,\n\t\t\t\t\t\tphotoUrl: '',\n\t\t\t\t\t},\n\t\t\t\t\tcategoryId: 4,\n\t\t\t\t\tcreateTime: '2022-2-22 22:22:22',\n\t\t\t\t\tupdateTime: '2022-2-22 22:22:22',\n\t\t\t\t\tpageView: 996,\n\t\t\t\t\tstarCount: 996,\n\t\t\t\t\tcommentCount: 20,\n\t\t\t\t\thtml: `这些图片辛苦找来的，大家且看且珍惜`,\n\t\t\t\t\tcomments: [],\n\t\t\t\t\tsimilarTopics: []\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\twatch: {\n\t\t\t'$route'(to, from) {\n\t\t\t\tif (to.params.id != from.params.id) {\n\t\t\t\t\tthis.init(to.params.id); //重新加载数据\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tmounted() {\n\t\t\t//获取参数\n\t\t\tlet id = this.$route.params.id;\n\t\t\tthis.init(id);\n\t\t},\n\t\tmethods: {\n\t\t\tinit(id) {\n\t\t\t\tif (CommonUtils.isEmpty(id)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet _this = this;\n\t\t\t\tqueryTopicAndFirstCommentPage(id).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.topic = result.data;\n\t\t\t\t\t\tconsole.log(_this.topic)\n\t\t\t\t\t\t_this.loading = false;\n\t\t\t\t\t\t_this.initDataForModify();\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tif (_this.$store.getters.isLogin) {\n\t\t\t\t\tqueryIsStar(id).then(result => {\n\t\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t\t_this.isStar = result.data;\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t},\n\t\t\tonClickStar() {\n\t\t\t\tlet _this = this;\n\t\t\t\tif (!_this.$store.getters.isLogin) {\n\t\t\t\t\tMessageUtils.notice(\"请先登录\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isStar) {\n\t\t\t\t\tdeleteStar(this.topic.id)\n\t\t\t\t\t\t.then(result => {\n\t\t\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t\t\t_this.isStar = !_this.isStar;\n\t\t\t\t\t\t\t\tMessageUtils.success(\"取消成功\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\taddStar(this.topic.id)\n\t\t\t\t\t\t.then(result => {\n\t\t\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t\t\t_this.isStar = !_this.isStar;\n\t\t\t\t\t\t\t\tMessageUtils.success(\"收藏成功\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\tonConfirmModifyTitle() {\n\t\t\t\tmodifyTitle(this.topic.id, this.titleModify).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"修改标题成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(0.5);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\tonConfirmModifyHtml() {\n\t\t\t\tlet data = { id: this.topic.id, html: this.$refs['editBoard'].Html };\n\t\t\t\tmodifyHtml(data).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\tMessageUtils.success(\"修改内容成功\", 1);\n\t\t\t\t\t\tCommonUtils.delayRefresh(0.5);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\tinitDataForModify() {\n\t\t\t\tthis.isModifyTopic = false;\n\t\t\t\tthis.contentModify = this.topic.content;\n\t\t\t\tthis.titleModify = this.topic.title;\n\t\t\t},\n\t\t\t//删除话题\n\t\t\tonClickDeleteTopic() {\n\t\t\t\tlet _this = this;\n\t\t\t\tMessageUtils.confirm('确认删除？操作不可逆').then(() => {\n\t\t\t\t\tdeleteTopic(_this.topic.id).then(res => {\n\t\t\t\t\t\tif (res.code == Code.OK) {\n\t\t\t\t\t\t\tMessageUtils.success('删除成功')\n\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\t_this.$router.push({ path: '/' })\n\t\t\t\t\t\t\t}, 1000);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t},\n\t\t\t//下载话题所有图片\n\t\t\tonClickDownloadImages() {\n\t\t\t\tlet _this = this;\n\t\t\t\tlet imageIds = [];\n\t\t\t\tfor (let image of this.topic.images) {\n\t\t\t\t\timageIds.push(image.id);\n\t\t\t\t}\n\t\t\t\tdownloadImages(imageIds, this.topic.id);\n\t\t\t},\n\t\t\t//获取对应页评论\n\t\t\tonPageNoChange() {\n\t\t\t\tlet _this = this;\n\t\t\t\tqueryCommentPage(this.topic.id, this.pageNo).then(result => {\n\t\t\t\t\tif (result.code == Code.OK) {\n\t\t\t\t\t\t_this.topic.comments = result.data;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t}\n</script>\n\n<style src=\"./TopicInfo.css\" scoped>\n\t.tox-tinymce-aux {\n\t\tz-index: 9999 !important;\n\t}\n</style>\n"
  },
  {
    "path": "vue_acimage_web/src/views/TopicInfo/UserComment/UserComment.css",
    "content": "/*comment组件*/\n.comment-container>div {\n\tdisplay: inline-block;\n\tvertical-align: top;\n\n}\n\n.comment-right {\n\tborder-bottom: 1.5px solid #DDDDDD;\n\tmargin-left: 10px;\n\twidth: 500px;\n\t/* padding-top: 5px; */\n\t/* padding-bottom: 5px; */\n}\n\n.comment-username {\n\tdisplay: block;\n\tmargin-top: 10px;\n\tcolor: #666666;\n\tfont-family: 'Times New Roman', Times, serif;\n\tfont-size: 14px;\n\tfont-weight: bold;\n}\n\n.comment-content {\n\tdisplay: block;\n\tpadding-top: 10px;\n\t/* margin-top:5px; */\n\tfont-family: FangSong_GB2312;\n\t/* font-weight: bold; */\n\t/* font-family: Tahoma,Helvetica,Arial,'宋体',sans-serif; */\n\tcolor: #555555;\n\tfont-size: 15px !important;\n}\n\n.comment-bottom {\n\tdisplay: block;\n\tposition: relative;\n\tmargin-top: 10px;\n\tcolor: #999999;\n\tfont-family: 'Times New Roman', Times, serif;\n}\n\n.edit-button {\n\tmargin-top: -5px;\n\tdisplay: inline-block;\n\tfloat: right;\n}\n\n/*End comment组件*/\n\n.gray-color {\n\tcolor: #AAAAAA;\n}\n"
  },
  {
    "path": "vue_acimage_web/src/views/TopicInfo/UserComment/UserComment.vue",
    "content": "<template>\n\t<div class=\"comment-container\">\n\t\t<div style=\"width:70px;\">\n\t\t\t<el-avatar :src=\"photoSrc\" :size=\"60\"></el-avatar>\n\t\t</div>\n\t\t<div class=\"comment-right\">\n\t\t\t<div class=\"comment-username\">\n\t\t\t\t{{username}}\n\t\t\t</div>\n\t\t\t<div class=\"comment-content\">\n\t\t\t\t{{content}}\n\t\t\t</div>\n\t\t\t<div class=\"comment-bottom\">\n\t\t\t\t{{createTime}}\n\t\t\t\t<div class=\"edit-button\" v-if=\"editable\">\n\t\t\t\t\t<el-button type=\"text\" class=\"gray-color\" size=\"mini\" @click=\"onClickEdit\">编辑</el-button>\n\t\t\t\t\t<el-button type=\"text\" class=\"gray-color\" size=\"mini\" @click=\"onClickDelete\">删除</el-button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- 修改评论对话框 -->\n\t\t<el-dialog title=\"修改评论\" :visible.sync=\"commentModifyVisible\">\n\t\t\t<el-form>\n\t\t\t\t<el-form-item label=\"修改评论(2-150字)\">\n\t\t\t\t\t<el-input v-model=\"contentModify\" maxlength=\"150\" type=\"textarea\" :rows=\"5\" autocomplete=\"off\">\n\t\t\t\t\t</el-input>\n\t\t\t\t</el-form-item>\n\t\t\t</el-form>\n\t\t\t<div slot=\"footer\" class=\"dialog-footer\">\n\t\t\t\t<el-button @click=\"commentModifyVisible = false\">取 消</el-button>\n\t\t\t\t<el-button type=\"primary\" @click=\"onConfirmModify\">确 定</el-button>\n\t\t\t</div>\n\t\t</el-dialog>\n\t</div>\n</template>\n\n<script>\n\timport { deleteComment, modifyComment } from '@/api/comment.js'\n\n\timport { Code } from '@/utils/result.js'\n\timport CommonUtils from '@/utils/CommonUtils'\n\timport MessageUtils from '@/utils/MessageUtils'\n\n\texport default {\n\t\tname: 'UserComment',\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\t// isModifyMode: false,\n\t\t\t\tcommentModifyVisible: false,\n\t\t\t\tcontentModify: ''\n\t\t\t};\n\t\t},\n\t\tprops: {\n\t\t\tphotoSrc: {},\n\t\t\tcommentId: {},\n\t\t\tusername: { default: '名字加载中' },\n\t\t\tcontent: { default: '内容加载中' },\n\t\t\tcreateTime: { default: '2022-2-22 22:22:22' },\n\t\t\teditable: { default: false, type: Boolean }\n\t\t},\n\t\twatch: {\n\t\t\tcontent: {\n\t\t\t\thandler(newVal, oldVal) {\n\t\t\t\t\tthis.contentModify = this.content;\n\t\t\t\t},\n\t\t\t\timmediate: true\n\t\t\t}\n\t\t},\n\t\tmethods: {\n\t\t\tonClickEdit() {\n\t\t\t\tthis.commentModifyVisible = true;\n\t\t\t},\n\t\t\t//修改评论\n\t\t\tonConfirmModify() {\n\t\t\t\tlet reqData = { 'id': this.commentId, 'content': this.contentModify };\n\t\t\t\tlet _this = this;\n\t\t\t\tCommonUtils.popMsgAndRefreshIfOk(modifyComment(reqData), '修改成功', 1);\n\t\t\t},\n\t\t\t//删除评论\n\t\t\tonClickDelete() {\n\t\t\t\tlet _this = this;\n\t\t\t\tMessageUtils.confirm('确认删除？操作不可逆').then(() => {\n\t\t\t\t\tCommonUtils.popMsgAndRefreshIfOk(deleteComment(_this.commentId), '删除成功', 1);\n\t\t\t\t})\n\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style src=\"./UserComment.css\" scoped>\n</style>\n"
  },
  {
    "path": "vue_acimage_web/vue.config.js",
    "content": "const { defineConfig } = require('@vue/cli-service')\nmodule.exports = defineConfig({ transpileDependencies: true })\n\nmodule.exports = {\n\tdevServer: {\n\t\tport: 8010,\n\t\tproxy: {\n\t\t\t\"/websocket\": {\n\t\t\t\ttarget: \"ws://127.0.0.1:81/\", // 需要代理访问的api地址\n\t\t\t\tchangeOrigin: true, // 允许跨域请求\n\t\t\t\tws: true\n\t\t\t},\n\t\t\t\"/api\": {\n\t\t\t\t// 代理名称   凡是使用/api开头的地址都是用此代理\n\t\t\t\ttarget: \"http://127.0.0.1:81/\", // 需要代理访问的api地址\n\t\t\t\tchangeOrigin: true, // 允许跨域请求\n\t\t\t\t// pathRewrite: {\n\t\t\t\t//     // 重写路径，替换请求地址中的指定路径\n\t\t\t\t//     \"^/api\": \"/\", // 将请求地址中的/api替换为空，也就是请求地址中不会包含/api/\n\t\t\t\t// },\n\t\t\t},\n\t\t\t\"/storage\": {\n\t\t\t\ttarget: \"http://127.0.0.1:81/\", // 需要代理访问的api地址\n\t\t\t\tchangeOrigin: true, // 允许跨域请求\n\t\t\t},\n\t\t\t\"/acfile\": {\n\t\t\t\ttarget: \"http://127.0.0.1:81/\", // 需要代理访问的api地址\n\t\t\t\tchangeOrigin: true, // 允许跨域请求\n\t\t\t},\n\t\t},\n\t}\n}\n"
  }
]