@ -0,0 +1,35 @@ |
|||||
|
HELP.md |
||||
|
target/ |
||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||
|
!**/src/main/**/target/ |
||||
|
!**/src/test/**/target/ |
||||
|
|
||||
|
### STS ### |
||||
|
.apt_generated |
||||
|
.classpath |
||||
|
.factorypath |
||||
|
.project |
||||
|
.settings |
||||
|
.springBeans |
||||
|
.sts4-cache |
||||
|
|
||||
|
### IntelliJ IDEA ### |
||||
|
.idea |
||||
|
*.iws |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
|
||||
|
### NetBeans ### |
||||
|
/nbproject/private/ |
||||
|
/nbbuild/ |
||||
|
/dist/ |
||||
|
/nbdist/ |
||||
|
/.nb-gradle/ |
||||
|
build/ |
||||
|
!**/src/main/**/build/ |
||||
|
!**/src/test/**/build/ |
||||
|
|
||||
|
### VS Code ### |
||||
|
.vscode/ |
||||
|
/output/ |
||||
|
/file-online-preview/server/src/main/file |
@ -0,0 +1,21 @@ |
|||||
|
MIT License |
||||
|
|
||||
|
Copyright (c) 2023 风行 |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1,144 @@ |
|||||
|
### 系统简介 |
||||
|
|
||||
|
企业级通用开发平台,前后端分离架构,单工程,多模块,部署形态为单体应用。 |
||||
|
前端基于vue3.2.47,element-plus 2.1.0,前端框架vue-element-plus-admin1.9.4深度整合改造。 |
||||
|
后端SSM+MybatisPlus,使用SpringBoot 2.3.0。 |
||||
|
数据库使用MySql 8.0. |
||||
|
|
||||
|
重度使用MybatisPlus,包括主键策略、逻辑删除、乐观锁、自动填充、数据分页、CURD接口、条件构造器等, |
||||
|
二次封装和扩展代码生成器,实现entity、dao、service、controller、vo及前端vue页面生成。 |
||||
|
|
||||
|
整体架构图如下: |
||||
|
 |
||||
|
|
||||
|
技术选型,详见专栏博客:https://blog.csdn.net/seawaving/article/details/130015830 |
||||
|
|
||||
|
### 后端架构 |
||||
|
|
||||
|
到目前为止,整个工程项目,后端共计19个模块,架构图和依赖关系如下图所示: |
||||
|
 |
||||
|
|
||||
|
模块分成三类,一类是平台内核模块,命名规则是platform+模块功能名称,图中用蓝色标示;一类是能力扩展模块,命名规则是platform-boot-starter+模块功能名称,图中用绿色标示;剩下的一类是接口平台,命名规则是cip |
||||
|
+模块功能名称,图中用紫色标示,相对平台独立,但又作为平台的重要组成部分。 |
||||
|
|
||||
|
#### 平台内核模块 |
||||
|
|
||||
|
platform-common作为公用基础模块,主要包括工具类、公用注解、公共父类、公共常量、公共枚举值,与前端UI交互定义的vo类,该模块为最基础的模块,无前置依赖。 |
||||
|
|
||||
|
platform-system是平台最核心的模块,主要包括组织机构、人员、角色(用户组)、权限、日志、系统参数、模块这些实体和服务的实现,需要注意的是,权限控制、日志记录,并不是在该模块实现,而是在platform-framework平台框架中实现,该模块依赖于platform-common。 |
||||
|
|
||||
|
platform-framework是平台框架,负责身份认证、权限控制、全局配置、数据分页、日志处理、自动填充(创建人、创建时间、修改人、修改时间),因为身份认证、权限控制等功能,不可避免需要使用处于platform-system模块中的人员、用户组等实体和服务,因此依赖于platform-system。 |
||||
|
|
||||
|
platform-support是一个业务支撑模块,基于技术组件进行功能设计与封装,实现一些通用的功能设计,更方便业务逻辑的实现,提供附件管理、通知公告、内容模板(用于短信、邮件、消息)、单据流水号、门户等功能。这些支撑模块同样需要位于platform-system模块中的人员、组织机构等实体和服务,因此依赖于platform-system。 |
||||
|
|
||||
|
platform-entityconfig属于低代码配置范畴,定义了业务实体的元数据,通过模块、实体、模型、视图多级配置,结合模板技术,实现细粒度的代码生成控制。 |
||||
|
|
||||
|
platform-workflow集成了工作流组件activiti的分支Camunda,实现了流程建模、流程模板,以及我的待办、我的已办和我的申请等功能。 |
||||
|
|
||||
|
platform-businessflow是业务流程的集中存储模块,实现了流程导航功能和请求申请示例流程。 |
||||
|
|
||||
|
platform-boot-starter是平台启动项目,整合平台基础功能,类似于spring-boot-starter,业务系统引入该包进行依赖。该模块自身没有实体与服务,而是汇总整合,把platform-framework |
||||
|
引用进来,同时进行配置。配置分两方面,一方面是做一个配置类,加一些注解(如:@EnableRetry、@ServletComponentScan、@EnableTransactionManagement),使用开发平台实现的业务系统,就不需要在启动类上重复添加这些注解;另一方面,是位于yml配置文件中的配置信息,也分为两部分,一部分是三方组件自身的,如数据源、连接池、redis、quartz、logback,另一方面是自定义的系统参数,如用户默认密码、导出excel数据的批次最大行数量。 |
||||
|
|
||||
|
platform-boot-starter-demo是示例项目,实际是模拟业务系统如何使用开发平台,用于平台自身功能开发与调试。 |
||||
|
|
||||
|
#### 能力扩展模块 |
||||
|
|
||||
|
绿色标示的五个模块,比较好理解,通常是对第三方组件的封装与整合,依赖于公共基础模块platform-common,这些模块可以不断扩展的,业务系统按需引入即可,这样就实现了核心模块必选、扩展模块可选的目的。 |
||||
|
|
||||
|
* platform-boot-starter-mail:邮件,集成springmail组件,实现邮件的发送功能封装 |
||||
|
* platform-boot-starter-oss: 对象存储,用于文件存储封装,底层可基于多种模式,如本地磁盘、对象存储系统等 |
||||
|
* platform-boot-starter-scheduler:任务调度,集成quartz组件,实现任务调度可视化配置 |
||||
|
* platform-boot-starter-notification:消息通知,基于netty实现的websocket,用于系统内置消息 |
||||
|
* platform-boot-starter-elasticsearch:全文搜索,集成elasticsearch组件 |
||||
|
对于扩展模块,平台的核心模块实际也可能会用到,例如platform-support中的附件功能,就会用到platform-boot-starter-oss;platform-system中的自动解锁用户功能,就会用到platform-boot-starter-scheduler。 |
||||
|
|
||||
|
#### 接口平台 |
||||
|
|
||||
|
将自己之前开源的通用接口平台进行了改造,将其作为一个模块,整合到应用开发平台当中来,由接口平台统一对外暴露应用系统的API数据接口以及推送事件消息。 |
||||
|
platform-cip-common:公共基础 |
||||
|
platform-cip-api:对外提供API数据接口,提供API服务 |
||||
|
platform-cip-message:基于netty的web socket服务端提供消息服务 |
||||
|
platform-cip-manage:平台自身基础数据的维护,如应用、API服务、消息服务、数据权限管理等。 |
||||
|
4个模块内关系为manage依赖common,api和message相互独立,但都依赖于manage。 |
||||
|
|
||||
|
### 如何运行 |
||||
|
|
||||
|
以下为简要说明,详细的开发环境搭建手册参见https://blog.csdn.net/seawaving/article/details/134895546 |
||||
|
|
||||
|
#### 1. 准备工作 |
||||
|
|
||||
|
预装redis、nodejs、mysql、ide |
||||
|
|
||||
|
#### 2. 初始化数据库 |
||||
|
|
||||
|
执行/resource目录下的init.sql,创建名字为abc的数据库。 |
||||
|
|
||||
|
#### 3 .前端 |
||||
|
|
||||
|
nodejs >=14.6 |
||||
|
|
||||
|
执行npm install pnpm -g,安装pnpm包 |
||||
|
|
||||
|
执行pnpm install命令,若nodejs版本过低会提示 |
||||
|
|
||||
|
使用vscode打开platform-web目录,执行pnpm install安装npm module |
||||
|
|
||||
|
执行结束会提示如下错误,不用理会,因为把husky移除导致的,不影响系统正常运行,进行下步dev脚本即可 |
||||
|
husky install |
||||
|
'husky' 不是内部或外部命令,也不是可运行的程序 |
||||
|
或批处理文件。 |
||||
|
|
||||
|
执行dev脚本,默认打开localhost:4000 |
||||
|
|
||||
|
默认管理员账号密码:admin/12345678 |
||||
|
|
||||
|
#### 4 .后端 |
||||
|
|
||||
|
标准SpringBoot项目,多模块,启动类位于platform-boot-starter-demo下,默认端口8080。 |
||||
|
|
||||
|
注:系统的下拉数据源,也即数据字典使用redis缓存,按上述步骤构建后,部分查询界面不显示中文名称,可在系统登录后,访问系统管理-》系统维护菜单下的“重建缓存”按钮,系统会自动将数据库的字典数据写入到redis中。 |
||||
|
|
||||
|
#### 5 .接口平台对接客户端 |
||||
|
|
||||
|
cip-client是一个模拟的接口平台客户端,是一个独立的springboot,相当于第三方系统,有自己独立的数据库,数据库脚本参见\cip-client\src\main\resources\init.sql |
||||
|
|
||||
|
#### 6 .minio启用说明 |
||||
|
|
||||
|
平台对于文件存储除了支持本地磁盘模式外,还实现了minio对象存储组件的集成。如需启用,需安装minio服务端,版本2021-04-22T15-44-28Z(最后一个基于apache |
||||
|
2.0开源协议的版本),并修改平台配置文件。 |
||||
|
|
||||
|
### 未来规划 |
||||
|
|
||||
|
客观地说,目前开发平台已经实现了大部分常用常见功能,可以投入使用了。 |
||||
|
由于是一路狂奔模式,速度提升,时间缩短,但不可避免一些功能遗留了待办项,以及未充分测试导致存在bug,后面需要再循着功能过一遍,进行重构、测试,完善功能,输出设计。特别是低代码配置部分,需要持续完善与改进,简化配置,进一步提升开发效率。 |
||||
|
|
||||
|
后面几块是平台欠缺的,需要补全和完善,每一块都是硬骨头,难度和工作量都不小,做了一些简单初步的了解,具体如下: |
||||
|
|
||||
|
输出系统使用手册(进行中) |
||||
|
集成图表组件(已完成) |
||||
|
集成工作流(常用功能已完成,进度80%,已可用) |
||||
|
实现可视化表单(常用功能已完成,进度90%,待打磨) |
||||
|
移动端实现(整合了一个移动端框架,打通了认证,尚未有具体的功能,暂挂起) |
||||
|
自定义查询(未开始) |
||||
|
实现数据权限(未开始) |
||||
|
|
||||
|
### 基于平台开发的应用 |
||||
|
|
||||
|
#### 文档管理系统 |
||||
|
|
||||
|
文档管理系统是基于平台实现的文档库管理应用,可作为企业内部文档库、知识库使用,主要包括以下功能: |
||||
|
|
||||
|
* 文件夹管理:创建、更名、删除、复制、移动、授权; |
||||
|
* 文档管理:上传、下载、更名、复制、移动、预览、分享; |
||||
|
* 权限控制:按组织机构和按用户组两种授权模式; |
||||
|
* 在线预览:无需下载,上百种格式文件在线预览; |
||||
|
* 收藏夹:支持将文件夹和文档加入收藏、查看和移除; |
||||
|
* 全文搜索:对文本类、office文档和pdf文档等进行全文搜索; |
||||
|
|
||||
|
### 系统设计资料 |
||||
|
|
||||
|
参见csdn博客专栏 [https://blog.csdn.net/seawaving/category_12230971.html](https://blog.csdn.net/seawaving/category_12230971.html) |
||||
|
平台研发过程中的设计思路、遇到的问题和方案的选择等一并分享出来,欢迎交流与讨论。 |
||||
|
|
||||
|
|
@ -0,0 +1,110 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
|
||||
|
<groupId>tech.popsoft.cip</groupId> |
||||
|
<artifactId>cip-client</artifactId> |
||||
|
<version>5.1.0</version> |
||||
|
|
||||
|
<name>cip-client</name> |
||||
|
|
||||
|
|
||||
|
<properties> |
||||
|
<!-- 文件拷贝时的编码 --> |
||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
||||
|
<!-- 编译时的编码 --> |
||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> |
||||
|
<maven.compiler.source>1.8</maven.compiler.source> |
||||
|
<maven.compiler.target>1.8</maven.compiler.target> |
||||
|
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> |
||||
|
<java.version>1.8</java.version> |
||||
|
</properties> |
||||
|
|
||||
|
|
||||
|
<!--版本依赖管理--> |
||||
|
<dependencyManagement> |
||||
|
<dependencies> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-dependencies</artifactId> |
||||
|
<version>2.3.0.RELEASE</version> |
||||
|
<type>pom</type> |
||||
|
<scope>import</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
</dependencies> |
||||
|
</dependencyManagement> |
||||
|
|
||||
|
<dependencies> |
||||
|
|
||||
|
<!--######开发平台核心模块依赖 区域开始#####--> |
||||
|
<dependency> |
||||
|
<groupId>tech.abc</groupId> |
||||
|
<artifactId>platform-framework</artifactId> |
||||
|
<version>5.1.0</version> |
||||
|
</dependency> |
||||
|
<!--######开发平台核心模块依赖 区域开始#####--> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||
|
|
||||
|
</dependency> |
||||
|
|
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||
|
<scope>test</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>tech.abc</groupId> |
||||
|
<artifactId>platform-cip-common</artifactId> |
||||
|
<version>5.1.0</version> |
||||
|
|
||||
|
</dependency> |
||||
|
|
||||
|
<!--mysql 驱动--> |
||||
|
<dependency> |
||||
|
<groupId>mysql</groupId> |
||||
|
<artifactId>mysql-connector-java</artifactId> |
||||
|
<version>8.0.20</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!--数据库连接池--> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>druid-spring-boot-starter</artifactId> |
||||
|
<version>1.2.5</version> |
||||
|
</dependency> |
||||
|
|
||||
|
|
||||
|
</dependencies> |
||||
|
|
||||
|
|
||||
|
<build> |
||||
|
<plugins> |
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<version>3.8.1</version> |
||||
|
<configuration> |
||||
|
<source>1.8</source> |
||||
|
<target>1.8</target> |
||||
|
<encoding>UTF-8</encoding> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
</plugins> |
||||
|
</build> |
||||
|
</project> |
@ -0,0 +1,73 @@ |
|||||
|
package tech.popsoft.cip.client; |
||||
|
|
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.mybatis.spring.annotation.MapperScan; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.boot.CommandLineRunner; |
||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
import org.springframework.boot.autoconfigure.web.ServerProperties; |
||||
|
import org.springframework.boot.builder.SpringApplicationBuilder; |
||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
||||
|
import org.springframework.boot.web.servlet.ServletComponentScan; |
||||
|
import org.springframework.context.ConfigurableApplicationContext; |
||||
|
import org.springframework.context.annotation.Import; |
||||
|
import org.springframework.retry.annotation.EnableRetry; |
||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement; |
||||
|
import org.springframework.util.StopWatch; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.abc.platform.framework.config.PlatformConfig; |
||||
|
import tech.popsoft.cip.client.framework.MessageClient; |
||||
|
import tech.popsoft.cip.client.framework.MessageClientConfig; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @author wqliu |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Import(SpringUtil.class) |
||||
|
@EnableRetry |
||||
|
@ServletComponentScan |
||||
|
@EnableTransactionManagement |
||||
|
@EnableConfigurationProperties({MessageClientConfig.class, PlatformConfig.class}) |
||||
|
@SpringBootApplication(scanBasePackages = {"tech.abc.platform.**", "tech.popsoft.cip.**"}) |
||||
|
@MapperScan({"tech.abc.platform.**.mapper", "tech.popsoft.cip.**.mapper"}) |
||||
|
public class CipClientApplication implements CommandLineRunner { |
||||
|
|
||||
|
@Autowired |
||||
|
private MessageClient messageClient; |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
|
||||
|
|
||||
|
StopWatch stopWatch = new StopWatch(); |
||||
|
stopWatch.start(); |
||||
|
ConfigurableApplicationContext context = new SpringApplicationBuilder(CipClientApplication.class) |
||||
|
.logStartupInfo(false) |
||||
|
.run(args); |
||||
|
stopWatch.stop(); |
||||
|
Integer port = context.getBean(ServerProperties.class).getPort(); |
||||
|
log.info("服务启动完成,耗时:{}s,端口号:{}", stopWatch.getTotalTimeSeconds(), port); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void run(String... args) throws Exception { |
||||
|
|
||||
|
// 此处通过单线程启动netty,是为了不堵塞主应用,不需要线程池
|
||||
|
// noinspection AlibabaAvoidManuallyCreateThread
|
||||
|
Thread thread = new Thread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
messageClient.start(); |
||||
|
} |
||||
|
}); |
||||
|
// 启动netty服务
|
||||
|
thread.start(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,112 @@ |
|||||
|
package tech.popsoft.cip.client.framework; |
||||
|
|
||||
|
import io.netty.bootstrap.Bootstrap; |
||||
|
import io.netty.channel.Channel; |
||||
|
import io.netty.channel.ChannelFuture; |
||||
|
import io.netty.channel.ChannelFutureListener; |
||||
|
import io.netty.channel.EventLoopGroup; |
||||
|
import io.netty.channel.nio.NioEventLoopGroup; |
||||
|
import io.netty.channel.socket.nio.NioSocketChannel; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import tech.popsoft.cip.client.framework.customhandler.WebSocketClientHandshakerHandler; |
||||
|
|
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
/** |
||||
|
* @author wqliu |
||||
|
* @date 2021-9-28 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class MessageClient { |
||||
|
|
||||
|
|
||||
|
@Autowired |
||||
|
private MessageClientConfig config; |
||||
|
|
||||
|
@Autowired |
||||
|
private MessageClientChannelInitializer messageClientChannelInitializer; |
||||
|
|
||||
|
@Autowired |
||||
|
private ResendMessage resendMessage; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 启动客户端方法 |
||||
|
*/ |
||||
|
public void start() { |
||||
|
|
||||
|
EventLoopGroup workerGroup = new NioEventLoopGroup(1); |
||||
|
try { |
||||
|
Bootstrap bootstrap = new Bootstrap(); |
||||
|
bootstrap.group(workerGroup); |
||||
|
bootstrap.channel(NioSocketChannel.class); |
||||
|
bootstrap.handler(messageClientChannelInitializer); |
||||
|
|
||||
|
// 客户端与服务端连接的通道,final修饰表示只会有一个
|
||||
|
ChannelFuture channelFuture = bootstrap.connect(config.getHost(), config.getPort()); |
||||
|
|
||||
|
channelFuture.addListener(new ChannelFutureListener() { |
||||
|
@Override |
||||
|
public void operationComplete(ChannelFuture future) throws Exception { |
||||
|
if (!future.isSuccess()) { |
||||
|
// 未成功
|
||||
|
log.error("连接失败", future.cause()); |
||||
|
// 执行重连
|
||||
|
reconnect(); |
||||
|
} else { |
||||
|
log.info("连接成功"); |
||||
|
Channel channel = future.channel(); |
||||
|
// 将channel保存到全局变量
|
||||
|
MessageClientGlobalHolder.channel = channel; |
||||
|
// 发起握手
|
||||
|
WebSocketClientHandshakerHandler handler = (WebSocketClientHandshakerHandler) channel.pipeline().get("hookedHandler"); |
||||
|
handler.handshake(MessageClientGlobalHolder.channel); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 消息重发
|
||||
|
if (config.isEnableResend()) { |
||||
|
log.info("启动消息重发机制"); |
||||
|
// 延迟30秒启动,给建立连接预留充足的时间
|
||||
|
workerGroup.scheduleAtFixedRate(() -> { |
||||
|
resendMessage.resend(); |
||||
|
}, 30, config.getSendMessageSpan(), TimeUnit.SECONDS); |
||||
|
} |
||||
|
// 等待服务器端关闭
|
||||
|
channelFuture.channel().closeFuture().sync(); |
||||
|
|
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
log.error("消息客户端启动失败:{}" + e.getMessage(), e); |
||||
|
// 执行重连
|
||||
|
reconnect(); |
||||
|
} finally { |
||||
|
workerGroup.shutdownGracefully(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重连 |
||||
|
*/ |
||||
|
public void reconnect() { |
||||
|
try { |
||||
|
// 重连期间将连接置空
|
||||
|
MessageClientGlobalHolder.channel = null; |
||||
|
// 休眠5秒
|
||||
|
Thread.sleep(5000); |
||||
|
// 执行重连
|
||||
|
log.info("消息客户端进行重连"); |
||||
|
start(); |
||||
|
|
||||
|
} catch (InterruptedException e) { |
||||
|
log.error("消息客户端重连过程中线程休眠失败", e); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,142 @@ |
|||||
|
package tech.popsoft.cip.client.framework; |
||||
|
|
||||
|
|
||||
|
import io.netty.channel.ChannelInitializer; |
||||
|
import io.netty.channel.ChannelPipeline; |
||||
|
import io.netty.channel.socket.SocketChannel; |
||||
|
import io.netty.handler.codec.http.HttpClientCodec; |
||||
|
import io.netty.handler.codec.http.HttpObjectAggregator; |
||||
|
import io.netty.handler.ssl.SslHandler; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.core.env.Environment; |
||||
|
import org.springframework.core.io.ClassPathResource; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import tech.abc.platform.cip.common.handler.JsonEncodeHandler; |
||||
|
import tech.abc.platform.cip.common.handler.MessageTypeDecodeHandler; |
||||
|
import tech.abc.platform.cip.common.handler.TextWebSocketFrameEncodeHandler; |
||||
|
import tech.abc.platform.cip.common.handler.ValidateMessageHandler; |
||||
|
import tech.popsoft.cip.client.framework.customhandler.*; |
||||
|
|
||||
|
import javax.net.ssl.KeyManagerFactory; |
||||
|
import javax.net.ssl.SSLContext; |
||||
|
import javax.net.ssl.SSLEngine; |
||||
|
import java.io.InputStream; |
||||
|
import java.security.KeyStore; |
||||
|
|
||||
|
/** |
||||
|
* 初始化通道 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-2-5 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class MessageClientChannelInitializer extends ChannelInitializer<SocketChannel> { |
||||
|
|
||||
|
|
||||
|
@Autowired |
||||
|
private MessageClientConfig config; |
||||
|
|
||||
|
@Autowired |
||||
|
private Environment environment; |
||||
|
|
||||
|
/** |
||||
|
* 生产运行模式 |
||||
|
*/ |
||||
|
private final String PRD_MODE = "prd"; |
||||
|
|
||||
|
/** |
||||
|
* 初始化channel |
||||
|
*/ |
||||
|
@Override |
||||
|
public void initChannel(SocketChannel socketChannel) throws Exception { |
||||
|
|
||||
|
|
||||
|
// 获取通道链路
|
||||
|
ChannelPipeline pipeline = socketChannel.pipeline(); |
||||
|
|
||||
|
// 仅在生产模式下加载ssl过滤器
|
||||
|
String mode = environment.getProperty("spring.profiles.active"); |
||||
|
if (PRD_MODE.equals(mode)) { |
||||
|
// ssl
|
||||
|
SSLContext sslContext = createSslContext(); |
||||
|
SSLEngine engine = sslContext.createSSLEngine(); |
||||
|
engine.setNeedClientAuth(false); |
||||
|
engine.setUseClientMode(false); |
||||
|
// 客户端第一个消息,即发起握手的消息不要加密
|
||||
|
pipeline.addLast(new SslHandler(engine, true)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// HTTP 编解码
|
||||
|
pipeline.addLast(new HttpClientCodec()); |
||||
|
|
||||
|
// 聚合为单个 FullHttpRequest 或者 FullHttpResponse
|
||||
|
pipeline.addLast(new HttpObjectAggregator(64 * 1024)); |
||||
|
|
||||
|
|
||||
|
// 读超时处理
|
||||
|
pipeline.addLast(new CustomReadTimeoutHandler(config.getReadIdleTimeOut())); |
||||
|
|
||||
|
|
||||
|
// 处理web socket协议与握手
|
||||
|
pipeline.addLast("hookedHandler", new WebSocketClientHandshakerHandler()); |
||||
|
|
||||
|
|
||||
|
// 数据基本验证
|
||||
|
pipeline.addLast(new ValidateMessageHandler()); |
||||
|
|
||||
|
// 去重
|
||||
|
pipeline.addLast(new DistinctMessageHandler()); |
||||
|
|
||||
|
|
||||
|
// 将文本按消息类型转换为请求消息或响应消息
|
||||
|
pipeline.addLast(new MessageTypeDecodeHandler()); |
||||
|
|
||||
|
// 请求消息业务逻辑处理器
|
||||
|
pipeline.addLast(new RequestMessageBusinessHandler()); |
||||
|
|
||||
|
|
||||
|
// 响应消息业务逻辑处理器
|
||||
|
pipeline.addLast(new ResponseMessageBusinessHandler()); |
||||
|
|
||||
|
// 编码为TextWebSocketFrame
|
||||
|
pipeline.addLast(new TextWebSocketFrameEncodeHandler()); |
||||
|
|
||||
|
// json序列化
|
||||
|
pipeline.addLast(new JsonEncodeHandler()); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建ssl上下文对象 |
||||
|
* |
||||
|
* @return |
||||
|
* @throws Exception |
||||
|
*/ |
||||
|
public SSLContext createSslContext() throws Exception { |
||||
|
|
||||
|
// 读取配置信息
|
||||
|
String path = environment.getProperty("server.ssl.key-store"); |
||||
|
String password = environment.getProperty("server.ssl.key-store-password"); |
||||
|
String type = environment.getProperty("server.ssl.key-store-type"); |
||||
|
|
||||
|
// 构建证书上下文对象
|
||||
|
KeyStore ks = KeyStore.getInstance(type); |
||||
|
path = path.replace("classpath:", ""); |
||||
|
ClassPathResource resource = new ClassPathResource(path); |
||||
|
InputStream ksInputStream = resource.getInputStream(); |
||||
|
ks.load(ksInputStream, password.toCharArray()); |
||||
|
// KeyManagerFactory充当基于密钥内容源的密钥管理器的工厂。
|
||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); |
||||
|
kmf.init(ks, password.toCharArray()); |
||||
|
// SSLContext的实例表示安全套接字协议的实现,它充当用于安全套接字工厂或 SSLEngine 的工厂。
|
||||
|
SSLContext sslContext = SSLContext.getInstance("TLS"); |
||||
|
sslContext.init(kmf.getKeyManagers(), null, null); |
||||
|
return sslContext; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,88 @@ |
|||||
|
package tech.popsoft.cip.client.framework; |
||||
|
|
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
|
||||
|
import java.text.MessageFormat; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 客户端配置参数 |
||||
|
* 使用spring单例模式 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-5 |
||||
|
*/ |
||||
|
@Data |
||||
|
@ConfigurationProperties(prefix = "params") |
||||
|
public class MessageClientConfig { |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 应用编码 |
||||
|
*/ |
||||
|
private String appCode; |
||||
|
/** |
||||
|
* 应用密钥 |
||||
|
*/ |
||||
|
private String appSecret; |
||||
|
/** |
||||
|
* 消息中心主机 |
||||
|
*/ |
||||
|
private String host; |
||||
|
/** |
||||
|
* 消息中心端口 |
||||
|
*/ |
||||
|
private int port = 8997; |
||||
|
|
||||
|
/** |
||||
|
* 心跳频率,单位秒 |
||||
|
*/ |
||||
|
private int heartbeatRate = 5; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 最大发送次数 |
||||
|
*/ |
||||
|
private int maxSendCount = 4; |
||||
|
|
||||
|
/** |
||||
|
* 定时发送消息间隔时间,单位秒 |
||||
|
*/ |
||||
|
private int sendMessageSpan = 30; |
||||
|
|
||||
|
/** |
||||
|
* 每次定时发送消息的数量 |
||||
|
*/ |
||||
|
private int sendMessageCount = 10; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 是否启用消息重发 |
||||
|
*/ |
||||
|
private boolean enableResend; |
||||
|
|
||||
|
/** |
||||
|
* web socket的路径 |
||||
|
*/ |
||||
|
private String webSocketPath; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 触发读空闲事件的时间,单位秒 |
||||
|
*/ |
||||
|
private int readIdleTimeOut; |
||||
|
|
||||
|
/** |
||||
|
* 获取web socket的url地址 |
||||
|
*/ |
||||
|
public String getWebSocketUrl() { |
||||
|
String url = MessageFormat.format("ws://{0}:{1}/{2}", this.getHost(), String.valueOf(this.getPort()), |
||||
|
this.getWebSocketPath()); |
||||
|
return url; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package tech.popsoft.cip.client.framework; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import lombok.experimental.UtilityClass; |
||||
|
|
||||
|
/** |
||||
|
* 客户端全局容器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-2-3 9:28 |
||||
|
**/ |
||||
|
@UtilityClass |
||||
|
public class MessageClientGlobalHolder { |
||||
|
/** |
||||
|
* 通道 |
||||
|
*/ |
||||
|
public static Channel channel = null; |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
package tech.popsoft.cip.client.framework; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import tech.popsoft.cip.client.framework.sender.MessageSenderFactory; |
||||
|
import tech.popsoft.cip.client.framework.sender.RequestMessageSender; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageLog; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 重发消息 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-24 |
||||
|
**/ |
||||
|
@Component |
||||
|
@Slf4j |
||||
|
public class ResendMessage { |
||||
|
@Autowired |
||||
|
private ApiMessageLogService apiMessageLogService; |
||||
|
|
||||
|
@Autowired |
||||
|
private MessageClientConfig config; |
||||
|
|
||||
|
public void resend() { |
||||
|
// 需要进行异常处理,否则某次异常会导致定时器停止运行
|
||||
|
try { |
||||
|
// 判断连接状态
|
||||
|
if (MessageClientGlobalHolder.channel != null) { |
||||
|
// 查找待重发的消息
|
||||
|
List<ApiMessageLog> list = |
||||
|
apiMessageLogService.getResendMessage(config.getSendMessageCount(), config.getMaxSendCount()); |
||||
|
for (int i = 0; i < list.size(); i++) { |
||||
|
ApiMessageLog log = list.get(i); |
||||
|
// 根据消息主题构建发送器
|
||||
|
RequestMessageSender sender = (RequestMessageSender) MessageSenderFactory.createSender(log.getRequestTopicCode()); |
||||
|
// 传入原请求的消息标识和消息内容
|
||||
|
sender.sendMessage(log.getRequestData(), log.getRequestId()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("消息重发处理异常", e); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,48 @@ |
|||||
|
package tech.popsoft.cip.client.framework.customhandler; |
||||
|
|
||||
|
import io.netty.channel.ChannelHandlerContext; |
||||
|
import io.netty.handler.timeout.ReadTimeoutHandler; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.framework.MessageClient; |
||||
|
import tech.popsoft.cip.client.framework.MessageClientGlobalHolder; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 自定义的读超时处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-2-10 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class CustomReadTimeoutHandler extends ReadTimeoutHandler { |
||||
|
public CustomReadTimeoutHandler(int timeoutSeconds) { |
||||
|
super(timeoutSeconds); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void readTimedOut(ChannelHandlerContext ctx) throws Exception { |
||||
|
log.info("读空闲"); |
||||
|
// 关闭连接
|
||||
|
ctx.channel().close(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
||||
|
// 置空连接通道
|
||||
|
MessageClientGlobalHolder.channel = null; |
||||
|
log.info("客户端检测通道失效,启动重连"); |
||||
|
MessageClient messageClient = SpringUtil.getBean(MessageClient.class); |
||||
|
messageClient.reconnect(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
||||
|
log.info("与服务端建立连接,通道开启!"); |
||||
|
// 必须调用父类方法,避免其他处理器的channelActive事件不再触发
|
||||
|
super.channelActive(ctx); |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
package tech.popsoft.cip.client.framework.customhandler; |
||||
|
|
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import io.netty.channel.ChannelHandlerContext; |
||||
|
import io.netty.channel.SimpleChannelInboundHandler; |
||||
|
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; |
||||
|
import io.netty.util.ReferenceCountUtil; |
||||
|
import tech.abc.platform.cip.common.entity.BaseMessage; |
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.abc.platform.cip.common.enums.MessageTypeEnum; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.framework.sender.MessageSenderFactory; |
||||
|
import tech.popsoft.cip.client.framework.sender.ResponseMessageSender; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageLog; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageTopicService; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 去重处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-19 |
||||
|
**/ |
||||
|
public class DistinctMessageHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { |
||||
|
|
||||
|
private ApiMessageLogService apiMessageLogService = SpringUtil.getBean(ApiMessageLogService.class); |
||||
|
|
||||
|
|
||||
|
private ApiMessageTopicService apiMessageTopicService = SpringUtil.getBean(ApiMessageTopicService.class); |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception { |
||||
|
String content = textWebSocketFrame.text(); |
||||
|
BaseMessage message = JSON.parseObject(content, BaseMessage.class); |
||||
|
String messageType = message.getMessageType(); |
||||
|
String messageId = message.getId(); |
||||
|
String topic = message.getTopic(); |
||||
|
boolean hasReceived = false; |
||||
|
if (messageType.equals(MessageTypeEnum.REQUEST.name())) { |
||||
|
hasReceived = apiMessageLogService.checkRequestMessageExist(messageId); |
||||
|
if (hasReceived) { |
||||
|
// 发送响应,终止流程
|
||||
|
String responseTopic = apiMessageTopicService.getResponseTopicCodeByCode(topic); |
||||
|
ResponseMessageSender sender = (ResponseMessageSender) MessageSenderFactory.createSender(responseTopic); |
||||
|
ApiMessageLog log = apiMessageLogService.getByRequestMessageId(messageId); |
||||
|
RequestMessage reqeustMessage = JSON.parseObject(content, RequestMessage.class); |
||||
|
sender.sendMessage(ctx.channel(), reqeustMessage); |
||||
|
|
||||
|
|
||||
|
} else { |
||||
|
// 继续往下传递
|
||||
|
ReferenceCountUtil.retain(textWebSocketFrame); |
||||
|
ctx.fireChannelRead(textWebSocketFrame); |
||||
|
} |
||||
|
|
||||
|
} else if (messageType.equals(MessageTypeEnum.RESPONSE.name())) { |
||||
|
hasReceived = apiMessageLogService.checkResponseMessageExist(messageId); |
||||
|
if (hasReceived) { |
||||
|
// 不做处理,终止流程
|
||||
|
} else { |
||||
|
// 继续往下传递
|
||||
|
ReferenceCountUtil.retain(textWebSocketFrame); |
||||
|
ctx.fireChannelRead(textWebSocketFrame); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
package tech.popsoft.cip.client.framework.customhandler; |
||||
|
|
||||
|
import io.netty.channel.ChannelHandlerContext; |
||||
|
import io.netty.channel.SimpleChannelInboundHandler; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.framework.handler.MessageHandlerFactory; |
||||
|
import tech.popsoft.cip.client.framework.handler.RequestMessageHandler; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
import tech.popsoft.cip.client.sender.response.system.ErrorResponseSender; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 客户端请求消息业务逻辑处理 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-2-5 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class RequestMessageBusinessHandler extends SimpleChannelInboundHandler<RequestMessage> { |
||||
|
|
||||
|
private ApiMessageLogService apiMessageLogService = SpringUtil.getBean(ApiMessageLogService.class); |
||||
|
|
||||
|
@Override |
||||
|
protected void channelRead0(ChannelHandlerContext ctx, RequestMessage message) throws Exception { |
||||
|
String requestMessageId = StringUtils.EMPTY; |
||||
|
String topic = StringUtils.EMPTY; |
||||
|
try { |
||||
|
|
||||
|
// 获取消息主题
|
||||
|
topic = message.getTopic(); |
||||
|
// 获取消息标识
|
||||
|
requestMessageId = message.getId(); |
||||
|
// 转具体的消息处理器进行处理
|
||||
|
RequestMessageHandler handler = (RequestMessageHandler) MessageHandlerFactory.createHandler(topic); |
||||
|
handler.handleMessage(message, ctx.channel()); |
||||
|
} catch (Exception e) { |
||||
|
// 记录消息请求日志
|
||||
|
apiMessageLogService.createRequestPart(message); |
||||
|
String errorMessage = e.getMessage(); |
||||
|
// 统一响应错误
|
||||
|
ErrorResponseSender errorResponseSender = new ErrorResponseSender(); |
||||
|
errorResponseSender.setErrorMessage(e.getMessage()); |
||||
|
errorResponseSender.sendMessage(ctx.channel(), message); |
||||
|
log.error("发生异常:", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,54 @@ |
|||||
|
package tech.popsoft.cip.client.framework.customhandler; |
||||
|
|
||||
|
import io.netty.channel.ChannelHandlerContext; |
||||
|
import io.netty.channel.SimpleChannelInboundHandler; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import tech.abc.platform.cip.common.entity.ResponseMessage; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.framework.handler.MessageHandlerFactory; |
||||
|
import tech.popsoft.cip.client.framework.handler.ResponseMessageHandler; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 客户端响应消息业务逻辑处理 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-2-5 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class ResponseMessageBusinessHandler extends SimpleChannelInboundHandler<ResponseMessage> { |
||||
|
|
||||
|
private ApiMessageLogService apiMessageLogService = SpringUtil.getBean(ApiMessageLogService.class); |
||||
|
|
||||
|
@Override |
||||
|
protected void channelRead0(ChannelHandlerContext ctx, ResponseMessage message) throws Exception { |
||||
|
String requestMessageId = StringUtils.EMPTY; |
||||
|
String topic = StringUtils.EMPTY; |
||||
|
try { |
||||
|
|
||||
|
// 获取消息主题
|
||||
|
topic = message.getTopic(); |
||||
|
// 获取消息标识
|
||||
|
requestMessageId = message.getId(); |
||||
|
// 转具体的消息处理器进行处理
|
||||
|
ResponseMessageHandler handler = (ResponseMessageHandler) MessageHandlerFactory.createHandler(topic); |
||||
|
handler.handleMessage(message, ctx.channel()); |
||||
|
} catch (Exception e) { |
||||
|
// 更新消息日志
|
||||
|
apiMessageLogService.updateResponsePart(message); |
||||
|
// 对于错误响应,只记录日志,不再向发送方反馈响应
|
||||
|
log.error("收到客户端的响应消息有误:", e); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { |
||||
|
log.error("发生异常", cause); |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,120 @@ |
|||||
|
package tech.popsoft.cip.client.framework.customhandler; |
||||
|
|
||||
|
import io.netty.channel.*; |
||||
|
import io.netty.handler.codec.http.DefaultHttpHeaders; |
||||
|
import io.netty.handler.codec.http.FullHttpResponse; |
||||
|
import io.netty.handler.codec.http.websocketx.*; |
||||
|
import io.netty.util.CharsetUtil; |
||||
|
import io.netty.util.ReferenceCountUtil; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.framework.MessageClientConfig; |
||||
|
import tech.popsoft.cip.client.sender.request.system.LoginRequestSender; |
||||
|
|
||||
|
import java.net.URI; |
||||
|
import java.net.URISyntaxException; |
||||
|
|
||||
|
/** |
||||
|
* 处理web socket握手 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-9-28 |
||||
|
**/ |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Slf4j |
||||
|
@Data |
||||
|
public class WebSocketClientHandshakerHandler extends SimpleChannelInboundHandler<Object> { |
||||
|
/** |
||||
|
* 握手 |
||||
|
*/ |
||||
|
private WebSocketClientHandshaker handshaker; |
||||
|
/** |
||||
|
* 握手 异步处理 |
||||
|
*/ |
||||
|
private ChannelPromise handshakeFuture; |
||||
|
|
||||
|
public WebSocketClientHandshakerHandler() { |
||||
|
// 初始化握手处理者
|
||||
|
MessageClientConfig config = SpringUtil.getBean(MessageClientConfig.class); |
||||
|
URI webSocketUri = null; |
||||
|
try { |
||||
|
webSocketUri = new URI(config.getWebSocketUrl()); |
||||
|
} catch (URISyntaxException e) { |
||||
|
log.error("解析远程服务器地址出错", e); |
||||
|
} |
||||
|
WebSocketClientHandshaker webSocketClientHandshaker = WebSocketClientHandshakerFactory.newHandshaker(webSocketUri, |
||||
|
WebSocketVersion.V13, (String) null, true, new DefaultHttpHeaders()); |
||||
|
this.setHandshaker(webSocketClientHandshaker); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { |
||||
|
|
||||
|
Channel ch = ctx.channel(); |
||||
|
FullHttpResponse response; |
||||
|
// 进行握手操作
|
||||
|
if (!this.handshaker.isHandshakeComplete()) { |
||||
|
try { |
||||
|
response = (FullHttpResponse) msg; |
||||
|
// 握手协议返回,设置结束握手
|
||||
|
this.handshaker.finishHandshake(ch, response); |
||||
|
// 设置成功
|
||||
|
this.handshakeFuture.setSuccess(); |
||||
|
|
||||
|
} catch (WebSocketHandshakeException var7) { |
||||
|
FullHttpResponse res = (FullHttpResponse) msg; |
||||
|
String errorMsg = String.format("握手失败,status:%s,reason:%s", res.status(), res.content().toString(CharsetUtil.UTF_8)); |
||||
|
this.handshakeFuture.setFailure(new Exception(errorMsg)); |
||||
|
} |
||||
|
} else if (msg instanceof FullHttpResponse) { |
||||
|
// 已握手成功并将http协议升级为了WebSocket协议,不应再收到Http消息,发生这种情况则抛出异常
|
||||
|
response = (FullHttpResponse) msg; |
||||
|
throw new IllegalStateException("Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')'); |
||||
|
} else if (msg instanceof CloseWebSocketFrame) { |
||||
|
log.info("收到关闭信息"); |
||||
|
|
||||
|
} else if (msg instanceof TextWebSocketFrame) { |
||||
|
ReferenceCountUtil.retain(msg); |
||||
|
ctx.fireChannelRead(msg); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void handlerAdded(ChannelHandlerContext ctx) { |
||||
|
|
||||
|
|
||||
|
this.handshakeFuture = ctx.newPromise(); |
||||
|
this.handshakeFuture.addListener(new ChannelFutureListener() { |
||||
|
@Override |
||||
|
public void operationComplete(ChannelFuture future) throws Exception { |
||||
|
if (future.isSuccess()) { |
||||
|
// 发送登录请求
|
||||
|
log.info("握手成功"); |
||||
|
LoginRequestSender login = new LoginRequestSender(); |
||||
|
login.sendMessage(null); |
||||
|
|
||||
|
} else { |
||||
|
// 握手失败
|
||||
|
log.error("握手失败", future.cause()); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发起握手 |
||||
|
*/ |
||||
|
public void handshake(Channel channel) { |
||||
|
this.getHandshaker().handshake(channel); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,45 @@ |
|||||
|
package tech.popsoft.cip.client.framework.handler; |
||||
|
|
||||
|
|
||||
|
import tech.abc.platform.cip.common.entity.MessageException; |
||||
|
import tech.abc.platform.common.enums.StatusEnum; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageTopic; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageTopicService; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息处理抽象类 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-13 |
||||
|
**/ |
||||
|
public class MessageHandler { |
||||
|
|
||||
|
|
||||
|
protected ApiMessageLogService apiMessageLogService = SpringUtil.getBean(ApiMessageLogService.class); |
||||
|
|
||||
|
protected ApiMessageTopicService apiMessageTopicService = SpringUtil.getBean(ApiMessageTopicService.class); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 验证主题编码 |
||||
|
* |
||||
|
* @param topicCode 主题编码 |
||||
|
*/ |
||||
|
protected void validateTopic(String topicCode) { |
||||
|
try { |
||||
|
|
||||
|
ApiMessageTopic messageTopic = apiMessageTopicService.getByCode(topicCode); |
||||
|
if (messageTopic.getStatus().equals(StatusEnum.DEAD.name())) { |
||||
|
throw new MessageException("S102", "消息主题不可用"); |
||||
|
} |
||||
|
} catch (Exception ex) { |
||||
|
throw new MessageException("S101", "消息主题不存在"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
package tech.popsoft.cip.client.framework.handler; |
||||
|
|
||||
|
|
||||
|
import tech.abc.platform.cip.common.entity.MessageException; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageTopicService; |
||||
|
import tech.popsoft.cip.client.platform.exception.CustomException; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息处理器工厂 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-13 |
||||
|
**/ |
||||
|
public class MessageHandlerFactory { |
||||
|
|
||||
|
|
||||
|
public static MessageHandler createHandler(String topic) { |
||||
|
// 使用反射技术获取类
|
||||
|
Class<MessageHandler> messageHandler = null; |
||||
|
try { |
||||
|
// 根据消息主题获取对应的消息处理类名
|
||||
|
ApiMessageTopicService service = SpringUtil.getBean(ApiMessageTopicService.class); |
||||
|
String handlerName = service.getHandlerByCode(topic); |
||||
|
messageHandler = (Class<MessageHandler>) Class.forName(handlerName); |
||||
|
// 返回消息处理类的实例
|
||||
|
return messageHandler.newInstance(); |
||||
|
} catch (CustomException e) { |
||||
|
throw new MessageException("S101", e.getMessage()); |
||||
|
} catch (Exception e) { |
||||
|
throw new MessageException("S102", "消息处理器不存在"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
package tech.popsoft.cip.client.framework.handler; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.popsoft.cip.client.framework.sender.MessageSenderFactory; |
||||
|
import tech.popsoft.cip.client.framework.sender.ResponseMessageSender; |
||||
|
|
||||
|
/** |
||||
|
* 请求消息处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-8 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class RequestMessageHandler extends MessageHandler { |
||||
|
/** |
||||
|
* 消息处理 |
||||
|
* |
||||
|
* @param message 消息 |
||||
|
* @param channel 通道 |
||||
|
*/ |
||||
|
public void handleMessage(RequestMessage requestMessage, Channel channel) { |
||||
|
|
||||
|
// 记录消息请求日志
|
||||
|
apiMessageLogService.createRequestPart(requestMessage); |
||||
|
|
||||
|
|
||||
|
// 消息主题验证(是否存在及是否可用)
|
||||
|
validateTopic(requestMessage.getTopic()); |
||||
|
|
||||
|
// 特殊处理
|
||||
|
messageOperation(requestMessage, channel); |
||||
|
|
||||
|
// 发送响应至消息发送者
|
||||
|
sendResponse(requestMessage, channel); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
private void sendResponse(RequestMessage requestMessage, Channel channel) { |
||||
|
// 获取响应消息的消息主题
|
||||
|
String responseTopicCode = getResponseTopicCode(requestMessage.getTopic()); |
||||
|
// 根据消息主题构建发送器
|
||||
|
ResponseMessageSender responseMessageSender = (ResponseMessageSender) MessageSenderFactory.createSender(responseTopicCode); |
||||
|
// 发送消息
|
||||
|
responseMessageSender.sendMessage(channel, requestMessage); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取响应消息主题 |
||||
|
* |
||||
|
* @return |
||||
|
*/ |
||||
|
protected String getResponseTopicCode(String topic) { |
||||
|
// 默认从消息主题实体类中获取
|
||||
|
return apiMessageTopicService.getResponseTopicCodeByCode(topic); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 请求消息处理 |
||||
|
* |
||||
|
* @param message |
||||
|
* @param channel |
||||
|
*/ |
||||
|
protected void messageOperation(RequestMessage message, Channel channel) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
package tech.popsoft.cip.client.framework.handler; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.cip.common.entity.ResponseMessage; |
||||
|
|
||||
|
/** |
||||
|
* 响应消息处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-8 11:07 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class ResponseMessageHandler extends MessageHandler { |
||||
|
|
||||
|
/** |
||||
|
* 消息处理 |
||||
|
* |
||||
|
* @param message 消息 |
||||
|
* @param channel 通道 |
||||
|
*/ |
||||
|
public void handleMessage(ResponseMessage responseMessage, Channel channel) { |
||||
|
|
||||
|
|
||||
|
// 消息主题验证(是否存在及是否可用)
|
||||
|
validateTopic(responseMessage.getTopic()); |
||||
|
|
||||
|
// 更新消息日志
|
||||
|
apiMessageLogService.updateResponsePart(responseMessage); |
||||
|
|
||||
|
// 特殊处理
|
||||
|
messageOperation(responseMessage, channel); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 响应消息处理 |
||||
|
* |
||||
|
* @param message |
||||
|
* @param channel |
||||
|
*/ |
||||
|
protected void messageOperation(ResponseMessage message, Channel channel) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,69 @@ |
|||||
|
package tech.popsoft.cip.client.framework.sender; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.framework.MessageClientConfig; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息发送器基类 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-5 14:02 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public class MessageSender { |
||||
|
|
||||
|
public MessageSender() { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 消息主题 |
||||
|
*/ |
||||
|
private String topic; |
||||
|
/** |
||||
|
* 消息内容 |
||||
|
*/ |
||||
|
private String content; |
||||
|
|
||||
|
public MessageSender(String topic) { |
||||
|
this.topic = topic; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取消息主题 |
||||
|
*/ |
||||
|
public String getTopic() { |
||||
|
return this.topic; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 设置消息内容 |
||||
|
* |
||||
|
* @param content |
||||
|
*/ |
||||
|
public void setContent(String content) { |
||||
|
this.content = content; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取消息内容 |
||||
|
*/ |
||||
|
public String getContent() { |
||||
|
return this.content; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 应用程序配置 |
||||
|
*/ |
||||
|
protected MessageClientConfig messageClientConfig = SpringUtil.getBean(MessageClientConfig.class); |
||||
|
|
||||
|
/** |
||||
|
* 消息日志服务 |
||||
|
*/ |
||||
|
protected ApiMessageLogService apiMessageLogService = SpringUtil.getBean(ApiMessageLogService.class); |
||||
|
|
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package tech.popsoft.cip.client.framework.sender; |
||||
|
|
||||
|
|
||||
|
import tech.abc.platform.cip.common.entity.MessageException; |
||||
|
import tech.abc.platform.common.exception.CustomException; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageTopicService; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息发送器工厂 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-01-29 16:30 |
||||
|
**/ |
||||
|
public class MessageSenderFactory { |
||||
|
private MessageSenderFactory() { |
||||
|
} |
||||
|
|
||||
|
; |
||||
|
|
||||
|
public static MessageSender createSender(String topic) { |
||||
|
// 使用反射技术获取类
|
||||
|
Class<MessageSender> messageSender = null; |
||||
|
try { |
||||
|
// 根据消息主题获取对应的消息处理类名
|
||||
|
ApiMessageTopicService service = SpringUtil.getBean(ApiMessageTopicService.class); |
||||
|
String senderName = service.getSenderByCode(topic); |
||||
|
messageSender = (Class<MessageSender>) Class.forName(senderName); |
||||
|
// 返回消息发送器类的实例
|
||||
|
return messageSender.newInstance(); |
||||
|
} catch (CustomException e) { |
||||
|
throw new MessageException("S101", e.getMessage()); |
||||
|
} catch (Exception e) { |
||||
|
throw new MessageException("S102", "消息发送器不存在"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,105 @@ |
|||||
|
package tech.popsoft.cip.client.framework.sender; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import io.netty.channel.ChannelFuture; |
||||
|
import io.netty.channel.ChannelFutureListener; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.abc.platform.cip.common.enums.MessageStatusEnum; |
||||
|
import tech.popsoft.cip.client.framework.MessageClientGlobalHolder; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageLog; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 请求消息发送器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-5 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public class RequestMessageSender extends MessageSender { |
||||
|
|
||||
|
|
||||
|
public RequestMessageSender(String topic) { |
||||
|
super(topic); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送消息 |
||||
|
* |
||||
|
* @param content 消息内容 |
||||
|
* @param id 消息标识 |
||||
|
*/ |
||||
|
public void sendMessage(String content, String id) { |
||||
|
// 生成请求消息
|
||||
|
RequestMessage message = new RequestMessage(); |
||||
|
// 使用已有ID重置默认生成的ID,用于关联消息
|
||||
|
if (StringUtils.isNotBlank(id)) { |
||||
|
message.setId(id); |
||||
|
} |
||||
|
// 设置相关属性
|
||||
|
message.setTopic(super.getTopic()); |
||||
|
// 参数中消息内容优先,如为空,取对象属性的值
|
||||
|
if (StringUtils.isNotBlank(content)) { |
||||
|
message.setContent(content); |
||||
|
} else { |
||||
|
message.setContent(this.getContent()); |
||||
|
} |
||||
|
|
||||
|
message.setPublishAppCode(messageClientConfig.getAppCode()); |
||||
|
|
||||
|
|
||||
|
// 获取发送通道
|
||||
|
Channel channel = MessageClientGlobalHolder.channel; |
||||
|
if (channel != null && channel.isActive()) { |
||||
|
ChannelFuture channelFuture = channel.writeAndFlush(message); |
||||
|
channelFuture.addListener(new ChannelFutureListener() { |
||||
|
@Override |
||||
|
public void operationComplete(ChannelFuture future) throws Exception { |
||||
|
|
||||
|
if (StringUtils.isBlank(id)) { |
||||
|
// 创建日志
|
||||
|
ApiMessageLog log = apiMessageLogService.createRequestPart(message); |
||||
|
// 设置状态为已请求
|
||||
|
apiMessageLogService.updateStatus(MessageStatusEnum.REQUESTED.name(), log.getRequestId()); |
||||
|
// 发送次数加1
|
||||
|
apiMessageLogService.incrementSendCount(log.getRequestId()); |
||||
|
} else { |
||||
|
// 更新发送次数
|
||||
|
apiMessageLogService.incrementSendCount(id); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} else { |
||||
|
// 创建日志
|
||||
|
apiMessageLogService.createRequestPart(message); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送请求消息 |
||||
|
* |
||||
|
* @param content 消息内容 |
||||
|
*/ |
||||
|
public void sendMessage(String content) { |
||||
|
// 发送消息
|
||||
|
sendMessage(content, null); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 发送请求消息 |
||||
|
* |
||||
|
* @param content 消息内容 |
||||
|
*/ |
||||
|
public void sendMessage() { |
||||
|
// 发送消息
|
||||
|
sendMessage(null, null); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
package tech.popsoft.cip.client.framework.sender; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.abc.platform.cip.common.entity.ResponseMessage; |
||||
|
import tech.abc.platform.cip.common.enums.MessageResponseResultEnum; |
||||
|
|
||||
|
/** |
||||
|
* 响应消息发送器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-31 8:14 |
||||
|
**/ |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Data |
||||
|
public class ResponseMessageSender extends MessageSender { |
||||
|
|
||||
|
private String result; |
||||
|
|
||||
|
private String errorCode; |
||||
|
|
||||
|
private String errorMessage; |
||||
|
|
||||
|
|
||||
|
public ResponseMessageSender(String topic) { |
||||
|
super(topic); |
||||
|
// 默认设置响应结果为成功
|
||||
|
this.result = MessageResponseResultEnum.SUCCESS.name(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 发送响应 |
||||
|
* |
||||
|
* @param channel 通道 |
||||
|
* @param requestMessage 请求消息 |
||||
|
*/ |
||||
|
public void sendMessage(Channel channel, RequestMessage requestMessage) { |
||||
|
|
||||
|
// 组织响应消息
|
||||
|
ResponseMessage responseMessage = new ResponseMessage(); |
||||
|
responseMessage.setPublishAppCode(messageClientConfig.getAppCode()); |
||||
|
responseMessage.setRequestMessageId(requestMessage.getId()); |
||||
|
// 设置主题
|
||||
|
responseMessage.setTopic(this.getTopic()); |
||||
|
// 设置默认值
|
||||
|
responseMessage.setResult(this.result); |
||||
|
responseMessage.setErrorCode(this.errorCode); |
||||
|
responseMessage.setErrorMessage(this.errorMessage); |
||||
|
// 设置响应
|
||||
|
setResponseContent(requestMessage, responseMessage, channel); |
||||
|
// 发送响应给请求方
|
||||
|
channel.writeAndFlush(responseMessage); |
||||
|
|
||||
|
// 更新消息日志
|
||||
|
apiMessageLogService.updateResponsePart(responseMessage); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置响应消息内容 |
||||
|
* |
||||
|
* @param requestMessage 请求消息 |
||||
|
* @param responseMessage 响应消息 |
||||
|
* @param channel 通道 |
||||
|
*/ |
||||
|
protected void setResponseContent(RequestMessage requestMessage, ResponseMessage responseMessage, Channel channel) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
package tech.popsoft.cip.client.handler.request.lms.transportbill; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.popsoft.cip.client.framework.handler.RequestMessageHandler; |
||||
|
|
||||
|
/** |
||||
|
* 委托单创建请求消息处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-22 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class ConsignmentBillCreateRequestHandler extends RequestMessageHandler { |
||||
|
|
||||
|
@Override |
||||
|
protected void messageOperation(RequestMessage message, Channel channel) { |
||||
|
|
||||
|
// 进行业务处理
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
package tech.popsoft.cip.client.handler.response.system; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.cip.common.entity.ResponseMessage; |
||||
|
import tech.popsoft.cip.client.framework.handler.ResponseMessageHandler; |
||||
|
|
||||
|
/** |
||||
|
* 错误响应处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-16 8:53 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class ErrorResponseHandler extends ResponseMessageHandler { |
||||
|
@Override |
||||
|
protected void messageOperation(ResponseMessage message, Channel channel) { |
||||
|
log.info("收到错误响应"); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,67 @@ |
|||||
|
package tech.popsoft.cip.client.handler.response.system; |
||||
|
|
||||
|
import io.netty.channel.Channel; |
||||
|
import io.netty.channel.ChannelFuture; |
||||
|
import io.netty.channel.EventLoop; |
||||
|
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.abc.platform.cip.common.entity.ResponseMessage; |
||||
|
import tech.abc.platform.cip.common.enums.MessageResponseResultEnum; |
||||
|
import tech.abc.platform.common.utils.SpringUtil; |
||||
|
import tech.popsoft.cip.client.framework.MessageClientConfig; |
||||
|
import tech.popsoft.cip.client.framework.MessageClientGlobalHolder; |
||||
|
import tech.popsoft.cip.client.framework.handler.ResponseMessageHandler; |
||||
|
|
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
/** |
||||
|
* 登录响应处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-16 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class LoginResponseHandler extends ResponseMessageHandler { |
||||
|
@Override |
||||
|
protected void messageOperation(ResponseMessage message, Channel channel) { |
||||
|
log.info("收到登录响应"); |
||||
|
|
||||
|
if (message.getResult().equals(MessageResponseResultEnum.SUCCESS.name())) { |
||||
|
// 登录成功,将连接保存到全局变量
|
||||
|
MessageClientGlobalHolder.channel = channel; |
||||
|
// 启动心跳
|
||||
|
startHeartbeat(channel); |
||||
|
|
||||
|
} else { |
||||
|
// 登录失败,关闭通道,重新连接
|
||||
|
log.error("登录失败:{}", message.getErrorMessage()); |
||||
|
channel.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void startHeartbeat(Channel channel) { |
||||
|
// 获取心跳发送频率
|
||||
|
MessageClientConfig config = SpringUtil.getBean(MessageClientConfig.class); |
||||
|
int heartbeatRate = config.getHeartbeatRate(); |
||||
|
// 启动心跳
|
||||
|
EventLoop eventLoop = channel.eventLoop(); |
||||
|
eventLoop.scheduleWithFixedDelay(new Runnable() { |
||||
|
private Channel channel; |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void run() { |
||||
|
// log.info("发送心跳");
|
||||
|
PingWebSocketFrame frame = new PingWebSocketFrame(); |
||||
|
ChannelFuture channelFuture = channel.writeAndFlush(frame); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public Runnable setChannel(Channel channel) { |
||||
|
this.channel = channel; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
}.setChannel(channel), 15, heartbeatRate, TimeUnit.SECONDS); |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package tech.popsoft.cip.client.handler.response.system; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import tech.popsoft.cip.client.framework.handler.ResponseMessageHandler; |
||||
|
|
||||
|
/** |
||||
|
* 消息确认 响应处理器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-16 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
public class MessageConfirmResponseHandler extends ResponseMessageHandler { |
||||
|
|
||||
|
} |
@ -0,0 +1,185 @@ |
|||||
|
package tech.popsoft.cip.client.manage.controller; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import io.swagger.annotations.ApiImplicitParam; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.http.ResponseEntity; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.validation.annotation.Validated; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
import tech.abc.platform.common.annotation.SystemLog; |
||||
|
import tech.abc.platform.common.base.BaseController; |
||||
|
import tech.abc.platform.common.query.QueryGenerator; |
||||
|
import tech.abc.platform.common.utils.ResultUtil; |
||||
|
import tech.abc.platform.common.vo.PageInfo; |
||||
|
import tech.abc.platform.common.vo.Result; |
||||
|
import tech.abc.platform.common.vo.SortInfo; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageLog; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
import tech.popsoft.cip.client.manage.vo.ApiMessageLogVO; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 消息日志 前端控制器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@RestController |
||||
|
@RequestMapping("/cip/apiMessageLog") |
||||
|
@Slf4j |
||||
|
public class ApiMessageLogController extends BaseController { |
||||
|
@Autowired |
||||
|
private ApiMessageLogService apiMessageLogService; |
||||
|
|
||||
|
// region 基本操作
|
||||
|
|
||||
|
/** |
||||
|
* 初始化 |
||||
|
*/ |
||||
|
@ApiOperation(value = "初始化") |
||||
|
@GetMapping("/init") |
||||
|
public ResponseEntity<Result> init() { |
||||
|
ApiMessageLog entity = apiMessageLogService.init(); |
||||
|
ApiMessageLogVO vo = convert2VO(entity); |
||||
|
return ResultUtil.success(vo); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 新增 |
||||
|
*/ |
||||
|
@ApiOperation(value = "新增") |
||||
|
@PostMapping("/") |
||||
|
@SystemLog(value = "消息日志-新增") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageLog:add')") |
||||
|
public ResponseEntity<Result> add(@Validated @RequestBody ApiMessageLogVO vo) { |
||||
|
ApiMessageLog entity = convert2Entity(vo); |
||||
|
apiMessageLogService.add(entity); |
||||
|
ApiMessageLogVO newVO = convert2VO(entity); |
||||
|
return ResultUtil.success(newVO); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改 |
||||
|
*/ |
||||
|
@ApiOperation(value = "修改") |
||||
|
@PutMapping("/") |
||||
|
@SystemLog(value = "消息日志-修改") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageLog:modify')") |
||||
|
public ResponseEntity<Result> modify(@Validated @RequestBody ApiMessageLogVO vo) { |
||||
|
ApiMessageLog entity = convert2Entity(vo); |
||||
|
// 此处数据库会更新 更新人和更新时间,但数据模型不会刷新
|
||||
|
// 个别需展示的该类信息的地方可以重新查询后返回
|
||||
|
apiMessageLogService.modify(entity); |
||||
|
ApiMessageLogVO newVO = convert2VO(entity); |
||||
|
return ResultUtil.success(newVO); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除数据,单条数据标识,或多条数据标识用逗号间隔拼成的字符串 |
||||
|
*/ |
||||
|
@ApiOperation(value = "删除") |
||||
|
@ApiImplicitParam(name = "id", value = "标识", required = true, dataType = "String", paramType = "path") |
||||
|
@DeleteMapping("/{id}") |
||||
|
@SystemLog(value = "消息日志-删除") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageLog:remove')") |
||||
|
public ResponseEntity<Result> remove(@PathVariable("id") String id) { |
||||
|
apiMessageLogService.remove(id); |
||||
|
return ResultUtil.success(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 分页 |
||||
|
*/ |
||||
|
@ApiOperation(value = "分页查询") |
||||
|
@GetMapping("/page") |
||||
|
@SystemLog(value = "消息日志-分页") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageLog:query')") |
||||
|
public ResponseEntity<Result> page(ApiMessageLogVO queryVO, PageInfo pageInfo, SortInfo sortInfo) { |
||||
|
|
||||
|
// 构造分页对象
|
||||
|
IPage<ApiMessageLog> page = new Page<ApiMessageLog>(pageInfo.getPageNum(), pageInfo.getPageSize()); |
||||
|
// 构造查询条件
|
||||
|
QueryWrapper<ApiMessageLog> queryWrapper = QueryGenerator.generateQueryWrapper(ApiMessageLog.class, queryVO, sortInfo); |
||||
|
|
||||
|
// 查询数据
|
||||
|
apiMessageLogService.page(page, queryWrapper); |
||||
|
// 转换vo
|
||||
|
IPage<ApiMessageLogVO> pageVO = mapperFacade.map(page, IPage.class); |
||||
|
List<ApiMessageLogVO> apiMessageLogVOList = new ArrayList<>(); |
||||
|
for (int i = 0; i < page.getRecords().size(); i++) { |
||||
|
ApiMessageLogVO vo = convert2VO(page.getRecords().get(i)); |
||||
|
apiMessageLogVOList.add(vo); |
||||
|
} |
||||
|
pageVO.setRecords(apiMessageLogVOList); |
||||
|
; |
||||
|
return ResultUtil.success(pageVO); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 列表 |
||||
|
*/ |
||||
|
@ApiOperation(value = "获取列表") |
||||
|
@GetMapping("/list") |
||||
|
@SystemLog(value = "消息日志-列表") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageLog:query')") |
||||
|
public ResponseEntity<Result> list(ApiMessageLogVO queryVO, SortInfo sortInfo) { |
||||
|
// 构造查询条件
|
||||
|
QueryWrapper<ApiMessageLog> queryWrapper = QueryGenerator.generateQueryWrapper(ApiMessageLog.class, queryVO, sortInfo); |
||||
|
List<ApiMessageLog> list = apiMessageLogService.list(queryWrapper); |
||||
|
// 转换vo
|
||||
|
List<ApiMessageLogVO> apiMessageLogVOList = new ArrayList<>(); |
||||
|
for (int i = 0; i < list.size(); i++) { |
||||
|
ApiMessageLogVO vo = convert2VO(list.get(i)); |
||||
|
apiMessageLogVOList.add(vo); |
||||
|
} |
||||
|
return ResultUtil.success(apiMessageLogVOList); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取单条数据 |
||||
|
*/ |
||||
|
@ApiOperation(value = "获取单条数据") |
||||
|
@ApiImplicitParam(name = "id", value = "标识", required = true, dataType = "String", paramType = "path") |
||||
|
@GetMapping("/{id}") |
||||
|
@SystemLog(value = "消息日志-详情") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageLog:view')") |
||||
|
public ResponseEntity<Result> get(@PathVariable("id") String id) { |
||||
|
ApiMessageLog entity = apiMessageLogService.query(id); |
||||
|
ApiMessageLogVO vo = convert2VO(entity); |
||||
|
return ResultUtil.success(vo); |
||||
|
} |
||||
|
|
||||
|
// endregion
|
||||
|
|
||||
|
// region 扩展操作
|
||||
|
|
||||
|
// endregion
|
||||
|
|
||||
|
// region 辅助操作
|
||||
|
|
||||
|
private ApiMessageLogVO convert2VO(ApiMessageLog entity) { |
||||
|
ApiMessageLogVO vo = mapperFacade.map(entity, ApiMessageLogVO.class); |
||||
|
String name = dictionaryUtil.getNameByCode("MessageResponseResult", entity.getResponseResult()); |
||||
|
vo.setResponseResultName(name); |
||||
|
String statusName = dictionaryUtil.getNameByCode("MessageStatus", entity.getResponseResult()); |
||||
|
vo.setStatusName(statusName); |
||||
|
|
||||
|
return vo; |
||||
|
} |
||||
|
|
||||
|
private ApiMessageLog convert2Entity(ApiMessageLogVO vo) { |
||||
|
|
||||
|
ApiMessageLog entity = mapperFacade.map(vo, ApiMessageLog.class); |
||||
|
return entity; |
||||
|
} |
||||
|
|
||||
|
// endregion
|
||||
|
} |
@ -0,0 +1,224 @@ |
|||||
|
package tech.popsoft.cip.client.manage.controller; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import io.swagger.annotations.ApiImplicitParam; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.http.ResponseEntity; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.validation.annotation.Validated; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
import tech.abc.platform.common.annotation.SystemLog; |
||||
|
import tech.abc.platform.common.base.BaseController; |
||||
|
import tech.abc.platform.common.query.QueryGenerator; |
||||
|
import tech.abc.platform.common.utils.ResultUtil; |
||||
|
import tech.abc.platform.common.vo.PageInfo; |
||||
|
import tech.abc.platform.common.vo.Result; |
||||
|
import tech.abc.platform.common.vo.SortInfo; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageTopic; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageTopicService; |
||||
|
import tech.popsoft.cip.client.manage.vo.ApiMessageTopicVO; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 消息主题 前端控制器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@RestController |
||||
|
@RequestMapping("/cip/apiMessageTopic") |
||||
|
@Slf4j |
||||
|
public class ApiMessageTopicController extends BaseController { |
||||
|
@Autowired |
||||
|
private ApiMessageTopicService apiMessageTopicService; |
||||
|
|
||||
|
// region 基本操作
|
||||
|
|
||||
|
/** |
||||
|
* 初始化 |
||||
|
*/ |
||||
|
@ApiOperation(value = "初始化") |
||||
|
@GetMapping("/init") |
||||
|
public ResponseEntity<Result> init() { |
||||
|
ApiMessageTopic entity = apiMessageTopicService.init(); |
||||
|
ApiMessageTopicVO vo = convert2VO(entity); |
||||
|
return ResultUtil.success(vo); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 新增 |
||||
|
*/ |
||||
|
@ApiOperation(value = "新增") |
||||
|
@PostMapping("/") |
||||
|
@SystemLog(value = "消息主题-新增") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:add')") |
||||
|
public ResponseEntity<Result> add(@Validated @RequestBody ApiMessageTopicVO vo) { |
||||
|
ApiMessageTopic entity = convert2Entity(vo); |
||||
|
apiMessageTopicService.add(entity); |
||||
|
ApiMessageTopicVO newVO = convert2VO(entity); |
||||
|
return ResultUtil.success(newVO); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改 |
||||
|
*/ |
||||
|
@ApiOperation(value = "修改") |
||||
|
@PutMapping("/") |
||||
|
@SystemLog(value = "消息主题-修改") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:modify')") |
||||
|
public ResponseEntity<Result> modify(@Validated @RequestBody ApiMessageTopicVO vo) { |
||||
|
ApiMessageTopic entity = convert2Entity(vo); |
||||
|
// 此处数据库会更新 更新人和更新时间,但数据模型不会刷新
|
||||
|
// 个别需展示的该类信息的地方可以重新查询后返回
|
||||
|
apiMessageTopicService.modify(entity); |
||||
|
ApiMessageTopicVO newVO = convert2VO(entity); |
||||
|
return ResultUtil.success(newVO); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除数据,单条数据标识,或多条数据标识用逗号间隔拼成的字符串 |
||||
|
*/ |
||||
|
@ApiOperation(value = "删除") |
||||
|
@ApiImplicitParam(name = "id", value = "标识", required = true, dataType = "String", paramType = "path") |
||||
|
@DeleteMapping("/{id}") |
||||
|
@SystemLog(value = "消息主题-删除") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:remove')") |
||||
|
public ResponseEntity<Result> remove(@PathVariable("id") String id) { |
||||
|
apiMessageTopicService.remove(id); |
||||
|
return ResultUtil.success(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 分页 |
||||
|
*/ |
||||
|
@ApiOperation(value = "分页查询") |
||||
|
@GetMapping("/page") |
||||
|
@SystemLog(value = "消息主题-分页") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:query')") |
||||
|
public ResponseEntity<Result> page(ApiMessageTopicVO queryVO, PageInfo pageInfo, SortInfo sortInfo) { |
||||
|
|
||||
|
// 构造分页对象
|
||||
|
IPage<ApiMessageTopic> page = new Page<ApiMessageTopic>(pageInfo.getPageNum(), pageInfo.getPageSize()); |
||||
|
// 构造查询条件
|
||||
|
QueryWrapper<ApiMessageTopic> queryWrapper = QueryGenerator.generateQueryWrapper(ApiMessageTopic.class, queryVO, sortInfo); |
||||
|
|
||||
|
// 查询数据
|
||||
|
apiMessageTopicService.page(page, queryWrapper); |
||||
|
// 转换vo
|
||||
|
IPage<ApiMessageTopicVO> pageVO = mapperFacade.map(page, IPage.class); |
||||
|
List<ApiMessageTopicVO> apiMessageTopicVOList = new ArrayList<>(); |
||||
|
for (int i = 0; i < page.getRecords().size(); i++) { |
||||
|
ApiMessageTopicVO vo = convert2VO(page.getRecords().get(i)); |
||||
|
apiMessageTopicVOList.add(vo); |
||||
|
} |
||||
|
pageVO.setRecords(apiMessageTopicVOList); |
||||
|
; |
||||
|
return ResultUtil.success(pageVO); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 列表 |
||||
|
*/ |
||||
|
@ApiOperation(value = "获取列表") |
||||
|
@GetMapping("/list") |
||||
|
@SystemLog(value = "消息主题-列表") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:query')") |
||||
|
public ResponseEntity<Result> list(ApiMessageTopicVO queryVO, SortInfo sortInfo) { |
||||
|
// 构造查询条件
|
||||
|
QueryWrapper<ApiMessageTopic> queryWrapper = QueryGenerator.generateQueryWrapper(ApiMessageTopic.class, queryVO, sortInfo); |
||||
|
List<ApiMessageTopic> list = apiMessageTopicService.list(queryWrapper); |
||||
|
// 转换vo
|
||||
|
List<ApiMessageTopicVO> apiMessageTopicVOList = new ArrayList<>(); |
||||
|
for (int i = 0; i < list.size(); i++) { |
||||
|
ApiMessageTopicVO vo = convert2VO(list.get(i)); |
||||
|
apiMessageTopicVOList.add(vo); |
||||
|
} |
||||
|
return ResultUtil.success(apiMessageTopicVOList); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取单条数据 |
||||
|
*/ |
||||
|
@ApiOperation(value = "获取单条数据") |
||||
|
@ApiImplicitParam(name = "id", value = "标识", required = true, dataType = "String", paramType = "path") |
||||
|
@GetMapping("/{id}") |
||||
|
@SystemLog(value = "消息主题-详情") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:view')") |
||||
|
public ResponseEntity<Result> get(@PathVariable("id") String id) { |
||||
|
ApiMessageTopic entity = apiMessageTopicService.query(id); |
||||
|
ApiMessageTopicVO vo = convert2VO(entity); |
||||
|
return ResultUtil.success(vo); |
||||
|
} |
||||
|
|
||||
|
// endregion
|
||||
|
|
||||
|
// region 扩展操作
|
||||
|
|
||||
|
/** |
||||
|
* 启用 |
||||
|
*/ |
||||
|
@ApiOperation(value = "启用") |
||||
|
@PutMapping("/{id}/enable") |
||||
|
@ApiImplicitParam(name = "id", value = "标识", required = true, dataType = "String", paramType = "path") |
||||
|
@SystemLog(value = "消息主题-启用") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:enable')") |
||||
|
public ResponseEntity<Result> enable(@PathVariable("id") String id) { |
||||
|
|
||||
|
apiMessageTopicService.enable(id); |
||||
|
return ResultUtil.success(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停用 |
||||
|
*/ |
||||
|
@ApiOperation(value = "停用") |
||||
|
@PutMapping("/{id}/disable") |
||||
|
@ApiImplicitParam(name = "id", value = "标识", required = true, dataType = "String", paramType = "path") |
||||
|
@SystemLog(value = "消息主题-停用") |
||||
|
@PreAuthorize("hasPermission(null,'cip:apiMessageTopic:disable')") |
||||
|
public ResponseEntity<Result> disable(@PathVariable("id") String id) { |
||||
|
|
||||
|
apiMessageTopicService.disable(id); |
||||
|
return ResultUtil.success(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// endregion
|
||||
|
|
||||
|
// region 辅助操作
|
||||
|
|
||||
|
private ApiMessageTopicVO convert2VO(ApiMessageTopic entity) { |
||||
|
ApiMessageTopicVO vo = mapperFacade.map(entity, ApiMessageTopicVO.class); |
||||
|
String statusName = dictionaryUtil.getNameByCode("Status", entity.getStatus()); |
||||
|
vo.setStatusName(statusName); |
||||
|
String categoryName = dictionaryUtil.getNameByCode("ApiMessageTopicCategory", entity.getCategory()); |
||||
|
vo.setCategoryName(categoryName); |
||||
|
|
||||
|
if (StringUtils.isNotBlank(entity.getHasPermission())) { |
||||
|
String hasPermission = dictionaryUtil.getNameByCode("YesOrNo", entity.getHasPermission()); |
||||
|
vo.setHasPermissionName(hasPermission); |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isNotBlank(entity.getHasSubscription())) { |
||||
|
String hasSubscription = dictionaryUtil.getNameByCode("YesOrNo", entity.getHasSubscription()); |
||||
|
vo.setHasSubscriptionName(hasSubscription); |
||||
|
} |
||||
|
return vo; |
||||
|
} |
||||
|
|
||||
|
private ApiMessageTopic convert2Entity(ApiMessageTopicVO vo) { |
||||
|
|
||||
|
ApiMessageTopic entity = mapperFacade.map(vo, ApiMessageTopic.class); |
||||
|
return entity; |
||||
|
} |
||||
|
|
||||
|
// endregion
|
||||
|
} |
@ -0,0 +1,116 @@ |
|||||
|
package tech.popsoft.cip.client.manage.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.experimental.Accessors; |
||||
|
import tech.abc.platform.common.base.BaseEntity; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
/** |
||||
|
* 消息日志 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Accessors(chain = true) |
||||
|
@TableName("cip_message_log") |
||||
|
public class ApiMessageLog extends BaseEntity { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
/** |
||||
|
* 请求消息标识 |
||||
|
*/ |
||||
|
@TableField("request_id") |
||||
|
private String requestId; |
||||
|
|
||||
|
/** |
||||
|
* 请求应用编码 |
||||
|
*/ |
||||
|
@TableField("request_app_code") |
||||
|
private String requestAppCode; |
||||
|
|
||||
|
/** |
||||
|
* 请求消息主题编码 |
||||
|
*/ |
||||
|
@TableField("request_topic_code") |
||||
|
private String requestTopicCode; |
||||
|
|
||||
|
/** |
||||
|
* 请求时间 |
||||
|
*/ |
||||
|
@TableField("request_time") |
||||
|
private LocalDateTime requestTime; |
||||
|
|
||||
|
/** |
||||
|
* 请求内容 |
||||
|
*/ |
||||
|
@TableField("request_data") |
||||
|
private String requestData; |
||||
|
|
||||
|
/** |
||||
|
* 响应消息标识 |
||||
|
*/ |
||||
|
@TableField("response_id") |
||||
|
private String responseId; |
||||
|
|
||||
|
/** |
||||
|
* 响应应用编码 |
||||
|
*/ |
||||
|
@TableField("response_app_code") |
||||
|
private String responseAppCode; |
||||
|
|
||||
|
/** |
||||
|
* 响应消息主题编码 |
||||
|
*/ |
||||
|
@TableField("response_topic_code") |
||||
|
private String responseTopicCode; |
||||
|
|
||||
|
/** |
||||
|
* 响应时间 |
||||
|
*/ |
||||
|
@TableField("response_time") |
||||
|
private LocalDateTime responseTime; |
||||
|
|
||||
|
/** |
||||
|
* 响应内容 |
||||
|
*/ |
||||
|
@TableField("response_data") |
||||
|
private String responseData; |
||||
|
|
||||
|
/** |
||||
|
* 响应结果 |
||||
|
*/ |
||||
|
@TableField("response_result") |
||||
|
private String responseResult; |
||||
|
|
||||
|
/** |
||||
|
* 错误编码 |
||||
|
*/ |
||||
|
@TableField("error_code") |
||||
|
private String errorCode; |
||||
|
|
||||
|
/** |
||||
|
* 错误信息 |
||||
|
*/ |
||||
|
@TableField("error_message") |
||||
|
private String errorMessage; |
||||
|
|
||||
|
/** |
||||
|
* 当前状态 |
||||
|
*/ |
||||
|
@TableField("status") |
||||
|
private String status; |
||||
|
|
||||
|
/** |
||||
|
* 发送次数 |
||||
|
*/ |
||||
|
@TableField("send_count") |
||||
|
private Integer sendCount; |
||||
|
|
||||
|
} |
@ -0,0 +1,93 @@ |
|||||
|
package tech.popsoft.cip.client.manage.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.experimental.Accessors; |
||||
|
import tech.abc.platform.common.base.BaseEntity; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息主题 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Accessors(chain = true) |
||||
|
@TableName("cip_message_topic") |
||||
|
public class ApiMessageTopic extends BaseEntity { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
/** |
||||
|
* 编码 |
||||
|
*/ |
||||
|
@TableField("code") |
||||
|
private String code; |
||||
|
|
||||
|
/** |
||||
|
* 名称 |
||||
|
*/ |
||||
|
@TableField("name") |
||||
|
private String name; |
||||
|
|
||||
|
/** |
||||
|
* 处理器 |
||||
|
*/ |
||||
|
@TableField("handler") |
||||
|
private String handler; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 发送器 |
||||
|
*/ |
||||
|
@TableField("sender") |
||||
|
private String sender; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 响应主题编码 |
||||
|
*/ |
||||
|
@TableField("response_topic_code") |
||||
|
private String responseTopicCode; |
||||
|
|
||||
|
/** |
||||
|
* 分类 |
||||
|
*/ |
||||
|
@TableField("category") |
||||
|
private String category; |
||||
|
|
||||
|
/** |
||||
|
* 状态 |
||||
|
*/ |
||||
|
@TableField("status") |
||||
|
private String status; |
||||
|
|
||||
|
/** |
||||
|
* 备注 |
||||
|
*/ |
||||
|
@TableField("remark") |
||||
|
private String remark; |
||||
|
|
||||
|
/** |
||||
|
* 排序号 |
||||
|
*/ |
||||
|
@TableField("order_no") |
||||
|
private String orderNo; |
||||
|
|
||||
|
/** |
||||
|
* 是否已授权 |
||||
|
*/ |
||||
|
@TableField(exist = false) |
||||
|
private String hasPermission; |
||||
|
|
||||
|
/** |
||||
|
* 是否已订阅 |
||||
|
*/ |
||||
|
@TableField(exist = false) |
||||
|
private String hasSubscription; |
||||
|
|
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package tech.popsoft.cip.client.manage.exception; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import tech.abc.platform.common.exception.ExceptionInterface; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息主题相关异常 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date: 2020-03-29 19:11 |
||||
|
*/ |
||||
|
@Getter |
||||
|
public enum ApiMessageLogExceptionEnum implements ExceptionInterface { |
||||
|
|
||||
|
/** |
||||
|
* 消息日志不存在 |
||||
|
*/ |
||||
|
MESSAGE_LOG_NOT_EXIST("消息日志不存在"), |
||||
|
|
||||
|
; |
||||
|
private String message; |
||||
|
|
||||
|
ApiMessageLogExceptionEnum(String message) { |
||||
|
this.message = message; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
package tech.popsoft.cip.client.manage.exception; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import tech.abc.platform.common.exception.ExceptionInterface; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息主题相关异常 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date: 2020-03-29 19:11 |
||||
|
*/ |
||||
|
@Getter |
||||
|
public enum ApiMessageTopicExceptionEnum implements ExceptionInterface { |
||||
|
|
||||
|
/** |
||||
|
* 消息主题不存在 |
||||
|
*/ |
||||
|
TOPIC_NOT_EXIST("消息主题不存在"), |
||||
|
/** |
||||
|
* 消息主题未设置处理器 |
||||
|
*/ |
||||
|
TOPIC_NOT_SET_HANDLER("消息主题未设置处理器"), |
||||
|
/** |
||||
|
* 消息主题未设置发送器 |
||||
|
*/ |
||||
|
TOPIC_NOT_SET_SENDER("消息主题未设置发送器"), |
||||
|
/** |
||||
|
* 响应消息主题未设置处理器 |
||||
|
*/ |
||||
|
RESPONSE_TOPIC_NOT_SET_HANDLER("响应消息主题未设置处理器"), |
||||
|
; |
||||
|
private String message; |
||||
|
|
||||
|
ApiMessageTopicExceptionEnum(String message) { |
||||
|
this.message = message; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package tech.popsoft.cip.client.manage.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageLog; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息日志 Mapper 接口 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
public interface ApiMessageLogMapper extends BaseMapper<ApiMessageLog> { |
||||
|
|
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="tech.popsoft.cip.client.manage.mapper.ApiMessageLogMapper"> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,15 @@ |
|||||
|
package tech.popsoft.cip.client.manage.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageTopic; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息主题 Mapper 接口 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
public interface ApiMessageTopicMapper extends BaseMapper<ApiMessageTopic> { |
||||
|
|
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="tech.popsoft.cip.client.manage.mapper.ApiMessageTopicMapper"> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,92 @@ |
|||||
|
package tech.popsoft.cip.client.manage.service; |
||||
|
|
||||
|
|
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.abc.platform.cip.common.entity.ResponseMessage; |
||||
|
import tech.abc.platform.common.base.BaseService; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageLog; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息日志 服务类 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
public interface ApiMessageLogService extends BaseService<ApiMessageLog> { |
||||
|
|
||||
|
/** |
||||
|
* 检查请求消息是否已存在 |
||||
|
* |
||||
|
* @param requestMessageId 请求消息标识 |
||||
|
* @return true 存在 false 不存在 |
||||
|
*/ |
||||
|
boolean checkRequestMessageExist(String requestMessageId); |
||||
|
|
||||
|
/** |
||||
|
* 检查响应消息是否已存在 |
||||
|
* |
||||
|
* @param responseMessageId 响应消息标识 |
||||
|
* @return true 存在 false 不存在 |
||||
|
*/ |
||||
|
boolean checkResponseMessageExist(String responseMessageId); |
||||
|
|
||||
|
/** |
||||
|
* 根据请求消息标识获取消息日志对象 |
||||
|
* |
||||
|
* @param requestMessageId 请求消息标识 |
||||
|
* @return 消息日志对象 |
||||
|
*/ |
||||
|
ApiMessageLog getByRequestMessageId(String requestMessageId); |
||||
|
|
||||
|
/** |
||||
|
* 更新状态 |
||||
|
* |
||||
|
* @param status 状态 |
||||
|
* @param messageId 消息标识 |
||||
|
*/ |
||||
|
void updateStatus(String status, String messageId); |
||||
|
|
||||
|
/** |
||||
|
* 递增发送计数 |
||||
|
* |
||||
|
* @param messageId 消息标识 |
||||
|
*/ |
||||
|
void incrementSendCount(String messageId); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 创建日志,填充请求消息部分 |
||||
|
* |
||||
|
* @param message |
||||
|
*/ |
||||
|
ApiMessageLog createRequestPart(RequestMessage message); |
||||
|
|
||||
|
/** |
||||
|
* 创建日志,填充请求消息部分 |
||||
|
* |
||||
|
* @param message |
||||
|
* @param responseAppCode 响应应用编码 |
||||
|
*/ |
||||
|
ApiMessageLog createRequestPart(RequestMessage message, String responseAppCode); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 更新日志,填充响应消息部分 |
||||
|
* |
||||
|
* @param message |
||||
|
*/ |
||||
|
void updateResponsePart(ResponseMessage message); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取重发消息 |
||||
|
* |
||||
|
* @param messageCount 消息数量 |
||||
|
* @param maxSendCount 最大发送次数 |
||||
|
* @return {@link List}<{@link ApiMessageLog}> |
||||
|
*/ |
||||
|
List<ApiMessageLog> getResendMessage(int messageCount, int maxSendCount); |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
package tech.popsoft.cip.client.manage.service; |
||||
|
|
||||
|
import tech.abc.platform.common.base.BaseService; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageTopic; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息主题 服务类 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
public interface ApiMessageTopicService extends BaseService<ApiMessageTopic> { |
||||
|
|
||||
|
/** |
||||
|
* 启用 |
||||
|
* |
||||
|
* @param id 标识 |
||||
|
*/ |
||||
|
void enable(String id); |
||||
|
|
||||
|
/** |
||||
|
* 停用 |
||||
|
* |
||||
|
* @param id 标识 |
||||
|
*/ |
||||
|
void disable(String id); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 根据消息主题编码获取处理类 |
||||
|
* |
||||
|
* @param code 消息主题 |
||||
|
* @return 处理类名(含路径) |
||||
|
*/ |
||||
|
String getHandlerByCode(String code); |
||||
|
|
||||
|
/** |
||||
|
* 根据消息主题编码获取响应主题编码 |
||||
|
* |
||||
|
* @param code 消息主题 |
||||
|
* @return 响应主题编码 |
||||
|
*/ |
||||
|
String getResponseTopicCodeByCode(String code); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 根据消息主题编码获取消息主题标识 |
||||
|
* |
||||
|
* @param code 消息主题编码 |
||||
|
* @return 消息主题标识 |
||||
|
*/ |
||||
|
String getIdByCode(String code); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 根据消息主题编码获取消息主题对象 |
||||
|
* |
||||
|
* @param code 消息主题编码 |
||||
|
* @return 消息主题对象 |
||||
|
*/ |
||||
|
ApiMessageTopic getByCode(String code); |
||||
|
|
||||
|
/** |
||||
|
* 根据消息主题编码获取发送类 |
||||
|
* |
||||
|
* @param code 消息主题 |
||||
|
* @return 发送类名(含路径) |
||||
|
*/ |
||||
|
String getSenderByCode(String code); |
||||
|
|
||||
|
} |
@ -0,0 +1,146 @@ |
|||||
|
package tech.popsoft.cip.client.manage.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.collections.CollectionUtils; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import tech.abc.platform.cip.common.entity.RequestMessage; |
||||
|
import tech.abc.platform.cip.common.entity.ResponseMessage; |
||||
|
import tech.abc.platform.cip.common.enums.MessageStatusEnum; |
||||
|
import tech.abc.platform.common.base.BaseServiceImpl; |
||||
|
import tech.abc.platform.common.exception.CustomException; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageLog; |
||||
|
import tech.popsoft.cip.client.manage.exception.ApiMessageLogExceptionEnum; |
||||
|
import tech.popsoft.cip.client.manage.mapper.ApiMessageLogMapper; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageLogService; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 消息日志 服务实现类 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class ApiMessageLogServiceImpl extends BaseServiceImpl<ApiMessageLogMapper, ApiMessageLog> implements ApiMessageLogService { |
||||
|
@Override |
||||
|
public ApiMessageLog init() { |
||||
|
ApiMessageLog entity = new ApiMessageLog(); |
||||
|
|
||||
|
return entity; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean checkRequestMessageExist(String requestMessageId) { |
||||
|
long count = this.lambdaQuery().eq(ApiMessageLog::getRequestId, requestMessageId).count(); |
||||
|
return count > 0; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean checkResponseMessageExist(String responseMessageId) { |
||||
|
long count = this.lambdaQuery().eq(ApiMessageLog::getResponseId, responseMessageId).count(); |
||||
|
return count > 0; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ApiMessageLog getByRequestMessageId(String requestMessageId) { |
||||
|
List<ApiMessageLog> list = this.lambdaQuery().eq(ApiMessageLog::getRequestId, requestMessageId).list(); |
||||
|
if (CollectionUtils.isNotEmpty(list)) { |
||||
|
return list.get(0); |
||||
|
|
||||
|
} else { |
||||
|
throw new CustomException(ApiMessageLogExceptionEnum.MESSAGE_LOG_NOT_EXIST); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void updateStatus(String status, String messageId) { |
||||
|
ApiMessageLog apiMessageLog = getByRequestMessageId(messageId); |
||||
|
apiMessageLog.setStatus(status); |
||||
|
modify(apiMessageLog); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public ApiMessageLog createRequestPart(RequestMessage message, String responseAppCode) { |
||||
|
|
||||
|
ApiMessageLog log = new ApiMessageLog(); |
||||
|
log.setRequestId(message.getId()); |
||||
|
log.setRequestAppCode(message.getPublishAppCode()); |
||||
|
log.setRequestTopicCode(message.getTopic()); |
||||
|
log.setRequestTime(LocalDateTime.now()); |
||||
|
log.setRequestData(message.getContent()); |
||||
|
log.setResponseAppCode(responseAppCode); |
||||
|
// 发送次数设置为0
|
||||
|
log.setSendCount(0); |
||||
|
add(log); |
||||
|
return log; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ApiMessageLog createRequestPart(RequestMessage message) { |
||||
|
return createRequestPart(message, null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void updateResponsePart(ResponseMessage message) { |
||||
|
|
||||
|
ApiMessageLog log = getByRequestMessageId(message.getRequestMessageId()); |
||||
|
// 响应消息的发布者,对应请求消息的响应
|
||||
|
log.setResponseAppCode(message.getPublishAppCode()); |
||||
|
log.setResponseTopicCode(message.getTopic()); |
||||
|
log.setResponseTime(LocalDateTime.now()); |
||||
|
log.setResponseData(message.getContent()); |
||||
|
log.setResponseResult(message.getResult()); |
||||
|
log.setErrorMessage(message.getErrorMessage()); |
||||
|
log.setErrorCode(message.getErrorCode()); |
||||
|
log.setResponseId(message.getId()); |
||||
|
// 将消息更新为已响应
|
||||
|
log.setStatus(MessageStatusEnum.RESPONSED.name()); |
||||
|
|
||||
|
modify(log); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<ApiMessageLog> getResendMessage(int messageCount, int maxSendCount) { |
||||
|
LocalDateTime now = LocalDateTime.now(); |
||||
|
// 获取当前时间之前15秒的时间点
|
||||
|
LocalDateTime beforeNow = now.minusSeconds(15); |
||||
|
try { |
||||
|
LambdaQueryChainWrapper<ApiMessageLog> query = this.lambdaQuery() |
||||
|
// 消息状态为待发送或已发送
|
||||
|
.and(x -> x.eq(ApiMessageLog::getStatus, MessageStatusEnum.WAIT_REQUEST.name()) |
||||
|
.or(y -> y.eq(ApiMessageLog::getStatus, MessageStatusEnum.REQUESTED.name()))) |
||||
|
// 排除掉登录请求
|
||||
|
.ne(ApiMessageLog::getRequestTopicCode, "framework.login.request") |
||||
|
// 发送次数小于设置的最大发送次数
|
||||
|
.lt(ApiMessageLog::getSendCount, maxSendCount) |
||||
|
// 请求时间小于当前时间15秒,避免刚产生的消息尚未收到服务端响应时就进行重发
|
||||
|
.lt(ApiMessageLog::getRequestTime, beforeNow) |
||||
|
// 按照请求时间升序排列
|
||||
|
.orderByAsc(ApiMessageLog::getRequestTime) |
||||
|
// 只取指定数量的消息
|
||||
|
.last("limit " + messageCount); |
||||
|
|
||||
|
|
||||
|
return query.list(); |
||||
|
} catch (Exception e) { |
||||
|
log.error("获取发送信息失败", e); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void incrementSendCount(String messageId) { |
||||
|
ApiMessageLog apiMessageLog = getByRequestMessageId(messageId); |
||||
|
apiMessageLog.setSendCount(apiMessageLog.getSendCount() + 1); |
||||
|
modify(apiMessageLog); |
||||
|
} |
||||
|
} |
@ -0,0 +1,150 @@ |
|||||
|
package tech.popsoft.cip.client.manage.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import org.apache.commons.collections.CollectionUtils; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import tech.abc.platform.common.base.BaseServiceImpl; |
||||
|
import tech.abc.platform.common.enums.StatusEnum; |
||||
|
import tech.abc.platform.common.exception.CommonException; |
||||
|
import tech.abc.platform.common.exception.CustomException; |
||||
|
import tech.popsoft.cip.client.manage.entity.ApiMessageTopic; |
||||
|
import tech.popsoft.cip.client.manage.exception.ApiMessageTopicExceptionEnum; |
||||
|
import tech.popsoft.cip.client.manage.mapper.ApiMessageTopicMapper; |
||||
|
import tech.popsoft.cip.client.manage.service.ApiMessageTopicService; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 消息主题 服务实现类 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@Service |
||||
|
public class ApiMessageTopicServiceImpl extends BaseServiceImpl<ApiMessageTopicMapper, ApiMessageTopic> implements ApiMessageTopicService { |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public ApiMessageTopic init() { |
||||
|
ApiMessageTopic entity = new ApiMessageTopic(); |
||||
|
entity.setStatus(StatusEnum.NORMAL.name()); |
||||
|
return entity; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void beforeAdd(ApiMessageTopic entity) { |
||||
|
|
||||
|
|
||||
|
// 验证编码全局唯一
|
||||
|
QueryWrapper<ApiMessageTopic> queryWrapper = new QueryWrapper<>(); |
||||
|
queryWrapper.lambda().eq(ApiMessageTopic::getCode, entity.getCode()); |
||||
|
long count = count(queryWrapper); |
||||
|
if (count > 0) { |
||||
|
throw new CustomException(CommonException.CODE_EXIST); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// 验证名称全局唯一
|
||||
|
queryWrapper.lambda().eq(ApiMessageTopic::getName, entity.getName()); |
||||
|
count = count(queryWrapper); |
||||
|
if (count > 0) { |
||||
|
throw new CustomException(CommonException.NAME_EXIST); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void beforeModify(ApiMessageTopic entity) { |
||||
|
|
||||
|
|
||||
|
// 验证编码全局唯一
|
||||
|
QueryWrapper<ApiMessageTopic> queryWrapper = new QueryWrapper<>(); |
||||
|
queryWrapper.lambda().eq(ApiMessageTopic::getCode, entity.getCode()) |
||||
|
.ne(ApiMessageTopic::getId, entity.getId()); |
||||
|
long count = count(queryWrapper); |
||||
|
if (count > 0) { |
||||
|
throw new CustomException(CommonException.CODE_EXIST); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// 验证名称全局唯一
|
||||
|
queryWrapper.lambda().eq(ApiMessageTopic::getName, entity.getName()) |
||||
|
.ne(ApiMessageTopic::getId, entity.getId()); |
||||
|
count = count(queryWrapper); |
||||
|
if (count > 0) { |
||||
|
throw new CustomException(CommonException.NAME_EXIST); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void enable(String id) { |
||||
|
ApiMessageTopic entity = getEntity(id); |
||||
|
entity.setStatus(StatusEnum.NORMAL.name()); |
||||
|
modify(entity); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void disable(String id) { |
||||
|
ApiMessageTopic entity = getEntity(id); |
||||
|
entity.setStatus(StatusEnum.DEAD.name()); |
||||
|
modify(entity); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public String getHandlerByCode(String code) { |
||||
|
List<ApiMessageTopic> list = this.lambdaQuery().eq(ApiMessageTopic::getCode, code).list(); |
||||
|
if (CollectionUtils.isEmpty(list)) { |
||||
|
throw new CustomException(ApiMessageTopicExceptionEnum.TOPIC_NOT_EXIST); |
||||
|
} |
||||
|
String handler = list.get(0).getHandler(); |
||||
|
if (StringUtils.isBlank(handler)) { |
||||
|
throw new CustomException(ApiMessageTopicExceptionEnum.TOPIC_NOT_SET_HANDLER); |
||||
|
} |
||||
|
return handler; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getResponseTopicCodeByCode(String code) { |
||||
|
List<ApiMessageTopic> list = this.lambdaQuery().eq(ApiMessageTopic::getCode, code).list(); |
||||
|
if (CollectionUtils.isEmpty(list)) { |
||||
|
throw new CustomException(ApiMessageTopicExceptionEnum.TOPIC_NOT_EXIST); |
||||
|
} |
||||
|
String responseTopicCode = list.get(0).getResponseTopicCode(); |
||||
|
if (StringUtils.isBlank(responseTopicCode)) { |
||||
|
throw new CustomException(ApiMessageTopicExceptionEnum.RESPONSE_TOPIC_NOT_SET_HANDLER); |
||||
|
} |
||||
|
return responseTopicCode; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getIdByCode(String code) { |
||||
|
return getByCode(code).getId(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ApiMessageTopic getByCode(String code) { |
||||
|
List<ApiMessageTopic> list = this.lambdaQuery().eq(ApiMessageTopic::getCode, code).list(); |
||||
|
if (CollectionUtils.isNotEmpty(list)) { |
||||
|
return list.get(0); |
||||
|
|
||||
|
} else { |
||||
|
throw new CustomException(ApiMessageTopicExceptionEnum.TOPIC_NOT_EXIST); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getSenderByCode(String code) { |
||||
|
ApiMessageTopic apiMessageTopic = getByCode(code); |
||||
|
String sender = apiMessageTopic.getSender(); |
||||
|
if (StringUtils.isBlank(sender)) { |
||||
|
throw new CustomException(ApiMessageTopicExceptionEnum.TOPIC_NOT_SET_SENDER); |
||||
|
} |
||||
|
return sender; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
package tech.popsoft.cip.client.manage.vo; |
||||
|
|
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.experimental.Accessors; |
||||
|
import tech.abc.platform.common.base.BaseVO; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
/** |
||||
|
* 消息日志 视图对象 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Accessors(chain = true) |
||||
|
|
||||
|
@ApiModel(value = "消息日志对象") |
||||
|
public class ApiMessageLogVO extends BaseVO { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@ApiModelProperty(value = "请求消息标识") |
||||
|
private String requestId; |
||||
|
|
||||
|
@ApiModelProperty(value = "请求应用编码") |
||||
|
private String requestAppCode; |
||||
|
|
||||
|
@ApiModelProperty(value = "请求消息主题编码") |
||||
|
private String requestTopicCode; |
||||
|
|
||||
|
@ApiModelProperty(value = "请求时间") |
||||
|
private LocalDateTime requestTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "请求内容") |
||||
|
private String requestData; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应消息标识") |
||||
|
private String responseId; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应应用编码") |
||||
|
private String responseAppCode; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应消息主题编码") |
||||
|
private String responseTopicCode; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应时间") |
||||
|
private LocalDateTime responseTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应内容") |
||||
|
private String responseData; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应结果") |
||||
|
private String responseResult; |
||||
|
|
||||
|
@ApiModelProperty(value = "错误编码") |
||||
|
private String errorCode; |
||||
|
|
||||
|
@ApiModelProperty(value = "错误信息") |
||||
|
private String errorMessage; |
||||
|
|
||||
|
@ApiModelProperty(value = "当前状态") |
||||
|
private String status; |
||||
|
|
||||
|
@ApiModelProperty(value = "发送次数") |
||||
|
private Integer sendCount; |
||||
|
/********自定义扩展*****/ |
||||
|
|
||||
|
/********字典类*****/ |
||||
|
@ApiModelProperty(value = "消息状态") |
||||
|
private String responseResultName; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应结果") |
||||
|
private String statusName; |
||||
|
/********子对象*****/ |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,78 @@ |
|||||
|
package tech.popsoft.cip.client.manage.vo; |
||||
|
|
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.experimental.Accessors; |
||||
|
import tech.abc.platform.common.base.BaseVO; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 消息主题 视图对象 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-08-21 |
||||
|
*/ |
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Accessors(chain = true) |
||||
|
|
||||
|
@ApiModel(value = "消息主题对象") |
||||
|
public class ApiMessageTopicVO extends BaseVO { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@ApiModelProperty(value = "编码") |
||||
|
private String code; |
||||
|
|
||||
|
@ApiModelProperty(value = "名称") |
||||
|
private String name; |
||||
|
|
||||
|
@ApiModelProperty(value = "处理器") |
||||
|
private String handler; |
||||
|
|
||||
|
@ApiModelProperty(value = "发送器") |
||||
|
private String sender; |
||||
|
|
||||
|
@ApiModelProperty(value = "响应主题编码") |
||||
|
private String responseTopicCode; |
||||
|
|
||||
|
@ApiModelProperty(value = "分类") |
||||
|
private String category; |
||||
|
|
||||
|
@ApiModelProperty(value = "状态") |
||||
|
private String status; |
||||
|
|
||||
|
@ApiModelProperty(value = "备注") |
||||
|
private String remark; |
||||
|
|
||||
|
@ApiModelProperty(value = "排序号") |
||||
|
private String orderNo; |
||||
|
/********自定义扩展*****/ |
||||
|
@ApiModelProperty(value = "应用标识") |
||||
|
private String appId; |
||||
|
|
||||
|
@ApiModelProperty(value = "是否已授权") |
||||
|
private String hasPermission; |
||||
|
|
||||
|
@ApiModelProperty(value = "是否已订阅") |
||||
|
private String hasSubscription; |
||||
|
/********字典类*****/ |
||||
|
@ApiModelProperty(value = "状态") |
||||
|
private String statusName; |
||||
|
|
||||
|
@ApiModelProperty(value = "分类") |
||||
|
private String categoryName; |
||||
|
|
||||
|
@ApiModelProperty(value = "是否已授权") |
||||
|
private String hasPermissionName; |
||||
|
|
||||
|
@ApiModelProperty(value = "是否已订阅") |
||||
|
private String hasSubscriptionName; |
||||
|
|
||||
|
/********子对象*****/ |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package tech.popsoft.cip.client.platform.exception; |
||||
|
|
||||
|
|
||||
|
import java.text.MessageFormat; |
||||
|
|
||||
|
/** |
||||
|
* 自定义异常 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
*/ |
||||
|
public class CustomException extends RuntimeException { |
||||
|
|
||||
|
/** |
||||
|
* 继承exception |
||||
|
*/ |
||||
|
public CustomException(ExceptionInterface exceptionInterface) { |
||||
|
super(exceptionInterface.getMessage()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 错误信息带参数 |
||||
|
*/ |
||||
|
public CustomException(ExceptionInterface exceptionInterface, Object... args) { |
||||
|
super(MessageFormat.format(exceptionInterface.getMessage(), args)); |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package tech.popsoft.cip.client.platform.exception; |
||||
|
|
||||
|
/** |
||||
|
* 异常接口 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-8-1 |
||||
|
*/ |
||||
|
public interface ExceptionInterface { |
||||
|
/** |
||||
|
* 获取异常信息 |
||||
|
* |
||||
|
* @return 异常信息 |
||||
|
*/ |
||||
|
String getMessage(); |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
package tech.popsoft.cip.client.receiver; |
||||
|
|
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import tech.abc.platform.cip.common.entity.MessageException; |
||||
|
import tech.popsoft.cip.client.framework.sender.MessageSenderFactory; |
||||
|
import tech.popsoft.cip.client.framework.sender.RequestMessageSender; |
||||
|
|
||||
|
/** |
||||
|
* 接收业务系统产生的消息 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-18 |
||||
|
**/ |
||||
|
@RestController |
||||
|
@RequestMapping("/message") |
||||
|
public class ReceiveMessageController { |
||||
|
|
||||
|
@GetMapping |
||||
|
public String receive(String topic, String id) { |
||||
|
// 数据验证
|
||||
|
if (StringUtils.isBlank(topic)) { |
||||
|
throw new RuntimeException("消息主题不能为空"); |
||||
|
} |
||||
|
// 根据消息主题查找发送器
|
||||
|
try { |
||||
|
RequestMessageSender messageSender = (RequestMessageSender) MessageSenderFactory.createSender(topic); |
||||
|
messageSender.sendMessage(id); |
||||
|
return "ok"; |
||||
|
} catch (MessageException e) { |
||||
|
throw new RuntimeException(e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
package tech.popsoft.cip.client.sender.request.lms.transportbill; |
||||
|
|
||||
|
import tech.popsoft.cip.client.framework.sender.RequestMessageSender; |
||||
|
|
||||
|
/** |
||||
|
* 委托单创建发送器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2022-1-18 |
||||
|
**/ |
||||
|
public class ConsignmentBillCreateSender extends RequestMessageSender { |
||||
|
|
||||
|
public ConsignmentBillCreateSender() { |
||||
|
super("lms.transportbill.consignmentbill.create"); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
package tech.popsoft.cip.client.sender.request.system; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||
|
import tech.popsoft.cip.client.framework.sender.RequestMessageSender; |
||||
|
|
||||
|
/** |
||||
|
* 登录请求发送器 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-6 |
||||
|
**/ |
||||
|
public class LoginRequestSender extends RequestMessageSender { |
||||
|
|
||||
|
|
||||
|
public LoginRequestSender() { |
||||
|
super("framework.login.request"); |
||||
|
|
||||
|
// 使用账号密钥构建消息内容
|
||||
|
JSONObject jsonObject = new JSONObject(); |
||||
|
jsonObject.put("appCode", messageClientConfig.getAppCode()); |
||||
|
// 对密钥进行加密处理
|
||||
|
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); |
||||
|
String password = encoder.encode(messageClientConfig.getAppSecret()); |
||||
|
jsonObject.put("appSecret", password); |
||||
|
super.setContent(jsonObject.toJSONString()); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
package tech.popsoft.cip.client.sender.response.system; |
||||
|
|
||||
|
|
||||
|
import tech.abc.platform.cip.common.enums.MessageResponseResultEnum; |
||||
|
import tech.popsoft.cip.client.framework.sender.ResponseMessageSender; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 向请求方发送发生错误的响应 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-14 10:38 |
||||
|
**/ |
||||
|
public class ErrorResponseSender extends ResponseMessageSender { |
||||
|
|
||||
|
public ErrorResponseSender() { |
||||
|
super("framework.error.response"); |
||||
|
// 默认设置结果为出错
|
||||
|
this.setResult(MessageResponseResultEnum.ERROR.name()); |
||||
|
// 默认设置错误编码
|
||||
|
this.setErrorCode("500"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
package tech.popsoft.cip.client.sender.response.system; |
||||
|
|
||||
|
|
||||
|
import tech.popsoft.cip.client.framework.sender.ResponseMessageSender; |
||||
|
|
||||
|
/** |
||||
|
* 向请求方发送发生消息确认的响应 |
||||
|
* |
||||
|
* @author wqliu |
||||
|
* @date 2021-10-14 10:38 |
||||
|
**/ |
||||
|
public class MessageConfirmResponseSender extends ResponseMessageSender { |
||||
|
|
||||
|
public MessageConfirmResponseSender() { |
||||
|
super("framework.message.confirm.response"); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
server: |
||||
|
port: 20001 |
||||
|
|
||||
|
params: |
||||
|
appCode: tms |
||||
|
appSecret: 8543692d-7ea7-4797-a746-47891bff2bec |
||||
|
host: localhost |
||||
|
port: 8997 |
||||
|
heartbeatRate: 5 |
||||
|
maxSendCount: 4 |
||||
|
sendMessageSpan: 30 |
||||
|
sendMessageCount: 10 |
||||
|
enableResend: true |
||||
|
webSocketPath: webSocket |
||||
|
readIdleTimeOut: 30 |
||||
|
spring: |
||||
|
# 数据库设置 |
||||
|
datasource: |
||||
|
url: jdbc:mysql://localhost:3306/cip_client?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai |
||||
|
username: root |
||||
|
password: root |
||||
|
# redis设置 |
||||
|
redis: |
||||
|
host: localhost |
||||
|
port: 6379 |
||||
|
password: test123 |
||||
|
platform-config: |
||||
|
message: |
||||
|
serverPort: 9998 |
@ -0,0 +1,94 @@ |
|||||
|
server: |
||||
|
port: 8080 |
||||
|
#数据连接 |
||||
|
spring: |
||||
|
datasource: |
||||
|
# 使用druid数据源 |
||||
|
type: com.alibaba.druid.pool.DruidDataSource |
||||
|
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
|
url: jdbc:mysql://localhost:3306/abc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true |
||||
|
username: root |
||||
|
password: root |
||||
|
druid: |
||||
|
# 连接池的配置信息 |
||||
|
# 初始化大小,最小,最大 |
||||
|
initial-size: 5 |
||||
|
min-idle: 5 |
||||
|
maxActive: 20 |
||||
|
# 配置获取连接等待超时的时间 |
||||
|
maxWait: 60000 |
||||
|
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 |
||||
|
timeBetweenEvictionRunsMillis: 600000 |
||||
|
# 配置一个连接在池中最小生存的时间,单位是毫秒 |
||||
|
minEvictableIdleTimeMillis: 300000 |
||||
|
validationQuery: SELECT 1 |
||||
|
testWhileIdle: false |
||||
|
testOnBorrow: true |
||||
|
testOnReturn: false |
||||
|
usePingMethod: false |
||||
|
devtools: |
||||
|
livereload: |
||||
|
enabled: true |
||||
|
redis: |
||||
|
host: localhost |
||||
|
port: 6379 |
||||
|
password: |
||||
|
#新版本redis的timeout是一个duration,需使用如下写法 |
||||
|
timeout: 10s |
||||
|
database: 0 |
||||
|
lettuce: |
||||
|
pool: |
||||
|
# 连接池中的最小空闲连接 |
||||
|
min-idle: 2 |
||||
|
# 连接池中的最大空闲连接 |
||||
|
max-idle: 2 |
||||
|
# 连接池的最大连接数 |
||||
|
max-active: 16 |
||||
|
#连接池最大阻塞等待时间 |
||||
|
max-wait: 30s |
||||
|
profiles: |
||||
|
active: dev |
||||
|
# session: |
||||
|
# store-type: REDIS |
||||
|
# timeout: 8h |
||||
|
servlet: |
||||
|
# 文件上传设置 |
||||
|
multipart: |
||||
|
#设置单个文件的大小,-1代表不限制 |
||||
|
max-file-size: -1 |
||||
|
#设置单次请求文件的大小,-1代表不限制 |
||||
|
max-request-size: -1 |
||||
|
|
||||
|
freemarker: |
||||
|
# 不检测是否存在模板,避免启动时控制台输出警告信息 |
||||
|
checkTemplateLocation: false |
||||
|
|
||||
|
|
||||
|
|
||||
|
#mybatis-plus配置 |
||||
|
mybatis-plus: |
||||
|
# mapper-locations: classpath*:/system/**.xml |
||||
|
global-config: |
||||
|
db-config: |
||||
|
logic-delete-value: "YES" # 逻辑已删除值 |
||||
|
logic-not-delete-value: "NO" # 逻辑未删除值 |
||||
|
#平台配置 |
||||
|
platform-config: |
||||
|
system: |
||||
|
enablePermission: false |
||||
|
userInitPassword: 12345678 |
||||
|
tokenSecret: wqliu |
||||
|
exportDataPageSize: 2 |
||||
|
message: |
||||
|
serverPort: 8997 |
||||
|
messageServerAppCode: CIP |
||||
|
readIdleTimeOut: 60 |
||||
|
maxSendCount: 10 |
||||
|
sendMessageSpan: 30 |
||||
|
sendMessageCount: 10 |
||||
|
enableResend: true |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,84 @@ |
|||||
|
-- -------------------------------------------------------- |
||||
|
-- 主机: 127.0.0.1 |
||||
|
-- 服务器版本: 8.0.19 - MySQL Community Server - GPL |
||||
|
-- 服务器操作系统: Win64 |
||||
|
-- HeidiSQL 版本: 12.5.0.6677 |
||||
|
-- -------------------------------------------------------- |
||||
|
|
||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; |
||||
|
/*!40101 SET NAMES utf8 */; |
||||
|
/*!50503 SET NAMES utf8mb4 */; |
||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; |
||||
|
/*!40103 SET TIME_ZONE='+00:00' */; |
||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; |
||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; |
||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; |
||||
|
|
||||
|
|
||||
|
-- 导出 cip_client 的数据库结构 |
||||
|
CREATE DATABASE IF NOT EXISTS `cip_client` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin */ /*!80016 DEFAULT ENCRYPTION='N' */; |
||||
|
USE `cip_client`; |
||||
|
|
||||
|
-- 导出 表 cip_client.cip_message_log 结构 |
||||
|
CREATE TABLE IF NOT EXISTS `cip_message_log` ( |
||||
|
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '标识', |
||||
|
`request_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求消息标识', |
||||
|
`request_app_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求应用编码', |
||||
|
`request_topic_code` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求消息主题编码', |
||||
|
`request_time` datetime DEFAULT NULL COMMENT '请求时间', |
||||
|
`request_data` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求内容', |
||||
|
`response_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '响应消息标识', |
||||
|
`response_app_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '响应应用编码', |
||||
|
`response_topic_code` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '响应消息主题编码', |
||||
|
`response_time` datetime DEFAULT NULL COMMENT '响应时间', |
||||
|
`response_data` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '响应内容', |
||||
|
`response_result` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '响应结果', |
||||
|
`error_code` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '错误编码', |
||||
|
`error_message` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '错误信息', |
||||
|
`status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '当前状态', |
||||
|
`send_count` int DEFAULT NULL COMMENT '发送次数', |
||||
|
`create_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人标识', |
||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间', |
||||
|
`update_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新人标识', |
||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间', |
||||
|
`version` int DEFAULT NULL COMMENT '版本', |
||||
|
`delete_flag` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '删除标志', |
||||
|
PRIMARY KEY (`id`) |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='消息日志'; |
||||
|
|
||||
|
-- 正在导出表 cip_client.cip_message_log 的数据:~0 rows (大约) |
||||
|
|
||||
|
-- 导出 表 cip_client.cip_message_topic 结构 |
||||
|
CREATE TABLE IF NOT EXISTS `cip_message_topic` ( |
||||
|
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '标识', |
||||
|
`code` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '编码', |
||||
|
`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '名称', |
||||
|
`handler` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '处理器', |
||||
|
`sender` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '发送器', |
||||
|
`response_topic_code` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '响应主题编码', |
||||
|
`category` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '分类', |
||||
|
`status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '状态', |
||||
|
`remark` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注', |
||||
|
`order_no` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '排序号', |
||||
|
`create_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人标识', |
||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间', |
||||
|
`update_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新人标识', |
||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间', |
||||
|
`version` int DEFAULT NULL COMMENT '版本', |
||||
|
`delete_flag` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '删除标志', |
||||
|
PRIMARY KEY (`id`) |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='消息主题'; |
||||
|
|
||||
|
-- 正在导出表 cip_client.cip_message_topic 的数据:~5 rows (大约) |
||||
|
INSERT INTO `cip_message_topic` (`id`, `code`, `name`, `handler`, `sender`, `response_topic_code`, `category`, `status`, `remark`, `order_no`, `create_id`, `create_time`, `update_id`, `update_time`, `version`, `delete_flag`) VALUES |
||||
|
('1448247847417774081', 'framework.login.response', '登录响应', 'tech.popsoft.cip.client.handler.response.system.LoginResponseHandler', NULL, '', 'USER', 'NORMAL', NULL, '01', '1', '2021-10-13 19:22:49', '1', '2021-10-13 19:22:49', 1, 'NO'), |
||||
|
('1448247847417774082', 'lms.transportbill.consignmentbill.create', '委托单创建', 'tech.popsoft.cip.client.handler.request.lms.transportbill.ConsignmentBillCreateRequestHandler', 'tech.popsoft.cip.client.sender.request.lms.transportbill.ConsignmentBillCreateSender', 'framework.message.confirm.response', 'USER', 'NORMAL', NULL, '01', '1', '2021-10-13 19:22:49', '1', '2021-10-13 19:22:49', 1, 'NO'), |
||||
|
('1448247847417774083', 'framework.error.response', '错误响应', 'tech.popsoft.cip.client.handler.response.system.ErrorResponseHandler', 'tech.popsoft.cip.client.sender.response.system.ErrorResponseSender', '', 'USER', 'NORMAL', NULL, '01', '1', '2021-10-13 19:22:49', '1', '2021-10-13 19:22:49', 1, 'NO'), |
||||
|
('1448247847417774084', 'framework.message.confirm.response', '消息确认响应', 'tech.popsoft.cip.client.handler.response.system.MessageConfirmResponseHandler', 'tech.popsoft.cip.client.sender.response.system.MessageConfirmResponseSender', '', 'USER', 'NORMAL', NULL, '01', '1', '2021-10-13 19:22:49', '1', '2021-10-13 19:22:49', 1, 'NO'), |
||||
|
('1448247847417774086', 'framework.login.request', '登录请求', '', 'tech.popsoft.cip.client.sender.request.system.LoginRequestSender', '', 'USER', 'NORMAL', NULL, '01', '1', '2021-10-13 19:22:49', '1', '2021-10-13 19:22:49', 1, 'NO'); |
||||
|
|
||||
|
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */; |
||||
|
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; |
||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; |
||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; |
||||
|
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */; |
@ -0,0 +1,18 @@ |
|||||
|
package tech.popsoft; |
||||
|
|
||||
|
import org.junit.Test; |
||||
|
|
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
|
||||
|
/** |
||||
|
* Unit test for simple App. |
||||
|
*/ |
||||
|
public class AppTest { |
||||
|
/** |
||||
|
* Rigorous Test :-) |
||||
|
*/ |
||||
|
@Test |
||||
|
public void shouldAnswerWithTrue() { |
||||
|
assertTrue(true); |
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
###################################################################### |
||||
|
# Build Tools |
||||
|
|
||||
|
/unpackage/* |
||||
|
/node_modules/* |
||||
|
|
||||
|
###################################################################### |
||||
|
# Development Tools |
||||
|
|
||||
|
/.idea/* |
||||
|
/.vscode/* |
||||
|
/.hbuilderx/* |
||||
|
|
||||
|
package-lock.json |
||||
|
yarn.lock |
||||
|
|
@ -0,0 +1,34 @@ |
|||||
|
<script> |
||||
|
import config from './config' |
||||
|
import store from '@/store' |
||||
|
import { getToken } from '@/utils/auth' |
||||
|
|
||||
|
export default { |
||||
|
onLaunch: function() { |
||||
|
this.initApp() |
||||
|
}, |
||||
|
methods: { |
||||
|
// 初始化应用 |
||||
|
initApp() { |
||||
|
// 初始化应用配置 |
||||
|
this.initConfig() |
||||
|
// 检查用户登录状态 |
||||
|
//#ifdef H5 |
||||
|
this.checkLogin() |
||||
|
//#endif |
||||
|
}, |
||||
|
initConfig() { |
||||
|
this.globalData.config = config |
||||
|
}, |
||||
|
checkLogin() { |
||||
|
if (!getToken()) { |
||||
|
this.$tab.reLaunch('/pages/login') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import '@/static/scss/index.scss' |
||||
|
</style> |
@ -0,0 +1,21 @@ |
|||||
|
MIT License |
||||
|
|
||||
|
Copyright (c) 2022 |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1,50 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
// 登录方法
|
||||
|
export function login(username, password) { |
||||
|
|
||||
|
return request({ |
||||
|
'url': '/system/user/login?username='+username+'&password='+password, |
||||
|
'method': 'post' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 注册方法
|
||||
|
export function register(data) { |
||||
|
return request({ |
||||
|
url: '/register', |
||||
|
headers: { |
||||
|
isToken: false |
||||
|
}, |
||||
|
method: 'post', |
||||
|
data: data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 获取用户详细信息
|
||||
|
export function getInfo() { |
||||
|
return request({ |
||||
|
'url': '/getInfo', |
||||
|
'method': 'get' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 退出方法
|
||||
|
export function logout() { |
||||
|
return request({ |
||||
|
'url': '/logout', |
||||
|
'method': 'post' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 获取验证码
|
||||
|
export function getCodeImg() { |
||||
|
return request({ |
||||
|
'url': '/captchaImage', |
||||
|
headers: { |
||||
|
isToken: false |
||||
|
}, |
||||
|
method: 'get', |
||||
|
timeout: 20000 |
||||
|
}) |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
import upload from '@/utils/upload' |
||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
// 用户密码重置
|
||||
|
export function updateUserPwd(oldPassword, newPassword) { |
||||
|
const data = { |
||||
|
oldPassword, |
||||
|
newPassword |
||||
|
} |
||||
|
return request({ |
||||
|
url: '/system/user/profile/updatePwd', |
||||
|
method: 'put', |
||||
|
params: data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 查询用户个人信息
|
||||
|
export function getUserProfile() { |
||||
|
return request({ |
||||
|
url: '/system/user/profile', |
||||
|
method: 'get' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 修改用户个人信息
|
||||
|
export function updateUserProfile(data) { |
||||
|
return request({ |
||||
|
url: '/system/user/profile', |
||||
|
method: 'put', |
||||
|
data: data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 用户头像上传
|
||||
|
export function uploadAvatar(data) { |
||||
|
return upload({ |
||||
|
url: '/system/user/profile/avatar', |
||||
|
name: data.name, |
||||
|
filePath: data.filePath |
||||
|
}) |
||||
|
} |
@ -0,0 +1,167 @@ |
|||||
|
<template> |
||||
|
<view class="uni-section"> |
||||
|
<view class="uni-section-header" @click="onClick"> |
||||
|
<view class="uni-section-header__decoration" v-if="type" :class="type" /> |
||||
|
<slot v-else name="decoration"></slot> |
||||
|
|
||||
|
<view class="uni-section-header__content"> |
||||
|
<text :style="{'font-size':titleFontSize,'color':titleColor}" class="uni-section__content-title" :class="{'distraction':!subTitle}">{{ title }}</text> |
||||
|
<text v-if="subTitle" :style="{'font-size':subTitleFontSize,'color':subTitleColor}" class="uni-section-header__content-sub">{{ subTitle }}</text> |
||||
|
</view> |
||||
|
|
||||
|
<view class="uni-section-header__slot-right"> |
||||
|
<slot name="right"></slot> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="uni-section-content" :style="{padding: _padding}"> |
||||
|
<slot /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
/** |
||||
|
* Section 标题栏 |
||||
|
* @description 标题栏 |
||||
|
* @property {String} type = [line|circle|square] 标题装饰类型 |
||||
|
* @value line 竖线 |
||||
|
* @value circle 圆形 |
||||
|
* @value square 正方形 |
||||
|
* @property {String} title 主标题 |
||||
|
* @property {String} titleFontSize 主标题字体大小 |
||||
|
* @property {String} titleColor 主标题字体颜色 |
||||
|
* @property {String} subTitle 副标题 |
||||
|
* @property {String} subTitleFontSize 副标题字体大小 |
||||
|
* @property {String} subTitleColor 副标题字体颜色 |
||||
|
* @property {String} padding 默认插槽 padding |
||||
|
*/ |
||||
|
|
||||
|
export default { |
||||
|
name: 'UniSection', |
||||
|
emits:['click'], |
||||
|
props: { |
||||
|
type: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
title: { |
||||
|
type: String, |
||||
|
required: true, |
||||
|
default: '' |
||||
|
}, |
||||
|
titleFontSize: { |
||||
|
type: String, |
||||
|
default: '14px' |
||||
|
}, |
||||
|
titleColor:{ |
||||
|
type: String, |
||||
|
default: '#333' |
||||
|
}, |
||||
|
subTitle: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
subTitleFontSize: { |
||||
|
type: String, |
||||
|
default: '12px' |
||||
|
}, |
||||
|
subTitleColor: { |
||||
|
type: String, |
||||
|
default: '#999' |
||||
|
}, |
||||
|
padding: { |
||||
|
type: [Boolean, String], |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
computed:{ |
||||
|
_padding(){ |
||||
|
if(typeof this.padding === 'string'){ |
||||
|
return this.padding |
||||
|
} |
||||
|
|
||||
|
return this.padding?'10px':'' |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
title(newVal) { |
||||
|
if (uni.report && newVal !== '') { |
||||
|
uni.report('title', newVal) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onClick() { |
||||
|
this.$emit('click') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
<style lang="scss" > |
||||
|
$uni-primary: #2979ff !default; |
||||
|
|
||||
|
.uni-section { |
||||
|
background-color: #fff; |
||||
|
.uni-section-header { |
||||
|
position: relative; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
padding: 12px 10px; |
||||
|
font-weight: normal; |
||||
|
|
||||
|
&__decoration{ |
||||
|
margin-right: 6px; |
||||
|
background-color: $uni-primary; |
||||
|
&.line { |
||||
|
width: 4px; |
||||
|
height: 12px; |
||||
|
border-radius: 10px; |
||||
|
} |
||||
|
|
||||
|
&.circle { |
||||
|
width: 8px; |
||||
|
height: 8px; |
||||
|
border-top-right-radius: 50px; |
||||
|
border-top-left-radius: 50px; |
||||
|
border-bottom-left-radius: 50px; |
||||
|
border-bottom-right-radius: 50px; |
||||
|
} |
||||
|
|
||||
|
&.square { |
||||
|
width: 8px; |
||||
|
height: 8px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&__content { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
flex: 1; |
||||
|
color: #333; |
||||
|
|
||||
|
.distraction { |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
} |
||||
|
&-sub { |
||||
|
margin-top: 2px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&__slot-right{ |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.uni-section-content{ |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,26 @@ |
|||||
|
// 应用全局配置
|
||||
|
module.exports = { |
||||
|
baseUrl: 'http://localhost:8080', |
||||
|
// baseUrl: 'http://localhost:8080',
|
||||
|
// 应用信息
|
||||
|
appInfo: { |
||||
|
// 应用名称
|
||||
|
name: "platform-app", |
||||
|
// 应用版本
|
||||
|
version: "1.0.0", |
||||
|
// 应用logo
|
||||
|
logo: "/static/logo.png", |
||||
|
// 官方网站
|
||||
|
site_url: "https://popsoft.tech", |
||||
|
// 政策协议
|
||||
|
agreements: [{ |
||||
|
title: "隐私政策", |
||||
|
url: "https://popsoft.tech/protocol.html" |
||||
|
}, |
||||
|
{ |
||||
|
title: "用户服务协议", |
||||
|
url: "https://popsoft.tech/protocol.html" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import App from './App' |
||||
|
import store from './store' // store
|
||||
|
import plugins from './plugins' // plugins
|
||||
|
import './permission' // permission
|
||||
|
Vue.use(plugins) |
||||
|
|
||||
|
Vue.config.productionTip = false |
||||
|
Vue.prototype.$store = store |
||||
|
|
||||
|
App.mpType = 'app' |
||||
|
|
||||
|
const app = new Vue({ |
||||
|
...App |
||||
|
}) |
||||
|
|
||||
|
app.$mount() |
@ -0,0 +1,69 @@ |
|||||
|
{ |
||||
|
"name" : "移动端", |
||||
|
"appid" : "__UNI__25A9D80", |
||||
|
"description" : "", |
||||
|
"versionName" : "1.1.0", |
||||
|
"versionCode" : "100", |
||||
|
"transformPx" : false, |
||||
|
"app-plus" : { |
||||
|
"usingComponents" : true, |
||||
|
"nvueCompiler" : "uni-app", |
||||
|
"splashscreen" : { |
||||
|
"alwaysShowBeforeRender" : true, |
||||
|
"waiting" : true, |
||||
|
"autoclose" : true, |
||||
|
"delay" : 0 |
||||
|
}, |
||||
|
"modules" : {}, |
||||
|
"distribute" : { |
||||
|
"android" : { |
||||
|
"permissions" : [ |
||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
||||
|
] |
||||
|
}, |
||||
|
"ios" : {}, |
||||
|
"sdkConfigs" : {} |
||||
|
} |
||||
|
}, |
||||
|
"quickapp" : {}, |
||||
|
"mp-weixin" : { |
||||
|
"appid" : "wxccd7e2a0911b3397", |
||||
|
"setting" : { |
||||
|
"urlCheck" : false, |
||||
|
"es6" : false, |
||||
|
"minified" : true, |
||||
|
"postcss" : true |
||||
|
}, |
||||
|
"optimization" : { |
||||
|
"subPackages" : true |
||||
|
}, |
||||
|
"usingComponents" : true |
||||
|
}, |
||||
|
"vueVersion" : "2", |
||||
|
"h5" : { |
||||
|
"template" : "static/index.html", |
||||
|
"devServer" : { |
||||
|
"port" : 9090, |
||||
|
"https" : false |
||||
|
}, |
||||
|
"title" : "Platform-App", |
||||
|
"router" : { |
||||
|
"mode" : "hash", |
||||
|
"base" : "./" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,102 @@ |
|||||
|
{ |
||||
|
"pages": [{ |
||||
|
"path": "pages/login", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "登录" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/register", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "注册" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "移动端框架", |
||||
|
"navigationStyle": "custom" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/work/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "工作台" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "我的" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/avatar/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "修改头像" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/info/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "个人信息" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/info/edit", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "编辑资料" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/pwd/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "修改密码" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/setting/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "应用设置" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/help/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "常见问题" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/mine/about/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "关于我们" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/common/webview/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "浏览网页" |
||||
|
} |
||||
|
}, { |
||||
|
"path": "pages/common/textview/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "浏览文本" |
||||
|
} |
||||
|
}], |
||||
|
"tabBar": { |
||||
|
"color": "#000000", |
||||
|
"selectedColor": "#000000", |
||||
|
"borderStyle": "white", |
||||
|
"backgroundColor": "#ffffff", |
||||
|
"list": [{ |
||||
|
"pagePath": "pages/index", |
||||
|
"iconPath": "static/images/tabbar/home.png", |
||||
|
"selectedIconPath": "static/images/tabbar/home_.png", |
||||
|
"text": "首页" |
||||
|
}, { |
||||
|
"pagePath": "pages/work/index", |
||||
|
"iconPath": "static/images/tabbar/work.png", |
||||
|
"selectedIconPath": "static/images/tabbar/work_.png", |
||||
|
"text": "工作台" |
||||
|
}, { |
||||
|
"pagePath": "pages/mine/index", |
||||
|
"iconPath": "static/images/tabbar/mine.png", |
||||
|
"selectedIconPath": "static/images/tabbar/mine_.png", |
||||
|
"text": "我的" |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
"globalStyle": { |
||||
|
"navigationBarTextStyle": "black", |
||||
|
"navigationBarTitleText": "App", |
||||
|
"navigationBarBackgroundColor": "#FFFFFF" |
||||
|
} |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<uni-card class="view-title" :title="title"> |
||||
|
<text class="uni-body view-content">{{ content }}</text> |
||||
|
</uni-card> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
title: '', |
||||
|
content: '' |
||||
|
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
this.title = options.title |
||||
|
this.content = options.content |
||||
|
uni.setNavigationBarTitle({ |
||||
|
title: options.title |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
page { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.view-title { |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.view-content { |
||||
|
font-size: 26rpx; |
||||
|
padding: 12px 5px 0; |
||||
|
color: #333; |
||||
|
line-height: 24px; |
||||
|
font-weight: normal; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,34 @@ |
|||||
|
<template> |
||||
|
<view v-if="params.url"> |
||||
|
<web-view :webview-styles="webviewStyles" :src="`${params.url}`"></web-view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
params: {}, |
||||
|
webviewStyles: { |
||||
|
progress: { |
||||
|
color: "#FF3333" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
src: { |
||||
|
type: [String], |
||||
|
default: null |
||||
|
} |
||||
|
}, |
||||
|
onLoad(event) { |
||||
|
this.params = event |
||||
|
if (event.title) { |
||||
|
uni.setNavigationBarTitle({ |
||||
|
title: event.title |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,43 @@ |
|||||
|
<template> |
||||
|
<view class="content"> |
||||
|
<image class="logo" src="@/static/logo.png"></image> |
||||
|
<view class="text-area"> |
||||
|
<text class="title">Hello ABC</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
onLoad: function() { |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.content { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.logo { |
||||
|
height: 200rpx; |
||||
|
width: 200rpx; |
||||
|
margin-top: 200rpx; |
||||
|
margin-left: auto; |
||||
|
margin-right: auto; |
||||
|
margin-bottom: 50rpx; |
||||
|
} |
||||
|
|
||||
|
.text-area { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
font-size: 36rpx; |
||||
|
color: #8f8f94; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,167 @@ |
|||||
|
<template> |
||||
|
<view class="normal-login-container"> |
||||
|
<view class="logo-content align-center justify-center flex"> |
||||
|
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix"> |
||||
|
</image> |
||||
|
<text class="title">移动端登录</text> |
||||
|
</view> |
||||
|
<view class="login-form-content"> |
||||
|
<view class="input-item flex align-center"> |
||||
|
<view class="iconfont icon-user icon"></view> |
||||
|
<input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" /> |
||||
|
</view> |
||||
|
<view class="input-item flex align-center"> |
||||
|
<view class="iconfont icon-password icon"></view> |
||||
|
<input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" /> |
||||
|
</view> |
||||
|
<view class="action-btn"> |
||||
|
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button> |
||||
|
</view> |
||||
|
<view class="xieyi text-center"> |
||||
|
<text class="text-grey1">登录即代表同意</text> |
||||
|
<text @click="handleUserAgrement" class="text-blue">《用户协议》</text> |
||||
|
<text @click="handlePrivacy" class="text-blue">《隐私协议》</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
codeUrl: "", |
||||
|
captchaEnabled: true, |
||||
|
// 用户注册开关 |
||||
|
register: false, |
||||
|
globalConfig: getApp().globalData.config, |
||||
|
loginForm: { |
||||
|
username: "admin", |
||||
|
password: "12345678" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
// 用户注册 |
||||
|
handleUserRegister() { |
||||
|
this.$tab.redirectTo(`/pages/register`) |
||||
|
}, |
||||
|
// 隐私协议 |
||||
|
handlePrivacy() { |
||||
|
let site = this.globalConfig.appInfo.agreements[0] |
||||
|
this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`) |
||||
|
}, |
||||
|
// 用户协议 |
||||
|
handleUserAgrement() { |
||||
|
let site = this.globalConfig.appInfo.agreements[1] |
||||
|
this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`) |
||||
|
}, |
||||
|
|
||||
|
// 登录方法 |
||||
|
async handleLogin() { |
||||
|
if (this.loginForm.username === "") { |
||||
|
this.$modal.msgError("请输入您的账号") |
||||
|
} else if (this.loginForm.password === "") { |
||||
|
this.$modal.msgError("请输入您的密码") |
||||
|
}else { |
||||
|
this.$modal.loading("登录中,请耐心等待...") |
||||
|
this.pwdLogin() |
||||
|
} |
||||
|
}, |
||||
|
// 密码登录 |
||||
|
async pwdLogin() { |
||||
|
this.$store.dispatch('Login', this.loginForm).then((res) => { |
||||
|
this.$modal.closeLoading() |
||||
|
this.$tab.reLaunch('/pages/index') |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
page { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.normal-login-container { |
||||
|
width: 100%; |
||||
|
|
||||
|
.logo-content { |
||||
|
width: 100%; |
||||
|
font-size: 21px; |
||||
|
text-align: center; |
||||
|
padding-top: 15%; |
||||
|
|
||||
|
image { |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.login-form-content { |
||||
|
text-align: center; |
||||
|
margin: 20px auto; |
||||
|
margin-top: 15%; |
||||
|
width: 80%; |
||||
|
|
||||
|
.input-item { |
||||
|
margin: 20px auto; |
||||
|
background-color: #f5f6f7; |
||||
|
height: 45px; |
||||
|
border-radius: 20px; |
||||
|
|
||||
|
.icon { |
||||
|
font-size: 38rpx; |
||||
|
margin-left: 10px; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
width: 100%; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
text-align: left; |
||||
|
padding-left: 15px; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.login-btn { |
||||
|
margin-top: 40px; |
||||
|
height: 45px; |
||||
|
} |
||||
|
|
||||
|
.reg { |
||||
|
margin-top: 15px; |
||||
|
} |
||||
|
|
||||
|
.xieyi { |
||||
|
color: #333; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.login-code { |
||||
|
height: 38px; |
||||
|
float: right; |
||||
|
|
||||
|
.login-code-img { |
||||
|
height: 38px; |
||||
|
position: absolute; |
||||
|
margin-left: 10px; |
||||
|
width: 200rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
</style> |
@ -0,0 +1,75 @@ |
|||||
|
<template> |
||||
|
<view class="about-container"> |
||||
|
<view class="header-section text-center"> |
||||
|
<image style="width: 150rpx;height: 150rpx;" src="/static/logo200.png" mode="widthFix"> |
||||
|
</image> |
||||
|
<uni-title type="h2" title="移动端"></uni-title> |
||||
|
</view> |
||||
|
|
||||
|
<view class="content-section"> |
||||
|
<view class="menu-list"> |
||||
|
<view class="list-cell list-cell-arrow"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view>版本信息</view> |
||||
|
<view class="text-right">v{{version}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view>官方邮箱</view> |
||||
|
<view class="text-right">sealy321@126.com</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view>服务热线</view> |
||||
|
<view class="text-right">400-999-9999</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view>公司网站</view> |
||||
|
<view class="text-right"> |
||||
|
<uni-link :href="url" :text="url" showUnderLine="false"></uni-link> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="copyright"> |
||||
|
<view>Copyright © 2022 popsoft.tech All Rights Reserved.</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
url: getApp().globalData.config.appInfo.site_url, |
||||
|
version: getApp().globalData.config.appInfo.version |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
page { |
||||
|
background-color: #f8f8f8; |
||||
|
} |
||||
|
|
||||
|
.copyright { |
||||
|
margin-top: 50rpx; |
||||
|
text-align: center; |
||||
|
line-height: 60rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.header-section { |
||||
|
display: flex; |
||||
|
padding: 30rpx 0 0; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,631 @@ |
|||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<view class="page-body uni-content-info"> |
||||
|
<view class='cropper-content'> |
||||
|
<view v-if="isShowImg" class="uni-corpper" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'"> |
||||
|
<view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'"> |
||||
|
<image :src="imageSrc" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image> |
||||
|
<view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing" @touchend.stop="contentTouchEnd" |
||||
|
:style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'"> |
||||
|
<view class="uni-cropper-view-box"> |
||||
|
<view class="uni-cropper-dashed-h"></view> |
||||
|
<view class="uni-cropper-dashed-v"></view> |
||||
|
<view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-point point-tr" data-drag="topTight"></view> |
||||
|
<view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove" @touchend.stop="dragEnd"></view> |
||||
|
<view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view> |
||||
|
<view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view> |
||||
|
<view class="uni-cropper-point point-lt" data-drag="leftTop"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class='cropper-config'> |
||||
|
<button type="primary reverse" @click="getImage" style='margin-top: 30rpx;'> 选择头像 </button> |
||||
|
<button type="warn" @click="getImageInfo" style='margin-top: 30rpx;'> 提交 </button> |
||||
|
</view> |
||||
|
<canvas canvas-id="myCanvas" :style="'position:absolute;border: 1px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import config from '@/config' |
||||
|
import store from "@/store" |
||||
|
import { uploadAvatar } from "@/api/system/user" |
||||
|
|
||||
|
const baseUrl = config.baseUrl |
||||
|
let sysInfo = uni.getSystemInfoSync() |
||||
|
let SCREEN_WIDTH = sysInfo.screenWidth |
||||
|
let PAGE_X, // 手按下的x位置 |
||||
|
PAGE_Y, // 手按下y的位置 |
||||
|
PR = sysInfo.pixelRatio, // dpi |
||||
|
T_PAGE_X, // 手移动的时候x的位置 |
||||
|
T_PAGE_Y, // 手移动的时候Y的位置 |
||||
|
CUT_L, // 初始化拖拽元素的left值 |
||||
|
CUT_T, // 初始化拖拽元素的top值 |
||||
|
CUT_R, // 初始化拖拽元素的 |
||||
|
CUT_B, // 初始化拖拽元素的 |
||||
|
CUT_W, // 初始化拖拽元素的宽度 |
||||
|
CUT_H, // 初始化拖拽元素的高度 |
||||
|
IMG_RATIO, // 图片比例 |
||||
|
IMG_REAL_W, // 图片实际的宽度 |
||||
|
IMG_REAL_H, // 图片实际的高度 |
||||
|
DRAFG_MOVE_RATIO = 1, //移动时候的比例, |
||||
|
INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度 |
||||
|
DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度 |
||||
|
|
||||
|
export default { |
||||
|
/** |
||||
|
* 页面的初始数据 |
||||
|
*/ |
||||
|
data() { |
||||
|
return { |
||||
|
imageSrc: store.getters.avatar, |
||||
|
isShowImg: false, |
||||
|
// 初始化的宽高 |
||||
|
cropperInitW: SCREEN_WIDTH, |
||||
|
cropperInitH: SCREEN_WIDTH, |
||||
|
// 动态的宽高 |
||||
|
cropperW: SCREEN_WIDTH, |
||||
|
cropperH: SCREEN_WIDTH, |
||||
|
// 动态的left top值 |
||||
|
cropperL: 0, |
||||
|
cropperT: 0, |
||||
|
|
||||
|
transL: 0, |
||||
|
transT: 0, |
||||
|
|
||||
|
// 图片缩放值 |
||||
|
scaleP: 0, |
||||
|
imageW: 0, |
||||
|
imageH: 0, |
||||
|
|
||||
|
// 裁剪框 宽高 |
||||
|
cutL: 0, |
||||
|
cutT: 0, |
||||
|
cutB: SCREEN_WIDTH, |
||||
|
cutR: '100%', |
||||
|
qualityWidth: DRAW_IMAGE_W, |
||||
|
innerAspectRadio: DRAFG_MOVE_RATIO |
||||
|
} |
||||
|
}, |
||||
|
/** |
||||
|
* 生命周期函数--监听页面初次渲染完成 |
||||
|
*/ |
||||
|
onReady: function () { |
||||
|
this.loadImage() |
||||
|
}, |
||||
|
methods: { |
||||
|
setData: function (obj) { |
||||
|
let that = this |
||||
|
Object.keys(obj).forEach(function (key) { |
||||
|
that.$set(that.$data, key, obj[key]) |
||||
|
}) |
||||
|
}, |
||||
|
getImage: function () { |
||||
|
var _this = this |
||||
|
uni.chooseImage({ |
||||
|
success: function (res) { |
||||
|
_this.setData({ |
||||
|
imageSrc: res.tempFilePaths[0], |
||||
|
}) |
||||
|
_this.loadImage() |
||||
|
}, |
||||
|
}) |
||||
|
}, |
||||
|
loadImage: function () { |
||||
|
var _this = this |
||||
|
|
||||
|
uni.getImageInfo({ |
||||
|
src: _this.imageSrc, |
||||
|
success: function success(res) { |
||||
|
IMG_RATIO = 1 / 1 |
||||
|
if (IMG_RATIO >= 1) { |
||||
|
IMG_REAL_W = SCREEN_WIDTH |
||||
|
IMG_REAL_H = SCREEN_WIDTH / IMG_RATIO |
||||
|
} else { |
||||
|
IMG_REAL_W = SCREEN_WIDTH * IMG_RATIO |
||||
|
IMG_REAL_H = SCREEN_WIDTH |
||||
|
} |
||||
|
let minRange = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H |
||||
|
INIT_DRAG_POSITION = minRange > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : minRange |
||||
|
// 根据图片的宽高显示不同的效果 保证图片可以正常显示 |
||||
|
if (IMG_RATIO >= 1) { |
||||
|
let cutT = Math.ceil((SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - INIT_DRAG_POSITION)) / 2) |
||||
|
let cutB = cutT |
||||
|
let cutL = Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH + INIT_DRAG_POSITION) / 2) |
||||
|
let cutR = cutL |
||||
|
_this.setData({ |
||||
|
cropperW: SCREEN_WIDTH, |
||||
|
cropperH: SCREEN_WIDTH / IMG_RATIO, |
||||
|
// 初始化left right |
||||
|
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2), |
||||
|
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2), |
||||
|
cutL: cutL, |
||||
|
cutT: cutT, |
||||
|
cutR: cutR, |
||||
|
cutB: cutB, |
||||
|
// 图片缩放值 |
||||
|
imageW: IMG_REAL_W, |
||||
|
imageH: IMG_REAL_H, |
||||
|
scaleP: IMG_REAL_W / SCREEN_WIDTH, |
||||
|
qualityWidth: DRAW_IMAGE_W, |
||||
|
innerAspectRadio: IMG_RATIO |
||||
|
}) |
||||
|
} else { |
||||
|
let cutL = Math.ceil((SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO)) / 2) |
||||
|
let cutR = cutL |
||||
|
let cutT = Math.ceil((SCREEN_WIDTH - INIT_DRAG_POSITION) / 2) |
||||
|
let cutB = cutT |
||||
|
_this.setData({ |
||||
|
cropperW: SCREEN_WIDTH * IMG_RATIO, |
||||
|
cropperH: SCREEN_WIDTH, |
||||
|
// 初始化left right |
||||
|
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2), |
||||
|
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2), |
||||
|
|
||||
|
cutL: cutL, |
||||
|
cutT: cutT, |
||||
|
cutR: cutR, |
||||
|
cutB: cutB, |
||||
|
// 图片缩放值 |
||||
|
imageW: IMG_REAL_W, |
||||
|
imageH: IMG_REAL_H, |
||||
|
scaleP: IMG_REAL_W / SCREEN_WIDTH, |
||||
|
qualityWidth: DRAW_IMAGE_W, |
||||
|
innerAspectRadio: IMG_RATIO |
||||
|
}) |
||||
|
} |
||||
|
_this.setData({ |
||||
|
isShowImg: true |
||||
|
}) |
||||
|
uni.hideLoading() |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
// 拖动时候触发的touchStart事件 |
||||
|
contentStartMove(e) { |
||||
|
PAGE_X = e.touches[0].pageX |
||||
|
PAGE_Y = e.touches[0].pageY |
||||
|
}, |
||||
|
|
||||
|
// 拖动时候触发的touchMove事件 |
||||
|
contentMoveing(e) { |
||||
|
var _this = this |
||||
|
var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO |
||||
|
var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO |
||||
|
// 左移 |
||||
|
if (dragLengthX > 0) { |
||||
|
if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL |
||||
|
} else { |
||||
|
if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR |
||||
|
} |
||||
|
|
||||
|
if (dragLengthY > 0) { |
||||
|
if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT |
||||
|
} else { |
||||
|
if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB |
||||
|
} |
||||
|
this.setData({ |
||||
|
cutL: this.cutL - dragLengthX, |
||||
|
cutT: this.cutT - dragLengthY, |
||||
|
cutR: this.cutR + dragLengthX, |
||||
|
cutB: this.cutB + dragLengthY |
||||
|
}) |
||||
|
|
||||
|
PAGE_X = e.touches[0].pageX |
||||
|
PAGE_Y = e.touches[0].pageY |
||||
|
}, |
||||
|
|
||||
|
contentTouchEnd() { |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
// 获取图片 |
||||
|
getImageInfo() { |
||||
|
var _this = this |
||||
|
uni.showLoading({ |
||||
|
title: '图片生成中...', |
||||
|
}) |
||||
|
// 将图片写入画布 |
||||
|
const ctx = uni.createCanvasContext('myCanvas') |
||||
|
ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H) |
||||
|
ctx.draw(true, () => { |
||||
|
// 获取画布要裁剪的位置和宽度 均为百分比 * 画布中图片的宽度 保证了在微信小程序中裁剪的图片模糊 位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio) |
||||
|
var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W |
||||
|
var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H |
||||
|
var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W |
||||
|
var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H |
||||
|
uni.canvasToTempFilePath({ |
||||
|
x: canvasL, |
||||
|
y: canvasT, |
||||
|
width: canvasW, |
||||
|
height: canvasH, |
||||
|
destWidth: canvasW, |
||||
|
destHeight: canvasH, |
||||
|
quality: 0.5, |
||||
|
canvasId: 'myCanvas', |
||||
|
success: function (res) { |
||||
|
uni.hideLoading() |
||||
|
let data = {name: 'avatarfile', filePath: res.tempFilePath} |
||||
|
uploadAvatar(data).then(response => { |
||||
|
store.commit('SET_AVATAR', baseUrl + response.imgUrl) |
||||
|
uni.showToast({ title: "修改成功", icon: 'success' }) |
||||
|
uni.navigateBack() |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
// 设置大小的时候触发的touchStart事件 |
||||
|
dragStart(e) { |
||||
|
T_PAGE_X = e.touches[0].pageX |
||||
|
T_PAGE_Y = e.touches[0].pageY |
||||
|
CUT_L = this.cutL |
||||
|
CUT_R = this.cutR |
||||
|
CUT_B = this.cutB |
||||
|
CUT_T = this.cutT |
||||
|
}, |
||||
|
|
||||
|
// 设置大小的时候触发的touchMove事件 |
||||
|
dragMove(e) { |
||||
|
var _this = this |
||||
|
var dragType = e.target.dataset.drag |
||||
|
switch (dragType) { |
||||
|
case 'right': |
||||
|
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO |
||||
|
if (CUT_R + dragLength < 0) dragLength = -CUT_R |
||||
|
this.setData({ |
||||
|
cutR: CUT_R + dragLength |
||||
|
}) |
||||
|
break |
||||
|
case 'left': |
||||
|
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO |
||||
|
if (CUT_L - dragLength < 0) dragLength = CUT_L |
||||
|
if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR) |
||||
|
this.setData({ |
||||
|
cutL: CUT_L - dragLength |
||||
|
}) |
||||
|
break |
||||
|
case 'top': |
||||
|
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO |
||||
|
if (CUT_T - dragLength < 0) dragLength = CUT_T |
||||
|
if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB) |
||||
|
this.setData({ |
||||
|
cutT: CUT_T - dragLength |
||||
|
}) |
||||
|
break |
||||
|
case 'bottom': |
||||
|
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO |
||||
|
if (CUT_B + dragLength < 0) dragLength = -CUT_B |
||||
|
this.setData({ |
||||
|
cutB: CUT_B + dragLength |
||||
|
}) |
||||
|
break |
||||
|
case 'rightBottom': |
||||
|
var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO |
||||
|
var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO |
||||
|
|
||||
|
if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B |
||||
|
if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R |
||||
|
let cutB = CUT_B + dragLengthY |
||||
|
let cutR = CUT_R + dragLengthX |
||||
|
|
||||
|
this.setData({ |
||||
|
cutB: cutB, |
||||
|
cutR: cutR |
||||
|
}) |
||||
|
break |
||||
|
default: |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
/* pages/uni-cropper/index.wxss */ |
||||
|
|
||||
|
.uni-content-info { |
||||
|
/* position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
display: block; |
||||
|
align-items: center; |
||||
|
flex-direction: column; */ |
||||
|
} |
||||
|
|
||||
|
.cropper-config { |
||||
|
padding: 20rpx 40rpx; |
||||
|
} |
||||
|
|
||||
|
.cropper-content { |
||||
|
min-height: 750rpx; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.uni-corpper { |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
-webkit-user-select: none; |
||||
|
-moz-user-select: none; |
||||
|
-ms-user-select: none; |
||||
|
user-select: none; |
||||
|
-webkit-tap-highlight-color: transparent; |
||||
|
-webkit-touch-callout: none; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
.uni-corpper-content { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.uni-corpper-content image { |
||||
|
display: block; |
||||
|
width: 100%; |
||||
|
min-width: 0 !important; |
||||
|
max-width: none !important; |
||||
|
height: 100%; |
||||
|
min-height: 0 !important; |
||||
|
max-height: none !important; |
||||
|
image-orientation: 0deg !important; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
/* 移动图片效果 */ |
||||
|
|
||||
|
.uni-cropper-drag-box { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
cursor: move; |
||||
|
background: rgba(0, 0, 0, 0.6); |
||||
|
z-index: 1; |
||||
|
} |
||||
|
/* 内部的信息 */ |
||||
|
|
||||
|
.uni-corpper-crop-box { |
||||
|
position: absolute; |
||||
|
background: rgba(255, 255, 255, 0.3); |
||||
|
z-index: 2; |
||||
|
} |
||||
|
|
||||
|
.uni-corpper-crop-box .uni-cropper-view-box { |
||||
|
position: relative; |
||||
|
display: block; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
overflow: visible; |
||||
|
outline: 1rpx solid #69f; |
||||
|
outline-color: rgba(102, 153, 255, .75) |
||||
|
} |
||||
|
/* 横向虚线 */ |
||||
|
|
||||
|
.uni-cropper-dashed-h { |
||||
|
position: absolute; |
||||
|
top: 33.33333333%; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 33.33333333%; |
||||
|
border-top: 1rpx dashed rgba(255, 255, 255, 0.5); |
||||
|
border-bottom: 1rpx dashed rgba(255, 255, 255, 0.5); |
||||
|
} |
||||
|
/* 纵向虚线 */ |
||||
|
|
||||
|
.uni-cropper-dashed-v { |
||||
|
position: absolute; |
||||
|
left: 33.33333333%; |
||||
|
top: 0; |
||||
|
width: 33.33333333%; |
||||
|
height: 100%; |
||||
|
border-left: 1rpx dashed rgba(255, 255, 255, 0.5); |
||||
|
border-right: 1rpx dashed rgba(255, 255, 255, 0.5); |
||||
|
} |
||||
|
/* 四个方向的线 为了之后的拖动事件*/ |
||||
|
|
||||
|
.uni-cropper-line-t { |
||||
|
position: absolute; |
||||
|
display: block; |
||||
|
width: 100%; |
||||
|
background-color: #69f; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
height: 1rpx; |
||||
|
opacity: 0.1; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-line-t::before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
right: 0rpx; |
||||
|
width: 100%; |
||||
|
-webkit-transform: translate3d(0, -50%, 0); |
||||
|
transform: translate3d(0, -50%, 0); |
||||
|
bottom: 0; |
||||
|
height: 41rpx; |
||||
|
background: transparent; |
||||
|
z-index: 11; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-line-r { |
||||
|
position: absolute; |
||||
|
display: block; |
||||
|
background-color: #69f; |
||||
|
top: 0; |
||||
|
right: 0rpx; |
||||
|
width: 1rpx; |
||||
|
opacity: 0.1; |
||||
|
height: 100%; |
||||
|
cursor: e-resize; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-line-r::before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 50%; |
||||
|
width: 41rpx; |
||||
|
-webkit-transform: translate3d(-50%, 0, 0); |
||||
|
transform: translate3d(-50%, 0, 0); |
||||
|
bottom: 0; |
||||
|
height: 100%; |
||||
|
background: transparent; |
||||
|
z-index: 11; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-line-b { |
||||
|
position: absolute; |
||||
|
display: block; |
||||
|
width: 100%; |
||||
|
background-color: #69f; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
height: 1rpx; |
||||
|
opacity: 0.1; |
||||
|
cursor: s-resize; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-line-b::before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
right: 0rpx; |
||||
|
width: 100%; |
||||
|
-webkit-transform: translate3d(0, -50%, 0); |
||||
|
transform: translate3d(0, -50%, 0); |
||||
|
bottom: 0; |
||||
|
height: 41rpx; |
||||
|
background: transparent; |
||||
|
z-index: 11; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-line-l { |
||||
|
position: absolute; |
||||
|
display: block; |
||||
|
background-color: #69f; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 1rpx; |
||||
|
opacity: 0.1; |
||||
|
height: 100%; |
||||
|
cursor: w-resize; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-line-l::before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 50%; |
||||
|
width: 41rpx; |
||||
|
-webkit-transform: translate3d(-50%, 0, 0); |
||||
|
transform: translate3d(-50%, 0, 0); |
||||
|
bottom: 0; |
||||
|
height: 100%; |
||||
|
background: transparent; |
||||
|
z-index: 11; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-point { |
||||
|
width: 5rpx; |
||||
|
height: 5rpx; |
||||
|
background-color: #69f; |
||||
|
opacity: .75; |
||||
|
position: absolute; |
||||
|
z-index: 3; |
||||
|
} |
||||
|
|
||||
|
.point-t { |
||||
|
top: -3rpx; |
||||
|
left: 50%; |
||||
|
margin-left: -3rpx; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
|
||||
|
.point-tr { |
||||
|
top: -3rpx; |
||||
|
left: 100%; |
||||
|
margin-left: -3rpx; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
|
||||
|
.point-r { |
||||
|
top: 50%; |
||||
|
left: 100%; |
||||
|
margin-left: -3rpx; |
||||
|
margin-top: -3rpx; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
|
||||
|
.point-rb { |
||||
|
left: 100%; |
||||
|
top: 100%; |
||||
|
-webkit-transform: translate3d(-50%, -50%, 0); |
||||
|
transform: translate3d(-50%, -50%, 0); |
||||
|
cursor: n-resize; |
||||
|
width: 36rpx; |
||||
|
height: 36rpx; |
||||
|
background-color: #69f; |
||||
|
position: absolute; |
||||
|
z-index: 1112; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
.point-b { |
||||
|
left: 50%; |
||||
|
top: 100%; |
||||
|
margin-left: -3rpx; |
||||
|
margin-top: -3rpx; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
|
||||
|
.point-bl { |
||||
|
left: 0%; |
||||
|
top: 100%; |
||||
|
margin-left: -3rpx; |
||||
|
margin-top: -3rpx; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
|
||||
|
.point-l { |
||||
|
left: 0%; |
||||
|
top: 50%; |
||||
|
margin-left: -3rpx; |
||||
|
margin-top: -3rpx; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
|
||||
|
.point-lt { |
||||
|
left: 0%; |
||||
|
top: 0%; |
||||
|
margin-left: -3rpx; |
||||
|
margin-top: -3rpx; |
||||
|
cursor: n-resize; |
||||
|
} |
||||
|
/* 裁剪框预览内容 */ |
||||
|
|
||||
|
.uni-cropper-viewer { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.uni-cropper-viewer image { |
||||
|
position: absolute; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,112 @@ |
|||||
|
<template> |
||||
|
<view class="help-container"> |
||||
|
<view v-for="(item, findex) in list" :key="findex" :title="item.title" class="list-title"> |
||||
|
<view class="text-title"> |
||||
|
<view :class="item.icon"></view>{{ item.title }} |
||||
|
</view> |
||||
|
<view class="childList"> |
||||
|
<view v-for="(child, zindex) in item.childList" :key="zindex" class="question" hover-class="hover" |
||||
|
@click="handleText(child)"> |
||||
|
<view class="text-item">{{ child.title }}</view> |
||||
|
<view class="line" v-if="zindex !== item.childList.length - 1"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
list: [{ |
||||
|
icon: 'iconfont icon-github', |
||||
|
title: '问题', |
||||
|
childList: [{ |
||||
|
title: '开源吗?', |
||||
|
content: '开源' |
||||
|
}, { |
||||
|
title: '可以商用吗?', |
||||
|
content: '可以' |
||||
|
}, { |
||||
|
title: '官网地址多少?', |
||||
|
content: 'http://popsoft.tech' |
||||
|
}, { |
||||
|
title: '文档地址多少?', |
||||
|
content: 'http://popsoft.tech' |
||||
|
}] |
||||
|
}, |
||||
|
{ |
||||
|
icon: 'iconfont icon-help', |
||||
|
title: '其他问题', |
||||
|
childList: [{ |
||||
|
title: '如何退出登录?', |
||||
|
content: '请点击[我的] - [应用设置] - [退出登录]即可退出登录', |
||||
|
}, { |
||||
|
title: '如何修改用户头像?', |
||||
|
content: '请点击[我的] - [选择头像] - [点击提交]即可更换用户头像', |
||||
|
}, { |
||||
|
title: '如何修改登录密码?', |
||||
|
content: '请点击[我的] - [应用设置] - [修改密码]即可修改登录密码', |
||||
|
}] |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
handleText(item) { |
||||
|
this.$tab.navigateTo(`/pages/common/textview/index?title=${item.title}&content=${item.content}`) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
page { |
||||
|
background-color: #f8f8f8; |
||||
|
} |
||||
|
|
||||
|
.help-container { |
||||
|
margin-bottom: 100rpx; |
||||
|
padding: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.list-title { |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.childList { |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2); |
||||
|
border-radius: 16rpx; |
||||
|
margin-top: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.line { |
||||
|
width: 100%; |
||||
|
height: 1rpx; |
||||
|
background-color: #F5F5F5; |
||||
|
} |
||||
|
|
||||
|
.text-title { |
||||
|
color: #303133; |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
margin-left: 10rpx; |
||||
|
|
||||
|
.iconfont { |
||||
|
font-size: 16px; |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.text-item { |
||||
|
font-size: 28rpx; |
||||
|
padding: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.question { |
||||
|
color: #606266; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,195 @@ |
|||||
|
<template> |
||||
|
<view class="mine-container" :style="{height: `${windowHeight}px`}"> |
||||
|
<!--顶部个人信息栏--> |
||||
|
<view class="header-section"> |
||||
|
<view class="flex padding justify-between"> |
||||
|
<view class="flex align-center"> |
||||
|
<view v-if="!avatar" class="cu-avatar xl round bg-white"> |
||||
|
<view class="iconfont icon-people text-gray icon"></view> |
||||
|
</view> |
||||
|
<image v-if="avatar" @click="handleToAvatar" :src="avatar" class="cu-avatar xl round" mode="widthFix"> |
||||
|
</image> |
||||
|
<view v-if="!name" @click="handleToLogin" class="login-tip"> |
||||
|
点击登录 |
||||
|
</view> |
||||
|
<view v-if="name" @click="handleToInfo" class="user-info"> |
||||
|
<view class="u_title"> |
||||
|
{{ name }} |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="content-section"> |
||||
|
<view class="mine-actions grid col-4 text-center"> |
||||
|
<view class="action-item" @click="handleJiaoLiuQun"> |
||||
|
<view class="iconfont icon-friendfill text-pink icon"></view> |
||||
|
<text class="text">交流群</text> |
||||
|
</view> |
||||
|
<view class="action-item" @click="handleBuilding"> |
||||
|
<view class="iconfont icon-service text-blue icon"></view> |
||||
|
<text class="text">在线客服</text> |
||||
|
</view> |
||||
|
<view class="action-item" @click="handleBuilding"> |
||||
|
<view class="iconfont icon-community text-mauve icon"></view> |
||||
|
<text class="text">反馈社区</text> |
||||
|
</view> |
||||
|
<view class="action-item" @click="handleBuilding"> |
||||
|
<view class="iconfont icon-dianzan text-green icon"></view> |
||||
|
<text class="text">点赞我们</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="menu-list"> |
||||
|
<view class="list-cell list-cell-arrow" @click="handleToEditInfo"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view class="iconfont icon-user menu-icon"></view> |
||||
|
<view>编辑资料</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow" @click="handleHelp"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view class="iconfont icon-help menu-icon"></view> |
||||
|
<view>常见问题</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow" @click="handleAbout"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view class="iconfont icon-aixin menu-icon"></view> |
||||
|
<view>关于我们</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow" @click="handleToSetting"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view class="iconfont icon-setting menu-icon"></view> |
||||
|
<view>应用设置</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import storage from '@/utils/storage' |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
name: this.$store.state.user.name, |
||||
|
version: getApp().globalData.config.appInfo.version |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
avatar() { |
||||
|
return this.$store.state.user.avatar |
||||
|
}, |
||||
|
windowHeight() { |
||||
|
return uni.getSystemInfoSync().windowHeight - 50 |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
handleToInfo() { |
||||
|
this.$tab.navigateTo('/pages/mine/info/index') |
||||
|
}, |
||||
|
handleToEditInfo() { |
||||
|
this.$tab.navigateTo('/pages/mine/info/edit') |
||||
|
}, |
||||
|
handleToSetting() { |
||||
|
this.$tab.navigateTo('/pages/mine/setting/index') |
||||
|
}, |
||||
|
handleToLogin() { |
||||
|
this.$tab.reLaunch('/pages/login') |
||||
|
}, |
||||
|
handleToAvatar() { |
||||
|
this.$tab.navigateTo('/pages/mine/avatar/index') |
||||
|
}, |
||||
|
handleLogout() { |
||||
|
this.$modal.confirm('确定注销并退出系统吗?').then(() => { |
||||
|
this.$store.dispatch('LogOut').then(() => { |
||||
|
this.$tab.reLaunch('/pages/index') |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
handleHelp() { |
||||
|
this.$tab.navigateTo('/pages/mine/help/index') |
||||
|
}, |
||||
|
handleAbout() { |
||||
|
this.$tab.navigateTo('/pages/mine/about/index') |
||||
|
}, |
||||
|
handleJiaoLiuQun() { |
||||
|
this.$modal.showToast('模块建设中~') |
||||
|
}, |
||||
|
handleBuilding() { |
||||
|
this.$modal.showToast('模块建设中~') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
page { |
||||
|
background-color: #f5f6f7; |
||||
|
} |
||||
|
|
||||
|
.mine-container { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
|
||||
|
|
||||
|
.header-section { |
||||
|
padding: 15px 15px 45px 15px; |
||||
|
background-color: #3c96f3; |
||||
|
color: white; |
||||
|
|
||||
|
.login-tip { |
||||
|
font-size: 18px; |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
|
||||
|
.cu-avatar { |
||||
|
border: 2px solid #eaeaea; |
||||
|
|
||||
|
.icon { |
||||
|
font-size: 40px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.user-info { |
||||
|
margin-left: 15px; |
||||
|
|
||||
|
.u_title { |
||||
|
font-size: 18px; |
||||
|
line-height: 30px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.content-section { |
||||
|
position: relative; |
||||
|
top: -50px; |
||||
|
|
||||
|
.mine-actions { |
||||
|
margin: 15px 15px; |
||||
|
padding: 20px 0px; |
||||
|
border-radius: 8px; |
||||
|
background-color: white; |
||||
|
|
||||
|
.action-item { |
||||
|
.icon { |
||||
|
font-size: 28px; |
||||
|
} |
||||
|
|
||||
|
.text { |
||||
|
display: block; |
||||
|
font-size: 13px; |
||||
|
margin: 8px 0px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,127 @@ |
|||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<view class="example"> |
||||
|
<uni-forms ref="form" :model="user" labelWidth="80px"> |
||||
|
<uni-forms-item label="用户昵称" name="nickName"> |
||||
|
<uni-easyinput v-model="user.nickName" placeholder="请输入昵称" /> |
||||
|
</uni-forms-item> |
||||
|
<uni-forms-item label="手机号码" name="phonenumber"> |
||||
|
<uni-easyinput v-model="user.phonenumber" placeholder="请输入手机号码" /> |
||||
|
</uni-forms-item> |
||||
|
<uni-forms-item label="邮箱" name="email"> |
||||
|
<uni-easyinput v-model="user.email" placeholder="请输入邮箱" /> |
||||
|
</uni-forms-item> |
||||
|
<uni-forms-item label="性别" name="sex" required> |
||||
|
<uni-data-checkbox v-model="user.sex" :localdata="sexs" /> |
||||
|
</uni-forms-item> |
||||
|
</uni-forms> |
||||
|
<button type="primary" @click="submit">提交</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getUserProfile } from "@/api/system/user" |
||||
|
import { updateUserProfile } from "@/api/system/user" |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
user: { |
||||
|
nickName: "", |
||||
|
phonenumber: "", |
||||
|
email: "", |
||||
|
sex: "" |
||||
|
}, |
||||
|
sexs: [{ |
||||
|
text: '男', |
||||
|
value: "0" |
||||
|
}, { |
||||
|
text: '女', |
||||
|
value: "1" |
||||
|
}], |
||||
|
rules: { |
||||
|
nickName: { |
||||
|
rules: [{ |
||||
|
required: true, |
||||
|
errorMessage: '用户昵称不能为空' |
||||
|
}] |
||||
|
}, |
||||
|
phonenumber: { |
||||
|
rules: [{ |
||||
|
required: true, |
||||
|
errorMessage: '手机号码不能为空' |
||||
|
}, { |
||||
|
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, |
||||
|
errorMessage: '请输入正确的手机号码' |
||||
|
}] |
||||
|
}, |
||||
|
email: { |
||||
|
rules: [{ |
||||
|
required: true, |
||||
|
errorMessage: '邮箱地址不能为空' |
||||
|
}, { |
||||
|
format: 'email', |
||||
|
errorMessage: '请输入正确的邮箱地址' |
||||
|
}] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
this.getUser() |
||||
|
}, |
||||
|
onReady() { |
||||
|
this.$refs.form.setRules(this.rules) |
||||
|
}, |
||||
|
methods: { |
||||
|
getUser() { |
||||
|
getUserProfile().then(response => { |
||||
|
this.user = response.data |
||||
|
}) |
||||
|
}, |
||||
|
submit(ref) { |
||||
|
this.$refs.form.validate().then(res => { |
||||
|
updateUserProfile(this.user).then(response => { |
||||
|
this.$modal.msgSuccess("修改成功") |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
page { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.example { |
||||
|
padding: 15px; |
||||
|
background-color: #fff; |
||||
|
} |
||||
|
|
||||
|
.segmented-control { |
||||
|
margin-bottom: 15px; |
||||
|
} |
||||
|
|
||||
|
.button-group { |
||||
|
margin-top: 15px; |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
} |
||||
|
|
||||
|
.form-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.button { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 35px; |
||||
|
line-height: 35px; |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,44 @@ |
|||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<uni-list> |
||||
|
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'person-filled'}" title="昵称" :rightText="user.nickName" /> |
||||
|
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'phone-filled'}" title="手机号码" :rightText="user.phonenumber" /> |
||||
|
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'email-filled'}" title="邮箱" :rightText="user.email" /> |
||||
|
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'auth-filled'}" title="岗位" :rightText="postGroup" /> |
||||
|
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'staff-filled'}" title="角色" :rightText="roleGroup" /> |
||||
|
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'calendar-filled'}" title="创建日期" :rightText="user.createTime" /> |
||||
|
</uni-list> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getUserProfile } from "@/api/system/user" |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
user: {}, |
||||
|
roleGroup: "", |
||||
|
postGroup: "" |
||||
|
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
this.getUser() |
||||
|
}, |
||||
|
methods: { |
||||
|
getUser() { |
||||
|
getUserProfile().then(response => { |
||||
|
this.user = response.data |
||||
|
this.roleGroup = response.roleGroup |
||||
|
this.postGroup = response.postGroup |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
page { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,85 @@ |
|||||
|
<template> |
||||
|
<view class="pwd-retrieve-container"> |
||||
|
<uni-forms ref="form" :value="user" labelWidth="80px"> |
||||
|
<uni-forms-item name="oldPassword" label="旧密码"> |
||||
|
<uni-easyinput type="password" v-model="user.oldPassword" placeholder="请输入旧密码" /> |
||||
|
</uni-forms-item> |
||||
|
<uni-forms-item name="newPassword" label="新密码"> |
||||
|
<uni-easyinput type="password" v-model="user.newPassword" placeholder="请输入新密码" /> |
||||
|
</uni-forms-item> |
||||
|
<uni-forms-item name="confirmPassword" label="确认密码"> |
||||
|
<uni-easyinput type="password" v-model="user.confirmPassword" placeholder="请确认新密码" /> |
||||
|
</uni-forms-item> |
||||
|
<button type="primary" @click="submit">提交</button> |
||||
|
</uni-forms> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { updateUserPwd } from "@/api/system/user" |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
user: { |
||||
|
oldPassword: undefined, |
||||
|
newPassword: undefined, |
||||
|
confirmPassword: undefined |
||||
|
}, |
||||
|
rules: { |
||||
|
oldPassword: { |
||||
|
rules: [{ |
||||
|
required: true, |
||||
|
errorMessage: '旧密码不能为空' |
||||
|
}] |
||||
|
}, |
||||
|
newPassword: { |
||||
|
rules: [{ |
||||
|
required: true, |
||||
|
errorMessage: '新密码不能为空', |
||||
|
}, |
||||
|
{ |
||||
|
minLength: 6, |
||||
|
maxLength: 20, |
||||
|
errorMessage: '长度在 6 到 20 个字符' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
confirmPassword: { |
||||
|
rules: [{ |
||||
|
required: true, |
||||
|
errorMessage: '确认密码不能为空' |
||||
|
}, { |
||||
|
validateFunction: (rule, value, data) => data.newPassword === value, |
||||
|
errorMessage: '两次输入的密码不一致' |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
onReady() { |
||||
|
this.$refs.form.setRules(this.rules) |
||||
|
}, |
||||
|
methods: { |
||||
|
submit() { |
||||
|
this.$refs.form.validate().then(res => { |
||||
|
updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => { |
||||
|
this.$modal.msgSuccess("修改成功") |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
page { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.pwd-retrieve-container { |
||||
|
padding-top: 36rpx; |
||||
|
padding: 15px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,78 @@ |
|||||
|
<template> |
||||
|
<view class="setting-container" :style="{height: `${windowHeight}px`}"> |
||||
|
<view class="menu-list"> |
||||
|
<view class="list-cell list-cell-arrow" @click="handleToPwd"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view class="iconfont icon-password menu-icon"></view> |
||||
|
<view>修改密码</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow" @click="handleToUpgrade"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view class="iconfont icon-refresh menu-icon"></view> |
||||
|
<view>检查更新</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="list-cell list-cell-arrow" @click="handleCleanTmp"> |
||||
|
<view class="menu-item-box"> |
||||
|
<view class="iconfont icon-clean menu-icon"></view> |
||||
|
<view>清理缓存</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="cu-list menu"> |
||||
|
<view class="cu-item item-box"> |
||||
|
<view class="content text-center" @click="handleLogout"> |
||||
|
<text class="text-black">退出登录</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
windowHeight: uni.getSystemInfoSync().windowHeight |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
handleToPwd() { |
||||
|
this.$tab.navigateTo('/pages/mine/pwd/index') |
||||
|
}, |
||||
|
handleToUpgrade() { |
||||
|
this.$modal.showToast('模块建设中~') |
||||
|
}, |
||||
|
handleCleanTmp() { |
||||
|
this.$modal.showToast('模块建设中~') |
||||
|
}, |
||||
|
handleLogout() { |
||||
|
this.$modal.confirm('确定注销并退出系统吗?').then(() => { |
||||
|
this.$store.dispatch('LogOut').then(() => { |
||||
|
this.$tab.reLaunch('/pages/index') |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.page { |
||||
|
background-color: #f8f8f8; |
||||
|
} |
||||
|
|
||||
|
.item-box { |
||||
|
background-color: #FFFFFF; |
||||
|
margin: 30rpx; |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding: 10rpx; |
||||
|
border-radius: 8rpx; |
||||
|
color: #303133; |
||||
|
font-size: 32rpx; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,196 @@ |
|||||
|
<template> |
||||
|
<view class="normal-login-container"> |
||||
|
<view class="logo-content align-center justify-center flex"> |
||||
|
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix"> |
||||
|
</image> |
||||
|
<text class="title">移动端注册</text> |
||||
|
</view> |
||||
|
<view class="login-form-content"> |
||||
|
<view class="input-item flex align-center"> |
||||
|
<view class="iconfont icon-user icon"></view> |
||||
|
<input v-model="registerForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" /> |
||||
|
</view> |
||||
|
<view class="input-item flex align-center"> |
||||
|
<view class="iconfont icon-password icon"></view> |
||||
|
<input v-model="registerForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" /> |
||||
|
</view> |
||||
|
<view class="input-item flex align-center"> |
||||
|
<view class="iconfont icon-password icon"></view> |
||||
|
<input v-model="registerForm.confirmPassword" type="password" class="input" placeholder="请输入重复密码" maxlength="20" /> |
||||
|
</view> |
||||
|
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled"> |
||||
|
<view class="iconfont icon-code icon"></view> |
||||
|
<input v-model="registerForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" /> |
||||
|
<view class="login-code"> |
||||
|
<image :src="codeUrl" @click="getCode" class="login-code-img"></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="action-btn"> |
||||
|
<button @click="handleRegister()" class="register-btn cu-btn block bg-blue lg round">注册</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="xieyi text-center"> |
||||
|
<text @click="handleUserLogin" class="text-blue">使用已有账号登录</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getCodeImg, register } from '@/api/login' |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
codeUrl: "", |
||||
|
captchaEnabled: true, |
||||
|
globalConfig: getApp().globalData.config, |
||||
|
registerForm: { |
||||
|
username: "", |
||||
|
password: "", |
||||
|
confirmPassword: "", |
||||
|
code: "", |
||||
|
uuid: '' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
this.getCode() |
||||
|
}, |
||||
|
methods: { |
||||
|
// 用户登录 |
||||
|
handleUserLogin() { |
||||
|
this.$tab.navigateTo(`/pages/login`) |
||||
|
}, |
||||
|
// 获取图形验证码 |
||||
|
getCode() { |
||||
|
getCodeImg().then(res => { |
||||
|
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled |
||||
|
if (this.captchaEnabled) { |
||||
|
this.codeUrl = 'data:image/gif;base64,' + res.img |
||||
|
this.registerForm.uuid = res.uuid |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
// 注册方法 |
||||
|
async handleRegister() { |
||||
|
if (this.registerForm.username === "") { |
||||
|
this.$modal.msgError("请输入您的账号") |
||||
|
} else if (this.registerForm.password === "") { |
||||
|
this.$modal.msgError("请输入您的密码") |
||||
|
} else if (this.registerForm.confirmPassword === "") { |
||||
|
this.$modal.msgError("请再次输入您的密码") |
||||
|
} else if (this.registerForm.password !== this.registerForm.confirmPassword) { |
||||
|
this.$modal.msgError("两次输入的密码不一致") |
||||
|
} else if (this.registerForm.code === "" && this.captchaEnabled) { |
||||
|
this.$modal.msgError("请输入验证码") |
||||
|
} else { |
||||
|
this.$modal.loading("注册中,请耐心等待...") |
||||
|
this.register() |
||||
|
} |
||||
|
}, |
||||
|
// 用户注册 |
||||
|
async register() { |
||||
|
register(this.registerForm).then(res => { |
||||
|
this.$modal.closeLoading() |
||||
|
uni.showModal({ |
||||
|
title: "系统提示", |
||||
|
content: "恭喜你,您的账号 " + this.registerForm.username + " 注册成功!", |
||||
|
success: function (res) { |
||||
|
if (res.confirm) { |
||||
|
uni.redirectTo({ url: `/pages/login` }); |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}).catch(() => { |
||||
|
if (this.captchaEnabled) { |
||||
|
this.getCode() |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
// 注册成功后,处理函数 |
||||
|
registerSuccess(result) { |
||||
|
// 设置用户信息 |
||||
|
this.$store.dispatch('GetInfo').then(res => { |
||||
|
this.$tab.reLaunch('/pages/index') |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
page { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.normal-login-container { |
||||
|
width: 100%; |
||||
|
|
||||
|
.logo-content { |
||||
|
width: 100%; |
||||
|
font-size: 21px; |
||||
|
text-align: center; |
||||
|
padding-top: 15%; |
||||
|
|
||||
|
image { |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.login-form-content { |
||||
|
text-align: center; |
||||
|
margin: 20px auto; |
||||
|
margin-top: 15%; |
||||
|
width: 80%; |
||||
|
|
||||
|
.input-item { |
||||
|
margin: 20px auto; |
||||
|
background-color: #f5f6f7; |
||||
|
height: 45px; |
||||
|
border-radius: 20px; |
||||
|
|
||||
|
.icon { |
||||
|
font-size: 38rpx; |
||||
|
margin-left: 10px; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
width: 100%; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
text-align: left; |
||||
|
padding-left: 15px; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.register-btn { |
||||
|
margin-top: 40px; |
||||
|
height: 45px; |
||||
|
} |
||||
|
|
||||
|
.xieyi { |
||||
|
color: #333; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.login-code { |
||||
|
height: 38px; |
||||
|
float: right; |
||||
|
|
||||
|
.login-code-img { |
||||
|
height: 38px; |
||||
|
position: absolute; |
||||
|
margin-left: 10px; |
||||
|
width: 200rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
</style> |
@ -0,0 +1,181 @@ |
|||||
|
<template> |
||||
|
<view class="work-container"> |
||||
|
<!-- 轮播图 --> |
||||
|
<uni-swiper-dot class="uni-swiper-dot-box" :info="data" :current="current" field="content"> |
||||
|
<swiper class="swiper-box" :current="swiperDotIndex" @change="changeSwiper"> |
||||
|
<swiper-item v-for="(item, index) in data" :key="index"> |
||||
|
<view class="swiper-item" @click="clickBannerItem(item)"> |
||||
|
<image :src="item.image" mode="aspectFill" :draggable="false" /> |
||||
|
</view> |
||||
|
</swiper-item> |
||||
|
</swiper> |
||||
|
</uni-swiper-dot> |
||||
|
|
||||
|
<!-- 宫格组件 --> |
||||
|
<uni-section title="系统管理" type="line"></uni-section> |
||||
|
<view class="grid-body"> |
||||
|
<uni-grid :column="4" :showBorder="false" @change="changeGrid"> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="person-filled" size="30"></uni-icons> |
||||
|
<text class="text">用户管理</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="staff-filled" size="30"></uni-icons> |
||||
|
<text class="text">角色管理</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="color" size="30"></uni-icons> |
||||
|
<text class="text">菜单管理</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="settings-filled" size="30"></uni-icons> |
||||
|
<text class="text">部门管理</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="heart-filled" size="30"></uni-icons> |
||||
|
<text class="text">岗位管理</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="bars" size="30"></uni-icons> |
||||
|
<text class="text">字典管理</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="gear-filled" size="30"></uni-icons> |
||||
|
<text class="text">参数设置</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="chat-filled" size="30"></uni-icons> |
||||
|
<text class="text">通知公告</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
<uni-grid-item> |
||||
|
<view class="grid-item-box"> |
||||
|
<uni-icons type="wallet-filled" size="30"></uni-icons> |
||||
|
<text class="text">日志管理</text> |
||||
|
</view> |
||||
|
</uni-grid-item> |
||||
|
</uni-grid> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
current: 0, |
||||
|
swiperDotIndex: 0, |
||||
|
data: [ |
||||
|
{ |
||||
|
image: '/static/images/banner/banner02.jpg' |
||||
|
}, |
||||
|
{ |
||||
|
image: '/static/images/banner/banner03.jpg' |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
clickBannerItem(item) { |
||||
|
console.info(item) |
||||
|
}, |
||||
|
changeSwiper(e) { |
||||
|
this.current = e.detail.current |
||||
|
}, |
||||
|
changeGrid(e) { |
||||
|
this.$modal.showToast('模块建设中~') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
page { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
box-sizing: border-box; |
||||
|
background-color: #fff; |
||||
|
min-height: 100%; |
||||
|
height: auto; |
||||
|
} |
||||
|
|
||||
|
view { |
||||
|
font-size: 14px; |
||||
|
line-height: inherit; |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
|
||||
|
.text { |
||||
|
text-align: center; |
||||
|
font-size: 26rpx; |
||||
|
margin-top: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.grid-item-box { |
||||
|
flex: 1; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 15px 0; |
||||
|
} |
||||
|
|
||||
|
.uni-margin-wrap { |
||||
|
width: 690rpx; |
||||
|
width: 100%; |
||||
|
; |
||||
|
} |
||||
|
|
||||
|
.swiper { |
||||
|
height: 300rpx; |
||||
|
} |
||||
|
|
||||
|
.swiper-box { |
||||
|
height: 150px; |
||||
|
} |
||||
|
|
||||
|
.swiper-item { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
color: #fff; |
||||
|
height: 300rpx; |
||||
|
line-height: 300rpx; |
||||
|
} |
||||
|
|
||||
|
@media screen and (min-width: 500px) { |
||||
|
.uni-swiper-dot-box { |
||||
|
width: 400px; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
margin: 0 auto; |
||||
|
/* #endif */ |
||||
|
margin-top: 8px; |
||||
|
} |
||||
|
|
||||
|
.image { |
||||
|
width: 100%; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,39 @@ |
|||||
|
import { getToken } from '@/utils/auth' |
||||
|
|
||||
|
// 登录页面
|
||||
|
const loginPage = "/pages/login" |
||||
|
|
||||
|
// 页面白名单
|
||||
|
const whiteList = [ |
||||
|
'/pages/login', '/pages/register', '/pages/common/webview/index' |
||||
|
] |
||||
|
|
||||
|
// 检查地址白名单
|
||||
|
function checkWhite(url) { |
||||
|
const path = url.split('?')[0] |
||||
|
return whiteList.indexOf(path) !== -1 |
||||
|
} |
||||
|
|
||||
|
// 页面跳转验证拦截器
|
||||
|
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"] |
||||
|
list.forEach(item => { |
||||
|
uni.addInterceptor(item, { |
||||
|
invoke(to) { |
||||
|
if (getToken()) { |
||||
|
if (to.url === loginPage) { |
||||
|
uni.reLaunch({ url: "/" }) |
||||
|
} |
||||
|
return true |
||||
|
} else { |
||||
|
if (checkWhite(to.url)) { |
||||
|
return true |
||||
|
} |
||||
|
uni.reLaunch({ url: loginPage }) |
||||
|
return false |
||||
|
} |
||||
|
}, |
||||
|
fail(err) { |
||||
|
console.log(err) |
||||
|
} |
||||
|
}) |
||||
|
}) |
@ -0,0 +1,60 @@ |
|||||
|
import store from '@/store' |
||||
|
|
||||
|
function authPermission(permission) { |
||||
|
const all_permission = "*:*:*" |
||||
|
const permissions = store.getters && store.getters.permissions |
||||
|
if (permission && permission.length > 0) { |
||||
|
return permissions.some(v => { |
||||
|
return all_permission === v || v === permission |
||||
|
}) |
||||
|
} else { |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function authRole(role) { |
||||
|
const super_admin = "admin" |
||||
|
const roles = store.getters && store.getters.roles |
||||
|
if (role && role.length > 0) { |
||||
|
return roles.some(v => { |
||||
|
return super_admin === v || v === role |
||||
|
}) |
||||
|
} else { |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
// 验证用户是否具备某权限
|
||||
|
hasPermi(permission) { |
||||
|
return authPermission(permission) |
||||
|
}, |
||||
|
// 验证用户是否含有指定权限,只需包含其中一个
|
||||
|
hasPermiOr(permissions) { |
||||
|
return permissions.some(item => { |
||||
|
return authPermission(item) |
||||
|
}) |
||||
|
}, |
||||
|
// 验证用户是否含有指定权限,必须全部拥有
|
||||
|
hasPermiAnd(permissions) { |
||||
|
return permissions.every(item => { |
||||
|
return authPermission(item) |
||||
|
}) |
||||
|
}, |
||||
|
// 验证用户是否具备某角色
|
||||
|
hasRole(role) { |
||||
|
return authRole(role) |
||||
|
}, |
||||
|
// 验证用户是否含有指定角色,只需包含其中一个
|
||||
|
hasRoleOr(roles) { |
||||
|
return roles.some(item => { |
||||
|
return authRole(item) |
||||
|
}) |
||||
|
}, |
||||
|
// 验证用户是否含有指定角色,必须全部拥有
|
||||
|
hasRoleAnd(roles) { |
||||
|
return roles.every(item => { |
||||
|
return authRole(item) |
||||
|
}) |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
import tab from './tab' |
||||
|
import auth from './auth' |
||||
|
import modal from './modal' |
||||
|
|
||||
|
export default { |
||||
|
install(Vue) { |
||||
|
// 页签操作
|
||||
|
Vue.prototype.$tab = tab |
||||
|
// 认证对象
|
||||
|
Vue.prototype.$auth = auth |
||||
|
// 模态框对象
|
||||
|
Vue.prototype.$modal = modal |
||||
|
} |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
export default { |
||||
|
// 消息提示
|
||||
|
msg(content) { |
||||
|
uni.showToast({ |
||||
|
title: content, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
}, |
||||
|
// 错误消息
|
||||
|
msgError(content) { |
||||
|
uni.showToast({ |
||||
|
title: content, |
||||
|
icon: 'error' |
||||
|
}) |
||||
|
}, |
||||
|
// 成功消息
|
||||
|
msgSuccess(content) { |
||||
|
uni.showToast({ |
||||
|
title: content, |
||||
|
icon: 'success' |
||||
|
}) |
||||
|
}, |
||||
|
// 隐藏消息
|
||||
|
hideMsg(content) { |
||||
|
uni.hideToast() |
||||
|
}, |
||||
|
// 弹出提示
|
||||
|
alert(content, title) { |
||||
|
uni.showModal({ |
||||
|
title: title || '系统提示', |
||||
|
content: content, |
||||
|
showCancel: false |
||||
|
}) |
||||
|
}, |
||||
|
// 确认窗体
|
||||
|
confirm(content, title) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
uni.showModal({ |
||||
|
title: title || '系统提示', |
||||
|
content: content, |
||||
|
cancelText: '取消', |
||||
|
confirmText: '确定', |
||||
|
success: function(res) { |
||||
|
if (res.confirm) { |
||||
|
resolve(res.confirm) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
// 提示信息
|
||||
|
showToast(option) { |
||||
|
if (typeof option === "object") { |
||||
|
uni.showToast(option) |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: option, |
||||
|
icon: "none", |
||||
|
duration: 2500 |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
// 打开遮罩层
|
||||
|
loading(content) { |
||||
|
uni.showLoading({ |
||||
|
title: content, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
}, |
||||
|
// 关闭遮罩层
|
||||
|
closeLoading() { |
||||
|
uni.hideLoading() |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
export default { |
||||
|
// 关闭所有页面,打开到应用内的某个页面
|
||||
|
reLaunch(url) { |
||||
|
return uni.reLaunch({ |
||||
|
url: url |
||||
|
}) |
||||
|
}, |
||||
|
// 跳转到tabBar页面,并关闭其他所有非tabBar页面
|
||||
|
switchTab(url) { |
||||
|
return uni.switchTab({ |
||||
|
url: url |
||||
|
}) |
||||
|
}, |
||||
|
// 关闭当前页面,跳转到应用内的某个页面
|
||||
|
redirectTo(url) { |
||||
|
return uni.redirectTo({ |
||||
|
url: url |
||||
|
}) |
||||
|
}, |
||||
|
// 保留当前页面,跳转到应用内的某个页面
|
||||
|
navigateTo(url) { |
||||
|
return uni.navigateTo({ |
||||
|
url: url |
||||
|
}) |
||||
|
}, |
||||
|
// 关闭当前页面,返回上一页面或多级页面
|
||||
|
navigateBack() { |
||||
|
return uni.navigateBack() |
||||
|
} |
||||
|
} |
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,90 @@ |
|||||
|
@font-face { |
||||
|
font-family: "iconfont"; |
||||
|
src: url('@/static/font/iconfont.ttf') format('truetype'); |
||||
|
} |
||||
|
|
||||
|
.iconfont { |
||||
|
font-family: "iconfont" !important; |
||||
|
font-size: 16px; |
||||
|
display: inline-block; |
||||
|
font-style: normal; |
||||
|
-webkit-font-smoothing: antialiased; |
||||
|
-moz-osx-font-smoothing: grayscale; |
||||
|
} |
||||
|
|
||||
|
.icon-user:before { |
||||
|
content: "\e7ae"; |
||||
|
} |
||||
|
|
||||
|
.icon-password:before { |
||||
|
content: "\e8b2"; |
||||
|
} |
||||
|
|
||||
|
.icon-code:before { |
||||
|
content: "\e699"; |
||||
|
} |
||||
|
|
||||
|
.icon-setting:before { |
||||
|
content: "\e6cc"; |
||||
|
} |
||||
|
|
||||
|
.icon-share:before { |
||||
|
content: "\e739"; |
||||
|
} |
||||
|
|
||||
|
.icon-edit:before { |
||||
|
content: "\e60c"; |
||||
|
} |
||||
|
|
||||
|
.icon-version:before { |
||||
|
content: "\e63f"; |
||||
|
} |
||||
|
|
||||
|
.icon-service:before { |
||||
|
content: "\e6ff"; |
||||
|
} |
||||
|
|
||||
|
.icon-friendfill:before { |
||||
|
content: "\e726"; |
||||
|
} |
||||
|
|
||||
|
.icon-community:before { |
||||
|
content: "\e741"; |
||||
|
} |
||||
|
|
||||
|
.icon-people:before { |
||||
|
content: "\e736"; |
||||
|
} |
||||
|
|
||||
|
.icon-dianzan:before { |
||||
|
content: "\ec7f"; |
||||
|
} |
||||
|
|
||||
|
.icon-right:before { |
||||
|
content: "\e7eb"; |
||||
|
} |
||||
|
|
||||
|
.icon-logout:before { |
||||
|
content: "\e61d"; |
||||
|
} |
||||
|
|
||||
|
.icon-help:before { |
||||
|
content: "\e616"; |
||||
|
} |
||||
|
|
||||
|
.icon-github:before { |
||||
|
content: "\e628"; |
||||
|
} |
||||
|
|
||||
|
.icon-aixin:before { |
||||
|
content: "\e601"; |
||||
|
} |
||||
|
|
||||
|
.icon-clean:before { |
||||
|
content: "\e607"; |
||||
|
} |
||||
|
|
||||
|
.icon-refresh:before { |
||||
|
content: "\e604"; |
||||
|
} |
||||
|
|
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.9 KiB |
@ -0,0 +1,20 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="zh-CN"> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
||||
|
<meta name="renderer" content="webkit"> |
||||
|
<title><%= htmlWebpackPlugin.options.title %></title> |
||||
|
<link rel="shortcut icon" type="image/x-icon" href="<%= BASE_URL %>static/favicon.ico"> |
||||
|
<script> |
||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)')) |
||||
|
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />') |
||||
|
</script> |
||||
|
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" /> |
||||
|
</head> |
||||
|
<body> |
||||
|
<noscript> |
||||
|
<strong>本站点必须要开启JavaScript才能运行.</strong> |
||||
|
</noscript> |
||||
|
<div id="app"></div> |
||||
|
</html> |
After Width: | Height: | Size: 975 B |
After Width: | Height: | Size: 3.7 KiB |