Commit 54e26d9e8b644fa67b11354b06b2fd9497620ace
Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/errorcode
Conflicts: src/main/resources/application.yaml
Showing
314 changed files
with
16140 additions
and
3768 deletions
Too many changes.
To preserve performance only 100 of 314 files are displayed.
README.md
| ... | ... | @@ -35,12 +35,12 @@ |
| 35 | 35 | | | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 | |
| 36 | 36 | | | 岗位管理 | 配置系统用户所属担任职务 | |
| 37 | 37 | | | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | |
| 38 | -| | 通知公告 | 系统通知公告信息发布维护 | | |
| 38 | +| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、云片等主流短信平台 | | |
| 39 | 39 | | 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 | |
| 40 | 40 | | | 登录日志 | 系统登录日志记录查询包含登录异常 | |
| 41 | +| | 通知公告 | 系统通知公告信息发布维护 | | |
| 41 | 42 | |
| 42 | 43 | 计划新增功能: |
| 43 | -* 短信 | |
| 44 | 44 | * 邮件 |
| 45 | 45 | * 钉钉、飞书等通知 |
| 46 | 46 | |
| ... | ... | @@ -50,6 +50,7 @@ |
| 50 | 50 | | --- | --- | --- | |
| 51 | 51 | | 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 | |
| 52 | 52 | | | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 | |
| 53 | +| 🚀 | 文件服务 | 支持本地文件存储,同时支持兼容 Amazon S3 协议的云服务、开源组件 | | |
| 53 | 54 | | 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 | |
| 54 | 55 | | | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 | |
| 55 | 56 | | | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | |
| ... | ... | @@ -64,7 +65,6 @@ |
| 64 | 65 | 计划新增: |
| 65 | 66 | * 工作流 |
| 66 | 67 | * 错误码 |
| 67 | -* 文件服务 | |
| 68 | 68 | |
| 69 | 69 | ### 研发工具 |
| 70 | 70 | |
| ... | ... | @@ -87,14 +87,15 @@ |
| 87 | 87 | |
| 88 | 88 | ## 技术栈 |
| 89 | 89 | |
| 90 | -**后端** | |
| 90 | +### 后端 | |
| 91 | 91 | |
| 92 | 92 | | 框架 | 说明 | 版本 | 学习指南 | |
| 93 | 93 | | --- | --- | --- | --- | |
| 94 | 94 | | [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.4.2 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | |
| 95 | 95 | | [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 | | |
| 96 | 96 | | [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.4 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | |
| 97 | -| [MyBatis-Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | | |
| 97 | +| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | | |
| 98 | +| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | | |
| 98 | 99 | | [Redis](https://redis.io/) | key-value 数据库 | 5.0 | | |
| 99 | 100 | | [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.1.46 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) | |
| 100 | 101 | | [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.4.2 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) | |
| ... | ... | @@ -102,16 +103,16 @@ |
| 102 | 103 | | [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.1.7 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) | |
| 103 | 104 | | [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) | |
| 104 | 105 | | [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 3.0.2 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) | |
| 105 | -| [Resilience4j](https://github.com/quartz-scheduler) | 服务保障组件 | 1.7.0 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) | | |
| 106 | +| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.0 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) | | |
| 106 | 107 | | [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) | |
| 107 | -| [Spring Boot Admin](https://github.com/skywalking) | Spring Boot 监控平台 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | | |
| 108 | +| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | | |
| 108 | 109 | | [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.11.4 | | |
| 109 | 110 | | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) | |
| 110 | 111 | | [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.16.14 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | |
| 111 | 112 | | [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.7.0 | - | |
| 112 | 113 | | [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 3.6.28 | - | |
| 113 | 114 | |
| 114 | -**前端** | |
| 115 | +### 前端 | |
| 115 | 116 | |
| 116 | 117 | | 框架 | 说明 | 版本 | |
| 117 | 118 | | --- | --- | --- | | ... | ... |
lombok.config
0 → 100644
pom.xml
| ... | ... | @@ -22,15 +22,16 @@ |
| 22 | 22 | <maven.compiler.target>${java.version}</maven.compiler.target> |
| 23 | 23 | <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version> |
| 24 | 24 | <!-- 统一依赖管理 --> |
| 25 | - <spring.boot.version>2.4.2</spring.boot.version> | |
| 25 | + <spring.boot.version>2.4.4</spring.boot.version> | |
| 26 | 26 | <!-- Web 相关 --> |
| 27 | 27 | <knife4j.version>3.0.2</knife4j.version> |
| 28 | 28 | <swagger-annotations.version>1.5.22</swagger-annotations.version> |
| 29 | 29 | <!-- DB 相关 --> |
| 30 | 30 | <mysql-connector-java.version>5.1.46</mysql-connector-java.version> |
| 31 | 31 | <druid.version>1.2.4</druid.version> |
| 32 | - <mybatis-plus.version>3.4.1</mybatis-plus.version> | |
| 33 | - <redisson.version>3.14.1</redisson.version> | |
| 32 | + <mybatis-plus.version>3.4.2</mybatis-plus.version> | |
| 33 | + <dynamic-datasource.version>3.3.2</dynamic-datasource.version> | |
| 34 | + <redisson.version>3.15.1</redisson.version> | |
| 34 | 35 | <!-- Config 配置中心相关 --> |
| 35 | 36 | <apollo.version>1.7.0</apollo.version> |
| 36 | 37 | <!-- 服务保障相关 --> |
| ... | ... | @@ -42,10 +43,16 @@ |
| 42 | 43 | <!-- 工具类相关 --> |
| 43 | 44 | <lombok.version>1.16.14</lombok.version> |
| 44 | 45 | <mapstruct.version>1.4.1.Final</mapstruct.version> |
| 45 | - <hutool.version>5.5.6</hutool.version> | |
| 46 | + <hutool.version>5.6.1</hutool.version> | |
| 46 | 47 | <easyexcel.verion>2.2.7</easyexcel.verion> |
| 47 | 48 | <velocity.version>2.2</velocity.version> |
| 48 | 49 | <screw.version>1.0.5</screw.version> |
| 50 | + <podam.version>7.2.6.RELEASE</podam.version> | |
| 51 | + <jedis-mock.version>0.1.16</jedis-mock.version> | |
| 52 | + <!-- 三方云服务相关 --> | |
| 53 | + <aliyun-java-sdk-core.version>4.5.18</aliyun-java-sdk-core.version> | |
| 54 | + <aliyun-java-sdk-dysmsapi.version>2.1.0</aliyun-java-sdk-dysmsapi.version> | |
| 55 | + <yunpian-java-sdk.version>1.2.7</yunpian-java-sdk.version> | |
| 49 | 56 | </properties> |
| 50 | 57 | |
| 51 | 58 | <!-- 依赖声明 --> |
| ... | ... | @@ -133,6 +140,11 @@ |
| 133 | 140 | <artifactId>mybatis-plus-boot-starter</artifactId> |
| 134 | 141 | <version>${mybatis-plus.version}</version> |
| 135 | 142 | </dependency> |
| 143 | + <dependency> | |
| 144 | + <groupId>com.baomidou</groupId> | |
| 145 | + <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 --> | |
| 146 | + <version>${dynamic-datasource.version}</version> | |
| 147 | + </dependency> | |
| 136 | 148 | |
| 137 | 149 | <dependency> |
| 138 | 150 | <groupId>org.redisson</groupId> |
| ... | ... | @@ -212,14 +224,14 @@ |
| 212 | 224 | <dependency> |
| 213 | 225 | <groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 --> |
| 214 | 226 | <artifactId>jedis-mock</artifactId> |
| 215 | - <version>0.1.16</version> | |
| 227 | + <version>${jedis-mock.version}</version> | |
| 216 | 228 | <scope>test</scope> |
| 217 | 229 | </dependency> |
| 218 | 230 | |
| 219 | 231 | <dependency> |
| 220 | 232 | <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 --> |
| 221 | 233 | <artifactId>podam</artifactId> |
| 222 | - <version>7.2.6.RELEASE</version> | |
| 234 | + <version>${podam.version}</version> | |
| 223 | 235 | <scope>test</scope> |
| 224 | 236 | </dependency> |
| 225 | 237 | |
| ... | ... | @@ -237,33 +249,19 @@ |
| 237 | 249 | </dependency> |
| 238 | 250 | <dependency> |
| 239 | 251 | <groupId>org.mapstruct</groupId> |
| 240 | - <artifactId>mapstruct-jdk8</artifactId> | |
| 252 | + <artifactId>mapstruct-processor</artifactId> | |
| 241 | 253 | <version>${mapstruct.version}</version> |
| 242 | - </dependency> | |
| 243 | - | |
| 244 | - <dependency> | |
| 245 | - <groupId>cn.hutool</groupId> | |
| 246 | - <artifactId>hutool-core</artifactId> | |
| 247 | - <version>${hutool.version}</version> | |
| 248 | - </dependency> | |
| 249 | - <dependency> | |
| 250 | - <groupId>cn.hutool</groupId> | |
| 251 | - <artifactId>hutool-extra</artifactId> | |
| 252 | - <version>${hutool.version}</version> | |
| 253 | - </dependency> | |
| 254 | - <dependency> | |
| 255 | - <groupId>cn.hutool</groupId> | |
| 256 | - <artifactId>hutool-captcha</artifactId> | |
| 257 | - <version>${hutool.version}</version> | |
| 254 | + <optional>true</optional> | |
| 258 | 255 | </dependency> |
| 259 | 256 | <dependency> |
| 260 | - <groupId>cn.hutool</groupId> | |
| 261 | - <artifactId>hutool-http</artifactId> | |
| 262 | - <version>${hutool.version}</version> | |
| 257 | + <groupId>org.mapstruct</groupId> | |
| 258 | + <artifactId>mapstruct-jdk8</artifactId> | |
| 259 | + <version>${mapstruct.version}</version> | |
| 263 | 260 | </dependency> |
| 261 | + | |
| 264 | 262 | <dependency> |
| 265 | 263 | <groupId>cn.hutool</groupId> |
| 266 | - <artifactId>hutool-crypto</artifactId> | |
| 264 | + <artifactId>hutool-all</artifactId> | |
| 267 | 265 | <version>${hutool.version}</version> |
| 268 | 266 | </dependency> |
| 269 | 267 | |
| ... | ... | @@ -285,6 +283,27 @@ |
| 285 | 283 | <version>${screw.version}</version> |
| 286 | 284 | </dependency> |
| 287 | 285 | |
| 286 | + <!-- 三方云服务相关 --> | |
| 287 | + | |
| 288 | + <!-- SMS SDK begin --> | |
| 289 | + <dependency> | |
| 290 | + <groupId>com.yunpian.sdk</groupId> | |
| 291 | + <artifactId>yunpian-java-sdk</artifactId> | |
| 292 | + <version>${yunpian-java-sdk.version}</version> | |
| 293 | + </dependency> | |
| 294 | + | |
| 295 | + <dependency> | |
| 296 | + <groupId>com.aliyun</groupId> | |
| 297 | + <artifactId>aliyun-java-sdk-core</artifactId> | |
| 298 | + <version>${aliyun-java-sdk-core.version}</version> | |
| 299 | + </dependency> | |
| 300 | + <dependency> | |
| 301 | + <groupId>com.aliyun</groupId> | |
| 302 | + <artifactId>aliyun-java-sdk-dysmsapi</artifactId> | |
| 303 | + <version>${aliyun-java-sdk-dysmsapi.version}</version> | |
| 304 | + </dependency> | |
| 305 | + <!-- SMS SDK end --> | |
| 306 | + | |
| 288 | 307 | </dependencies> |
| 289 | 308 | |
| 290 | 309 | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java deleted
| 1 | -package com.ruoyi.common.annotation; | |
| 2 | - | |
| 3 | -import java.lang.annotation.Documented; | |
| 4 | -import java.lang.annotation.ElementType; | |
| 5 | -import java.lang.annotation.Retention; | |
| 6 | -import java.lang.annotation.RetentionPolicy; | |
| 7 | -import java.lang.annotation.Target; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * 数据权限过滤注解 | |
| 11 | - * | |
| 12 | - * @author ruoyi | |
| 13 | - */ | |
| 14 | -@Target(ElementType.METHOD) | |
| 15 | -@Retention(RetentionPolicy.RUNTIME) | |
| 16 | -@Documented | |
| 17 | -public @interface DataScope { | |
| 18 | - /** | |
| 19 | - * 部门表的别名 | |
| 20 | - */ | |
| 21 | - public String deptAlias() default ""; | |
| 22 | - | |
| 23 | - /** | |
| 24 | - * 用户表的别名 | |
| 25 | - */ | |
| 26 | - public String userAlias() default ""; | |
| 27 | -} |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScopeAspect.java deleted
| 1 | -package com.ruoyi.framework.aspectj; | |
| 2 | - | |
| 3 | -import java.lang.reflect.Method; | |
| 4 | - | |
| 5 | -import org.aspectj.lang.JoinPoint; | |
| 6 | -import org.aspectj.lang.Signature; | |
| 7 | -import org.aspectj.lang.annotation.Aspect; | |
| 8 | -import org.aspectj.lang.annotation.Before; | |
| 9 | -import org.aspectj.lang.annotation.Pointcut; | |
| 10 | -import org.aspectj.lang.reflect.MethodSignature; | |
| 11 | -import org.springframework.stereotype.Component; | |
| 12 | -import com.ruoyi.common.annotation.DataScope; | |
| 13 | -import com.ruoyi.common.core.domain.BaseEntity; | |
| 14 | -import com.ruoyi.common.core.domain.entity.SysRole; | |
| 15 | -import com.ruoyi.common.core.domain.entity.SysUser; | |
| 16 | -import com.ruoyi.common.core.domain.model.LoginUser; | |
| 17 | -import com.ruoyi.common.utils.ServletUtils; | |
| 18 | -import com.ruoyi.common.utils.StringUtils; | |
| 19 | -import com.ruoyi.common.utils.spring.SpringUtils; | |
| 20 | -import com.ruoyi.framework.web.service.TokenService; | |
| 21 | - | |
| 22 | -/** | |
| 23 | - * 数据过滤处理 | |
| 24 | - * | |
| 25 | - * @author ruoyi | |
| 26 | - */ | |
| 27 | -@Aspect | |
| 28 | -@Component | |
| 29 | -public class DataScopeAspect { | |
| 30 | - /** | |
| 31 | - * 全部数据权限 | |
| 32 | - */ | |
| 33 | - public static final String DATA_SCOPE_ALL = "1"; | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 自定数据权限 | |
| 37 | - */ | |
| 38 | - public static final String DATA_SCOPE_CUSTOM = "2"; | |
| 39 | - | |
| 40 | - /** | |
| 41 | - * 部门数据权限 | |
| 42 | - */ | |
| 43 | - public static final String DATA_SCOPE_DEPT = "3"; | |
| 44 | - | |
| 45 | - /** | |
| 46 | - * 部门及以下数据权限 | |
| 47 | - */ | |
| 48 | - public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; | |
| 49 | - | |
| 50 | - /** | |
| 51 | - * 仅本人数据权限 | |
| 52 | - */ | |
| 53 | - public static final String DATA_SCOPE_SELF = "5"; | |
| 54 | - | |
| 55 | - /** | |
| 56 | - * 数据权限过滤关键字 | |
| 57 | - */ | |
| 58 | - public static final String DATA_SCOPE = "dataScope"; | |
| 59 | - | |
| 60 | - // 配置织入点 | |
| 61 | - @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)") | |
| 62 | - public void dataScopePointCut() { | |
| 63 | - } | |
| 64 | - | |
| 65 | - @Before("dataScopePointCut()") | |
| 66 | - public void doBefore(JoinPoint point) throws Throwable { | |
| 67 | - handleDataScope(point); | |
| 68 | - } | |
| 69 | - | |
| 70 | - protected void handleDataScope(final JoinPoint joinPoint) { | |
| 71 | - // 获得注解 | |
| 72 | - DataScope controllerDataScope = getAnnotationLog(joinPoint); | |
| 73 | - if (controllerDataScope == null) { | |
| 74 | - return; | |
| 75 | - } | |
| 76 | - // 获取当前的用户 | |
| 77 | - LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest()); | |
| 78 | - if (StringUtils.isNotNull(loginUser)) { | |
| 79 | - SysUser currentUser = loginUser.getUser(); | |
| 80 | - // 如果是超级管理员,则不过滤数据 | |
| 81 | - if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) { | |
| 82 | - dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), | |
| 83 | - controllerDataScope.userAlias()); | |
| 84 | - } | |
| 85 | - } | |
| 86 | - } | |
| 87 | - | |
| 88 | - /** | |
| 89 | - * 数据范围过滤 | |
| 90 | - * | |
| 91 | - * @param joinPoint 切点 | |
| 92 | - * @param user 用户 | |
| 93 | - * @param userAlias 别名 | |
| 94 | - */ | |
| 95 | - public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) { | |
| 96 | - StringBuilder sqlString = new StringBuilder(); | |
| 97 | - | |
| 98 | - for (SysRole role : user.getRoles()) { | |
| 99 | - String dataScope = role.getDataScope(); | |
| 100 | - if (DATA_SCOPE_ALL.equals(dataScope)) { | |
| 101 | - sqlString = new StringBuilder(); | |
| 102 | - break; | |
| 103 | - } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) { | |
| 104 | - sqlString.append(StringUtils.format( | |
| 105 | - " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, | |
| 106 | - role.getRoleId())); | |
| 107 | - } else if (DATA_SCOPE_DEPT.equals(dataScope)) { | |
| 108 | - sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); | |
| 109 | - } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) { | |
| 110 | - sqlString.append(StringUtils.format( | |
| 111 | - " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", | |
| 112 | - deptAlias, user.getDeptId(), user.getDeptId())); | |
| 113 | - } else if (DATA_SCOPE_SELF.equals(dataScope)) { | |
| 114 | - if (StringUtils.isNotBlank(userAlias)) { | |
| 115 | - sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); | |
| 116 | - } else { | |
| 117 | - // 数据权限为仅本人且没有userAlias别名不查询任何数据 | |
| 118 | - sqlString.append(" OR 1=0 "); | |
| 119 | - } | |
| 120 | - } | |
| 121 | - } | |
| 122 | - | |
| 123 | - if (StringUtils.isNotBlank(sqlString.toString())) { | |
| 124 | - Object params = joinPoint.getArgs()[0]; | |
| 125 | - if (StringUtils.isNotNull(params) && params instanceof BaseEntity) { | |
| 126 | - BaseEntity baseEntity = (BaseEntity) params; | |
| 127 | - baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); | |
| 128 | - } | |
| 129 | - } | |
| 130 | - } | |
| 131 | - | |
| 132 | - /** | |
| 133 | - * 是否存在注解,如果存在就获取 | |
| 134 | - */ | |
| 135 | - private DataScope getAnnotationLog(JoinPoint joinPoint) { | |
| 136 | - Signature signature = joinPoint.getSignature(); | |
| 137 | - MethodSignature methodSignature = (MethodSignature) signature; | |
| 138 | - Method method = methodSignature.getMethod(); | |
| 139 | - | |
| 140 | - if (method != null) { | |
| 141 | - return method.getAnnotation(DataScope.class); | |
| 142 | - } | |
| 143 | - return null; | |
| 144 | - } | |
| 145 | -} |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java deleted
| 1 | -package com.ruoyi.common.annotation; | |
| 2 | - | |
| 3 | -import java.lang.annotation.Documented; | |
| 4 | -import java.lang.annotation.ElementType; | |
| 5 | -import java.lang.annotation.Inherited; | |
| 6 | -import java.lang.annotation.Retention; | |
| 7 | -import java.lang.annotation.RetentionPolicy; | |
| 8 | -import java.lang.annotation.Target; | |
| 9 | - | |
| 10 | -import com.ruoyi.common.enums.DataSourceType; | |
| 11 | - | |
| 12 | -/** | |
| 13 | - * 自定义多数据源切换注解 | |
| 14 | - * <p> | |
| 15 | - * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 | |
| 16 | - * | |
| 17 | - * @author ruoyi | |
| 18 | - */ | |
| 19 | -@Target({ElementType.METHOD, ElementType.TYPE}) | |
| 20 | -@Retention(RetentionPolicy.RUNTIME) | |
| 21 | -@Documented | |
| 22 | -@Inherited | |
| 23 | -public @interface DataSource { | |
| 24 | - /** | |
| 25 | - * 切换数据源名称 | |
| 26 | - */ | |
| 27 | - public DataSourceType value() default DataSourceType.MASTER; | |
| 28 | -} |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceAspect.java deleted
| 1 | -package com.ruoyi.framework.aspectj; | |
| 2 | - | |
| 3 | -import java.util.Objects; | |
| 4 | - | |
| 5 | -import org.aspectj.lang.ProceedingJoinPoint; | |
| 6 | -import org.aspectj.lang.annotation.Around; | |
| 7 | -import org.aspectj.lang.annotation.Aspect; | |
| 8 | -import org.aspectj.lang.annotation.Pointcut; | |
| 9 | -import org.aspectj.lang.reflect.MethodSignature; | |
| 10 | -import org.slf4j.Logger; | |
| 11 | -import org.slf4j.LoggerFactory; | |
| 12 | -import org.springframework.core.annotation.AnnotationUtils; | |
| 13 | -import org.springframework.core.annotation.Order; | |
| 14 | -import org.springframework.stereotype.Component; | |
| 15 | -import com.ruoyi.common.annotation.DataSource; | |
| 16 | -import com.ruoyi.common.utils.StringUtils; | |
| 17 | -import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; | |
| 18 | - | |
| 19 | -/** | |
| 20 | - * 多数据源处理 | |
| 21 | - * | |
| 22 | - * @author ruoyi | |
| 23 | - */ | |
| 24 | -@Aspect | |
| 25 | -@Order(1) | |
| 26 | -@Component | |
| 27 | -public class DataSourceAspect { | |
| 28 | - protected Logger logger = LoggerFactory.getLogger(getClass()); | |
| 29 | - | |
| 30 | - @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" | |
| 31 | - + "|| @within(com.ruoyi.common.annotation.DataSource)") | |
| 32 | - public void dsPointCut() { | |
| 33 | - | |
| 34 | - } | |
| 35 | - | |
| 36 | - @Around("dsPointCut()") | |
| 37 | - public Object around(ProceedingJoinPoint point) throws Throwable { | |
| 38 | - DataSource dataSource = getDataSource(point); | |
| 39 | - | |
| 40 | - if (StringUtils.isNotNull(dataSource)) { | |
| 41 | - DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); | |
| 42 | - } | |
| 43 | - | |
| 44 | - try { | |
| 45 | - return point.proceed(); | |
| 46 | - } finally { | |
| 47 | - // 销毁数据源 在执行方法之后 | |
| 48 | - DynamicDataSourceContextHolder.clearDataSourceType(); | |
| 49 | - } | |
| 50 | - } | |
| 51 | - | |
| 52 | - /** | |
| 53 | - * 获取需要切换的数据源 | |
| 54 | - */ | |
| 55 | - public DataSource getDataSource(ProceedingJoinPoint point) { | |
| 56 | - MethodSignature signature = (MethodSignature) point.getSignature(); | |
| 57 | - DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); | |
| 58 | - if (Objects.nonNull(dataSource)) { | |
| 59 | - return dataSource; | |
| 60 | - } | |
| 61 | - | |
| 62 | - return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); | |
| 63 | - } | |
| 64 | -} |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceType.java deleted
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSource.java deleted
| 1 | -package com.ruoyi.framework.datasource; | |
| 2 | - | |
| 3 | -import java.util.Map; | |
| 4 | -import javax.sql.DataSource; | |
| 5 | - | |
| 6 | -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; | |
| 7 | - | |
| 8 | -/** | |
| 9 | - * 动态数据源 | |
| 10 | - * | |
| 11 | - * @author ruoyi | |
| 12 | - */ | |
| 13 | -public class DynamicDataSource extends AbstractRoutingDataSource { | |
| 14 | - public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { | |
| 15 | - super.setDefaultTargetDataSource(defaultTargetDataSource); | |
| 16 | - super.setTargetDataSources(targetDataSources); | |
| 17 | - super.afterPropertiesSet(); | |
| 18 | - } | |
| 19 | - | |
| 20 | - @Override | |
| 21 | - protected Object determineCurrentLookupKey() { | |
| 22 | - return DynamicDataSourceContextHolder.getDataSourceType(); | |
| 23 | - } | |
| 24 | -} |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSourceContextHolder.java deleted
| 1 | -package com.ruoyi.framework.datasource; | |
| 2 | - | |
| 3 | -import org.slf4j.Logger; | |
| 4 | -import org.slf4j.LoggerFactory; | |
| 5 | - | |
| 6 | -/** | |
| 7 | - * 数据源切换处理 | |
| 8 | - * | |
| 9 | - * @author ruoyi | |
| 10 | - */ | |
| 11 | -public class DynamicDataSourceContextHolder { | |
| 12 | - public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); | |
| 13 | - | |
| 14 | - /** | |
| 15 | - * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, | |
| 16 | - * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 | |
| 17 | - */ | |
| 18 | - private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); | |
| 19 | - | |
| 20 | - /** | |
| 21 | - * 设置数据源的变量 | |
| 22 | - */ | |
| 23 | - public static void setDataSourceType(String dsType) { | |
| 24 | - log.info("切换到{}数据源", dsType); | |
| 25 | - CONTEXT_HOLDER.set(dsType); | |
| 26 | - } | |
| 27 | - | |
| 28 | - /** | |
| 29 | - * 获得数据源的变量 | |
| 30 | - */ | |
| 31 | - public static String getDataSourceType() { | |
| 32 | - return CONTEXT_HOLDER.get(); | |
| 33 | - } | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 清空数据源变量 | |
| 37 | - */ | |
| 38 | - public static void clearDataSourceType() { | |
| 39 | - CONTEXT_HOLDER.remove(); | |
| 40 | - } | |
| 41 | -} |
ruoyi-common/src/main/java/com/ruoyi/common/config/DruidConfig.java deleted
| 1 | -package com.ruoyi.framework.config; | |
| 2 | - | |
| 3 | -import java.io.IOException; | |
| 4 | -import java.util.HashMap; | |
| 5 | -import java.util.Map; | |
| 6 | -import javax.servlet.Filter; | |
| 7 | -import javax.servlet.FilterChain; | |
| 8 | -import javax.servlet.ServletException; | |
| 9 | -import javax.servlet.ServletRequest; | |
| 10 | -import javax.servlet.ServletResponse; | |
| 11 | -import javax.sql.DataSource; | |
| 12 | - | |
| 13 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 14 | -import org.springframework.boot.context.properties.ConfigurationProperties; | |
| 15 | -import org.springframework.boot.web.servlet.FilterRegistrationBean; | |
| 16 | -import org.springframework.context.annotation.Bean; | |
| 17 | -import org.springframework.context.annotation.Configuration; | |
| 18 | -import org.springframework.context.annotation.Primary; | |
| 19 | -import com.alibaba.druid.pool.DruidDataSource; | |
| 20 | -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; | |
| 21 | -import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; | |
| 22 | -import com.alibaba.druid.util.Utils; | |
| 23 | -import com.ruoyi.common.enums.DataSourceType; | |
| 24 | -import com.ruoyi.common.utils.spring.SpringUtils; | |
| 25 | -import com.ruoyi.framework.config.properties.DruidProperties; | |
| 26 | -import com.ruoyi.framework.datasource.DynamicDataSource; | |
| 27 | - | |
| 28 | -/** | |
| 29 | - * druid 配置多数据源 | |
| 30 | - * | |
| 31 | - * @author ruoyi | |
| 32 | - */ | |
| 33 | -@Configuration | |
| 34 | -public class DruidConfig { | |
| 35 | - @Bean | |
| 36 | - @ConfigurationProperties("spring.datasource.druid.master") | |
| 37 | - public DataSource masterDataSource(DruidProperties druidProperties) { | |
| 38 | - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); | |
| 39 | - return druidProperties.dataSource(dataSource); | |
| 40 | - } | |
| 41 | - | |
| 42 | - @Bean | |
| 43 | - @ConfigurationProperties("spring.datasource.druid.slave") | |
| 44 | - @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") | |
| 45 | - public DataSource slaveDataSource(DruidProperties druidProperties) { | |
| 46 | - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); | |
| 47 | - return druidProperties.dataSource(dataSource); | |
| 48 | - } | |
| 49 | - | |
| 50 | - @Bean(name = "dynamicDataSource") | |
| 51 | - @Primary | |
| 52 | - public DynamicDataSource dataSource(DataSource masterDataSource) { | |
| 53 | - Map<Object, Object> targetDataSources = new HashMap<>(); | |
| 54 | - targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); | |
| 55 | - setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); | |
| 56 | - return new DynamicDataSource(masterDataSource, targetDataSources); | |
| 57 | - } | |
| 58 | - | |
| 59 | - /** | |
| 60 | - * 设置数据源 | |
| 61 | - * | |
| 62 | - * @param targetDataSources 备选数据源集合 | |
| 63 | - * @param sourceName 数据源名称 | |
| 64 | - * @param beanName bean名称 | |
| 65 | - */ | |
| 66 | - public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) { | |
| 67 | - try { | |
| 68 | - DataSource dataSource = SpringUtils.getBean(beanName); | |
| 69 | - targetDataSources.put(sourceName, dataSource); | |
| 70 | - } catch (Exception e) { | |
| 71 | - } | |
| 72 | - } | |
| 73 | - | |
| 74 | - /** | |
| 75 | - * 去除监控页面底部的广告 | |
| 76 | - */ | |
| 77 | - @SuppressWarnings({"rawtypes", "unchecked"}) | |
| 78 | - @Bean | |
| 79 | - @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") | |
| 80 | - public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) { | |
| 81 | - // 获取web监控页面的参数 | |
| 82 | - DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); | |
| 83 | - // 提取common.js的配置路径 | |
| 84 | - String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; | |
| 85 | - String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); | |
| 86 | - final String filePath = "support/http/resources/js/common.js"; | |
| 87 | - // 创建filter进行过滤 | |
| 88 | - Filter filter = new Filter() { | |
| 89 | - @Override | |
| 90 | - public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { | |
| 91 | - } | |
| 92 | - | |
| 93 | - @Override | |
| 94 | - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |
| 95 | - throws IOException, ServletException { | |
| 96 | - chain.doFilter(request, response); | |
| 97 | - // 重置缓冲区,响应头不会被重置 | |
| 98 | - response.resetBuffer(); | |
| 99 | - // 获取common.js | |
| 100 | - String text = Utils.readFromResource(filePath); | |
| 101 | - // 正则替换banner, 除去底部的广告信息 | |
| 102 | - text = text.replaceAll("<a.*?banner\"></a><br/>", ""); | |
| 103 | - text = text.replaceAll("powered.*?shrek.wang</a>", ""); | |
| 104 | - response.getWriter().write(text); | |
| 105 | - } | |
| 106 | - | |
| 107 | - @Override | |
| 108 | - public void destroy() { | |
| 109 | - } | |
| 110 | - }; | |
| 111 | - FilterRegistrationBean registrationBean = new FilterRegistrationBean(); | |
| 112 | - registrationBean.setFilter(filter); | |
| 113 | - registrationBean.addUrlPatterns(commonJsPattern); | |
| 114 | - return registrationBean; | |
| 115 | - } | |
| 116 | -} |
ruoyi-common/src/main/resources/application-druid.yml deleted
| 1 | -# 数据源配置 | |
| 2 | -spring: | |
| 3 | - datasource: | |
| 4 | - type: com.alibaba.druid.pool.DruidDataSource | |
| 5 | - driverClassName: com.mysql.cj.jdbc.Driver | |
| 6 | - druid: | |
| 7 | - # 主库数据源 | |
| 8 | - master: | |
| 9 | - url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 | |
| 10 | - username: root | |
| 11 | - password: password | |
| 12 | - # 从库数据源 | |
| 13 | - slave: | |
| 14 | - # 从数据源开关/默认关闭 | |
| 15 | - enabled: false | |
| 16 | - url: | |
| 17 | - username: | |
| 18 | - password: | |
| 19 | - # 初始连接数 | |
| 20 | - initialSize: 5 | |
| 21 | - # 最小连接池数量 | |
| 22 | - minIdle: 10 | |
| 23 | - # 最大连接池数量 | |
| 24 | - maxActive: 20 | |
| 25 | - # 配置获取连接等待超时的时间 | |
| 26 | - maxWait: 60000 | |
| 27 | - # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |
| 28 | - timeBetweenEvictionRunsMillis: 60000 | |
| 29 | - # 配置一个连接在池中最小生存的时间,单位是毫秒 | |
| 30 | - minEvictableIdleTimeMillis: 300000 | |
| 31 | - # 配置一个连接在池中最大生存的时间,单位是毫秒 | |
| 32 | - maxEvictableIdleTimeMillis: 900000 | |
| 33 | - # 配置检测连接是否有效 | |
| 34 | - validationQuery: SELECT 1 FROM DUAL | |
| 35 | - testWhileIdle: true | |
| 36 | - testOnBorrow: false | |
| 37 | - testOnReturn: false |
ruoyi-ui/.env.demo1024
0 → 100644
ruoyi-ui/package.json
| ... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 | "dev": "vue-cli-service serve", |
| 9 | 9 | "build:prod": "vue-cli-service build", |
| 10 | 10 | "build:stage": "vue-cli-service build --mode staging", |
| 11 | + "build:demo1024": "vue-cli-service build --mode demo1024", | |
| 11 | 12 | "preview": "node build/index.js --preview", |
| 12 | 13 | "lint": "eslint --ext .js,.vue src" |
| 13 | 14 | }, | ... | ... |
ruoyi-ui/src/api/infra/dbDoc.js
| ... | ... | @@ -8,3 +8,19 @@ export function exportHtml() { |
| 8 | 8 | responseType: 'blob' |
| 9 | 9 | }) |
| 10 | 10 | } |
| 11 | + | |
| 12 | +export function exportWord() { | |
| 13 | + return request({ | |
| 14 | + url: '/infra/db-doc/export-word', | |
| 15 | + method: 'get', | |
| 16 | + responseType: 'blob' | |
| 17 | + }) | |
| 18 | +} | |
| 19 | + | |
| 20 | +export function exportMarkdown() { | |
| 21 | + return request({ | |
| 22 | + url: '/infra/db-doc/export-markdown', | |
| 23 | + method: 'get', | |
| 24 | + responseType: 'blob' | |
| 25 | + }) | |
| 26 | +} | ... | ... |
ruoyi-ui/src/api/infra/file.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// 删除文件 | |
| 4 | +export function deleteFile(id) { | |
| 5 | + return request({ | |
| 6 | + url: '/infra/file/delete?id=' + id, | |
| 7 | + method: 'delete' | |
| 8 | + }) | |
| 9 | +} | |
| 10 | + | |
| 11 | +// 获得文件分页 | |
| 12 | +export function getFilePage(query) { | |
| 13 | + return request({ | |
| 14 | + url: '/infra/file/page', | |
| 15 | + method: 'get', | |
| 16 | + params: query | |
| 17 | + }) | |
| 18 | +} | ... | ... |
ruoyi-ui/src/api/system/dept.js
| ... | ... | @@ -46,7 +46,7 @@ export function addDept(data) { |
| 46 | 46 | export function updateDept(data) { |
| 47 | 47 | return request({ |
| 48 | 48 | url: '/system/dept/update', |
| 49 | - method: 'post', | |
| 49 | + method: 'put', | |
| 50 | 50 | data: data |
| 51 | 51 | }) |
| 52 | 52 | } |
| ... | ... | @@ -55,6 +55,6 @@ export function updateDept(data) { |
| 55 | 55 | export function delDept(id) { |
| 56 | 56 | return request({ |
| 57 | 57 | url: '/system/dept/delete?id=' + id, |
| 58 | - method: 'post' | |
| 58 | + method: 'delete' | |
| 59 | 59 | }) |
| 60 | 60 | } | ... | ... |
ruoyi-ui/src/api/system/dict/data.js
| ... | ... | @@ -38,7 +38,7 @@ export function addData(data) { |
| 38 | 38 | export function updateData(data) { |
| 39 | 39 | return request({ |
| 40 | 40 | url: '/system/dict-data/update', |
| 41 | - method: 'post', | |
| 41 | + method: 'put', | |
| 42 | 42 | data: data |
| 43 | 43 | }) |
| 44 | 44 | } |
| ... | ... | @@ -47,7 +47,7 @@ export function updateData(data) { |
| 47 | 47 | export function delData(dictCode) { |
| 48 | 48 | return request({ |
| 49 | 49 | url: '/system/dict-data/delete?id=' + dictCode, |
| 50 | - method: 'post' | |
| 50 | + method: 'delete' | |
| 51 | 51 | }) |
| 52 | 52 | } |
| 53 | 53 | ... | ... |
ruoyi-ui/src/api/system/dict/type.js
| ... | ... | @@ -30,7 +30,7 @@ export function addType(data) { |
| 30 | 30 | export function updateType(data) { |
| 31 | 31 | return request({ |
| 32 | 32 | url: '/system/dict-type/update', |
| 33 | - method: 'post', | |
| 33 | + method: 'put', | |
| 34 | 34 | data: data |
| 35 | 35 | }) |
| 36 | 36 | } |
| ... | ... | @@ -39,7 +39,7 @@ export function updateType(data) { |
| 39 | 39 | export function delType(dictId) { |
| 40 | 40 | return request({ |
| 41 | 41 | url: '/system/dict-type/delete?id=' + dictId, |
| 42 | - method: 'post' | |
| 42 | + method: 'delete' | |
| 43 | 43 | }) |
| 44 | 44 | } |
| 45 | 45 | ... | ... |
ruoyi-ui/src/api/system/menu.js
| ... | ... | @@ -38,7 +38,7 @@ export function addMenu(data) { |
| 38 | 38 | export function updateMenu(data) { |
| 39 | 39 | return request({ |
| 40 | 40 | url: '/system/menu/update', |
| 41 | - method: 'post', | |
| 41 | + method: 'put', | |
| 42 | 42 | data: data |
| 43 | 43 | }) |
| 44 | 44 | } |
| ... | ... | @@ -47,6 +47,6 @@ export function updateMenu(data) { |
| 47 | 47 | export function delMenu(id) { |
| 48 | 48 | return request({ |
| 49 | 49 | url: '/system/menu/delete?id=' + id, |
| 50 | - method: 'post' | |
| 50 | + method: 'delete' | |
| 51 | 51 | }) |
| 52 | 52 | } | ... | ... |
ruoyi-ui/src/api/system/notice.js
| ... | ... | @@ -30,7 +30,7 @@ export function addNotice(data) { |
| 30 | 30 | export function updateNotice(data) { |
| 31 | 31 | return request({ |
| 32 | 32 | url: '/system/notice/update', |
| 33 | - method: 'post', | |
| 33 | + method: 'put', | |
| 34 | 34 | data: data |
| 35 | 35 | }) |
| 36 | 36 | } |
| ... | ... | @@ -39,6 +39,6 @@ export function updateNotice(data) { |
| 39 | 39 | export function delNotice(noticeId) { |
| 40 | 40 | return request({ |
| 41 | 41 | url: '/system/notice/delete?id=' + noticeId, |
| 42 | - method: 'post' | |
| 42 | + method: 'delete' | |
| 43 | 43 | }) |
| 44 | 44 | } | ... | ... |
ruoyi-ui/src/api/system/post.js
| ... | ... | @@ -38,7 +38,7 @@ export function addPost(data) { |
| 38 | 38 | export function updatePost(data) { |
| 39 | 39 | return request({ |
| 40 | 40 | url: '/system/post/update', |
| 41 | - method: 'post', | |
| 41 | + method: 'put', | |
| 42 | 42 | data: data |
| 43 | 43 | }) |
| 44 | 44 | } |
| ... | ... | @@ -47,7 +47,7 @@ export function updatePost(data) { |
| 47 | 47 | export function delPost(postId) { |
| 48 | 48 | return request({ |
| 49 | 49 | url: '/system/post/delete?id=' + postId, |
| 50 | - method: 'post' | |
| 50 | + method: 'delete' | |
| 51 | 51 | }) |
| 52 | 52 | } |
| 53 | 53 | ... | ... |
ruoyi-ui/src/api/system/role.js
| ... | ... | @@ -38,7 +38,7 @@ export function addRole(data) { |
| 38 | 38 | export function updateRole(data) { |
| 39 | 39 | return request({ |
| 40 | 40 | url: '/system/role/update', |
| 41 | - method: 'post', | |
| 41 | + method: 'put', | |
| 42 | 42 | data: data |
| 43 | 43 | }) |
| 44 | 44 | } |
| ... | ... | @@ -51,7 +51,7 @@ export function changeRoleStatus(id, status) { |
| 51 | 51 | } |
| 52 | 52 | return request({ |
| 53 | 53 | url: '/system/role/update-status', |
| 54 | - method: 'post', | |
| 54 | + method: 'put', | |
| 55 | 55 | data: data |
| 56 | 56 | }) |
| 57 | 57 | } |
| ... | ... | @@ -60,7 +60,7 @@ export function changeRoleStatus(id, status) { |
| 60 | 60 | export function delRole(roleId) { |
| 61 | 61 | return request({ |
| 62 | 62 | url: '/system/role/delete?id=' + roleId, |
| 63 | - method: 'post' | |
| 63 | + method: 'delete' | |
| 64 | 64 | }) |
| 65 | 65 | } |
| 66 | 66 | ... | ... |
ruoyi-ui/src/api/system/sms/smsChannel.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// 创建短信渠道 | |
| 4 | +export function createSmsChannel(data) { | |
| 5 | + return request({ | |
| 6 | + url: '/system/sms-channel/create', | |
| 7 | + method: 'post', | |
| 8 | + data: data | |
| 9 | + }) | |
| 10 | +} | |
| 11 | + | |
| 12 | +// 更新短信渠道 | |
| 13 | +export function updateSmsChannel(data) { | |
| 14 | + return request({ | |
| 15 | + url: '/system/sms-channel/update', | |
| 16 | + method: 'put', | |
| 17 | + data: data | |
| 18 | + }) | |
| 19 | +} | |
| 20 | + | |
| 21 | +// 删除短信渠道 | |
| 22 | +export function deleteSmsChannel(id) { | |
| 23 | + return request({ | |
| 24 | + url: '/system/sms-channel/delete?id=' + id, | |
| 25 | + method: 'delete' | |
| 26 | + }) | |
| 27 | +} | |
| 28 | + | |
| 29 | +// 获得短信渠道 | |
| 30 | +export function getSmsChannel(id) { | |
| 31 | + return request({ | |
| 32 | + url: '/system/sms-channel/get?id=' + id, | |
| 33 | + method: 'get' | |
| 34 | + }) | |
| 35 | +} | |
| 36 | + | |
| 37 | +// 获得短信渠道分页 | |
| 38 | +export function getSmsChannelPage(query) { | |
| 39 | + return request({ | |
| 40 | + url: '/system/sms-channel/page', | |
| 41 | + method: 'get', | |
| 42 | + params: query | |
| 43 | + }) | |
| 44 | +} | |
| 45 | + | |
| 46 | +// 获得短信渠道精简列表 | |
| 47 | +export function getSimpleSmsChannels() { | |
| 48 | + return request({ | |
| 49 | + url: '/system/sms-channel/list-all-simple', | |
| 50 | + method: 'get', | |
| 51 | + }) | |
| 52 | +} | ... | ... |
ruoyi-ui/src/api/system/sms/smsLog.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// 获得短信日志分页 | |
| 4 | +export function getSmsLogPage(query) { | |
| 5 | + return request({ | |
| 6 | + url: '/system/sms-log/page', | |
| 7 | + method: 'get', | |
| 8 | + params: query | |
| 9 | + }) | |
| 10 | +} | |
| 11 | + | |
| 12 | +// 导出短信日志 Excel | |
| 13 | +export function exportSmsLogExcel(query) { | |
| 14 | + return request({ | |
| 15 | + url: '/system/sms-log/export-excel', | |
| 16 | + method: 'get', | |
| 17 | + params: query, | |
| 18 | + responseType: 'blob' | |
| 19 | + }) | |
| 20 | +} | ... | ... |
ruoyi-ui/src/api/system/sms/smsTemplate.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// 创建短信模板 | |
| 4 | +export function createSmsTemplate(data) { | |
| 5 | + return request({ | |
| 6 | + url: '/system/sms-template/create', | |
| 7 | + method: 'post', | |
| 8 | + data: data | |
| 9 | + }) | |
| 10 | +} | |
| 11 | + | |
| 12 | +// 更新短信模板 | |
| 13 | +export function updateSmsTemplate(data) { | |
| 14 | + return request({ | |
| 15 | + url: '/system/sms-template/update', | |
| 16 | + method: 'put', | |
| 17 | + data: data | |
| 18 | + }) | |
| 19 | +} | |
| 20 | + | |
| 21 | +// 删除短信模板 | |
| 22 | +export function deleteSmsTemplate(id) { | |
| 23 | + return request({ | |
| 24 | + url: '/system/sms-template/delete?id=' + id, | |
| 25 | + method: 'delete' | |
| 26 | + }) | |
| 27 | +} | |
| 28 | + | |
| 29 | +// 获得短信模板 | |
| 30 | +export function getSmsTemplate(id) { | |
| 31 | + return request({ | |
| 32 | + url: '/system/sms-template/get?id=' + id, | |
| 33 | + method: 'get' | |
| 34 | + }) | |
| 35 | +} | |
| 36 | + | |
| 37 | +// 获得短信模板分页 | |
| 38 | +export function getSmsTemplatePage(query) { | |
| 39 | + return request({ | |
| 40 | + url: '/system/sms-template/page', | |
| 41 | + method: 'get', | |
| 42 | + params: query | |
| 43 | + }) | |
| 44 | +} | |
| 45 | + | |
| 46 | +// 创建短信模板 | |
| 47 | +export function sendSms(data) { | |
| 48 | + return request({ | |
| 49 | + url: '/system/sms-template/send-sms', | |
| 50 | + method: 'post', | |
| 51 | + data: data | |
| 52 | + }) | |
| 53 | +} | |
| 54 | + | |
| 55 | +// 导出短信模板 Excel | |
| 56 | +export function exportSmsTemplateExcel(query) { | |
| 57 | + return request({ | |
| 58 | + url: '/system/sms-template/export-excel', | |
| 59 | + method: 'get', | |
| 60 | + params: query, | |
| 61 | + responseType: 'blob' | |
| 62 | + }) | |
| 63 | +} | |
| 64 | + | ... | ... |
ruoyi-ui/src/api/system/user.js
| ... | ... | @@ -31,7 +31,7 @@ export function addUser(data) { |
| 31 | 31 | export function updateUser(data) { |
| 32 | 32 | return request({ |
| 33 | 33 | url: '/system/user/update', |
| 34 | - method: 'post', | |
| 34 | + method: 'put', | |
| 35 | 35 | data: data |
| 36 | 36 | }) |
| 37 | 37 | } |
| ... | ... | @@ -62,7 +62,7 @@ export function resetUserPwd(id, password) { |
| 62 | 62 | } |
| 63 | 63 | return request({ |
| 64 | 64 | url: '/system/user/update-password', |
| 65 | - method: 'post', | |
| 65 | + method: 'put', | |
| 66 | 66 | data: data |
| 67 | 67 | }) |
| 68 | 68 | } |
| ... | ... | @@ -75,7 +75,7 @@ export function changeUserStatus(id, status) { |
| 75 | 75 | } |
| 76 | 76 | return request({ |
| 77 | 77 | url: '/system/user/update-status', |
| 78 | - method: 'post', | |
| 78 | + method: 'put', | |
| 79 | 79 | data: data |
| 80 | 80 | }) |
| 81 | 81 | } |
| ... | ... | @@ -83,7 +83,7 @@ export function changeUserStatus(id, status) { |
| 83 | 83 | // 查询用户个人信息 |
| 84 | 84 | export function getUserProfile() { |
| 85 | 85 | return request({ |
| 86 | - url: '/system/user/profile', | |
| 86 | + url: '/system/user/profile/get', | |
| 87 | 87 | method: 'get' |
| 88 | 88 | }) |
| 89 | 89 | } |
| ... | ... | @@ -91,7 +91,7 @@ export function getUserProfile() { |
| 91 | 91 | // 修改用户个人信息 |
| 92 | 92 | export function updateUserProfile(data) { |
| 93 | 93 | return request({ |
| 94 | - url: '/system/user/profile', | |
| 94 | + url: '/system/user/profile/update', | |
| 95 | 95 | method: 'put', |
| 96 | 96 | data: data |
| 97 | 97 | }) |
| ... | ... | @@ -104,9 +104,9 @@ export function updateUserPwd(oldPassword, newPassword) { |
| 104 | 104 | newPassword |
| 105 | 105 | } |
| 106 | 106 | return request({ |
| 107 | - url: '/system/user/profile/updatePwd', | |
| 107 | + url: '/system/user/profile/update-password', | |
| 108 | 108 | method: 'put', |
| 109 | - params: data | |
| 109 | + data: data | |
| 110 | 110 | }) |
| 111 | 111 | } |
| 112 | 112 | |
| ... | ... | @@ -114,7 +114,7 @@ export function updateUserPwd(oldPassword, newPassword) { |
| 114 | 114 | export function uploadAvatar(data) { |
| 115 | 115 | return request({ |
| 116 | 116 | url: '/system/user/profile/avatar', |
| 117 | - method: 'post', | |
| 117 | + method: 'put', | |
| 118 | 118 | data: data |
| 119 | 119 | }) |
| 120 | 120 | } | ... | ... |
ruoyi-ui/src/main.js
| ... | ... | @@ -25,7 +25,10 @@ import { |
| 25 | 25 | download, |
| 26 | 26 | handleTree, |
| 27 | 27 | downloadExcel, |
| 28 | - downloadZip | |
| 28 | + downloadWord, | |
| 29 | + downloadZip, | |
| 30 | + downloadHtml, | |
| 31 | + downloadMarkdown, | |
| 29 | 32 | } from "@/utils/ruoyi"; |
| 30 | 33 | import Pagination from "@/components/Pagination"; |
| 31 | 34 | // 自定义表格工具扩展 |
| ... | ... | @@ -48,6 +51,9 @@ Vue.prototype.getDictDataLabel = getDictDataLabel |
| 48 | 51 | Vue.prototype.DICT_TYPE = DICT_TYPE |
| 49 | 52 | Vue.prototype.download = download |
| 50 | 53 | Vue.prototype.downloadExcel = downloadExcel |
| 54 | +Vue.prototype.downloadWord = downloadWord | |
| 55 | +Vue.prototype.downloadHtml = downloadHtml | |
| 56 | +Vue.prototype.downloadMarkdown = downloadMarkdown | |
| 51 | 57 | Vue.prototype.downloadZip = downloadZip |
| 52 | 58 | Vue.prototype.handleTree = handleTree |
| 53 | 59 | ... | ... |
ruoyi-ui/src/store/modules/user.js
| ... | ... | @@ -53,7 +53,7 @@ const user = { |
| 53 | 53 | getInfo(state.token).then(res => { |
| 54 | 54 | res = res.data; // 读取 data 数据 |
| 55 | 55 | const user = res.user |
| 56 | - const avatar = user.avatar === "" ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar; | |
| 56 | + const avatar = user.avatar === "" ? require("@/assets/images/profile.jpg") : user.avatar; | |
| 57 | 57 | if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组 |
| 58 | 58 | commit('SET_ROLES', res.roles) |
| 59 | 59 | commit('SET_PERMISSIONS', res.permissions) | ... | ... |
ruoyi-ui/src/utils/dict.js
| ... | ... | @@ -17,6 +17,10 @@ export const DICT_TYPE = { |
| 17 | 17 | SYS_OPERATE_TYPE: 'sys_operate_type', |
| 18 | 18 | SYS_LOGIN_RESULT: 'sys_login_result', |
| 19 | 19 | SYS_CONFIG_TYPE: 'sys_config_type', |
| 20 | + SYS_SMS_CHANNEL_CODE: 'sys_sms_channel_code', | |
| 21 | + SYS_SMS_TEMPLATE_TYPE: 'sys_sms_template_type', | |
| 22 | + SYS_SMS_SEND_STATUS: 'sys_sms_send_status', | |
| 23 | + SYS_SMS_RECEIVE_STATUS: 'sys_sms_receive_status', | |
| 20 | 24 | |
| 21 | 25 | INF_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type', |
| 22 | 26 | INF_JOB_STATUS: 'inf_job_status', | ... | ... |
ruoyi-ui/src/utils/request.js
| ... | ... | @@ -73,6 +73,18 @@ service.interceptors.response.use(res => { |
| 73 | 73 | type: 'error' |
| 74 | 74 | }) |
| 75 | 75 | return Promise.reject(new Error(msg)) |
| 76 | + } else if (code === 901) { | |
| 77 | + Message({ | |
| 78 | + type: 'error', | |
| 79 | + duration: 0, | |
| 80 | + dangerouslyUseHTMLString: true, | |
| 81 | + message: '<div>演示模式,不发进行写操作</div>' | |
| 82 | + + '<div> </div>' | |
| 83 | + + '<div>参考 https://www.iocoder.cn/Yudao/build-debugger-environment 教程</div>' | |
| 84 | + + '<div> </div>' | |
| 85 | + + '<div>5 分钟搭建本地环境</div>', | |
| 86 | + }) | |
| 87 | + return Promise.reject(new Error(msg)) | |
| 76 | 88 | } else if (code !== 200) { |
| 77 | 89 | Notification.error({ |
| 78 | 90 | title: msg | ... | ... |
ruoyi-ui/src/utils/ruoyi.js
| ... | ... | @@ -120,11 +120,26 @@ export function downloadExcel(data, fileName) { |
| 120 | 120 | download0(data, fileName, 'application/vnd.ms-excel'); |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | +// 下载 Word 方法 | |
| 124 | +export function downloadWord(data, fileName) { | |
| 125 | + download0(data, fileName, 'application/msword'); | |
| 126 | +} | |
| 127 | + | |
| 123 | 128 | // 下载 Zip 方法 |
| 124 | 129 | export function downloadZip(data, fileName) { |
| 125 | 130 | download0(data, fileName, 'application/zip'); |
| 126 | 131 | } |
| 127 | 132 | |
| 133 | +// 下载 Html 方法 | |
| 134 | +export function downloadHtml(data, fileName) { | |
| 135 | + download0(data, fileName, 'text/html'); | |
| 136 | +} | |
| 137 | + | |
| 138 | +// 下载 Markdown 方法 | |
| 139 | +export function downloadMarkdown(data, fileName) { | |
| 140 | + download0(data, fileName, 'text/markdown'); | |
| 141 | +} | |
| 142 | + | |
| 128 | 143 | function download0(data, fileName, mineType) { |
| 129 | 144 | // 创建 blob |
| 130 | 145 | let blob = new Blob([data], {type: mineType}); | ... | ... |
ruoyi-ui/src/views/infra/config/index.vue
| ... | ... | @@ -2,24 +2,12 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| 4 | 4 | <el-form-item label="参数名称" prop="name"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.name" | |
| 7 | - placeholder="请输入参数名称" | |
| 8 | - clearable | |
| 9 | - size="small" | |
| 10 | - style="width: 240px" | |
| 11 | - @keyup.enter.native="handleQuery" | |
| 12 | - /> | |
| 5 | + <el-input v-model="queryParams.name" placeholder="请输入参数名称" clearable size="small" style="width: 240px" | |
| 6 | + @keyup.enter.native="handleQuery"/> | |
| 13 | 7 | </el-form-item> |
| 14 | 8 | <el-form-item label="参数键名" prop="key"> |
| 15 | - <el-input | |
| 16 | - v-model="queryParams.key" | |
| 17 | - placeholder="请输入参数键名" | |
| 18 | - clearable | |
| 19 | - size="small" | |
| 20 | - style="width: 240px" | |
| 21 | - @keyup.enter.native="handleQuery" | |
| 22 | - /> | |
| 9 | + <el-input v-model="queryParams.key" placeholder="请输入参数键名" clearable size="small" style="width: 240px" | |
| 10 | + @keyup.enter.native="handleQuery"/> | |
| 23 | 11 | </el-form-item> |
| 24 | 12 | <el-form-item label="系统内置" prop="type"> |
| 25 | 13 | <el-select v-model="queryParams.type" placeholder="系统内置" clearable size="small"> |
| ... | ... | @@ -56,7 +44,7 @@ |
| 56 | 44 | icon="el-icon-plus" |
| 57 | 45 | size="mini" |
| 58 | 46 | @click="handleAdd" |
| 59 | - v-hasPermi="['infra:config:add']" | |
| 47 | + v-hasPermi="['infra:config:create']" | |
| 60 | 48 | >新增</el-button> |
| 61 | 49 | </el-col> |
| 62 | 50 | <el-col :span="1.5"> |
| ... | ... | @@ -95,31 +83,15 @@ |
| 95 | 83 | </el-table-column> |
| 96 | 84 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 97 | 85 | <template slot-scope="scope"> |
| 98 | - <el-button | |
| 99 | - size="mini" | |
| 100 | - type="text" | |
| 101 | - icon="el-icon-edit" | |
| 102 | - @click="handleUpdate(scope.row)" | |
| 103 | - v-hasPermi="['infra:config:edit']" | |
| 104 | - >修改</el-button> | |
| 105 | - <el-button | |
| 106 | - size="mini" | |
| 107 | - type="text" | |
| 108 | - icon="el-icon-delete" | |
| 109 | - @click="handleDelete(scope.row)" | |
| 110 | - v-hasPermi="['infra:config:remove']" | |
| 111 | - >删除</el-button> | |
| 86 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 87 | + v-hasPermi="['infra:config:update']">修改</el-button> | |
| 88 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 89 | + v-hasPermi="['infra:config:delete']">删除</el-button> | |
| 112 | 90 | </template> |
| 113 | 91 | </el-table-column> |
| 114 | 92 | </el-table> |
| 115 | 93 | |
| 116 | - <pagination | |
| 117 | - v-show="total>0" | |
| 118 | - :total="total" | |
| 119 | - :page.sync="queryParams.pageNo" | |
| 120 | - :limit.sync="queryParams.pageSize" | |
| 121 | - @pagination="getList" | |
| 122 | - /> | |
| 94 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" @pagination="getList"/> | |
| 123 | 95 | |
| 124 | 96 | <!-- 添加或修改参数配置对话框 --> |
| 125 | 97 | <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | ... | ... |
ruoyi-ui/src/views/infra/file/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="app-container"> | |
| 3 | + | |
| 4 | + <!-- 搜索工作栏 --> | |
| 5 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> | |
| 6 | + <el-form-item label="文件路径" prop="id"> | |
| 7 | + <el-input v-model="queryParams.id" placeholder="请输入文件路径" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 8 | + </el-form-item> | |
| 9 | + <el-form-item label="文件类型" prop="type"> | |
| 10 | + <el-select v-model="queryParams.type" placeholder="请选择文件类型" clearable size="small"> | |
| 11 | + <el-option label="请选择字典生成" value="" /> | |
| 12 | + </el-select> | |
| 13 | + </el-form-item> | |
| 14 | + <el-form-item label="创建时间"> | |
| 15 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 16 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 17 | + </el-form-item> | |
| 18 | + <el-form-item> | |
| 19 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |
| 20 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |
| 21 | + </el-form-item> | |
| 22 | + </el-form> | |
| 23 | + | |
| 24 | + <!-- 操作工具栏 --> | |
| 25 | + <el-row :gutter="10" class="mb8"> | |
| 26 | + <el-col :span="1.5"> | |
| 27 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">上传文件</el-button> | |
| 28 | + </el-col> | |
| 29 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |
| 30 | + </el-row> | |
| 31 | + | |
| 32 | + <!-- 列表 --> | |
| 33 | + <el-table v-loading="loading" :data="list"> | |
| 34 | + <el-table-column label="文件路径" align="center" prop="id" width="300" /> | |
| 35 | + <el-table-column label="文件类型" align="center" prop="type" width="80" /> | |
| 36 | + <el-table-column label="文件内容" align="center" prop="content"> | |
| 37 | + <template slot-scope="scope"> | |
| 38 | + <img v-if="scope.row.type === 'jpg' || scope.row.type === 'png' || scope.row.type === 'gif'" | |
| 39 | + width="200px" :src="getFileUrl + scope.row.id"> | |
| 40 | + <i v-else>非图片,无法预览</i> | |
| 41 | + </template> | |
| 42 | + </el-table-column> | |
| 43 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | |
| 44 | + <template slot-scope="scope"> | |
| 45 | + <span>{{ parseTime(scope.row.createTime) }}</span> | |
| 46 | + </template> | |
| 47 | + </el-table-column> | |
| 48 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | |
| 49 | + <template slot-scope="scope"> | |
| 50 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 51 | + v-hasPermi="['infra:file:delete']">删除</el-button> | |
| 52 | + </template> | |
| 53 | + </el-table-column> | |
| 54 | + </el-table> | |
| 55 | + <!-- 分页组件 --> | |
| 56 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 57 | + @pagination="getList"/> | |
| 58 | + | |
| 59 | + <!-- 对话框(添加 / 修改) --> | |
| 60 | + <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body> | |
| 61 | + <el-upload ref="upload" :limit="1" accept=".jpg, .png, .gif" :auto-upload="false" drag | |
| 62 | + :headers="upload.headers" :action="upload.url" :data="upload.data" :disabled="upload.isUploading" | |
| 63 | + :on-change="handleFileChange" | |
| 64 | + :on-progress="handleFileUploadProgress" | |
| 65 | + :on-success="handleFileSuccess"> | |
| 66 | + <i class="el-icon-upload"></i> | |
| 67 | + <div class="el-upload__text"> | |
| 68 | + 将文件拖到此处,或 <em>点击上传</em> | |
| 69 | + </div> | |
| 70 | + <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入 jpg、png、gif 格式文件!</div> | |
| 71 | + </el-upload> | |
| 72 | + <div slot="footer" class="dialog-footer"> | |
| 73 | + <el-button type="primary" @click="submitFileForm">确 定</el-button> | |
| 74 | + <el-button @click="upload.open = false">取 消</el-button> | |
| 75 | + </div> | |
| 76 | + </el-dialog> | |
| 77 | + | |
| 78 | + </div> | |
| 79 | +</template> | |
| 80 | + | |
| 81 | +<script> | |
| 82 | +import { deleteFile, getFilePage } from "@/api/infra/file"; | |
| 83 | +import {getToken} from "@/utils/auth"; | |
| 84 | + | |
| 85 | +export default { | |
| 86 | + name: "File", | |
| 87 | + data() { | |
| 88 | + return { | |
| 89 | + getFileUrl: process.env.VUE_APP_BASE_API + '/api/infra/file/get/', | |
| 90 | + // 遮罩层 | |
| 91 | + loading: true, | |
| 92 | + // 显示搜索条件 | |
| 93 | + showSearch: true, | |
| 94 | + // 总条数 | |
| 95 | + total: 0, | |
| 96 | + // 文件列表 | |
| 97 | + list: [], | |
| 98 | + // 弹出层标题 | |
| 99 | + title: "", | |
| 100 | + dateRangeCreateTime: [], | |
| 101 | + // 查询参数 | |
| 102 | + queryParams: { | |
| 103 | + pageNo: 1, | |
| 104 | + pageSize: 10, | |
| 105 | + id: null, | |
| 106 | + type: null, | |
| 107 | + }, | |
| 108 | + // 用户导入参数 | |
| 109 | + upload: { | |
| 110 | + open: false, // 是否显示弹出层 | |
| 111 | + title: "", // 弹出层标题 | |
| 112 | + isUploading: false, // 是否禁用上传 | |
| 113 | + url: process.env.VUE_APP_BASE_API + '/api/' + "/infra/file/upload", // 请求地址 | |
| 114 | + headers: { Authorization: "Bearer " + getToken() }, // 设置上传的请求头部 | |
| 115 | + data: {} // 上传的额外数据,用于文件名 | |
| 116 | + }, | |
| 117 | + }; | |
| 118 | + }, | |
| 119 | + created() { | |
| 120 | + this.getList(); | |
| 121 | + }, | |
| 122 | + methods: { | |
| 123 | + /** 查询列表 */ | |
| 124 | + getList() { | |
| 125 | + this.loading = true; | |
| 126 | + // 处理查询参数 | |
| 127 | + let params = {...this.queryParams}; | |
| 128 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | |
| 129 | + // 执行查询 | |
| 130 | + getFilePage(params).then(response => { | |
| 131 | + this.list = response.data.list; | |
| 132 | + this.total = response.data.total; | |
| 133 | + this.loading = false; | |
| 134 | + }); | |
| 135 | + }, | |
| 136 | + /** 取消按钮 */ | |
| 137 | + cancel() { | |
| 138 | + this.open = false; | |
| 139 | + this.reset(); | |
| 140 | + }, | |
| 141 | + /** 表单重置 */ | |
| 142 | + reset() { | |
| 143 | + this.form = { | |
| 144 | + content: undefined, | |
| 145 | + }; | |
| 146 | + this.resetForm("form"); | |
| 147 | + }, | |
| 148 | + /** 搜索按钮操作 */ | |
| 149 | + handleQuery() { | |
| 150 | + this.queryParams.pageNo = 1; | |
| 151 | + this.getList(); | |
| 152 | + }, | |
| 153 | + /** 重置按钮操作 */ | |
| 154 | + resetQuery() { | |
| 155 | + this.dateRangeCreateTime = []; | |
| 156 | + this.resetForm("queryForm"); | |
| 157 | + this.handleQuery(); | |
| 158 | + }, | |
| 159 | + /** 新增按钮操作 */ | |
| 160 | + handleAdd() { | |
| 161 | + this.upload.open = true; | |
| 162 | + this.upload.title = "上传文件"; | |
| 163 | + }, | |
| 164 | + /** 处理上传的文件发生变化 */ | |
| 165 | + handleFileChange(file, fileList) { | |
| 166 | + this.upload.data.path = file.name; | |
| 167 | + }, | |
| 168 | + /** 处理文件上传中 */ | |
| 169 | + handleFileUploadProgress(event, file, fileList) { | |
| 170 | + this.upload.isUploading = true; // 禁止修改 | |
| 171 | + }, | |
| 172 | + /** 发起文件上窜 */ | |
| 173 | + submitFileForm() { | |
| 174 | + this.$refs.upload.submit(); | |
| 175 | + }, | |
| 176 | + /** 文件上传成功处理 */ | |
| 177 | + handleFileSuccess(response, file, fileList) { | |
| 178 | + // 清理 | |
| 179 | + this.upload.open = false; | |
| 180 | + this.upload.isUploading = false; | |
| 181 | + this.$refs.upload.clearFiles(); | |
| 182 | + // 提示成功,并刷新 | |
| 183 | + this.msgSuccess("上传成功"); | |
| 184 | + this.getList(); | |
| 185 | + }, | |
| 186 | + /** 删除按钮操作 */ | |
| 187 | + handleDelete(row) { | |
| 188 | + const id = row.id; | |
| 189 | + this.$confirm('是否确认删除文件编号为"' + id + '"的数据项?', "警告", { | |
| 190 | + confirmButtonText: "确定", | |
| 191 | + cancelButtonText: "取消", | |
| 192 | + type: "warning" | |
| 193 | + }).then(function() { | |
| 194 | + return deleteFile(id); | |
| 195 | + }).then(() => { | |
| 196 | + this.getList(); | |
| 197 | + this.msgSuccess("删除成功"); | |
| 198 | + }) | |
| 199 | + }, | |
| 200 | + } | |
| 201 | +}; | |
| 202 | +</script> | ... | ... |
ruoyi-ui/src/views/system/dept/index.vue
| ... | ... | @@ -2,22 +2,11 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch"> |
| 4 | 4 | <el-form-item label="部门名称" prop="name"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.name" | |
| 7 | - placeholder="请输入部门名称" | |
| 8 | - clearable | |
| 9 | - size="small" | |
| 10 | - @keyup.enter.native="handleQuery" | |
| 11 | - /> | |
| 5 | + <el-input v-model="queryParams.name" placeholder="请输入部门名称" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 12 | 6 | </el-form-item> |
| 13 | 7 | <el-form-item label="状态" prop="status"> |
| 14 | 8 | <el-select v-model="queryParams.status" placeholder="菜单状态" clearable size="small"> |
| 15 | - <el-option | |
| 16 | - v-for="dict in statusDictDatas" | |
| 17 | - :key="parseInt(dict.value)" | |
| 18 | - :label="dict.label" | |
| 19 | - :value="parseInt(dict.value)" | |
| 20 | - /> | |
| 9 | + <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 21 | 10 | </el-select> |
| 22 | 11 | </el-form-item> |
| 23 | 12 | <el-form-item> |
| ... | ... | @@ -28,24 +17,13 @@ |
| 28 | 17 | |
| 29 | 18 | <el-row :gutter="10" class="mb8"> |
| 30 | 19 | <el-col :span="1.5"> |
| 31 | - <el-button | |
| 32 | - type="primary" | |
| 33 | - icon="el-icon-plus" | |
| 34 | - size="mini" | |
| 35 | - @click="handleAdd" | |
| 36 | - v-hasPermi="['system:dept:add']" | |
| 37 | - >新增</el-button> | |
| 20 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:dept:create']">新增</el-button> | |
| 38 | 21 | </el-col> |
| 39 | 22 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 40 | 23 | </el-row> |
| 41 | 24 | |
| 42 | - <el-table | |
| 43 | - v-loading="loading" | |
| 44 | - :data="deptList" | |
| 45 | - row-key="id" | |
| 46 | - default-expand-all | |
| 47 | - :tree-props="{children: 'children', hasChildren: 'hasChildren'}" | |
| 48 | - > | |
| 25 | + <el-table v-loading="loading" :data="deptList" row-key="id" default-expand-all | |
| 26 | + :tree-props="{children: 'children', hasChildren: 'hasChildren'}"> | |
| 49 | 27 | <el-table-column prop="name" label="部门名称" width="260"></el-table-column> |
| 50 | 28 | <el-table-column prop="sort" label="排序" width="200"></el-table-column> |
| 51 | 29 | <el-table-column prop="status" label="状态" :formatter="statusFormat" width="100"></el-table-column> |
| ... | ... | @@ -56,28 +34,12 @@ |
| 56 | 34 | </el-table-column> |
| 57 | 35 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 58 | 36 | <template slot-scope="scope"> |
| 59 | - <el-button | |
| 60 | - size="mini" | |
| 61 | - type="text" | |
| 62 | - icon="el-icon-edit" | |
| 63 | - @click="handleUpdate(scope.row)" | |
| 64 | - v-hasPermi="['system:dept:edit']" | |
| 65 | - >修改</el-button> | |
| 66 | - <el-button | |
| 67 | - size="mini" | |
| 68 | - type="text" | |
| 69 | - icon="el-icon-plus" | |
| 70 | - @click="handleAdd(scope.row)" | |
| 71 | - v-hasPermi="['system:dept:add']" | |
| 72 | - >新增</el-button> | |
| 73 | - <el-button | |
| 74 | - v-if="scope.row.parentId !== 0" | |
| 75 | - size="mini" | |
| 76 | - type="text" | |
| 77 | - icon="el-icon-delete" | |
| 78 | - @click="handleDelete(scope.row)" | |
| 79 | - v-hasPermi="['system:dept:remove']" | |
| 80 | - >删除</el-button> | |
| 37 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 38 | + v-hasPermi="['system:dept:update']">修改</el-button> | |
| 39 | + <el-button size="mini" type="text" icon="el-icon-plus" @click="handleAdd(scope.row)" | |
| 40 | + v-hasPermi="['system:dept:create']">新增</el-button> | |
| 41 | + <el-button v-if="scope.row.parentId !== 0" size="mini" type="text" icon="el-icon-delete" | |
| 42 | + @click="handleDelete(scope.row)" v-hasPermi="['system:dept:delete']">删除</el-button> | |
| 81 | 43 | </template> |
| 82 | 44 | </el-table-column> |
| 83 | 45 | </el-table> |
| ... | ... | @@ -119,11 +81,8 @@ |
| 119 | 81 | <el-col :span="12"> |
| 120 | 82 | <el-form-item label="部门状态" prop="status"> |
| 121 | 83 | <el-radio-group v-model="form.status"> |
| 122 | - <el-radio | |
| 123 | - v-for="dict in statusDictDatas" | |
| 124 | - :key="parseInt(dict.value)" | |
| 125 | - :label="parseInt(dict.value)" | |
| 126 | - >{{dict.label}}</el-radio> | |
| 84 | + <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)"> | |
| 85 | + {{dict.label}}</el-radio> | |
| 127 | 86 | </el-radio-group> |
| 128 | 87 | </el-form-item> |
| 129 | 88 | </el-col> | ... | ... |
ruoyi-ui/src/views/system/dict/data.vue
| ... | ... | @@ -3,31 +3,15 @@ |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| 4 | 4 | <el-form-item label="字典名称" prop="dictType"> |
| 5 | 5 | <el-select v-model="queryParams.dictType" size="small"> |
| 6 | - <el-option | |
| 7 | - v-for="item in typeOptions" | |
| 8 | - :key="item.id" | |
| 9 | - :label="item.name" | |
| 10 | - :value="item.type" | |
| 11 | - /> | |
| 6 | + <el-option v-for="item in typeOptions" :key="item.id" :label="item.name" :value="item.type"/> | |
| 12 | 7 | </el-select> |
| 13 | 8 | </el-form-item> |
| 14 | 9 | <el-form-item label="字典标签" prop="label"> |
| 15 | - <el-input | |
| 16 | - v-model="queryParams.label" | |
| 17 | - placeholder="请输入字典标签" | |
| 18 | - clearable | |
| 19 | - size="small" | |
| 20 | - @keyup.enter.native="handleQuery" | |
| 21 | - /> | |
| 10 | + <el-input v-model="queryParams.label" placeholder="请输入字典标签" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 22 | 11 | </el-form-item> |
| 23 | 12 | <el-form-item label="状态" prop="status"> |
| 24 | 13 | <el-select v-model="queryParams.status" placeholder="数据状态" clearable size="small"> |
| 25 | - <el-option | |
| 26 | - v-for="dict in statusOptions" | |
| 27 | - :key="dict.value" | |
| 28 | - :label="dict.label" | |
| 29 | - :value="dict.value" | |
| 30 | - /> | |
| 14 | + <el-option v-for="dict in statusOptions" :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 31 | 15 | </el-select> |
| 32 | 16 | </el-form-item> |
| 33 | 17 | <el-form-item> |
| ... | ... | @@ -38,22 +22,12 @@ |
| 38 | 22 | |
| 39 | 23 | <el-row :gutter="10" class="mb8"> |
| 40 | 24 | <el-col :span="1.5"> |
| 41 | - <el-button | |
| 42 | - type="primary" | |
| 43 | - icon="el-icon-plus" | |
| 44 | - size="mini" | |
| 45 | - @click="handleAdd" | |
| 46 | - v-hasPermi="['system:dict:add']" | |
| 47 | - >新增</el-button> | |
| 25 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 26 | + v-hasPermi="['system:dict:create']">新增</el-button> | |
| 48 | 27 | </el-col> |
| 49 | 28 | <el-col :span="1.5"> |
| 50 | - <el-button | |
| 51 | - type="warning" | |
| 52 | - icon="el-icon-download" | |
| 53 | - size="mini" | |
| 54 | - @click="handleExport" | |
| 55 | - v-hasPermi="['system:dict:export']" | |
| 56 | - >导出</el-button> | |
| 29 | + <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" | |
| 30 | + v-hasPermi="['system:dict:export']">导出</el-button> | |
| 57 | 31 | </el-col> |
| 58 | 32 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 59 | 33 | </el-row> |
| ... | ... | @@ -72,31 +46,16 @@ |
| 72 | 46 | </el-table-column> |
| 73 | 47 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 74 | 48 | <template slot-scope="scope"> |
| 75 | - <el-button | |
| 76 | - size="mini" | |
| 77 | - type="text" | |
| 78 | - icon="el-icon-edit" | |
| 79 | - @click="handleUpdate(scope.row)" | |
| 80 | - v-hasPermi="['system:dict:edit']" | |
| 81 | - >修改</el-button> | |
| 82 | - <el-button | |
| 83 | - size="mini" | |
| 84 | - type="text" | |
| 85 | - icon="el-icon-delete" | |
| 86 | - @click="handleDelete(scope.row)" | |
| 87 | - v-hasPermi="['system:dict:remove']" | |
| 88 | - >删除</el-button> | |
| 49 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 50 | + v-hasPermi="['system:dict:update']">修改</el-button> | |
| 51 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 52 | + v-hasPermi="['system:dict:delete']">删除</el-button> | |
| 89 | 53 | </template> |
| 90 | 54 | </el-table-column> |
| 91 | 55 | </el-table> |
| 92 | 56 | |
| 93 | - <pagination | |
| 94 | - v-show="total>0" | |
| 95 | - :total="total" | |
| 96 | - :page.sync="queryParams.pageNo" | |
| 97 | - :limit.sync="queryParams.pageSize" | |
| 98 | - @pagination="getList" | |
| 99 | - /> | |
| 57 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 58 | + @pagination="getList"/> | |
| 100 | 59 | |
| 101 | 60 | <!-- 添加或修改参数配置对话框 --> |
| 102 | 61 | <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> |
| ... | ... | @@ -115,11 +74,7 @@ |
| 115 | 74 | </el-form-item> |
| 116 | 75 | <el-form-item label="状态" prop="status"> |
| 117 | 76 | <el-radio-group v-model="form.status"> |
| 118 | - <el-radio | |
| 119 | - v-for="dict in statusDictDatas" | |
| 120 | - :key="parseInt(dict.value)" | |
| 121 | - :label="parseInt(dict.value)" | |
| 122 | - >{{dict.label}}</el-radio> | |
| 77 | + <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | |
| 123 | 78 | </el-radio-group> |
| 124 | 79 | </el-form-item> |
| 125 | 80 | <el-form-item label="备注" prop="remark"> | ... | ... |
ruoyi-ui/src/views/system/dict/index.vue
| ... | ... | @@ -2,52 +2,19 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| 4 | 4 | <el-form-item label="字典名称" prop="name"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.name" | |
| 7 | - placeholder="请输入字典名称" | |
| 8 | - clearable | |
| 9 | - size="small" | |
| 10 | - style="width: 240px" | |
| 11 | - @keyup.enter.native="handleQuery" | |
| 12 | - /> | |
| 5 | + <el-input v-model="queryParams.name" placeholder="请输入字典名称" clearable size="small" style="width: 240px" @keyup.enter.native="handleQuery"/> | |
| 13 | 6 | </el-form-item> |
| 14 | 7 | <el-form-item label="字典类型" prop="type"> |
| 15 | - <el-input | |
| 16 | - v-model="queryParams.type" | |
| 17 | - placeholder="请输入字典类型" | |
| 18 | - clearable | |
| 19 | - size="small" | |
| 20 | - style="width: 240px" | |
| 21 | - @keyup.enter.native="handleQuery" | |
| 22 | - /> | |
| 8 | + <el-input v-model="queryParams.type" placeholder="请输入字典类型" clearable size="small" style="width: 240px" @keyup.enter.native="handleQuery"/> | |
| 23 | 9 | </el-form-item> |
| 24 | 10 | <el-form-item label="状态" prop="status"> |
| 25 | - <el-select | |
| 26 | - v-model="queryParams.status" | |
| 27 | - placeholder="字典状态" | |
| 28 | - clearable | |
| 29 | - size="small" | |
| 30 | - style="width: 240px" | |
| 31 | - > | |
| 32 | - <el-option | |
| 33 | - v-for="dict in statusDictDatas" | |
| 34 | - :key="parseInt(dict.value)" | |
| 35 | - :label="dict.label" | |
| 36 | - :value="parseInt(dict.value)" | |
| 37 | - /> | |
| 11 | + <el-select v-model="queryParams.status" placeholder="字典状态" clearable size="small" style="width: 240px"> | |
| 12 | + <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 38 | 13 | </el-select> |
| 39 | 14 | </el-form-item> |
| 40 | 15 | <el-form-item label="创建时间"> |
| 41 | - <el-date-picker | |
| 42 | - v-model="dateRangeCreateTime" | |
| 43 | - size="small" | |
| 44 | - style="width: 240px" | |
| 45 | - value-format="yyyy-MM-dd" | |
| 46 | - type="daterange" | |
| 47 | - range-separator="-" | |
| 48 | - start-placeholder="开始日期" | |
| 49 | - end-placeholder="结束日期" | |
| 50 | - ></el-date-picker> | |
| 16 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" | |
| 17 | + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> | |
| 51 | 18 | </el-form-item> |
| 52 | 19 | <el-form-item> |
| 53 | 20 | <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
| ... | ... | @@ -57,22 +24,12 @@ |
| 57 | 24 | |
| 58 | 25 | <el-row :gutter="10" class="mb8"> |
| 59 | 26 | <el-col :span="1.5"> |
| 60 | - <el-button | |
| 61 | - type="primary" | |
| 62 | - icon="el-icon-plus" | |
| 63 | - size="mini" | |
| 64 | - @click="handleAdd" | |
| 65 | - v-hasPermi="['system:dict:add']" | |
| 66 | - >新增</el-button> | |
| 27 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 28 | + v-hasPermi="['system:dict:create']">新增</el-button> | |
| 67 | 29 | </el-col> |
| 68 | 30 | <el-col :span="1.5"> |
| 69 | - <el-button | |
| 70 | - type="warning" | |
| 71 | - icon="el-icon-download" | |
| 72 | - size="mini" | |
| 73 | - @click="handleExport" | |
| 74 | - v-hasPermi="['system:dict:export']" | |
| 75 | - >导出</el-button> | |
| 31 | + <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" | |
| 32 | + v-hasPermi="['system:dict:export']">导出</el-button> | |
| 76 | 33 | </el-col> |
| 77 | 34 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 78 | 35 | </el-row> |
| ... | ... | @@ -96,31 +53,16 @@ |
| 96 | 53 | </el-table-column> |
| 97 | 54 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 98 | 55 | <template slot-scope="scope"> |
| 99 | - <el-button | |
| 100 | - size="mini" | |
| 101 | - type="text" | |
| 102 | - icon="el-icon-edit" | |
| 103 | - @click="handleUpdate(scope.row)" | |
| 104 | - v-hasPermi="['system:dict:edit']" | |
| 105 | - >修改</el-button> | |
| 106 | - <el-button | |
| 107 | - size="mini" | |
| 108 | - type="text" | |
| 109 | - icon="el-icon-delete" | |
| 110 | - @click="handleDelete(scope.row)" | |
| 111 | - v-hasPermi="['system:dict:remove']" | |
| 112 | - >删除</el-button> | |
| 56 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 57 | + v-hasPermi="['system:dict:update']">修改</el-button> | |
| 58 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 59 | + v-hasPermi="['system:dict:delete']">删除</el-button> | |
| 113 | 60 | </template> |
| 114 | 61 | </el-table-column> |
| 115 | 62 | </el-table> |
| 116 | 63 | |
| 117 | - <pagination | |
| 118 | - v-show="total>0" | |
| 119 | - :total="total" | |
| 120 | - :page.sync="queryParams.pageNo" | |
| 121 | - :limit.sync="queryParams.pageSize" | |
| 122 | - @pagination="getList" | |
| 123 | - /> | |
| 64 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 65 | + @pagination="getList"/> | |
| 124 | 66 | |
| 125 | 67 | <!-- 添加或修改参数配置对话框 --> |
| 126 | 68 | <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> |
| ... | ... | @@ -133,11 +75,7 @@ |
| 133 | 75 | </el-form-item> |
| 134 | 76 | <el-form-item label="状态" prop="status"> |
| 135 | 77 | <el-radio-group v-model="form.status"> |
| 136 | - <el-radio | |
| 137 | - v-for="dict in statusDictDatas" | |
| 138 | - :key="parseInt(dict.value)" | |
| 139 | - :label="parseInt(dict.value)" | |
| 140 | - >{{dict.label}}</el-radio> | |
| 78 | + <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | |
| 141 | 79 | </el-radio-group> |
| 142 | 80 | </el-form-item> |
| 143 | 81 | <el-form-item label="备注" prop="remark"> | ... | ... |
ruoyi-ui/src/views/system/loginlog/index.vue
| ... | ... | @@ -2,56 +2,22 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| 4 | 4 | <el-form-item label="登录地址" prop="userIp"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.userIp" | |
| 7 | - placeholder="请输入登录地址" | |
| 8 | - clearable | |
| 9 | - style="width: 240px;" | |
| 10 | - size="small" | |
| 11 | - @keyup.enter.native="handleQuery" | |
| 12 | - /> | |
| 5 | + <el-input v-model="queryParams.userIp" placeholder="请输入登录地址" clearable style="width: 240px;" size="small" | |
| 6 | + @keyup.enter.native="handleQuery"/> | |
| 13 | 7 | </el-form-item> |
| 14 | 8 | <el-form-item label="用户名称" prop="username"> |
| 15 | - <el-input | |
| 16 | - v-model="queryParams.username" | |
| 17 | - placeholder="请输入用户名称" | |
| 18 | - clearable | |
| 19 | - style="width: 240px;" | |
| 20 | - size="small" | |
| 21 | - @keyup.enter.native="handleQuery" | |
| 22 | - /> | |
| 9 | + <el-input v-model="queryParams.username" placeholder="请输入用户名称" clearable style="width: 240px;" size="small" | |
| 10 | + @keyup.enter.native="handleQuery"/> | |
| 23 | 11 | </el-form-item> |
| 24 | 12 | <el-form-item label="状态" prop="status"> |
| 25 | - <el-select | |
| 26 | - v-model="queryParams.status" | |
| 27 | - placeholder="结果" | |
| 28 | - clearable | |
| 29 | - size="small" | |
| 30 | - style="width: 240px" | |
| 31 | - > | |
| 32 | - <el-option | |
| 33 | - :key="true" | |
| 34 | - label="成功" | |
| 35 | - :value="true" | |
| 36 | - /> | |
| 37 | - <el-option | |
| 38 | - :key="false" | |
| 39 | - label="失败" | |
| 40 | - :value="false" | |
| 41 | - /> | |
| 13 | + <el-select v-model="queryParams.status" placeholder="结果" clearable size="small" style="width: 240px"> | |
| 14 | + <el-option :key="true" label="成功" :value="true"/> | |
| 15 | + <el-option :key="false" label="失败" :value="false"/> | |
| 42 | 16 | </el-select> |
| 43 | 17 | </el-form-item> |
| 44 | 18 | <el-form-item label="登录时间"> |
| 45 | - <el-date-picker | |
| 46 | - v-model="dateRange" | |
| 47 | - size="small" | |
| 48 | - style="width: 240px" | |
| 49 | - value-format="yyyy-MM-dd" | |
| 50 | - type="daterange" | |
| 51 | - range-separator="-" | |
| 52 | - start-placeholder="开始日期" | |
| 53 | - end-placeholder="结束日期" | |
| 54 | - ></el-date-picker> | |
| 19 | + <el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 20 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> | |
| 55 | 21 | </el-form-item> |
| 56 | 22 | <el-form-item> |
| 57 | 23 | <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
| ... | ... | @@ -61,13 +27,8 @@ |
| 61 | 27 | |
| 62 | 28 | <el-row :gutter="10" class="mb8"> |
| 63 | 29 | <el-col :span="1.5"> |
| 64 | - <el-button | |
| 65 | - type="warning" | |
| 66 | - icon="el-icon-download" | |
| 67 | - size="mini" | |
| 68 | - @click="handleExport" | |
| 69 | - v-hasPermi="['system:login-log:export']" | |
| 70 | - >导出</el-button> | |
| 30 | + <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" | |
| 31 | + v-hasPermi="['system:login-log:export']">导出</el-button> | |
| 71 | 32 | </el-col> |
| 72 | 33 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 73 | 34 | </el-row> |
| ... | ... | @@ -76,7 +37,7 @@ |
| 76 | 37 | <el-table-column label="访问编号" align="center" prop="id" /> |
| 77 | 38 | <el-table-column label="日志类型" align="center" prop="logType"> |
| 78 | 39 | <template slot-scope="scope"> |
| 79 | - <span>{{ scope.row.logType === 1 ? '登录' : '退出' }}</span> | |
| 40 | + <span>{{ scope.row.logType === 100 ? '登录' : '退出' }}</span> | |
| 80 | 41 | </template> |
| 81 | 42 | </el-table-column> |
| 82 | 43 | <el-table-column label="用户名称" align="center" prop="username" /> |
| ... | ... | @@ -95,13 +56,8 @@ |
| 95 | 56 | </el-table-column> |
| 96 | 57 | </el-table> |
| 97 | 58 | |
| 98 | - <pagination | |
| 99 | - v-show="total>0" | |
| 100 | - :total="total" | |
| 101 | - :page.sync="queryParams.pageNo" | |
| 102 | - :limit.sync="queryParams.pageSize" | |
| 103 | - @pagination="getList" | |
| 104 | - /> | |
| 59 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 60 | + @pagination="getList"/> | |
| 105 | 61 | </div> |
| 106 | 62 | </template> |
| 107 | 63 | ... | ... |
ruoyi-ui/src/views/system/menu/index.vue
| ... | ... | @@ -2,22 +2,11 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch"> |
| 4 | 4 | <el-form-item label="菜单名称" prop="name"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.name" | |
| 7 | - placeholder="请输入菜单名称" | |
| 8 | - clearable | |
| 9 | - size="small" | |
| 10 | - @keyup.enter.native="handleQuery" | |
| 11 | - /> | |
| 5 | + <el-input v-model="queryParams.name" placeholder="请输入菜单名称" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 12 | 6 | </el-form-item> |
| 13 | 7 | <el-form-item label="状态" prop="status"> |
| 14 | 8 | <el-select v-model="queryParams.status" placeholder="菜单状态" clearable size="small"> |
| 15 | - <el-option | |
| 16 | - v-for="dict in statusDictDatas" | |
| 17 | - :key="parseInt(dict.value)" | |
| 18 | - :label="dict.label" | |
| 19 | - :value="parseInt(dict.value)" | |
| 20 | - /> | |
| 9 | + <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 21 | 10 | </el-select> |
| 22 | 11 | </el-form-item> |
| 23 | 12 | <el-form-item> |
| ... | ... | @@ -28,23 +17,14 @@ |
| 28 | 17 | |
| 29 | 18 | <el-row :gutter="10" class="mb8"> |
| 30 | 19 | <el-col :span="1.5"> |
| 31 | - <el-button | |
| 32 | - type="primary" | |
| 33 | - icon="el-icon-plus" | |
| 34 | - size="mini" | |
| 35 | - @click="handleAdd" | |
| 36 | - v-hasPermi="['system:menu:add']" | |
| 37 | - >新增</el-button> | |
| 20 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 21 | + v-hasPermi="['system:menu:create']">新增</el-button> | |
| 38 | 22 | </el-col> |
| 39 | 23 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 40 | 24 | </el-row> |
| 41 | 25 | |
| 42 | - <el-table | |
| 43 | - v-loading="loading" | |
| 44 | - :data="menuList" | |
| 45 | - row-key="id" | |
| 46 | - :tree-props="{children: 'children', hasChildren: 'hasChildren'}" | |
| 47 | - > | |
| 26 | + <el-table v-loading="loading" :data="menuList" row-key="id" | |
| 27 | + :tree-props="{children: 'children', hasChildren: 'hasChildren'}"> | |
| 48 | 28 | <el-table-column prop="name" label="菜单名称" :show-overflow-tooltip="true" width="200"></el-table-column> |
| 49 | 29 | <el-table-column prop="icon" label="图标" align="center" width="100"> |
| 50 | 30 | <template slot-scope="scope"> |
| ... | ... | @@ -62,26 +42,12 @@ |
| 62 | 42 | </el-table-column> |
| 63 | 43 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 64 | 44 | <template slot-scope="scope"> |
| 65 | - <el-button size="mini" | |
| 66 | - type="text" | |
| 67 | - icon="el-icon-edit" | |
| 68 | - @click="handleUpdate(scope.row)" | |
| 69 | - v-hasPermi="['system:menu:edit']" | |
| 70 | - >修改</el-button> | |
| 71 | - <el-button | |
| 72 | - size="mini" | |
| 73 | - type="text" | |
| 74 | - icon="el-icon-plus" | |
| 75 | - @click="handleAdd(scope.row)" | |
| 76 | - v-hasPermi="['system:menu:add']" | |
| 77 | - >新增</el-button> | |
| 78 | - <el-button | |
| 79 | - size="mini" | |
| 80 | - type="text" | |
| 81 | - icon="el-icon-delete" | |
| 82 | - @click="handleDelete(scope.row)" | |
| 83 | - v-hasPermi="['system:menu:remove']" | |
| 84 | - >删除</el-button> | |
| 45 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 46 | + v-hasPermi="['system:menu:update']">修改</el-button> | |
| 47 | + <el-button size="mini" type="text" icon="el-icon-plus" @click="handleAdd(scope.row)" | |
| 48 | + v-hasPermi="['system:menu:create']">新增</el-button> | |
| 49 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 50 | + v-hasPermi="['system:menu:delete']">删除</el-button> | |
| 85 | 51 | </template> |
| 86 | 52 | </el-table-column> |
| 87 | 53 | </el-table> |
| ... | ... | @@ -92,43 +58,25 @@ |
| 92 | 58 | <el-row> |
| 93 | 59 | <el-col :span="24"> |
| 94 | 60 | <el-form-item label="上级菜单"> |
| 95 | - <treeselect | |
| 96 | - v-model="form.parentId" | |
| 97 | - :options="menuOptions" | |
| 98 | - :normalizer="normalizer" | |
| 99 | - :show-count="true" | |
| 100 | - placeholder="选择上级菜单" | |
| 101 | - /> | |
| 61 | + <treeselect v-model="form.parentId" :options="menuOptions" :normalizer="normalizer" :show-count="true" | |
| 62 | + placeholder="选择上级菜单"/> | |
| 102 | 63 | </el-form-item> |
| 103 | 64 | </el-col> |
| 104 | 65 | <el-col :span="24"> |
| 105 | 66 | <el-form-item label="菜单类型" prop="type"> |
| 106 | 67 | <el-radio-group v-model="form.type"> |
| 107 | - <el-radio | |
| 108 | - v-for="dict in menuTypeDictDatas" | |
| 109 | - :key="parseInt(dict.value)" | |
| 110 | - :label="parseInt(dict.value)" | |
| 111 | - >{{dict.label}}</el-radio> | |
| 68 | + <el-radio v-for="dict in menuTypeDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)"> | |
| 69 | + {{dict.label}}</el-radio> | |
| 112 | 70 | </el-radio-group> |
| 113 | 71 | </el-form-item> |
| 114 | 72 | </el-col> |
| 115 | 73 | <el-col :span="24"> |
| 116 | 74 | <el-form-item v-if="form.type != '3'" label="菜单图标"> |
| 117 | - <el-popover | |
| 118 | - placement="bottom-start" | |
| 119 | - width="460" | |
| 120 | - trigger="click" | |
| 121 | - @show="$refs['iconSelect'].reset()" | |
| 122 | - > | |
| 75 | + <el-popover placement="bottom-start" width="460" trigger="click" @show="$refs['iconSelect'].reset()"> | |
| 123 | 76 | <IconSelect ref="iconSelect" @selected="selected" /> |
| 124 | 77 | <el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly> |
| 125 | - <svg-icon | |
| 126 | - v-if="form.icon" | |
| 127 | - slot="prefix" | |
| 128 | - :icon-class="form.icon" | |
| 129 | - class="el-input__icon" | |
| 130 | - style="height: 32px;width: 16px;" | |
| 131 | - /> | |
| 78 | + <svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" | |
| 79 | + style="height: 32px;width: 16px;"/> | |
| 132 | 80 | <i v-else slot="prefix" class="el-icon-search el-input__icon" /> |
| 133 | 81 | </el-input> |
| 134 | 82 | </el-popover> | ... | ... |
ruoyi-ui/src/views/system/notice/index.vue
| ... | ... | @@ -2,31 +2,14 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| 4 | 4 | <el-form-item label="公告标题" prop="title"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.title" | |
| 7 | - placeholder="请输入公告标题" | |
| 8 | - clearable | |
| 9 | - size="small" | |
| 10 | - @keyup.enter.native="handleQuery" | |
| 11 | - /> | |
| 5 | + <el-input v-model="queryParams.title" placeholder="请输入公告标题" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 12 | 6 | </el-form-item> |
| 13 | 7 | <el-form-item label="操作人员" prop="createBy"> |
| 14 | - <el-input | |
| 15 | - v-model="queryParams.createBy" | |
| 16 | - placeholder="请输入操作人员" | |
| 17 | - clearable | |
| 18 | - size="small" | |
| 19 | - @keyup.enter.native="handleQuery" | |
| 20 | - /> | |
| 8 | + <el-input v-model="queryParams.createBy" placeholder="请输入操作人员" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 21 | 9 | </el-form-item> |
| 22 | 10 | <el-form-item label="类型" prop="type"> |
| 23 | 11 | <el-select v-model="queryParams.type" placeholder="公告类型" clearable size="small"> |
| 24 | - <el-option | |
| 25 | - v-for="dict in noticeTypeDictDatas" | |
| 26 | - :key="parseInt(dict.value)" | |
| 27 | - :label="dict.label" | |
| 28 | - :value="parseInt(dict.value)" | |
| 29 | - /> | |
| 12 | + <el-option v-for="dict in noticeTypeDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 30 | 13 | </el-select> |
| 31 | 14 | </el-form-item> |
| 32 | 15 | <el-form-item> |
| ... | ... | @@ -37,39 +20,16 @@ |
| 37 | 20 | |
| 38 | 21 | <el-row :gutter="10" class="mb8"> |
| 39 | 22 | <el-col :span="1.5"> |
| 40 | - <el-button | |
| 41 | - type="primary" | |
| 42 | - icon="el-icon-plus" | |
| 43 | - size="mini" | |
| 44 | - @click="handleAdd" | |
| 45 | - v-hasPermi="['system:notice:add']" | |
| 46 | - >新增</el-button> | |
| 23 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:notice:create']"s>新增</el-button> | |
| 47 | 24 | </el-col> |
| 48 | 25 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 49 | 26 | </el-row> |
| 50 | 27 | |
| 51 | 28 | <el-table v-loading="loading" :data="noticeList"> |
| 52 | 29 | <el-table-column label="序号" align="center" prop="id" width="100" /> |
| 53 | - <el-table-column | |
| 54 | - label="公告标题" | |
| 55 | - align="center" | |
| 56 | - prop="title" | |
| 57 | - :show-overflow-tooltip="true" | |
| 58 | - /> | |
| 59 | - <el-table-column | |
| 60 | - label="公告类型" | |
| 61 | - align="center" | |
| 62 | - prop="type" | |
| 63 | - :formatter="typeFormat" | |
| 64 | - width="100" | |
| 65 | - /> | |
| 66 | - <el-table-column | |
| 67 | - label="状态" | |
| 68 | - align="center" | |
| 69 | - prop="status" | |
| 70 | - :formatter="statusFormat" | |
| 71 | - width="100" | |
| 72 | - /> | |
| 30 | + <el-table-column label="公告标题" align="center" prop="title" :show-overflow-tooltip="true"/> | |
| 31 | + <el-table-column label="公告类型" align="center" prop="type" :formatter="typeFormat" width="100"/> | |
| 32 | + <el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" width="100"/> | |
| 73 | 33 | <el-table-column label="创建者" align="center" prop="createBy" width="100" /> |
| 74 | 34 | <el-table-column label="创建时间" align="center" prop="createTime" width="100"> |
| 75 | 35 | <template slot-scope="scope"> |
| ... | ... | @@ -78,31 +38,16 @@ |
| 78 | 38 | </el-table-column> |
| 79 | 39 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 80 | 40 | <template slot-scope="scope"> |
| 81 | - <el-button | |
| 82 | - size="mini" | |
| 83 | - type="text" | |
| 84 | - icon="el-icon-edit" | |
| 85 | - @click="handleUpdate(scope.row)" | |
| 86 | - v-hasPermi="['system:notice:edit']" | |
| 87 | - >修改</el-button> | |
| 88 | - <el-button | |
| 89 | - size="mini" | |
| 90 | - type="text" | |
| 91 | - icon="el-icon-delete" | |
| 92 | - @click="handleDelete(scope.row)" | |
| 93 | - v-hasPermi="['system:notice:remove']" | |
| 94 | - >删除</el-button> | |
| 41 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 42 | + v-hasPermi="['system:notice:update']">修改</el-button> | |
| 43 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 44 | + v-hasPermi="['system:notice:delete']">删除</el-button> | |
| 95 | 45 | </template> |
| 96 | 46 | </el-table-column> |
| 97 | 47 | </el-table> |
| 98 | 48 | |
| 99 | - <pagination | |
| 100 | - v-show="total>0" | |
| 101 | - :total="total" | |
| 102 | - :page.sync="queryParams.pageNo" | |
| 103 | - :limit.sync="queryParams.pageSize" | |
| 104 | - @pagination="getList" | |
| 105 | - /> | |
| 49 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 50 | + @pagination="getList"/> | |
| 106 | 51 | |
| 107 | 52 | <!-- 添加或修改公告对话框 --> |
| 108 | 53 | <el-dialog :title="title" :visible.sync="open" width="780px" append-to-body> | ... | ... |
ruoyi-ui/src/views/system/operatelog/index.vue
| ... | ... | @@ -2,72 +2,28 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| 4 | 4 | <el-form-item label="系统模块" prop="title"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.title" | |
| 7 | - placeholder="请输入系统模块" | |
| 8 | - clearable | |
| 9 | - style="width: 240px;" | |
| 10 | - size="small" | |
| 11 | - @keyup.enter.native="handleQuery" | |
| 12 | - /> | |
| 5 | + <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;" size="small" | |
| 6 | + @keyup.enter.native="handleQuery"/> | |
| 13 | 7 | </el-form-item> |
| 14 | 8 | <el-form-item label="操作人员" prop="operName"> |
| 15 | - <el-input | |
| 16 | - v-model="queryParams.operName" | |
| 17 | - placeholder="请输入操作人员" | |
| 18 | - clearable | |
| 19 | - style="width: 240px;" | |
| 20 | - size="small" | |
| 21 | - @keyup.enter.native="handleQuery" | |
| 22 | - /> | |
| 9 | + <el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable style="width: 240px;" size="small" | |
| 10 | + @keyup.enter.native="handleQuery"/> | |
| 23 | 11 | </el-form-item> |
| 24 | 12 | <el-form-item label="类型" prop="type"> |
| 25 | - <el-select | |
| 26 | - v-model="queryParams.type" | |
| 27 | - placeholder="操作类型" | |
| 28 | - clearable | |
| 29 | - size="small" | |
| 30 | - style="width: 240px" | |
| 31 | - > | |
| 32 | - <el-option | |
| 33 | - v-for="dict in this.getDictDatas(DICT_TYPE.SYS_OPERATE_TYPE)" | |
| 34 | - :key="parseInt(dict.value)" | |
| 35 | - :label="dict.label" | |
| 36 | - :value="parseInt(dict.value)" | |
| 37 | - /> | |
| 13 | + <el-select v-model="queryParams.type" placeholder="操作类型" clearable size="small" style="width: 240px"> | |
| 14 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_OPERATE_TYPE)" :key="parseInt(dict.value)" | |
| 15 | + :label="dict.label" :value="parseInt(dict.value)"/> | |
| 38 | 16 | </el-select> |
| 39 | 17 | </el-form-item> |
| 40 | 18 | <el-form-item label="状态" prop="status"> |
| 41 | - <el-select | |
| 42 | - v-model="queryParams.success" | |
| 43 | - placeholder="操作状态" | |
| 44 | - clearable | |
| 45 | - size="small" | |
| 46 | - style="width: 240px" | |
| 47 | - > | |
| 48 | - <el-option | |
| 49 | - :key="true" | |
| 50 | - label="成功" | |
| 51 | - :value="true" | |
| 52 | - /> | |
| 53 | - <el-option | |
| 54 | - :key="false" | |
| 55 | - label="失败" | |
| 56 | - :value="false" | |
| 57 | - /> | |
| 19 | + <el-select v-model="queryParams.success" placeholder="操作状态" clearable size="small" style="width: 240px"> | |
| 20 | + <el-option :key="true" label="成功" :value="true"/> | |
| 21 | + <el-option :key="false" label="失败" :value="false"/> | |
| 58 | 22 | </el-select> |
| 59 | 23 | </el-form-item> |
| 60 | 24 | <el-form-item label="操作时间"> |
| 61 | - <el-date-picker | |
| 62 | - v-model="dateRange" | |
| 63 | - size="small" | |
| 64 | - style="width: 240px" | |
| 65 | - value-format="yyyy-MM-dd" | |
| 66 | - type="daterange" | |
| 67 | - range-separator="-" | |
| 68 | - start-placeholder="开始日期" | |
| 69 | - end-placeholder="结束日期" | |
| 70 | - ></el-date-picker> | |
| 25 | + <el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 26 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> | |
| 71 | 27 | </el-form-item> |
| 72 | 28 | <el-form-item> |
| 73 | 29 | <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
| ... | ... | @@ -77,13 +33,8 @@ |
| 77 | 33 | |
| 78 | 34 | <el-row :gutter="10" class="mb8"> |
| 79 | 35 | <el-col :span="1.5"> |
| 80 | - <el-button | |
| 81 | - type="warning" | |
| 82 | - icon="el-icon-download" | |
| 83 | - size="mini" | |
| 84 | - @click="handleExport" | |
| 85 | - v-hasPermi="['system:config:export']" | |
| 86 | - >导出</el-button> | |
| 36 | + <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" | |
| 37 | + v-hasPermi="['system:operate-log:export']">导出</el-button> | |
| 87 | 38 | </el-col> |
| 88 | 39 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 89 | 40 | </el-row> |
| ... | ... | @@ -212,7 +163,6 @@ export default { |
| 212 | 163 | businessType: undefined, |
| 213 | 164 | status: undefined |
| 214 | 165 | }, |
| 215 | - | |
| 216 | 166 | }; |
| 217 | 167 | }, |
| 218 | 168 | created() { | ... | ... |
ruoyi-ui/src/views/system/post/index.vue
| ... | ... | @@ -2,31 +2,14 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| 4 | 4 | <el-form-item label="岗位编码" prop="code"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.code" | |
| 7 | - placeholder="请输入岗位编码" | |
| 8 | - clearable | |
| 9 | - size="small" | |
| 10 | - @keyup.enter.native="handleQuery" | |
| 11 | - /> | |
| 5 | + <el-input v-model="queryParams.code" placeholder="请输入岗位编码" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 12 | 6 | </el-form-item> |
| 13 | 7 | <el-form-item label="岗位名称" prop="name"> |
| 14 | - <el-input | |
| 15 | - v-model="queryParams.name" | |
| 16 | - placeholder="请输入岗位名称" | |
| 17 | - clearable | |
| 18 | - size="small" | |
| 19 | - @keyup.enter.native="handleQuery" | |
| 20 | - /> | |
| 8 | + <el-input v-model="queryParams.name" placeholder="请输入岗位名称" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 21 | 9 | </el-form-item> |
| 22 | 10 | <el-form-item label="状态" prop="status"> |
| 23 | 11 | <el-select v-model="queryParams.status" placeholder="岗位状态" clearable size="small"> |
| 24 | - <el-option | |
| 25 | - v-for="dict in statusDictDatas" | |
| 26 | - :key="parseInt(dict.value)" | |
| 27 | - :label="dict.label" | |
| 28 | - :value="parseInt(dict.value)" | |
| 29 | - /> | |
| 12 | + <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 30 | 13 | </el-select> |
| 31 | 14 | </el-form-item> |
| 32 | 15 | <el-form-item> |
| ... | ... | @@ -37,22 +20,12 @@ |
| 37 | 20 | |
| 38 | 21 | <el-row :gutter="10" class="mb8"> |
| 39 | 22 | <el-col :span="1.5"> |
| 40 | - <el-button | |
| 41 | - type="primary" | |
| 42 | - icon="el-icon-plus" | |
| 43 | - size="mini" | |
| 44 | - @click="handleAdd" | |
| 45 | - v-hasPermi="['system:post:add']" | |
| 46 | - >新增</el-button> | |
| 23 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 24 | + v-hasPermi="['system:post:create']">新增</el-button> | |
| 47 | 25 | </el-col> |
| 48 | 26 | <el-col :span="1.5"> |
| 49 | - <el-button | |
| 50 | - type="warning" | |
| 51 | - icon="el-icon-download" | |
| 52 | - size="mini" | |
| 53 | - @click="handleExport" | |
| 54 | - v-hasPermi="['system:post:export']" | |
| 55 | - >导出</el-button> | |
| 27 | + <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" | |
| 28 | + v-hasPermi="['system:post:export']">导出</el-button> | |
| 56 | 29 | </el-col> |
| 57 | 30 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 58 | 31 | </el-row> |
| ... | ... | @@ -70,31 +43,16 @@ |
| 70 | 43 | </el-table-column> |
| 71 | 44 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 72 | 45 | <template slot-scope="scope"> |
| 73 | - <el-button | |
| 74 | - size="mini" | |
| 75 | - type="text" | |
| 76 | - icon="el-icon-edit" | |
| 77 | - @click="handleUpdate(scope.row)" | |
| 78 | - v-hasPermi="['system:post:edit']" | |
| 79 | - >修改</el-button> | |
| 80 | - <el-button | |
| 81 | - size="mini" | |
| 82 | - type="text" | |
| 83 | - icon="el-icon-delete" | |
| 84 | - @click="handleDelete(scope.row)" | |
| 85 | - v-hasPermi="['system:post:remove']" | |
| 86 | - >删除</el-button> | |
| 46 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 47 | + v-hasPermi="['system:post:update']">修改</el-button> | |
| 48 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 49 | + v-hasPermi="['system:post:delete']">删除</el-button> | |
| 87 | 50 | </template> |
| 88 | 51 | </el-table-column> |
| 89 | 52 | </el-table> |
| 90 | 53 | |
| 91 | - <pagination | |
| 92 | - v-show="total>0" | |
| 93 | - :total="total" | |
| 94 | - :page.sync="queryParams.pageNo" | |
| 95 | - :limit.sync="queryParams.pageSize" | |
| 96 | - @pagination="getList" | |
| 97 | - /> | |
| 54 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 55 | + @pagination="getList"/> | |
| 98 | 56 | |
| 99 | 57 | <!-- 添加或修改岗位对话框 --> |
| 100 | 58 | <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> |
| ... | ... | @@ -110,11 +68,8 @@ |
| 110 | 68 | </el-form-item> |
| 111 | 69 | <el-form-item label="岗位状态" prop="status"> |
| 112 | 70 | <el-radio-group v-model="form.status"> |
| 113 | - <el-radio | |
| 114 | - v-for="dict in statusDictDatas" | |
| 115 | - :key="parseInt(dict.value)" | |
| 116 | - :label="parseInt(dict.value)" | |
| 117 | - >{{dict.label}}</el-radio> | |
| 71 | + <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)"> | |
| 72 | + {{dict.label}}</el-radio> | |
| 118 | 73 | </el-radio-group> |
| 119 | 74 | </el-form-item> |
| 120 | 75 | <el-form-item label="备注" prop="remark"> | ... | ... |
ruoyi-ui/src/views/system/role/index.vue
| ... | ... | @@ -2,52 +2,21 @@ |
| 2 | 2 | <div class="app-container"> |
| 3 | 3 | <el-form :model="queryParams" ref="queryForm" v-show="showSearch" :inline="true"> |
| 4 | 4 | <el-form-item label="角色名称" prop="name"> |
| 5 | - <el-input | |
| 6 | - v-model="queryParams.name" | |
| 7 | - placeholder="请输入角色名称" | |
| 8 | - clearable | |
| 9 | - size="small" | |
| 10 | - style="width: 240px" | |
| 11 | - @keyup.enter.native="handleQuery" | |
| 12 | - /> | |
| 5 | + <el-input v-model="queryParams.name" placeholder="请输入角色名称" clearable size="small" style="width: 240px" | |
| 6 | + @keyup.enter.native="handleQuery"/> | |
| 13 | 7 | </el-form-item> |
| 14 | 8 | <el-form-item label="角色标识" prop="code"> |
| 15 | - <el-input | |
| 16 | - v-model="queryParams.code" | |
| 17 | - placeholder="请输入角色标识" | |
| 18 | - clearable | |
| 19 | - size="small" | |
| 20 | - style="width: 240px" | |
| 21 | - @keyup.enter.native="handleQuery" | |
| 22 | - /> | |
| 9 | + <el-input v-model="queryParams.code" placeholder="请输入角色标识" clearable size="small" style="width: 240px" | |
| 10 | + @keyup.enter.native="handleQuery"/> | |
| 23 | 11 | </el-form-item> |
| 24 | 12 | <el-form-item label="状态" prop="status"> |
| 25 | - <el-select | |
| 26 | - v-model="queryParams.status" | |
| 27 | - placeholder="角色状态" | |
| 28 | - clearable | |
| 29 | - size="small" | |
| 30 | - style="width: 240px" | |
| 31 | - > | |
| 32 | - <el-option | |
| 33 | - v-for="dict in statusDictDatas" | |
| 34 | - :key="parseInt(dict.value)" | |
| 35 | - :label="dict.label" | |
| 36 | - :value="parseInt(dict.value)" | |
| 37 | - /> | |
| 13 | + <el-select v-model="queryParams.status" placeholder="角色状态" clearable size="small" style="width: 240px"> | |
| 14 | + <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 38 | 15 | </el-select> |
| 39 | 16 | </el-form-item> |
| 40 | 17 | <el-form-item label="创建时间"> |
| 41 | - <el-date-picker | |
| 42 | - v-model="dateRange" | |
| 43 | - size="small" | |
| 44 | - style="width: 240px" | |
| 45 | - value-format="yyyy-MM-dd" | |
| 46 | - type="daterange" | |
| 47 | - range-separator="-" | |
| 48 | - start-placeholder="开始日期" | |
| 49 | - end-placeholder="结束日期" | |
| 50 | - ></el-date-picker> | |
| 18 | + <el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" | |
| 19 | + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> | |
| 51 | 20 | </el-form-item> |
| 52 | 21 | <el-form-item> |
| 53 | 22 | <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
| ... | ... | @@ -57,22 +26,12 @@ |
| 57 | 26 | |
| 58 | 27 | <el-row :gutter="10" class="mb8"> |
| 59 | 28 | <el-col :span="1.5"> |
| 60 | - <el-button | |
| 61 | - type="primary" | |
| 62 | - icon="el-icon-plus" | |
| 63 | - size="mini" | |
| 64 | - @click="handleAdd" | |
| 65 | - v-hasPermi="['system:role:add']" | |
| 66 | - >新增</el-button> | |
| 29 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 30 | + v-hasPermi="['system:role:create']">新增</el-button> | |
| 67 | 31 | </el-col> |
| 68 | 32 | <el-col :span="1.5"> |
| 69 | - <el-button | |
| 70 | - type="warning" | |
| 71 | - icon="el-icon-download" | |
| 72 | - size="mini" | |
| 73 | - @click="handleExport" | |
| 74 | - v-hasPermi="['system:role:export']" | |
| 75 | - >导出</el-button> | |
| 33 | + <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" | |
| 34 | + v-hasPermi="['system:role:export']">导出</el-button> | |
| 76 | 35 | </el-col> |
| 77 | 36 | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| 78 | 37 | </el-row> |
| ... | ... | @@ -85,12 +44,7 @@ |
| 85 | 44 | <el-table-column label="显示顺序" prop="sort" width="100" /> |
| 86 | 45 | <el-table-column label="状态" align="center" width="100"> |
| 87 | 46 | <template slot-scope="scope"> |
| 88 | - <el-switch | |
| 89 | - v-model="scope.row.status" | |
| 90 | - :active-value="0" | |
| 91 | - :inactive-value="1" | |
| 92 | - @change="handleStatusChange(scope.row)" | |
| 93 | - ></el-switch> | |
| 47 | + <el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> | |
| 94 | 48 | </template> |
| 95 | 49 | </el-table-column> |
| 96 | 50 | <el-table-column label="创建时间" align="center" prop="createTime" width="180"> |
| ... | ... | @@ -100,45 +54,20 @@ |
| 100 | 54 | </el-table-column> |
| 101 | 55 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 102 | 56 | <template slot-scope="scope"> |
| 103 | - <el-button | |
| 104 | - size="mini" | |
| 105 | - type="text" | |
| 106 | - icon="el-icon-edit" | |
| 107 | - @click="handleUpdate(scope.row)" | |
| 108 | - v-hasPermi="['system:role:edit']" | |
| 109 | - >修改</el-button> | |
| 110 | - <el-button | |
| 111 | - size="mini" | |
| 112 | - type="text" | |
| 113 | - icon="el-icon-circle-check" | |
| 114 | - @click="handleMenu(scope.row)" | |
| 115 | - v-hasPermi="['system:permission:assign-role-menu']" | |
| 116 | - >菜单权限</el-button> | |
| 117 | - <el-button | |
| 118 | - size="mini" | |
| 119 | - type="text" | |
| 120 | - icon="el-icon-circle-check" | |
| 121 | - @click="handleDataScope(scope.row)" | |
| 122 | - v-hasPermi="['system:permission:assign-role-data-scope']" | |
| 123 | - >数据权限</el-button> | |
| 124 | - <el-button | |
| 125 | - size="mini" | |
| 126 | - type="text" | |
| 127 | - icon="el-icon-delete" | |
| 128 | - @click="handleDelete(scope.row)" | |
| 129 | - v-hasPermi="['system:role:remove']" | |
| 130 | - >删除</el-button> | |
| 57 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 58 | + v-hasPermi="['system:role:update']">修改</el-button> | |
| 59 | + <el-button size="mini" type="text" icon="el-icon-circle-check" @click="handleMenu(scope.row)" | |
| 60 | + v-hasPermi="['system:permission:assign-role-menu']">菜单权限</el-button> | |
| 61 | + <el-button size="mini" type="text" icon="el-icon-circle-check" @click="handleDataScope(scope.row)" | |
| 62 | + v-hasPermi="['system:permission:assign-role-data-scope']">数据权限</el-button> | |
| 63 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 64 | + v-hasPermi="['system:role:delete']">删除</el-button> | |
| 131 | 65 | </template> |
| 132 | 66 | </el-table-column> |
| 133 | 67 | </el-table> |
| 134 | 68 | |
| 135 | - <pagination | |
| 136 | - v-show="total>0" | |
| 137 | - :total="total" | |
| 138 | - :page.sync="queryParams.pageNum" | |
| 139 | - :limit.sync="queryParams.pageSize" | |
| 140 | - @pagination="getList" | |
| 141 | - /> | |
| 69 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" | |
| 70 | + @pagination="getList"/> | |
| 142 | 71 | |
| 143 | 72 | <!-- 添加或修改角色配置对话框 --> |
| 144 | 73 | <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | ... | ... |
ruoyi-ui/src/views/system/sms/smsChannel.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="app-container"> | |
| 3 | + | |
| 4 | + <!-- 搜索工作栏 --> | |
| 5 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> | |
| 6 | + <el-form-item label="短信签名" prop="signature"> | |
| 7 | + <el-input v-model="queryParams.signature" placeholder="请输入短信签名" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 8 | + </el-form-item> | |
| 9 | + <el-form-item label="启用状态" prop="status"> | |
| 10 | + <el-select v-model="queryParams.status" placeholder="请选择启用状态" clearable size="small"> | |
| 11 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | |
| 12 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 13 | + </el-select> | |
| 14 | + </el-form-item> | |
| 15 | + <el-form-item label="创建时间"> | |
| 16 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 17 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 18 | + </el-form-item> | |
| 19 | + <el-form-item> | |
| 20 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |
| 21 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |
| 22 | + </el-form-item> | |
| 23 | + </el-form> | |
| 24 | + | |
| 25 | + <!-- 操作工具栏 --> | |
| 26 | + <el-row :gutter="10" class="mb8"> | |
| 27 | + <el-col :span="1.5"> | |
| 28 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 29 | + v-hasPermi="['system:sms-channel:create']">新增</el-button> | |
| 30 | + </el-col> | |
| 31 | + <el-col :span="1.5"> | |
| 32 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | |
| 33 | + v-hasPermi="['system:sms-channel:export']">导出</el-button> | |
| 34 | + </el-col> | |
| 35 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |
| 36 | + </el-row> | |
| 37 | + | |
| 38 | + <!-- 列表 --> | |
| 39 | + <el-table v-loading="loading" :data="list"> | |
| 40 | + <el-table-column label="编号" align="center" prop="id" /> | |
| 41 | + <el-table-column label="短信签名" align="center" prop="signature" /> | |
| 42 | + <el-table-column label="渠道编码" align="center" prop="code"> | |
| 43 | + <template slot-scope="scope"> | |
| 44 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.code) }}</span> | |
| 45 | + </template> | |
| 46 | + </el-table-column>> | |
| 47 | + <el-table-column label="启用状态" align="center" prop="status"> | |
| 48 | + <template slot-scope="scope"> | |
| 49 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span> | |
| 50 | + </template> | |
| 51 | + </el-table-column>> | |
| 52 | + <el-table-column label="备注" align="center" prop="remark" /> | |
| 53 | + <el-table-column label="短信 API 的账号" align="center" prop="apiKey" /> | |
| 54 | + <el-table-column label="短信 API 的秘钥" align="center" prop="apiSecret" /> | |
| 55 | + <el-table-column label="短信发送回调 URL" align="center" prop="callbackUrl" /> | |
| 56 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | |
| 57 | + <template slot-scope="scope"> | |
| 58 | + <span>{{ parseTime(scope.row.createTime) }}</span> | |
| 59 | + </template> | |
| 60 | + </el-table-column> | |
| 61 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | |
| 62 | + <template slot-scope="scope"> | |
| 63 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 64 | + v-hasPermi="['system:sms-channel:update']">修改</el-button> | |
| 65 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 66 | + v-hasPermi="['system:sms-channel:delete']">删除</el-button> | |
| 67 | + </template> | |
| 68 | + </el-table-column> | |
| 69 | + </el-table> | |
| 70 | + <!-- 分页组件 --> | |
| 71 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 72 | + @pagination="getList"/> | |
| 73 | + | |
| 74 | + <!-- 对话框(添加 / 修改) --> | |
| 75 | + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | |
| 76 | + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> | |
| 77 | + <el-form-item label="短信签名" prop="signature"> | |
| 78 | + <el-input v-model="form.signature" placeholder="请输入短信签名" /> | |
| 79 | + </el-form-item> | |
| 80 | + <el-form-item label="渠道编码" prop="code"> | |
| 81 | + <el-input v-model="form.code" placeholder="请输入渠道编码" /> | |
| 82 | + </el-form-item> | |
| 83 | + <el-form-item label="启用状态"> | |
| 84 | + <el-radio-group v-model="form.status"> | |
| 85 | + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | |
| 86 | + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | |
| 87 | + </el-radio-group> | |
| 88 | + </el-form-item> | |
| 89 | + <el-form-item label="备注" prop="remark"> | |
| 90 | + <el-input v-model="form.remark" placeholder="请输入备注" /> | |
| 91 | + </el-form-item> | |
| 92 | + <el-form-item label="短信 API 的账号" prop="apiKey"> | |
| 93 | + <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" /> | |
| 94 | + </el-form-item> | |
| 95 | + <el-form-item label="短信 API 的秘钥" prop="apiSecret"> | |
| 96 | + <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的秘钥" /> | |
| 97 | + </el-form-item> | |
| 98 | + <el-form-item label="短信发送回调 URL" prop="callbackUrl"> | |
| 99 | + <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" /> | |
| 100 | + </el-form-item> | |
| 101 | + </el-form> | |
| 102 | + <div slot="footer" class="dialog-footer"> | |
| 103 | + <el-button type="primary" @click="submitForm">确 定</el-button> | |
| 104 | + <el-button @click="cancel">取 消</el-button> | |
| 105 | + </div> | |
| 106 | + </el-dialog> | |
| 107 | + </div> | |
| 108 | +</template> | |
| 109 | + | |
| 110 | +<script> | |
| 111 | +import { createSmsChannel, updateSmsChannel, deleteSmsChannel, getSmsChannel, getSmsChannelPage, | |
| 112 | + getSimpleSmsChannels } from "@/api/system/sms/smsChannel"; | |
| 113 | + | |
| 114 | +export default { | |
| 115 | + name: "SmsChannel", | |
| 116 | + components: { | |
| 117 | + }, | |
| 118 | + data() { | |
| 119 | + return { | |
| 120 | + // 遮罩层 | |
| 121 | + loading: true, | |
| 122 | + // 显示搜索条件 | |
| 123 | + showSearch: true, | |
| 124 | + // 总条数 | |
| 125 | + total: 0, | |
| 126 | + // 短信渠道列表 | |
| 127 | + list: [], | |
| 128 | + // 弹出层标题 | |
| 129 | + title: "", | |
| 130 | + // 是否显示弹出层 | |
| 131 | + open: false, | |
| 132 | + dateRangeCreateTime: [], | |
| 133 | + // 查询参数 | |
| 134 | + queryParams: { | |
| 135 | + pageNo: 1, | |
| 136 | + pageSize: 10, | |
| 137 | + signature: null, | |
| 138 | + status: null, | |
| 139 | + }, | |
| 140 | + // 表单参数 | |
| 141 | + form: {}, | |
| 142 | + // 表单校验 | |
| 143 | + rules: { | |
| 144 | + signature: [{ required: true, message: "短信签名不能为空", trigger: "blur" }], | |
| 145 | + code: [{ required: true, message: "渠道编码不能为空", trigger: "blur" }], | |
| 146 | + status: [{ required: true, message: "启用状态不能为空", trigger: "blur" }], | |
| 147 | + apiKey: [{ required: true, message: "短信 API 的账号不能为空", trigger: "blur" }], | |
| 148 | + }, | |
| 149 | + }; | |
| 150 | + }, | |
| 151 | + created() { | |
| 152 | + this.getList(); | |
| 153 | + }, | |
| 154 | + methods: { | |
| 155 | + /** 查询列表 */ | |
| 156 | + getList() { | |
| 157 | + this.loading = true; | |
| 158 | + // 处理查询参数 | |
| 159 | + let params = {...this.queryParams}; | |
| 160 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | |
| 161 | + // 执行查询 | |
| 162 | + getSmsChannelPage(params).then(response => { | |
| 163 | + this.list = response.data.list; | |
| 164 | + this.total = response.data.total; | |
| 165 | + this.loading = false; | |
| 166 | + }); | |
| 167 | + }, | |
| 168 | + /** 取消按钮 */ | |
| 169 | + cancel() { | |
| 170 | + this.open = false; | |
| 171 | + this.reset(); | |
| 172 | + }, | |
| 173 | + /** 表单重置 */ | |
| 174 | + reset() { | |
| 175 | + this.form = { | |
| 176 | + id: undefined, | |
| 177 | + signature: undefined, | |
| 178 | + code: undefined, | |
| 179 | + status: undefined, | |
| 180 | + remark: undefined, | |
| 181 | + apiKey: undefined, | |
| 182 | + apiSecret: undefined, | |
| 183 | + callbackUrl: undefined, | |
| 184 | + }; | |
| 185 | + this.resetForm("form"); | |
| 186 | + }, | |
| 187 | + /** 搜索按钮操作 */ | |
| 188 | + handleQuery() { | |
| 189 | + this.queryParams.pageNo = 1; | |
| 190 | + this.getList(); | |
| 191 | + }, | |
| 192 | + /** 重置按钮操作 */ | |
| 193 | + resetQuery() { | |
| 194 | + this.dateRangeCreateTime = []; | |
| 195 | + this.resetForm("queryForm"); | |
| 196 | + this.handleQuery(); | |
| 197 | + }, | |
| 198 | + /** 新增按钮操作 */ | |
| 199 | + handleAdd() { | |
| 200 | + this.reset(); | |
| 201 | + this.open = true; | |
| 202 | + this.title = "添加短信渠道"; | |
| 203 | + }, | |
| 204 | + /** 修改按钮操作 */ | |
| 205 | + handleUpdate(row) { | |
| 206 | + this.reset(); | |
| 207 | + const id = row.id; | |
| 208 | + getSmsChannel(id).then(response => { | |
| 209 | + this.form = response.data; | |
| 210 | + this.open = true; | |
| 211 | + this.title = "修改短信渠道"; | |
| 212 | + }); | |
| 213 | + }, | |
| 214 | + /** 提交按钮 */ | |
| 215 | + submitForm() { | |
| 216 | + this.$refs["form"].validate(valid => { | |
| 217 | + if (!valid) { | |
| 218 | + return; | |
| 219 | + } | |
| 220 | + // 修改的提交 | |
| 221 | + if (this.form.id != null) { | |
| 222 | + updateSmsChannel(this.form).then(response => { | |
| 223 | + this.msgSuccess("修改成功"); | |
| 224 | + this.open = false; | |
| 225 | + this.getList(); | |
| 226 | + }); | |
| 227 | + return; | |
| 228 | + } | |
| 229 | + // 添加的提交 | |
| 230 | + createSmsChannel(this.form).then(response => { | |
| 231 | + this.msgSuccess("新增成功"); | |
| 232 | + this.open = false; | |
| 233 | + this.getList(); | |
| 234 | + }); | |
| 235 | + }); | |
| 236 | + }, | |
| 237 | + /** 删除按钮操作 */ | |
| 238 | + handleDelete(row) { | |
| 239 | + const id = row.id; | |
| 240 | + this.$confirm('是否确认删除短信渠道编号为"' + id + '"的数据项?', "警告", { | |
| 241 | + confirmButtonText: "确定", | |
| 242 | + cancelButtonText: "取消", | |
| 243 | + type: "warning" | |
| 244 | + }).then(function() { | |
| 245 | + return deleteSmsChannel(id); | |
| 246 | + }).then(() => { | |
| 247 | + this.getList(); | |
| 248 | + this.msgSuccess("删除成功"); | |
| 249 | + }) | |
| 250 | + }, | |
| 251 | + /** 导出按钮操作 */ | |
| 252 | + handleExport() { | |
| 253 | + // 处理查询参数 | |
| 254 | + let params = {...this.queryParams}; | |
| 255 | + params.pageNo = undefined; | |
| 256 | + params.pageSize = undefined; | |
| 257 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | |
| 258 | + // 执行导出 | |
| 259 | + this.$confirm('是否确认导出所有短信渠道数据项?', "警告", { | |
| 260 | + confirmButtonText: "确定", | |
| 261 | + cancelButtonText: "取消", | |
| 262 | + type: "warning" | |
| 263 | + }).then(function() { | |
| 264 | + return exportSmsChannelExcel(params); | |
| 265 | + }).then(response => { | |
| 266 | + this.downloadExcel(response, '短信渠道.xls'); | |
| 267 | + }) | |
| 268 | + } | |
| 269 | + } | |
| 270 | +}; | |
| 271 | +</script><template> | |
| 272 | + <div class="app-container"> | |
| 273 | + | |
| 274 | + <!-- 搜索工作栏 --> | |
| 275 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> | |
| 276 | + <el-form-item label="短信签名" prop="signature"> | |
| 277 | + <el-input v-model="queryParams.signature" placeholder="请输入短信签名" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 278 | + </el-form-item> | |
| 279 | + <el-form-item label="启用状态" prop="status"> | |
| 280 | + <el-select v-model="queryParams.status" placeholder="请选择启用状态" clearable size="small"> | |
| 281 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | |
| 282 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 283 | + </el-select> | |
| 284 | + </el-form-item> | |
| 285 | + <el-form-item label="创建时间"> | |
| 286 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 287 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 288 | + </el-form-item> | |
| 289 | + <el-form-item> | |
| 290 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |
| 291 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |
| 292 | + </el-form-item> | |
| 293 | + </el-form> | |
| 294 | + | |
| 295 | + <!-- 操作工具栏 --> | |
| 296 | + <el-row :gutter="10" class="mb8"> | |
| 297 | + <el-col :span="1.5"> | |
| 298 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 299 | + v-hasPermi="['system:sms-channel:create']">新增</el-button> | |
| 300 | + </el-col> | |
| 301 | + <el-col :span="1.5"> | |
| 302 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | |
| 303 | + v-hasPermi="['system:sms-channel:export']">导出</el-button> | |
| 304 | + </el-col> | |
| 305 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |
| 306 | + </el-row> | |
| 307 | + | |
| 308 | + <!-- 列表 --> | |
| 309 | + <el-table v-loading="loading" :data="list"> | |
| 310 | + <el-table-column label="编号" align="center" prop="id" /> | |
| 311 | + <el-table-column label="短信签名" align="center" prop="signature" /> | |
| 312 | + <el-table-column label="渠道编码" align="center" prop="code"> | |
| 313 | + <template slot-scope="scope"> | |
| 314 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.code) }}</span> | |
| 315 | + </template> | |
| 316 | + </el-table-column>> | |
| 317 | + <el-table-column label="启用状态" align="center" prop="status"> | |
| 318 | + <template slot-scope="scope"> | |
| 319 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span> | |
| 320 | + </template> | |
| 321 | + </el-table-column>> | |
| 322 | + <el-table-column label="备注" align="center" prop="remark" /> | |
| 323 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | |
| 324 | + <template slot-scope="scope"> | |
| 325 | + <span>{{ parseTime(scope.row.createTime) }}</span> | |
| 326 | + </template> | |
| 327 | + </el-table-column> | |
| 328 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | |
| 329 | + <template slot-scope="scope"> | |
| 330 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 331 | + v-hasPermi="['system:sms-channel:update']">修改</el-button> | |
| 332 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 333 | + v-hasPermi="['system:sms-channel:delete']">删除</el-button> | |
| 334 | + </template> | |
| 335 | + </el-table-column> | |
| 336 | + </el-table> | |
| 337 | + <!-- 分页组件 --> | |
| 338 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 339 | + @pagination="getList"/> | |
| 340 | + | |
| 341 | + <!-- 对话框(添加 / 修改) --> | |
| 342 | + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | |
| 343 | + <el-form ref="form" :model="form" :rules="rules" label-width="130px"> | |
| 344 | + <el-form-item label="短信签名" prop="signature"> | |
| 345 | + <el-input v-model="form.signature" placeholder="请输入短信签名" /> | |
| 346 | + </el-form-item> | |
| 347 | + <el-form-item label="渠道编码" prop="code"> | |
| 348 | + <el-select v-model="form.code" placeholder="请选择渠道编码" :disabled="form.id > 0"> | |
| 349 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_CHANNEL_CODE)" | |
| 350 | + :key="dict.value" :label="dict.label" :value="dict.value" /> | |
| 351 | + </el-select> | |
| 352 | + </el-form-item> | |
| 353 | + <el-form-item label="启用状态"> | |
| 354 | + <el-radio-group v-model="form.status"> | |
| 355 | + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | |
| 356 | + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | |
| 357 | + </el-radio-group> | |
| 358 | + </el-form-item> | |
| 359 | + <el-form-item label="备注" prop="remark"> | |
| 360 | + <el-input v-model="form.remark" placeholder="请输入备注" /> | |
| 361 | + </el-form-item> | |
| 362 | + <el-form-item label="短信 API 的账号" prop="apiKey"> | |
| 363 | + <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" /> | |
| 364 | + </el-form-item> | |
| 365 | + <el-form-item v-if="form.code !== 'YUN_PIAN'" label="短信 API 的秘钥" prop="apiSecret"> | |
| 366 | + <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的秘钥" /> | |
| 367 | + </el-form-item> | |
| 368 | + <el-form-item label="短信发送回调 URL" prop="callbackUrl"> | |
| 369 | + <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" /> | |
| 370 | + </el-form-item> | |
| 371 | + </el-form> | |
| 372 | + <div slot="footer" class="dialog-footer"> | |
| 373 | + <el-button type="primary" @click="submitForm">确 定</el-button> | |
| 374 | + <el-button @click="cancel">取 消</el-button> | |
| 375 | + </div> | |
| 376 | + </el-dialog> | |
| 377 | + </div> | |
| 378 | +</template> | |
| 379 | + | |
| 380 | +<script> | |
| 381 | +import { createSmsChannel, updateSmsChannel, deleteSmsChannel, getSmsChannel, getSmsChannelPage } from "@/api/system/sms/smsChannel"; | |
| 382 | + | |
| 383 | +export default { | |
| 384 | + name: "SmsChannel", | |
| 385 | + components: { | |
| 386 | + }, | |
| 387 | + data() { | |
| 388 | + return { | |
| 389 | + // 遮罩层 | |
| 390 | + loading: true, | |
| 391 | + // 显示搜索条件 | |
| 392 | + showSearch: true, | |
| 393 | + // 总条数 | |
| 394 | + total: 0, | |
| 395 | + // 短信渠道列表 | |
| 396 | + list: [], | |
| 397 | + // 弹出层标题 | |
| 398 | + title: "", | |
| 399 | + // 是否显示弹出层 | |
| 400 | + open: false, | |
| 401 | + dateRangeCreateTime: [], | |
| 402 | + // 查询参数 | |
| 403 | + queryParams: { | |
| 404 | + pageNo: 1, | |
| 405 | + pageSize: 10, | |
| 406 | + signature: null, | |
| 407 | + status: null, | |
| 408 | + }, | |
| 409 | + // 表单参数 | |
| 410 | + form: {}, | |
| 411 | + // 表单校验 | |
| 412 | + apiKeyEnableChannelCodes: ['YUN_PIAN'], | |
| 413 | + rules: { | |
| 414 | + signature: [{ required: true, message: "短信签名不能为空", trigger: "blur" }], | |
| 415 | + code: [{ required: true, message: "渠道编码不能为空", trigger: "blur" }], | |
| 416 | + status: [{ required: true, message: "启用状态不能为空", trigger: "blur" }], | |
| 417 | + apiKey: [{ required: true, message: "短信 API 的账号不能为空", trigger: "blur" }], | |
| 418 | + apiSecret: [{ required: true, message: "短信 API 的秘钥不能为空", trigger: "blur" }], | |
| 419 | + } | |
| 420 | + }; | |
| 421 | + }, | |
| 422 | + created() { | |
| 423 | + this.getList(); | |
| 424 | + }, | |
| 425 | + methods: { | |
| 426 | + /** 查询列表 */ | |
| 427 | + getList() { | |
| 428 | + this.loading = true; | |
| 429 | + // 处理查询参数 | |
| 430 | + let params = {...this.queryParams}; | |
| 431 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | |
| 432 | + // 执行查询 | |
| 433 | + getSmsChannelPage(params).then(response => { | |
| 434 | + this.list = response.data.list; | |
| 435 | + this.total = response.data.total; | |
| 436 | + this.loading = false; | |
| 437 | + }); | |
| 438 | + }, | |
| 439 | + /** 取消按钮 */ | |
| 440 | + cancel() { | |
| 441 | + this.open = false; | |
| 442 | + this.reset(); | |
| 443 | + }, | |
| 444 | + /** 表单重置 */ | |
| 445 | + reset() { | |
| 446 | + this.form = { | |
| 447 | + id: undefined, | |
| 448 | + signature: undefined, | |
| 449 | + code: undefined, | |
| 450 | + status: undefined, | |
| 451 | + remark: undefined, | |
| 452 | + apiKey: undefined, | |
| 453 | + apiSecret: undefined, | |
| 454 | + callbackUrl: undefined, | |
| 455 | + }; | |
| 456 | + this.resetForm("form"); | |
| 457 | + }, | |
| 458 | + /** 搜索按钮操作 */ | |
| 459 | + handleQuery() { | |
| 460 | + this.queryParams.pageNo = 1; | |
| 461 | + this.getList(); | |
| 462 | + }, | |
| 463 | + /** 重置按钮操作 */ | |
| 464 | + resetQuery() { | |
| 465 | + this.dateRangeCreateTime = []; | |
| 466 | + this.resetForm("queryForm"); | |
| 467 | + this.handleQuery(); | |
| 468 | + }, | |
| 469 | + /** 新增按钮操作 */ | |
| 470 | + handleAdd() { | |
| 471 | + this.reset(); | |
| 472 | + this.open = true; | |
| 473 | + this.title = "添加短信渠道"; | |
| 474 | + }, | |
| 475 | + /** 修改按钮操作 */ | |
| 476 | + handleUpdate(row) { | |
| 477 | + this.reset(); | |
| 478 | + const id = row.id; | |
| 479 | + getSmsChannel(id).then(response => { | |
| 480 | + this.form = response.data; | |
| 481 | + this.open = true; | |
| 482 | + this.title = "修改短信渠道"; | |
| 483 | + }); | |
| 484 | + }, | |
| 485 | + /** 提交按钮 */ | |
| 486 | + submitForm() { | |
| 487 | + this.$refs["form"].validate(valid => { | |
| 488 | + if (!valid) { | |
| 489 | + return; | |
| 490 | + } | |
| 491 | + // 修改的提交 | |
| 492 | + if (this.form.id != null) { | |
| 493 | + updateSmsChannel(this.form).then(response => { | |
| 494 | + this.msgSuccess("修改成功"); | |
| 495 | + this.open = false; | |
| 496 | + this.getList(); | |
| 497 | + }); | |
| 498 | + return; | |
| 499 | + } | |
| 500 | + // 添加的提交 | |
| 501 | + createSmsChannel(this.form).then(response => { | |
| 502 | + this.msgSuccess("新增成功"); | |
| 503 | + this.open = false; | |
| 504 | + this.getList(); | |
| 505 | + }); | |
| 506 | + }); | |
| 507 | + }, | |
| 508 | + /** 删除按钮操作 */ | |
| 509 | + handleDelete(row) { | |
| 510 | + const id = row.id; | |
| 511 | + this.$confirm('是否确认删除短信渠道编号为"' + id + '"的数据项?', "警告", { | |
| 512 | + confirmButtonText: "确定", | |
| 513 | + cancelButtonText: "取消", | |
| 514 | + type: "warning" | |
| 515 | + }).then(function() { | |
| 516 | + return deleteSmsChannel(id); | |
| 517 | + }).then(() => { | |
| 518 | + this.getList(); | |
| 519 | + this.msgSuccess("删除成功"); | |
| 520 | + }) | |
| 521 | + }, | |
| 522 | + /** 导出按钮操作 */ | |
| 523 | + handleExport() { | |
| 524 | + // 处理查询参数 | |
| 525 | + let params = {...this.queryParams}; | |
| 526 | + params.pageNo = undefined; | |
| 527 | + params.pageSize = undefined; | |
| 528 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | |
| 529 | + // 执行导出 | |
| 530 | + this.$confirm('是否确认导出所有短信渠道数据项?', "警告", { | |
| 531 | + confirmButtonText: "确定", | |
| 532 | + cancelButtonText: "取消", | |
| 533 | + type: "warning" | |
| 534 | + }).then(function() { | |
| 535 | + return exportSmsChannelExcel(params); | |
| 536 | + }).then(response => { | |
| 537 | + this.downloadExcel(response, '短信渠道.xls'); | |
| 538 | + }) | |
| 539 | + } | |
| 540 | + } | |
| 541 | +}; | |
| 542 | +</script> | ... | ... |
ruoyi-ui/src/views/system/sms/smsLog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="app-container"> | |
| 3 | + | |
| 4 | + <!-- 搜索工作栏 --> | |
| 5 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px"> | |
| 6 | + <el-form-item label="手机号" prop="mobile"> | |
| 7 | + <el-input v-model="queryParams.mobile" placeholder="请输入手机号" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 8 | + </el-form-item> | |
| 9 | + <el-form-item label="短信渠道" prop="channelId"> | |
| 10 | + <el-select v-model="queryParams.channelId" placeholder="请选择短信渠道" clearable size="small"> | |
| 11 | + <el-option v-for="channel in channelOptions" | |
| 12 | + :key="channel.id" :value="channel.id" | |
| 13 | + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" /> | |
| 14 | + </el-select> | |
| 15 | + </el-form-item> | |
| 16 | + <el-form-item label="模板编号" prop="templateId"> | |
| 17 | + <el-input v-model="queryParams.templateId" placeholder="请输入模板编号" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 18 | + </el-form-item> | |
| 19 | + <el-form-item label="发送状态" prop="sendStatus"> | |
| 20 | + <el-select v-model="queryParams.sendStatus" placeholder="请选择发送状态" clearable size="small"> | |
| 21 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_SEND_STATUS)" | |
| 22 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 23 | + </el-select> | |
| 24 | + </el-form-item> | |
| 25 | + <el-form-item label="发送时间"> | |
| 26 | + <el-date-picker v-model="dateRangeSendTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 27 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 28 | + </el-form-item> | |
| 29 | + <el-form-item label="接收状态" prop="receiveStatus"> | |
| 30 | + <el-select v-model="queryParams.receiveStatus" placeholder="请选择接收状态" clearable size="small"> | |
| 31 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_RECEIVE_STATUS)" | |
| 32 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 33 | + </el-select> | |
| 34 | + </el-form-item> | |
| 35 | + <el-form-item label="接收时间"> | |
| 36 | + <el-date-picker v-model="dateRangeReceiveTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 37 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 38 | + </el-form-item> | |
| 39 | + <el-form-item> | |
| 40 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |
| 41 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |
| 42 | + </el-form-item> | |
| 43 | + </el-form> | |
| 44 | + | |
| 45 | + <!-- 操作工具栏 --> | |
| 46 | + <el-row :gutter="10" class="mb8"> | |
| 47 | + <el-col :span="1.5"> | |
| 48 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 49 | + v-hasPermi="['system:sms-log:create']">新增</el-button> | |
| 50 | + </el-col> | |
| 51 | + <el-col :span="1.5"> | |
| 52 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | |
| 53 | + v-hasPermi="['system:sms-log:export']">导出</el-button> | |
| 54 | + </el-col> | |
| 55 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |
| 56 | + </el-row> | |
| 57 | + | |
| 58 | + <!-- 列表 --> | |
| 59 | + <el-table v-loading="loading" :data="list"> | |
| 60 | + <el-table-column label="编号" align="center" prop="id" /> | |
| 61 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | |
| 62 | + <template slot-scope="scope"> | |
| 63 | + <span>{{ parseTime(scope.row.createTime) }}</span> | |
| 64 | + </template> | |
| 65 | + </el-table-column> | |
| 66 | + <el-table-column label="手机号" align="center" prop="mobile" width="120"> | |
| 67 | + <template slot-scope="scope"> | |
| 68 | + <div>{{ scope.row.mobile }}</div> | |
| 69 | + <div v-if="scope.row.userType && scope.row.userId"> | |
| 70 | + {{ getDictDataLabel(DICT_TYPE.USER_TYPE, scope.row.userType) + '(' + scope.row.userId + ')' }} | |
| 71 | + </div> | |
| 72 | + </template> | |
| 73 | + </el-table-column> | |
| 74 | + <el-table-column label="短信内容" align="center" prop="templateContent" width="300" /> | |
| 75 | + <el-table-column label="发送状态" align="center" width="180"> | |
| 76 | + <template slot-scope="scope"> | |
| 77 | + <div>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_SEND_STATUS, scope.row.sendStatus) }}</div> | |
| 78 | + <div>{{ parseTime(scope.row.sendTime) }}</div> | |
| 79 | + </template> | |
| 80 | + </el-table-column> | |
| 81 | + <el-table-column label="接收状态" align="center" width="180"> | |
| 82 | + <template slot-scope="scope"> | |
| 83 | + <div>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_RECEIVE_STATUS, scope.row.receiveStatus) }}</div> | |
| 84 | + <div>{{ parseTime(scope.row.receiveTime) }}</div> | |
| 85 | + </template> | |
| 86 | + </el-table-column> | |
| 87 | + <el-table-column label="短信渠道" align="center" width="120"> | |
| 88 | + <template slot-scope="scope"> | |
| 89 | + <div>{{ formatChannelSignature(scope.row.channelId) }}</div> | |
| 90 | + <div>【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.channelCode) }}】</div> | |
| 91 | + </template> | |
| 92 | + </el-table-column> | |
| 93 | + <el-table-column label="模板编号" align="center" prop="templateId" /> | |
| 94 | + <el-table-column label="短信类型" align="center" prop="templateType"> | |
| 95 | + <template slot-scope="scope"> | |
| 96 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, scope.row.templateType) }}</span> | |
| 97 | + </template> | |
| 98 | + </el-table-column> | |
| 99 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | |
| 100 | + <template slot-scope="scope"> | |
| 101 | + <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)" | |
| 102 | + v-hasPermi="['system:sms-log:query']">详细</el-button> | |
| 103 | + </template> | |
| 104 | + </el-table-column> | |
| 105 | + </el-table> | |
| 106 | + <!-- 分页组件 --> | |
| 107 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 108 | + @pagination="getList"/> | |
| 109 | + | |
| 110 | + <!-- 短信日志详细 --> | |
| 111 | + <el-dialog title="短信日志详细" :visible.sync="open" width="700px" append-to-body> | |
| 112 | + <el-form ref="form" :model="form" label-width="140px" size="mini"> | |
| 113 | + <el-row> | |
| 114 | + <el-col :span="24"> | |
| 115 | + <el-form-item label="日志主键:">{{ form.id }}</el-form-item> | |
| 116 | + </el-col> | |
| 117 | + <el-col :span="24"> | |
| 118 | + <el-form-item label="短信渠道:"> | |
| 119 | + {{ formatChannelSignature(form.channelId) }}【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, form.channelCode) }}】 | |
| 120 | + </el-form-item> | |
| 121 | + </el-col> | |
| 122 | + <el-col :span="24"> | |
| 123 | + <el-form-item label="短信模板:"> | |
| 124 | + {{ form.templateId }} | {{ form.templateCode}} | {{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, form.templateType) }} | |
| 125 | + </el-form-item> | |
| 126 | + </el-col> | |
| 127 | + <el-col :span="24"> | |
| 128 | + <el-form-item label="API 的模板编号:">{{ form.apiTemplateId }}</el-form-item> | |
| 129 | + </el-col> | |
| 130 | + <el-col :span="24"> | |
| 131 | + <el-form-item label="用户信息:">{{ form.mobile }} | |
| 132 | + <span v-if="form.userType && form.userId"> | {{ getDictDataLabel(DICT_TYPE.USER_TYPE, form.userType) }} | {{ form.userId }}</span> | |
| 133 | + </el-form-item> | |
| 134 | + </el-col> | |
| 135 | + <el-col :span="24"> | |
| 136 | + <el-form-item label="短信内容:">{{ form.templateContent }}</el-form-item> | |
| 137 | + </el-col> | |
| 138 | + <el-col :span="24"> | |
| 139 | + <el-form-item label="短信参数:">{{ form.templateParams }}</el-form-item> | |
| 140 | + </el-col> | |
| 141 | + <el-col :span="24"> | |
| 142 | + <el-form-item label="创建时间:">{{ parseTime(form.createTime) }}</el-form-item> | |
| 143 | + </el-col> | |
| 144 | + <el-col :span="24"> | |
| 145 | + <el-form-item label="发送状态:">{{ getDictDataLabel(DICT_TYPE.SYS_SMS_SEND_STATUS, form.sendStatus) }}</el-form-item> | |
| 146 | + </el-col> | |
| 147 | + <el-col :span="24"> | |
| 148 | + <el-form-item label="发送时间:">{{ parseTime(form.sendTime) }}</el-form-item> | |
| 149 | + </el-col> | |
| 150 | + <el-col :span="24"> | |
| 151 | + <el-form-item label="发送结果:">{{ form.sendCode }} | {{ form.sendMsg }} | |
| 152 | + </el-form-item> | |
| 153 | + </el-col> | |
| 154 | + <el-col :span="24"> | |
| 155 | + <el-form-item label="API 发送结果:">{{ form.apiSendCode }} | {{ form.apiSendMsg }}</el-form-item> | |
| 156 | + </el-col> | |
| 157 | + <el-col :span="24"> | |
| 158 | + <el-form-item label="API 短信编号:">{{ form.apiSerialNo }}</el-form-item> | |
| 159 | + </el-col> | |
| 160 | + <el-col :span="24"> | |
| 161 | + <el-form-item label="API 请求编号:">{{ form.apiRequestId }}</el-form-item> | |
| 162 | + </el-col> | |
| 163 | + <el-col :span="24"> | |
| 164 | + <el-form-item label="接收状态:">{{ getDictDataLabel(DICT_TYPE.SYS_SMS_RECEIVE_STATUS, form.receiveStatus) }}</el-form-item> | |
| 165 | + </el-col> | |
| 166 | + <el-col :span="24"> | |
| 167 | + <el-form-item label="接收时间:">{{ parseTime(form.receiveTime) }}</el-form-item> | |
| 168 | + </el-col> | |
| 169 | + <el-col :span="24"> | |
| 170 | + <el-form-item label="API 接收结果:">{{ form.apiReceiveCode }} | {{ form.apiReceiveMsg }} | |
| 171 | + </el-form-item> | |
| 172 | + </el-col> | |
| 173 | + </el-row> | |
| 174 | + </el-form> | |
| 175 | + <div slot="footer" class="dialog-footer"> | |
| 176 | + <el-button @click="open = false">关 闭</el-button> | |
| 177 | + </div> | |
| 178 | + </el-dialog> | |
| 179 | + | |
| 180 | + </div> | |
| 181 | +</template> | |
| 182 | + | |
| 183 | +<script> | |
| 184 | +import { getSmsLogPage, exportSmsLogExcel } from "@/api/system/sms/smsLog"; | |
| 185 | +import { getSimpleSmsChannels } from "@/api/system/sms/smsChannel"; | |
| 186 | + | |
| 187 | +export default { | |
| 188 | + name: "SmsLog", | |
| 189 | + components: { | |
| 190 | + }, | |
| 191 | + data() { | |
| 192 | + return { | |
| 193 | + // 遮罩层 | |
| 194 | + loading: true, | |
| 195 | + // 显示搜索条件 | |
| 196 | + showSearch: true, | |
| 197 | + // 总条数 | |
| 198 | + total: 0, | |
| 199 | + // 短信日志列表 | |
| 200 | + list: [], | |
| 201 | + // 弹出层标题 | |
| 202 | + title: "", | |
| 203 | + // 是否显示弹出层 | |
| 204 | + open: false, | |
| 205 | + dateRangeSendTime: [], | |
| 206 | + dateRangeReceiveTime: [], | |
| 207 | + // 表单参数 | |
| 208 | + form: {}, | |
| 209 | + // 查询参数 | |
| 210 | + queryParams: { | |
| 211 | + pageNo: 1, | |
| 212 | + pageSize: 10, | |
| 213 | + channelId: null, | |
| 214 | + templateId: null, | |
| 215 | + mobile: null, | |
| 216 | + sendStatus: null, | |
| 217 | + receiveStatus: null, | |
| 218 | + }, | |
| 219 | + // 短信渠道 | |
| 220 | + channelOptions: [], | |
| 221 | + }; | |
| 222 | + }, | |
| 223 | + created() { | |
| 224 | + this.getList(); | |
| 225 | + // 获得短信渠道 | |
| 226 | + getSimpleSmsChannels().then(response => { | |
| 227 | + this.channelOptions = response.data; | |
| 228 | + }) | |
| 229 | + }, | |
| 230 | + methods: { | |
| 231 | + /** 查询列表 */ | |
| 232 | + getList() { | |
| 233 | + this.loading = true; | |
| 234 | + // 处理查询参数 | |
| 235 | + let params = {...this.queryParams}; | |
| 236 | + this.addBeginAndEndTime(params, this.dateRangeSendTime, 'sendTime'); | |
| 237 | + this.addBeginAndEndTime(params, this.dateRangeReceiveTime, 'receiveTime'); | |
| 238 | + // 执行查询 | |
| 239 | + getSmsLogPage(params).then(response => { | |
| 240 | + this.list = response.data.list; | |
| 241 | + this.total = response.data.total; | |
| 242 | + this.loading = false; | |
| 243 | + }); | |
| 244 | + }, | |
| 245 | + /** 取消按钮 */ | |
| 246 | + cancel() { | |
| 247 | + this.open = false; | |
| 248 | + this.reset(); | |
| 249 | + }, | |
| 250 | + /** 搜索按钮操作 */ | |
| 251 | + handleQuery() { | |
| 252 | + this.queryParams.pageNo = 1; | |
| 253 | + this.getList(); | |
| 254 | + }, | |
| 255 | + /** 重置按钮操作 */ | |
| 256 | + resetQuery() { | |
| 257 | + this.dateRangeSendTime = []; | |
| 258 | + this.dateRangeReceiveTime = []; | |
| 259 | + this.resetForm("queryForm"); | |
| 260 | + this.handleQuery(); | |
| 261 | + }, | |
| 262 | + /** 导出按钮操作 */ | |
| 263 | + handleExport() { | |
| 264 | + // 处理查询参数 | |
| 265 | + let params = {...this.queryParams}; | |
| 266 | + params.pageNo = undefined; | |
| 267 | + params.pageSize = undefined; | |
| 268 | + this.addBeginAndEndTime(params, this.dateRangeSendTime, 'sendTime'); | |
| 269 | + this.addBeginAndEndTime(params, this.dateRangeReceiveTime, 'receiveTime'); | |
| 270 | + // 执行导出 | |
| 271 | + this.$confirm('是否确认导出所有短信日志数据项?', "警告", { | |
| 272 | + confirmButtonText: "确定", | |
| 273 | + cancelButtonText: "取消", | |
| 274 | + type: "warning" | |
| 275 | + }).then(function() { | |
| 276 | + return exportSmsLogExcel(params); | |
| 277 | + }).then(response => { | |
| 278 | + this.downloadExcel(response, '短信日志.xls'); | |
| 279 | + }) | |
| 280 | + }, | |
| 281 | + /** 详细按钮操作 */ | |
| 282 | + handleView(row) { | |
| 283 | + this.open = true; | |
| 284 | + this.form = row; | |
| 285 | + }, | |
| 286 | + /** 格式化短信渠道 */ | |
| 287 | + formatChannelSignature(channelId) { | |
| 288 | + for (const channel of this.channelOptions) { | |
| 289 | + if (channel.id === channelId) { | |
| 290 | + return channel.signature; | |
| 291 | + } | |
| 292 | + } | |
| 293 | + return '找不到签名:' + channelId; | |
| 294 | + } | |
| 295 | + } | |
| 296 | +}; | |
| 297 | +</script> | ... | ... |
ruoyi-ui/src/views/system/sms/smsTemplate.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="app-container"> | |
| 3 | + | |
| 4 | + <!-- 搜索工作栏 --> | |
| 5 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="150px"> | |
| 6 | + <el-form-item label="短信类型" prop="type"> | |
| 7 | + <el-select v-model="queryParams.type" placeholder="请选择短信类型" clearable size="small"> | |
| 8 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE)" | |
| 9 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 10 | + </el-select> | |
| 11 | + </el-form-item> | |
| 12 | + <el-form-item label="开启状态" prop="status"> | |
| 13 | + <el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small"> | |
| 14 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | |
| 15 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 16 | + </el-select> | |
| 17 | + </el-form-item> | |
| 18 | + <el-form-item label="模板编码" prop="code"> | |
| 19 | + <el-input v-model="queryParams.code" placeholder="请输入模板编码" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 20 | + </el-form-item> | |
| 21 | + <el-form-item label="短信 API 的模板编号" prop="apiTemplateId"> | |
| 22 | + <el-input v-model="queryParams.apiTemplateId" placeholder="请输入短信 API 的模板编号" clearable size="small" @keyup.enter.native="handleQuery"/> | |
| 23 | + </el-form-item> | |
| 24 | + <el-form-item label="短信渠道" prop="channelId"> | |
| 25 | + <el-select v-model="queryParams.channelId" placeholder="请选择短信渠道" clearable size="small"> | |
| 26 | + <el-option v-for="channel in channelOptions" | |
| 27 | + :key="channel.id" :value="channel.id" | |
| 28 | + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" /> | |
| 29 | + </el-select> | |
| 30 | + </el-form-item> | |
| 31 | + <el-form-item label="创建时间"> | |
| 32 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 33 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 34 | + </el-form-item> | |
| 35 | + <el-form-item> | |
| 36 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |
| 37 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |
| 38 | + </el-form-item> | |
| 39 | + </el-form> | |
| 40 | + | |
| 41 | + <!-- 操作工具栏 --> | |
| 42 | + <el-row :gutter="10" class="mb8"> | |
| 43 | + <el-col :span="1.5"> | |
| 44 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 45 | + v-hasPermi="['system:sms-template:create']">新增</el-button> | |
| 46 | + </el-col> | |
| 47 | + <el-col :span="1.5"> | |
| 48 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | |
| 49 | + v-hasPermi="['system:sms-template:export']">导出</el-button> | |
| 50 | + </el-col> | |
| 51 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |
| 52 | + </el-row> | |
| 53 | + | |
| 54 | + <!-- 列表 --> | |
| 55 | + <el-table v-loading="loading" :data="list"> | |
| 56 | + <el-table-column label="模板编码" align="center" prop="code" /> | |
| 57 | + <el-table-column label="模板名称" align="center" prop="name" /> | |
| 58 | + <el-table-column label="模板内容" align="center" prop="content" width="300" /> | |
| 59 | + <el-table-column label="短信类型" align="center" prop="type"> | |
| 60 | + <template slot-scope="scope"> | |
| 61 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, scope.row.type) }}</span> | |
| 62 | + </template> | |
| 63 | + </el-table-column> | |
| 64 | + <el-table-column label="开启状态" align="center" prop="status"> | |
| 65 | + <template slot-scope="scope"> | |
| 66 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span> | |
| 67 | + </template> | |
| 68 | + </el-table-column> | |
| 69 | + <el-table-column label="备注" align="center" prop="remark" /> | |
| 70 | + <el-table-column label="短信 API 的模板编号" align="center" prop="apiTemplateId" width="180" /> | |
| 71 | + <el-table-column label="短信渠道" align="center" width="120"> | |
| 72 | + <template slot-scope="scope"> | |
| 73 | + <div>{{ formatChannelSignature(scope.row.channelId) }}</div> | |
| 74 | + <div>【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.channelCode) }}】</div> | |
| 75 | + </template> | |
| 76 | + </el-table-column> | |
| 77 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | |
| 78 | + <template slot-scope="scope"> | |
| 79 | + <span>{{ parseTime(scope.row.createTime) }}</span> | |
| 80 | + </template> | |
| 81 | + </el-table-column> | |
| 82 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150"> | |
| 83 | + <template slot-scope="scope"> | |
| 84 | + <el-button size="mini" type="text" icon="el-icon-share" @click="handleSendSms(scope.row)" | |
| 85 | + v-hasPermi="['system:sms-template:send-sms']">测试</el-button> | |
| 86 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 87 | + v-hasPermi="['system:sms-template:update']">修改</el-button> | |
| 88 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 89 | + v-hasPermi="['system:sms-template:delete']">删除</el-button> | |
| 90 | + </template> | |
| 91 | + </el-table-column> | |
| 92 | + </el-table> | |
| 93 | + <!-- 分页组件 --> | |
| 94 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 95 | + @pagination="getList"/> | |
| 96 | + | |
| 97 | + <!-- 对话框(添加 / 修改) --> | |
| 98 | + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | |
| 99 | + <el-form ref="form" :model="form" :rules="rules" label-width="140px"> | |
| 100 | + <el-form-item label="短信渠道编号" prop="channelId"> | |
| 101 | + <el-select v-model="form.channelId" placeholder="请选择短信渠道编号"> | |
| 102 | + <el-option v-for="channel in channelOptions" | |
| 103 | + :key="channel.id" :value="channel.id" | |
| 104 | + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" /> | |
| 105 | + </el-select> | |
| 106 | + </el-form-item> | |
| 107 | + <el-form-item label="短信类型" prop="type"> | |
| 108 | + <el-select v-model="form.type" placeholder="请选择短信类型"> | |
| 109 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE)" | |
| 110 | + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> | |
| 111 | + </el-select> | |
| 112 | + </el-form-item> | |
| 113 | + <el-form-item label="模板编号" prop="code"> | |
| 114 | + <el-input v-model="form.code" placeholder="请输入模板编号" /> | |
| 115 | + </el-form-item> | |
| 116 | + <el-form-item label="模板名称" prop="name"> | |
| 117 | + <el-input v-model="form.name" placeholder="请输入模板名称" /> | |
| 118 | + </el-form-item> | |
| 119 | + <el-form-item label="模板内容" prop="content"> | |
| 120 | + <el-input type="textarea" v-model="form.content" placeholder="请输入模板内容" /> | |
| 121 | + </el-form-item> | |
| 122 | + <el-form-item label="开启状态" prop="status"> | |
| 123 | + <el-radio-group v-model="form.status"> | |
| 124 | + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | |
| 125 | + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | |
| 126 | + </el-radio-group> | |
| 127 | + </el-form-item> | |
| 128 | + <el-form-item label="短信 API 模板编号" prop="apiTemplateId"> | |
| 129 | + <el-input v-model="form.apiTemplateId" placeholder="请输入短信 API 的模板编号" /> | |
| 130 | + </el-form-item> | |
| 131 | + <el-form-item label="备注" prop="remark"> | |
| 132 | + <el-input v-model="form.remark" placeholder="请输入备注" /> | |
| 133 | + </el-form-item> | |
| 134 | + </el-form> | |
| 135 | + <div slot="footer" class="dialog-footer"> | |
| 136 | + <el-button type="primary" @click="submitForm">确 定</el-button> | |
| 137 | + <el-button @click="cancel">取 消</el-button> | |
| 138 | + </div> | |
| 139 | + </el-dialog> | |
| 140 | + | |
| 141 | + <!-- 对话框(发送短信) --> | |
| 142 | + <el-dialog title="测试发送短信" :visible.sync="sendSmsOpen" width="500px" append-to-body> | |
| 143 | + <el-form ref="sendSmsForm" :model="sendSmsForm" :rules="sendSmsRules" label-width="140px"> | |
| 144 | + <el-form-item label="模板内容" prop="content"> | |
| 145 | + <el-input v-model="sendSmsForm.content" type="textarea" placeholder="请输入模板内容" readonly /> | |
| 146 | + </el-form-item> | |
| 147 | + <el-form-item label="手机号" prop="mobile"> | |
| 148 | + <el-input v-model="sendSmsForm.mobile" placeholder="请输入手机号" /> | |
| 149 | + </el-form-item> | |
| 150 | + <el-form-item v-for="param in sendSmsForm.params" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param"> | |
| 151 | + <el-input v-model="sendSmsForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" /> | |
| 152 | + </el-form-item> | |
| 153 | + </el-form> | |
| 154 | + <div slot="footer" class="dialog-footer"> | |
| 155 | + <el-button type="primary" @click="submitSendSmsForm">确 定</el-button> | |
| 156 | + <el-button @click="cancelSendSms">取 消</el-button> | |
| 157 | + </div> | |
| 158 | + </el-dialog> | |
| 159 | + | |
| 160 | + </div> | |
| 161 | +</template> | |
| 162 | + | |
| 163 | +<script> | |
| 164 | +import { createSmsTemplate, updateSmsTemplate, deleteSmsTemplate, getSmsTemplate, getSmsTemplatePage, | |
| 165 | + exportSmsTemplateExcel, sendSms } from "@/api/system/sms/smsTemplate"; | |
| 166 | +import { getSimpleSmsChannels } from "@/api/system/sms/smsChannel"; | |
| 167 | + | |
| 168 | +export default { | |
| 169 | + name: "SmsTemplate", | |
| 170 | + data() { | |
| 171 | + return { | |
| 172 | + // 遮罩层 | |
| 173 | + loading: true, | |
| 174 | + // 显示搜索条件 | |
| 175 | + showSearch: true, | |
| 176 | + // 总条数 | |
| 177 | + total: 0, | |
| 178 | + // 短信模板列表 | |
| 179 | + list: [], | |
| 180 | + // 弹出层标题 | |
| 181 | + title: "", | |
| 182 | + // 是否显示弹出层 | |
| 183 | + open: false, | |
| 184 | + dateRangeCreateTime: [], | |
| 185 | + // 查询参数 | |
| 186 | + queryParams: { | |
| 187 | + pageNo: 1, | |
| 188 | + pageSize: 10, | |
| 189 | + type: null, | |
| 190 | + status: null, | |
| 191 | + code: null, | |
| 192 | + content: null, | |
| 193 | + apiTemplateId: null, | |
| 194 | + channelId: null, | |
| 195 | + }, | |
| 196 | + // 表单参数 | |
| 197 | + form: {}, | |
| 198 | + // 表单校验 | |
| 199 | + rules: { | |
| 200 | + type: [{ required: true, message: "短信类型不能为空", trigger: "change" }], | |
| 201 | + status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }], | |
| 202 | + code: [{ required: true, message: "模板编码不能为空", trigger: "blur" }], | |
| 203 | + name: [{ required: true, message: "模板名称不能为空", trigger: "blur" }], | |
| 204 | + content: [{ required: true, message: "模板内容不能为空", trigger: "blur" }], | |
| 205 | + apiTemplateId: [{ required: true, message: "短信 API 的模板编号不能为空", trigger: "blur" }], | |
| 206 | + channelId: [{ required: true, message: "短信渠道编号不能为空", trigger: "change" }], | |
| 207 | + }, | |
| 208 | + // 短信渠道 | |
| 209 | + channelOptions: [], | |
| 210 | + // 发送短信 | |
| 211 | + sendSmsOpen: false, | |
| 212 | + sendSmsForm: { | |
| 213 | + params: [], // 模板的参数列表 | |
| 214 | + }, | |
| 215 | + sendSmsRules: { | |
| 216 | + mobile: [{ required: true, message: "手机不能为空", trigger: "blur" }], | |
| 217 | + templateCode: [{ required: true, message: "手机不能为空", trigger: "blur" }], | |
| 218 | + templateParams: { } | |
| 219 | + } | |
| 220 | + }; | |
| 221 | + }, | |
| 222 | + created() { | |
| 223 | + this.getList(); | |
| 224 | + // 获得短信渠道 | |
| 225 | + getSimpleSmsChannels().then(response => { | |
| 226 | + this.channelOptions = response.data; | |
| 227 | + }) | |
| 228 | + }, | |
| 229 | + methods: { | |
| 230 | + /** 查询列表 */ | |
| 231 | + getList() { | |
| 232 | + this.loading = true; | |
| 233 | + // 处理查询参数 | |
| 234 | + let params = {...this.queryParams}; | |
| 235 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | |
| 236 | + // 执行查询 | |
| 237 | + getSmsTemplatePage(params).then(response => { | |
| 238 | + this.list = response.data.list; | |
| 239 | + this.total = response.data.total; | |
| 240 | + this.loading = false; | |
| 241 | + }); | |
| 242 | + }, | |
| 243 | + /** 取消按钮 */ | |
| 244 | + cancel() { | |
| 245 | + this.open = false; | |
| 246 | + this.reset(); | |
| 247 | + }, | |
| 248 | + /** 表单重置 */ | |
| 249 | + reset() { | |
| 250 | + this.form = { | |
| 251 | + id: undefined, | |
| 252 | + type: undefined, | |
| 253 | + status: undefined, | |
| 254 | + code: undefined, | |
| 255 | + name: undefined, | |
| 256 | + content: undefined, | |
| 257 | + remark: undefined, | |
| 258 | + apiTemplateId: undefined, | |
| 259 | + channelId: undefined, | |
| 260 | + }; | |
| 261 | + this.resetForm("form"); | |
| 262 | + }, | |
| 263 | + /** 搜索按钮操作 */ | |
| 264 | + handleQuery() { | |
| 265 | + this.queryParams.pageNo = 1; | |
| 266 | + this.getList(); | |
| 267 | + }, | |
| 268 | + /** 重置按钮操作 */ | |
| 269 | + resetQuery() { | |
| 270 | + this.dateRangeCreateTime = []; | |
| 271 | + this.resetForm("queryForm"); | |
| 272 | + this.handleQuery(); | |
| 273 | + }, | |
| 274 | + /** 新增按钮操作 */ | |
| 275 | + handleAdd() { | |
| 276 | + this.reset(); | |
| 277 | + this.open = true; | |
| 278 | + this.title = "添加短信模板"; | |
| 279 | + }, | |
| 280 | + /** 修改按钮操作 */ | |
| 281 | + handleUpdate(row) { | |
| 282 | + this.reset(); | |
| 283 | + const id = row.id; | |
| 284 | + getSmsTemplate(id).then(response => { | |
| 285 | + this.form = response.data; | |
| 286 | + this.open = true; | |
| 287 | + this.title = "修改短信模板"; | |
| 288 | + }); | |
| 289 | + }, | |
| 290 | + /** 提交按钮 */ | |
| 291 | + submitForm() { | |
| 292 | + this.$refs["form"].validate(valid => { | |
| 293 | + if (!valid) { | |
| 294 | + return; | |
| 295 | + } | |
| 296 | + // 修改的提交 | |
| 297 | + if (this.form.id != null) { | |
| 298 | + updateSmsTemplate(this.form).then(response => { | |
| 299 | + this.msgSuccess("修改成功"); | |
| 300 | + this.open = false; | |
| 301 | + this.getList(); | |
| 302 | + }); | |
| 303 | + return; | |
| 304 | + } | |
| 305 | + // 添加的提交 | |
| 306 | + createSmsTemplate(this.form).then(response => { | |
| 307 | + this.msgSuccess("新增成功"); | |
| 308 | + this.open = false; | |
| 309 | + this.getList(); | |
| 310 | + }); | |
| 311 | + }); | |
| 312 | + }, | |
| 313 | + /** 删除按钮操作 */ | |
| 314 | + handleDelete(row) { | |
| 315 | + const id = row.id; | |
| 316 | + this.$confirm('是否确认删除短信模板编号为"' + id + '"的数据项?', "警告", { | |
| 317 | + confirmButtonText: "确定", | |
| 318 | + cancelButtonText: "取消", | |
| 319 | + type: "warning" | |
| 320 | + }).then(function() { | |
| 321 | + return deleteSmsTemplate(id); | |
| 322 | + }).then(() => { | |
| 323 | + this.getList(); | |
| 324 | + this.msgSuccess("删除成功"); | |
| 325 | + }) | |
| 326 | + }, | |
| 327 | + /** 导出按钮操作 */ | |
| 328 | + handleExport() { | |
| 329 | + // 处理查询参数 | |
| 330 | + let params = {...this.queryParams}; | |
| 331 | + params.pageNo = undefined; | |
| 332 | + params.pageSize = undefined; | |
| 333 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | |
| 334 | + // 执行导出 | |
| 335 | + this.$confirm('是否确认导出所有短信模板数据项?', "警告", { | |
| 336 | + confirmButtonText: "确定", | |
| 337 | + cancelButtonText: "取消", | |
| 338 | + type: "warning" | |
| 339 | + }).then(function() { | |
| 340 | + return exportSmsTemplateExcel(params); | |
| 341 | + }).then(response => { | |
| 342 | + this.downloadExcel(response, '短信模板.xls'); | |
| 343 | + }) | |
| 344 | + }, | |
| 345 | + /** 发送短息按钮 */ | |
| 346 | + handleSendSms(row) { | |
| 347 | + this.resetSendSms(row); | |
| 348 | + // 设置参数 | |
| 349 | + this.sendSmsForm.content = row.content; | |
| 350 | + this.sendSmsForm.params = row.params; | |
| 351 | + this.sendSmsForm.templateCode = row.code; | |
| 352 | + this.sendSmsForm.templateParams = row.params.reduce(function(obj, item) { | |
| 353 | + obj[item] = undefined; | |
| 354 | + return obj; | |
| 355 | + }, {}); | |
| 356 | + // 根据 row 重置 rules | |
| 357 | + this.sendSmsRules.templateParams = row.params.reduce(function(obj, item) { | |
| 358 | + obj[item] = { required: true, message: '参数 ' + item + " 不能为空", trigger: "change" }; | |
| 359 | + return obj; | |
| 360 | + }, {}); | |
| 361 | + // 设置打开 | |
| 362 | + this.sendSmsOpen = true; | |
| 363 | + }, | |
| 364 | + /** 重置发送短信的表单 */ | |
| 365 | + resetSendSms() { | |
| 366 | + // 根据 row 重置表单 | |
| 367 | + this.sendSmsForm = { | |
| 368 | + content: undefined, | |
| 369 | + params: undefined, | |
| 370 | + mobile: undefined, | |
| 371 | + templateCode: undefined, | |
| 372 | + templateParams: {} | |
| 373 | + }; | |
| 374 | + this.resetForm("sendSmsForm"); | |
| 375 | + }, | |
| 376 | + /** 取消发送短信 */ | |
| 377 | + cancelSendSms() { | |
| 378 | + this.sendSmsOpen = false; | |
| 379 | + this.resetSendSms(); | |
| 380 | + }, | |
| 381 | + /** 提交按钮 */ | |
| 382 | + submitSendSmsForm() { | |
| 383 | + this.$refs["sendSmsForm"].validate(valid => { | |
| 384 | + if (!valid) { | |
| 385 | + return; | |
| 386 | + } | |
| 387 | + // 添加的提交 | |
| 388 | + sendSms(this.sendSmsForm).then(response => { | |
| 389 | + this.msgSuccess("提交发送成功!发送结果,见发送日志编号:" + response.data); | |
| 390 | + this.sendSmsOpen = false; | |
| 391 | + }); | |
| 392 | + }); | |
| 393 | + }, | |
| 394 | + /** 格式化短信渠道 */ | |
| 395 | + formatChannelSignature(channelId) { | |
| 396 | + for (const channel of this.channelOptions) { | |
| 397 | + if (channel.id === channelId) { | |
| 398 | + return channel.signature; | |
| 399 | + } | |
| 400 | + } | |
| 401 | + return '找不到签名:' + channelId; | |
| 402 | + } | |
| 403 | + } | |
| 404 | +}; | |
| 405 | +</script> | ... | ... |
ruoyi-ui/src/views/system/user/index.vue
| 1 | -<template> | |
| 2 | - <div class="app-container"> | |
| 3 | - <el-row :gutter="20"> | |
| 4 | - <!--部门数据--> | |
| 5 | - <el-col :span="4" :xs="24"> | |
| 6 | - <div class="head-container"> | |
| 7 | - <el-input | |
| 8 | - v-model="deptName" | |
| 9 | - placeholder="请输入部门名称" | |
| 10 | - clearable | |
| 11 | - size="small" | |
| 12 | - prefix-icon="el-icon-search" | |
| 13 | - style="margin-bottom: 20px" | |
| 14 | - /> | |
| 15 | - </div> | |
| 16 | - <div class="head-container"> | |
| 17 | - <el-tree | |
| 18 | - :data="deptOptions" | |
| 19 | - :props="defaultProps" | |
| 20 | - :expand-on-click-node="false" | |
| 21 | - :filter-node-method="filterNode" | |
| 22 | - ref="tree" | |
| 23 | - default-expand-all | |
| 24 | - @node-click="handleNodeClick" | |
| 25 | - /> | |
| 26 | - </div> | |
| 27 | - </el-col> | |
| 28 | - <!--用户数据--> | |
| 29 | - <el-col :span="20" :xs="24"> | |
| 30 | - <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> | |
| 31 | - <el-form-item label="用户名称" prop="username"> | |
| 32 | - <el-input | |
| 33 | - v-model="queryParams.username" | |
| 34 | - placeholder="请输入用户名称" | |
| 35 | - clearable | |
| 36 | - size="small" | |
| 37 | - style="width: 240px" | |
| 38 | - @keyup.enter.native="handleQuery" | |
| 39 | - /> | |
| 40 | - </el-form-item> | |
| 41 | - <el-form-item label="手机号码" prop="mobile"> | |
| 42 | - <el-input | |
| 43 | - v-model="queryParams.mobile" | |
| 44 | - placeholder="请输入手机号码" | |
| 45 | - clearable | |
| 46 | - size="small" | |
| 47 | - style="width: 240px" | |
| 48 | - @keyup.enter.native="handleQuery" | |
| 49 | - /> | |
| 50 | - </el-form-item> | |
| 51 | - <el-form-item label="状态" prop="status"> | |
| 52 | - <el-select | |
| 53 | - v-model="queryParams.status" | |
| 54 | - placeholder="用户状态" | |
| 55 | - clearable | |
| 56 | - size="small" | |
| 57 | - style="width: 240px" | |
| 58 | - > | |
| 59 | - <el-option | |
| 60 | - v-for="dict in statusDictDatas" | |
| 61 | - :key="parseInt(dict.value)" | |
| 62 | - :label="dict.label" | |
| 63 | - :value="parseInt(dict.value)" | |
| 64 | - /> | |
| 65 | - </el-select> | |
| 66 | - </el-form-item> | |
| 67 | - <el-form-item label="创建时间"> | |
| 68 | - <el-date-picker | |
| 69 | - v-model="dateRange" | |
| 70 | - size="small" | |
| 71 | - style="width: 240px" | |
| 72 | - value-format="yyyy-MM-dd" | |
| 73 | - type="daterange" | |
| 74 | - range-separator="-" | |
| 75 | - start-placeholder="开始日期" | |
| 76 | - end-placeholder="结束日期" | |
| 77 | - ></el-date-picker> | |
| 78 | - </el-form-item> | |
| 79 | - <el-form-item> | |
| 80 | - <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |
| 81 | - <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |
| 82 | - </el-form-item> | |
| 83 | - </el-form> | |
| 84 | - | |
| 85 | - <el-row :gutter="10" class="mb8"> | |
| 86 | - <el-col :span="1.5"> | |
| 87 | - <el-button | |
| 88 | - type="primary" | |
| 89 | - icon="el-icon-plus" | |
| 90 | - size="mini" | |
| 91 | - @click="handleAdd" | |
| 92 | - v-hasPermi="['system:user:add']" | |
| 93 | - >新增</el-button> | |
| 94 | - </el-col> | |
| 95 | - <el-col :span="1.5"> | |
| 96 | - <el-button | |
| 97 | - type="info" | |
| 98 | - icon="el-icon-upload2" | |
| 99 | - size="mini" | |
| 100 | - @click="handleImport" | |
| 101 | - v-hasPermi="['system:user:import']" | |
| 102 | - >导入</el-button> | |
| 103 | - </el-col> | |
| 104 | - <el-col :span="1.5"> | |
| 105 | - <el-button | |
| 106 | - type="warning" | |
| 107 | - icon="el-icon-download" | |
| 108 | - size="mini" | |
| 109 | - @click="handleExport" | |
| 110 | - v-hasPermi="['system:user:export']" | |
| 111 | - >导出</el-button> | |
| 112 | - </el-col> | |
| 113 | - <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |
| 114 | - </el-row> | |
| 115 | - | |
| 116 | - <el-table v-loading="loading" :data="userList"> | |
| 117 | - <el-table-column label="用户编号" align="center" prop="id" /> | |
| 118 | - <el-table-column label="用户名称" align="center" prop="username" :show-overflow-tooltip="true" /> | |
| 119 | - <el-table-column label="用户昵称" align="center" prop="nickname" :show-overflow-tooltip="true" /> | |
| 120 | - <el-table-column label="部门" align="center" prop="dept.name" :show-overflow-tooltip="true" /> | |
| 121 | - <el-table-column label="手机号码" align="center" prop="mobile" width="120" /> | |
| 122 | - <el-table-column label="状态" align="center"> | |
| 123 | - <template slot-scope="scope"> | |
| 124 | - <el-switch | |
| 125 | - v-model="scope.row.status" | |
| 126 | - :active-value="0" | |
| 127 | - :inactive-value="1" | |
| 128 | - @change="handleStatusChange(scope.row)" | |
| 129 | - ></el-switch> | |
| 130 | - </template> | |
| 131 | - </el-table-column> | |
| 132 | - <el-table-column label="创建时间" align="center" prop="createTime" width="160"> | |
| 133 | - <template slot-scope="scope"> | |
| 134 | - <span>{{ parseTime(scope.row.createTime) }}</span> | |
| 135 | - </template> | |
| 136 | - </el-table-column> | |
| 137 | - <el-table-column | |
| 138 | - label="操作" | |
| 139 | - align="center" | |
| 140 | - width="160" | |
| 141 | - class-name="small-padding fixed-width" | |
| 142 | - > | |
| 143 | - <template slot-scope="scope"> | |
| 144 | - <el-button | |
| 145 | - size="mini" | |
| 146 | - type="text" | |
| 147 | - icon="el-icon-edit" | |
| 148 | - @click="handleUpdate(scope.row)" | |
| 149 | - v-hasPermi="['system:user:edit']" | |
| 150 | - >修改</el-button> | |
| 151 | - <el-button | |
| 152 | - v-if="scope.row.id !== 1" | |
| 153 | - size="mini" | |
| 154 | - type="text" | |
| 155 | - icon="el-icon-delete" | |
| 156 | - @click="handleDelete(scope.row)" | |
| 157 | - v-hasPermi="['system:user:remove']" | |
| 158 | - >删除</el-button> | |
| 159 | - <el-button | |
| 160 | - size="mini" | |
| 161 | - type="text" | |
| 162 | - icon="el-icon-key" | |
| 163 | - @click="handleResetPwd(scope.row)" | |
| 164 | - v-hasPermi="['system:user:resetPwd']" | |
| 165 | - >重置</el-button> | |
| 166 | - <el-button | |
| 167 | - size="mini" | |
| 168 | - type="text" | |
| 169 | - icon="el-icon-circle-check" | |
| 170 | - @click="handleRole(scope.row)" | |
| 171 | - v-hasPermi="['system:permission:assign-user-role']" | |
| 172 | - >分配角色</el-button> | |
| 173 | - </template> | |
| 174 | - </el-table-column> | |
| 175 | - </el-table> | |
| 176 | - | |
| 177 | - <pagination | |
| 178 | - v-show="total>0" | |
| 179 | - :total="total" | |
| 180 | - :page.sync="queryParams.pageNo" | |
| 181 | - :limit.sync="queryParams.pageSize" | |
| 182 | - @pagination="getList" | |
| 183 | - /> | |
| 184 | - </el-col> | |
| 185 | - </el-row> | |
| 186 | - | |
| 187 | - <!-- 添加或修改参数配置对话框 --> | |
| 188 | - <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body> | |
| 189 | - <el-form ref="form" :model="form" :rules="rules" label-width="80px"> | |
| 190 | - <el-row> | |
| 191 | - <el-col :span="12"> | |
| 192 | - <el-form-item label="用户昵称" prop="nickname"> | |
| 193 | - <el-input v-model="form.nickname" placeholder="请输入用户昵称" /> | |
| 194 | - </el-form-item> | |
| 195 | - </el-col> | |
| 196 | - <el-col :span="12"> | |
| 197 | - <el-form-item label="归属部门" prop="deptId"> | |
| 198 | - <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" | |
| 199 | - placeholder="请选择归属部门" :normalizer="normalizer" | |
| 200 | - /> | |
| 201 | - </el-form-item> | |
| 202 | - </el-col> | |
| 203 | - </el-row> | |
| 204 | - <el-row> | |
| 205 | - <el-col :span="12"> | |
| 206 | - <el-form-item label="手机号码" prop="mobile"> | |
| 207 | - <el-input v-model="form.mobile" placeholder="请输入手机号码" maxlength="11" /> | |
| 208 | - </el-form-item> | |
| 209 | - </el-col> | |
| 210 | - <el-col :span="12"> | |
| 211 | - <el-form-item label="邮箱" prop="email"> | |
| 212 | - <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" /> | |
| 213 | - </el-form-item> | |
| 214 | - </el-col> | |
| 215 | - </el-row> | |
| 216 | - <el-row> | |
| 217 | - <el-col :span="12"> | |
| 218 | - <el-form-item v-if="form.id === undefined" label="用户名称" prop="username"> | |
| 219 | - <el-input v-model="form.username" placeholder="请输入用户名称" /> | |
| 220 | - </el-form-item> | |
| 221 | - </el-col> | |
| 222 | - <el-col :span="12"> | |
| 223 | - <el-form-item v-if="form.id === undefined" label="用户密码" prop="password"> | |
| 224 | - <el-input v-model="form.password" placeholder="请输入用户密码" type="password" /> | |
| 225 | - </el-form-item> | |
| 226 | - </el-col> | |
| 227 | - </el-row> | |
| 228 | - <el-row> | |
| 229 | - <el-col :span="12"> | |
| 230 | - <el-form-item label="用户性别"> | |
| 231 | - <el-select v-model="form.sex" placeholder="请选择"> | |
| 232 | - <el-option | |
| 233 | - v-for="dict in sexDictDatas" | |
| 234 | - :key="parseInt(dict.value)" | |
| 235 | - :label="dict.label" | |
| 236 | - :value="parseInt(dict.value)" | |
| 237 | - ></el-option> | |
| 238 | - </el-select> | |
| 239 | - </el-form-item> | |
| 240 | - </el-col> | |
| 241 | - <el-col :span="12"> | |
| 242 | - <el-form-item label="岗位"> | |
| 243 | - <el-select v-model="form.postIds" multiple placeholder="请选择"> | |
| 244 | - <el-option | |
| 245 | - v-for="item in postOptions" | |
| 246 | - :key="item.id" | |
| 247 | - :label="item.name" | |
| 248 | - :value="item.id" | |
| 249 | - ></el-option> | |
| 250 | - </el-select> | |
| 251 | - </el-form-item> | |
| 252 | - </el-col> | |
| 253 | - </el-row> | |
| 254 | - <el-row> | |
| 255 | - <el-col :span="24"> | |
| 256 | - <el-form-item label="备注"> | |
| 257 | - <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> | |
| 258 | - </el-form-item> | |
| 259 | - </el-col> | |
| 260 | - </el-row> | |
| 261 | - </el-form> | |
| 262 | - <div slot="footer" class="dialog-footer"> | |
| 263 | - <el-button type="primary" @click="submitForm">确 定</el-button> | |
| 264 | - <el-button @click="cancel">取 消</el-button> | |
| 265 | - </div> | |
| 266 | - </el-dialog> | |
| 267 | - | |
| 268 | - <!-- 用户导入对话框 --> | |
| 269 | - <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body> | |
| 270 | - <el-upload | |
| 271 | - ref="upload" | |
| 272 | - :limit="1" | |
| 273 | - accept=".xlsx, .xls" | |
| 274 | - :headers="upload.headers" | |
| 275 | - :action="upload.url + '?updateSupport=' + upload.updateSupport" | |
| 276 | - :disabled="upload.isUploading" | |
| 277 | - :on-progress="handleFileUploadProgress" | |
| 278 | - :on-success="handleFileSuccess" | |
| 279 | - :auto-upload="false" | |
| 280 | - drag | |
| 281 | - > | |
| 282 | - <i class="el-icon-upload"></i> | |
| 283 | - <div class="el-upload__text"> | |
| 284 | - 将文件拖到此处,或 | |
| 285 | - <em>点击上传</em> | |
| 286 | - </div> | |
| 287 | - <div class="el-upload__tip" slot="tip"> | |
| 288 | - <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据 | |
| 289 | - <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link> | |
| 290 | - </div> | |
| 291 | - <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div> | |
| 292 | - </el-upload> | |
| 293 | - <div slot="footer" class="dialog-footer"> | |
| 294 | - <el-button type="primary" @click="submitFileForm">确 定</el-button> | |
| 295 | - <el-button @click="upload.open = false">取 消</el-button> | |
| 296 | - </div> | |
| 297 | - </el-dialog> | |
| 298 | - | |
| 299 | - <!-- 分配角色 --> | |
| 300 | - <el-dialog title="分配角色" :visible.sync="openRole" width="500px" append-to-body> | |
| 301 | - <el-form :model="form" label-width="80px"> | |
| 302 | - <el-form-item label="用户名称"> | |
| 303 | - <el-input v-model="form.username" :disabled="true" /> | |
| 304 | - </el-form-item> | |
| 305 | - <el-form-item label="用户昵称"> | |
| 306 | - <el-input v-model="form.nickname" :disabled="true" /> | |
| 307 | - </el-form-item> | |
| 308 | - <el-form-item label="角色"> | |
| 309 | - <el-select v-model="form.roleIds" multiple placeholder="请选择"> | |
| 310 | - <el-option | |
| 311 | - v-for="item in roleOptions" | |
| 312 | - :key="parseInt(item.id)" | |
| 313 | - :label="item.name" | |
| 314 | - :value="parseInt(item.id)" | |
| 315 | - ></el-option> | |
| 316 | - </el-select> | |
| 317 | - </el-form-item> | |
| 318 | - </el-form> | |
| 319 | - <div slot="footer" class="dialog-footer"> | |
| 320 | - <el-button type="primary" @click="submitRole">确 定</el-button> | |
| 321 | - <el-button @click="cancelRole">取 消</el-button> | |
| 322 | - </div> | |
| 323 | - </el-dialog> | |
| 324 | - | |
| 325 | - </div> | |
| 326 | -</template> | |
| 327 | - | |
| 328 | -<script> | |
| 329 | -import { listUser, getUser, delUser, addUser, updateUser, exportUser, resetUserPwd, changeUserStatus, importTemplate } from "@/api/system/user"; | |
| 330 | -import { getToken } from "@/utils/auth"; | |
| 331 | -import Treeselect from "@riophae/vue-treeselect"; | |
| 332 | -import "@riophae/vue-treeselect/dist/vue-treeselect.css"; | |
| 333 | - | |
| 334 | -import {listSimpleDepts} from "@/api/system/dept"; | |
| 335 | -import {listSimplePosts} from "@/api/system/post"; | |
| 336 | - | |
| 337 | -import {SysCommonStatusEnum} from "@/utils/constants"; | |
| 338 | -import {DICT_TYPE, getDictDatas} from "@/utils/dict"; | |
| 339 | -import {assignUserRole, listUserRoles} from "@/api/system/permission"; | |
| 340 | -import {listSimpleRoles} from "@/api/system/role"; | |
| 341 | - | |
| 342 | -export default { | |
| 343 | - name: "User", | |
| 344 | - components: { Treeselect }, | |
| 345 | - data() { | |
| 346 | - return { | |
| 347 | - // 遮罩层 | |
| 348 | - loading: true, | |
| 349 | - // 显示搜索条件 | |
| 350 | - showSearch: true, | |
| 351 | - // 总条数 | |
| 352 | - total: 0, | |
| 353 | - // 用户表格数据 | |
| 354 | - userList: null, | |
| 355 | - // 弹出层标题 | |
| 356 | - title: "", | |
| 357 | - // 部门树选项 | |
| 358 | - deptOptions: undefined, | |
| 359 | - // 是否显示弹出层 | |
| 360 | - open: false, | |
| 361 | - // 部门名称 | |
| 362 | - deptName: undefined, | |
| 363 | - // 默认密码 | |
| 364 | - initPassword: undefined, | |
| 365 | - // 日期范围 | |
| 366 | - dateRange: [], | |
| 367 | - // 状态数据字典 | |
| 368 | - statusOptions: [], | |
| 369 | - // 性别状态字典 | |
| 370 | - sexOptions: [], | |
| 371 | - // 岗位选项 | |
| 372 | - postOptions: [], | |
| 373 | - // 角色选项 | |
| 374 | - roleOptions: [], | |
| 375 | - // 表单参数 | |
| 376 | - form: {}, | |
| 377 | - defaultProps: { | |
| 378 | - children: "children", | |
| 379 | - label: "name" | |
| 380 | - }, | |
| 381 | - // 用户导入参数 | |
| 382 | - upload: { | |
| 383 | - // 是否显示弹出层(用户导入) | |
| 384 | - open: false, | |
| 385 | - // 弹出层标题(用户导入) | |
| 386 | - title: "", | |
| 387 | - // 是否禁用上传 | |
| 388 | - isUploading: false, | |
| 389 | - // 是否更新已经存在的用户数据 | |
| 390 | - updateSupport: 0, | |
| 391 | - // 设置上传的请求头部 | |
| 392 | - headers: { Authorization: "Bearer " + getToken() }, | |
| 393 | - // 上传的地址 | |
| 394 | - url: process.env.VUE_APP_BASE_API + '/api/' + "/system/user/import" | |
| 395 | - }, | |
| 396 | - // 查询参数 | |
| 397 | - queryParams: { | |
| 398 | - pageNo: 1, | |
| 399 | - pageSize: 10, | |
| 400 | - username: undefined, | |
| 401 | - mobile: undefined, | |
| 402 | - status: undefined, | |
| 403 | - deptId: undefined | |
| 404 | - }, | |
| 405 | - // 表单校验 | |
| 406 | - rules: { | |
| 407 | - username: [ | |
| 408 | - { required: true, message: "用户名称不能为空", trigger: "blur" } | |
| 409 | - ], | |
| 410 | - nickname: [ | |
| 411 | - { required: true, message: "用户昵称不能为空", trigger: "blur" } | |
| 412 | - ], | |
| 413 | - password: [ | |
| 414 | - { required: true, message: "用户密码不能为空", trigger: "blur" } | |
| 415 | - ], | |
| 416 | - email: [ | |
| 417 | - { | |
| 418 | - type: "email", | |
| 419 | - message: "'请输入正确的邮箱地址", | |
| 420 | - trigger: ["blur", "change"] | |
| 421 | - } | |
| 422 | - ], | |
| 423 | - mobile: [ | |
| 424 | - { | |
| 425 | - pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, | |
| 426 | - message: "请输入正确的手机号码", | |
| 427 | - trigger: "blur" | |
| 428 | - } | |
| 429 | - ] | |
| 430 | - }, | |
| 431 | - // 是否显示弹出层(角色权限) | |
| 432 | - openRole: false, | |
| 433 | - | |
| 434 | - // 枚举 | |
| 435 | - SysCommonStatusEnum: SysCommonStatusEnum, | |
| 436 | - // 数据字典 | |
| 437 | - statusDictDatas: getDictDatas(DICT_TYPE.SYS_COMMON_STATUS), | |
| 438 | - sexDictDatas: getDictDatas(DICT_TYPE.SYS_USER_SEX), | |
| 439 | - }; | |
| 440 | - }, | |
| 441 | - watch: { | |
| 442 | - // 根据名称筛选部门树 | |
| 443 | - deptName(val) { | |
| 444 | - this.$refs.tree.filter(val); | |
| 445 | - } | |
| 446 | - }, | |
| 447 | - created() { | |
| 448 | - this.getList(); | |
| 449 | - this.getTreeselect(); | |
| 450 | - this.getConfigKey("sys.user.initPassword").then(response => { | |
| 451 | - this.initPassword = response.msg; | |
| 452 | - }); | |
| 453 | - }, | |
| 454 | - methods: { | |
| 455 | - /** 查询用户列表 */ | |
| 456 | - getList() { | |
| 457 | - this.loading = true; | |
| 458 | - listUser(this.addDateRange(this.queryParams, [ | |
| 459 | - this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined, | |
| 460 | - this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined, | |
| 461 | - ])).then(response => { | |
| 462 | - this.userList = response.data.list; | |
| 463 | - this.total = response.data.total; | |
| 464 | - this.loading = false; | |
| 465 | - } | |
| 466 | - ); | |
| 467 | - }, | |
| 468 | - /** 查询部门下拉树结构 + 岗位下拉 */ | |
| 469 | - getTreeselect() { | |
| 470 | - listSimpleDepts().then(response => { | |
| 471 | - // 处理 deptOptions 参数 | |
| 472 | - this.deptOptions = []; | |
| 473 | - this.deptOptions.push(...this.handleTree(response.data, "id")); | |
| 474 | - }); | |
| 475 | - listSimplePosts().then(response => { | |
| 476 | - // 处理 postOptions 参数 | |
| 477 | - this.postOptions = []; | |
| 478 | - this.postOptions.push(...response.data); | |
| 479 | - }); | |
| 480 | - }, | |
| 481 | - // 筛选节点 | |
| 482 | - filterNode(value, data) { | |
| 483 | - if (!value) return true; | |
| 484 | - return data.name.indexOf(value) !== -1; | |
| 485 | - }, | |
| 486 | - // 节点单击事件 | |
| 487 | - handleNodeClick(data) { | |
| 488 | - this.queryParams.deptId = data.id; | |
| 489 | - this.getList(); | |
| 490 | - }, | |
| 491 | - // 用户状态修改 | |
| 492 | - handleStatusChange(row) { | |
| 493 | - let text = row.status === SysCommonStatusEnum.ENABLE ? "启用" : "停用"; | |
| 494 | - this.$confirm('确认要"' + text + '""' + row.username + '"用户吗?', "警告", { | |
| 495 | - confirmButtonText: "确定", | |
| 496 | - cancelButtonText: "取消", | |
| 497 | - type: "warning" | |
| 498 | - }).then(function() { | |
| 499 | - return changeUserStatus(row.id, row.status); | |
| 500 | - }).then(() => { | |
| 501 | - this.msgSuccess(text + "成功"); | |
| 502 | - }).catch(function() { | |
| 503 | - row.status = row.status === SysCommonStatusEnum.ENABLE ? SysCommonStatusEnum.DISABLE | |
| 504 | - : SysCommonStatusEnum.ENABLE; | |
| 505 | - }); | |
| 506 | - }, | |
| 507 | - // 取消按钮 | |
| 508 | - cancel() { | |
| 509 | - this.open = false; | |
| 510 | - this.reset(); | |
| 511 | - }, | |
| 512 | - // 取消按钮(角色权限) | |
| 513 | - cancelRole() { | |
| 514 | - this.openRole = false; | |
| 515 | - this.reset(); | |
| 516 | - }, | |
| 517 | - // 表单重置 | |
| 518 | - reset() { | |
| 519 | - this.form = { | |
| 520 | - id: undefined, | |
| 521 | - deptId: undefined, | |
| 522 | - username: undefined, | |
| 523 | - nickname: undefined, | |
| 524 | - password: undefined, | |
| 525 | - mobile: undefined, | |
| 526 | - email: undefined, | |
| 527 | - sex: undefined, | |
| 528 | - status: "0", | |
| 529 | - remark: undefined, | |
| 530 | - postIds: [], | |
| 531 | - roleIds: [] | |
| 532 | - }; | |
| 533 | - this.resetForm("form"); | |
| 534 | - }, | |
| 535 | - /** 搜索按钮操作 */ | |
| 536 | - handleQuery() { | |
| 537 | - this.queryParams.pageNo = 1; | |
| 538 | - this.getList(); | |
| 539 | - }, | |
| 540 | - /** 重置按钮操作 */ | |
| 541 | - resetQuery() { | |
| 542 | - this.dateRange = []; | |
| 543 | - this.resetForm("queryForm"); | |
| 544 | - this.handleQuery(); | |
| 545 | - }, | |
| 546 | - /** 新增按钮操作 */ | |
| 547 | - handleAdd() { | |
| 548 | - this.reset(); | |
| 549 | - // 获得下拉数据 | |
| 550 | - this.getTreeselect(); | |
| 551 | - // 打开表单,并设置初始化 | |
| 552 | - this.open = true; | |
| 553 | - this.title = "添加用户"; | |
| 554 | - this.form.password = this.initPassword; | |
| 555 | - }, | |
| 556 | - /** 修改按钮操作 */ | |
| 557 | - handleUpdate(row) { | |
| 558 | - this.reset(); | |
| 559 | - this.getTreeselect(); | |
| 560 | - const id = row.id; | |
| 561 | - getUser(id).then(response => { | |
| 562 | - this.form = response.data; | |
| 563 | - this.open = true; | |
| 564 | - this.title = "修改用户"; | |
| 565 | - this.form.password = ""; | |
| 566 | - }); | |
| 567 | - }, | |
| 568 | - /** 重置密码按钮操作 */ | |
| 569 | - handleResetPwd(row) { | |
| 570 | - this.$prompt('请输入"' + row.username + '"的新密码', "提示", { | |
| 571 | - confirmButtonText: "确定", | |
| 572 | - cancelButtonText: "取消" | |
| 573 | - }).then(({ value }) => { | |
| 574 | - resetUserPwd(row.id, value).then(response => { | |
| 575 | - this.msgSuccess("修改成功,新密码是:" + value); | |
| 576 | - }); | |
| 577 | - }).catch(() => {}); | |
| 578 | - }, | |
| 579 | - /** 分配用户角色操作 */ | |
| 580 | - handleRole(row) { | |
| 581 | - this.reset(); | |
| 582 | - const id = row.id | |
| 583 | - // 处理了 form 的用户 username 和 nickname 的展示 | |
| 584 | - this.form.id = id; | |
| 585 | - this.form.username = row.username; | |
| 586 | - this.form.nickname = row.nickname; | |
| 587 | - // 打开弹窗 | |
| 588 | - this.openRole = true; | |
| 589 | - // 获得角色列表 | |
| 590 | - listSimpleRoles().then(response => { | |
| 591 | - // 处理 roleOptions 参数 | |
| 592 | - this.roleOptions = []; | |
| 593 | - this.roleOptions.push(...response.data); | |
| 594 | - }); | |
| 595 | - // 获得角色拥有的菜单集合 | |
| 596 | - listUserRoles(id).then(response => { | |
| 597 | - // 设置选中 | |
| 598 | - this.form.roleIds = response.data; | |
| 599 | - }) | |
| 600 | - }, | |
| 601 | - /** 提交按钮 */ | |
| 602 | - submitForm: function() { | |
| 603 | - this.$refs["form"].validate(valid => { | |
| 604 | - if (valid) { | |
| 605 | - if (this.form.id !== undefined) { | |
| 606 | - updateUser(this.form).then(response => { | |
| 607 | - this.msgSuccess("修改成功"); | |
| 608 | - this.open = false; | |
| 609 | - this.getList(); | |
| 610 | - }); | |
| 611 | - } else { | |
| 612 | - addUser(this.form).then(response => { | |
| 613 | - this.msgSuccess("新增成功"); | |
| 614 | - this.open = false; | |
| 615 | - this.getList(); | |
| 616 | - }); | |
| 617 | - } | |
| 618 | - } | |
| 619 | - }); | |
| 620 | - }, | |
| 621 | - /** 提交按钮(角色权限) */ | |
| 622 | - submitRole: function() { | |
| 623 | - if (this.form.id !== undefined) { | |
| 624 | - assignUserRole({ | |
| 625 | - userId: this.form.id, | |
| 626 | - roleIds: this.form.roleIds, | |
| 627 | - }).then(response => { | |
| 628 | - this.msgSuccess("分配角色成功"); | |
| 629 | - this.openRole = false; | |
| 630 | - this.getList(); | |
| 631 | - }); | |
| 632 | - } | |
| 633 | - }, | |
| 634 | - /** 删除按钮操作 */ | |
| 635 | - handleDelete(row) { | |
| 636 | - const ids = row.id || this.ids; | |
| 637 | - this.$confirm('是否确认删除用户编号为"' + ids + '"的数据项?', "警告", { | |
| 638 | - confirmButtonText: "确定", | |
| 639 | - cancelButtonText: "取消", | |
| 640 | - type: "warning" | |
| 641 | - }).then(function() { | |
| 642 | - return delUser(ids); | |
| 643 | - }).then(() => { | |
| 644 | - this.getList(); | |
| 645 | - this.msgSuccess("删除成功"); | |
| 646 | - }) | |
| 647 | - }, | |
| 648 | - /** 导出按钮操作 */ | |
| 649 | - handleExport() { | |
| 650 | - const queryParams = this.addDateRange(this.queryParams, [ | |
| 651 | - this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined, | |
| 652 | - this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined, | |
| 653 | - ]); | |
| 654 | - this.$confirm('是否确认导出所有用户数据项?', "警告", { | |
| 655 | - confirmButtonText: "确定", | |
| 656 | - cancelButtonText: "取消", | |
| 657 | - type: "warning" | |
| 658 | - }).then(function() { | |
| 659 | - return exportUser(queryParams); | |
| 660 | - }).then(response => { | |
| 661 | - this.downloadExcel(response, '用户数据.xls'); | |
| 662 | - }) | |
| 663 | - }, | |
| 664 | - /** 导入按钮操作 */ | |
| 665 | - handleImport() { | |
| 666 | - this.upload.title = "用户导入"; | |
| 667 | - this.upload.open = true; | |
| 668 | - }, | |
| 669 | - /** 下载模板操作 */ | |
| 670 | - importTemplate() { | |
| 671 | - importTemplate().then(response => { | |
| 672 | - this.downloadExcel(response, '用户导入模板.xls'); | |
| 673 | - }); | |
| 674 | - }, | |
| 675 | - // 文件上传中处理 | |
| 676 | - handleFileUploadProgress(event, file, fileList) { | |
| 677 | - this.upload.isUploading = true; | |
| 678 | - }, | |
| 679 | - // 文件上传成功处理 | |
| 680 | - handleFileSuccess(response, file, fileList) { | |
| 681 | - this.upload.open = false; | |
| 682 | - this.upload.isUploading = false; | |
| 683 | - this.$refs.upload.clearFiles(); | |
| 684 | - // 拼接提示语 | |
| 685 | - let data = response.data; | |
| 686 | - let text = '创建成功数量:' + data.createUsernames.length; | |
| 687 | - for (const username of data.createUsernames) { | |
| 688 | - text += '<br /> ' + username; | |
| 689 | - } | |
| 690 | - text += '<br />更新成功数量:' + data.updateUsernames.length; | |
| 691 | - for (const username of data.updateUsernames) { | |
| 692 | - text += '<br /> ' + username; | |
| 693 | - } | |
| 694 | - text += '<br />更新失败数量:' + Object.keys(data.failureUsernames).length; | |
| 695 | - for (const username in data.failureUsernames) { | |
| 696 | - text += '<br /> ' + username + ':' + data.failureUsernames[username]; | |
| 697 | - } | |
| 698 | - this.$alert(text, "导入结果", { dangerouslyUseHTMLString: true }); | |
| 699 | - this.getList(); | |
| 700 | - }, | |
| 701 | - // 提交上传文件 | |
| 702 | - submitFileForm() { | |
| 703 | - this.$refs.upload.submit(); | |
| 704 | - }, | |
| 705 | - // 格式化部门的下拉框 | |
| 706 | - normalizer(node) { | |
| 707 | - return { | |
| 708 | - id: node.id, | |
| 709 | - label: node.name, | |
| 710 | - children: node.children | |
| 711 | - } | |
| 712 | - } | |
| 713 | - } | |
| 714 | -}; | |
| 715 | -</script> | |
| 1 | +<template> | |
| 2 | + <div class="app-container"> | |
| 3 | + <el-row :gutter="20"> | |
| 4 | + <!--部门数据--> | |
| 5 | + <el-col :span="4" :xs="24"> | |
| 6 | + <div class="head-container"> | |
| 7 | + <el-input v-model="deptName" placeholder="请输入部门名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px"/> | |
| 8 | + </div> | |
| 9 | + <div class="head-container"> | |
| 10 | + <el-tree :data="deptOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" | |
| 11 | + ref="tree" default-expand-all @node-click="handleNodeClick"/> | |
| 12 | + </div> | |
| 13 | + </el-col> | |
| 14 | + <!--用户数据--> | |
| 15 | + <el-col :span="20" :xs="24"> | |
| 16 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> | |
| 17 | + <el-form-item label="用户名称" prop="username"> | |
| 18 | + <el-input v-model="queryParams.username" placeholder="请输入用户名称" clearable size="small" style="width: 240px" | |
| 19 | + @keyup.enter.native="handleQuery"/> | |
| 20 | + </el-form-item> | |
| 21 | + <el-form-item label="手机号码" prop="mobile"> | |
| 22 | + <el-input v-model="queryParams.mobile" placeholder="请输入手机号码" clearable size="small" style="width: 240px" | |
| 23 | + @keyup.enter.native="handleQuery"/> | |
| 24 | + </el-form-item> | |
| 25 | + <el-form-item label="状态" prop="status"> | |
| 26 | + <el-select v-model="queryParams.status" placeholder="用户状态" clearable size="small" style="width: 240px"> | |
| 27 | + <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 28 | + </el-select> | |
| 29 | + </el-form-item> | |
| 30 | + <el-form-item label="创建时间"> | |
| 31 | + <el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" | |
| 32 | + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> | |
| 33 | + </el-form-item> | |
| 34 | + <el-form-item> | |
| 35 | + <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |
| 36 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |
| 37 | + </el-form-item> | |
| 38 | + </el-form> | |
| 39 | + | |
| 40 | + <el-row :gutter="10" class="mb8"> | |
| 41 | + <el-col :span="1.5"> | |
| 42 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd" | |
| 43 | + v-hasPermi="['system:user:create']">新增</el-button> | |
| 44 | + </el-col> | |
| 45 | + <el-col :span="1.5"> | |
| 46 | + <el-button type="info" icon="el-icon-upload2" size="mini" @click="handleImport" | |
| 47 | + v-hasPermi="['system:user:import']">导入</el-button> | |
| 48 | + </el-col> | |
| 49 | + <el-col :span="1.5"> | |
| 50 | + <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" | |
| 51 | + v-hasPermi="['system:user:export']">导出</el-button> | |
| 52 | + </el-col> | |
| 53 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |
| 54 | + </el-row> | |
| 55 | + | |
| 56 | + <el-table v-loading="loading" :data="userList"> | |
| 57 | + <el-table-column label="用户编号" align="center" prop="id" /> | |
| 58 | + <el-table-column label="用户名称" align="center" prop="username" :show-overflow-tooltip="true" /> | |
| 59 | + <el-table-column label="用户昵称" align="center" prop="nickname" :show-overflow-tooltip="true" /> | |
| 60 | + <el-table-column label="部门" align="center" prop="dept.name" :show-overflow-tooltip="true" /> | |
| 61 | + <el-table-column label="手机号码" align="center" prop="mobile" width="120" /> | |
| 62 | + <el-table-column label="状态" align="center"> | |
| 63 | + <template slot-scope="scope"> | |
| 64 | + <el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)" /> | |
| 65 | + </template> | |
| 66 | + </el-table-column> | |
| 67 | + <el-table-column label="创建时间" align="center" prop="createTime" width="160"> | |
| 68 | + <template slot-scope="scope"> | |
| 69 | + <span>{{ parseTime(scope.row.createTime) }}</span> | |
| 70 | + </template> | |
| 71 | + </el-table-column> | |
| 72 | + <el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width"> | |
| 73 | + <template slot-scope="scope"> | |
| 74 | + <el-button size="large" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | |
| 75 | + v-hasPermi="['system:role:update']">修改</el-button> | |
| 76 | + <el-dropdown @command="(command) => handleCommand(command, scope.$index, scope.row)"> | |
| 77 | + <span class="el-dropdown-link"> | |
| 78 | + 更多操作<i class="el-icon-arrow-down el-icon--right"></i> | |
| 79 | + </span> | |
| 80 | + <el-dropdown-menu slot="dropdown"> | |
| 81 | + <el-dropdown-item command="handleDelete" v-if="scope.row.id !== 1" size="mini" type="text" icon="el-icon-delete" | |
| 82 | + v-hasPermi="['system:user:delete']">删除</el-dropdown-item> | |
| 83 | + <el-dropdown-item command="handleResetPwd" size="mini" type="text" icon="el-icon-key" | |
| 84 | + v-hasPermi="['system:user:update-password']">重置密码</el-dropdown-item> | |
| 85 | + <el-dropdown-item command="handleRole" size="mini" type="text" icon="el-icon-circle-check" | |
| 86 | + v-hasPermi="['system:permission:assign-user-role']">分配角色</el-dropdown-item> | |
| 87 | + </el-dropdown-menu> | |
| 88 | + </el-dropdown> | |
| 89 | + </template> | |
| 90 | + </el-table-column> | |
| 91 | + </el-table> | |
| 92 | + | |
| 93 | + <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | |
| 94 | + @pagination="getList"/> | |
| 95 | + </el-col> | |
| 96 | + </el-row> | |
| 97 | + | |
| 98 | + <!-- 添加或修改参数配置对话框 --> | |
| 99 | + <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body> | |
| 100 | + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> | |
| 101 | + <el-row> | |
| 102 | + <el-col :span="12"> | |
| 103 | + <el-form-item label="用户昵称" prop="nickname"> | |
| 104 | + <el-input v-model="form.nickname" placeholder="请输入用户昵称" /> | |
| 105 | + </el-form-item> | |
| 106 | + </el-col> | |
| 107 | + <el-col :span="12"> | |
| 108 | + <el-form-item label="归属部门" prop="deptId"> | |
| 109 | + <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" | |
| 110 | + placeholder="请选择归属部门" :normalizer="normalizer"/> | |
| 111 | + </el-form-item> | |
| 112 | + </el-col> | |
| 113 | + </el-row> | |
| 114 | + <el-row> | |
| 115 | + <el-col :span="12"> | |
| 116 | + <el-form-item label="手机号码" prop="mobile"> | |
| 117 | + <el-input v-model="form.mobile" placeholder="请输入手机号码" maxlength="11" /> | |
| 118 | + </el-form-item> | |
| 119 | + </el-col> | |
| 120 | + <el-col :span="12"> | |
| 121 | + <el-form-item label="邮箱" prop="email"> | |
| 122 | + <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" /> | |
| 123 | + </el-form-item> | |
| 124 | + </el-col> | |
| 125 | + </el-row> | |
| 126 | + <el-row> | |
| 127 | + <el-col :span="12"> | |
| 128 | + <el-form-item v-if="form.id === undefined" label="用户名称" prop="username"> | |
| 129 | + <el-input v-model="form.username" placeholder="请输入用户名称" /> | |
| 130 | + </el-form-item> | |
| 131 | + </el-col> | |
| 132 | + <el-col :span="12"> | |
| 133 | + <el-form-item v-if="form.id === undefined" label="用户密码" prop="password"> | |
| 134 | + <el-input v-model="form.password" placeholder="请输入用户密码" type="password" /> | |
| 135 | + </el-form-item> | |
| 136 | + </el-col> | |
| 137 | + </el-row> | |
| 138 | + <el-row> | |
| 139 | + <el-col :span="12"> | |
| 140 | + <el-form-item label="用户性别"> | |
| 141 | + <el-select v-model="form.sex" placeholder="请选择"> | |
| 142 | + <el-option v-for="dict in sexDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/> | |
| 143 | + </el-select> | |
| 144 | + </el-form-item> | |
| 145 | + </el-col> | |
| 146 | + <el-col :span="12"> | |
| 147 | + <el-form-item label="岗位"> | |
| 148 | + <el-select v-model="form.postIds" multiple placeholder="请选择"> | |
| 149 | + <el-option | |
| 150 | + v-for="item in postOptions" | |
| 151 | + :key="item.id" | |
| 152 | + :label="item.name" | |
| 153 | + :value="item.id" | |
| 154 | + ></el-option> | |
| 155 | + </el-select> | |
| 156 | + </el-form-item> | |
| 157 | + </el-col> | |
| 158 | + </el-row> | |
| 159 | + <el-row> | |
| 160 | + <el-col :span="24"> | |
| 161 | + <el-form-item label="备注"> | |
| 162 | + <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> | |
| 163 | + </el-form-item> | |
| 164 | + </el-col> | |
| 165 | + </el-row> | |
| 166 | + </el-form> | |
| 167 | + <div slot="footer" class="dialog-footer"> | |
| 168 | + <el-button type="primary" @click="submitForm">确 定</el-button> | |
| 169 | + <el-button @click="cancel">取 消</el-button> | |
| 170 | + </div> | |
| 171 | + </el-dialog> | |
| 172 | + | |
| 173 | + <!-- 用户导入对话框 --> | |
| 174 | + <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body> | |
| 175 | + <el-upload | |
| 176 | + ref="upload" | |
| 177 | + :limit="1" | |
| 178 | + accept=".xlsx, .xls" | |
| 179 | + :headers="upload.headers" | |
| 180 | + :action="upload.url + '?updateSupport=' + upload.updateSupport" | |
| 181 | + :disabled="upload.isUploading" | |
| 182 | + :on-progress="handleFileUploadProgress" | |
| 183 | + :on-success="handleFileSuccess" | |
| 184 | + :auto-upload="false" | |
| 185 | + drag | |
| 186 | + > | |
| 187 | + <i class="el-icon-upload"></i> | |
| 188 | + <div class="el-upload__text"> | |
| 189 | + 将文件拖到此处,或 | |
| 190 | + <em>点击上传</em> | |
| 191 | + </div> | |
| 192 | + <div class="el-upload__tip" slot="tip"> | |
| 193 | + <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据 | |
| 194 | + <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link> | |
| 195 | + </div> | |
| 196 | + <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div> | |
| 197 | + </el-upload> | |
| 198 | + <div slot="footer" class="dialog-footer"> | |
| 199 | + <el-button type="primary" @click="submitFileForm">确 定</el-button> | |
| 200 | + <el-button @click="upload.open = false">取 消</el-button> | |
| 201 | + </div> | |
| 202 | + </el-dialog> | |
| 203 | + | |
| 204 | + <!-- 分配角色 --> | |
| 205 | + <el-dialog title="分配角色" :visible.sync="openRole" width="500px" append-to-body> | |
| 206 | + <el-form :model="form" label-width="80px"> | |
| 207 | + <el-form-item label="用户名称"> | |
| 208 | + <el-input v-model="form.username" :disabled="true" /> | |
| 209 | + </el-form-item> | |
| 210 | + <el-form-item label="用户昵称"> | |
| 211 | + <el-input v-model="form.nickname" :disabled="true" /> | |
| 212 | + </el-form-item> | |
| 213 | + <el-form-item label="角色"> | |
| 214 | + <el-select v-model="form.roleIds" multiple placeholder="请选择"> | |
| 215 | + <el-option | |
| 216 | + v-for="item in roleOptions" | |
| 217 | + :key="parseInt(item.id)" | |
| 218 | + :label="item.name" | |
| 219 | + :value="parseInt(item.id)" | |
| 220 | + ></el-option> | |
| 221 | + </el-select> | |
| 222 | + </el-form-item> | |
| 223 | + </el-form> | |
| 224 | + <div slot="footer" class="dialog-footer"> | |
| 225 | + <el-button type="primary" @click="submitRole">确 定</el-button> | |
| 226 | + <el-button @click="cancelRole">取 消</el-button> | |
| 227 | + </div> | |
| 228 | + </el-dialog> | |
| 229 | + | |
| 230 | + </div> | |
| 231 | +</template> | |
| 232 | + | |
| 233 | +<script> | |
| 234 | +import { | |
| 235 | + addUser, | |
| 236 | + changeUserStatus, | |
| 237 | + delUser, | |
| 238 | + exportUser, | |
| 239 | + getUser, | |
| 240 | + importTemplate, | |
| 241 | + listUser, | |
| 242 | + resetUserPwd, | |
| 243 | + updateUser | |
| 244 | +} from "@/api/system/user"; | |
| 245 | +import {getToken} from "@/utils/auth"; | |
| 246 | +import Treeselect from "@riophae/vue-treeselect"; | |
| 247 | +import "@riophae/vue-treeselect/dist/vue-treeselect.css"; | |
| 248 | + | |
| 249 | +import {listSimpleDepts} from "@/api/system/dept"; | |
| 250 | +import {listSimplePosts} from "@/api/system/post"; | |
| 251 | + | |
| 252 | +import {SysCommonStatusEnum} from "@/utils/constants"; | |
| 253 | +import {DICT_TYPE, getDictDatas} from "@/utils/dict"; | |
| 254 | +import {assignUserRole, listUserRoles} from "@/api/system/permission"; | |
| 255 | +import {listSimpleRoles} from "@/api/system/role"; | |
| 256 | + | |
| 257 | +export default { | |
| 258 | + name: "User", | |
| 259 | + components: { Treeselect }, | |
| 260 | + data() { | |
| 261 | + return { | |
| 262 | + // 遮罩层 | |
| 263 | + loading: true, | |
| 264 | + // 显示搜索条件 | |
| 265 | + showSearch: true, | |
| 266 | + // 总条数 | |
| 267 | + total: 0, | |
| 268 | + // 用户表格数据 | |
| 269 | + userList: null, | |
| 270 | + // 弹出层标题 | |
| 271 | + title: "", | |
| 272 | + // 部门树选项 | |
| 273 | + deptOptions: undefined, | |
| 274 | + // 是否显示弹出层 | |
| 275 | + open: false, | |
| 276 | + // 部门名称 | |
| 277 | + deptName: undefined, | |
| 278 | + // 默认密码 | |
| 279 | + initPassword: undefined, | |
| 280 | + // 日期范围 | |
| 281 | + dateRange: [], | |
| 282 | + // 状态数据字典 | |
| 283 | + statusOptions: [], | |
| 284 | + // 性别状态字典 | |
| 285 | + sexOptions: [], | |
| 286 | + // 岗位选项 | |
| 287 | + postOptions: [], | |
| 288 | + // 角色选项 | |
| 289 | + roleOptions: [], | |
| 290 | + // 表单参数 | |
| 291 | + form: {}, | |
| 292 | + defaultProps: { | |
| 293 | + children: "children", | |
| 294 | + label: "name" | |
| 295 | + }, | |
| 296 | + // 用户导入参数 | |
| 297 | + upload: { | |
| 298 | + // 是否显示弹出层(用户导入) | |
| 299 | + open: false, | |
| 300 | + // 弹出层标题(用户导入) | |
| 301 | + title: "", | |
| 302 | + // 是否禁用上传 | |
| 303 | + isUploading: false, | |
| 304 | + // 是否更新已经存在的用户数据 | |
| 305 | + updateSupport: 0, | |
| 306 | + // 设置上传的请求头部 | |
| 307 | + headers: { Authorization: "Bearer " + getToken() }, | |
| 308 | + // 上传的地址 | |
| 309 | + url: process.env.VUE_APP_BASE_API + '/api/' + "/system/user/import" | |
| 310 | + }, | |
| 311 | + // 查询参数 | |
| 312 | + queryParams: { | |
| 313 | + pageNo: 1, | |
| 314 | + pageSize: 10, | |
| 315 | + username: undefined, | |
| 316 | + mobile: undefined, | |
| 317 | + status: undefined, | |
| 318 | + deptId: undefined | |
| 319 | + }, | |
| 320 | + // 表单校验 | |
| 321 | + rules: { | |
| 322 | + username: [ | |
| 323 | + { required: true, message: "用户名称不能为空", trigger: "blur" } | |
| 324 | + ], | |
| 325 | + nickname: [ | |
| 326 | + { required: true, message: "用户昵称不能为空", trigger: "blur" } | |
| 327 | + ], | |
| 328 | + password: [ | |
| 329 | + { required: true, message: "用户密码不能为空", trigger: "blur" } | |
| 330 | + ], | |
| 331 | + email: [ | |
| 332 | + { | |
| 333 | + type: "email", | |
| 334 | + message: "'请输入正确的邮箱地址", | |
| 335 | + trigger: ["blur", "change"] | |
| 336 | + } | |
| 337 | + ], | |
| 338 | + mobile: [ | |
| 339 | + { | |
| 340 | + pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, | |
| 341 | + message: "请输入正确的手机号码", | |
| 342 | + trigger: "blur" | |
| 343 | + } | |
| 344 | + ] | |
| 345 | + }, | |
| 346 | + // 是否显示弹出层(角色权限) | |
| 347 | + openRole: false, | |
| 348 | + | |
| 349 | + // 枚举 | |
| 350 | + SysCommonStatusEnum: SysCommonStatusEnum, | |
| 351 | + // 数据字典 | |
| 352 | + statusDictDatas: getDictDatas(DICT_TYPE.SYS_COMMON_STATUS), | |
| 353 | + sexDictDatas: getDictDatas(DICT_TYPE.SYS_USER_SEX), | |
| 354 | + }; | |
| 355 | + }, | |
| 356 | + watch: { | |
| 357 | + // 根据名称筛选部门树 | |
| 358 | + deptName(val) { | |
| 359 | + this.$refs.tree.filter(val); | |
| 360 | + } | |
| 361 | + }, | |
| 362 | + created() { | |
| 363 | + this.getList(); | |
| 364 | + this.getTreeselect(); | |
| 365 | + this.getConfigKey("sys.user.init-password").then(response => { | |
| 366 | + this.initPassword = response.msg; | |
| 367 | + }); | |
| 368 | + }, | |
| 369 | + methods: { | |
| 370 | + // 更多操作 | |
| 371 | + handleCommand(command, index, row) { | |
| 372 | + switch (command) { | |
| 373 | + case 'handleUpdate': | |
| 374 | + this.handleUpdate(row);//修改客户信息 | |
| 375 | + break; | |
| 376 | + case 'handleDelete': | |
| 377 | + this.handleDelete(row);//红号变更 | |
| 378 | + break; | |
| 379 | + case 'handleResetPwd': | |
| 380 | + this.handleResetPwd(row); | |
| 381 | + break; | |
| 382 | + case 'handleRole': | |
| 383 | + this.handleRole(row); | |
| 384 | + break; | |
| 385 | + default: | |
| 386 | + break; | |
| 387 | + } | |
| 388 | + }, | |
| 389 | + /** 查询用户列表 */ | |
| 390 | + getList() { | |
| 391 | + this.loading = true; | |
| 392 | + listUser(this.addDateRange(this.queryParams, [ | |
| 393 | + this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined, | |
| 394 | + this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined, | |
| 395 | + ])).then(response => { | |
| 396 | + this.userList = response.data.list; | |
| 397 | + this.total = response.data.total; | |
| 398 | + this.loading = false; | |
| 399 | + } | |
| 400 | + ); | |
| 401 | + }, | |
| 402 | + /** 查询部门下拉树结构 + 岗位下拉 */ | |
| 403 | + getTreeselect() { | |
| 404 | + listSimpleDepts().then(response => { | |
| 405 | + // 处理 deptOptions 参数 | |
| 406 | + this.deptOptions = []; | |
| 407 | + this.deptOptions.push(...this.handleTree(response.data, "id")); | |
| 408 | + }); | |
| 409 | + listSimplePosts().then(response => { | |
| 410 | + // 处理 postOptions 参数 | |
| 411 | + this.postOptions = []; | |
| 412 | + this.postOptions.push(...response.data); | |
| 413 | + }); | |
| 414 | + }, | |
| 415 | + // 筛选节点 | |
| 416 | + filterNode(value, data) { | |
| 417 | + if (!value) return true; | |
| 418 | + return data.name.indexOf(value) !== -1; | |
| 419 | + }, | |
| 420 | + // 节点单击事件 | |
| 421 | + handleNodeClick(data) { | |
| 422 | + this.queryParams.deptId = data.id; | |
| 423 | + this.getList(); | |
| 424 | + }, | |
| 425 | + // 用户状态修改 | |
| 426 | + handleStatusChange(row) { | |
| 427 | + let text = row.status === SysCommonStatusEnum.ENABLE ? "启用" : "停用"; | |
| 428 | + this.$confirm('确认要"' + text + '""' + row.username + '"用户吗?', "警告", { | |
| 429 | + confirmButtonText: "确定", | |
| 430 | + cancelButtonText: "取消", | |
| 431 | + type: "warning" | |
| 432 | + }).then(function() { | |
| 433 | + return changeUserStatus(row.id, row.status); | |
| 434 | + }).then(() => { | |
| 435 | + this.msgSuccess(text + "成功"); | |
| 436 | + }).catch(function() { | |
| 437 | + row.status = row.status === SysCommonStatusEnum.ENABLE ? SysCommonStatusEnum.DISABLE | |
| 438 | + : SysCommonStatusEnum.ENABLE; | |
| 439 | + }); | |
| 440 | + }, | |
| 441 | + // 取消按钮 | |
| 442 | + cancel() { | |
| 443 | + this.open = false; | |
| 444 | + this.reset(); | |
| 445 | + }, | |
| 446 | + // 取消按钮(角色权限) | |
| 447 | + cancelRole() { | |
| 448 | + this.openRole = false; | |
| 449 | + this.reset(); | |
| 450 | + }, | |
| 451 | + // 表单重置 | |
| 452 | + reset() { | |
| 453 | + this.form = { | |
| 454 | + id: undefined, | |
| 455 | + deptId: undefined, | |
| 456 | + username: undefined, | |
| 457 | + nickname: undefined, | |
| 458 | + password: undefined, | |
| 459 | + mobile: undefined, | |
| 460 | + email: undefined, | |
| 461 | + sex: undefined, | |
| 462 | + status: "0", | |
| 463 | + remark: undefined, | |
| 464 | + postIds: [], | |
| 465 | + roleIds: [] | |
| 466 | + }; | |
| 467 | + this.resetForm("form"); | |
| 468 | + }, | |
| 469 | + /** 搜索按钮操作 */ | |
| 470 | + handleQuery() { | |
| 471 | + this.queryParams.pageNo = 1; | |
| 472 | + this.getList(); | |
| 473 | + }, | |
| 474 | + /** 重置按钮操作 */ | |
| 475 | + resetQuery() { | |
| 476 | + this.dateRange = []; | |
| 477 | + this.resetForm("queryForm"); | |
| 478 | + this.handleQuery(); | |
| 479 | + }, | |
| 480 | + /** 新增按钮操作 */ | |
| 481 | + handleAdd() { | |
| 482 | + this.reset(); | |
| 483 | + // 获得下拉数据 | |
| 484 | + this.getTreeselect(); | |
| 485 | + // 打开表单,并设置初始化 | |
| 486 | + this.open = true; | |
| 487 | + this.title = "添加用户"; | |
| 488 | + this.form.password = this.initPassword; | |
| 489 | + }, | |
| 490 | + /** 修改按钮操作 */ | |
| 491 | + handleUpdate(row) { | |
| 492 | + this.reset(); | |
| 493 | + this.getTreeselect(); | |
| 494 | + const id = row.id; | |
| 495 | + getUser(id).then(response => { | |
| 496 | + this.form = response.data; | |
| 497 | + this.open = true; | |
| 498 | + this.title = "修改用户"; | |
| 499 | + this.form.password = ""; | |
| 500 | + }); | |
| 501 | + }, | |
| 502 | + /** 重置密码按钮操作 */ | |
| 503 | + handleResetPwd(row) { | |
| 504 | + this.$prompt('请输入"' + row.username + '"的新密码', "提示", { | |
| 505 | + confirmButtonText: "确定", | |
| 506 | + cancelButtonText: "取消" | |
| 507 | + }).then(({ value }) => { | |
| 508 | + resetUserPwd(row.id, value).then(response => { | |
| 509 | + this.msgSuccess("修改成功,新密码是:" + value); | |
| 510 | + }); | |
| 511 | + }).catch(() => {}); | |
| 512 | + }, | |
| 513 | + /** 分配用户角色操作 */ | |
| 514 | + handleRole(row) { | |
| 515 | + this.reset(); | |
| 516 | + const id = row.id | |
| 517 | + // 处理了 form 的用户 username 和 nickname 的展示 | |
| 518 | + this.form.id = id; | |
| 519 | + this.form.username = row.username; | |
| 520 | + this.form.nickname = row.nickname; | |
| 521 | + // 打开弹窗 | |
| 522 | + this.openRole = true; | |
| 523 | + // 获得角色列表 | |
| 524 | + listSimpleRoles().then(response => { | |
| 525 | + // 处理 roleOptions 参数 | |
| 526 | + this.roleOptions = []; | |
| 527 | + this.roleOptions.push(...response.data); | |
| 528 | + }); | |
| 529 | + // 获得角色拥有的菜单集合 | |
| 530 | + listUserRoles(id).then(response => { | |
| 531 | + // 设置选中 | |
| 532 | + this.form.roleIds = response.data; | |
| 533 | + }) | |
| 534 | + }, | |
| 535 | + /** 提交按钮 */ | |
| 536 | + submitForm: function() { | |
| 537 | + this.$refs["form"].validate(valid => { | |
| 538 | + if (valid) { | |
| 539 | + if (this.form.id !== undefined) { | |
| 540 | + updateUser(this.form).then(response => { | |
| 541 | + this.msgSuccess("修改成功"); | |
| 542 | + this.open = false; | |
| 543 | + this.getList(); | |
| 544 | + }); | |
| 545 | + } else { | |
| 546 | + addUser(this.form).then(response => { | |
| 547 | + this.msgSuccess("新增成功"); | |
| 548 | + this.open = false; | |
| 549 | + this.getList(); | |
| 550 | + }); | |
| 551 | + } | |
| 552 | + } | |
| 553 | + }); | |
| 554 | + }, | |
| 555 | + /** 提交按钮(角色权限) */ | |
| 556 | + submitRole: function() { | |
| 557 | + if (this.form.id !== undefined) { | |
| 558 | + assignUserRole({ | |
| 559 | + userId: this.form.id, | |
| 560 | + roleIds: this.form.roleIds, | |
| 561 | + }).then(response => { | |
| 562 | + this.msgSuccess("分配角色成功"); | |
| 563 | + this.openRole = false; | |
| 564 | + this.getList(); | |
| 565 | + }); | |
| 566 | + } | |
| 567 | + }, | |
| 568 | + /** 删除按钮操作 */ | |
| 569 | + handleDelete(row) { | |
| 570 | + const ids = row.id || this.ids; | |
| 571 | + this.$confirm('是否确认删除用户编号为"' + ids + '"的数据项?', "警告", { | |
| 572 | + confirmButtonText: "确定", | |
| 573 | + cancelButtonText: "取消", | |
| 574 | + type: "warning" | |
| 575 | + }).then(function() { | |
| 576 | + return delUser(ids); | |
| 577 | + }).then(() => { | |
| 578 | + this.getList(); | |
| 579 | + this.msgSuccess("删除成功"); | |
| 580 | + }) | |
| 581 | + }, | |
| 582 | + /** 导出按钮操作 */ | |
| 583 | + handleExport() { | |
| 584 | + const queryParams = this.addDateRange(this.queryParams, [ | |
| 585 | + this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined, | |
| 586 | + this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined, | |
| 587 | + ]); | |
| 588 | + this.$confirm('是否确认导出所有用户数据项?', "警告", { | |
| 589 | + confirmButtonText: "确定", | |
| 590 | + cancelButtonText: "取消", | |
| 591 | + type: "warning" | |
| 592 | + }).then(function() { | |
| 593 | + return exportUser(queryParams); | |
| 594 | + }).then(response => { | |
| 595 | + this.downloadExcel(response, '用户数据.xls'); | |
| 596 | + }) | |
| 597 | + }, | |
| 598 | + /** 导入按钮操作 */ | |
| 599 | + handleImport() { | |
| 600 | + this.upload.title = "用户导入"; | |
| 601 | + this.upload.open = true; | |
| 602 | + }, | |
| 603 | + /** 下载模板操作 */ | |
| 604 | + importTemplate() { | |
| 605 | + importTemplate().then(response => { | |
| 606 | + this.downloadExcel(response, '用户导入模板.xls'); | |
| 607 | + }); | |
| 608 | + }, | |
| 609 | + // 文件上传中处理 | |
| 610 | + handleFileUploadProgress(event, file, fileList) { | |
| 611 | + this.upload.isUploading = true; | |
| 612 | + }, | |
| 613 | + // 文件上传成功处理 | |
| 614 | + handleFileSuccess(response, file, fileList) { | |
| 615 | + this.upload.open = false; | |
| 616 | + this.upload.isUploading = false; | |
| 617 | + this.$refs.upload.clearFiles(); | |
| 618 | + // 拼接提示语 | |
| 619 | + let data = response.data; | |
| 620 | + let text = '创建成功数量:' + data.createUsernames.length; | |
| 621 | + for (const username of data.createUsernames) { | |
| 622 | + text += '<br /> ' + username; | |
| 623 | + } | |
| 624 | + text += '<br />更新成功数量:' + data.updateUsernames.length; | |
| 625 | + for (const username of data.updateUsernames) { | |
| 626 | + text += '<br /> ' + username; | |
| 627 | + } | |
| 628 | + text += '<br />更新失败数量:' + Object.keys(data.failureUsernames).length; | |
| 629 | + for (const username in data.failureUsernames) { | |
| 630 | + text += '<br /> ' + username + ':' + data.failureUsernames[username]; | |
| 631 | + } | |
| 632 | + this.$alert(text, "导入结果", { dangerouslyUseHTMLString: true }); | |
| 633 | + this.getList(); | |
| 634 | + }, | |
| 635 | + // 提交上传文件 | |
| 636 | + submitFileForm() { | |
| 637 | + this.$refs.upload.submit(); | |
| 638 | + }, | |
| 639 | + // 格式化部门的下拉框 | |
| 640 | + normalizer(node) { | |
| 641 | + return { | |
| 642 | + id: node.id, | |
| 643 | + label: node.name, | |
| 644 | + children: node.children | |
| 645 | + } | |
| 646 | + } | |
| 647 | + } | |
| 648 | +}; | |
| 649 | +</script> | |
| 650 | +<style> | |
| 651 | + .el-dropdown-link { | |
| 652 | + cursor: pointer; | |
| 653 | + color: #1890ff; | |
| 654 | + margin-left: 5px; | |
| 655 | + } | |
| 656 | + .el-icon-arrow-down { | |
| 657 | + font-size: 14px; | |
| 658 | + } | |
| 659 | +</style> | ... | ... |
ruoyi-ui/src/views/system/user/profile/index.vue
| ... | ... | @@ -13,11 +13,11 @@ |
| 13 | 13 | <ul class="list-group list-group-striped"> |
| 14 | 14 | <li class="list-group-item"> |
| 15 | 15 | <svg-icon icon-class="user" />用户名称 |
| 16 | - <div class="pull-right">{{ user.userName }}</div> | |
| 16 | + <div class="pull-right">{{ user.username }}</div> | |
| 17 | 17 | </li> |
| 18 | 18 | <li class="list-group-item"> |
| 19 | 19 | <svg-icon icon-class="phone" />手机号码 |
| 20 | - <div class="pull-right">{{ user.phonenumber }}</div> | |
| 20 | + <div class="pull-right">{{ user.mobile }}</div> | |
| 21 | 21 | </li> |
| 22 | 22 | <li class="list-group-item"> |
| 23 | 23 | <svg-icon icon-class="email" />用户邮箱 |
| ... | ... | @@ -25,15 +25,19 @@ |
| 25 | 25 | </li> |
| 26 | 26 | <li class="list-group-item"> |
| 27 | 27 | <svg-icon icon-class="tree" />所属部门 |
| 28 | - <div class="pull-right" v-if="user.dept">{{ user.dept.deptName }} / {{ postGroup }}</div> | |
| 28 | + <div class="pull-right" v-if="user.dept">{{ user.dept.name }}</div> | |
| 29 | + </li> | |
| 30 | + <li class="list-group-item"> | |
| 31 | + <svg-icon icon-class="tree" />所属岗位 | |
| 32 | + <div class="pull-right" v-if="user.posts">{{ user.posts.map(post => post.name).join(',') }}</div> | |
| 29 | 33 | </li> |
| 30 | 34 | <li class="list-group-item"> |
| 31 | 35 | <svg-icon icon-class="peoples" />所属角色 |
| 32 | - <div class="pull-right">{{ roleGroup }}</div> | |
| 36 | + <div class="pull-right">{{ user.roles.map(post => post.name).join(',') }}</div> | |
| 33 | 37 | </li> |
| 34 | 38 | <li class="list-group-item"> |
| 35 | 39 | <svg-icon icon-class="date" />创建日期 |
| 36 | - <div class="pull-right">{{ user.createTime }}</div> | |
| 40 | + <div class="pull-right">{{ parseTime(user.createTime) }}</div> | |
| 37 | 41 | </li> |
| 38 | 42 | </ul> |
| 39 | 43 | </div> |
| ... | ... | @@ -82,8 +86,6 @@ export default { |
| 82 | 86 | getUser() { |
| 83 | 87 | getUserProfile().then(response => { |
| 84 | 88 | this.user = response.data; |
| 85 | - this.roleGroup = response.roleGroup; | |
| 86 | - this.postGroup = response.postGroup; | |
| 87 | 89 | }); |
| 88 | 90 | } |
| 89 | 91 | } | ... | ... |
ruoyi-ui/src/views/system/user/profile/userInfo.vue
| 1 | 1 | <template> |
| 2 | 2 | <el-form ref="form" :model="user" :rules="rules" label-width="80px"> |
| 3 | 3 | <el-form-item label="用户昵称" prop="nickName"> |
| 4 | - <el-input v-model="user.nickName" /> | |
| 5 | - </el-form-item> | |
| 6 | - <el-form-item label="手机号码" prop="phonenumber"> | |
| 7 | - <el-input v-model="user.phonenumber" maxlength="11" /> | |
| 4 | + <el-input v-model="user.nickname" /> | |
| 5 | + </el-form-item> | |
| 6 | + <el-form-item label="手机号码" prop="mobile"> | |
| 7 | + <el-input v-model="user.mobile" maxlength="11" /> | |
| 8 | 8 | </el-form-item> |
| 9 | 9 | <el-form-item label="邮箱" prop="email"> |
| 10 | 10 | <el-input v-model="user.email" maxlength="50" /> |
| 11 | 11 | </el-form-item> |
| 12 | 12 | <el-form-item label="性别"> |
| 13 | 13 | <el-radio-group v-model="user.sex"> |
| 14 | - <el-radio label="0">男</el-radio> | |
| 15 | - <el-radio label="1">女</el-radio> | |
| 14 | + <el-radio :label="1">男</el-radio> | |
| 15 | + <el-radio :label="2">女</el-radio> | |
| 16 | 16 | </el-radio-group> |
| 17 | 17 | </el-form-item> |
| 18 | 18 | <el-form-item> |
| ... | ... | @@ -35,7 +35,7 @@ export default { |
| 35 | 35 | return { |
| 36 | 36 | // 表单校验 |
| 37 | 37 | rules: { |
| 38 | - nickName: [ | |
| 38 | + nickname: [ | |
| 39 | 39 | { required: true, message: "用户昵称不能为空", trigger: "blur" } |
| 40 | 40 | ], |
| 41 | 41 | email: [ |
| ... | ... | @@ -46,7 +46,7 @@ export default { |
| 46 | 46 | trigger: ["blur", "change"] |
| 47 | 47 | } |
| 48 | 48 | ], |
| 49 | - phonenumber: [ | |
| 49 | + mobile: [ | |
| 50 | 50 | { required: true, message: "手机号码不能为空", trigger: "blur" }, |
| 51 | 51 | { |
| 52 | 52 | pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, | ... | ... |
ruoyi-ui/src/views/tool/dbDoc/index.vue
| 1 | 1 | <template> |
| 2 | - <div v-loading="loading" :style="'height:'+ height"> | |
| 3 | - <iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" /> | |
| 2 | + <div class="app-container"> | |
| 3 | + <el-row :gutter="10" class="mb8"> | |
| 4 | + <el-col :span="1.5"> | |
| 5 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportHtml">导出 HTML</el-button> | |
| 6 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportWord">导出 Word</el-button> | |
| 7 | + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportMarkdown">导出 Markdown</el-button> | |
| 8 | + </el-col> | |
| 9 | + </el-row> | |
| 10 | + | |
| 11 | + <!-- 展示文档 --> | |
| 12 | + <div v-loading="loading" :style="'height:'+ height"> | |
| 13 | + <iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" /> | |
| 14 | + </div> | |
| 4 | 15 | </div> |
| 5 | 16 | </template> |
| 6 | 17 | <script> |
| 7 | -import {exportHtml} from "@/api/infra/dbDoc"; | |
| 18 | +import { exportHtml, exportWord, exportMarkdown} from "@/api/infra/dbDoc"; | |
| 8 | 19 | |
| 9 | 20 | export default { |
| 10 | 21 | name: "DBDoc", |
| ... | ... | @@ -25,10 +36,31 @@ export default { |
| 25 | 36 | }; |
| 26 | 37 | }, |
| 27 | 38 | created() { |
| 39 | + // 加载 Html,进行预览 | |
| 28 | 40 | exportHtml().then(response => { |
| 29 | - // var blob = new Blob(['<a id="a"><b id="b">hey!</b></a>'], {type : 'text/html'}); | |
| 30 | - this.src = window.URL.createObjectURL(response); | |
| 41 | + let blob = new Blob([response], {type : 'text/html'}); | |
| 42 | + this.src = window.URL.createObjectURL(blob); | |
| 31 | 43 | }) |
| 32 | 44 | }, |
| 45 | + methods: { | |
| 46 | + /** 处理导出 HTML */ | |
| 47 | + handleExportHtml() { | |
| 48 | + exportHtml().then(response => { | |
| 49 | + this.downloadHtml(response, '数据库文档.html'); | |
| 50 | + }) | |
| 51 | + }, | |
| 52 | + /** 处理导出 Word */ | |
| 53 | + handleExportWord() { | |
| 54 | + exportWord().then(response => { | |
| 55 | + this.downloadWord(response, '数据库文档.doc'); | |
| 56 | + }) | |
| 57 | + }, | |
| 58 | + /** 处理导出 Markdown */ | |
| 59 | + handleExportMarkdown() { | |
| 60 | + exportMarkdown().then(response => { | |
| 61 | + this.downloadMarkdown(response, '数据库文档.md'); | |
| 62 | + }) | |
| 63 | + } | |
| 64 | + } | |
| 33 | 65 | }; |
| 34 | 66 | </script> | ... | ... |
sql/quartz.sql
| ... | ... | @@ -178,3 +178,11 @@ CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIG |
| 178 | 178 | CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); |
| 179 | 179 | |
| 180 | 180 | commit; |
| 181 | + | |
| 182 | +-- 初始化默认任务 用户 Session 超时 Job | |
| 183 | +INSERT INTO QRTZ_JOB_DETAILS VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', NULL, 'cn.iocoder.dashboard.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000047400104A4F425F48414E444C45525F4E414D457400187379735573657253657373696F6E54696D656F75744A6F627800); | |
| 184 | +commit; | |
| 185 | +INSERT INTO QRTZ_TRIGGERS VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', 'sysUserSessionTimeoutJob', 'DEFAULT', NULL, 1615706340000, 1615706280000, 5, 'WAITING', 'CRON', 1615706125000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000007D074000F4A4F425F52455452595F434F554E547371007E0009000000037800); | |
| 186 | +commit; | |
| 187 | +INSERT INTO QRTZ_CRON_TRIGGERS VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', '0 * * * * ? *', 'Asia/Shanghai'); | |
| 188 | +commit; | ... | ... |
sql/ruoyi-vue-pro.sql
| 1 | 1 | /* |
| 2 | 2 | Navicat Premium Data Transfer |
| 3 | 3 | |
| 4 | - Source Server : 127.0.0.1 | |
| 4 | + Source Server : local-mysql001 | |
| 5 | 5 | Source Server Type : MySQL |
| 6 | 6 | Source Server Version : 50718 |
| 7 | 7 | Source Host : localhost:3306 |
| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | Target Server Version : 50718 |
| 12 | 12 | File Encoding : 65001 |
| 13 | 13 | |
| 14 | - Date: 09/03/2021 20:39:41 | |
| 14 | + Date: 18/04/2021 00:36:06 | |
| 15 | 15 | */ |
| 16 | 16 | |
| 17 | 17 | SET NAMES utf8mb4; |
| ... | ... | @@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` ( |
| 43 | 43 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 44 | 44 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 45 | 45 | PRIMARY KEY (`id`) USING BTREE |
| 46 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; | |
| 46 | +) ENGINE=InnoDB AUTO_INCREMENT=3750 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; | |
| 47 | 47 | |
| 48 | 48 | -- ---------------------------- |
| 49 | 49 | -- Records of inf_api_access_log |
| ... | ... | @@ -84,7 +84,7 @@ CREATE TABLE `inf_api_error_log` ( |
| 84 | 84 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 85 | 85 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 86 | 86 | PRIMARY KEY (`id`) USING BTREE |
| 87 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; | |
| 87 | +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; | |
| 88 | 88 | |
| 89 | 89 | -- ---------------------------- |
| 90 | 90 | -- Records of inf_api_error_log |
| ... | ... | @@ -125,6 +125,32 @@ INSERT INTO `inf_config` VALUES (5, 'xxx', 2, 'xxx', 'xxx', 'xxx', b'1', 'xxx', |
| 125 | 125 | COMMIT; |
| 126 | 126 | |
| 127 | 127 | -- ---------------------------- |
| 128 | +-- Table structure for inf_file | |
| 129 | +-- ---------------------------- | |
| 130 | +DROP TABLE IF EXISTS `inf_file`; | |
| 131 | +CREATE TABLE `inf_file` ( | |
| 132 | + `id` varchar(188) NOT NULL COMMENT '文件路径', | |
| 133 | + `type` varchar(63) DEFAULT NULL COMMENT '文件类型', | |
| 134 | + `content` blob NOT NULL COMMENT '文件内容', | |
| 135 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 136 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 137 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 138 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 139 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 140 | + PRIMARY KEY (`id`) USING BTREE | |
| 141 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件表'; | |
| 142 | + | |
| 143 | +-- ---------------------------- | |
| 144 | +-- Records of inf_file | |
| 145 | +-- ---------------------------- | |
| 146 | +BEGIN; | |
| 147 | +INSERT INTO `inf_file` VALUES ('427.jpg', 'jpg', 0xFFD8FFE000104A46494600010100000100010000FFDB00430006040506050406060506070706080A100A0A09090A140E0F0C1017141818171416161A1D251F1A1B231C1616202C20232627292A29191F2D302D283025282928FFDB0043010707070A080A130A0A13281A161A2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828FFC0001108017F038403012200021101031101FFC4001C0001000203010101000000000000000000000607010405020308FFC40051100001030301040507060A08030803000000010203040511060712213113415161811422357191A1B115233242C1D1161752545572749394B2242536537392E1F0264362083334374482A2F163C2E2FFC4001C0101000203010101000000000000000000000102030405060807FFC4003811010002010204030507030402030000000001020304110512213113415114223371B13235617281A1D11552C1233454910642B2E1F1FFDA000C03010002110311003F00E5000D57E96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001832600905ABD1F0F8FC4DA356D5E8F87C7E26D1EA307C3AFCA1F35F1DFBCB51F9EDF564006572800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000115001E49F5480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060C9802416AF47C3E3F136CD4B57A3E1F1F89B67A8C1F0EBF287CD7C77EF2D47E7B7D400195CA000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000454007927D5200000000000000000000000000000000000000000002407DA9692A6B24E8E8E9E6A893F26262B97DC84928B67BA9EB1A8E6DBD206F6D44AD67BB8A931599ECC3933E2C5F6ED11FAA2A09D26CC6E8C44F2AB9DAA05EC74AABF6184D9B55AFF00DDDF2CEE5EC4949E4960FEA1A6FEFF00AA0C09AD46CCF51C6D5740CA4AA6A7F753267DF823B73B05DED795B85B6AE06A7D758F2DF6A6508989865A6AB0E49DA968997301E517D5ED3D10CE000000000000000000000000000000000000000000001832600905ABD1F0F8FC4DB352D5E8F87C7E26D9EA307C3AFCA1F35F1DFBCB51F9EDF500065728000000000000000000000000000004BF41E9EB75E9B54DAD95ED9E3C2358D5C70EDC7ACE4EA7B14F62AF5865F3A27718DFF948638CB59B7279B6EDA2CB5C11A8DBDD96BE9FA165CEF549472395AD95F8554E7C957EC3ABAE6C94F63B945152B9EB1C91EF6EBBA9727334ED4BE86F14D58CA7927481DBCE6B13B950EAEBAB9BEEF7064E9473D3C71337732B719E3EE2B336F1636EDB3631D717B15B78F7F7E9F24681B16CA29EE3591D352B15F2BF97777AF7131D47A4286CFA7BCA24A87F96B5513B9EAABC5A89D98CF12D7CD5ADA2B3DDAF8347973D2D92B1EED7BCA060032350000000000000000000000000000000000000000000000000000000000000000115001E49F54800000000000000000000000000000000000063217FDFAC9D58F4A525B6DECBD6B391D4D46BC61A245F9C9D7AB29CD3D5ED2621873E7AE18DEDDFCA3CE51FD37A6AEBA8A454B6D32AC2D5F3A793CD8DBEB5EDEE4C9275B7E8FD2EDC5D677DFEE2C5E34F0E12362F7AF2F6AAAF71C8D49AD6B6EF079150B3E4DB4B3CD65343C329FF0052A7C1381172DBC4766BC62CFA8EB967963D23BFEB3FC2695BB47B9A46B4F63A5A3B452FD56C51239D8F5AF0F71DFB1E89D41A9EDD4F71BBEA3A88E2A96F48D8BCE55C2F2CF144F7155390FD41A178E8EB3FECB1FF002A16ACEEE7F1498D0E2ACE0888999EFDE50A8B63B6A545E9AE55B23D79AA6EA7D87D1763B65C79B5B5C8BDBBCDFB8B0EE956DB7DB6AEADCD57A4113A556A2E15D84CE08F684D6516AC6563A2A39297C995A8BBEE45DEDE4CF0C16DA1C5AEB75D6A4E48B748EFD9127EC966A3C3AD3A82AA07A72DE454C7F95508954EADD53A5EED516DA9B836ADD4EFDD7B676A488E4E6985E0B83F43751F9B369E99D7977FF11BFC8D22D1111BBA3C2F516D6649C79E22D1B7A43AC97FD29A85772FD69F936A9FC3CAE8F967BD138E3D68A685E341D5C34C95F60A98EF16D5E28F8573237D6DEBF0E3DC43B0746C97AB8D8EA92A2D754F81FD6DE6C77EB379298F789EEED7B2E4C5D705BF49EB1FCC39EA8A8E5454C2A73EE058A8FB1ED09158F6C369D4CD4E0A9FF7752BF7FBFD641EED6CABB457CB457185D0D446B8545E28A9D4A8BD68A44C32E1D4C649E4B46D78F2FE3D5A40021B00000000000000000000000000000000000001832600905ABD1F0F8FC4DB352D5E8F87C7E26D9EA307C3AFCA1F35F1DFBCB51F9EDF5000657280000000000000000000000003260EB586C35B7C9256D0B1AEE89115EAE76319CE3E0A45AD158DE593162BE5B72638DE5A343593D05547534B22B2662E5153E0BDC4F6D90566BAA886A6E6D6C5434996E234FF00BC770CF1F611ABC693B9DA6916A6AE362C48B85563B7950B0AE73A69ED0ACF2576EC9D135AC7638EF3B9AFC54D3CD7ACED34EB32EF70DD364C7CF5D4EF14AC6F35F5F462B750D8B4DA3A929A26BA46705644DE4BDEBDA6AD1EBAB3DCB14F5F4EB0A3D513E7111CDF12A9739CF7AB9EE5739572AABD664B7B1D7BCCF562B71FCFCDEE5622BE9B2D1BC58D6C73A5F34EB58A8C4557C38CB55ABD68402FF7CADBE552CD56F446A7D18DBF45A84F765D717D5DB6B282A155ED871BAABD4D5EAF72911FC19ACB85EEE14B6E63563A7915155CEC22679214C3314BCC64F2F367D752D9B0D2DA5E95BF7AC7AA38090DDF48DD6D544FAAA98D8B133E92B5E8B823C6E56F178DEB2E0E6C19304F2E5AED2006CD0D1D457D4360A385F34AEFAAD4F8F61699888DE58AB59B4C56B1BCB58FB535354554891D2C124CFF00C96372A593A7F67F0C2C6CB7A547BF19E898B86A7AD799D0AED5561B1B560A18D923DBC1594ED4C7B791AB6D56F3B638DDDBC5C1B92B1935778A47A79A1743A0EF550D47491C74ED5FEF1DC7D8874D9B36AD5CEFD7409EA62A9F1B9ED0EE3322B68A28E99BD4ABE73BDE71E5D5F7C91DBCB5CF4EE6A22215DB5168DFA42D36E138E79622D6FC5DE9366D588DCC75D0B97B158A872EBF42DE695AAE8E2654353FBB771F628B7EACD473CCC8296774F33B9377117259F6375CDB48F7DF1606BF1BC891FD54EBCA98F265CD87ED4C4B7749A3D06BFA62A5ABF8F928CA8A79E9A45654C3244F4E0A8F6E307C4BF116D77E8248D3A1AB8D8AAD72A71445EE5217A8B67B86BA6B2BF971E8645F82FDE64C7ACACCED78D9A9ABE01931D7C4C16E7AFEEADC1F4A8864A699F0CF1BA3958B8735C98543E66E38131313B4C00008000000000000000000000000000000000000454007927D5200000000000000000000000000000000180A4CB67F65A693CA7505ED11B68B779F85E3D2C89C51B8EB44E1C3B704C43167CD18693796FE9FB551695B3C7A9351C6AEAA7F9D6FA17261CE7753D53DFDDCF99C1D58DBE5C60A6BFDEDAE6C558E5640DEA6A226530DFAA8BED534B545F6AB51DD64AEABCB73E6C5167846CEA4FBFBCB6E66FE1BEC9DAE6377EBA18B7B7539F4B1F344F5A27BD0BC46FD1CBCB6BE9AD4CF9BACDA7699FED8F48FF2816CEB453756256495334B052C0A8C6BA2C2ABDCA99C71ECE1ED4265F8A0B5F5DE6A53FC86E56AA685D96A45F42E13B373873E95FCFF00CA9F028E491E9F5DFF00E6513111DD5C56D4EB6F7C98F272D62768E9BAE45D90DA7F4CD4FB23FB8B26C940CB659E8E8A29165653C4D89AF5E6E444C654FCA4B23FF2DFFE653F4FE85E3A3ACFFB2C7FCA85A9313D9CEE3183363C75F1727346FE9B205B50B3EA9AABBD4D65BA6959676D326FB12A3753088AAEF333D857DA3ED7A8EE6CAAFC189A46246AD499193F45955CE3DC7E93AFA58EB68A6A69F3D14CC58DD85C2E1530A7274B695B66996D425AA391893AB55FBEF57724C2732663AEEC5838B785A79C5CB1BF4DBA7D5D6B7325650D3B6A155666C6D47AAAE72B8E3EF3F3A6D3BFB7977FF11BFC8D3F49A1526AFD9A5D2F7A8EB6E1056D23239DC8E46C8D76530D44FB08B44CC6D0AF08D463C19ED7C93B46CA7CCB237CD345144D574923918D6A75AAAE110B1FF14179FD2141FE471E742E8DABA3DA2A535CE2456DB99E51D2373BAFCF0663C73FE531C527CDE92DC4F4FC96B52DBCC46EDCDA3E9AB4D9B4F58E28B14F7557B299B331708EE19739DEA5E39E79535A92AA3D4913B4B6B0CD35EA9FCDA5AC7A61CAEEA455EBCF0EE72779CFDB25DFE52D58EA58DD98685891FF00EF5E2EFB13C0DB9DD6CD59A1E965A8AEA6A4D4540DE8D1F2C88D59913922AAF6A630BD4B9267BB9F4ADE34F8EF977DE67BF9C6FDBF4F5416F16DAAB3DCA6A1AF8963A88970A9D4E4EA722F5A29A858D0B9BB42D32F8E4DD4D4F6B8F79ABD7511FDBCBDBEB2B84CA2AA2A6153A94ACC6CEB69B3CE589ADFA5A3BFF3F2964004364000000000000000000000000000000003064C0120B57A3E1F1F89B66A5ABD1F0F8FC4DB3D460F875F943E6BE3BF796A3F3DBEA000CAE5000000000000000000000000074ECB7CAEB2BA575BE548FA54447A2B51738CE39FAD4E6022622D1B4AF8F25F15A2F49DA5DCBAEAABADD697C9EB2663A1CA2AB5AC46E71CB9162DCA1FC21D0ACF244CC9D1B5CC6AF5AB7A8AA2DB433DC6AE3A6A462BE47AF57244ED52796D9EB3424D0D3DD5EC9E86A9729D1E556254E784F1434F3D2B1B453BC793BFC375192DCF6D56F34B46D33E8AEA46BA391CC91AAD7B570A8BCD0CA16FD65874FEA755ACA791A93393CE7C2EC2AFEB27DE7CE9744596DCADA8AB95D2A3173991E88DF12DED95F38EAC73FF008FE69B6F4B44D7D776AECBEDEFA4B5D4D7D43771B51F45179EE275FB7243DDA9AB28AF171A9B5CC8C65448AAA8AD4545C725C2F592FBDDF16F953F2069D5661ED54926CE1AD6A7344F7103D4164ACB1D524558C4563932D95BF45C570C45AD3393BCF933710BDB0E1A534BBF2D3BDA3D5F7BB6ABBB5D29169AAE76AC2E5CAA3588DCF71C3077748E9E96FD5E8C4CB69A35459644E58EC4EF3667930D77DB6871A3C7D6E58AEF36B4BC69BD395B7E9D1B4EDDCA6477CE4AF4E089DDDE5979B3689B5F9A89D22A72E7248BDFFEF81F2D437DA1D236E6D150318B528DF323EA6A7E5394A9AE15B3DC2ADF535723A495FCD57ABB93B8D488B6A3ADBA55DABE4C1C223931C45B2CF79F2876F51EADAFBC4AE62B960A6FEE5BD7EB5EB23CBC7060F46ED295A46D58707367C99EDCF927790D9B650D45CABA2A4A466FCB22E13B113B57B8F8451BE591B1C4D57C8E5C35A9CD57B0EBC4B73D29748A5960E8A656E51AFE2D722F34E1CC5E676DABDCC18E26D17BC4F246DBCC2C6B65B6D7A2AD4EABAC91AEA854C3A554E2E5FC96A7615FEA7D575B7A7AB379D051E5710B579FAD7ACD0BDDE6B2F556B3D6BD17098631A98462771CEC1AF8B4FB4F3E4EB2E8EB78A73C781A68E5C71FBFCD3AD93D6B9974A9A4CF9B2B37D3D69FFD9B57ED435DA6B55D4B51566A295525E89FD59E7BABD5C5148D6839961D5743BBF59CAD5F61DDDADC28DBAD2CC9CDF12B7D8BFEA63B52BE3ED31D261BB87519238673E39DAD4B24371B75AF5B5A1B5346E6B6A9A9E6BFEB357F25C9D855573A29EDB5B252D5B1592B170A8BD7DE9DC6D69DBD5458EE2CA9A755E8D78491F53D0B23525B29B56E9F657DBF756A9ADDE8D7AD7B5ABFEF9889B69EDB4CFBB2AE4AE3E2D86725236CB5EF1EAA8C1E958A8AA8BC15382A28C1BCF3BD9E40010000000000000000000000000000000008A800F24FAA400000000000000000000000000000004B62D9452DC6E14D474C999A77A46DF5AFFA71F0261B47B8436F8A874ADA9F8A3B73516754FF00992AA75FAB2ABEB5EE3DECBE28A821BC6A6AA6A2C76E85590F1E72393FFA4F138D43A5EF17EB5576A06A31D1A39EF915CEC3E454E2EC263AB897ED0E6E4C94B6A37C93B569FF00CA7F8847510B4F6157748EBEBACF22F99335278D157EB27077B787B0AB10DFD3F7296CD7AA4B8C0ABBF03F2ADFCA6AA61508A4ED2D8D6E9E35182D8FCFC933DB55F56BF51C76E89D9A7A06E17BE45E7EC4C215E9F4AB9E4AAA996A2672BA595EAF7B97AD5572A7CC5A7795B498234F86B8E3C83F49ECDAE34F5DA3ADBD048C57430B6291B9E2D72261514FCD87B8A5923CAC52491AAF5B1EAD5F7135B72B0710D0C6B6914DF6D9FAEB982B9D8EDCE066908A3ADAE8D6A5F50FC3669B2F5555E09C5724EEB2BE9691A8957510C0AECEEA48F46E7D5932EFD37789CD82D8B2CE2EFB361EF6B1AAE739A889D6AA7E7AD65AD6EB26A7B8FC9179AA650B64DC8D237E1BC111171E3922F74A999F70AE62554EE8D677E13A45DD54DE5E499C60D2C7798E6EF55A0E114C13CF79E6DE3D121A6D69A8E1A98A65BC55C9D1BD1DB8F765AE44EA54EC52F4A2D534357A424D4316EF46C815CF6AF36B9138B17C781F9A910DDA5B955D35A6B2DB14CA94954E6BA4677A2E787BB3EA222FEACFACE178B5115E4888989FFB8F36B54CEFA9ABA8A99955D2CEF591EABD6AAB93E58E27A05267774E2368D9BD60BB4F63BBD357D2AFCE44EE29F94D5E6D5F5A122DA65B608AB69AF96C6FF56DD5892B31C9B2638A77679FB4873932A84FB492FE10686BC58645DFABA2FE99479E689CD513BB394FFDC5A3B6CD2D57FA57AE78F94FCA7F894010C9843255BA000000000000000000000000000000001832600905ABD1F0F8FC4DB352D5E8F87C7E26D9EA307C3AFCA1F35F1DFBCB51F9EDF50006572800000000000000000000000000012DD0BA8686C3154F95C323E57AF9AE6222F0EC393A8EF93DF2E0EA89BCD8DBC2367E4A1C808638C558B73F9B6EFADCB7C31A799F761D4D394EFADBC52D2367921499DBAAE62F1443A7AF6D8EB3DC618A3AA9E68E466F7CEBB795173DA722C35CDB65E292B1EC57B617EF2B514E96B7BDC37EB94735344E6471B37115E985771E65662FE2C6DDB667C57C5EC56DE7DFDFA7C9C6A1AC9A82B22AAA57AB268D72D54F82932D47AC28EF1A696966A6779639132AA89BAD776A2906C054C96BE1ADED169EED7C1ADCB8296C749F76DDE188911CF4472EEA2AF15E782F4B23AD36AB5C105354D3B62DDCEF2C89972F69456015CF83C6888DF66D70DE25EC369B4577995BB5DA734DDCAA5F34956C59DEEDE7392A132B939170D9BE51CEB65723BB1923787B50AE5155392AA1D8B56A0B9DADED5A5AB9309CD8F5DE6AF82987C0C94EB4B36E78968B3DBFD7C3B6FE712F85DACB70B4CAACAEA67313A9E9C5AE4ED45439E5B162D61417C44A2BB431C72BF8223B8B245ECE3D671AF7A01EDBA40B6CF3A8A593CF6AF3893ED42F4D4ED3CB9636953370BADE9E2E8ADCF5F4F387D7669624CADDEADA88D66521DEE5DEE23FAF2F6DBCDDFE670B4F4F9631C9F5B9657D44D35EDCE2B269F8ADB46A8934CCE8DA89CDAC4E0ABF615314C11E25A72CFE8C9C4AF1A5C55D0E3F2EB6F9867ACC036DC077345AFF00C556CFF17EC251B5D5FE9741FAAEF891FD9DD3F4FAAE957FBBCBFDDFEA7536B336F5F29E2CF06439C7AD57EE352DFEE23E4EEE19E5E159267CEC84AA130D9C5FD2DB5CFA3AB911B493AF9AAE5E0C77FA90E43286CE5A464AF2CB95A5D45F4B9632D3C922D7B1D1B350CD2504AD91B2A23DFB9C511CBCF0BEFF00123A8B93BD45A5ABAB2C6FB9C4B1A46D457231578AA2673F038284629AEDCB13BECB6B2324E49CB7AF2F37587900191A8000000000000000000000000000000008A800F24FAA4000000000000000000000000000000C2B55EDC378AAAA22027A427B7DCDA365965A16A2B65B9CCEA9953FE945CE17FF00892DD9DAA7E28EE1C3AAA7E0A4476BAE4A7BA5A2DCCFA14544C6227AF87C1097ECEB09B23B8F0EAA9F829963ED6CF39A98DF495BCF7B5B7FFB95231AFCD33F550FA1DAD11A6AAF54D7B29699ED8A38D8D7CD33933B8DEEEF52D766C7ECDBA9BF595CAFC717239A99F7158A4CBABA9E25834B6E4BCF551E0BAABF63D6C5A7779157D5C7509F4164DD7373DE98429EBADBEA2D572A8A1AD66E5440EDD722725EC54EE5422D598EEBE975F8757BC629EB0D606327D994D52E6E5296A1517AD2272FD846CDB9B447796E69A6A2EA6B46533FD2E2FE742C4FFB40222D4D9F2D4779B273F02B7A1656D1D7535545493ABE091B2B5AE85D8556AE533C0ECEAED4577D54FA67D75BD635A747237A389FC5171CF29DC5B7E9B39B9B14DF578F2C6DCB113BA30864FA792D5FE6951FBA77DC3C96AFF34A8FDD3BEE29B3A5CF4F57C81F6F24ABFCD67FDD3BEE1E4957F9ACFF00BA77DC3639ABEAF883E8FA7A88DB9929E66A76B98E4F8A1F14767B3DA0DE27B4BD926D9AD7FC9DACEDEF72FCD4EE5A77A7739387BD108C9EE095D4F5104CC5547472B5E8A9DCB9FB0989DA58F3523263B527CE1D1D5543F25EA6B9D12261B14EE567EAB97793DCA72C9AED7E344D61D3B795452C7267B578A7D884285A369534979C982969EFB3000219C00000000000000000000000000003064C0120B57A3E1F1F89B66A5ABD1F0F8FC4DB3D460F875F943E6BE3BF796A3F3DBEA000CAE500000000000000000000000000000000000080005920000000AA1E8B1F456B58A2A54A3BD4D8731311CEE4CEF2762F7F795C031E5C55CB1B4B7345AECBA3BF3E39FD16F576AAD332488B53D1CEEE48E743BDEF53A13592C35F44CA9968E08E191A8E47AFCDF05F615269AB6FCAB7BA5A6C658AE473FB9A9CC9DED52E694F6FA7B6C0A89D2F9CE44FC94E4685F0456F14A4CEF2F4FA6E2339B4F9353A9A56623B74EF2F9DDB6750CAC74B68AB54EB48E5F39BEA472724F6901BA5AEB2D550B0D740E8DC9C979A2A76A293ED93C756E8EA279679168DA9B8C8D578679AAA7FBEB3BCDB8D9F533EAEDB2235EF89CADC3B9AE3EB34B57364C569ACF58861C9C3B4DADC35CB8E3C3B5BB47AA25B26A5E92E557549F4638D1A8BEBE3F61C3D7D56955AA2AD5AECB23546278271F7E4B3F4FD95BA6ED754C8DCB3B95CE7B7098554C7042A2AEB65C96592A2A68EA5AB2395EB98D79AAE54BE1BD6F966FBF46BF10D364D36871E9F6EBBEF2E6032A8E6B951ED56AA75298375E776D96BECB2A52A34FD452393290BD5BE0BC7EF2B4BA53AD25CEAE05FA92B913DA4CB6472B9B71AD853E8BA34763BD14E3ED0E9FC97534FFF00E54E93DAABF71A98BDDCF68F577B591E370DC593FB7A23200371E7C0000000000000000000000000000000115001E49F54800000000000000000000000000007DA878D7D237A9D33117FCC87C4FA533FA3A9824C7D0918EF63917EC08B7D994BF6C0F576BCAA6AF26431227B1549B6CE7FF292E3EAA9F8290BDB1468DD7123D39494D13BDCA9F6135D9C7FE525C7D553F0532D7EDCBCFEABFD8E2F9D51ED84DDA9686E1574352F464956C63A2739708AAD45F37D6A8B9F02F4CA769F91ADF4F3D64D4F4F4713E5A8911118C67355EE2C28748ED05226EED554B13A9AB5DC93DA296DA36D95E29A0C59337893922B33E52BD6491B1B1CF7B911AD4CAAAAF043F3F5EA2875B6D4960A273BC9267231D2C69CD8C6F9CE4FF7D67DAE5A3B5DCB472255BEA2AA1C79D0A566F6F78678FA8DAD8D5CE86DB7BA8B6D6D2AC771A95DC64F2734DDFF0094A8A9C3EDF61333CD310C5A6D357498B266C578BDB6DBA797E2B4AC1A46CD628D128A8A3E93AE591379EABFACA4811AD44C235113D4641936D9E7F265BE49DEF3BCB184EC4184EC432031B184EC4184EC43200C613B10613B10C803CAB1AEE0AD6AA77A114D4FA0ACB7E85CAFA665354F36CF0351AE45EF4E4BE24B4113113DD97166C98A79A93B4BF2DEAAD3D5BA66E7E495ED4563F8C5337E8C89F62F71C593E8397B1327E93DA5D823BF697A98D199A981AB3C0E4E68E6F578A653C4FCD8EE2D7276A60C36AED2F69C375BED78666DF6A3BA7BB57F3A4D3CFEB75B98AA40C9E6D7711DC6CD4D9E30DBD88BEDFF00420645FBB370EFF6D5FD7EA000AB7400000000000000000000000000003064C0120B57A3E1F1F89B66A5ABD1F0F8FC4DB3D460F875F943E6BE3BF796A3F3DBEA000CAE50000000000000000000000000000000000000002C00000000001F482274F3C7146997BDC8D44F5952B1369DA1636CA6D8E8E19EE52A265C9D1C6AA9D5D6A43B565C9D78BFCF33155EDDEE8E344EB44E09ED2C7D412B34D68A4A68977667B12262A73DE5E6BE0995207A06D6B71D4502B93E669FE75FE1CBDE68E3B44CDB34FE8F4DACC3315C3C3A9DE7ACFEA9DCCE4D27A11ADCA24E91E329D6F77595452D4CB4B591D540F564EC72391C9DA4DB6AF74496B21B6C6ECA429BEF44FCA5E5EECFB481A1934B49E49B5BCDA9C673FF00AF18B1CED18FA47CD65D2ED2A9FC9A34A8A2996A31E76EAA6157B8F69B4AA4FAF6F9BC1C8A56461C9927D9317A2238EEB3688E6FDA16FC17ED357F448AA5226C8BC9B3B3757C17B4E45FF67CC7C6E9AC92F7A42F5CB7C14AD53872243A7356D7D96446A3967A5EB85EBC113BBB0C7E05F1F5C53FA33D78A60D57B9ACA47E68EEEBECE20A8A1D552D354C4F8A5485779AE4C2F343E7B564FF008962E3CE99BFCCE2C5B3D4DB6F71C572A6463E66A6EE7939B9E6D52BADAB393F096344C2E29DBFCCE2986FCF9F798EBB3775DA5AE9B86F2D6DCD133BC4A16003A0F22000000000000000000000000000000008A800F24FAA4000000000000000000000000000030A64013CDA7E6B6834CDD5BC52A6912355FF00AD31F79AD69A9D676CB33ED9436FAB4A37EF6F316937B3BDCF8F89B14C897ED924D0A7FE2ACB51D22679F46BC797A957D86FD06D6AB696869E07DAE199F1C68C5916654572A26338C193A6FBCCB8B1E2462F0A948B72CCC6D3FF0071FB23D60A7D57A7EADD536DB3D5367737737E4A5DF544EEEC242BAAB68B95FE8753FC127DC7DD36C75A8A9FD4F022F674EBF719FC7257AF1F91E0FDF2FDC4ED5F553263D565B735F05667E6D776AADA2F0FE8753CFF00324FB88EDC29354D75E52EB35A2AD2B9AF6BFA48E9B772E6E30B84EBE084A976C75EAA9FD4D073FEF97EE0BB63AFE3FD4F073FEFD7EE1EEFA94C7AAC73BD30563F56AFE156D1BF33AAFE093EE33F853B465FFD1D57F049F71B3F8E2AC44E367833FE3AFDC6536C757FA1E0FDFAFDC3DDF563F66CFF00F1EBFB357F0A368DF9A557F0483F0A368DF9A557F0486D7E392AFF0043C1FBF5FB87E392AFF43C1FBF5FB87BBEA7B367FF008F5FD9ABF851B46FCD2ABF8241F851B46FCD2ABF82436BF1C957FA1E0FDFAFDC3F1C957FA1E0FDFAFDC3DDF53D9B3FFC7AFECD5FC28DA37E6955FC120FC28DA37E6955FC121B5F8E4ABFD0F07EFD7EE1F8E4ABFD0F07EFD7EE1EEFA9ECD9FF00E3D7F66AFE146D1BF34AAFE0907E146D1BF34AAFE090DAFC7255FE8783F7EBF718FC7256FE8883F7CBF70E9EA7B367FF008F5FD9A926A7DA33915AB4753854E3FD09086525A6AD75050D05652CF4F3544ECF32566EAE15DC571D9C3DC4F936C95B9F4441FBE5FB8F8E8FB83B54EBEA8D457089B0D3DBE9FA456A3B28C544546A7BDCBE0368F566C739B4F4BDAD8A2B1B7979CF9381B56AB4AAD735AD6AE590323853C1B95F7A9123EF70AB7DC2E3575B2AAEFD4CAE9573D48ABC0F818E6779DDD6D363F0F0D69E9000086500000000000000000000000000000C19300482D5E8F87C7E26D9A96AF47C3E3F136CF5183E1D7E50F9AF8EFDE5A8FCF6FA80032B94000000000000000000000000000000000000000B00000000012FD9A5B52BAFCB3BD33152B77D7F5BABED2225B9A3E99BA7F46BEB2A3849235677F7A7D5F763DA6AEAAFCB4DA3BCBABC1B4F1975117B7D9AF5945F6A1745AABCB68D8B98E99BE0AABCC92ECF28E3B5E9892E350DC3A66AC8ABD7BA9CBDDC4ADE9229EF97C6B132B2544B973B9E11578AF827C0B1768B5CCB669F8ADB4AA8C599123444E68C435F2D76AD30C7EAEA68F2F364CDC432768ECAC6EB58FAFB8D45548AAAE95EAEF51AC8A39837E3A4443CCDED37B4DA7BCBD000954306401D5D3D7EABB1D4BA5A554735C98746EE4BFEA6B5DEE53DD6B9F5554A8AF77244E4D4EC434C15E4AF3736DD59673649C718B9BDD8F27900191840000000000000000000000000000000115001E41F5480000000000000000000000000000000259B2FBAC741A93C8EB38D15C58B4D2B579657822AFB71E270F525AE5B1DF6B2DF322AF42FF31DF94C5E2D5F61CD5CA2A2A2AA2A7254EA2C5BF3135A68E86F50222DDED8DE8AB1889E7491FE5227BFDBD85BBC6CD1CBBE0CF197FF005B749F9F94FF008482AE9E24D87365E8A3E956919E7EE2678B9133929CC96E4772A6AED87D4454F222CB4B036195BD6D76F27B95389512136F263E17CDFEAF37F74B2002AEA8000000000000000058BB0F8A3935157B64635E9E4C8EC39329F490AE8B1361B2322D457292472358DA3455555E089BDC549A7768714998D2DF64675F2359AD2F0D6B5111B3E11113098C275120AACE97D99474DC1B72BF3B7E444E0E6C489CBD9C3FF729E6DB6EA6D59B40BADC5DE848245A89A57A61AAD444C357D7BAABEA381AD2FCED457D9AB1379B4ADF9BA762F0DD8D392E3A9578A9333B4CCB5EBBE6F0F079562267E7E51FE5C2001475C0004000000000000000000000000000000003BF6AF47C3E3F136CD4B57A3E1F1F89B67A8C1F0EBF287CD5C77EF2D47E7B7D400195C90000000000000000000000000000000000000196B55CB86A2AAF62264CB1AAF7B58D4CB9CB84253B4BC827743B38AE9988B55590C2BD8D6ABFEE3AF1ECEADB1373575D3B913B15AC4F81AD6D5E38F37571704D664EBCBB47E32AB816CB74BE94819F3B246FFD79F39F78F93F44B39F92F8C8EFBCAFB6567B44B63FA0E48FB592B1FAAB9D376E75D2F34D4C9F41CECBF8670DEB5279B53B8A52D053DB61546EFE1CE6B7A9A9C13FDF71D8B63F4ADBA7596DF352C52AA6EEF23B2B83DDCA9F4D5D67E9AB24A79A54444CACB8E1ED35AF9F9F245A6B3B43A98387F81A4BE1A64AF3DBCF7F245B6516BDEABA8B8CA986C69D1C7EBEB5F670381AF6E9F29EA19FA3766183E699D9C39A96B5B1B69A3A1F24A09E28E2E3F464455CAF5E72466A367543222F93574E8E55CAEFE1FF007134CF5F16725FF45753C3737B1534DA7989F39EAAB0137AED9D5CA1CAD2CD0D42272CF98ABF123770B15D2DEDDEAAA299ACEA722653DC6ED72D2D1D25E733683538637C9497300EE54C283234DE800480000F2002C0000000000000000000000000000000022A003C83EA8000000000000000000000000000000000ED692BFCFA76F11D5C397C2E4DC9E1EA919D7E29CCE2810AE4C75C959A5BB4A71AC6D4B6CA49AF1A72557E9DBBB375EC6F28D55728D54EAE3C97AB910545251A33543AC6E9E92B61F2CB355652A299533CF9B93BFB53AFD66C6ACD2294B49F2C69F93CBAC7226F2398997C3DCEEE4EDE69D65A7AB4B0E49D3DBC1CBE7DA7D7E7F8FD51107969E88744000480000000000007574CD35D2E35925AACF9DEAE674736170891A2E55557A93B7D878D3F64AFBF5C19496D855EE55F3E45E0C8D3B5CBD44C6ED78B7E8DA096C9A6A549AE9279B5B7044CAB57ADADEACFB93D64C7AB47559FAF838E37B4FEDF8CB5F5A5C68EC9676E93B0C88E631735F50DE0B2BFADBF7F821040AAAAAAAAAAAABC5557AC1599DD974F8630D36EF3E73EB2000360000000000000000000000000000000000001DFB57A3E1F1F89B66A5ABD1F0F8FC4DB3D460F875F943E6AE3BF796A3F3DBEA000CAE48000000000000000000000000000064C1D0B25AAA6F35CDA5A36E5EBC5CE5E4D6F5AA916B45637964C78AF96D14A46F32D4A6A79AAA66454F1BA491CB846B533927564D9E4D32364BACDD0B57FE5B78BBDA48E286CFA26D8D7CBE754BBEB632F91DDC9D49EE20B7FD6572BB6FC71C8EA5A65E4C8D70ABEB5E669F899337C3E91EAEF7B269387D62757EF5FFB63FCA6EEA9D33A61158CE85274E0A8D4DF7F8F6156DCEA23A8B954555347D147249BEC6A751A88864CF8B07873BCCEF2E76B388CEA62295AC56B1DB67726D5D7B9A1489F5AF4444C65A9BAABE27267AEABA8E3354CAFF5BD4F8221932463A57B4356FA8CD93ED5A65E40305B661DE59000D8DE59472A72554F529B305C6B29F1D0D54ECC7E4BD4D403963CD6ADEF5EB59949A835ADE6937512A3A5627D591339F1E649EDBB4785ED6B6E74CAC775BE3E29EC2B23260BE9B1DFC9D0C1C5F57827A5F7F9F55BCB43A6754C2AEA6585B3AA6731F98F4F5B48A5FF0041D75035F2D0392AE24E3BA9C1FEC21B13DD1C8D9237398F6F16B9AB8545269A6F5DD651B9B0DD57CAA9F96FE3CF6F8F598BC2CB8BE1CEF1E92DEAEB347AEF77534E4B7F747F984338A2AA39151C9C1517A816D5FEC16ED554495D6C7C5E538CA48DE4FEE7779555653CD4750F82A58AC958B87357A94CD8734648F4973F5FC3F268E627BD67B4C3E20C193339EF2002C0000000000000000000000000000000022A003C83EA8000000000000000000000000000000000000614EDE96D4B70D3952AFA2911F0BD7E769E4E31C89DE9D4BDE9EF38A60989D95C98EB92B34BC6F12B0DF66D3FAD58B3E9C9E2B4DE1C9E7D04FC237AF5EEE3B7BBD88436F565B8D96758AE7492C0BD4F54CB1DDE8E4E0A73D1551C8E6AAA3938A2A2E153BD17A949759F5F5DE8A04A4B824375A0C6162AB6EF3B1D9BDF7A293BC4F769C62D460E98A79ABE93DFF49FE51045C99276AED077B4CCACABB04EEE2AADCBA3DEF7A7B907E2F5954DDFB26A2B6D73179239DB8BEEC93C933D978D7E3AF4C9135F9C7F9EC82026B26CC75335331D3D34A9DADA84FB707C9366BAA57FF431277AD4331F1239656F6FD37F7C7FDA1E09D45B34BAB1BBF5F5F6DA38FAD5F36F63DD8F7994B0E8CB53B376BF4B719539C144D4C2AF670CFC49E59F356788619E94DED3F846E83451BE591B1C4C7C923D70D63132AABEA26B6BD06B0D3F97EADAC65A681A9BC8C5722CCFEEC757BD7B8FACBAF696D71BE1D2566A7A16AA63A79911F22FFBEF5521973B8D6DDAA9D5171A996A265EB7AF044EC44E4883DD5667519FA7D88FFB9FE23F74AEFBACA265B56D1A4E97E4DB5E30F97FE6CDDB95E69EDCFA8861E51307A2932D8C382986BB523F99600043280025600000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF5183E1D7E50F9AB8EFDE5A8FCF6FA80032B92000000000000000000000000120D1FA7D6FF005CF8DD32450C288E7AE32E5CF5211F3668AB6A68645928E77C322A63798B829939A6BB57BB369ED8E9922D9637AC792D6752E95D36DCCEDA7E99A9C77FE724FB4FA52EB4D3CC7E237244ABD69163E053F23DD2395D2395CF5E6E5EB3C9AFEC713F6A665D98E3D7C73FE8E3AD63E4B8AFBA7EDBAA99E554D53F3FBBBAD918EDE6A772A15EDDB49DDADB22A3E95F347D52429BCDC7D8722DF5F556F9D26A399F149DAD5E7EB26F6DDA44B1B11971A44957ADF1BB0ABE0A56299B0FD8EB0BDF51A0E213BE78E4BFAC7641DB4B52E7A3129E6572AE1137172AA496C3A1EE3707A3AADAB494DF595DF497BB04A1DB43B5747BC94753BFF93BAD4FB4E05E75FD6D5C6B1D04494AD5E1BD9DE77FA133933DFA56BB295D2F0DD3CF3DF273FE1094250696D36C449920595BD722EFBD54F2BAA34B54B7724647BBFF00541C0A9E59649A474933DD23DDCDCE5CA9E10B7B26FD6D699944F1C9A74C38EB15F92D8ABD2DA7AFD0F4B6D7C513978EFC18C78B79107BC690BB5B65545A774F0F54B12653C53A8E35256D4D04C92D1CCE8654FACD526F69DA44F1C691DCA95B32E31BF1AE157C0AC53362EB5EB09F1B41ADF8D5F0EDEB1D9075A4A8E93A3E825DFE5BA8C5CE491D8744DCEE32B5D551BA929FADCFE0E5EE44257F8C4B5247BCB4751BFF0093BADFBCE15DF6895752C732DD036991786FAAEF3B1F613E267BF4AD76234BC3B04F3E4C9CFF008424EDB6697D371A36A920E9719574D87397BF1F71E3F0AF4BCB1A325466EFFD54FC3E054D34D2CF2BA49A4748F771573972AABEB3C389F64DFADED33289E3B349E5C38EB15F92DAAAD37A72FECDFB6BE38A444FA54EA889E2DE441EF3A3EED6D7F9B02D4C5D524499F6A751C2A3AAA8A2A86CD4933A291BD6D5C139B5ED1AA216232E14AD9D7ADEC5C2AF811C997175ACF341ED1A1D6FC68F0EDEB1D908F23A8E9773A1951DD8AC5424363D1772B948D5A889D4B065155F2271C762212C4DA1DA5D1E5D4751BFF92AC6FDE71AEBB45AAA8639B6DA76D3B57948E76F2FB303C4CF7E95AEC9AE9387609E7BE5E6FC2212DA082CBA3E95D1AD5246E7E15EAF7E55EA9D78EAF035AA35669AA97AB6A3122F2557C394F815255544B5333A5A891D24AEE2E73972AAA7C488D1C77B4F55B271FB563C3C38E22BE92B764D3DA6B5042AB6E74314C9C51D02A22A7ADA56FA92D12D92E4B4B339AFCB51ED73792A2AAA7D8685354CD4B2A4B4F23A395BC9CD5C2A18A99E5A999D2CEF74923B9B9CB95532E2C56C73F6B7868EB35B83538E27C3E5BFAC767CC00677300000000000000000000000000000000115001E49F5480000000000000000000000000000000000000C003200212185C271C267A97AD0C804ED3DDF48AB2AE36EEC75752C4EC6CCE44F89F575C2B9CDC2D755AA75A2CCE545F79AB83284EEAF875F41EAB22E65739EBDAE72AFC42222270320895A221E4C80000000001200090000000000000000000000000000000000001DFB57A3E1F1F89B66A5ABD1F0F8FC4DB3D460F875F943E6AE3BF796A3F3DBEA000CAE4800000000000000000000000064DDB6DAAB6E7D279053BE758F1BC8DEACE7EE3E75F41536FA8582B617432A26775DD8579E37E5DFAAF38B2453C49AFBBEBE4D5001650000197183AF1E9DBBCB4EDA88E825753B9BBED9131C5BCF2724AC5EB6ED2C97C3929B73D6637650C9843AD6DD3B77B8D3A4F4B412BA25FA2E5C3729DA99C649B5E2B1BCC98F0E4CB3CB8EB33F27254C60E85DACD70B56EADC69A4811CB86AAAA2A2F8A2A9CF4E02B3168DE117A5B1CF2DE36914C19530595650F4793AD53A76EF4D4CEA89A865642D4DE572E38276F322D78AF795A98AF9379A5667672000140C9800003AD169CBBCB4EDA88E825753B9BBED9131C5BDA56D78AF7964A62C9937E4ACCEDE8E51930096260004A40000000000000000000000000000000115001E49F54800000000000000000000000000000185036AD9433DCEE34D454AD474F3BD23622F6A9743F4DE8FD196D864BE3595133FCD49266AC8E7AF5EEB392271F81586CF2B22A0D676AA8A876EC4D955AAABD5BCD56A7BDC5A5B5CD2B72D40943536A6A4EB4E8E6BA2DF46AAEF2A2E533C3ABB7B0C958DA3770B8966B7B4530DEDCB49EF30F355A4B4AEAEB33EAB4F24704A996B65851588D7A753D9C3B53AB257BA0B4749A86FF53495AAE869E8B854A27D2DECAA6E278A2F1EE2D0D9369BAFD3F67AB6DD11239EA25DEE851E8EDC6A261155538657BB3C3079D13554B0EB3D59429B8C9DD5093353ADC98E3EF5F796DB78DDA11ADC9863363C569B4476969D737679A7EBD2D7574B4A950DC6FF004913A5DDCA7D6771C29C0DA5E85A2A2B5FCB5624DDA76E166898BBCD56AAE11EDF154CA72C2E7860D6D61B3DBF55EA8AC9E8216D4D3554CE95B2ACAD4DD472E551D95CF0CE3867860B1AF141F27ECD2AEDF23925753DB1D1B97A955B1F34F14276E9B4A273469FC2C98B24DA67BC6FBABED9A684A4B8DBD2F17CF3A9D573143BDBA8A89CDCE5ECCA7B892DB23D9FDFEB1D6CA2A3A474D85C6EC4E8D5E9D6AD77055FF7D477FE467D468186D146F48DF251C70EFBB8E115111CABE1922745B30B22E528AF957E5B1F37C5333CD5EBE08994F69131B76567575CF6BDF2E49ACEFD36ED1F8CABEDA069A4D337C5A68A47C94B2B7A485EEC671C9517BD17E2871EC3047537DB6C33377A392A63639BD4A8AE445453775969FACD397875256B925DE4DF8E744E12A2AF3F5E79A1A9A6FF00B4B68FDB22FE7429E6F498ED33A5DF9B9BA775A3B55D2D67B46974A9B7514704EB3359BCD55CE1517B54E26CEB67A97BA76DCAF0E923A157622898BBAE971C15557A933E2BDDD735DB5FF6359FB4C7F071D8D46AFD3FA02A9287E69D4F4BD146A9F55708D47782AE4B4C46EF398F5D9ABA6AD2B6F7AD698DFF000E8E43AD7B3FA49FE4E99B6D64EDF3375F2F9CD5EC572AF05F1227B44D9DC16BB7C973B12BFC9E2F3A5A772EF2B5BF94D5E784EBC9583955CAAAAB955E7DE48A4D6D7E75A62B6A572B69591742A88C4DE73718C2AAA67970226636DA61D5A6835382F5BE3CBBFAEEB0B65FA52CB78D27155DC68239A7591EDDE555CE11787594FD6B1ADAC99AD4C35AF7227AB25F9B18E3A162FF001A4F89425C13FA7D47F88EFE6522D1D224E1D92F7D5668B4EFB4FF002B37641A6ED37BB657CB73A38EA1F1CC8C6ABB3C1319211AE28E0A1D5972A4A48922822930C6A2F044C21676C13D0F74FDA13F950AE3693C35CDDFFC6FB106DEEC234B92F3AFC94999DA23F84700055DC00000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9AB8EFDE5A8FCF6FA80032B920000037AC75CB6DBCD1D52265237A2B93B5ABC17DCA5A5B47A08EAB4ABEA226377A07B256AB53AB92FB973E060C99BC3BC5663BBA7A4E1FED583265ADBAD7C94F82D1D95D0363B4D555CCD45E9A4DDCBB8E5AD4EFEF55399B58A0486BE8AAA34444918B1AE3A95AB94FE62B1A989C9E1B2DB845EBA38D56FFA2020BAAC71C162D150C93469F334FD33D1C9C55CA9BCA9ED5C113D9F69E8AF12D45DAE8DE998B22A318E4F35EEEB72A75A71E5CB9911AA8DAD331D2192DC12F16C78EB6DED68DFE5081A31EE6AB9AD556B7E92A2706A76A9E50B86E1AD2CF6AAF7D0F412BBA25DC7AC4C4DC62F5A734E5D7833A9B4ED05FED0EADA16312A959D2C534698E93AF75DDB9E5C7914F6B98DB9ABB44B2DB8145A2D1832C5AD5EF0A74F4D6B9D9DD6AAE1155709C910B4F64B1B24B0D52BDAD77F497734CFD461BD2DE2C3A6EB27A573F153348E9665646AE54572E7CE5EECFB09B6AE62D34AD779571F04ACE1A66C9962B13EBFFEB81B1F5F9EBAFEAC7F171C8DA8AFFC56FF00F099F6926D9DD67CA3A82FD54888D64AAC73513879B9763DC7CF50E9B9EFFAD5EEC2C7451C6C49264EDE786F7FC33EDC3178AE79B5FA746F5B4D6CDC3298B0F5F7BFCCAB005C7A8EF16DD2F6E6D252C50BAA91BF350639762BBAF9F8AFBC8169CB14DAB6AABA69AB12196356AB9563DEDEDECF7A631836A9A8E6AF3DA36872753C2FC2C95C18EDCD927CBD3F5464162FE2CA4FD28CFDC7FF00D1CDD43A1DF66B44F5EB5E93245BBE6245BAAB972273CF793ED38A6768952FC1F598EB37B53A475EF0B0ACBFD8AA3FD85BFC88413416A3B5DA2D33C37255E95D3ABD311EF70DD6A7D84EACABFF000551FEC2DFE4423BB298209AC555D2451BB76A5C89BCD45C26EB4E7D6622B6DFD5EA7356F6CD8231CC44F2CF7EBE8835DE782EDAA24922CAD3544CD44CA6155382722DED4B5EB62D3D355D2C2C73A246B58C5E0D4CAA2272EAEE21F4178A3B2EB5BBC53C123FCA266471F46D4546AF7E553B509CDFAE505A6D72D65546F9216632D6222AAE571C9553B49CF69B4D6BB7462E198AB4C79EFCF116999DFA76DB7531A8F50D55FE581F56D8DBD1355A8D622E32ABC578AAF77B0E4265CE46B532ABC93AD549356AC3ABB5840DA06BE2867C22AAA615A8D4E2BC17B13813DB957DA345D143143499924CEEB1A89BCFC632E72A9B7E3785B52B5EBE8E25787DB596BE7CB97DD8FF00DBD7F4538F4731CAD7B55AE4EA54C28452DFB2DFADBABE3968AAE85AD95ADDE58A4C3DAA9CB28BDBCBB17895CEB1B2FC897C969D8AAB03DA9244ABCF757ABC1514C98B51CF6E4B46D2C1ADE19E0628CF8AFCF49F3ECE3977EADFEC857FF81F6147A17FD7B29E5B3CADAE544A5E8F32E796EA2657260D6CED6A4BA3FF008F579F1E7AFAC7F2A011AE56B9C8D556B79AA27043C97258356D9EBAB9B6EA589F4EBF463DE62358EF5617811DDA85860A564572A385B12B9FB93358984555E28EC7B7DA5E9AA99BF25ABB353370788C13A8C3922D11DD5E9F4E8A4E8FA458DE8CFCADD5C7B7916168BABD376AB52544D5113AE1BBBF22BD8AAE6AE3E8B729F0EB3A769D72CB95C9B4B0DAA7742AE46F48C5DE56E579B91138278936D45A26796BD20C3C270DEB59C99A226DDA2237FF00B54C5EB65FEC551FEC2DFE44227B4DB0524148CB95344D8A457A32646261AE45CE171DB9E1E24AECD84D1547CFFF0002DFE4435B519632D2B6875B8568ADA2CF9715A77F75461E991BDF9E8D8E7639EEA6495E824B1433CD517B9A2599AA8D8A2918AAD4EBDE5CA633D9D9825157AFA8E3AEF24B6D03EAD8C76EAB99C33FAA888B936AD9AD13B56BBECE2E9F8762C98E32E5CB11BF977955460B9B54D9A9AF9627D5793AC356D87A68D55BB923571F45DF035B65D1B1DA71CAE6B557A777154CF5214F6C8E49B6DD9B3FD06D1A88C336E931BC4EDFE151E1CBC1A995EA4EB55EC430B945547261C9CD3B0B62A2FDA7B4DDDAA69DB03DF52F955F3CAC622AA2B955DCFB133D474F5A5AE96EFA72A6658DAB2471ACB148DE7844CE33D8B823DAE62637AF4947F43ADA979C79626D5EF0A50F4D6B9D9DD6AAE132B84E484E3675A660B8A495F708FA48A37EEC71AFD1739138AAF6E3284AEEFABACF64ABF2148A47BE3C239208D1519DDCD3DC5EFAADADC948DE58B4FC222D8633E7C91489ECA73031DE5B1A9A8EC17DB3A55A54D2D34F237A48A773918AE5EA4777110D05A763BD5CA5756377A929B0AE445FA6EEA4F5732D5D4C4D66D31B6CC59B84E4A67AE1C768B7376945DB1BDF948DAAF544CE113A8F05BB7BD5D6DD3B55F27D2D1748F89137DAC446B19D78E5CF1DC6C4715A35B595D3A41BAFE2C47AB51248DDEBECE463F6B98DA6D5E8DBFE874BDAD8F1E589BC796DFE54D983ED594EFA4AC9E9A54C490BD58EF5A29F1373BF5702D59ACED20002A000000008A800F24FAA400000000000000000000000000000001B36DA292E15F4F4703A36CB33D236AC8EDD6E5782657D782CBB97E1DE90B440E4AC4ABA654566236F4AB172C6555B9F6E53815631CE639AE6395AF6AA2A39382A2A75A16659F6B55D4B44C82E56F8EB646A63A56CBD12B93BFCD5E25AB31E72E6EBF166BCD671D22F11DE27FF00B4D765EDBABAD75B75D4324AB5154E4DCE993755236A705DDC26EA655DD5DFD656D414373D59ACEE971B055454F347374AD91F22B17755551B8C22AAF04E29DE7AD59B49B95F28DD474B0328291FC246B5EAF73DBF92AEE1C3C08AD86F35B60B9C75D6E911B33515AA8E4CB5ED5EA54ECE05F9A22366AE9B459AB1932CC445ADD223CA13CBE5E75F5BEBBE4674D34B2B9C8D64F153B7E7117B177797BD09AEAC992C1B319E9AAE6E92A56952955D9CABE47261CBED5555F122ADDB34BD1A23EC91BA4FCA4A9544F66EFDA41B576ACB8EA99D8EAE56B208D731C11F06B7BFBD7BFD98226D11DA5829C3F3E7BD2B931C5223ACEDB755D50CD35F3661BD6C72AD5CD43B8D46AF1DF46E1CDCF6E5150ABB66D65BCB359D13D292AA9A381EAB3BA462B111B85CA2E7B7960D2D19ADAE1A5F7E281AD9E8E476F3A07AAA222F5AB57A95496546D8AA16354A5B3C5148BC9CFA857A7B11A9F12DCF129F63D569A3262C548B56DE6DADB4C1F29DD74FDB69918EAF9164C22BB1C155A888BEBC2FB08737485DB4FDE6CB51728A3646FAE8636E1E8B95DECF57A8E5B351D63F53C37BAD72D454C72A49BB9DD4C27D54EC4243A9B6892DF56DAAEB7320F23AA654A625DEDFDDCF9BCB8732B368DF796CE3D3EAB4F4AE0A444D769DE7E69F6DB3868C8FF698FE0A75AD7241AC342358E7E3CAA99617AA7D493185E1DCA992A8D69B419753DA1281F6E653A248D937D25DEE5DD84385A5F54DD34DCCE75BA66F46F545921913798FF0FB899B44CB4E9C2B34E9A2BDAF13BC3E957A2F50D35C92896D55524AAB847C6C5746BDFBFCB1EB542C5BA682B1DAF447945DA356575341BD24D048A8AE7AF1DD4EA5E2B84CA761A71ED8E548D3A4B2C6E913ADB51BA9ECDD5217ABB595CF53AB1957D1C548C76F369E34F373DAB9E6A47BB0D9E4D7EA2D4AE48E488EF313DD676C42BA39B4C4F468E6F4D4F33955B9E3BAEE28BEDDE4F02BCD5BA26F3417CAB482DF53534B2C8E7C32411ABD15157288B8E4BD5C71C8E0586F55D62AF6D5DB65E8E544DD722A65AF6F62A7616353ED8E7489A953668A495138B9950AD45F056AFC47344C6D28B69F55A5D45B2E9E39A2C956C974FD5E9FD3F3FCA2C58AA2A65593A35545DC6A222222F7F055F1294D5F5CDB9EA7B9D646A8B14B3B958A8B9CB51708BE2888A493546D2AEB79A57D2D346CA0A67A61FD1BB79EE4EB4DE5E48BDC840C8B5A3688867E1DA4CB4CB7D467FB56F2640055D900000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000171E8DA88EF9A35B4B33B7B76375349DB8C613FF8AA14E128D0FA9A3D3B254A54C52CB0CDBB848F0AA8E4CF6E3ABE06B6AB1CDE9EEF7875F836AEBA6CFB649F76D1B4A6775CE9DD9C369D331D43A148F08B8547BD7CEC75F5B94D8BC52FE13699B6CC89973A486454C752AA35DEE55F610BD6FAAE2D410D2C54B14B1471B9CE7A4889C57184C617D7ED37B496B6A6B3D962A2AC82A247C6AEC3A344C61573D6A9DAA6AF8178A45F6EBBBB71C4F4D7CF6C336FF4B96223E70EDED52E294D638A89AEC3AA9F877EAB70BF1DD36B6613324D2D1C6C5CAC723DAEF5E73F0542B9D637D4BF5D52A236BD903188C635D8CA76F2FF007C8F3A5F5154E9FAB7490B52585FC2485CB8476392E7A94C9ECD6F07963BF76A7F57C71C4A734CFB9B72FE8F86A7A6969350DC239D8AD72CCF7A653E922B95517D8A5C3A4A1928B4B5147548B1BDB0EF391C98DDEBC2F6703851ED06CD335AFA8A6A86C8DE288B1A3B1EA5C91FD55AE9F74A27D15BA17D3C12262491EA9BCE45E6984E09ED52B6AE4CD1159AEDB33E0C9A3E1F6C99E9979B9BB424DB2D9124B3D7C8D4546BEADEE4CA617E8B4AD75348B36A2B9BDDCFCA246F823951090E8AD594BA7EDD3D35553CF23E49964CC7BB8445444C715EE22972A86D5DCAB2A58D735B34CF9511DCD115CAB8532E1C76AE6B4CB9FAFD5E3CBA2C54A4F58EF09E6C753FA4DCFF00563F8B898DDB51D05AAE1494750F5E96A1C89C3EA22F255EECE3FDA159688D474DA79F58EA98A593A746A37A3C70C6739CAF79A5ABEF10DEEF4EACA7648C62C6D6EEBD133C33D9EB31DF4F39334EFD9B9A6E2F4D270FA56931CF13DBF54D3699A716A62F95A8D999634F9F44E6E6FE57AD3E1EA2B6A5ACA9A4DEF25A89A1DEFA5D1C8ADCE396704FAC1B40829ED51535CA9EA67958DDC57B11151C9D59CAA71209767D24B5F2C96F6C8CA67AEF35922222B73D5C15781974F17AC78778ECD0E2D7C196D5D569EDD67BC79C4BEDF2BDCB8FF58D6FF10FFBCF94D70ADA889D1545655491BB196BE6739171DCAA6A21E9BD66CF2D63C9C9F1B24F49B4AF1B327FC1547FB0B7F910E36C8D3FA8AB3F6A77F2B0E5506BBA1A7B0C16F75354AC91D3242AE4DDC2AA371DA73F43EACA3B0DB27A6AA86A1F23E65951636A2A222B5A9D6A9D873270DF96D1B79BD95788E9BC7C36E78DA2B313FB34B50FF6F65FDB19F61606D23FB1F55FAD1FF3A1575D2E50D5EA675C636C9D03A76CB8544DEC263298CF3E04A3566B5A0BC58E6A1A7A7AA64AF562A2BDADC70722F538CD7C5799A74ECE7E9F5986B8F5516B6DCDBEDF8F77336673321D55123F199237B5BEBE7F61616AEBD45656C12D4DBBCAA27AAA6FF0C317861173DBF614B413494F5114D0B959246EDE6B939A2963DB76894D25224577A47ABF1873A344735DDF85F871275386D378C911BA384710C74D3DB4F7BF2DB7DE276DE1B169D62CADA87A5B2C2F9256B72EE895A9C3D7822DAFEE53DCAB699F536FA8A27C6D73712A7D2E29CB87124EFD7B65A389CDB75148AF54CEEB6346267BD7FD14AFEFD77A9BD5C1F5556E4E3C18C4E4C6F6211831CF3F372ED0713D5D674DE178BCF33E91110E7A17B6A2824A9D3159140D57C8EA75DD6A75F9BC8A250B366DA3D1B6955B4D4D3F4CD44DDDF44C78F12FABC76BCD66B1BECC3C0F53830D32D735B6DE3F942F47D2CF57A9681B4E8ECB256C8F544CE1AD5455CF67678962ED4E546699E8DDF4A499A89EB4CAFD86BC3B40B4246B23E9A78A77226F31AC4CAAFAF3F120FAC752CBA86A63548FA2A58F8C6C55F39557EB2E3BB063E5C9972D6D35DB66CF8BA6D0E8B262C7939E6E9EDB34F5A74F587CBABE9A3A89A38D1F2BDEDDFE3D8D45E073E8F5B54DCEB62A1B2DB1B1ABDC88AE72E775BD6E5444E18F5A9F2B06BFA46DB23A5BB4122CAD62315CD6A39AF4C63976F69F2B9EBCA58617C760A148A477395EC46A7B13ED31C62C9369E6AEF3FB36FDB34D4C549C1922958EF111D6521DA871D2927F8ACF89D0B3FF0062A8FF00616FF22108D55ACE82F763928E3A6A9648F56B91CE46EEF05CF6E78F2E47DEDFAEE820B053DBDF4954B2C74C90AB911B8CA371DA57C1BF8711B79B2CF12D2FB55EFCF1B4D76FD5EB679A628AAEDE973AF8D27DF72A471BF8B511170AAA9D6B9F81F6ACD75494150EA4B2DAD1DBAEE8F7B1BA8AA9D8D44CAFB8E368BD60DB2D3BE8EBA37C94EAE57B1ECC658ABCD30BCD3ACEDD5EBBB3C2F74D436F592ADC8B87BA36B3DABCCC96C7926F3CF5E68FD9AB8353A6A69A9E0E48A4C77E9BCA5B13EAE4D3CE7DC11ADAA7D3ABA46B5308D5545E18EEE470F653C74BAE787CF3FEC39147B42A75B6F4374A7A87543D1C8F744898C2E708995EC5434B486B0A3B0DA568EA29EA647F48E7658D4C71F131F81922968DBCDB7FD534939B1DF9FA444FF8EE8AEA37B9DA86E7BCB9C554889FE654FB0B9D7FB1ABFB17FF00A147DD2A1B5773ACA9635CD6CD33E44477344572AF1F693FFC3CA04B22512D3D4EF793F45BD86E33BB8CF3363518ED7AD368ECE570AD6E1C3933DAF6DB9BB7EEEDECC26649A5D91B172E8E57A3BD6AB9F82A15A6ABA59E975157B6A5AE6B9F339ED554FA4D5555454F053EFA5B51D4E9FAA73E26A4B4EFFA712AE33D985EA5276CDA159A68DAB5305431E8B9DD7468B8F795E5BE1C93688DF764F1B4DC43494C3932725A88851E86BBD5504554C4818D91BBC8C7B951E89DE9824FB257B194972A6739AE959322AAB573BC8A98E1DD96AFB4E76A6D7E957472525AA17C69222B1F2BD51151392E113E2442C178AAB257B6A69153B1EC5E4F6F62FDE5FC3CB931CC5A1869A8D1683554B6199988EF2B2752EA9A7B2DD24A6AAB42CAB8CB64554C3D31D59416DD592D450F4F6ED3B52B4EAABC6356F15F51F06EBFB2D5C2D4B951488FC6775CC491117BBFDA1AF72DA1D2C54AE82D14922BB186BA4446B1BD8B84CE78FA8D78C56988AF27EEEB5B5F8E26D97DA2397CA2223741B51543EAAF95751240FA7748EDEE8A44C39BC3AFE3E2734FB544AFA89E49A672BE591DBCF72F5A9F3C1D4AC6D110F179AFE25ED6F59790012C4000000008A800F24FAA4000000000000000000000000000000000005800054000580005800000005400120002150001200094800090000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F4614C9850960C9801564C80580C29930A40C000AA59064043C8009480000000000000003D00025E40010000B0000000000000000AA193201000003C80091E8005800007900048000000022A003C83EA900000000000000000000000000000000000160000000000016000000000004A0000400000000000090000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F400097900040002C00000002A0000000000000000000000003D000240001E4005800000000000000010000A800000000000B000000000000000008A800F20FAA400000000000000000000000000000000000580000000000058000000000012800000000000000000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F40009000079001600000001540000000000000000000000003D000240001E40058000000000000000000055000000000000B000000000000000008A800F20FAA400000000000000000000000000000000000580000000000058000000000012800000000000000000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F40009000079001600000001540000000000000000000000003D000240001E40058000000000000000000055000000000000B000000000000000008A800F20FAA400000000000000000000000000000000000580000000000058000000000012800000000000000000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F40009000079001600000001540000000000000000000000003D000240001E40058000000000000000000055000000000000B000000000000000008A800F20FAA400000000000000000000000000000000000580000000000058000000000012800000000000000000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F40009000079001600000001540000000000000000000000003D000240001E40058000000000000000000055000000000000B000000000000000008A800F20FAA400000000000000000000000000000000000580000000000058000000000012800000000000000000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F40009000079001600000001540000000000000000000000003D000240001E40058000000000000000000055000000000000B000000000000000008A800F20FAA400000000000000000000000000000000000580000000000058000000000012800000000000000000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F40009000079001600000001540000000000000000000000003D000240001E40058000000000000000000055000000000000B000000000000000008A800F20FAA400000000000000000000000000000000000580000000000058000000000012800000000000000000000000000000000000000000000000077ED5E8F87C7E26D9A96AF47C3E3F136CF4F83E1D7E50F9A78EFDE5A8FCF6FA80032B920000F40009000079001600000001540000000000000000000000003D000240001E40058000000000000000000055000000000000B00000000000000000FFFD9, '1', '2021-03-13 13:10:28', '1', '2021-03-13 13:10:28', b'0'); | |
| 148 | +INSERT INTO `inf_file` VALUES ('5e8609290e915c4fa8b08e67.jpg', 'jpg', 0xFFD8FFE10DFA4578696600004D4D002A000000080007011200030000000100010000011A00050000000100000062011B0005000000010000006A012800030000000100020000013100020000001F000000720132000200000014000000918769000400000001000000A8000000D4000AFC8000002710000AFC800000271041646F62652050686F746F73686F702032312E30202857696E646F77732900323032303A30343A30322031373A32343A3135000000000003A001000300000001FFFF0000A00200040000000100000320A003000400000001000001C20000000000000006010300030000000100060000011A00050000000100000122011B0005000000010000012A012800030000000100020000020100040000000100000132020200040000000100000CC00000000000000048000000010000004800000001FFD8FFED000C41646F62655F434D0002FFEE000E41646F626500648000000001FFDB0084000C08080809080C09090C110B0A0B11150F0C0C0F1518131315131318110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C010D0B0B0D0E0D100E0E10140E0E0E14140E0E0E0E14110C0C0C0C0C11110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC0001108005A00A003012200021101031101FFDD0004000AFFC4013F0000010501010101010100000000000000030001020405060708090A0B0100010501010101010100000000000000010002030405060708090A0B1000010401030204020507060805030C33010002110304211231054151611322718132061491A1B14223241552C16233347282D14307259253F0E1F163733516A2B283264493546445C2A3743617D255E265F2B384C3D375E3F3462794A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F637475767778797A7B7C7D7E7F711000202010204040304050607070605350100021103213112044151617122130532819114A1B14223C152D1F0332462E1728292435315637334F1250616A2B283072635C2D2449354A317644555367465E2F2B384C3D375E3F34694A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F62737475767778797A7B7C7FFDA000C03010002110311003F00F544924925293A64E929F18FADE19FF3ABAA48DC4DEDE49FF454ACD369FB3380796ED036EA74F26A9FD78C97B3EB8F55606C8F581FFC0A958D6E43DC037807B28C86505BB87D62EE9F91F68AEC7BEE6B5CC6BDC6480F11A4A1FA7D77AB07DD8F8F93934564FAAF631CEADB03D477AD77F315ED67BFF48F540820C1D7CD751F55053FB1FAB6EA8E4BFD3DA69AF09B90F26CBB0598CCFB45EFF45EFBED67E830BD1B1FFA1B327DFE95753D002D0644079DB3A5755AB26AC4B316DAF26FD68ADE20D83FE05C7F4767F61FF4FF0047FCE223BA3758AADF4DF876B2D01C4B4B44B431AFB2DF536BBF44E6574DCEDB6FBFF4562EC7AD136756E977D8DFB3D565B6DA2CC3763D4E190FC7AB1ECC4C6C8B1EFA6DB30EDC5F4F333A8F52CFB55946162D5F6BA2947EAB4B68CAEA05D6E43AFC6C4B9B6E28A7631BA6536BEA18F5E55F6751CFC1DFD532EAF53D4F4E8C965D93956E374FFB2E3D6FA0B6DE4BA6617D6B654DCBE9D8B9FE9583755763D76ED70F169AFD962EC3A17D64EA76D96616774CCAB32714B5B9365143DEF66EFA1F6BC6637757B9BEF66CFA7FE8D65F45774BA0FD5DC3774F77AEEEA15E583F68792CF55D87898D9AF69C766E6E6BB1ECCAA719DFF69ABAECF53D3CA62E7F17071B3FA8E3518782E155F6D6D6E2073AF7866E69CAFD27A75D9FCDB6EB5FECFD13130C227AAF8CE4351B3E96329ADCE6B032C60225A2EA6DAC3B69F7B7F4F535AF757BBF495AF37EB34578DD5F3E8ADBE9D75E4581958101AD277358D6FEEED77B56C3BA8DF4F5CC91439ADAF1AFC8A716AAC06D2CA197595B71E8A2ADB4D753FD365967A6DF52EB3F4F6BEDB150FACEFF00B47557E735A5ACCA6B09932058C6B6BB5AD77F65088119505D3265004F47296AFD52FF00C55748FF00C34DFF00A97AC995ADF54BFF00155D23FF000D37FEA5EA4627DE13274C8AD524924929FFD0F544924925293A64E929F09FAEEC9FAE1D59DFF0CD1FF8152B01DCF9ADEFAF0E23EB87578ED734C7FD6A9584E1EE07B4C7CD3482B82C0CB869F246A6B0F3B5C246920F1F728B29703C401C9F056B1D90F1A73AFC934AE0750D9C3C5ADD680D6001C35811C15D0E374AC79935336EEDE06D1F4A3E971F4952E958FEF693C49D7E2B7EB969E254323AB6B18EAD46F49C7F730D4DDAFF0073840824FD29597D6FA5D54EE2D1B77341606E9A8F6ED11FC95D334FB493A203B1ABBF21B65A37067D169E1464906ED9B844855393D1FA1399563DD60DAF68690D8D23F74FEEFB5687D60E8746474DB2DA5B16D23D481DE072B535EDC4A85969D0763A1F81D101908365470C786A9F2EDC473F72D5FAA07FECAFA47FE1A67E47ACFCBA855977560406BDC00F20568FD516FF00D95F473FF7699FF52F5781D9CC23523B3EF6993A6450A49249253FFFD1F544924925293A64E929F05FAF263EBA756F036B47FE05542C62241EFBB50B63EBE83FF3C7AAB876B87FE7AA56383EE1E1FEBEE40AE4AE2E207808251702C7D9696BB9747E2546BA9CF7348D7C63556FA7623EBCA24F0D2224729922297C626DE9B0A88EC0761F2F6AD4656EDBA842C0A9A183C5690600DD79EEA06E44506ABC32B682F78682A01F48FA37327CCA3BA9A277D906741BB80AA64D9D348F4CBEA6DA4C0687B43893DB6EE4C902BC16C8C80E86348772496F1082FCEC4F53F4B686469E253E3E2399510C044F8F654EDCBE97D3AD1F6C7B2BBEC05CDDE1C4C03B67DAD7353575E9ABC8F5EA5B5758CAD9AD56BCDB518896BFDDFF00548FF545B1F5ABA47FE1A67FD4BD5BFADF6D195660E650439B657657BDA080763B737E906FD1F5551FAA766EFADDD1C0ED94CFC8E57212B01CDC91A9C878BEF8993A65231A92492494FF00FFD2F544924925293A64E929F07FAF4377D6EEADFF001EDFFCF54ACEE8F875E7754A31ED1348DCFB478B180D85BFDA5A5F5E04FD6DEAE3FE1C7FE7AA5677D5BCAAF1FAC62BEC23D379752F9FF84696367FB4992D8D2F85710BEEF656F4FC73B5B93815D41803BD7C56ECF4C1FA2DBC7E77F2DC8195D2D941DF4925BCEBE20AD5B711F71BADCCB36E331D3B0BA1A770F6EF8F759FB9B10290CB2921ED2DD7DAC773B7F35D0AB027BB7A518F64BD39C7609E56A31C0F3ACAC9A5BE9FB55EA9F1051486593D2B032B5C8C7AEE3DB7B7700818DF57FA7E3DA2DAF1A8AC8320B2B00C8F376E5A55BC10028DD76D6B9BE4A39DD32C00ECCBD4AC360113DD0ACC7ADC009DBBF50476542C3D4FD3230D958B5D1165B25AD13EF3B1BF49C9E9ABABB2E0E7BD9E8B87BE352E70FE49FA09A9156D6FAE5D2DD6FD5E7E4077AAFC1B1B7B4F70C77E86F1FE6B98FF00FADAE33EA79FFB2EE91FF8699F91CBD22EB59753762BC82CBEB7D4E1C887B4B3FEFCBCD7EA583FF3ABA313CFDA980FDCE56701B15D9A5CDC6A57FBCFD04A2A4A2AC351749249253FFFD3F544924925293A64E929F06FAF13FF003BFABFFC70FF00CF74AC0C5A9F6BEC63352C63AC81FF000637BB5FEAAE8BEB9D2FBFEB97576B4682E6973DDA35A3D3AB955FA1E1D15F556398F3634D193EA480266B737D8D4384D13D14271E211BF53B9D07EB6E364D2DC7EA4F155EC01A2D78FD1D9FBAE738FF00376AE86C6B1CCF546C1A4EF90411CFD29DABCE31F01ECC9BF19F040D58EEC44E85AB5717A65C5A186D2299135C983FC90D55E7117A3771E43C3A8D5E9F7B79699F056B1DEC70826216457B9839D111993E9EB29ACA24EDFA80409F821DF6082663B954075105B13D92AAD16BE5CE803523C5327AE8BC11BB62EEA36D5596D741B081A1710D6FDFAB952C6EABD6ACBC0BE8AABA5C61D1634B88EDB55CB286E437520055D9D3E8C571BDD74EC05D06001085689120CF2F26BC5C5BF2DE76BA8ADCE209FCE03D8CFF003F6AE2FEA5B09FAD5D1FCB25B27E4E5B3D55CECF2EA812DA9C7D4DAE05A5DFE8DFEF8DCCFCF46FAA5D2998FD7B02C277385E3F2394D8FD3E64B5739E33A6D10FB028A928AB2D35D2492494FF00FFD4F544924925293A64E929F1DFACEDFF00B24EB193635CDC4A725AD73F69D9EA3AAA6373FF003ACFE42CBC7E8D93D7F2ECCBC1B5B895E3868B6F735CC68304CB760DDF41BFA47ADEFF0018DFF8A0C4FA3FCE5DFCC7F52BFE77FEEF7FEEB7A6B56AFF00C478FE6FFA3B3E87D1FA2CFE95FCBFFB91FC8F553A5C7C236A598FDBE296F77ABCF3306FFB0FA79370BEBACFACCC86506BB1A5C1BFA11BEE6EF764B5F47E83D1DFBEEA7F3DEA66814BFD116B4E4080EC7782CB8388935FB3D6ABD467F5DEB62EFE9479FE7EFF00E6B8E723FE4EFF00CD97FA1FF805CBFE60FA1F487F3BFCDFD21FCEFF0027FD2AAD2E2BE8DE87B75BCFFABC207ED7571DCEC88143D8E71240638ED7123E9358EFE6EC43C86E5D409B68786FEF012DFF0039B2B773BF9ABBFE4BFE6AAFE6BF9CE7FC07F23FEE27F2D407F363E9FD11F4BE97F6FF00EFC992ABD131E2AD5E6DB90D2ED343DC23D7925BA83007753EABF4FF00C17CBE925D17FE50C4FA1FCF33F9CFA3CFFAEC4CD1935AD1D0651955E39C9CEB3EC58AD05CE7B9A5D66D1F9C31D9EFDBBBD9FA445E82FC6EA37BC574DB752DDAD7E45CE643647AAE2719BBBE9D6DD8CFD25B6FFA5F4D56FADDFD1B2BF9FF00E98DFA7C7D13FCFF00FC0FFDC7FF0087F595CFA93FF22BB8FA777D1E7E8D7FCE7FDF7FE0D3870AD971D6BB3A9D4F0CE660D95B768B1CC2F617090D73C86D6E688FE73D3DFE9FFC26C5CEF4CCAA717EBBD5D2DAC706D394C635D21DA966F732CDA7F95F4D7517FF003C3FA5FD3ABF98FE6F86FD2FE4FF00DFD55BBFF141D2BFA1FF0049FF000BFD33E8DBF4BFF44A78DC5B19D8D767B851524CAC3554924924A7FFD9FFED160050686F746F73686F7020332E30003842494D0425000000000010000000000000000000000000000000003842494D043A0000000000D7000000100000000100000000000B7072696E744F7574707574000000050000000050737453626F6F6C0100000000496E7465656E756D00000000496E746500000000496D67200000000F7072696E745369787465656E426974626F6F6C000000000B7072696E7465724E616D65544558540000000100000000000F7072696E7450726F6F6653657475704F626A6300000005682168378BBE7F6E00000000000A70726F6F6653657475700000000100000000426C746E656E756D0000000C6275696C74696E50726F6F660000000970726F6F66434D594B003842494D043B00000000022D00000010000000010000000000127072696E744F75747075744F7074696F6E7300000017000000004370746E626F6F6C0000000000436C6272626F6F6C00000000005267734D626F6F6C000000000043726E43626F6F6C0000000000436E7443626F6F6C00000000004C626C73626F6F6C00000000004E677476626F6F6C0000000000456D6C44626F6F6C0000000000496E7472626F6F6C000000000042636B674F626A630000000100000000000052474243000000030000000052642020646F7562406FE000000000000000000047726E20646F7562406FE0000000000000000000426C2020646F7562406FE000000000000000000042726454556E744623526C74000000000000000000000000426C6420556E744623526C7400000000000000000000000052736C74556E74462350786C40520000000000000000000A766563746F7244617461626F6F6C010000000050675073656E756D00000000506750730000000050675043000000004C656674556E744623526C74000000000000000000000000546F7020556E744623526C7400000000000000000000000053636C20556E74462350726340590000000000000000001063726F705768656E5072696E74696E67626F6F6C000000000E63726F7052656374426F74746F6D6C6F6E67000000000000000C63726F70526563744C6566746C6F6E67000000000000000D63726F705265637452696768746C6F6E67000000000000000B63726F7052656374546F706C6F6E6700000000003842494D03ED000000000010004800000001000200480000000100023842494D042600000000000E000000000000000000003F8000003842494D040D0000000000040000005A3842494D04190000000000040000001E3842494D03F3000000000009000000000000000001003842494D271000000000000A000100000000000000023842494D03F5000000000048002F66660001006C66660006000000000001002F6666000100A1999A0006000000000001003200000001005A00000006000000000001003500000001002D000000060000000000013842494D03F80000000000700000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800003842494D040000000000000200093842494D040200000000001400000000000000000000000000000000000000003842494D043000000000000A010101010101010101013842494D042D00000000000600010000000A3842494D0408000000000010000000010000024000000240000000003842494D041E000000000004000000003842494D041A00000000033F000000060000000000000000000001C20000032000000005672A68079898002D0034000000010000000000000000000000000000000000000001000000000000000000000320000001C200000000000000000000000000000000010000000000000000000000000000000000000010000000010000000000006E756C6C0000000200000006626F756E64734F626A6300000001000000000000526374310000000400000000546F70206C6F6E6700000000000000004C6566746C6F6E67000000000000000042746F6D6C6F6E67000001C200000000526768746C6F6E670000032000000006736C69636573566C4C73000000014F626A6300000001000000000005736C6963650000001200000007736C69636549446C6F6E67000000000000000767726F757049446C6F6E6700000000000000066F726967696E656E756D0000000C45536C6963654F726967696E0000000D6175746F47656E6572617465640000000054797065656E756D0000000A45536C6963655479706500000000496D672000000006626F756E64734F626A6300000001000000000000526374310000000400000000546F70206C6F6E6700000000000000004C6566746C6F6E67000000000000000042746F6D6C6F6E67000001C200000000526768746C6F6E67000003200000000375726C54455854000000010000000000006E756C6C54455854000000010000000000004D7367655445585400000001000000000006616C74546167544558540000000100000000000E63656C6C54657874497348544D4C626F6F6C010000000863656C6C546578745445585400000001000000000009686F727A416C69676E656E756D0000000F45536C696365486F727A416C69676E0000000764656661756C740000000976657274416C69676E656E756D0000000F45536C69636556657274416C69676E0000000764656661756C740000000B6267436F6C6F7254797065656E756D0000001145536C6963654247436F6C6F7254797065000000004E6F6E6500000009746F704F75747365746C6F6E67000000000000000A6C6566744F75747365746C6F6E67000000000000000C626F74746F6D4F75747365746C6F6E67000000000000000B72696768744F75747365746C6F6E6700000000003842494D042800000000000C000000023FF00000000000003842494D041100000000000101003842494D04140000000000040000000A3842494D040C000000000CDC00000001000000A00000005A000001E00000A8C000000CC000180001FFD8FFED000C41646F62655F434D0002FFEE000E41646F626500648000000001FFDB0084000C08080809080C09090C110B0A0B11150F0C0C0F1518131315131318110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C010D0B0B0D0E0D100E0E10140E0E0E14140E0E0E0E14110C0C0C0C0C11110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC0001108005A00A003012200021101031101FFDD0004000AFFC4013F0000010501010101010100000000000000030001020405060708090A0B0100010501010101010100000000000000010002030405060708090A0B1000010401030204020507060805030C33010002110304211231054151611322718132061491A1B14223241552C16233347282D14307259253F0E1F163733516A2B283264493546445C2A3743617D255E265F2B384C3D375E3F3462794A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F637475767778797A7B7C7D7E7F711000202010204040304050607070605350100021103213112044151617122130532819114A1B14223C152D1F0332462E1728292435315637334F1250616A2B283072635C2D2449354A317644555367465E2F2B384C3D375E3F34694A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F62737475767778797A7B7C7FFDA000C03010002110311003F00F544924925293A64E929F18FADE19FF3ABAA48DC4DEDE49FF454ACD369FB3380796ED036EA74F26A9FD78C97B3EB8F55606C8F581FFC0A958D6E43DC037807B28C86505BB87D62EE9F91F68AEC7BEE6B5CC6BDC6480F11A4A1FA7D77AB07DD8F8F93934564FAAF631CEADB03D477AD77F315ED67BFF48F540820C1D7CD751F55053FB1FAB6EA8E4BFD3DA69AF09B90F26CBB0598CCFB45EFF45EFBED67E830BD1B1FFA1B327DFE95753D002D0644079DB3A5755AB26AC4B316DAF26FD68ADE20D83FE05C7F4767F61FF4FF0047FCE223BA3758AADF4DF876B2D01C4B4B44B431AFB2DF536BBF44E6574DCEDB6FBFF4562EC7AD136756E977D8DFB3D565B6DA2CC3763D4E190FC7AB1ECC4C6C8B1EFA6DB30EDC5F4F333A8F52CFB55946162D5F6BA2947EAB4B68CAEA05D6E43AFC6C4B9B6E28A7631BA6536BEA18F5E55F6751CFC1DFD532EAF53D4F4E8C965D93956E374FFB2E3D6FA0B6DE4BA6617D6B654DCBE9D8B9FE9583755763D76ED70F169AFD962EC3A17D64EA76D96616774CCAB32714B5B9365143DEF66EFA1F6BC6637757B9BEF66CFA7FE8D65F45774BA0FD5DC3774F77AEEEA15E583F68792CF55D87898D9AF69C766E6E6BB1ECCAA719DFF69ABAECF53D3CA62E7F17071B3FA8E3518782E155F6D6D6E2073AF7866E69CAFD27A75D9FCDB6EB5FECFD13130C227AAF8CE4351B3E96329ADCE6B032C60225A2EA6DAC3B69F7B7F4F535AF757BBF495AF37EB34578DD5F3E8ADBE9D75E4581958101AD277358D6FEEED77B56C3BA8DF4F5CC91439ADAF1AFC8A716AAC06D2CA197595B71E8A2ADB4D753FD365967A6DF52EB3F4F6BEDB150FACEFF00B47557E735A5ACCA6B09932058C6B6BB5AD77F65088119505D3265004F47296AFD52FF00C55748FF00C34DFF00A97AC995ADF54BFF00155D23FF000D37FEA5EA4627DE13274C8AD524924929FFD0F544924925293A64E929F09FAEEC9FAE1D59DFF0CD1FF8152B01DCF9ADEFAF0E23EB87578ED734C7FD6A9584E1EE07B4C7CD3482B82C0CB869F246A6B0F3B5C246920F1F728B29703C401C9F056B1D90F1A73AFC934AE0750D9C3C5ADD680D6001C35811C15D0E374AC79935336EEDE06D1F4A3E971F4952E958FEF693C49D7E2B7EB969E254323AB6B18EAD46F49C7F730D4DDAFF0073840824FD29597D6FA5D54EE2D1B77341606E9A8F6ED11FC95D334FB493A203B1ABBF21B65A37067D169E1464906ED9B844855393D1FA1399563DD60DAF68690D8D23F74FEEFB5687D60E8746474DB2DA5B16D23D481DE072B535EDC4A85969D0763A1F81D101908365470C786A9F2EDC473F72D5FAA07FECAFA47FE1A67E47ACFCBA855977560406BDC00F20568FD516FF00D95F473FF7699FF52F5781D9CC23523B3EF6993A6450A49249253FFFD1F544924925293A64E929F05FAF263EBA756F036B47FE05542C62241EFBB50B63EBE83FF3C7AAB876B87FE7AA56383EE1E1FEBEE40AE4AE2E207808251702C7D9696BB9747E2546BA9CF7348D7C63556FA7623EBCA24F0D2224729922297C626DE9B0A88EC0761F2F6AD4656EDBA842C0A9A183C5690600DD79EEA06E44506ABC32B682F78682A01F48FA37327CCA3BA9A277D906741BB80AA64D9D348F4CBEA6DA4C0687B43893DB6EE4C902BC16C8C80E86348772496F1082FCEC4F53F4B686469E253E3E2399510C044F8F654EDCBE97D3AD1F6C7B2BBEC05CDDE1C4C03B67DAD7353575E9ABC8F5EA5B5758CAD9AD56BCDB518896BFDDFF00548FF545B1F5ABA47FE1A67FD4BD5BFADF6D195660E650439B657657BDA080763B737E906FD1F5551FAA766EFADDD1C0ED94CFC8E57212B01CDC91A9C878BEF8993A65231A92492494FF00FFD2F544924925293A64E929F07FAF4377D6EEADFF001EDFFCF54ACEE8F875E7754A31ED1348DCFB478B180D85BFDA5A5F5E04FD6DEAE3FE1C7FE7AA5677D5BCAAF1FAC62BEC23D379752F9FF84696367FB4992D8D2F85710BEEF656F4FC73B5B93815D41803BD7C56ECF4C1FA2DBC7E77F2DC8195D2D941DF4925BCEBE20AD5B711F71BADCCB36E331D3B0BA1A770F6EF8F759FB9B10290CB2921ED2DD7DAC773B7F35D0AB027BB7A518F64BD39C7609E56A31C0F3ACAC9A5BE9FB55EA9F1051486593D2B032B5C8C7AEE3DB7B7700818DF57FA7E3DA2DAF1A8AC8320B2B00C8F376E5A55BC10028DD76D6B9BE4A39DD32C00ECCBD4AC360113DD0ACC7ADC009DBBF50476542C3D4FD3230D958B5D1165B25AD13EF3B1BF49C9E9ABABB2E0E7BD9E8B87BE352E70FE49FA09A9156D6FAE5D2DD6FD5E7E4077AAFC1B1B7B4F70C77E86F1FE6B98FF00FADAE33EA79FFB2EE91FF8699F91CBD22EB59753762BC82CBEB7D4E1C887B4B3FEFCBCD7EA583FF3ABA313CFDA980FDCE56701B15D9A5CDC6A57FBCFD04A2A4A2AC351749249253FFFD3F544924925293A64E929F06FAF13FF003BFABFFC70FF00CF74AC0C5A9F6BEC63352C63AC81FF000637BB5FEAAE8BEB9D2FBFEB97576B4682E6973DDA35A3D3AB955FA1E1D15F556398F3634D193EA480266B737D8D4384D13D14271E211BF53B9D07EB6E364D2DC7EA4F155EC01A2D78FD1D9FBAE738FF00376AE86C6B1CCF546C1A4EF90411CFD29DABCE31F01ECC9BF19F040D58EEC44E85AB5717A65C5A186D2299135C983FC90D55E7117A3771E43C3A8D5E9F7B79699F056B1DEC70826216457B9839D111993E9EB29ACA24EDFA80409F821DF6082663B954075105B13D92AAD16BE5CE803523C5327AE8BC11BB62EEA36D5596D741B081A1710D6FDFAB952C6EABD6ACBC0BE8AABA5C61D1634B88EDB55CB286E437520055D9D3E8C571BDD74EC05D06001085689120CF2F26BC5C5BF2DE76BA8ADCE209FCE03D8CFF003F6AE2FEA5B09FAD5D1FCB25B27E4E5B3D55CECF2EA812DA9C7D4DAE05A5DFE8DFEF8DCCFCF46FAA5D2998FD7B02C277385E3F2394D8FD3E64B5739E33A6D10FB028A928AB2D35D2492494FF00FFD4F544924925293A64E929F1DFACEDFF00B24EB193635CDC4A725AD73F69D9EA3AAA6373FF003ACFE42CBC7E8D93D7F2ECCBC1B5B895E3868B6F735CC68304CB760DDF41BFA47ADEFF0018DFF8A0C4FA3FCE5DFCC7F52BFE77FEEF7FEEB7A6B56AFF00C478FE6FFA3B3E87D1FA2CFE95FCBFFB91FC8F553A5C7C236A598FDBE296F77ABCF3306FFB0FA79370BEBACFACCC86506BB1A5C1BFA11BEE6EF764B5F47E83D1DFBEEA7F3DEA66814BFD116B4E4080EC7782CB8388935FB3D6ABD467F5DEB62EFE9479FE7EFF00E6B8E723FE4EFF00CD97FA1FF805CBFE60FA1F487F3BFCDFD21FCEFF0027FD2AAD2E2BE8DE87B75BCFFABC207ED7571DCEC88143D8E71240638ED7123E9358EFE6EC43C86E5D409B68786FEF012DFF0039B2B773BF9ABBFE4BFE6AAFE6BF9CE7FC07F23FEE27F2D407F363E9FD11F4BE97F6FF00EFC992ABD131E2AD5E6DB90D2ED343DC23D7925BA83007753EABF4FF00C17CBE925D17FE50C4FA1FCF33F9CFA3CFFAEC4CD1935AD1D0651955E39C9CEB3EC58AD05CE7B9A5D66D1F9C31D9EFDBBBD9FA445E82FC6EA37BC574DB752DDAD7E45CE643647AAE2719BBBE9D6DD8CFD25B6FFA5F4D56FADDFD1B2BF9FF00E98DFA7C7D13FCFF00FC0FFDC7FF0087F595CFA93FF22BB8FA777D1E7E8D7FCE7FDF7FE0D3870AD971D6BB3A9D4F0CE660D95B768B1CC2F617090D73C86D6E688FE73D3DFE9FFC26C5CEF4CCAA717EBBD5D2DAC706D394C635D21DA966F732CDA7F95F4D7517FF003C3FA5FD3ABF98FE6F86FD2FE4FF00DFD55BBFF141D2BFA1FF0049FF000BFD33E8DBF4BFF44A78DC5B19D8D767B851524CAC3554924924A7FFD93842494D042100000000005700000001010000000F00410064006F00620065002000500068006F0074006F00730068006F00700000001400410064006F00620065002000500068006F0074006F00730068006F00700020003200300032003000000001003842494D04060000000000070006000000010100FFE10DD2687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F003C3F787061636B657420626567696E3D22EFBBBF222069643D2257354D304D7043656869487A7265537A4E54637A6B633964223F3E203C783A786D706D65746120786D6C6E733A783D2261646F62653A6E733A6D6574612F2220783A786D70746B3D2241646F626520584D5020436F726520352E362D633134382037392E3136343033362C20323031392F30382F31332D30313A30363A35372020202020202020223E203C7264663A52444620786D6C6E733A7264663D22687474703A2F2F7777772E77332E6F72672F313939392F30322F32322D7264662D73796E7461782D6E7323223E203C7264663A4465736372697074696F6E207264663A61626F75743D222220786D6C6E733A786D703D22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F2220786D6C6E733A786D704D4D3D22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F6D6D2F2220786D6C6E733A73744576743D22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F73547970652F5265736F757263654576656E74232220786D6C6E733A64633D22687474703A2F2F7075726C2E6F72672F64632F656C656D656E74732F312E312F2220786D6C6E733A70686F746F73686F703D22687474703A2F2F6E732E61646F62652E636F6D2F70686F746F73686F702F312E302F2220786D703A43726561746F72546F6F6C3D2241646F62652050686F746F73686F702032312E30202857696E646F7773292220786D703A437265617465446174653D22323032302D30342D30325431373A32343A31352B30383A30302220786D703A4D65746164617461446174653D22323032302D30342D30325431373A32343A31352B30383A30302220786D703A4D6F64696679446174653D22323032302D30342D30325431373A32343A31352B30383A30302220786D704D4D3A496E7374616E636549443D22786D702E6969643A34656266653730652D643236652D623834312D613962612D3232623834663439646366312220786D704D4D3A446F63756D656E7449443D2261646F62653A646F6369643A70686F746F73686F703A64633333616566382D343164372D343134372D626566652D3133623366363839326632642220786D704D4D3A4F726967696E616C446F63756D656E7449443D22786D702E6469643A39633734393932312D633861652D393634382D613065372D613762366131393131336266222064633A666F726D61743D22696D6167652F6A706567222070686F746F73686F703A436F6C6F724D6F64653D2233223E203C786D704D4D3A486973746F72793E203C7264663A5365713E203C7264663A6C692073744576743A616374696F6E3D2263726561746564222073744576743A696E7374616E636549443D22786D702E6969643A39633734393932312D633861652D393634382D613065372D613762366131393131336266222073744576743A7768656E3D22323032302D30342D30325431373A32343A31352B30383A3030222073744576743A736F6674776172654167656E743D2241646F62652050686F746F73686F702032312E30202857696E646F777329222F3E203C7264663A6C692073744576743A616374696F6E3D227361766564222073744576743A696E7374616E636549443D22786D702E6969643A34656266653730652D643236652D623834312D613962612D323262383466343964636631222073744576743A7768656E3D22323032302D30342D30325431373A32343A31352B30383A3030222073744576743A736F6674776172654167656E743D2241646F62652050686F746F73686F702032312E30202857696E646F777329222073744576743A6368616E6765643D222F222F3E203C2F7264663A5365713E203C2F786D704D4D3A486973746F72793E203C2F7264663A4465736372697074696F6E3E203C2F7264663A5244463E203C2F783A786D706D6574613E2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203C3F787061636B657420656E643D2277223F3EFFEE000E41646F626500644000000001FFDB0084000202020202020202020203020202030403020203040504040404040506050505050505060607070807070609090A0A09090C0C0C0C0C0C0C0C0C0C0C0C0C0C0C01030303050405090606090D0A090A0D0F0E0E0E0E0F0F0C0C0C0C0C0F0F0C0C0C0C0C0C0F0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC000110801C2032003011100021101031101FFDD00040064FFC401A20000000701010101010000000000000000040503020601000708090A0B0100020203010101010100000000000000010002030405060708090A0B1000020103030204020607030402060273010203110400052112314151061361227181143291A10715B14223C152D1E1331662F0247282F12543345392A2B26373C235442793A3B33617546474C3D2E2082683090A181984944546A4B456D355281AF2E3F3C4D4E4F465758595A5B5C5D5E5F566768696A6B6C6D6E6F637475767778797A7B7C7D7E7F738485868788898A8B8C8D8E8F82939495969798999A9B9C9D9E9F92A3A4A5A6A7A8A9AAABACADAEAFA110002020102030505040506040803036D0100021103042112314105511361220671819132A1B1F014C1D1E1234215526272F1332434438216925325A263B2C20773D235E2448317549308090A18192636451A2764745537F2A3B3C32829D3E3F38494A4B4C4D4E4F465758595A5B5C5D5E5F5465666768696A6B6C6D6E6F6475767778797A7B7C7D7E7F738485868788898A8B8C8D8E8F839495969798999A9B9C9D9E9F92A3A4A5A6A7A8A9AAABACADAEAFAFFDA000C03010002110311003F00FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8E35D5BCC737C2D3C9BFBFF666A389DAABAAEB92D09964A9F7C78D5CFA56B736CD2C94EF538F1AA82F96F534916762EE10D78F8E112B54DEDED35BD3EEA09B45B992D25620CA14D3265239BD8EDBCD5E6BB286DA2B8BE79A375FDFA924D4D3205B0232DB5BD4EF199998E5459C58F6A9ABEA16F71CB9114EF9125B120B8F3A5CDA026573418AA583F34D616346AB5298A09A424FF98F2DD0F80D2BE18691C4925DEBD7DAB05B61522434CAF2C51C6FBA7FE7122F60D17CBBA84175B333D4D7E6329E172714DF5E6AFE7ED22CE1043865814FAAB5F118F0B9A26F8C7F30BF306DEE7549AE2C8889391A95FA71E1499BC4758F3D5037A77ACDEA0AB47CBAE4E30713366E12F35D43F302DA14901531CE7ECCA7C72C11718E7796EAFE6ED57516663AA12A0515431E99608B8F2D430D9A677E524F3FAA4F5A9AE5822D0750978B98B8D140D8E4C63B63F9843BDC13F64532431B1F1AD0C5DB916EE72D8E362656ACB7457BE4BC3606368A13F2553F7E5321BB5F0EEDD0B6F916C0141ADD0558F5385BE2690CC5536E98B682985A4527312ABF1A6F5C9F44527A6F5255114979C48EC4E424136EE70A2D2593E54CA2516432D265A76A6F6B5FAACACBE041E995F0B319DE9DE5BFCCBF37E8C54E9FE619A075E8A1C8FE18699F88FA4FC9FF00F395FE6ED08C0BACAB6A71A302D2BFC5B0F98C890D8323EA6F247FCE5769BE63B8449ECD62F50F5D875AE564360C8FA2EC3CC5A46B32DADF898514F2F4EBD72B21BF14F74FF4D7626F1F53F8A192BF5207B785320439FC4115A4693711DBDF5F5F12CE8C0D913D857B640865CD910BAFAE46A41FEE46F95909115A6CE4BB46B98FAC20D7214C643662170CF7323C858AFA27E2FA314452DD56DE2D4F4C9F94A6B1827AF862DF163FA65C8FD1C2C11EADCA80D716F09EC33DE42A6D1472A2824F861069AB502C331D3BCB3E55F3C412795FCE9A746D05DC6CB0CEEBBD4A9A6FF3399784BA4D445F8C9FF3931F9253FE507E61EA96B145E9F97B57919B426A6C402C4D3AF866C605D3E41BBE5BAFA5230FC32DB583B90724F865D8B930CC5BCB5A16375C55AC5578E98AB78AA8BFDA38AAA2F7C55A3D7156B155EADD315560D8ABFADEFF009C6EFF00D677FC84FF00CD73E56FFBA45AE5A39349E6F68C28762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF917B754FE45FBB34FC2ED532A855A803C71E1551370C0E3C2AADF5A26361ED863155D0DC52840DFC72C2A39A6893B3D0124F8D7205B423E172F227A6C401D86D9516714AB5F0D52FD80A139193607916A21A49641526A722125825DE9F335C1F4C75ED96C434929D699A6C96C434DD0F639708B519B2F86D62F8446DC7EB1F0971B52990CB0D9871BE87FCB0F341D3236D0D5B8C6E3E2981A1FBF28E17231CD35F3479E9A06B8D356666080FC7CAA4D6BDF0F0B9919BE7FD5BCC7246F2B3BB10C4EC4E3C2994DE7979AD87666AFDAE9921171331B2C0F56D4F996EFF003CB045C5931D8EF2A483F765822E3C93012875EBB1C988B4108794A034D865910C68BA38D5CFDACB0048895EF171AE4C36479A0A470A7A6580362A4738A2F877CC49C775A4724E0D32149016B354935AD71A4A1DD6BD77C7853689173C4000D282981B835EBA75A0AF8D30316FEB00F53CBE780C5810510B7617A1A7CB21C2C68DA6FA7EA011872EBE39021BC16463520EBC791A11BF8640867128FB5D46EECDD26B2B9642A6A021A6408660BEA2FCB2FCFDD73CBED676DA8D64D3C301752BEE42FCCE56437639517E927903F33746F37E9D6F35B5CC6CC69C5188FE272B21CE195EF305FA4D63FBD6531AAF4047864086E8E4B486CAF94DD3C40F0427627A64086D8CD96A5DDCDA59B81C5A07EAC065720CA52B09035922C373792515250683B641116276C6D6D61BBE6F53302029E9BE2DF179F4113DAEA124A8F54249515DB16F0CA34926E2FE49AEE5291C6018F7A54F860465161EA22FDB4CB9B1BE11ABC42946201CC9C45D46A60F8AFF00E73E6C935ED07CAFE6151B69FEA733E1CB966C6127479A3BBF21B5802DEF21BB07F772A9A2F6CB896A8B7129F492423FBC15197E12C7338F7CBDA14FB8C555475C55CDD7156B156A98AB78AA8B1353BE2AB6A7C7155E3156EA7C7157F5CBFF0038D9FF00ACEBF907FF009AE3CABFF748B5CB472693CDED5850EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF91688D3E8CD770BB55732D29BED5C3C2AA125C2826831E155317408208D8E02155E1B85AF4C8951CD358A7AEC0640B684DB436F56492BD16B9596615B5884496D2F7DF6CAE41B2245BC82F22E370C08A6F82213221279612262C82A46E7E599100E3CCAE5B8127C2F41C732621C6949524D42354F4A234917A1AE47306A2535D3BCDEBA4425797FA536DCC1FEDCC76FC4509A9F9B4384B899EB24952C6B5FE38D39824C1F50F30477E480698D24CAD8EDC5C8D85474E95C9C766360A4574C1CF635E9EF930D6404B3D13EA5541F0229BD72C01A26023D4B200A41E5D969BFDD9688B498A9C8A643CC30A7422BD32241B4C601C9CD0D0D4622D78559E6DA85BE9CB031A4148391EBD7EFC95A3758ABD0572A31B6611B1823A838384A95415A9D8EDDA98F09415600537EFDB070962DFD589EDEE477CA8B782D7D5877207CCE16629B36E3C4570F267C214BD23C80EE4F4C2427802690DB9461BF5CA484009D470F214276A6FE232B21980A5CBD19290CC580EB4DE83DF2B660332D27535212DEE8FFA3C9B487DB20591D9EA7E53F3E7983CB371CF47BA75B588D42722361ED5CACD22322FADFF002DBFE729E486FECA3F324A5B4F43C6EB7AD6B403F6B2B2DF0917D9AFF997E53F30E976D75A2CF1C72CA0114615DFE9C8172A322F48F2E1BBD5F477844BCCF02EA6BE032B937028CD596783CBD6EAE486690291DF2A6C890F3BF35C434FB6B478C9AC8A0B530D37C4879A9BFB99670B1D41AED5EFF2C0DE0B228A4BA8602D3935603D3A78FD1912CE554CFF004DBBBDD461B5B6949F4D3BB6DFAF32313ABD48BE4F08FF009CBD8E0D47F29B5582DF79B4BF4C311B9F889E99B081D9D0E789B7E39EB9664FE87818D4B86E5F7E5A1C5029425A239806C20F8732B0726190DA1CF7CC86A531D462AAABD7157375C55AC55D8ABB15506FB47156B155E3A0C55BC55FD72FFCE367FEB3AFE417FE6B8F2AFF00DD22D72D1C9A4F37B56143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFF00FFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF91745DBAE6B5DAA0270038DFBF4C557A007DF1B569937DBBE025217C042073DC654C931B4B9E50CA83AF638153ED2A660AE69F7614A367BA58ED6466DC57BE210497946B3748D33508EB930030322C65AED519B7A6D930C0A4B25EA976A1A6581AD2C9AE872FB4704D38D012DCEFD72B6D40CD37A94DCD07BE2CACA024DF7F0C56CA8140C8FF00BB32CBD8569F0F7CAB27369C922D18005B7B89916CE2B561340E5BAB2F4EB92C64B5D943DB5FDB89F5295B5385DAF559A094950D1CE7B04AEF99B8C2D95595FCC31E95FA5A2F2DEAB79369A4C971E6692CA54B64886DBBF1E1B7BE654698F1747A07953F25FF00357CD5E47B8FCC3F2CF921F5AF26DAC924BAB6B8AEDF686F20A01B713919D1482F2ED2EE9AFF0053F4EEAE9567B5774B4D35A8AB291B1591BF678F6AF5C85320537786D4C727D62D7D2746ACB0024AAB57E1A37BE16C1BA0A592333C1F5AB6956F6452D6B040A646283AFC2B8B2D91111FAC46DF5AD0357B7907FBCF3A594ADC97BBF4ED8D30F103505E5DC61A19341D524914F1B6B916725593FDF9C698D3094C2BC17577C9A69749D5DD17E004584A7930F1DB6C698198519259239ED629ECE48EE637F5EE6CEF94DAB7A0DF64D1E87010C6D165EFA492CFF437FCEC1AD7982636B06996E39B2843F0A8095272AE0B6424C9F59F2D79A3CADA9E9BA7F9E7C8D3686FAFA7FA33DD1786BC056A3985C85331262333C3A54F76F7BC648AB4B360D5AF80195D16C12513E60D2CD80917D45D661916331703F0A31DDBE818981642749BC3AE6906375955CC900F87512A429FE19594FE60222D35781D3EB68CAD1034926660015F9572B3129FCC840C7E65D1D24BEB4B581E196ED089EE402E021EE32070CAAD7F3211EFE61B4168AB6D299934E50F75301F184E8085EF90F02523417F34193E95ABBEB4AB7361A9FA4427EFD88A72403A53C6980E92498EA8047DBF9C7CB567070FAFC915C4CE561708580643F1127B54E0FCA49C886AA2CCF4DFCE48343113DBF98273C2842D0D36FA71FCA172A3AB8BE9FF00CBBFF9CDB4D1FD0B6D435C905A2AD18F0A9FF896425A434CA7AC14FAC348FF009CD2FC9CD42C53F4D798CACC46EA631D7FE0B043465C6FCE29EAFF00F393FF00939ABD91B3B0F33299266A44F22AA8527BD4B659F932D90D6EEC1B4AFF009C92FC948F548E1D6BCDBEB7D5488E20B10E0B20DAA581E9954B4843B0C5AC0FA4F4CF3168FABC116ADA4C424D0EE943D9DEF51231DCD3B653E018736EC9A8E21B326B4D5ED26FDD28E20EDB6D8D535DDBC63FE7231043F97F76F6EA5FD68D8B83BD681BAE646271B34453F1A350B93EBFD65BAC723023C3E239B1C54E8B53B294BF1309FB5C0E4332C38504337F1C2CD662AAABFC3155F8ABB15587AE2AD62AEE15DEB8ABB80F1C55DD36F0C55D8ABFAE5FF9C6CFFD675FC82FFCD71E55FF00BA45AE5A39349E6F6AC28762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF90949CD3DF35AED52FB89CF2AF7C55B86E091D7E78AA21AE147461F7E02A39A924D50F534F0CA99A334E620B96D86F4AE054F2D6EFD2473D3C3154A750D59DAD254AF1F88648734179BDEDD33C8C49D8E4C302C72E27356DE9930C6492F362E77DB26D6A44FC66A69B7538CD38D42523B1FA72B6E435483D7156B155F63A7CDACEBBE57F2EC774BA6C5E65D66CF4A9755A8E708BA9021600F600D6B93C70123BB466354FD9DB4FF9F49FE51E9B35B5DF9C7FE725E596D2C19259EC276B48A07400311EA48EBB7C865808ADA838F7BBEA8D27FE704FFE70B7F2CBC93ADFE62EB5E4ED2FCFDA1F97AD1F53B8D68CE66458A1AB332885B893F4E5D007DEA4EFDC5F217E7E7FCFC07FE716353FC95F327E49FE4E7952E35AD3FCD1A73D92DB5DE9DF548AD1A421898DE956229B1CBE2A0D9B7BB7FCFBAED6DAEFFE7DEDABC11C097125CEA9E608E58A641C5CAD0F02C474D8EF91931E27CE5FF386BF901FF38ADFF3939F947F9A5E46D4AC20D13F3DE4D4AFA3D5B502693D8AA5C31B692CB911EA29FDBA74FA3229E2A7E6C7E75FE48FE62FE43F9FB55FCAFF003FD93DAC31B9934FF32509B7B8B752CD0949A9C492805403D7037465694FE5779EB49FCAEFCC9F267E62EBFA147E6CF2EE9F1490DDE9774291BF26A6F8B2E2EAFDABFC97FF009CC4FCA9FCF4F37697E48F21FF00CE3B5ADDCD7052DF51BD5B3AD9D9DB103D4066084034DC570B1E305EEDFF00393BF9B5F917FF0038B736896771F9223CD9E6AD663E3A258D869FF588BEAA48520BA275A9D862C38DF24AFF00CFC07F285102BFFCE28EA5A6B472F2BE54D1A4243F7A829818193E5CF2EEB1F945FF0039A1FF0039BFA5493F92469FE42D5B4CB5D2E7D04C660314D0D43332281435070B0326BF337F28BCA9F919FF003F16F267E5FF00E5ED82683A169F269B7D6F08FDE822E95988A3D41D80C89E4CE25EFF00FF003F7E5B6B6D7BF216FAED56EE1B8866F5D446B172F848FD803B9CAEB9B3069F945A4FF8720F3D7902F7CCBA4407CA305FC0757B66735684CAA5B90FF572BE8DAFDCAB4F397FCFACEEF5A9560F2669B26AD218AD25536AE12579540001F528C7B5460970F716278AB9BE8CF3FF00E497FCE0E7E5E7E5C69FE7AF387E5468DA6F90EF8441677470419BEC02858124FCF23C239D7E3E4E29B7CC49E6EFF9F50CE925A2F93B4A8B4E1113C84120527C38FAB5AFD3938C224F543E93FC95FC95FF009C13FCF1F2BEA3E60FCB0FCA9D1F52D0EC646D3EEEE9E378D870A93D5DBC32D18815DDE0B79E65FF009F5A683AA6B3A4DE792B4AB0D6B4B9A7D36FE25B7763CE0AAB0E5EA53AFB75C9C71041BA7E10595CE9716ADE686D3E258B449B5DD4A4D123AD185A191BD15A7871A65BC23AB5926F67DC5FF3827AD7FCE27E9FA779F3FE865346B2D425D52E61FF000CC570865F4A341FBDA85652A49A1EB8384364645FA0ADE64FF9F54F0E2FE56D1154776B7907FCCEC7843689C97DBEBFFF003EAD240B6F2B684EC4D14185C024F6AFAD80C026539232EB55FF009F5B229F5BCA9A1AB20ABA7A7202A3DFF7DB618E30D5C459F7E5FF0090FF00E7DF7F9C370BA2790BF2B6D35DE5F66FADEDE6FAB236FD67595941DBBE1300CE32931BFF009CC4FF009C6AFF009C4DFC9FFF009C73FCD3D534FF0021E85E59F375CE85247E5495A765BA92F792F130866DD857B0CA66221CAC5399237D9E1DFF0038E06E1FF24BC8D6FA85CDC48EB67037D52742BC18C62AC09DCD7357A900BB9C123D5F4B695A658ED26C48DC819824539F1281FCC5F2E5A79A3C9FAA5A7107EAF03F15EE7E16E99386CD59793F083CE5A73699AFEB5A5BC6D088677081C52B466E999F8CBA3D4C5283B5B59A9054AA1A5733226C38110866FE39364B315545FE18AAFC55BC554CF5C55D8AB5CE9B53157733E18AB7B9DE9D715750F862AFEB97FE71B3FF0059D7F20FFF0035C7957FEE916B968E4D279BDAB0A1D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8EE4B96A57F0CD6BB54BEE6E4F2FD78AAE86E01502BB62AB1E41CC1F7C05211724A0FA7E1953247B5D0458CD771D302AC9756E0B4AE2AC5EFF0053332B28F1E9921CD058C5DDC9A1C98605209A72C684F7DB2618C96890713936B5093F7854786F8CD38DBF476F6CADB96AC542DF462ABFD318AAA691E55F3379E7CD9A1793FC99650DD799AF254BAD31DD8AC8AF1B7C3C69EF976201C4CE5F60F993FE70D3FE739BCCF6ED6FE636BAD4D0A7A0BA64974C55430141414FC72EA01A2CBF643F2F3F273CFDA2FF00CE0F79D3F27751D3560F3D6A7E56BAD3AD2D236678CCF22154058926A49DF7C9C764136FC52D1FFE701FFE72CB48B23A6C3E43D327E0C6E11A590B4CA587B532C0520D3F6A7FE70B3F25FF00307F2DBFE70CB52FCA9F37592691E78BBBFD6E6B3B02DD56EF8984D76EA01C892C0C9F94DF94FF00F389FF00F3995F93FF0099EDF98DE46F2ADA43ACF97B58B9B9BFF5A5741716D34DCA84568C0AD6981417AE7FCE7FAFFCE51FE6E79534AF35F9F3F2AB43D1FF002EBCAD1B32EA1612BCB7E1CA1F51AE118D5072AD074C590953F38FFE71DF41F23F993F327C9F61F9CBA9DDE9DF96FAE3B23DCB201688E1C22891CF40316CE27EE04FFF003953FF0038D3FF0038DF77A57E43FF00CE2AF97F45D77F31FCC37D67656F7518A6957724DF097B9BD427E3626800C58195B18FF9CAAFF9CCDFCF4FC80D7BCAFA67E687FCE3DF94F59BED56CC3691AFD83497715B5C16A2A4323861EA03B85271636FABFF002FFF0038507E40CFF9F1F9F7E46D13C8B05BDA9B9D3F4E4445935042AC10CEACBB48C69B0C5812FC5DFF009C15D5A2D67FE7381F58D3AD6DADAD7CCBAE5CEAD690D688B677723CB1C6941D830DB16264F56FF9CB6980FF009FA17962F26790A01A3C56CD08AFC512BA95FA2BBE25B81671FF003F8D92E6E35DFC86B458964B3FAB48F6A53790B537046408641F93B24769776F1C5A8D8BDC5E5005AA9AD32A21B8153B8D1ACADF59F24FD5E27B49CEB5640015A8FDE83B570034CB9BF793FE7E412ABFFCE01F962299A79644D5F458DA471425D6A77F6DF2C8C6A9818EE5F822DA1697A868B63A7E97A1869E3B413DF5D92F5F5075EF84367007EF97FCFA0C347FF38C9E7F8608E44B9FF10DDC61C0FDA11353EECB049AB806DF17E12F9AB47B1BAFCC0FCCEB8BA8DA7BDFF156A70A3396A02262C6BBF8938253D9B3806EB5E3B64FAAB416104933FC0DC4D7EC8CC7F14B1F0829A683A75C3FD616C230D727F7CA091B8F91C7C52918C27967E5AF2D4C04779A2A4DE22AFF00D71F14A784226DB4BF2FEA7AAD9F93BCA1F97771E62F34DF4C9FA3B4FB38E4964E75A091954D4283D4E4A323252007E937E427FCFA63CE7E6BD624F38FFCE42EA16DE50D126845CC1A268937AB24A08A94B9E6D5414EA2B970B69343DEFAFBF303FE72AFFE716BFE708FCA571F975F92DA2D9EB9E72107A561A1E8CA67B496FC01F15E4C0B1526A6B4C8CA7B338C093BF27E59F9CF5FFCCAFF009C81D6EDBF31FF003BB537D6EE21B8F5743FCBD8E42FA6D9467EC96E276A53BE60E6CF5C9D8E0C3B6E1F657E5BFE6369D731586871B246D63122D22A1B745A50468C07ECE61711939F5C2FA534DBE8E15925460EAE054D72328B7C24CC22D5AC61D12EA696CBEB7CA290B102BC7E13918AE4E4FC43FCFDBED3F5AF3EEAA2CE0F41D256DA94A6ED997093A7CE1E1FEA48CAB1C8FCFD01C57D8667623B3AF229D96A1D8ABB155CBDF15546ED8AAC1D3156F15770077AE2AEE03C7156B9D36A74DB157733E18ABFAE6FF9C6CDFF00E71D7F208F8FE5C7957FEE916B968E4D279BDAB0A1D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8D34BC51D4D0F8E6BA9DAA5D757887900C2A71A55B6D7229BB0AE34A897909F881D877C041506914D7085128E2A32AA2CB88285DDE7100A372E23B634578825125F3B6DE3DB0526D0734E0296AD08C9008291DD5D541EF920C0A4C65777FB27E7930C485FCC81BFD3930585174720E677DA9824530148B0F5141BE41B2D48B91D8FCF15B6BD4C56D917917CEFE6DFCBCF3E68FE74F25D8DA4FADE8686E21BDBD1FBA4E07970AF89F0CC8C3C9C5CFCDF7349FF3F5EFF9CA09EC6E2F7F40F96EDEE2C9C5CDC4A63A3948C50D169EDE39750B71DFAE1E46FF009C8DF3C7997FE70D7CC7F9F77D6D65079CF43D126F318B287FDE7668D4B05229D0853DB26021F945E58FF9FADFFCE5A79B35516DE55F22681E62D56E938AD859DA9967DABFB0057B78614BF5BFFE7113F3E7F303F3EFFE71A6FF00F34BCD7656107E6269DAB6B5A78B38978242DA7FD8475A7C2DB1A8C04354DF9BBF941FF3F0CFF9CA9FCDFF00CFA87F28345D1BCB916A1ABEA975A7BCB321458A3B4765E4CD4EDC70522934FF009CF1FCF6FF009CA8FCBB54FC9DF3CC3E4CD4B45FCDAB77FABC1A0167BE8560F85BD6561F0D6B51F2C5987E547903C85E7FFCE1F31E95F90DA75CE97A4DCC5234B6925EBFA51B3F2E410B7892705864FD16F257FCFA23F3F26D7B44BFF33F987CB7E4EB2D2CACD0DEF97A57FAFA5C210D14B5634E4AC01AD70A0BF577CE1E75FC81FCA3FCB5D0BCB7F9F7E7DD07F32353F23411CEF26B7325C6AD737D0F499537AB531624BF32FF00E7323CE1F9DBFF00392DF975379D3CA7E4D9FCB9FF0038CFE50893559ED75188C37B3C51FDA96341406234F84E1A60FCCAFC9CF2BF9BFCFDF9CDE47F2C7E5B79864F2479AFCE0F1D9F917558E43088E40A5C7AAC3703883D31E140897EB27E5E7FCFB5BFE727B4DFCF1F24FE6C7E63FE61689E709343BC49F53B89E691E765414DAB4AD6B5C69B86CC2FFE7EF9AD68DFE37FC9DF2EE8BA9A4BE67F2DDBDCBEAF6D237C36EB22928CDE0180DB2259805F3C7FCE28FF00CE04F9B3FE7277C8B379FED3CEF2E942398C3C1E42073EA05011EF913BF26775CDF5A68BFF003E80F35DB6ADA36B1ABFE62B5E3E957F6F77F57E65AA20903D055BA9A5300891D14CC17E927FCE507FCE31CDFF003909F913A5FE4E5B6A8FA49B39ECEFCDFF00EC97B4D954EFDF2C0C0CC9DDF9B23FE7CF5E6CB5D3445A7FE68CD0DDBB5651EA3052BDD766A644829F10BF463FE710FF00E717AF3FE717FF002A75DF235E6BF36A771A95F4DA9497719F84348A568373BEF954C90A245F9F1E67FF009F44F98B5AF37F9975FB0FCC06B6B3D7B50B8D4C42F211F1DCC86460C037515C4094BA289D17C03FF393DFF38B1AC7FCE29F9FBC85A4EA5AE9D56D3CC32395915890415F72727E1D06CF11E5115ADBC0BC0ACCAD3B3189E9B75DE990E109E22AF02A25CA98A4775EE7B60A0C6CBEB5FF009F6E24C3FE73B742F456393FE752D5CC824EC9C7E265F714DB2E8061225F657FCFCFFF003C3F35FC97E6DFCBAFC96F2179A27F2BE93E74D264D47CC3E6289FD3B9611CDE998B9D3BA8E9965048912FC9FF002E796B4CF2F6A1746D8A5FCB75704DF6B7A99ADCBC86A4C8A6BD77CAB200DF025ECD06A30454B7B39648D29FBFB81D251E0C7C335D962E7E29109E69570F65730DF69E56CA20DF15B45B4751D5BE6731E31A72B8ADF587E5979ECDFABDB5F5C28E2005E47AFE18261BE05F56793754B6B9B7BDD2DD11E3BD8D979B741B532B00B39BF217FE726FCA5FE19FCC1D4EE238EAB732332D3A1A96E999100EAF5043E588DA30F351BE22DF1A9EA0E67E2D83AD9157A1F0CB98BB7F0C3452D12075C08B5CA46FBE2AA8C46DBE2AB01DB156EA3C715773A6D4C16134EE67C31B5A6F883BD7AE3687701E3855FD72FF00CE367FEB3AFE417FE6B8F2AFFDD22D72D1C9A4F37B56143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87A96FEF941ACBF8E623B3B0963DF5F16DA5AE34B611515DDF541F571A5D9318B53BD42A1E4AA7ED2E0931927706A3CC0E9BE52D61166E7638B38A5E6EBE33D4E2D8103717049C52833FBC23155616C42F21DF15434B1D3B62A868D7E36F0F0C5894746A3DBE8C582BB461BE43155168462AC93F2FBF2CFCE1F9BDE7FD1FF2EBC9776965A8EAF19905C4E6900A1A7C7976271B31DDFA2BA47FCF9FFF003D7597F43CC9F997E58B4B688049A0B3123CA57AD18AEDF7E640F268B7EC4F907F217CA1F937FF0038F179F955E75D6E26F23C1A335AF9A359B87280447FBCAB9AD12872616DF1CEABFF003963FF00381FFF0038BD6BA743F96BE5BD23CF9A8D847C5755F29411C972838D2B3CEC2B5DC8241C905B5BFF003EB4FCC58FCF1F979F9AFA45BDB470DBEA3E63D675CB68140E5026A2EC544B4EFF00150E4BA2F0DB17FF009F7DFF00CE324FF963E63FCDAFCFEFCCDB44B09FF4B6ACDE5AB8B8F84DBC31CECCF23927F6C74DB010CB81F96DF9DDF9D7FF002BE3FE7253CE9F98D6F74D045A7CCD65A2E9970D50A9033C3CA01414E4054E56436C616CDFFE712FF28FF257F327CF9E6FF31FE72FE7149F963AB796353B69BCBCB6F75F56B8942307356F0AE459786FD51F38F93BFE71CFCE8D7B3AFF00CE71F98B48B4344BB5D3B5DE01005028483E032A32A3CDA4E2277A79DF977FE7DABFF388BF9C724FA9681F9DFE67FCC6BB8EA6F35A1A8ADDB0A750CEC07864F8C745E0A7D95F97BE4CFC8BFC81F25FFD0B3F99FF0036D7CCFA7F9B4496BA47977CC577EBDF4905C8A0B58A951C00FB2B931913C07B9F1A79AFFE7DDDFF003895F947E615F32F9A7F3CF59FCB2D465BD7B8F29DF0D405ACB64ECC4AADAB863C4A5683DB26269AA1C932B9FC9FFF009C6DD4E4B4D31BFE73E3CEE249C520787CC4435C9EA55985795326770C385F15FF00CE607FCE3DFF00CE35793FC93A9FE677927FE722AEFF0035BF320CB15BDBE9DABEA02FAEAE854A30AD6A780ED95C837C075A7C55E4DF31FE71792B429EDFCA5F991ADF923465E12CD616174D00776151B2E634B6673887D0DFF38F9E6FFF009C92F3EFE7CFE58F92FF00E576F99F50875F9E3D66F2CA5BF77A5BD9C80C9148A76A3807614C8F38DD72712517E82FFCFD9BF3BBCF5E43D53F2BFC95E46F376ABE50D47CC9A7CBA8CF75A64ED0168E297830629BF6F1CB9B7146F67E4949F9B1FF0039096D688A9F9E7E6F8AD66FDFCB72DA8CA013ECD5AFE382DCA9C283F6FBFE7D73E78F3479EFFE71CBCEFAC79EBCD5AC79AEFB4CF30DDC2BA85EDC34F27A0B192A159EA695A9EB8251704737E2FF009DFF003E7FE720AFBF30FF003022B5FCE7F34697A7DAEBF7D69A7410DF3A2450C729F4D68294016836C31A05CD8E332890C035DD5BCF3E67D574EF30FE64F9EEFF00CECFA07C7647509CCECBCF614AE4E73586911714D00BE54BA9663752A97D3E2AFEEC022A6A3E5989293951D3D2B5AB7A48ED5FDAFE3862584B1BEBEFF9F69C9EA7FCE75684C3FEA4ED647FC933991170F2C69EDFFF003F6FDFF3EFF26475FF0070139A7FD1C60916A897C11A8C06541DF8C9CA998F22E5C0A7769767EA8B08FD9ED9448B971679A34DCA08E393ECD77C85B75DB34B4D4A5D2EE2196CBA0209A64245CAC6FA93F2F7F32121B8B35B9702BC565AFBD3DB220B64F93C6BFE7316D6CB516B1D6AC183B3AD5A9F4E64C0BAAD445F9DD35A43EA99631F13EF2FCF2FE275A614A6E782D075CBE05690E243D3DF2EE882BC0E79596B937C78ED819479378B2762AEC55ACC79736D1C9BC4312578CB43592EEB920C097F5CDFF38D9FFACE9F905FF9AE3CABFF00748B5CB87260F6AC2AEC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF864BB048AACD51DB311CEB4BC2B147612EE01C56D17621DC0AC98ADA65E8307525EA2BB8C124129D5B45D38F4CA501306040229D716C8A58E28E49D862D88571C9F6DC629548E36A8DB154D235AAD08E9DB1541DC42687E1FA7154A1D5D0F43BE282A91C8E0F4C58F0A3519A9BEC3B62BC2B8891BA2938AF0B21F2279EFCFF00F94DE7DD13F323F2F2682DBCC7A3AFA3682ED3D5818B1AFC51F7CBB1CA9C4D40DDEF1F99FF00F39CFF00F3963F99305EDCF983CE163A19D22DDC48BE5D88D8BAB28AD1F89353F4E5DC40971E9FAFDF975AC7983CE1FF003EDFFCC2D67CC3ADCBADEAFAC793AF6692F6F5CCACAEC87A96F0C9476B5A7F387A7BE85A5F97E686F0C4B71A859A08A7863264776DF812A09AEFDF240AD3F707FE7D29F951F9A9E58D67F327CEFAF794AFFCB3F979E6FD06D2D74586F289F5A962209786B4A13D6A72D06C289D3D0FFE7EADF9D7E75FCBCFCB8F2D7E4FF95F4E9747D37F3126922D63CDB6EA5623146D510C6CA2AAC4FDADF7C494F1EEF91AF7FE70E3C83AA7FCE05F92BF3AEF2F5BC95F997E5B8A7B89B5A998AC37BEA48591645EA49A0A13EF9592D8323E2BFF009C7CFC90FCC1FF009C9CF3F6A1E5DF285F695A3F99A0478754D4EFA15781907C3CD50EF5A6F91B6632ECFD44F20FFCFAB7F26BF2AB40D53CD9FF003943F98B1EAF1E9122DD31D16E0D85B451A7C6C6757AB301DE83A60113F0419DBEF0F30F9D3F2CFF00297FE7157CDDF985FF0038E51E8F77A2E99A34D3E87AB584002CA507C25DB882FDF7A647C2A533B3BBF9ABD5FCC1E62BDBFD3BF38B52F3E1D5FF00332009E6CB79656692289588A59C486BC597C06110A62723F717F317CA1E4DFF009F8A7FCE1B683E61F29DEAA79DFC9B6E93C371250BAEACB12ADCC53746E24D763921162676F3AFF9C76FF9C4AFCACFF9C2AF256A3F9DDFF392BAF68DAC6BB25AC8348D2EEE1F56DE1050845489C37C66B4AE4B8D1C4FC98F3C683E4FF3F7E6A7987CE9A0F95AFF00CB7F97BAD5E493E91A34D227AD21690B17B72BF654D4103C32B3367E3523BCC3368DE5ED38437CCF631C29EAC4D72DCC48631544A0AD49A532A3BA7C6B7EAA7FCFAF3F21AF74CB6F31FF00CE577E62D9AE9F1EAD6D2BF93BD61C560D302B7AF32F2A5140527135F00C38ADF0C7FCE507E6BC1F9E5FF3923E60F3559336B1E4BF277ADA2792A590F34B9B5726B3475EC1AB91F136761A7C24EEF13D4A2B98216F556DEFED9374B08928CA3C0D76C7C47272E2D9FB2FFF003E9130C7FF0038CDF98534B0936D279BAFFD6B75D8AA08A9C7E8CB89DBE4EAB83D4FC42B8373FF002B0FF346E6C618E5B6B7F36EA8E7D75E4157D634041F0C864950761A68F34C66823B9FAC35E7A572978ABF0C0BC42537E99852CCEC052AAA97948E11B154E30923E3000EC721E2309CC24B3CE90432F3902146A3D4F427A65F09383972D3EC4FF9F65C7327FCE717971E44204FE4DD65E13D792F03BED9990E41D74F2713DD7FE7ED2449F9FF00F92E10F22FE5F9CA7B833E5732B10F862E5828B84A5655AD5331E45CBC6126D3AF59262937EEF7D81CC79172E2F55D2984B0523352AB5DB204B918E36C8F4C9E559024919604D2A72B949CA80A679663EAD494CBE9348414FA32024DB241F9EDEF35FB18ED9C35C431A101FA819938E4E0E585BE39D634AB9D26FEEA39A268D2662D096EE3DB2EE370E5898E354B6FF7E6463938D38D34D18D88FA73281D9C5915363C7A76C8940709053734381B03B9A78E2AB81077071575478E2ADD3C329237656EA1F0C900C495DD865803592DE4A9812FEB9BFE71B3FF0059D7F20BFF0035C7957FEE916B96057B562AEC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF86BB8802C67311CBE240DB460B32F63B1C57899159D94483A7CB15E25595139003AE465C97891F6DF00CA9215659878E2D914A669013D6A7B62D81411BE21E18A519137BE2A8C57A6F8AB52BD57DF154A6615A629014E35DFF008629A469140B8AD2262C5682DBA92EE182EA4897D5B636D227A6366F588F8581F6C944B46585BF40FF00E70A3FE70BFF0024FF00E723FF0029B5BF3279AFCCB2E85E6CD3F54FD1B7D1CF7CB1F2A8279716604E5FC449B718C2BA3F4B7F3B2EFF00283FE71B3FE7103CF7F9456BE72B18EF350F2ECFA4796A059D267BDB87144850231219B975392053E1DEEF8D7FE707F47FF9C0DF27FE58699F9D5E7A920D17CE3645F4EF33E9FE629BEBB0472C5F6A58ED983503B74A0C9DA38084AFFE7217FE7E75E7BF365C5E7907FE719F438FCB7A1D9BB245E63302FA77D68A6A9F554D84551B9A76C78E9A678492FBA3F267F36BF20BFE73B3F246D3F2E3CE2F6ADE69D3EDE14F3568B7AC8B7B693DB95E53432C86A3D4707ECFBE0333C988C469F327FCFCB3F353CABE53FCBAF26FFCE267E5CDFDA3C1E60317E9B4B77594D8C5624344ACC8763215A1191E3A663097E57797752F3AFE5FF9C6C7CCDF943A9C9A0F9CF47B37B69ADC3304BA5917E290814069EF94F8AD834F27D89FF38B3F927AAFFCE6FDA7E64798BF3E7F37350B297C93A92E973E8C3516B68AE39C65D8B46CE015A8A5732465003038E4FD4FB9B5FF009C5EFCACFC8DB6FC88F3D7E61E976FF96F73A61B09B4D8665F5F8B1DCB4D11635DFAD301CF148C322F85A05FF9F51FE534B65AAF9524BCF3E5F69779EB5A587D6E6BA592E54502399556AA41E94A644E701B069645F0D7E4CFE707E71FE4DFE6779E7F353F27ACA7B7F2BDEEB5777FAA7901F91B67D325959D04716C81E8C454657F9A8B68D148F47EAA587FCE5D7FCE207FCE64F97A0F277E7DD8CFE44D68148A2F29EA333219A56D898E455E2373B03F7E40E51F51E5E4C65A390DBABF3FBFE735BFE7193F2E7FE71D6EBCA4BF931E6B9F599BF306493F44F97BEB7F5C7B6892AE2854B05A8A0C81CA48B3C9A8E9A47A24BFF3851F919E41FCF8FCE08BCB7F9D5E708F47BEF273A5EE8BF97374C527BFF4784AEC676A2B2AF75EE36C1C6472EBD57F2E62FB7BFE738BFE730D64B5BBFF009C5CFF009C7DB8B4B9D4AE02D96BFAD6931886D349B68C7096D02A1E279A93BAED8F89E9B972FBDC8C7A49134FCD7F2F791E1F2BD8A68176E65963AD2F474AB6E687E67304EA2CBD060D36C9D5FF00962CDEC2E4C7290D6F197694577A7CB271CD6DB9F4FB3F537FE7D471DBC1FF0038CDF9836D79756F682FFCD9A8A47EB4F1C6DC425391566046FE39B033B1B793CF4F170CC03DE7EF7E383E88DABF9ABF332F6D2329158F9BF538A57471C64549295DBED5731F3CEA2ECB1E2B0B64B2FAB6DC295ED9A83959F8450935A7AA80863180EB571D695CB2191AE78497A47FCE3F7E527E5A7E66FF00CE4DE8BF97BF9B2B736BE4AF3858DC5D45789762D544B6F1928399200E4453E9CDB69C931BBE4EB73E120D53F757F207FE70C7FE718BFE71AFCE927E65F912FE4935C165716D61ACDEEB114F0C16970A04CA23671527C4666125C2188F73F2B3FE7E3BF9A5E4EFCDFF00F9C87FCBEB2FCB3F30C1E6A87C89E5FB8B0F31FD554D22B812972BEA74AD08CA67CADBF1E3DDF31DAC4D05A3A188C69337A8439E4E1BFD6CC69173A18924F4C0BA048EF98E4B950C4F51D01855631D5C01954E4E4C31D3D46DF46944493053B8AE63CA4DA229835ADEBC6A5222C22EBB64449245AB5B5C4A54C33C541EF9938E4C4C2DE59F9A3E5C4BCB14D4ADE3DED50AB53DF2D949A32627CC778E915B014A4B5A1CC8C527559E348462CA23AF565AE6C22EB25CDCA390DFB64CB3839A315C836114B7D35C50EA53618ABB155C1C014F0C8916825AF572403025BF532603025DEAE5822D64BFAEAFF9C6B35FF9C74FC823E3F971E55FFBA45AE0661ED78A5D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF881BC880420F5CC3E20E424B0A857A1EA4ED8F12B24B50B4218D31E20AA8F08670CBB9ED809B0A39AA03C41076395F0B604BE5662C6870F0B30504E1C9AD31E165C6D29E277FA7070B21205171354FC382928CF8A83E78D2B8D698D2A12406B8A625A8D0D7A6D8B2B45B23300145698AD85F1EDB1EB8ADAA4A225884FC4CD3AB045B6AD0153D5BE8C429A2868F47BB86133689E66D47465BDBA56BFD32CEE6585789FB529E0402465832531E005338FCBB6FA84CE751D4B53D461B17E766D79772CEAF20E8C15C9030F8BDE98E11D11F0795F499EFEDAF6F2C266B595F85D69026222007FBB997ECB57C30F8C19F83BDB33B782934292BC7E8C4ECB672C2823F4231B2D78815DB6CA67985B64710EA91EA7E5A437115F68D25EF97EEA32FFA4F52D3EE5EDCDE06FB3511914E390F1E9B3C081768DE5FD3ED8FD72E26BBD7F5E0DB6AB793BCAF427B1724ED909E7B671C1001E86B6EF2DC590922A49044CAFABA1E2483FB240DF7CA8E50DBE0C52883F2E6D4EB125EC771AA59E917959750B0B1BE96DBEB128E858C64549F7CAFF3738F271A7A404BD4B4AFCA0F20CF6D75A85CD86A90996129159DE6A12DD0527BD5C9CA726AF24F9B762D180ED0FC95E5FD05E19F4BD0603769271769155C18BC68475F7CC796693B0C7A50F52D3ED674B48A28163B4912E1A66754142ADFB0C0751EC72BF1A4E6474B163FABFE5BF943CCB3DC5D6B1A12EA3716A39D98B522DA49246EB491402299911D4D7544B4B1287F227E4F59E81AD5C7992EAE2F3EB112D346B2D46E1EF7EAA0FDA0A64AD36F0C9CB59C5CCB8FF901D143CF1F94BA579AAE96F74992E3CADE648DAA3CDF6933C721435E6948E8DF1034EB9286B047CC2FF2782ED17F2EB4DF2AD9BD969566F737F74419BCC533F3993F9B9330E4C5B213D582092E54745188D833187CB62EAD134DB88792A75D40F53FC73046716D98F152B4FE598AD6DD2D6CC85957EC5CB0A807C48EF96C753109CB8F886CF269BFE71FC4B7D793E99E62D5B4A17B5B899AC6F26B783EB2E7E26F4A3206F99B1ED1A157B3A89682E5C54CA34AFCA7D23CB5A63D9C76B2DDDC475996F4C86AD3B7DA771FB44FBE5597571946ADCAC7A7203CF75FD0E64998087A1F8B35E32370D384AE0D338C0E49F4DD47C2C45687B6646292CB00609E67F2EBEAF0D8DC5DC93DB6B36CFC6DEFEDA5689E28C9F8802BBEE336DA7CDC2E0E7D2C65CD8A6A3A0EA36B2B45A7F9B35FBC8C7C715BFE91B85141D57ED77CCD3A814EBB369611E5F794CB42B39B42944DA3DD8824D64F3D75661EACAA4ECCBEA1DCE512CE1A2189EBB1D84090ACF0C6C2D8AF0F58B16E4DF2CA279A2E6431A0534EF527AA2D77CA2598391114CF346D3E7867B762945E42B944B286FC71E3E4FA7F44D352F2C21089CCF115DBDB293305B0E0932587CBA3D27458E8CC3714FECC00B0F0A439B17D4FCA324619C2D3AF41FD99930986420C0753D0A76B1BCB59D39C12EE58F63965F1726196161F1E79A3CB8D63A8CFCA32B6FC8956CCBC669D26AB1960ECBCB91614543C53E599F09874D381054A46F4E95D81E872EE265034A4F2F1343D698096C26DAF52B8A1557703155C411D462A8377F888AE203592B79FBE48062EE7EF93B6041772F7C9713120BFAF7FF9C68FFD671FF9C7FF00FCD6FE54FF00BA45AE066393DB714BB15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFF00FFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8779E6690D0E6BDC86E0B62C55A9D0D6B8AA7B1454C555F8814C473508693A9CB59A5E480C49E98AB4CE9E18AA1A465236C05945742699536F1047092A078E2B61716DBF8E15B08391B7AD701482A9130DB026D16ADB1FD78ADADE4395715B5CC6A062A9859FD93912D914FACBEC37FAD95C9BA09CC1906F8A6B0A0645AF8E565124EE5B5E56640EE320531482C21F4EE0AFF29DF2B9330CEACE0F56829B9C8368E4CDAC6C8B24629D3205B22F49B3B43FA35969534C81722095DBD8BF3A50F5CA8B97065B6766FC7EC91909392190E9DA51E60F1A6F539510D822C94E9842EE2B4182922294BE9CBBFC3DF1A6D10413E9C95E9808D9918A3AD6C100A53AE505ABC3569B4C47A10BB8C57C3455AE9E00E9D714786EB9D2B9A3F14DE9B62830A0F10F35689708F3388C77C945800C0A1B0924B69D5D00DF7CCCC658CA2C43CC9A64B085600D3332127032879C5F44D1D651B3014CBB8DD6E68EC9569E93493D373BE4096BC707D0DE5ED224BCD3238D81217E2194CE4EC2186C27B0F95E4120223E9ED98F22CCE9CB2BB2D12485E12E869514DB2B3272F498082FA3BC976F1FA31C6CBD80C00B9B2C4F5CB6D22065A85FB5D72C12689E35F75E5C826423D304E4C16838DE5DE70F28FA16FCA18FE12A4B003FB332B0C9A270A0F913CE5E598A54B82F0FC42BDB33232B755A885BE51D7EC24D3EE1A3E344EA332A05D2E68531CB9903428D5E99931710841897950E5A98F25656C595ABAC805315B5479401F3C56D2F91FE33BE4C354B9ACE7EF8B1773F7C55DCFDF157F5FF00FF0038CFFF00ACE1FF0038FDFF009ADBCA9FF747B5C9ABDBB15762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88316FCCD45335EE42630C7C17891F11D862AAE55A3FB5F862AA7CC9751DB08E6A164BD4E58CD00D192493F67C71552654F1E98AA832AF6EB91290B797134C832564620D6BDB1557E551B1EBDB1542C8AC687016512BE3246C4E0656881228DBC715B5E11CEE294C56C2AD0D003D7B629B4CACC1A53B9C896D8B20B34655607B9A8CAE4DD04EA089F20DF14C63AA7153E3BE56512656AC0D9EFE1902CA2C7A01FE94CD4D89DB2B9320F44D1A22C54D36A641B43D174E882B0E4366E9902D917A259DB936C4506FD32B2E44175B598E7F677AEF95972E0CB2D2D502EE3225C90C86CA10A46DB6574E481B270E8A474ED8D360012CFAA73AF15EA71A6C0109258B03BAD3011B3202D523B609F6B314B2E04D22B3590034AD714702362B38D4D08A62BC08D92CE26864A0DC8D8E2C670D9E75E64D0627B39A4352789ED928B8A214F04F4608659A2058B96A71236EB97424C65040F9A34E89EC95C28AF1CC98CDC1CB17806B5108838E3DE800CB3C475D9216A7E5FD3A7B8B80238C31276180CD9E1C4FACFC9BA25D0B4F4DA1019539357C331E591DBE1C2F4FD1740FAEC9C12352E3AD76CC796473469D93DC796E24291B4604886A6991E2B6EC7800667E5DD3440E9F0D0786112672C6F5FB14B68E3024AD587C3B65824D13C69CA476CD41BFB6D9609B41C4A77FA1C17F6B22320208EF97E39B899A0F913F337CA4B6E2E0DBC436AD4D3E59970C8EB33E2B0F827CFDA605F55B851D76FD599B8E56E935189E113865050F8E66C0BAAC91A520A4003B66406B05554D3AFD38A6D706A1AF6C56C36D20A7738ADA14B124EF930D6D54E28754E2AEA9C55FD837FCE337FEB37FFCE3EFFE6B5F2A7FDD1ED726AF6EC55D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88EB5527A8EF9AF72138584000F862AB580622B8AAAAC0BC4B6D5188E6A1033851F3CB59A058A952062A876E3FD715506A74EF912BC90EDF6B2B5E254AD00A615E25367618AF129FAA5B6F0C0520ABC75207BE0648911B1236E98A11A88C314DB60126A7E8C59C135B35DC644B745925BAED95C9BE09EC0BF08C837C559C5181CACA24CA6DC7A9694F6C814C52DB7B73EBF4EF95C9987A5E876C788DBB641B4727A3E9B685DA314E832B2CE2F46B2B6263094C8172609D5AE983903E395972E0C8E1B001694C8C9C909A5A5A8AF4E9869B849363682831A6626BA2B44A7D9C69B04D42E2D12A76C8C86CCC4B74B4DA1AF4FA3304B7DA6F69684F1D8605B4CC590AD298ADA30598F4DB6ED8944B7093EA9610CB613A9515E27F5620B488BE60D5F4C486FA62ABD1F7FBF260B19063FE62F4CDA08EA2BC72624E0E58BC2354B0E572DC96A9434F9E4FC470B82CB37FCB4D05AEB534E70FC3CBA91F3C066E5E2C54FB7B4ED02D6DED144680394A1CA2537678A349BE91A6AD9CA5F852BDF2894DC8B013A36A269D98AEDDB0464D9020B26D2ECD232A4AF4CB049259A5BAC2FC435053A659C4D134E3D3B7420F2C989341451B988008879061BE4E33A71F246D83F9B7CB36BA8E997521505CA93FAB32F1C9C2CB8DF97BF9BDA0BD95D5CC623A0DC8FC336584BA6D563A7C937D66DEA91D2873658CBCEEA0520658FD26E077D81CC98F27094F0AB44818ADA8B362B6B7AE4C2BB15762AEC55FD837FCE337FEB377FCE3EFFE6B5F2A7FDD1ED726AF6FC55D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8A0B7896BB6D9AEB72116C388EA3DF1B553E05BEC918DAAA51C2F1E43082A94DDF21DF25C4CAD2AA495D8E3C4B68696564D88C78D6D62B926A41380C946EAA2367351B0F7C8DB2F0D50055346618DAF86B1E30C2AA7AE36BE1A82A146F886C7A6048852696F172DE98A784A64B1AAF5EA7A62BC255982D0D06F8AF0950E209D87CF10120F0A696686A36C0436C66192DBA1DBA6552722124FEDE2240E9ED951939312AB340C29B8391E69E1B647A78ADBF1EF4C8908BA46DB5848CE1C5284E57209137A2E836CCC428A5477CACB744BD434BB378594BD083E1954A5BB7443D074EB477A11403DF21C4E5420591431716F8874DB225CA8C29378D4505075C8916DDC48BB65A374DB0ADA6C232C36A615E3A44288D054A934C5B233B509551C6C0D7232E4DC0A10C04B0D866010DE3284DED6DD956B4191B65E222429077180C93C48F8684712363913365CD21D4D084954746072226BC34F9E7CCF02C124EED4249DA9F3C98935C9E37AF34B2B829F647519306DC3C91B49ECB467D52EE2855284EE49C375CDA31E2B2FA6BC83E4C86CA38E568D79F723227239F0C74F6EB4B13EA0DA8A17A1CA897240A4E96C569F0819598B2E1255C5B46801A6E3AE08C59C2242716691BF414A78E4D914D2283E3156D97240B4C85A36508CB452464F8DA4C54EDA89255892064E32B6990A446A33A3DABC22B475A0CC9C73A699C6C3E1FFCF3F2C7A51C978C8ACA63AFC3D7B66D304DD46AB1ECFCEAF30DB98DE4923015549DBBE6DB16EF2FAB86EC2259FD56E46B502999575B3AEE02A7CC7BE4830229631A9F6C242A91563DF224D32112570141BF6C226B54B4B81E3878969776AD7256B4B4B01D8E2B4FEC23FE71977FF9C6EFF9C7C3E3F96BE53FFBA3DAE4D0F6FC55D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8A889788DF6CD7539086B99B8D6871A5538666618D2A20C8C0548E98D2A0E6FDE0A8FBB1A54BC2D1E871A540DC805F7F1C6954F9704AD37E98D3387344464BAEC298D37F120A6B794B7C24E34BC48A855900E5DBC71A5E257DA62140DD37271A5E24C625E031A5E256049231A5E2452AF2071A5E25F1435AEDF4E4834CF74CAD63A1F6C816706456895207BE51272E0CAADE1A2034E994C9CA8F25EE9C8D298C4ECD80A75A5C7521299125AA4CB9221146AB4F880DF2A2562CC7CAEB59D790DAB9025CB845EC16F10E4B45DBC731E5CDC880671A58A28DB22E6E309C2A0E75A77C5CB099451A918B021116F1FC74F1C5253A48A806D8B54954A2D3F8E2D9050E0B5A78E097272A28848158F4E9984425191A0414CAC8641A6A02476CAC86C0BD245515F0C810CC1A63DADCE1629187864405E37CFDE636699A6205687A64C313BB03B7D224BFB9E012A2B97458F876F5CF2D792A38E786478A8DC7C32333B2638A8BDE749D320B481005008194B7009FC50C75D862D8023046A0785316C010F20500E29A57B324134C2A426292B066C0D642A3CAD4A62D6421C5C145624EF5DB2C816B942D0525F173C589A65F02C0E379AFE66E996FAAE8570B40CE10D3F0CD9603BBACD5637E4D7E6158C9A7DFDCC0148018FF000CDE603B3CA6B31EEF25039027DF326F7757214D85F965D171A4E2BF2C99601BE3D3A65127231A9323576E9910C64A0C8D920C56A96AD0E58155D0035AE495FD83FF00CE337FEB37FF00CE3EFF00E6B6F2A7FDD1ED72618BDBB15762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8A8F54702DC7305BD2695FD57E9424E2A898542F520E2A887914A15F11D71542A9AD452A7C7154BA7942494A6F8AA5D31323020D2B8AA22180135770569B8C5944EE8E411D28B8B6A1979198A114DBAE2AAF28F4D775E55F0C556DA71E4E7895A8C551EEEA829D7C062ABA08DA46240A0C55341198D6A472F962ADC13A92CBE99153D7161229DC101A06AFD19596704DECFED57C0F4CA24E5C198DB30740A053DF2993971E4ABE8947A7DAF7195B34F74A819650FD457A64496B3CD99C712CF2569C41A6C72B25B718671A0D9849148A139025CD84767AD58C01D5052847539496E8C7765768521A2952D4F0C0E66308812564340684EC3172404CE1734AF86282138B70052A7AEF4C5849368D948F0F6C5AA4B1C507DADB16D8286E7BF4380B9314744D4ED98A42551989208046F9590C8284C5BEEC810D810AEEDC0F6C8109972633AD4B5B765A9269D7234E3C4BCC24D39EE5DAA6B53D31AA72602D3FF2DE80AB74ACEA0EF5A530F139508BD86DECA389A3E294A0C04B394682747E08C7C3906B015ADA60E7885208DEB8B301184D462CC0404A18EDCA9E18B346D8802B5607160423FD440C7DB162434F32018B59096CD729E1B6374988492EAED549A0CB2326528B1CD465FADA3C4DF65D78D0E6CB049D5EAA2FCECFCE9D1E2B4D6AE233462E49047BD3377824F2BAB86EF96678C452C89D402733E06DD0E6145439004ED991170A4B8508AD29932C03741E19449C8C6DF25029C77C8863258789FD9C218A1D901AD053BE5A154591BB1A6495FD84FF00CE32FF00EB377FCE3E57AFFCAB5F29FF00DD1ED72618BDBF15762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF89F0E3D33BE6370B90957306400773B63C2A893C9057A531A5414B39AF107738D2A6360038353D31E154BB508584E08FB23BE34A81F41BAF8634ADD1B74AFBE3C2A0A32D41F9E0E14F1353B32CAA4277A63C2BC49B7C0D082CBBE3C2BC484A537514C785789A1148F22D477C78578995C16E91C4848DC8EB8F0AF122B8464531E15E2524B74120A0EB919041DD3B55548C6525BA2BAD1AAF41E3BE5127320CD6CD78C60D329939514DA35E681B8D77CA645927FA70008DBE79592C3AB2FB280C8E1E9B1E99025CAC4377A3E8500E6A4FD39592EC71C5EA7A75B5694E990655453A11716DB172F105648A86A7BE2E5008C8F614C58108849CD7AF4C5A64994539D8E2D5269AE0B122BF2C5B20A911248AF42712E5453D8A9435CA2412BCD011E195C83243CCCBE34AE564360404C418A403C36C8D32E618B5E219BE06DFC31A6B1043DBE9C88EAC57E795C9BE019569D6D142438503DF2A2E4C4D0653115722831059C8D84C245063E98B00142301771D7BE2CC05766DB16C010ACA64AD0D08C595226D548A8AE2C486D890C77C5AC8519588077C2C084B26734EB954CA46C92DC826BBED8C249914B48F8C0FA7365824EAB54762F867F3DA0FF738E69DBFA66EF049E5B56777C797AA05CCBB53739B5C6E833F34BD97AE65479B8325C3651932C02ECA24E441AA0C88632688184315608BC47CB2D0AA4C8BE1D724AFEBEFFE719F6FF9C70FF9C7E1E1F96DE54FFBA3DAE4C317B6E2AEC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88C69196127AED9570B9096DAC8649D09D80618F0AB26963050B571E15484A8697C287070AA610308CD41AF88C3C2ADCF2ACBFB1F11C78556B47C63E5C7B74C78552891B91200E27C7052A6FA64609DCD70213292D95E4A70A537AE0295CF6C586DB64789544DBB12AA13EC9DCF8E08CAD53386D49A129C6992B545CC68AA03538F6F1C6D54EDCB4A695C6D5368AD893CB9F4ED913BA2DB9A42B48C0DBC72A945BA324C34BB33248007EA6A4D328945CB8167013EAEA13EDD7BE5243951927DA622C90056D8D4E52609326436500120402BEF95CA34DB0C76F41D3AD38471ED94C8539B8F15335D2217F500029EF95172E269EA9A7A88E38EA2A69BE066237BA6E6207E2AEDE18B91134E238D3BF862DE26AF1C7CE9BF1C549B5AABF11F638B4C8A3D361F2C585371C7CBE2AD37C52364CED5796DE180B7C7226491D3F6EBF4640B3E25B27C3DEB4CAC864248095B96F5A65526D0508CFB78E479320503E887979D694ED9599B6844B5B86150DC699519338A6F6E802019592D802776D1D5280EFE3823CD289766E3403A65BC2B6A28581A15A0F1C7859892282961B1DCE3C2DA0A998985487EBD4E3C2C81B5D1A322B1E75A63C2B24BE49DCB9DBA63C2D6A4CEEFDE871E16B2859A1928183D699465145318DA477AF242A0D0B1390894CA0878F9B3A9A54115AE6C7049D4EAE3B3E30FCFBB7923D45AE78F2AEDC7EECDCE1C941E53571DDF1B6A16ACB3C8EC69CB7E3F3CDBE09ECE8B34774918D0D3C333625C1986E95EF97736B6CAD3BE5320DD02B9412064445245AF1193DF080BC2B0AB02464C15E1586363DF1B5E17F5F3FF38D029FF38E3FF38FE3C3F2DBCA9FF747B5CB03597B6E143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D98A8B7639072125B39544C3E78AB27797F75F462A948156A62A88A30C554E163F584E5F67154E27E0D0D075A62AC7648CA82701546E95381285F7C8143339230D1864153DF2252A0A08201195211D0C695078D7C718F34A39A48047C76072C548AEC8E69C0D41EB8AA63A7C144E647CF154CE175AB8F7C082B1A1F524141D4E466CE2CA34AB6F488246F98F37331B2066569157282E4C59269F6CC5D4A8F848D865726639B34D32D97EB09C865322E6620F41855508551B28DB289976100C974992928A8DF2825917A04120E2A7C70B743927B6EDEA003B62D88958F93118B64516917100E2CC287A7F113E2716B3156040F9628E144C4B45DF7C578530B55EB812228A1CA83205B5425E546C8499452E7E54CA4B70429EA3C32259072FDA39410CC146A005699021B01B4CD015400E4086C0532B7978AD46301452792351CB76CBD00A2163E54DB61BE2CC145A41B74A571660AC922A0DB6C5B6254516BC875C5050BF54E4EE695C581287961E1DBA62D64A02694468478E63E50CE058FEA24BAA9032B8865296CA5C9A28E33C7AED99D846EEAB57C9F257FCE4246D0C493B2ECDDE9F2CDCE11B3CAEAB9BE25D56759642C050140299B6C01D0E61BB139012C69F4667E37026BD010287AE5E1A951BF865726C8AE465005722DA15D5D7EEC2AA9453BD3AE2AEE2BE18ABFAEAFF009C6BFF00D674FC82FF00CD71E55FFBA45AE5A39349E6F6BC28762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87EBA9D962318514F1CA2DBD2086568E74237E4DBE36ACCE37F5223DB1B543A8E2F5EB4C6D51AC57D3E58DAA5F50581AD0D76C6D5305E4C8057E9C6D5053A108C08DBC71B5405BAFA73F20DBD712ACF6C6E8AA54A06DA943912944170E6BC403E195A15D2E022BD5474DB0412C76F2F19A4214D07B658AA96FCD8835E7E3ED8AB258A65861DE9B8E98AA5E977C653C68C09A9C5053DB39C33A92A0E426CE2CC6DD8351BA517A663CDCCC689854B5CA6F9417262F56D06C8488A37F63944CB7445A791C0F0DE2A28A8AF5CA497371067F6567249C7634A7DACC79173E316496561E8C80F224E524B221964638A281BED930D914DECE765A7C35C2CD3B81F93569D4E2D91473B6DD3166146353254D29BE285850F2229B62AAE8EE9B7018AA6768F4EA3AE2551ECCA83C72B2C908F286AED909328A11C8A74CA4B7041484006942476C052A51824824532B21368F8EB5DF61E390219829824818053B656436028F80A82141AD71884996C9BC4AAA011BE588053355AA29A52A77C5B01560CAA37C5982879248DC30AF4C59C4A0165547206F5C524A3519284FF375C5AC940DDAA052791C5812C62F1BE02C054A9E994E4512A414BC1EDD5C8A1F0C8C53C4E6712A20540420AE66E1165D76A4ECF9B3FE724B4F79FCBB1DD2C615947D91ED4CDCE10F35AA7E736A49E9BD1CD0F1155CDA61E4E833F349C0A0E43E23E199D8DD7CDD52DF115A13DB2F0D4D9A93D32B93645408F888AD37C8B6856403BB6154482B403962AA8154FED9C55FD75FFCE35FFEB3A7E417FE6B8F2AFF00DD22D72D1C9A4F37B5E143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFF00FFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF876BBE87315C8485CF160DE06B8AB2DD364F561FA31555941018E2A88452D01F962A955584943E3B0C5538B771C77FBB156AE1D1D4A8EA70C552A02928F9E48A197589AAD3DB22528D63C72A4202EA7288287AE304B1A7959E5A7BE58A9D5BCAD12123BD3156E5D4491C4B6F8AA8C37039160D5A9C50591E9B745A5515EF909B38BD1ED5FECFB8CC79B998D3AB14E57287DF282E4C5ED7E598D43A46DE198D939B918D92DD411A5DA35075DB289173F10675A5BC7E94629BD331E45CF804DE8CCD551B786524B221388549550763DF2D8F2484DA24E00649926D6DDB16C8A3DFA62CC23ADE35E35F6C59D34235E67DF15A44FD5C374C569AF4590EDB602B4BDB91C8162A251B7ED909328A83A353AE525B8210A357A6053C9B58DC6FC71402AA0B1143B7BE408660AF5661DF2B219828BB695BD51BF6C88E6C89D936FAE84E35392620B23B5BB8E48802462D81AB89929B362D80A5227ABB0E58B3057AA826BE38A928D8CA804571604A1AEDD781DF16B258E3CF17C5191CB91A8CAE41ACC964482E3946A9B0ED8C42789A82CE732B2AA1E20666E1145C4D472795FE7CE96F2792E591A3FB009CDC610F39A97E4E6BCE5EF64A6C17E1A7CB3658DD06A39A5F08063DF3371BAF9B7407A65E393535C4572B9364541E262C48E9916D0B789185555436D8AABAAB53157F5DFF00F38D5FFACE7F903FF9AE3CABFF00748B5CB472693CDED7850EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87BBB5531935DF31E9C863A543921B61ED8D2A6FA4DD324A200070AD2A7AE1A54E75173071F4C0656FB44E0A54C34F904B0716006DB61A54B2E222B72A00AA93D7052A342468952C41231A543955506452491B50F4C2020A1A83D415385871325D3E4F8B8F6E38299DA6373444E4BD7DF0708674C7EE1E4956941B1C1C20208A4BE38BF780B57E8C076502D386904501E00134E87236C8458C4B2C92CA6A00DFB640C99885A3AC80F4E466635076C788B194190E94DFBE06BDF2066CE117A759CCC4AF4D865129B9708B35F2E446F2ED44BB007F67283272A317B669F69F5495668EA76037E998F236E4420C87D2378E1CECC3F972B21CA86CCC749B3A471835AF7CA65172A136530C25187C35CAF80368926D1C2397235A9ED92029B446D328E20C37AFB6165C29A4302A01427E9C590D915C1586F5C520A362015401DF167C4E650096A9AE2BC48A85CED418AF1225959874FA3015E25AB6EFD5853293265C2E3180694EBB642526622AA2C95C7C24D72A25B4042B69D3A49CB882A0EF91E25217B5A3FECA60E363C28792D66542C62F8475A6478923642B46C3A46D8136B6349C3AF08CF23DCF4C40DD0648EB9B19446B21A863D874C953113455A7AB1A0E47638D33135D7771E9AFDADFDF1A6CE341C127AA58D7A0C04536C668E499D76FC7032E26A5BBE20107AE2C52A9EFA490F1DA98A0C6DD691722CF4E4D5D864A301269C9B328D32131373F4D4D7AD4648630A192470DBC70BCC8A0DC336E87A53333144071353C982FE6C68306B9E45BF515F552363C53C7369843CDEAA443F147CC3A4CB6DAC5F5B5C03188A4603E55CD96301D2651658DB22C5550DF2CCB8389382C415515CB9A0C5771CA6452154283414C900D814E445180AA9C6C4B71A6C3224A53058C507BE438D5FD727FCE36EDFF0038EBF9063C3F2E7CADFF00748B5CCA8F20D079BDA70A1D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8749E4AC447B652DE9347F131A77DB1557B6AC3386E9BE2AC8AE5C5C427F9A9B62AABA7B98D4027154C4C625607BF6C550579CA31C7BE2ABA08CC916E31415268087C5AD34B4E51B03F462D8134B97E51FD18B68415AA076901F0C05126A5B602A5464648097CC484653D32B6C4A963A927C7BE565B62B91FD30E3C4E218493CD1A4E532FCF2126507A969FBBA8F1198E5CB83D5FC9B6D5BAE9DF292E541ED92A18ADD683282E54026BA2C2EC39B0D8644B7443D0F4C8D782E5527222190C71AF31B641BC04C52104834EBD316F8A67142076F9E2C9305B72698AAB0B638AA323B7341B62C9CF6C7154C2DAD136DF154E560840F703014851692255F88019410DA95CF7766A08E601F1C810C9049AA5BC469EA8DBDF2B21B0228EAD1488C88E3938F872045325B15C4951591720C11C2E55455DD597B8C55DF5AB56EC98AAEF5ED88FD95F7184319206EAF2071C3D41926210735CC7140A43D77ED8B20C62FB52E4E141F6C5B137D2C92189E8C36C05B2298CAC1109C8B3B486F2EC28DCECB8ADB1A7D7218A5E25FA75C56D33B7F355A5BD393800E5B02C08B4DD3CFDA7A2D0381F4E49908D2D5F3FDB199B8C808E1B0AF7CBB1C9C6CF0B4A0F9D67BAB6BFB49B7826560A0FBE6CB14DD0EA30597E74FE7368325B6B97B7B04748A51C811E273638A7B3A2D462E12F9DA60F1BA07EE73371C9D76514887015A83C065D6E21E6B7204A40554FE1841D99A9C9D701296E28F7AF8E564AA37A0CAED5FD707FCE36FFEB3AFE41FFE6B9F2B7FDD22D733A1F48683CDED39243B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8739D15050D6994B7A56838C8A53A72DEB8AAACCDFBCF87C7154E2DE86024FDA036C551B6EBB6E37C551F1CA237523B763EF8AADBF40F1FA83ED1DF156ACE5E30D0D3962828D58E3937A5716B5748B7A1FB23A62D817CA6A287718B68528488CB15EF80A24AE5CB0C8C9012BBB4F877CADB12C8C1DC0CACB6C50EC84B1E58B0927DA3C6038206F5C849941EA7A38F526507AD36CC72E5C1EDFE4F83D39B95329939507AE9ACD1A2D3E119417371F2659A55BFA70F151DB225BE219469B190429D88CAA4E44427D6EAED3053F6720DE032686D01A6C698B604D52D1788A038A5328ED8F15DB6C5518966081B7CF1555312203418B2433E2ABE3623BD3155579F82925BA024E2AC5AFF5A8F81A380720437304BEBDB89598A49F09CAC8641266B8946E646FBF2B21B436BAB4F1FC21CFCF2B904AE3ADDCAF499BEF394B05875FBCA504E7E938AAD5F30DE29DE7A62A887F32DC471971354F4A610C648287CCB712CB4692B9260190C1AB4D31285AB45A818B30EB6E7753FC7B8AF6C5B1E81A7C451197A055DB0499DD35773908DB8DB22BC4F3ED56EE5A482BB0AE2BC4F2DBFBB732B166A11E18AF124536A2EE4297240E94CB20CE25080DECB25217600F8EF8934D9619058E9FA9F2462599BB1DE9928CF76899B67B67A56AB7118528294EC3336191C2C98ADE51F9ABE4D9A5D3EF649E127D0879A353BD333F164D9D06B70D17E7FDFAC4CD70B30FDF40E4474D86C7BE67E2C8F3DA914972492301EA50B77CCC8C9D613BABAEF4AE025B61BAB2004D3240ECCCB6F12D2A7224AAC0FC7E8CAC9558F3B76A7CB236AFEBAFFE71A8D7FE71CFF2049EA7F2E3CAA4FF00DC22D73610FA47B9A0F37B5E490EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87FBA8BD4048194B7B1D95BD16A1EB8AAAC3FBC353F3C553112FA6A476C553AB12255A8EF8AAF9E321AA3B74C5549AE015F4D8EF8AAC4AD76E871414E6D7B138B5A6C801E9E18B6042DC6DF3C5B52DF50A927C701449176CFEA353B646480985DD972841A74195B63152BC1C8EC0E565B62A4D4635F0C430927FA42FEF07EBC849941EA7A0445AE51BB0EB98F272E0F7DF2E22D40194C9CA83D12CB99611F5DF6CA0B9F886CCEB4D0C38AD3E8C89E4E44597E9F0FF00A415237DB2A939116530DA8F5850641BC06490460103C31649B2C6B4C551491B6D41B62A8D446A0F871569EDCD09A6E7164817B73BED8AA1243C176ED8AA4379764065AD6A08C55874D652DCEC95381B9520D0AE08355241C890C9171F961E43464A8F97F66565B0233FC191B293C7E2ED954F932E88197C9A00E998CC12CB8F2B8B78DA5E05F8761DF156297F6621AD206DBBD31563176EC6328B0B29AF5C218C90964D289B743D724C0335D3999E52A548F8716619DE89680B863D716C6573968D2918DFF6A980A4A4924C4F20FB7BE4516C475754653C37AD6B8ADBCDEE74C92791A83A9C56D42CFCB5732CDC1622E09EB4CB22764893D3347F216E924D1F1EFB8FECC848A99BD2EC7CA9630AA82AAC7AE40163C4CA6DF4AB5800E2829F2CCA84940619F99FE5A1A8F937549EDE1065F4986C3C01CCFC52D9D36BA1BBF1A35FD2CD96A5A84338E327ACDC54FF00AC7363864F27AD0C6648CC52943D466C205D213BAF4C9B978BBD156E393D0E36CA489BB4E2A3B57204A1219A52B5C892A8749F91A57A6564A1FD7F7FCE346FFF0038E3FF0038FE7C7F2DBCA9FF00747B5CD9E3FA47B9A4F37B6E4D0EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88FE09E89206F986265C86217D18790FE1928CAD5640FE91DF2684C42ACA093B8C2A9DE95F01A1FB3E18AA6578547D8D81EB91B4A41229E7CB2064847C0EA29CB718612B2829FC7E888EAA37F1CBA835A26DDD016277DB225B0284E19CD17A65664DA103242114B30EBD30192D5AAE9FFDE644C9908B241CE489D4EE00E982D980C4F50B52AC59453DF06CD8024EBC91B89EF9125B238C1E6C93493FBD51DB2B914F000F5FD094ACC9C45052A731E45B601EE3E5621A401B7AE504B9B08BD5ECA14AAB22D189EB98B3951765860299FE9502D15996AD90E22DE22199E9F6D1FA9EA71F8CD2A7236DB10C9E1894B82474EF8B626B144ACE7E793114A611464B713D3C325C2153A8E3000DBA0D8E0E10AAAC0A8DBEFC7842AAB0F86BED8F085B40BF5C7842DA577708E2DC0531E15B62F3E9EF2B13C490712026D1765A5246DFDDED9512BC4C892DE355A7015A656496C125C88A29F08CAC96F8C95C01D29D72B2CEDC638DBED2D722634B4A0F676EC0D23049ED9548522987EABA7C6C5FF74BF2A0CA49626DE75A8E99C8B462314AF4A61848DEE903BD278F4708F511EF5CBEC32E109FDA69C435553E223AE3B278598E9903C34EC3BE1B09A4E652163241F88F5C8C9B211058B5DFED6FD6B9149800C6258DA4675AD47718B510A967A6C4CFF147518B512CE74AD3ECADD59961018904138AC4B2B49232B420500F9603BA69689A388D176AEFD71A452256FA9FB59206992716CF16A9A65E69B3012452C6FCA33DFE1399FA736EA759B97E31FE70E94BA6FE656B564578DAA3B3431781A939B9D3879AD6403C655A49EB34E794A5882DD36068336511B3A1C9000AA00074DB2B9158121176E55483DFBE52665CB8C6C29DF5C301B361E22A62C6E6766AF235C9B02104B38434069878583FB10FF9C653CBFE71BBFE71F1BC7F2D7CA67FEE4F6B9B087D23DCD079BDBF248762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D8DAB09F719801BD8DDC8A4849EC77CB2294B5C736AA9CB1531B69285636FDADB0A19143FBB5AD7A60554F53D45627B602942BA77F0CA8A143D4DC01E38C0D154FE02C621E19719288A362069F3CACC9B045308E2046FF007E5664D822A7731064000E991253C2A7A7DB7EF3A64789908B2BB5B6AF30456B8F1331143EA7A627A65A831E26C1160A6CF94D41DB6C04B6C62CA746D3099936AF8E5722A43DA746D302F134DC65132CE01E9BE5C88ADC5076398F22E7630F63D1A20F22A1F1CC69F376388507A3D9C3C19401F4641B87366D610D29B6F8B6C53A4401C7862CD31B789CB123A65A392A610C3273C2AC8228C71507AD3155EF18A53BE2AD32EDFC715413AEF8AA19A307AFD38AB696D19EC36C0555BD140360065549010B22D180F7C810C8158401F3CAC86624B3991B9391E16C8C9DEB0C4C5B4168DC8405ABD3299452C72F6F1199878E6398A6AD875ECAA5C90054E400A4F0D2053E371B77C3C48643656C6B5A6D4DB1E254E12260BB63C4AB5A195C30F0C902DB89239AD253CBF56499C9237B57491B6A16C5A24995AC0C083DF16892730C8D1EC72405A225182E881D70F0B2B51376A49E477C7856D512ED36DFDB1E15B649A15D1127C3FEEC0CB4FA0E6660D9D6EAB72FCAFF00F9C90B196DBF342F66238A4AC493F7E6E74E5E73561F394A156460BF66B9B2076745946EA7FC72A996B886BD42A72AE6E643925D7331634C980892493DCFDA51DB6C980C4A56CE49D8E4C06A7F645FF38C1FFACD3FF38F1FF9ACFCA5FF00746B4CCD8F20D079BDCF248762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D880E040E87B66006F492FA2F88803AE5914A1C5B7186BC7E2F1CB108781585C46CDBAA9C2ACB15E3684803738154955A35663F6476C05285594BC9C6B55AF4CA8A157D052E2837C8F567109CDB3AA808D8993688A6B0C44B0A7D93DB2064D8229EFA01635A2D09CACC9B0456081581F86A720649E15D6F6AC1C7A6BC71E26422C9AC6D242D535A63C4CC4532BFD3CBC068B534C789988B036D34C731F82849C789B04597E8765C654257204B121EB9648B18400509198F32CE019C688A82652A286BB9CA497371F27B068CA81D582D0F8E5122E7E3E4CF6CCF29D2BD3C322DA39B36B4AF3F84D17B62DB14D56A5B03364368E811011BD284E5D1E4A9B200402A389F1C2A8F49102AEDBD3738AAE3221EA31543927715DBB62A82949036C550F473FB471556457A801A82BB9F6C555E458A31549BD4F639021925B35DB80CA2DD189E8DE190216D45EFA38AD889621EA53EDF7C810C4C92A4BB8DA29A46958B28F8129D72206EB19A0D2EA607D53C9D3F9299618B68CADB5ECB7069E87A71D3E2343944E0DB1C9691DD7EF83F01C58577198F28B609B1B78A560C492483F6B299C68365DA2ACEDDCC82BBE55C28675676A0AA80BF1531E154D52C588E98F0AAE5B1707A75D8E4A21B71A12EF4F2A84AAD0F8E58CE4C4E6B26939A96E2F5F84E2D1256787D2B16319E52C42AD20F018B449E6F79F9A5E52B099ACF51BD4B6B984F16151BE5916B2692D93F387C822A1B5B2BF223254BC4BED3F363F2FAE24A0D6D9B7DEB4C697899345E73F28DE11F53D5D2A7A02C31A5E2665E5DD574C9264F4F555256A428208DF2FC45C2D46EF833FE7278423F31E36DA482653507A13439B7D39741AB8BE3F6565BCB84635556345EC01CD903B3A2CA374422214625457B65536B8849677605E86832500E4C521B8B97527E3CB00625262EEECC791DCD7260312B94E4C06A7F64DFF0038C1FF00ACD3FF0038F1FF009ACFCA5FF746B4CCA8F2683CDEE7850EC55D8ABB155AE683E9C554F97B62AEE5ED8ABB97B62AEE5ED8ABB97B62AEE5ED8ABB97B62ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88BB53C866006F5971082E0D3BE5914A16E00488FCB2C42550D19E83AD76C2A98DBC844BC3DFA6054E2E29F56603BD3014A4515564CA8F3423C5C0420F7A65726DC7CD745704B8232B25C9AB6490DEA22A6E2B5CAC96C8C593433ACB1AEF9025B04535B4B70E188A1DB20649E14FAC6C55985464789970B281A7848C328C3C4D9108EB6D3DA78DAA2BB63C4CC063B71A3137205325129AA4EACB4A68DD6829824C4866305B32B2023B663C8B38B35D1A03EAA6D9492E6630F56B106109951E6E6C59D69DF100D81B073673A6FC4AA69D7BE2DB14E917E3C59A7D6B6C48461D0EF96C792A751A711BE155C57DF157703E38AB5F4E2A849BA62A82798462BBD71541B6A616B5AE2AC7F51F31D9C1192CC63A77C910D8C02F7F30F4BB763CB520A57AA93FD99590C0B179FF003C3CB167270BABC593DABFD99590D04A47A97FCE48793ED2A91852C47C27DFEEC111BB59953CAB56FF009CAA82DEE78DAC21A3AD1401FD99710C2390DA5F27FCE4A6BFA821FA8D89E0C09E407F665120DF1C858D45FF00391BAEDAFAED776C556BDC7F6663C839519BE91FC9EF3EBFE61426231805D4B961ED954837C256FA2AC7437AA9E3F4655C0DCC9EDB4C78CD48C3C0A9DC160481515C1C2AACFA79A834EF8914DD8D0379A79E1F7E06526177962A03963C585787BE2D1248D61963B1D453ABCB1B08FE74DB16893F32BF313F293CFDA8F9AB50BE8209A5B79A4668F8F402A7DF32314801C9A64C462FCACF385B6D73A4CF2D3BF13FD725B215D7F2EBCDF34CB1D9E893C751BB713D7EFC7657A0F977F243F30A7991E4967B343BD09A7F1C695F5FFE597E526B1A5DBC6DA96A0F34CAF5662D5D89E9D7251E6D1946CF1DFF009CCDF253680DA3EB11356474EBDFA1CDAE9CBA0D587E7F593C9373965D9DBAD73640ECE8B28DD16A7E07F1ED954DAE218BDCDC0124895E8694CBB1F26F88D9239EAE4E5A031921F8D065803595226876EB9301ADFD94FF00CE2FFF00EB347FCE3BFF00E6B2F297FDD1AD32F1C9A0F37BA6143B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D8294F845330037B52D4B29AF7CB2294AAF6BB8CB152D8D5830A547CB0B13C930854870DBD7C72122C2253953585ABBE54496C4A1BFBC6F9E441552641238567283AD7F86322299E306D98F967CADABF98AEE2B5B3B62C8C69EA01DB31CC9D8E385BEDAF227FCE2EC17515B5D6B32155900A293FB5BFBE5129B9F8F102F43D57FE716EDA2B988E9CF5403EC76FF89663CA65BBC10F25F35FE476B9E5FB932C687D193E1555ED4FA72066C7C10F3E5D0750B0B8F4E5571C4D0D7009A462A6616D632491014AF11BE59C4CFC20BCD9DC4428B551ED931309F0D012DB49CD59C50F8E094BB94C02636B1134F1C8711699C53368A452A791E9B60B6BA65DA0C94750C706CE442DEA763229E15DF289F3765879334D35C55476F0C8B7B3EB023E103618AA7D18AB0DB02DB27B16F8554F6197479330980DDC006A3C30A551862AAB18046E31545718986C82B4C585A57716ACD5E228322B691CDA75D716F8493BD32B2516F3FD674BD4DCBA4523A73040A1E99124A5E3BAE7E4DF9C35F467835BBB8977AAABD3F8E4F88A402F1CD4BFE71BFCE6F3D06AD732B576ABF5F9EF82D9D16357DFF38C5E757A97919DFF0098FF00B78B218D8A5C7FCE3179D43F22BCCAF4AFFB78F2639310E1427FD0B379CC48ACF6EBB1EE3FB720723891C459FF00973F203CD715C436F39F4A0604391B536F9E4253722185E97A27FCE37581BC59757B812AF2ABC2C6A0EFE15CA8CDCA8627D4BE51F22F97BCA10C49A3D9C5048ABC4B462808EFDF2A9CB67271E3A2F52B68AA828287C72102DE629D5BD9B4AA07877CB835909BC569E9815CAE4C0AAB428C0D08F837CA81B402424B733060D18415E95C2DC0B0ED474F6A973B835A0F0C5B0C424F1C216A196B4C5A6710105736314EDCC205E1B103BE0B21C59C2F9249756B69103CE352464F88B5F865298A6B78A5A450278D699602BE19661653F35154070DAF8659369F7D0DA144745FF4821547BE46CDACA1B6EC37FE7263C99179DFF2D64586D926BCB088C826A55C0E2C7AD7365A693A4D5C03F119A136BA9CF6922F131C8D191EEA48FE19B8C727439318B5194888DD21D890787B6DDB26402B080616EB44AB6EE6BC98F5C9844A21047ED1CB034C821DBBFD3930D12438DC9AE58D25FD957FCE2FFF00EB347FCE3BFF00E6B2F297FDD1AD32F1C9A4F37B9E143B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF889B535CC00DEAF28EF9645528BA1C8E58AA10C60B8AE2A5181789C045B590982FF7669E191314829431FDE36532D9BE22D52D2C9F52BE82D233467351F466365C941CCC18ACBF49BF21FC950E9D6105EDCDB2BD003C88F9E611CAEDB1617D71FE2ED0B4B8E21385E286813C0E512C8EC71E057B7FCCAD0EF6EE386270A76037CA2591BCE165F7369A4EAD6CF2CC527122FC209AD32BF111E03C475DFCB0B2D424966B78946E69B63E22F80F329BF2DEE609654856A17ED532D195071525571E49BB86BCD0ED93191071A437BE567442CEB4206DB6113B6AC90628BA7345395F7E98789C5945329ADB8462BD69D71B6BE156D224E33818F137462F48B49887515EC32B917618850679A4C84B2FBE06D7A3E9CDBAD76C5594DB8E4C3154E6D5C89197C32D1C9984DE13571852886FE38AAAC7D3154C228BBF638A6959A21DC74C82D21999882380CAD692C9ECFD53BA0A9DABE1919288EEE8ADE6B65F4924EA29838DC8F0D092DACA8793C8013D1BC31E36631A5773F003CAF01FA4E3C4D831B1E96F2047DEE86DEE72139ECCBC2B496EF568492A9722A7E798A7228D3282B5C489EAC373561D1413959C8DD1D3A274EB7964B80D2C859ABB2E40E46E1829E8D6914CC1226B6E117FBF48C8F1DB2963A65B6D6E11176AED96C0B5109D4238A82075CBAD810ACFCB8E5732D6425CED22568763D72B896043A38A39012454F8E49B6297DC5AF21257A0FB38B7249FA3958B6DF4E2D3916C1A423ACBCBB1DB22538B1F131ED53CBE8FCA87AE20B6F82C364D0258A60231C93A93938C97C047C505C5BAF43B6596BE026B6B0C976D645BED452127E583AB899B1D3D3AD2DC6ABA76B1A6CC39ACF6C63507DD48FE399D84D3A3D5C2DF87DF9F7E50FF0003F9EB50B20BC6970CF4A7F3B31CDB6296CE872E3A2F1CD4855D641FB4BBE650DDC7E4C4AE362C3C32C6322969FB67241A64876EFF004E581C7921C753963497F655FF0038BFFF00ACD1FF0038EFFF009ACBCA5FF746B4CBC72692F73C28762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF889B360BD730037A3E4A1E9DB2C8A529BB42C76CB10A50AF165A8A625211B55C622D8494E52786C77A8C998ECD60EE83BCF8AE9388A506F4CC2CFB39F845BD1FF2CB493A96BD68047CB8CAA6B4ED5CD5E59DBB7D3E3DDFA93A3D8B69DA6DABA0F4D1211555D8663176D8A2F2BD72D751D7353962B3321084B1504D32893B1C71D952DBCBBABE9EA26657046F5EF98F26FE009EE8DE78D5B41BD1CCC9344D45911892001DE99595107B868DE79D1B5B31AC92FA5310014069BE36930658D6A8E1DD40E128F81BC72C69945299F4A421AAA0D7C46581A0B0DD574359012117A1ED9312A6A9C5E51A9E83C2524201BF618789C69C58F5D69DC410476C789AB85274B5E126DF0D0F518F137C22CBB4752028356DFA9C376E5C0507A4E974053B7F98C2C9E83A6CA15803DA98AB288E7008A62A9AC2E480C3BF5CB63C998E49A42E69D4E14A395BA62AACADEFB62A9BDA5D536206D8B35F7373CA945A5720B4840ACDD09CACAD221232BD6BF3C84CA62375D2AF53D0E637139A12B9622FF0D49E5B6F8789B404A2EB492CA69DB071338B1A9BCBCCEE47106BED919CB66D88DD47FC2E3FDF43EECC4B6F115787CB8D1C8A40200ED902590A661A6E841543155A8F6CA8A788271E97A478D6A3C3082899B08E801D854E5F02E3109AA82A0153990C4869A471EF954CB590A2D2B32B0200C840DB54C25A4B89050903C32D6714C963F5A3DCD0A8DB16E4B64B295492010B5C5A72289825D8455A7ED655334DDA734BBEA32CA28687E782DC9E2583494460B201C9B71F2CB014F10448D060906EA3EEC9028E25F1796914F258FE9196C5C4CC2D36B6B45B53B2B03DC8EF97E39EEE9F518DF9A7FF0039C1E5223518FCCD15B8F4EE295217F9797539B2C32743A8C6FCE1BE9B8832EE55968066CF13AB9ECC50124B549CB4B594249D4E48354940F4CB038F2511D4E58D25FD957FCE2FFF00EB347FCE3BFF00E6B2F297FDD1AD32F1C9A4BDCF0A1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88089A86B9801BD338CD40F1CB22969E30C32C420D8713B6291CD6D4E4A00B1C8B58D46E69979AA681CD40FC7331F019ABD4BB4D353E9EFF009C74D21751D692A952B535A78669C9DDDDE101FA397F6CB6DA3D360563A01DF2120ECB0B7F96FE5B8F503777862ABD08AB0EDBF88CA661D96309BF99F4A3043246918E9D8663C8373C0752D0F50796436F096321A37C35DBEECAE99458DCB65AAF96E48EF4A4B50C0D02B78FCB101127D05E48F375D6BB6612E418CDA850BCB626B4F1CB0071E6CFEE66658C7BE594E34C149A525D1AA3AE46450030AD4EC83B31A6F91DDAA51613A958D0F4A6DD71DDA845864B6A56423EFC6CB7C629EE92854283D6B9747937067DA79A32E4959BE9FBB8FA31564A83718153C85A8A9F2DB2D076660A6B136DBE1B4A354EC3F1C2AAEA4D3154742E148AF862DC119556C8324C6DE256A74DB2B28559630B95CF92421D979661B900A87A7BF4C0CE25B30F2ED8B60696C4160481B74C05B224DAAFD487F2D7E8CACC5B812EFA90F0032042D9575FDD0E23E9CAC861C454DE124FA9D7DB214CA3225673F4CF4CB62C885C353111AB6E3A65CD65506A91C9DB2120C0B6F771D36DABD7238F9B44D0E675AD69972628BB797D571C7609D716F64B58A587871DE98B4CC162B7F21D31F8769872194E45C7612F87519A46A274F6C886EE229B44E646066D9874F96482F116416C22E23A64D788A756F2AAC4151030AF5CC989D98917BAA344D37488610E0E78BE6AFF009C9CFCBF4F347E5EEA73BC5FBCD3E2661B54FD963E199D824E83541F83FAA5A1856F216D9ADA5650A7AECC7B66EB09D9D0E6D8B0FEA58E5E5AC1D9072F7DF2516B9287ECF5ED96071E4A2A773963490FECABFE717FFF0059A3FE71DFFF00359794BFEE8D69978E4D279BDCF0A1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFF00FFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87EB35F7CC572138A74DB2415A634196054B67900E44E487355B1CE38D2B96EC8282B9AB74F1CA665880A912F22CBDE99ABCE5CCC5B3EEAFF9C4EB113DD988815E25AB4DF6CD69767A626DF647999435D5A5BAB1D880403FC32B93BDD3F37B97962CD6D34832280ACD0EF414CA245DC621B30AD5A77B849A302A6A77CA2459494B4AD209812478C115DC951FD32316A1CD3AD5F49D15B4D91DCC3EAAA134655EB4F964993E64BFF300D235631DB30085C8F82806C7DB1453D5F45D7E4D52D54D6A683F56147084DE0BB3C9C4B4DB619645A728A49EE7799DFB1E992A68A627A944199EBD4EF8D2D30A9AD944BF3C6928AB440B28A61566BA79A30C559A58BFC43E8AE2A9F46FF10C55388EE00551E191BDD04A3A3BAA8EB8414A363B91B6FF003CB1B02312E47CE993661328DEB43E3DB16E099C4E38FF000C82A2606A656528D2A5C0A75C84D5BF4CE6310DA1A2877C890DB169537E99590E405754DC572239B656C98C511A74AE4E419C10D7111A1F7CA64E4001085780DFE9CC793090097CD7263A953F17860873681CD26B8D4E58EB5A6F97AC92A7BD9A405877E9938B4C9AB7BC9D09CB29A095433DDC8E4AD7010D68F82EEE0515D6BEF819C5368272159E9C69DF16F82223F307A4425457A62DD1016DF39D5F871FB4A28299092C804A6386EB4B928C84A9EE722C53D86EA3640CFF0068E2A98DBCF0D462A9EC13D221C3ECD76CCBC5C94A6B67744115CB1C2CE16F9AB4B1E63F2E6A1A4900FD7A090014FF00248CC9C45D06AE0FE76FF3A3CA4FE54FCC6D7347652AA9348C05283AB1F6CD96293CFE68EEF03BA6FDE483A10732E25A2B648E7152773BE5C1C69F35315000AE6445A96E5A036C407F661FF38BBFFACCFF00F38EDFF9AC7CA3FF00746B4CB43833FA8FBDEEB8B1762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF884B31D2B98AE426E47C3920A8694EDB6F9605482ED982BD01AF864952E86670D435F961B414D50730396D909A0370370B8907B7539AECCE5637DD7FF388F76A359642E05617A0AF7CD74C53B4D3737DAD359B5DEB30730410C280E63C9DEE9F9BD5EE3513A558B2745F4F8E5327718B9304B6D4629D679188EA69BE63C99496DDF9DEDB46B231B952D355547707C72316A1CDE15E63F38EA77F2C896933047A8A2939364C22DF48D42E6669E7E72173553D7157A56853DCE9A1622080682A715672D70DE9A3AB862FB900D72D838F991729E56E186EC7A81D726D2905FC25CA9FF277C558B5CDA9049A6D8AA95AA71931565567B32EF8AB2BB37A11BEDDCE2A9E46FF0010A9C55358CD40DF21D58946C792641191E58D811B1D6993661368998003C316F4D6273415E990423918D4532B294EED8065DF6F0C849513C465242789A2828720437464B553C7AE4087263255A002B4E9910376CB454520E3D69ED864DD0536219C0EA3C7289736F082BB1C50D3AF8E63C98C986DE4AC3950EE0E087368EAC3758BA99281493F2CBD125897130B346FDA66A11938B4C9111C92B15A6FEC32C0E396596B6D298D0AA924F51896B45C92C36A959402E3AAF7C8B6458E5FF99A0A18E21E9F0AF3AED8B7C5875C7984090BF30057662716F8A79A379A8C328726A95EBDB2052433C9FCC16DA94694515EE46458F0B514913505413E0315E14EED0C408E5F0FCF6C53C29D2CD1A2715229D46F97E33B302AD6F78A1BA81F4E4F89AA70B09B5AEA9CAE91030658C153FECB327149D36AF13F22FF00E7387C9B168FE686F3446800BE6259C0DAA6BDF3618E4F37A8C745F9A7786B7136D4F8B3638DD7C85256E09ED9787167CD408A66445A9665A1BA2FECC3FE7177FF00599FFE71DBFF00358F947FEE8D6996B813FA8FBDEEB8B1762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF884B33D37AE62B909B337C24788C2150E57E1396854A665FDE0C4AA4F29A4D5F7C802828E0F545DFC36C24A02E97EDB53C33132072B13EABFF009C5AD5E3B7F345BC6ED40411BF8D735F986CED34BCDFA88F12B6A36578375E20D730A4EF74FCD11E719DCE8F2CD16E6BC49CA64EE317278326AB710C14E5BF2E998F26526317AF79AA5F3210480A29918F36A1CD97683E45BBB86599E33C4D0D48C9B27A641E5FB0D360637340C47C18AB1BBFB6B494B7A43A74C550FA4A869DA222841A0CB60E3E6667058D09A8EBBE4DA503358061257F9B154A27D257D36FE98AA4034E546229DFAE2A9D5B5B22815C5536854290A3A0A62A9E47D302A2524A151E191EAC4A670CC7EFC90649847374CB1B022D26AE4D984E6396A06FB5316F098C6D5A64109A44C2A32B294D216A50D76AE4648298FAAA76C890805BE55CAC86D896F9656439112E26A322DD12A65C0EFF003C8C9C9C6D7D657A65126F086BA9818CF8E63C98C983EA32F10EC3C7A6087368EAC1AF2F19A4A7865E8936B7C562A1DF261A648ED37518849CA4DA996071E48ED7BF31F4AF2D69F25D4AEA0A834AFB62583E6AD6FF00E7226C2732CF030660C40A1FECC8B38A3BCB7E7A8FCCF6F73717527A224A7A5BD2B8B7C53237D1CC5E2AF348F7AE2DF1627A87E63C76529B0869198F6A0EF902D8032DF2B79D6FEEDC0E478B7BE4534F6FD0F5096499096DCE2B4F4225E5E1F175C5691261936A4DC76E95C9034D13E6EF425FF968FC71E26C10B08CB698D9D02B739240791AE64E29381AAC3B3E3CFF009CDAD163D5FF002CA3BF02B25A30634EBB54E6C71CDE57578F77E2BCF2195DE43FB44FE1B66DB0F27479051428EA3E79921C39A1E41B9F9E6445A90A577CB83745FD98FF00CE2EFF00EB337FCE3B7FE6B1F28FFDD1AD32C7027F51F7BDD7162EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87DB371502A2B98B4DF69C13D01C90554E3B7D19312094A275F8FF005609482B1EB8DA6F964225055E3625900049F0C9A014D110167327C238EC4FCB31F240B938E41EA3F935A8FE8AF31DB4AB2846332802BD413D7303340D3B3D2C85BF5FF4FBEFAC6876376AC1C7A43E319AF90A77DA722D34B906FF00CB573DDC3120663C8BB9C4367CFB35ACCF70B0202CDCF751D466392999A7A8E81E5AB7F53D69286408A48F0C62D319025EA16D3456B0085221D280E499DA4B7BA7C9A99268691EF418DA5E5DE60BA83439191E550D5FB24EF82D52DD0754FAE6A30B28E20FE396C0868CA1EC8564540C632011B31193B684ADC1624914DF738ADA94F1A985A8C09A614816C6CDB92C4D3BE282295046CBD01C55150EC77FC7154F22208EB8AAB2005AB9162533840A75C21923E351B64DB0146C69B64EC3208A89CF2A5698DB784EA17D86F90B54D616AD28722529AC6F414272242AA2CC01153898B0E0368F4DC546FF002C818B6C415F46F7CACC4B9116B953A9A7CF20634D91284B89000486195483978C84A4DC7C62AD4F7CA241C805B966AA1DFB65120C64C33569F843235413D86463CDA3917973DEF2BA615AEFD32FB6264159AF9A12ECC85815D864E2D5229249E6048924DC230AD01EB9686821F337E6C6A3ADEBF6F1D8D8A4A07A8DCD97B838086145E59E5AFCABD62E21ADDBBAF26E546FA0F8E459C43E91F2CF96E2D234B68AE18C92C207A2ABB9F7C69BA3B2AEA725EDB594AF65138918100537C5B8178FE9DE5BD66FF547BABE57ABBD547B64241B4483E92F2CE8A6CE18A886A06E70527883D934189F9AB73E86871A5E20F4069278F8904D3C7052F105C6ED89AB392DED80DB44B9B5F5BFF29B23BB91090554BE113C4C5AAA55AB5EDF3CC9C4D3A88D8D9E47F9E9A41F30FE5579801AC92C51B32C23AD02935A66C71BCAEB31905F8377711867B8848A18A46047FB239BAC3F4BCCE7152418076DBBE65C5C19858EBB9DB2F8B4A1580CB04806E8BFB2CFF9C5EFFD667FF9C76FFCD63E51FF00BA35A65A393813FA8FBDEE9858BB15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF874B265328CA593246507853C72259C513C07139596D0935D28562721D5279311BC929375CBA1C9A24BE0B90268E872F8F26013B9650F1EE72126FC68DD12FDAC351B5B98DB8B472015F6CC2CE36761A73BBF5CBF267CCA9AFF966D6D24903148C0A66AB287A1D2C9EF3A5AA3C9FA30AF10FBD735D91E8714B6615AEF96C695E6084C43E0998548F7CA29A739B7B3E89E598CC6B3247EA19D0034DE996403878B9B2EFF095959C5F5ABB70AA054A9C910E482F39F3AF9B340D26C192C028B85560E57B900D32B21982F8EB53D425F345F4CD2A91463C09F9E565B014C740B2D46CEEC39AF18D870F964E0C66FA4B4DB8379651ACB27C400D8E5B17166956A21E262886AA456B9634F54B2332383B6581C8835E9301B8DF0144D690475C0C694FF6CE2B48F85C01F2C569188D520D698AD2650B50628A4D633D314D23636DBE58A8B6D1C73A62E60E49A44FD37EF8A53BB6906D8AA64241B6298F36CB0241CB9C9149B5ACC1471AD2BB656BB2600823AE40ADA1A7038B37865534DA493C828731CB740A4B3C94AD3AE51272E054DAE1BD322BDB2892C8B17BEFDE73E7D32B71E658826968D7264A7535C21A2D327D2BD40401D475CC88AA42FE528A69099146F99015747E43B157E7243EA03D0787E38C92194DAF9574C8900FAA8DBDB20C9CFE5AD3CCA812D40F1DB154F57C91A75C45536C3953C3FB714A4375F9736C2559628F804EBFE75C8948445AF97520223A74C0A9FDBE962CF7036AD4E2A9DA32BC746C550727A49B03912CFA2025B845DAA3DB20D40EE827B9574702868466463E4E55EC93F9C6E3D6F27F986253F11B4947FC21CD962745AE2377E0879810C7ADEAA87AACEFFF001239B6C3C9E3B55F594A17F8E67637026B24077FD796171D02EBBE5526E0FECABFE717BFF599FF00E71DBFF358F947FEE8D6999D0FA47B9D7E4FA8FBDEE99260EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF861D3DE432034DBC72964CB565278537DF2259C51C6600152C2BE19516D093DDB13CBDF23D5279308D403ACA3623C32E8726892193D65742548F039911E4C03258F9B415E276CAE4DF8D49039755A94A1AD7E5989945873B0BEE7FF9C71F39AD95CC3652DE0EC3893F3CD7658BBAD34E8BF49F4D9C4C12F512944AAC9E39AEC90779872A6B75045AA442EA420CF17D95EFB653C2CF24AD9FF94B596B3B6953D1FAC49C6853BA8F1C9014D111458679F7CD173E94886630F20691FDF890DC0BE62BB6B9D42E984E8E6366F858F7A9C810CC1645A779360216644E04EF9510CC1640DA298978C49CDA9BD30C42C98F1D46F6C2F56DDB946A4F4CB22E3CDE84B492C83BB0666DF2D69EA81876276DB261C88372321620900D3A602C8A0E414DFB604521F73B8FBF15A5652C3A0C5691F1F2A2FEBC56933849A5295C569348C9D85715A46AB506FF004E290162960DD3E9C5BC724C627A002B4F1C529DDB38DA8698AA6AA4F615C5239ABA1EE72E6DB541280CA396F5DB2056D324B903E127E2CAD6D7CF23189FC29D72A9A82C7AE255E81B7F0CC72DF0293DC39505A9D3289B95028069DE9D0D3C7289A494AAE999D08A6F5DF2B699157B5B55600D37C21A93AB6B3ABD38D76DF3222AAB3D8AEC547CF3202A360B3429B10580F881C64C8223EAE9F6792FCB20C913069A240EC08057ECE2AEE735BBF02E062A8EE64C4C18862DD32254242E8166AB0A6F812A978C161AFE38AA412DD98E062AD52062AC4DB54B862C0D46E72126CE88492FE43B16A6443472288B5B8E714A7957A664636F32F4A035894C9A16BEA4D57EAD257FE04E6CB13A0D74DF86DE6D503CCFAE85DC0B96A7DE736D8793C9EA0DC98F053E199D8DC19B9CAD3AEF96171D06E2A76CAA4DC1FD92FF00CE2FFF00EB347FCE3BFF00E6B2F297FDD1AD333A1F48F73AFC9F51F7BDCF24C1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFF00FFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85DD35C96DF2964CB6023E1F9E44B20BA47FDE1DFBE5526E081BC7A2935E8323D527930FD45CB4887BE5D0E4D1253673CE1073223C9806516854C237CAE4DF8D12618E343336FDB31E4E66366BE45D767D175482E623455606BF7E61E58BB1C137EB17E5579EA1D734AB485E60D23280457E7EF9ADC917738723DF2D678ECAE638E75062946C7B6F94F0B9B1DD1535D496978CF6BF0C53281B63C283B21750D223D6007B800D7C7224282803E4ED387A4428AAE4086C05192E8F15BC5C621B814C810CC1632F697AB3858A22EA7A90322366576C7353F2BDE5CDCA4E632287241A26C820B0921B658DF7232D691CD4A5B431C6C476C9872209210DC8961BE02D94A72313B605E15200814C5695D6BBE2B48D4278A8F6C578531B76E9B62BC29B420938AD261E9B1FA714D2F119FA462D815D63DE98A53680D0818AA6F149414EB8AAE6980D89CB99F12D5605948EC6B5CACAF12A7D6292EE77C815E245CD755888F11954D416352CE7D43ED98E5BA086B89B92F1F1CA66E5C4A5D24C146E731E4A4A57713165F8776AE56D5229B69DCDA95C218B2AB5421AA4F514CC88AA37EAE5F71D33202A8C917A2373C6BB63264103FBB06A64A78E4192610DD451A37192BE3BE2A96DC319E4E4B21A0C551714AF1B202D514C894847CBE93A87D8918158F6AB72681074A74C55894D73C636538AB16B9999559972326D1F4A4C2EE477A1AD322E393BA716F70638580EAC4664636523B286BB2341E5FD79D8F10D6B21DFFD439B1C4F39AF93F11BCC8E24F30EB0E0D435C3107E939B5C3C9E6729B293667637126A2CB5AE5A5C7502BBE5526E0FEC8BFE7183FF0059A7FE71E3FF00359794BFEE8D6999D0FA47B9D7E4FA8FBDEE79260EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85EB109D5771E394B24EE19497451D3950E44B208D724C840F1CAA4DC1037B50A4378647AA4F263B7B18AC6C72E872689206EC51A2653D32F8F26B08F82E48A440FEF295E39093918D3FB23F595759AA225EA7FCAED94172E299412FD5A2223FB75F8328C9172B1CA9F48FE507E663685756D0DE5D985C90B1A13B577F7CD7648D3B4C3983F4F342F325A6BDA35B5C3DC235D7006300824FE398C767698B202C860D46692358E54A18B704E0B05BA62D35B6BD77DBA0C890C63128A79D8107964086D102AC932B00246DBB9C898ADD2296EE2B643E8C425E5BB31ED95914CE3BA167D4A2991B946108F6C431940B13BCBC049111AD32624D3E19B4ABEB13382AC2993130DD114974E3892186F8DDB6092041DCD46D8AF12E6EA4D36F1C57897091456A715E2565993615C578C2656CEA69EF8AF104EEDA4407738A6C27B132151BE295540189A62CD1023351B7D38AA24232D0914C55148DD29D31559373AEC32DB544DB9A0F8BE9C812869C7C7C874F1CAC94B6F229000395CCDA84AA68DB996A6DDCE63B7C36414CE95A577CA66E540A45792D09DEA331E6C88B41C2FCE4006E7C32B6B9448DD9259C81283A64C45AF8939FAD3715E0475DF2E0BC4135B7BD2A9566CBC49788295C6A1137DA3CA9D4612CE26D8F5F6A9044A5AAAB4DF761FD723C2CE982EA5E7AB3B105566524D796E36FC71E15A63C9F9AB6B1D47A8A5BE63FAE34B4A4DF9AE92238128592BFBB507AFE39190544695F99324B308EE25A027615FEDC8D26DE956DA8C1A8C0662D56E831A5B63BA93FC7C23A927B531A543456723C3FBD4A313F86464D8262A90ADA70462426F91693024ECB5ADCAA0929448D8733E19763906194F0879FFE736BA343F26EA571EA7A69776AE96EDFCCC54803EFCD86298797D76404BF1AE617524D2CB74BC6791D9A415AFED1CDBE290AD9D1CE24EEA7C18F419990C81C498A5AE8C06E2872EE305C7A52E058ECB5CACEEDC1FD8DFF00CE308A7FCE35FF00CE3C8F0FCB3F290FFB935AE67C3E91EE75D93EA3EF7B964983B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85BD3BEC652C938B6FEF53FD6C89641399500ABE5526E090DF3B32B11D40C8F549E4C7AFDE4E31FE397439344905217668C78E644793584C2C406BA27F9475CAE4E4637A6796B49FAFD95DBA0E455AB94D5B951E4C7EF266B5BC6B775A50D304A2CC4D7C26EBD7496194C7E97C48D5EF985931B918B23EA0FCA5FCDFD5B48BEB6B6D4EF19AD9481F131A537F139833C4EDB06601FA51E59F3FF9775DB2B764950C8CA3910457F5E53C1C2EC6192DE8D0C7672422789AA08AED91B72E050F34C84A88C74FB59025BC6ED2C8DF3F0C899354A289496A8CBD2BDB21236CF0C50922D011E390A6E314BA4B22416A634D44255246633BF638D3021033905B7F0E996C5ACA0682B5C92AC734247E18AA8337BFCF1572B74FD78B594CEDA6A0A62D914EADE652462CC325B6605462D89B4282A3BD71668EE031558ED8AB68DD3C3155772A77C9AD2989950100E56534A46707A9C84969A1254D474CA8A40DD648F5539516D8A4B350354E5126F821258048390DC0CA26DF14B638945C71E5C4D3AE463CD728B8A6358E05E4641B75DF2C70F850ADAC5B16E0660BC4D6B5C9C53C0D4FE71D2ACA2224B95A8F7FEDCB578581EB3F9B1A3DAC4FC640C4D42D3C7EFC945B2029F38F9ABF379DE597D19DD63A9E95FEB926C7906A1F99CD71EA52E9AABD4127FAE2AC557F301DE46ADD356BE3FDB8AA656BE75BD915E4B79CB2A1A36FDFEFC8C9059D7947CED24B7F18BB6661C803D7FAE455F61F973CE960B68891EE0EED5F1C559347E6182E6752B106A9C55939B95940658F8823A64486A3CD0CEF5AFC19590E4C64A2FE9B2FA4C2866EDF2C9C22E0EAA5B3E40FF9CA7F308B7D046911BFC71A9F86BF3CCEC71793D61DDF9A864E681D8FC4D527375861E975929521B91AEDE399020E26492E33A13C5B7236CB40A68B4D2CE28A4EAB9600DA0BFB03FF009C6A017FE71CBF2000E83F2DFCAA07FDC22D73363C83AFC9F51F7BDB324C1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85CD3C715A1EB94B24E228DC303B75A8C896514F44323C058D3EFCA8B70499EDCB24B5A647AA4F2492F638CAAFF91F6B2E87268925A5E07F8D2B48F63F4E6447935854B3E4B73327ED15247DD95C9C8C6FA0BF26AC66D5D2F6D22512486A287A6541C8E8C6FCFDA1A586A5710950B75093EA0ED936B32A79F192E1220A4D2A39291954A1698CD562D4A485158BB2C8A762329389CFC59DEF9E40FCD2BDD0960334F2B46E42F52694FA7313538A83B3D36A1F767917F3C6C350B782092EC06A00431A78660183B5C595F44693E60D3F558D7D39939103BF8E53214E7C260B295B66E21832303D286B94CB666775DE8488EA781A1F6C31DD944D35246EC6A23603B922992A64648A11A98B8820B569418D06B24062FABC2D683D4996887A531A6B25884B751B92C84D0F8E16050F57FB5514C285BEA57BEF8AA9B9F7F962AEE42837C5ACA2612716C8A3A17756152298B30CB2CE71C455B16C64115C28A55A9418B3470B943D0E2AE3206E877C55B57A0F7C5548CFC07C677C9B6D20649CB37C26801CACAAAC659B71D32125562DC76AEE72A2AB7983515DF2A2CA2955F3D15941F88D29944B937C1A8A4021F8CEF4CA26DF1631A9DD7A3C9D1A86B4C8C39B29724A9EEE49E3203354E58D54C4758B3D4562F5226DCB5363BE4E2B4C2A5D0754BE722494F1EF56396AD05A7C8B6654FD61CBB1EC7A64A2B4935E7E5C79767564907C4C3C324AC0F51FC93D2262CF6E7883526BB62A935B7E45D8FA9B9A927AE2ACCB4AFC83B510CCFEA705E42A323241677A0FE53E83A5C81A62A597C722AF51B0F2BE90CEA96BB1514A2F4C55153583E99708A895DF638AB2386FDFD3459051FC3034CB9A2D6E19876C8F0AF1D34D342CF1CA4F1FAA2B35D7CBAED9918A3BB81AAC9B3F327FE7257CD967AF79AEF2D74D9DA48E0E4AEAFB6E2BD33638F1BCDEA4D97C82C5D763D3B66E30C7D2EA66511000C4572DE4E24CA356D6126BFB44EF86D880C92C2CE20061E26F8C5FD757FCE3780BFF0038EFF90AA3A2FE5D79580FA349B5CCE87D21D764FA8FBDED19260EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85BB6F848AE52C9904352B5F0C896514D3D7E36C77CA8B704AE3943AC9BEF91EA93C921BBFB2FF004E5D0E4D124854D2198FF94BFAF2F8F2601358471B966F18FF0086424DF8DF5BFF00CE24D8A6A7E63B9B6715564634F7198F234E544585BF9F9E5A934FD775396342055B7FBB1136B9C1F3100C614E4370B96036D06C20E54F8078D72756DB09334D247FA1C629B9EF9467C761CCC597853D827BFD3254B8B6BE642A6A141CC09E27698350F58F2A7E73EBFA4DC470CF3B988951CABE14CC49E276F872BECDF23FE7858DD476EB3DD8694815527E5ED9893839A26FA16D3F306DEFE28A55911380A28AF5FC32318A99D2723CCC6F10233A229FDAC970A0E54AEEB5C834C9030BD578C8E4C6BDF1E16A395E4DE74FCD485A64B586753DB63FD98F0B039103A66BAD7D124BCEBCBC30726C89B4F45EB1A2F2DCE2CD1092357AE2A88F888EB8AA834850D3C31605562B923BE2CE2895B86AD6BD716613AB4BB7000AF5C5B03238AF8FC3BEF4C59A651DD934DFAE2A9925C57BE2A89593E1FA314C79A065763BD726DF4A22BD7E9CACAF0AAACC13BE424BC2E6B8AFC43B6545045209AFB8BF5CA8AC50571741DAA4F5CA64DF04BE7D43802AA731E6DF148E59BD77A3FD9AD4E461CD390D04F6CCD998C291BD32C71B8D4EE6D2240640BCD5B6E3FC72617898FB58A732E4045EF962F1A51A8CB688AA88FF001D77C9C59C656C3A7748E4E6D27C35AE49938EB9A728547DC8C55522D734F520C7154FCB154C63F30B49F0C44411FED29DAB91920A5375AAF2B9553217A9ED9157A77945B99568C1E05A8D5F1C559B798AC80B713AA0E406F8ABCDE3D44F221CD181A53080E3CCA6D1EA238F5C20346495060DF999E7087CAFE5C17AB2859EED1C15AEFD08CCCC31757A89D87E54F99EE6EB5CF30EA3A93312B2B311F4939B5C50D9D266365881B52546D999195075F38DABDB5A9EC3194DA2504F60B08D8AF8F7CACE4651C4CBAC3490C071E9E190395CA8627F58BFF38EE9E9FF00CE3F7E45C7FEFBFCBDF2C2FDDA55B0CDCE23708FB83A3CC2B24BDE5EC59635BB15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85884F361C76CAF8536C96DC111904D6A301816424D4A58C5C075A65462CC6441C41E1560CA5C9E80654765394525D76A423B31095ECD96E336D12CA18F05E69242AC0B484107B6C733230348134E0FC0EA6849910814F964278C86F84DF60FF00CE1AFA8BE6C997890CB5427B6F98594539D8657B3DBBFE720B44F56E352AA8320526BF40CC7E2A679214FCFAD422F413895A713C4E591CAE14F6485D8351286B5CCA84C14464F40D0A20D0C709A127BE3964006F88E264377A544A16AC797E19892C81CEC008418B78D5951D6BE04662642EDF164A099E9E2EED2E565B29CA1435A1398920E58D500F50B0FCCBD6A0F4E192770F0ECBC4ED4F7DB2996CCBF3024CDED7F37358E01259E4E245363FD991B41CAA7A97E61EA7771FA705CCD461B963FD98DB0F1187DA6A375A8DFA09E4919F97DAED8DB1337D27E58965B5B3854B971D70F0DB958A7419A457ACD2AB576F0C781B78D90C37CB51F153E79122990926B15CABAECE3E9C090B6491493DCF8E282143D555F1F962C86C8859EB4A57DB1640A696D24840DF167C4C82094556B5C53E204EA09169DF14F1A3A19949FB58A414DE27523ED8F962C816DD4788A0C9B68CA165630A41EB4D8E40848CA12C9E4E3FB5F3C810CB8D086E9551AB5395485053249E694B3F2076F0CA0C911507B8526B5E998F2986E8EC954D3879295CA8FA999CA229B69D6F1CF22ABF422A708853564D4090A6616FA65804AFC5CA985C7F1107776E2353E99A8EC3240AF88C3AFC4EA181A508E8327C6BE2306BCB3672F20635EA41CB319B6DC79186DFC7292CA49A65DC2DBE284858411732558B1E95C7857C5097FE9178643C5453DF1E14F8A11BF5DFAF71E6CD13A0A2F1E87E795CC5309660CAF42B41EAA34BFBDDF6AE52649196DEDBA3AC91C6A21554A374C1E2065C69DEB17D722C9A3908269D7263763E2878E3CB28B89030DEB52C3A53261C7C99022CDF47042D34AFC238FED13964604B87972BE3AFCEAF345C799357B8D2F4F98FD4ECA80723B1AD6B4CD861C6EAB3650F99E6B7BA8AF3882BE99D9877CD9E3141D5E49A0EE2DC076006C3265C594952DA2A76CAE450236CA34DB28E591405353D7316526F8637AF68BA242630596BB6527239718BFA78FC884117E47FE4D443611F91BCBAA07B0D32DC674FA7FEEE3EE1F73CBEA3FBD97BCFDEF56CB9A5D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFF00FFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF858B4FB432AE36EF0D94C1F6457A77C789061488210F4C89DD81539220637E0C15E9F0B7865660C244524D756F13A52693D47F019761C6E31905B67E5ABED43F756D68EAAFB89E9B0A66C61065198B425DE8B7BA7DD4114CFD036D95640E66220BEC4FF9C378DD7CE37019830E62A335D99D8E10017D63F9B7A7A6A3AB6A7005E47D36DBE8198526DCA6DF997E77B23A6EAD25AB0E2A0D7200D38138B07922A10C33271C9AC459B79526E573E9B7602992CC6C3958B67A75E5A7308CA3A8CC493B0C692359B89375DF28C8E6C792BD9C6E26238E63C925131C00CEC5C5083B644C6D946549998D5694C1C0CB8D1B1395E31815AEF8F02F1BD5FCABE5F49D16764F8BAE3C0BC4F5AD3A16810474D94ED86A9CAC4764E919B983F7E25B814DE201A8CCD4272A93644A690F1029C8E41B0146FAD40003B0C5928B4FEF8A559273F0D4F862A9A5B5DD286B8109DC57CA08DF0B105358F50403ED7CF16C051297B08228F8B3051E9A8A01B3F4E98B3B6CEAC4FED64896622B7F4913DF205988A067BD249F8B204B608A0FEB47725B6CAA6526282B8BC201A1CC599445269AF5FB5731E4DBD17DAFA93480F63928868996716303222B74DB2E1071AF74EA3770295230F84B6A53B32AF23BD71F097898EDED5C1F871F096D8D4F0121885EBD72CC71A6C81B63B77A6990310BB9AE5CDAC12FF4AB80CDC50D0F4C558EC9A3DE3BEC877F6C551B65A2DE8956B1B52BB653910636F54D134C962319910E6290D9183D46C42C7C05295DE980067253D7A655B76DFB665422E34CBC8AE2ED959C0FB3FCD968838939879279F7CD7E8594D630B9591C1008F7AE5F8E0E1E49BE6986D2E66B998CE4C8EC49673EF99D8F675D96492EA9A6709AA13BE65C4B8336373E9D234AD45EF84968E04759E8D2B015427299C9B61167BA2E84EAF1931F5CC6939110F67D1F4BE318F87B65326F8BFA35FC955E1F937F94A9FCBE4CD047DDA7419D5E9BFBA87B87DCF29A9FEF67EF3F7BD372E69762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF855B670AC2A330CB984B2785D5A23C4EE46C300938D3C8037146EC0D640A57B1EA73271C6DC59E7013FD17CBB7FAEB98A106353D588EB9991D3B85A8D50112F59D1BF2CACE1557BF01993724E5F8F03A39768B3EB2B0D26C08B45317A454D401F16C3C732BC3D971F68BE7EFCC1D3E117D1DCDBB5631C8041D7BE60E78D3B9D2EB789F437FCE1B5928F37DC48C4A6FCAADEDDB35595DFE973F117D6DE76B8853CCBA848F19742AC38FDD98530EC4EEFCF4FCE7D0AE26D50DF5BC263889E5D3AF4DB292D5283C3230655657431B275AF7CB612A6A31A651E578C9BE1C5A94A65933619E30F7148792460EFB653273F1AF4B1ABB029C89E87C3289B9B16EDB4CFF004A3F0F5F6CC7924A3C6845A6E741D7A530C5814DA3F2DB4D4A2D3DE99246E9B59F955DAE230141A7B62BBBD7748D35ACA255DABE00615B2C9238C83C9969ED95973709D912B1FC4081F31912E48298C7C788D8E552660A2D08A743EF90660AA7A95D80E9D31656EE05BF6A98A6D772A7C35E9B62B6898A4E3D4D7145A244DDF9D315B442DD11D1B1640A3E1B84A8AB6F8B65A6D1DC42109A9E98B3077524BA534DFE5809724144FAE0A9A1A6DB6449660A02595BFDF832A25B0150FAD71DB9572B994CCEC8692E958D28731A4D112BE38BD42083D7B656D97B325D36D5415240F965D8E2D332CC15408C00287C732E107189B2B7E207ED5465BC010A95E638B0AE3C0150B2DB07E8B8F00543A699CF9D453C32328D3762431D138B54D18785320DE8693CBF0CE45502D3C7156E2F295B83560A47CB154CE0F2E598707D351C4F865536704CA6D16085032D001945365A47724C33A90A7805EA3C7271834E4C94C63CC5A8C0B6EDC9E840DF3371E375B9333C3755D61A4574B67F1ABE640C4E064CFBBC875ED367D4A61297F881DC9C988538F3C96A56DA488630CD1737237232D8871A52B492FF00449A798BAC3B78658254D2425FFE1E666DE2A1F9644CD90C6C86C3CBDC40AC5DBC321C4D831B38D3F468D387EEF7A6F9592D822CEAD2C5234038819596603F7D7F27871FCA4FCAD5F0F286883FE9C21CEAB4FF00DD47DC3EE792D4FF007B3F79FBDE8D9734BB15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF84F5A9238F5CAA50699EA135805C2D19413C7A0F1CA841D7E5D4BD77C9FE4C9F5955BAB9531A36FB8A7F0CDAE9B15BABCBABA7B25BC363E568B8428AD2D3E13B75CDC474FE975F9B54642929D43CD1733551578D7C3251C4EA660B1C79EEA4E530908600D30CE1B231715A437D65F5A82269F76DFAE6A75317A5D04097D2BFF38A5603FC513C69B50F519A5C8F59A4818BDF3F32C8B6D7EE550D4EF503E8CC4C8EDA06DE35E6CD274ED634C8E1745F5C2F2DC6FDB31645BC45F0F79C3499F4CD4E482088842DD40C6326A9C55BCB703DBDDC25B66722B97036C22FA334EB45963889EE06093998D91DB69F1FA87E1AE5191CD8F245DB5845F5ADD46512E6929EC1A746D253877F0C8DD2631B66365A3C2101283071B2E0641A7E9B6AB2F22A0536C7897813496D944AA517E11D71E35E0463C28C415E94186DB63B2E580602DA0AA88B88F6F0CAA4DA0B74A641902A65A84D3164D09A9D7155F50686BD77C55DCC0DB90C554DA6A54F2C516A26EE9D1B16C06D552F883F6B0330518BA937F37E3859834AE97F4FDACAC96C1345AEA15523976C812CC4D4DAF474E5BE544B671A88B82CD4E5D72124CA7B2AAB0E42A7BE504B18B23B331FC3B8C039B6B27B39A35A6F9978A2D190A73F5D8F8D2BBF6CCBAA71495CB728DB636BC4ACB301B8F9636BC498DB3ABF518DAF126255154103AE4265C8C25AA467ED0CADC97048491B0C5556910EE31550692256A823DF2B9ADD216EEF23E34AE5643194D846AFA82420B33058E952C72EC637703365781F99FCDF662678397A8A4D0919B2C7074D9B3BCEE7D5EC0C9584D10EFF004E6588389E2DAFB7F4AF4811906B80C11C68897479C39A3D01E8BE19514A9FE8B71F69B204A695974DA007803EF4CA8C9CA84764C6DED78EDC0636CF85905B45180BF08069BE368213650028A0FA3157EEEFE50FFE4A6FCAFF00FC04B44FFA80873A8D3FF771F70FB9E4753FDECFFAC7EF7A265CD0EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85F8ECA6827585979C84FD919084FC474B9674F6CF29F906F35036D773218A18D833A95FB437DB719B1C3A1E274FA8D43DA2E0C763686D6D2216DC06CE3BE6D30E8F81D5CF2F11610F34D76CEB212EC8767CCC3300535006ED2AB986E56455F4CB03FB54CC7F1699CB7539675B32AB335030DEBB532B9E76EC1877633ABEBD05BC4817E3095DEBE39AFCDEA7A8D0E301F49FFCE3379B34BD275C3753CAABF58466DC81C4F875CD5E6C342DE931914F72F3ADEC17BACC9A94171F5A4B9DFD21FB35CD7CE36E44254F2DD5E40F74AC253188D3E28FDB31A78CB951C8F3BD4BCA69E67B826C23334CBD471FEC395F0D322049E6DA8F93752D03518E4BC89E28D5BEC95207DF4C2254C6389EA9A33F3B58E48979851B8C4CDC884699569EEACE6BD5FF000CA652722324EBD158255940E61BB6545927D6AB1D43EDBEE47865728DB644D32A8A7884600F0C1C0CC4ADAFAC952283BE3C0DD18DB27B39964B73CA3DE9F6B1E06C186DD1A90A6A77A9C9C62D528515707AED84848D950380A2A37CAA41902A4CC7B0FA3214CC490EC549353438190921A42AB53CB14F1297390814AD3B62B6A2C66F7EB8ADA8BCAE2B5E98AD201E46AFDBA62C97FAE07FBB2A7BE2CB89DF5C03ABD0F8E02CB8DB5BFE34F8ABF4E506569051B1DF7203E2C896C89561755FDBC8D3305556E7810FCAB4ED8382D24A252ECBB57A7D39138523252716B7C626525B901DB11869978CC9ACEFD64A7C5C72F87A5AE53B4DE394935E5B53AE5BC77B35108B8EE82903957DF229114CEDEE62E5566E43C3DF1663127705DC0A2B8B3181305D460208A74F7C05B618F854A4BF87C46001B2D06DA8C6BBF2FA3088ADA1A4D622029CFF001C9702DA17F4AC4C681BE23D05729CB1A5AB405CDD731BBF0F01918C2DC7C9B3C4BF35FCCF268FA60B6B78DEE2F27158E34AF434DEA332F1E3751A8C8AFF00959F947A8F9D3433AAEA90B5BFAA2A3983B75F119B1C717499A565BD67FE71E6F9B549EC74CD4B9F04E6005EE7B74CCC842C38E254C2A7FCA7F39795E57730C9731C7D5829FE991941B23240C3785A76B4BD06DEE22DA507B6634E0E4455269E35902A2FAA9DE40731CC5B8056F5410027D9FD939518B9319505E8CE36AE222C84938B13CC8A8E9D708091BA7885760570A781FBABF947FF0092A3F2C7FF00013D17FEA061CE9F4FFDDC7DC3EE78DD57F7D3FEB1FBDE8797343B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8D2F23F91A57BA4BFD6D6BC0D68D99B874D4F199B556F6FBFF00315959471D8E9F1AA0A7024019B9C18F85D466CD6C59E49EF25615E4332A52A0D50DCB2DF2D6856B70C52551CE43DF31324DCEC38B88D3D353C81A7CB0F32AB551E03FA663193B0FCA3E7DFCCDF2B2E9FEABC7B2C7E1F4E53293938B4F4F9C6F56297D485DF7198E64EDB4F0E146E83AFDC797196E2DE46AA9A00091D7E9CC6CDB876719D3DDBCB3F9BD24F2C31DE0DA94AB1FEA7308C1B0667B4D94D69ACB0BD12208D97E3151D3289636F8657A07911B4F7D723B3D2E146909A39DB7CC738DBE395EB1F997F96F6BAE687305B355D42DA332D55454D47B0C8F03938A76F86AD6E6EFCB17173A65F42C9F1955E429DF0183920B2BB7BC4616F2C5B16DDC65660CC165F14EB3463B9032071B30532B566E2C3B83B65538D1499526D019BBD6990E16719A6917C5DBA63C2DF19A6D6F7CB1008C7E8C785BE395344BA420107AE061396E881329C892A0DAD663C8D0ED9592CC054571DF2BB6C017F2B7FDA1BF7C2A8599ADE876DBBE2A82372A361D3A0C5541AE7155295D4AD477C52934EC6BB6D8AA0C87F138AA8307DB7380A43AAD502BB7865062DA0A3E1269D7A60A6C0510A5BF98E34D80ABA3316515F98C69369846687AE16B4C2075322A96D8F7C55328A7F498156AD31564767AA23108EDDB08544B5EC55DA4FC724C81444378A5A9EA7BF5C2CC14C92F401F6F6F9E06C1277E9458EB593AEC37C84CB6715A849AC27412F4F7C8829421D6A25AF292BE1BE580AA026D6226DC3FE39602A841AA52B2C64B329A0032AC82DC5CF9B81E8FA0797EFF00CC36DF582AC8AA3AD30C62E064D45B23D2FF002E746D666FABEB1682EAEE27FDDB915A27874CCCC61C0CB2B7BEC5E5C8B43F2FBD969AA96E889B050053F01997034E0CE16C3BC93A446D35E6A37975CAECB32853E03A665C25B38C63BA6F3C3CEDB526BEB5496201B83151EFED82526710F942FF00F2B62F34EABAAEA1631FA2F393450294A57DB31E45C8807897983488FC97713E9FA8CA1A624844277CA0B7C52BB760F0C4E05030A819590C81A45AF5C14CC14D74F6F888F0C7859829D237C58D3305FBB9F945FF0092A3F2C3FF00012D17FEA021CE9F4FFDDC7DC3EE78DD57F7D3FEB1FBDE8796B43B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF911B5D4CA4450905BBAE74F1C603E6D3B48AE67FACCEA900FDE16A6D9682038A626D9EF97B49957795D99CFEC9195CE6E660C6F47B1B58EC87AAF540BB961D4661CE4EE74B87D4191C5AFAAC65616692836E5B65066EFC600F1FF3E4D71AB45708F1045607E21F4E5329B64703E3BD62C6EECB539792FEE8B1F88FCF31CCDB04292D9AE9C158E18C484915AE57C56C8DA31A49596358E416F21EA54E44840059C68FE69D574B44824D51BD24151F16E7D8E63C837C6DEEDF96BF9A30697AC437F733A5B98C8A9E5D7E798F272216FBDBC9DF9E9E4FD7AF7EAF7B7111F56309212474C003978362F14FCFAD33C9F3EA31DFE8572B34929E46314A54D7C301736DE23636377C39471820F5F6C8101B0499559B2DBA8F59E847619590CC14FAC751B37996105CB374DB6CA671B613932D59618940988453D0F7C8F0A04D136CD6F2352390BA9FDA1BEFE18385B064A593E9D74D30689498C9DD8E3C2CC66461E76A80354A81BB65538EE9F15B87528DDB846416F0272B316C8E54DE3BC454025A06EF4CACC5C98E45296FE05DD4B37B5321C0DE24BA2BC570095007BF5C8B0326E59EDE9BB004E28B4B5E48F7A1A8C56D41A44DF7C56D45A4DB167C482797AEC315E24135C76A62BC4A06E0D7A75C4A89287AB43D72921B414545734A53E9C785B01B45ADC8F1C785982AE2E42D0D7A74C14C89D95D2EEA2B5DF035DAAADD92C17953DF15B4CE0BB006EDCBDF15B44BCE78F247A3FB6109050E2EEE01FB44FD392664A69697B329A93DB147123FF4A3014240FA71533A434FA846D148EF394310AA81DF018DB28654A6E3518B9DBAC324B2198D1CD3A60106CF142A299A4BC92D622F2040086237DF2622BE28651A77947CC5A9C4F35BD83945E8C4102996082F881EB3E4EFCA9BD9D44FA8388EA7918FE5898BAAD7E4DDF4559E9967A26872A40024C828A0EC09C9083AFE3280F24DBDEACF7535F5BA2C8EC5A075DFE1DE997C4522ED966AD33DA5A4934D2963312A213D299785E152D23CBF696D62D3ABB191FF007A2BDCB76CB449A670DD4BCD4C2CF402DC4249715529F3DAB80C944698D7953405B5B06BB9A61140D1C923CDFB5D2BD32B25980FCBFF003D4B379FFF003D6EF4486FCC7A769D39ACAA7ED509EB9021982FA1F54FC9D96DF4DB7BFD27525BB9258C39B52C28BB0D85322907778AEA52AE8F70D677C443780D046761F7E2C814CEC599516534A30AEDD30365A6515E425FE26A7B61676FDE9FCA021BF297F2B997756F28E8841F63610E74783FBB8FB87DCF21A9FEF67FD63F7BD172D68762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8EF578F9B5C06FDDBF4CE9B8DE267A6651E54D1967BAFADC9BA72AAE552C8E29C1BBD62DE25826E4AB45AE56656E6E0C28FB8B9068427241F697C72B9F276F871F0EE94C97B4256184257314C9D96395B10D522BB9CC8D23058687965129399116F07F30E857BACDF35B58446572D4F8728949651A4E34BFC8DF335FC6918B3912693756A76FBF2B126118D966FA67FCE2079E3569D2425E28DBA7F9F2C4C9C918596BFF00CE13F9AA29D1E5BD255853896E9FF0D944A4DD0C4CC2CBFE706B5A9E359DB5858C2EECBCFF00E6ECA2526F8E265563FF0038CDA7E8171691DF7994594A8F4F50494E4476EB91126D11E17A95A7E487956EAE62177E6E597881D6407F8E032660A7179F957E4CD35D2DE1F32C444A28C798DA995993602A69F929E539E932F9A6293BF0120C812D82491F987F2C348D2EDBEB1A7EAA048836F886E7EFC46ED3964C33FE55FF009A2FECCDDDAAFD7517A01BFF001C970B57131F6D13CF3A6481DB4B6B5B68FED02A454F8E3C28F11136DE77BCB598417D6E4229A31A634BE2338B4D6B48D6A25442179EC6BE380C2D81CD45BBAF28385F5EC65A93B8A1C81C6DF0CC92CBA4EB91A13E9B3D3BE40E37331E54BCCDA9DAB7EF6D0923DB2BF0DCA8CD53F48A85E722346C7ED2F8661CC516CBB516D4ED5BED311F3C82AFFAEC741C5B63D3E58AA8B5E27F362AA2FA82F4AEC3BE2D96817BF5AF5DBBE29B59F5A88FED62B6DFAF19EF8A82A2648EBF6BAE45B42BC6CB4FB5BE1640AB2B8DB7C0D815B92D3AD70164AD1B0A75CA9AD7F2DC50EF8551D0135A5715477A8513956BBD310CA3CD6FD61BAD324CCA9BDF49F663EB8B548A3ED2C750BFA705635F0C5ACC99E795BF2D359D7F538AD591845506507C0FD396E38DB0964A7D476DF933A0D959033C4867812AEC7B6DF3CBBC363E2A47A4F937C9BA5DEDDEABA95DC29145BF027AF1FF006B2C18D7C5629E7CFF009C98F23796F4ABBD13CB56714B76A0A0950577029E1ED93F0D7C57937E5BFE746B779ACC375AC4AD0E9CC4B70EDC6BF2C84E14E3E63C6FAAB53FCD9F25F9AEDA0D1F43981BDA8590AF5AF7ED80069E07B5E83A3AD8E9B6019FD4927456E47B03DB260278588F9D0BDE6AB67A65BF660580C902ACD043E8C3A7C1D2A02B8F96264C4861FF00987FE997DA7E996FD178F35191E2470A335A8DA0F265F5869B66D3EA4B6CCA0815DD908C921F9C3F96FE439FCB3E7ED77CC1E77D224852EE6768DCA6F424FB9C3487D831793349D5EC21D5BCBBADB441E3E6D64ED4E1ED4C348B7E587FCE45F9ADF47F3B0D3667A346FC5A64F6A60E1660A1FCA5F98F1DD086D6693F771A84563DC78E34D80D3DA2CEEF4FBD50F1CC391F7C69982FE81FF26E83F287F2A829A81E4FD0E87DBF47C19D061FEEE3EE0F25A9FEF67EF3F7BD232C69762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8BED12FA6D457F46FA12ADBC7F626A7C47E6736E72BCD5193DAFCA1772C114D67796EF4894FD51E31D5BB72F6C899A63A6B6609AB5DA32C37B6E2DD587C2E452BF2C78DCDC5A4012093CFBA5D95C5C4123AA9B7157693A7EAC13C9B39630D30CD5FF003318397B28A39E2ECD18AFF0CC4326D863A62B73E79D575085C4517A61F6F886DFAB29949CC807D51FF38D7F96B36BF24BE60D7A33C62F8E30BF64F43BEF98F29378C40BEE7D22C74E69E9269D0C76D07C10CB1AD18BF45AFB655C4CBC1E1DD9E8BFB58ED1ED0C421BF857933462881478E266DF8E16F93FCD9F9B3A89F3543E5DD06E92E5E7904370EC6BC2BD48DB2894DCD86114C9FCEFA4EB5E4DF2D0D6EE7CD33CD77749CA3B3864DAA7B5331A536C38E9E45E5CFCBEF30F9C746D43CE7E64D66E20B0B2532DB425E858FB0C89C94C463B64BF967F973179E359223BABDB6D26DEBEBDD31A6CBE06A32072527C17977E75585B796FCC53E81E54B9BBD4A68BE15773C8F2DC640E56DC7A7B286FCAFF002B79ECCC351D62C6F9A0421B801F0F1EB959CAEC31767F13E9997F303F2DADECC68BE67D3A6B4BB20707028DB6C4E1198863A8ECDA28BB3F307935EDBD0F2EEBB25AA1FB2B2BD313A92D3FC9A82D7B509BF41DC2C9AADB5F475256456ABFCB251CE4B4E4ECFE17805ADC68374F7306A7693F224F0940D8E644276EB3361314A5FCB71DB96BED36F0C31F22521734232F0C2386C59661A17994D9010DE5D212BB75C90882931A7AE681AF68FAB442DD6E22172B5E418ED919419C7210C953CBDA4EA2A566A7ADFB253ECE63CB6726398B18D47F2CE695A4789223131AA377A7DF98192365CC86434C1755FCB7B8B552CB1866F0EDFAF2A316CE379BDEE87A8DB170B6D29E269D36FA3234C81481A3BD462258245A7B645210F34BE98F8C1030B7525C6F2035058838B3115212F03591CFB53148802A6DA8C6845198E2C8E20022A1BB59A95241F6C5AC48A3967229C6B4F138B30510B73D37E98B74510B703ECD7AE03BA65B045070AB50D91E171BC42AA92EC64AEEB9131489A36DAEC3380DB0F118084892266BD759111006889DEBD71886C81DD1F0992E18C50C4657A7C210572CE16D243D3BC97F957AF798E482792310C524A1645E8426FBF5EB8F0B8D393EB1D1FF002374CD2AD24B8FACC8B2409CBF7A76247D38F0B8D29A0B4DF33689E5E3793CF730C571A7D79F134A8197618B10789E11F993FF003955A5E97697F6FA0A9BABC98323B49BA8EA36F87325AE669F188F3B7E6079FAF9ADAD2F561B59FD46B94534201A9006C3C70DB8E7290C5B4AD3E1D275392DB592F717334A03C8FBF53DB27C69194BEACF2AF93E2D6E1161A2A492974A3B36FC411DB2B99B6C8CDED5F941F9172796BCC4FA84AD2CE8CFCA6F537E277E9BE56CF8EDF74C691C2A8109E1025501F6C3652082F3BB1AEA1E6A7BCA739223C78FECED8DA683D06F1115A198578A312DEC72F8C0485A90180C006AFE6C9243F14708DA982500189A66B1C4F1488518AA4B5E5F25CAA52A6069E4DE67B48BCC7E67B6D2DADD25B585C7AEC07C54077CABC621AF64279ABF2B6EF4DD2B5AD5741BE6B484216823534A0A0EBBE4865B6C1005F879F9B02E6E7CD9A80D61E3BA9E1958073B9DB26269E00F3089B568627BAD3D12358E4A1E636E3ED960368D83D2B43F3C4F63259C11C9CE5968272DF641EF4DB274178807F551F9093B5D7E45FE4BDD350B5CF913CB92B11D2AFA65BB7F1CDEE1FA23EE0F2DA8DF2CBDE7EF7ACE58D2EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF90FF00CB7F20CFABEB70F9784FE9BBB0569CD76FA727E3B8A3474FD1DD03F23BF2F3C93A589BCC5E60B79AEDE3E4119F707EFF007C7C76C8E969F1C7FCE4679DBCA97925BE9BE5331C6DA651649E3E8DC698F8EDF1C34F985743D435AB73A95C5AB244568B514F57DBDF01CEC8E34D74AFCB3F36EB344B1B65B6B63FB4C074FBF29395022CB74FFC91D5CEA169646F0CB792B82B02D6868457299657271C5FAB7F90DF96FA8695E588AC2E2331968C027E81941CAE6460F7693CA361A1E8F705C896E5DC145EFCBB1CA8E46E10BD9F397E76FE60587923CA7269D6404DE66D67F72186EC81E9BE0395C8861A60BF911F93BA0E956D75E74F38EA0B3DC5CC06ED8B36E8C6BB75CC79657371E36216F637DF9B9F9A52D868F792CBE5AD35E9287354A2FDDE198F2CACE589ED7E70F2C5C6B0963E42F2E5C1B68B4F939DEBC6688E0F63959CAD4715259E7AF3EE89F933F97B37956C5565F325C2141243F6F930237A57227236E3C5C4F19FC99FC9EF3579E357B2F3B7982E85BDACF299409BAB2F20DEDE390395DBE97476FA57F323F30348FCBD8C68F62F6D3B88F837115DC0A7BF8656723D0E9B40F813CDFACB79A75A6BE9E0145242328E80E0F11CC9F65F131F9AC2248F925F4B6EDE08D4C81CAC4F6481D1434AB8BFB2BB8A73AACD71A746F59A177AD4F7C9C32BAFD4F66D07D412F9D7C87AC79623B2D3ECA38F5644A34B415E599B8F23CE6A7414F0F3A6EAF7777248B3B95AD16253B53E599D8E6E8F2E1E03499E9BE5C56BD53AB192DE2247276DB2F127127165D7FA0E8F0317F2EEA85EF1002C8ADB93E18CA4D6229B7953F33A6D26F5346D6AD65424F1FACB0FA3AD330E72722117D2D63AAD9DEDB42F6B79CD5D4155AE634B9B971E48AF5AD89E372BCD7C72B219A1DACF4FB9E4A6C57813B371EA3C72B21B2293DE792B49BB25BD08D49F6FEDC810CC3CE75CFCAFB296390C4A2B534A7FB78B787916A1F965240EFC54D05698B6061D7DE50D42353FBB240E9F462CC31B974CB8B7E4B25B9AF8D31672E4848D2584EE87171AB74D61B9A8E25295D89C5B150951DF1640B4B28A80A773DB14CCEC9AC658A0DF22E2A3ED62326C7A1AE34B68D58A15824653F18E831E15B645E43F296A9E67D5E1B75899A194D2B966385948C9C2FB83CAFF0092FA779295353D5A2170654E410EF4AFD3991E1A0E77A4E82DA4699F5ED6AE255B3D3ADE3630C55A55C7418F86D32CCF3DD5BF33AE7CE574749D20496F6ECDE9B4E36047CE98F84E34B23CEBCF1A3792FCA7A1EA926BBAA97BEBD80FA4BCF7E743D725185328647E5DEBFA8C32EA57C2CE212DA991B8B91534AE4A98E4928F9735ABDD1EF0DD69B251CAB7A918F9634E29927A350BBF32B5ACB1DBB3DFB5C207A0DFED634CA2FD64FC8CF29C1E5EF2B0D66F63A5CC9081F17662A3010D8F77F23D85DCB6FA9DE5CC815667261AF8646922D3CD6EEC69D62189A168F8D7DF1A6C8DA43E4EB4FAB9B9D4661B4D5E24FBE34CF74E755BCFABE8B3C8C68ECEDC7E9E9878E934520F29DB1B2824D4E71BDC92149F7CAE5918905956A139B4B19A73B7D514927FD615CA253B6B312C23C8F6AD35FDFEB972B54B824424FBE5129313128BFCC0FD23079435716F2B969636A460F4DB2D8DD391189E17E06F9FFCB7AE0F34EA9A85FD94CD01998F220D2996C4B0902C7749D26F7CCD7834686136494AA9229C865824E348909B47E4F86CB506D288E57711A09325C6E399C83FAA2FF9C7E80DB7E437E495B375B7F20F96E33F34D2ED87F0CE9307F771F70FB9D2E5DE67DE5EBB96B0762AEC55D8AAC7DC7D38AA9D0E2AEA1C55D438ABA8715750E2AEA1C55D438AAFC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF233CB9FF0038C3E4DF2ADCBEB13B133D0B1911C0CC3B2E7ECFCE5FF9C95D5A6B1F346A1A7E95AB4E6C625612C5CC96E229B03DB1B29A0F89DEDDEF00926B97974FF5394909AFAA47BB63653C2FA87CC9AF797F56F2279774DD25D34B4D3A86707FBD6A0A751BE365061B337FC8DF2C5E79C752B7D26D6F2E0CAEEA11B937122A32B258460FD2CD3FF27FCBDE569F47B4B9B432EAF28065BC6DF8F427299172618DF40456B6BA6D9C16FA4C4102A51E4661D69EE72891A732106272C4F737978F79C94DADBC970B272AA1F4C578D077395D972B1E3DDF9E76B6317E6E7E6FDCDAEA08F69636B318F8CA7ED509FB35F96425272B829F42FE69F927CBDF977E4FBCB5B1FAE896F63315A4924C4A7A8694A0F0DF28916D8079F7E5DDB5EF907C986636E96B7BAC9ACD7DC6878B03D0E512939118A6307E6459E97A6DCD968086FF00CD3316F4EE07C67936DBD2A72B2594B0DBCCBCB9F94BE65F327999FCDDE78B1B8BA4327A932C95E0141AEC08DB2264E4E9F4FBBD4FCFBF9ABE57F2AE8F0E81E52E7677F0A18E242F550D420EC32B26DE8F498007C75AEDF5D6B378B7DADDCB4D7B292CBBEDD7C32B327A0C31012DFADC21FEAF343EABAED118C5283DF07139825174B3C0842B583B13D2B91254CE285BAB580C5EAADAB41714FB1FB253C699289759AA31AD93BF21FE5BF997CF5AA25B7942D9E390B52E6465257DE9B6676393CB6AA9FA0BF97DFF0038C4BE5FB186E7CD3299AF1497996BFC33618E5B3CA6B620CB646F9AFF0025F45F3497D3AC6330A8F843A6C7C3AE64893AE9C5E7FE59FF009C4B6F2E6BEFA88BA9E78B90608EE5878F4C12935883D1FCE5F91FA16B366919822B5BC450A27550A6B4CC49CB7722317CD97FE5DF30FE5DDECA90C72EAB696CD44F4EA683000D8F40F2E79AEC35E884733AD9DD746B7907C55FA7010CD983EA0F1AFA3C1404F85180EB9590D812B7FADCADCBD6F84F4036C810C834F248AB406BDB7CADBD21BA53293CD41C5B42552E936F221AA038B20C2B54F2BC3217A44BB83DB16C3BBCCF54F2A4B17F751803E58B5F0B13B9D1E5B706AB4FA314243346F1FDA53BF4C50A5146DC838041076C0589927F096E03236C0A6F6C582FC3D7FCEB931BB548A3A0D2A6BABC820B40CC2660251D7AE5A22D7C4FD0BFC98F24E95A1E9D6B7A2D985F08F90673515A7865F8A3BB4CF23D4FCD9E6780AC36D247CE78E3ACC3F6683AED99341C7F11F21DDF9E17CF3E61BCF275ADF243A7D94865BB8D0856A0EA2B5F6C691E25BD5935BFCBEF2569062B7A34F0A7266EADCBFD6C683025F9C1F9BBF987A879ABCC37C25AAE903E1B5727627C0602144A9E07E85AB1755670AC4D77C1C2C2791D64B67A6DC898BB2CBB8849355DFAD463C2D5C56FA7FF00203CAB16AFAF433BBC73C664570817DC1C785BE127EA25D4634ED0934E0BC04DC5828DBA0ED9121CDC401E6CF7CB6D0268F089CBAB2814A1A7DF829BC4425FE6C65BF8AD6D636E3561C877E38D331009F689666F74B92DE195614B215766F6C69B0462C1FCF9AFE876D2C3A341AA4308545370ECC0EE7AF7CC79C776C1008D7F39FE5FDA687656D77E64B5630D0944700D7DF7CACC14C22C4BCD5F9C5E4792D27B1D3F5980FD6C2ADC55813F08A0A6FB640E32D6718653E49F37F93EEB4286C1756817D221A3F8C035EBBEF90F08B0F0C273AE79A34C3A7CF099619CCBB2B5410C3DB7CC8863A0E44202980CDF92FE44F3BE963EBBA72FD6AF3ED3A902B5C3C0C258C3E78F3E7FCE23685A523EB9E5EBAFAB6A963FBB823076A2F4040EB83869C69E10F9423FC92F3C4BE66FAC49A6CB33C8D46B8553C4FE18971CE9EDFD0E7E5058CFA67E52FE5769B72A52E74EF28E896D7087A8786C21461F78CE9F4FFDD47DC3EE797D40AC921E67EF7A2E5CD4EC55D8ABB1568FBE2AD547862AEA8F0C55D51E18ABAA3C3157547862AEA8F0C55D51E18AADC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF30EE7FE3932FFBDDF60FF7998EE4BF25BF357FE537D53FE39BFB5FEF6F5EDD716D0F1497FDDBFF001C5EA7FB9E98B745253FDFDAFF00BC7FDE0FEFBFB9EBDF23264793F4DFFE7143FE3BBA67FC707A2FFBC7FDE74194B18BF42B56FF0094853EC7D8FF0077F5E9FB194CDCA83C8BCEFF0061BFE521FB5FF1E7F63A9E9ED98F272E09DF927FDE49BEDFFBCB27FBDFFDEFFB2FE3953938DF1A59FF00E4EBB4FB1FEF50FF00783FBAEA7EDE464DF27D01FF00395FFF001CEF2EFDAFEF63FF008C1DBAFBE53258F3625F985FF92AB47EBFDCAFFBCDF6BA663C9CDC6F00FF009C69FF0094FA7FEE7ED0FF008EAFCFB65679B9717EA37997FE51BD43FDE3FF00799BFDE4E9F64F4C94F936E93EB7E3179D7FE531BAEBFEF43FF7DFEB9E998E5E9B4FC921BDFF008EDDB74E9FEEEFB1959E4EC23C936D3BFE3A375FEF1FDB1D3E5DB029F8ABEB3FDF27D8FF009E790973627E2AB17FBD717F75FDC8FF007A7EC7D39383AFD47C5FA1DFF389BD2EBFE385D0FF00BC3FDF77EB99F89E675DC8F37D49AFFF007D71FDF743F6FECE6645D066E6C5344FF7A24FEE3AFECFDBEB99117124CCAD3FBE7FB5FECFA60C9C9AC3CD3CF1F69BEDF5FF0075FD398736F8BCA74FFF00A587FBC7DFFE3A19747E9497CB7AEFFCA552FF00BCDF6FFE95B80A43D7A1FF00792DFF00BCFEEC7F7DD722DB1504FB67EDFD1959641565E9DF2A2DE12997ED606D0843F4E2C825571D5BED7D38B6B12BFF00DAFB3FECB14179F6ADF6BFDD78B51611A8F5FF00757D18B192017FBB3F67A603C9804543FDD8C805926D69DF2E8B44F9BD2FC89FF1D6B7FEEBED8FEF7E797C5A25CDFA15E5BFF8E6DA75EA9FDD7D1F865F071F27262BE69FF8EB6A5F6BFDE47EBFC32E71DF97FE4FFF00C995E66FF7BFFDEC93FBBFB7F4FF00938ABEC7BDFF00943F52FF008E77F70DFEF47F7FF47BE2AFCCCD7FFDEDBBFF007A3FBF7FB7F63FD8E1632E4C6A3FB4DF6FE8C0D3245C3FDD5C74EDFDEF5FF638583EDFFF009C50FF008E9C5FDCFDA5FB5D7A8C0E4C1FA41E6DFEFB48FB3FDCF7FB3909399064D63FF1CF8BFBBE9FB3D320DC3E2C67CC5FF1D0B0FB7F647F77FC71671F8A62DFF28CEB5FEF67F74DFEF07F7BF4E167F37E67FE65FF00C746F7FE526E9FEEDFB5903CDBA3F178737D97FF008EDFFCF6EB8127E2952FF7FF00F4B5EBDFAE2D72F8BD3FCA9F6D7FE526E9FF001EFF006708607E2FA3EDBFDE1D1FFE523E8BFDFF004FF65930DD1E5D5F5BF977FDE0D27FDEFF00B2BFDD7DAFA72054A7FE6EFF007960FF007B3A8EBF47DAC816324FB4DFF8E7D97FC733A0FB5FDEF4EF902C1F62796FFE51ED07ECFF00C73AD7ECFD9FEE57A7B674DA7FEEE3EE1F73C7EABFBD9FF58FDE9D65AD0EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD9, '1', '2021-03-13 12:45:48', '1', '2021-03-13 12:45:48', b'0'); | |
| 149 | +INSERT INTO `inf_file` VALUES ('8448cada8c714e4ab61f521c8da21990', 'jpg', 0xFFD8FFE10DFA4578696600004D4D002A000000080007011200030000000100010000011A00050000000100000062011B0005000000010000006A012800030000000100020000013100020000001F000000720132000200000014000000918769000400000001000000A8000000D4000AFC8000002710000AFC800000271041646F62652050686F746F73686F702032312E30202857696E646F77732900323032303A30343A30322031373A32343A3135000000000003A001000300000001FFFF0000A00200040000000100000320A003000400000001000001C20000000000000006010300030000000100060000011A00050000000100000122011B0005000000010000012A012800030000000100020000020100040000000100000132020200040000000100000CC00000000000000048000000010000004800000001FFD8FFED000C41646F62655F434D0002FFEE000E41646F626500648000000001FFDB0084000C08080809080C09090C110B0A0B11150F0C0C0F1518131315131318110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C010D0B0B0D0E0D100E0E10140E0E0E14140E0E0E0E14110C0C0C0C0C11110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC0001108005A00A003012200021101031101FFDD0004000AFFC4013F0000010501010101010100000000000000030001020405060708090A0B0100010501010101010100000000000000010002030405060708090A0B1000010401030204020507060805030C33010002110304211231054151611322718132061491A1B14223241552C16233347282D14307259253F0E1F163733516A2B283264493546445C2A3743617D255E265F2B384C3D375E3F3462794A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F637475767778797A7B7C7D7E7F711000202010204040304050607070605350100021103213112044151617122130532819114A1B14223C152D1F0332462E1728292435315637334F1250616A2B283072635C2D2449354A317644555367465E2F2B384C3D375E3F34694A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F62737475767778797A7B7C7FFDA000C03010002110311003F00F544924925293A64E929F18FADE19FF3ABAA48DC4DEDE49FF454ACD369FB3380796ED036EA74F26A9FD78C97B3EB8F55606C8F581FFC0A958D6E43DC037807B28C86505BB87D62EE9F91F68AEC7BEE6B5CC6BDC6480F11A4A1FA7D77AB07DD8F8F93934564FAAF631CEADB03D477AD77F315ED67BFF48F540820C1D7CD751F55053FB1FAB6EA8E4BFD3DA69AF09B90F26CBB0598CCFB45EFF45EFBED67E830BD1B1FFA1B327DFE95753D002D0644079DB3A5755AB26AC4B316DAF26FD68ADE20D83FE05C7F4767F61FF4FF0047FCE223BA3758AADF4DF876B2D01C4B4B44B431AFB2DF536BBF44E6574DCEDB6FBFF4562EC7AD136756E977D8DFB3D565B6DA2CC3763D4E190FC7AB1ECC4C6C8B1EFA6DB30EDC5F4F333A8F52CFB55946162D5F6BA2947EAB4B68CAEA05D6E43AFC6C4B9B6E28A7631BA6536BEA18F5E55F6751CFC1DFD532EAF53D4F4E8C965D93956E374FFB2E3D6FA0B6DE4BA6617D6B654DCBE9D8B9FE9583755763D76ED70F169AFD962EC3A17D64EA76D96616774CCAB32714B5B9365143DEF66EFA1F6BC6637757B9BEF66CFA7FE8D65F45774BA0FD5DC3774F77AEEEA15E583F68792CF55D87898D9AF69C766E6E6BB1ECCAA719DFF69ABAECF53D3CA62E7F17071B3FA8E3518782E155F6D6D6E2073AF7866E69CAFD27A75D9FCDB6EB5FECFD13130C227AAF8CE4351B3E96329ADCE6B032C60225A2EA6DAC3B69F7B7F4F535AF757BBF495AF37EB34578DD5F3E8ADBE9D75E4581958101AD277358D6FEEED77B56C3BA8DF4F5CC91439ADAF1AFC8A716AAC06D2CA197595B71E8A2ADB4D753FD365967A6DF52EB3F4F6BEDB150FACEFF00B47557E735A5ACCA6B09932058C6B6BB5AD77F65088119505D3265004F47296AFD52FF00C55748FF00C34DFF00A97AC995ADF54BFF00155D23FF000D37FEA5EA4627DE13274C8AD524924929FFD0F544924925293A64E929F09FAEEC9FAE1D59DFF0CD1FF8152B01DCF9ADEFAF0E23EB87578ED734C7FD6A9584E1EE07B4C7CD3482B82C0CB869F246A6B0F3B5C246920F1F728B29703C401C9F056B1D90F1A73AFC934AE0750D9C3C5ADD680D6001C35811C15D0E374AC79935336EEDE06D1F4A3E971F4952E958FEF693C49D7E2B7EB969E254323AB6B18EAD46F49C7F730D4DDAFF0073840824FD29597D6FA5D54EE2D1B77341606E9A8F6ED11FC95D334FB493A203B1ABBF21B65A37067D169E1464906ED9B844855393D1FA1399563DD60DAF68690D8D23F74FEEFB5687D60E8746474DB2DA5B16D23D481DE072B535EDC4A85969D0763A1F81D101908365470C786A9F2EDC473F72D5FAA07FECAFA47FE1A67E47ACFCBA855977560406BDC00F20568FD516FF00D95F473FF7699FF52F5781D9CC23523B3EF6993A6450A49249253FFFD1F544924925293A64E929F05FAF263EBA756F036B47FE05542C62241EFBB50B63EBE83FF3C7AAB876B87FE7AA56383EE1E1FEBEE40AE4AE2E207808251702C7D9696BB9747E2546BA9CF7348D7C63556FA7623EBCA24F0D2224729922297C626DE9B0A88EC0761F2F6AD4656EDBA842C0A9A183C5690600DD79EEA06E44506ABC32B682F78682A01F48FA37327CCA3BA9A277D906741BB80AA64D9D348F4CBEA6DA4C0687B43893DB6EE4C902BC16C8C80E86348772496F1082FCEC4F53F4B686469E253E3E2399510C044F8F654EDCBE97D3AD1F6C7B2BBEC05CDDE1C4C03B67DAD7353575E9ABC8F5EA5B5758CAD9AD56BCDB518896BFDDFF00548FF545B1F5ABA47FE1A67FD4BD5BFADF6D195660E650439B657657BDA080763B737E906FD1F5551FAA766EFADDD1C0ED94CFC8E57212B01CDC91A9C878BEF8993A65231A92492494FF00FFD2F544924925293A64E929F07FAF4377D6EEADFF001EDFFCF54ACEE8F875E7754A31ED1348DCFB478B180D85BFDA5A5F5E04FD6DEAE3FE1C7FE7AA5677D5BCAAF1FAC62BEC23D379752F9FF84696367FB4992D8D2F85710BEEF656F4FC73B5B93815D41803BD7C56ECF4C1FA2DBC7E77F2DC8195D2D941DF4925BCEBE20AD5B711F71BADCCB36E331D3B0BA1A770F6EF8F759FB9B10290CB2921ED2DD7DAC773B7F35D0AB027BB7A518F64BD39C7609E56A31C0F3ACAC9A5BE9FB55EA9F1051486593D2B032B5C8C7AEE3DB7B7700818DF57FA7E3DA2DAF1A8AC8320B2B00C8F376E5A55BC10028DD76D6B9BE4A39DD32C00ECCBD4AC360113DD0ACC7ADC009DBBF50476542C3D4FD3230D958B5D1165B25AD13EF3B1BF49C9E9ABABB2E0E7BD9E8B87BE352E70FE49FA09A9156D6FAE5D2DD6FD5E7E4077AAFC1B1B7B4F70C77E86F1FE6B98FF00FADAE33EA79FFB2EE91FF8699F91CBD22EB59753762BC82CBEB7D4E1C887B4B3FEFCBCD7EA583FF3ABA313CFDA980FDCE56701B15D9A5CDC6A57FBCFD04A2A4A2AC351749249253FFFD3F544924925293A64E929F06FAF13FF003BFABFFC70FF00CF74AC0C5A9F6BEC63352C63AC81FF000637BB5FEAAE8BEB9D2FBFEB97576B4682E6973DDA35A3D3AB955FA1E1D15F556398F3634D193EA480266B737D8D4384D13D14271E211BF53B9D07EB6E364D2DC7EA4F155EC01A2D78FD1D9FBAE738FF00376AE86C6B1CCF546C1A4EF90411CFD29DABCE31F01ECC9BF19F040D58EEC44E85AB5717A65C5A186D2299135C983FC90D55E7117A3771E43C3A8D5E9F7B79699F056B1DEC70826216457B9839D111993E9EB29ACA24EDFA80409F821DF6082663B954075105B13D92AAD16BE5CE803523C5327AE8BC11BB62EEA36D5596D741B081A1710D6FDFAB952C6EABD6ACBC0BE8AABA5C61D1634B88EDB55CB286E437520055D9D3E8C571BDD74EC05D06001085689120CF2F26BC5C5BF2DE76BA8ADCE209FCE03D8CFF003F6AE2FEA5B09FAD5D1FCB25B27E4E5B3D55CECF2EA812DA9C7D4DAE05A5DFE8DFEF8DCCFCF46FAA5D2998FD7B02C277385E3F2394D8FD3E64B5739E33A6D10FB028A928AB2D35D2492494FF00FFD4F544924925293A64E929F1DFACEDFF00B24EB193635CDC4A725AD73F69D9EA3AAA6373FF003ACFE42CBC7E8D93D7F2ECCBC1B5B895E3868B6F735CC68304CB760DDF41BFA47ADEFF0018DFF8A0C4FA3FCE5DFCC7F52BFE77FEEF7FEEB7A6B56AFF00C478FE6FFA3B3E87D1FA2CFE95FCBFFB91FC8F553A5C7C236A598FDBE296F77ABCF3306FFB0FA79370BEBACFACCC86506BB1A5C1BFA11BEE6EF764B5F47E83D1DFBEEA7F3DEA66814BFD116B4E4080EC7782CB8388935FB3D6ABD467F5DEB62EFE9479FE7EFF00E6B8E723FE4EFF00CD97FA1FF805CBFE60FA1F487F3BFCDFD21FCEFF0027FD2AAD2E2BE8DE87B75BCFFABC207ED7571DCEC88143D8E71240638ED7123E9358EFE6EC43C86E5D409B68786FEF012DFF0039B2B773BF9ABBFE4BFE6AAFE6BF9CE7FC07F23FEE27F2D407F363E9FD11F4BE97F6FF00EFC992ABD131E2AD5E6DB90D2ED343DC23D7925BA83007753EABF4FF00C17CBE925D17FE50C4FA1FCF33F9CFA3CFFAEC4CD1935AD1D0651955E39C9CEB3EC58AD05CE7B9A5D66D1F9C31D9EFDBBBD9FA445E82FC6EA37BC574DB752DDAD7E45CE643647AAE2719BBBE9D6DD8CFD25B6FFA5F4D56FADDFD1B2BF9FF00E98DFA7C7D13FCFF00FC0FFDC7FF0087F595CFA93FF22BB8FA777D1E7E8D7FCE7FDF7FE0D3870AD971D6BB3A9D4F0CE660D95B768B1CC2F617090D73C86D6E688FE73D3DFE9FFC26C5CEF4CCAA717EBBD5D2DAC706D394C635D21DA966F732CDA7F95F4D7517FF003C3FA5FD3ABF98FE6F86FD2FE4FF00DFD55BBFF141D2BFA1FF0049FF000BFD33E8DBF4BFF44A78DC5B19D8D767B851524CAC3554924924A7FFD9FFED160050686F746F73686F7020332E30003842494D0425000000000010000000000000000000000000000000003842494D043A0000000000D7000000100000000100000000000B7072696E744F7574707574000000050000000050737453626F6F6C0100000000496E7465656E756D00000000496E746500000000496D67200000000F7072696E745369787465656E426974626F6F6C000000000B7072696E7465724E616D65544558540000000100000000000F7072696E7450726F6F6653657475704F626A6300000005682168378BBE7F6E00000000000A70726F6F6653657475700000000100000000426C746E656E756D0000000C6275696C74696E50726F6F660000000970726F6F66434D594B003842494D043B00000000022D00000010000000010000000000127072696E744F75747075744F7074696F6E7300000017000000004370746E626F6F6C0000000000436C6272626F6F6C00000000005267734D626F6F6C000000000043726E43626F6F6C0000000000436E7443626F6F6C00000000004C626C73626F6F6C00000000004E677476626F6F6C0000000000456D6C44626F6F6C0000000000496E7472626F6F6C000000000042636B674F626A630000000100000000000052474243000000030000000052642020646F7562406FE000000000000000000047726E20646F7562406FE0000000000000000000426C2020646F7562406FE000000000000000000042726454556E744623526C74000000000000000000000000426C6420556E744623526C7400000000000000000000000052736C74556E74462350786C40520000000000000000000A766563746F7244617461626F6F6C010000000050675073656E756D00000000506750730000000050675043000000004C656674556E744623526C74000000000000000000000000546F7020556E744623526C7400000000000000000000000053636C20556E74462350726340590000000000000000001063726F705768656E5072696E74696E67626F6F6C000000000E63726F7052656374426F74746F6D6C6F6E67000000000000000C63726F70526563744C6566746C6F6E67000000000000000D63726F705265637452696768746C6F6E67000000000000000B63726F7052656374546F706C6F6E6700000000003842494D03ED000000000010004800000001000200480000000100023842494D042600000000000E000000000000000000003F8000003842494D040D0000000000040000005A3842494D04190000000000040000001E3842494D03F3000000000009000000000000000001003842494D271000000000000A000100000000000000023842494D03F5000000000048002F66660001006C66660006000000000001002F6666000100A1999A0006000000000001003200000001005A00000006000000000001003500000001002D000000060000000000013842494D03F80000000000700000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03E800003842494D040000000000000200093842494D040200000000001400000000000000000000000000000000000000003842494D043000000000000A010101010101010101013842494D042D00000000000600010000000A3842494D0408000000000010000000010000024000000240000000003842494D041E000000000004000000003842494D041A00000000033F000000060000000000000000000001C20000032000000005672A68079898002D0034000000010000000000000000000000000000000000000001000000000000000000000320000001C200000000000000000000000000000000010000000000000000000000000000000000000010000000010000000000006E756C6C0000000200000006626F756E64734F626A6300000001000000000000526374310000000400000000546F70206C6F6E6700000000000000004C6566746C6F6E67000000000000000042746F6D6C6F6E67000001C200000000526768746C6F6E670000032000000006736C69636573566C4C73000000014F626A6300000001000000000005736C6963650000001200000007736C69636549446C6F6E67000000000000000767726F757049446C6F6E6700000000000000066F726967696E656E756D0000000C45536C6963654F726967696E0000000D6175746F47656E6572617465640000000054797065656E756D0000000A45536C6963655479706500000000496D672000000006626F756E64734F626A6300000001000000000000526374310000000400000000546F70206C6F6E6700000000000000004C6566746C6F6E67000000000000000042746F6D6C6F6E67000001C200000000526768746C6F6E67000003200000000375726C54455854000000010000000000006E756C6C54455854000000010000000000004D7367655445585400000001000000000006616C74546167544558540000000100000000000E63656C6C54657874497348544D4C626F6F6C010000000863656C6C546578745445585400000001000000000009686F727A416C69676E656E756D0000000F45536C696365486F727A416C69676E0000000764656661756C740000000976657274416C69676E656E756D0000000F45536C69636556657274416C69676E0000000764656661756C740000000B6267436F6C6F7254797065656E756D0000001145536C6963654247436F6C6F7254797065000000004E6F6E6500000009746F704F75747365746C6F6E67000000000000000A6C6566744F75747365746C6F6E67000000000000000C626F74746F6D4F75747365746C6F6E67000000000000000B72696768744F75747365746C6F6E6700000000003842494D042800000000000C000000023FF00000000000003842494D041100000000000101003842494D04140000000000040000000A3842494D040C000000000CDC00000001000000A00000005A000001E00000A8C000000CC000180001FFD8FFED000C41646F62655F434D0002FFEE000E41646F626500648000000001FFDB0084000C08080809080C09090C110B0A0B11150F0C0C0F1518131315131318110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C010D0B0B0D0E0D100E0E10140E0E0E14140E0E0E0E14110C0C0C0C0C11110C0C0C0C0C0C110C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC0001108005A00A003012200021101031101FFDD0004000AFFC4013F0000010501010101010100000000000000030001020405060708090A0B0100010501010101010100000000000000010002030405060708090A0B1000010401030204020507060805030C33010002110304211231054151611322718132061491A1B14223241552C16233347282D14307259253F0E1F163733516A2B283264493546445C2A3743617D255E265F2B384C3D375E3F3462794A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F637475767778797A7B7C7D7E7F711000202010204040304050607070605350100021103213112044151617122130532819114A1B14223C152D1F0332462E1728292435315637334F1250616A2B283072635C2D2449354A317644555367465E2F2B384C3D375E3F34694A485B495C4D4E4F4A5B5C5D5E5F55666768696A6B6C6D6E6F62737475767778797A7B7C7FFDA000C03010002110311003F00F544924925293A64E929F18FADE19FF3ABAA48DC4DEDE49FF454ACD369FB3380796ED036EA74F26A9FD78C97B3EB8F55606C8F581FFC0A958D6E43DC037807B28C86505BB87D62EE9F91F68AEC7BEE6B5CC6BDC6480F11A4A1FA7D77AB07DD8F8F93934564FAAF631CEADB03D477AD77F315ED67BFF48F540820C1D7CD751F55053FB1FAB6EA8E4BFD3DA69AF09B90F26CBB0598CCFB45EFF45EFBED67E830BD1B1FFA1B327DFE95753D002D0644079DB3A5755AB26AC4B316DAF26FD68ADE20D83FE05C7F4767F61FF4FF0047FCE223BA3758AADF4DF876B2D01C4B4B44B431AFB2DF536BBF44E6574DCEDB6FBFF4562EC7AD136756E977D8DFB3D565B6DA2CC3763D4E190FC7AB1ECC4C6C8B1EFA6DB30EDC5F4F333A8F52CFB55946162D5F6BA2947EAB4B68CAEA05D6E43AFC6C4B9B6E28A7631BA6536BEA18F5E55F6751CFC1DFD532EAF53D4F4E8C965D93956E374FFB2E3D6FA0B6DE4BA6617D6B654DCBE9D8B9FE9583755763D76ED70F169AFD962EC3A17D64EA76D96616774CCAB32714B5B9365143DEF66EFA1F6BC6637757B9BEF66CFA7FE8D65F45774BA0FD5DC3774F77AEEEA15E583F68792CF55D87898D9AF69C766E6E6BB1ECCAA719DFF69ABAECF53D3CA62E7F17071B3FA8E3518782E155F6D6D6E2073AF7866E69CAFD27A75D9FCDB6EB5FECFD13130C227AAF8CE4351B3E96329ADCE6B032C60225A2EA6DAC3B69F7B7F4F535AF757BBF495AF37EB34578DD5F3E8ADBE9D75E4581958101AD277358D6FEEED77B56C3BA8DF4F5CC91439ADAF1AFC8A716AAC06D2CA197595B71E8A2ADB4D753FD365967A6DF52EB3F4F6BEDB150FACEFF00B47557E735A5ACCA6B09932058C6B6BB5AD77F65088119505D3265004F47296AFD52FF00C55748FF00C34DFF00A97AC995ADF54BFF00155D23FF000D37FEA5EA4627DE13274C8AD524924929FFD0F544924925293A64E929F09FAEEC9FAE1D59DFF0CD1FF8152B01DCF9ADEFAF0E23EB87578ED734C7FD6A9584E1EE07B4C7CD3482B82C0CB869F246A6B0F3B5C246920F1F728B29703C401C9F056B1D90F1A73AFC934AE0750D9C3C5ADD680D6001C35811C15D0E374AC79935336EEDE06D1F4A3E971F4952E958FEF693C49D7E2B7EB969E254323AB6B18EAD46F49C7F730D4DDAFF0073840824FD29597D6FA5D54EE2D1B77341606E9A8F6ED11FC95D334FB493A203B1ABBF21B65A37067D169E1464906ED9B844855393D1FA1399563DD60DAF68690D8D23F74FEEFB5687D60E8746474DB2DA5B16D23D481DE072B535EDC4A85969D0763A1F81D101908365470C786A9F2EDC473F72D5FAA07FECAFA47FE1A67E47ACFCBA855977560406BDC00F20568FD516FF00D95F473FF7699FF52F5781D9CC23523B3EF6993A6450A49249253FFFD1F544924925293A64E929F05FAF263EBA756F036B47FE05542C62241EFBB50B63EBE83FF3C7AAB876B87FE7AA56383EE1E1FEBEE40AE4AE2E207808251702C7D9696BB9747E2546BA9CF7348D7C63556FA7623EBCA24F0D2224729922297C626DE9B0A88EC0761F2F6AD4656EDBA842C0A9A183C5690600DD79EEA06E44506ABC32B682F78682A01F48FA37327CCA3BA9A277D906741BB80AA64D9D348F4CBEA6DA4C0687B43893DB6EE4C902BC16C8C80E86348772496F1082FCEC4F53F4B686469E253E3E2399510C044F8F654EDCBE97D3AD1F6C7B2BBEC05CDDE1C4C03B67DAD7353575E9ABC8F5EA5B5758CAD9AD56BCDB518896BFDDFF00548FF545B1F5ABA47FE1A67FD4BD5BFADF6D195660E650439B657657BDA080763B737E906FD1F5551FAA766EFADDD1C0ED94CFC8E57212B01CDC91A9C878BEF8993A65231A92492494FF00FFD2F544924925293A64E929F07FAF4377D6EEADFF001EDFFCF54ACEE8F875E7754A31ED1348DCFB478B180D85BFDA5A5F5E04FD6DEAE3FE1C7FE7AA5677D5BCAAF1FAC62BEC23D379752F9FF84696367FB4992D8D2F85710BEEF656F4FC73B5B93815D41803BD7C56ECF4C1FA2DBC7E77F2DC8195D2D941DF4925BCEBE20AD5B711F71BADCCB36E331D3B0BA1A770F6EF8F759FB9B10290CB2921ED2DD7DAC773B7F35D0AB027BB7A518F64BD39C7609E56A31C0F3ACAC9A5BE9FB55EA9F1051486593D2B032B5C8C7AEE3DB7B7700818DF57FA7E3DA2DAF1A8AC8320B2B00C8F376E5A55BC10028DD76D6B9BE4A39DD32C00ECCBD4AC360113DD0ACC7ADC009DBBF50476542C3D4FD3230D958B5D1165B25AD13EF3B1BF49C9E9ABABB2E0E7BD9E8B87BE352E70FE49FA09A9156D6FAE5D2DD6FD5E7E4077AAFC1B1B7B4F70C77E86F1FE6B98FF00FADAE33EA79FFB2EE91FF8699F91CBD22EB59753762BC82CBEB7D4E1C887B4B3FEFCBCD7EA583FF3ABA313CFDA980FDCE56701B15D9A5CDC6A57FBCFD04A2A4A2AC351749249253FFFD3F544924925293A64E929F06FAF13FF003BFABFFC70FF00CF74AC0C5A9F6BEC63352C63AC81FF000637BB5FEAAE8BEB9D2FBFEB97576B4682E6973DDA35A3D3AB955FA1E1D15F556398F3634D193EA480266B737D8D4384D13D14271E211BF53B9D07EB6E364D2DC7EA4F155EC01A2D78FD1D9FBAE738FF00376AE86C6B1CCF546C1A4EF90411CFD29DABCE31F01ECC9BF19F040D58EEC44E85AB5717A65C5A186D2299135C983FC90D55E7117A3771E43C3A8D5E9F7B79699F056B1DEC70826216457B9839D111993E9EB29ACA24EDFA80409F821DF6082663B954075105B13D92AAD16BE5CE803523C5327AE8BC11BB62EEA36D5596D741B081A1710D6FDFAB952C6EABD6ACBC0BE8AABA5C61D1634B88EDB55CB286E437520055D9D3E8C571BDD74EC05D06001085689120CF2F26BC5C5BF2DE76BA8ADCE209FCE03D8CFF003F6AE2FEA5B09FAD5D1FCB25B27E4E5B3D55CECF2EA812DA9C7D4DAE05A5DFE8DFEF8DCCFCF46FAA5D2998FD7B02C277385E3F2394D8FD3E64B5739E33A6D10FB028A928AB2D35D2492494FF00FFD4F544924925293A64E929F1DFACEDFF00B24EB193635CDC4A725AD73F69D9EA3AAA6373FF003ACFE42CBC7E8D93D7F2ECCBC1B5B895E3868B6F735CC68304CB760DDF41BFA47ADEFF0018DFF8A0C4FA3FCE5DFCC7F52BFE77FEEF7FEEB7A6B56AFF00C478FE6FFA3B3E87D1FA2CFE95FCBFFB91FC8F553A5C7C236A598FDBE296F77ABCF3306FFB0FA79370BEBACFACCC86506BB1A5C1BFA11BEE6EF764B5F47E83D1DFBEEA7F3DEA66814BFD116B4E4080EC7782CB8388935FB3D6ABD467F5DEB62EFE9479FE7EFF00E6B8E723FE4EFF00CD97FA1FF805CBFE60FA1F487F3BFCDFD21FCEFF0027FD2AAD2E2BE8DE87B75BCFFABC207ED7571DCEC88143D8E71240638ED7123E9358EFE6EC43C86E5D409B68786FEF012DFF0039B2B773BF9ABBFE4BFE6AAFE6BF9CE7FC07F23FEE27F2D407F363E9FD11F4BE97F6FF00EFC992ABD131E2AD5E6DB90D2ED343DC23D7925BA83007753EABF4FF00C17CBE925D17FE50C4FA1FCF33F9CFA3CFFAEC4CD1935AD1D0651955E39C9CEB3EC58AD05CE7B9A5D66D1F9C31D9EFDBBBD9FA445E82FC6EA37BC574DB752DDAD7E45CE643647AAE2719BBBE9D6DD8CFD25B6FFA5F4D56FADDFD1B2BF9FF00E98DFA7C7D13FCFF00FC0FFDC7FF0087F595CFA93FF22BB8FA777D1E7E8D7FCE7FDF7FE0D3870AD971D6BB3A9D4F0CE660D95B768B1CC2F617090D73C86D6E688FE73D3DFE9FFC26C5CEF4CCAA717EBBD5D2DAC706D394C635D21DA966F732CDA7F95F4D7517FF003C3FA5FD3ABF98FE6F86FD2FE4FF00DFD55BBFF141D2BFA1FF0049FF000BFD33E8DBF4BFF44A78DC5B19D8D767B851524CAC3554924924A7FFD93842494D042100000000005700000001010000000F00410064006F00620065002000500068006F0074006F00730068006F00700000001400410064006F00620065002000500068006F0074006F00730068006F00700020003200300032003000000001003842494D04060000000000070006000000010100FFE10DD2687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F003C3F787061636B657420626567696E3D22EFBBBF222069643D2257354D304D7043656869487A7265537A4E54637A6B633964223F3E203C783A786D706D65746120786D6C6E733A783D2261646F62653A6E733A6D6574612F2220783A786D70746B3D2241646F626520584D5020436F726520352E362D633134382037392E3136343033362C20323031392F30382F31332D30313A30363A35372020202020202020223E203C7264663A52444620786D6C6E733A7264663D22687474703A2F2F7777772E77332E6F72672F313939392F30322F32322D7264662D73796E7461782D6E7323223E203C7264663A4465736372697074696F6E207264663A61626F75743D222220786D6C6E733A786D703D22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F2220786D6C6E733A786D704D4D3D22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F6D6D2F2220786D6C6E733A73744576743D22687474703A2F2F6E732E61646F62652E636F6D2F7861702F312E302F73547970652F5265736F757263654576656E74232220786D6C6E733A64633D22687474703A2F2F7075726C2E6F72672F64632F656C656D656E74732F312E312F2220786D6C6E733A70686F746F73686F703D22687474703A2F2F6E732E61646F62652E636F6D2F70686F746F73686F702F312E302F2220786D703A43726561746F72546F6F6C3D2241646F62652050686F746F73686F702032312E30202857696E646F7773292220786D703A437265617465446174653D22323032302D30342D30325431373A32343A31352B30383A30302220786D703A4D65746164617461446174653D22323032302D30342D30325431373A32343A31352B30383A30302220786D703A4D6F64696679446174653D22323032302D30342D30325431373A32343A31352B30383A30302220786D704D4D3A496E7374616E636549443D22786D702E6969643A34656266653730652D643236652D623834312D613962612D3232623834663439646366312220786D704D4D3A446F63756D656E7449443D2261646F62653A646F6369643A70686F746F73686F703A64633333616566382D343164372D343134372D626566652D3133623366363839326632642220786D704D4D3A4F726967696E616C446F63756D656E7449443D22786D702E6469643A39633734393932312D633861652D393634382D613065372D613762366131393131336266222064633A666F726D61743D22696D6167652F6A706567222070686F746F73686F703A436F6C6F724D6F64653D2233223E203C786D704D4D3A486973746F72793E203C7264663A5365713E203C7264663A6C692073744576743A616374696F6E3D2263726561746564222073744576743A696E7374616E636549443D22786D702E6969643A39633734393932312D633861652D393634382D613065372D613762366131393131336266222073744576743A7768656E3D22323032302D30342D30325431373A32343A31352B30383A3030222073744576743A736F6674776172654167656E743D2241646F62652050686F746F73686F702032312E30202857696E646F777329222F3E203C7264663A6C692073744576743A616374696F6E3D227361766564222073744576743A696E7374616E636549443D22786D702E6969643A34656266653730652D643236652D623834312D613962612D323262383466343964636631222073744576743A7768656E3D22323032302D30342D30325431373A32343A31352B30383A3030222073744576743A736F6674776172654167656E743D2241646F62652050686F746F73686F702032312E30202857696E646F777329222073744576743A6368616E6765643D222F222F3E203C2F7264663A5365713E203C2F786D704D4D3A486973746F72793E203C2F7264663A4465736372697074696F6E3E203C2F7264663A5244463E203C2F783A786D706D6574613E2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203C3F787061636B657420656E643D2277223F3EFFEE000E41646F626500644000000001FFDB0084000202020202020202020203020202030403020203040504040404040506050505050505060607070807070609090A0A09090C0C0C0C0C0C0C0C0C0C0C0C0C0C0C01030303050405090606090D0A090A0D0F0E0E0E0E0F0F0C0C0C0C0C0F0F0C0C0C0C0C0C0F0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC000110801C2032003011100021101031101FFDD00040064FFC401A20000000701010101010000000000000000040503020601000708090A0B0100020203010101010100000000000000010002030405060708090A0B1000020103030204020607030402060273010203110400052112314151061361227181143291A10715B14223C152D1E1331662F0247282F12543345392A2B26373C235442793A3B33617546474C3D2E2082683090A181984944546A4B456D355281AF2E3F3C4D4E4F465758595A5B5C5D5E5F566768696A6B6C6D6E6F637475767778797A7B7C7D7E7F738485868788898A8B8C8D8E8F82939495969798999A9B9C9D9E9F92A3A4A5A6A7A8A9AAABACADAEAFA110002020102030505040506040803036D0100021103042112314105511361220671819132A1B1F014C1D1E1234215526272F1332434438216925325A263B2C20773D235E2448317549308090A18192636451A2764745537F2A3B3C32829D3E3F38494A4B4C4D4E4F465758595A5B5C5D5E5F5465666768696A6B6C6D6E6F6475767778797A7B7C7D7E7F738485868788898A8B8C8D8E8F839495969798999A9B9C9D9E9F92A3A4A5A6A7A8A9AAABACADAEAFAFFDA000C03010002110311003F00FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8E35D5BCC737C2D3C9BFBFF666A389DAABAAEB92D09964A9F7C78D5CFA56B736CD2C94EF538F1AA82F96F534916762EE10D78F8E112B54DEDED35BD3EEA09B45B992D25620CA14D3265239BD8EDBCD5E6BB286DA2B8BE79A375FDFA924D4D3205B0232DB5BD4EF199998E5459C58F6A9ABEA16F71CB9114EF9125B120B8F3A5CDA026573418AA583F34D616346AB5298A09A424FF98F2DD0F80D2BE18691C4925DEBD7DAB05B61522434CAF2C51C6FBA7FE7122F60D17CBBA84175B333D4D7E6329E172714DF5E6AFE7ED22CE1043865814FAAB5F118F0B9A26F8C7F30BF306DEE7549AE2C8889391A95FA71E1499BC4758F3D5037A77ACDEA0AB47CBAE4E30713366E12F35D43F302DA14901531CE7ECCA7C72C11718E7796EAFE6ED57516663AA12A0515431E99608B8F2D430D9A677E524F3FAA4F5A9AE5822D0750978B98B8D140D8E4C63B63F9843BDC13F64532431B1F1AD0C5DB916EE72D8E362656ACB7457BE4BC3606368A13F2553F7E5321BB5F0EEDD0B6F916C0141ADD0558F5385BE2690CC5536E98B682985A4527312ABF1A6F5C9F44527A6F5255114979C48EC4E424136EE70A2D2593E54CA2516432D265A76A6F6B5FAACACBE041E995F0B319DE9DE5BFCCBF37E8C54E9FE619A075E8A1C8FE18699F88FA4FC9FF00F395FE6ED08C0BACAB6A71A302D2BFC5B0F98C890D8323EA6F247FCE5769BE63B8449ECD62F50F5D875AE564360C8FA2EC3CC5A46B32DADF898514F2F4EBD72B21BF14F74FF4D7626F1F53F8A192BF5207B785320439FC4115A4693711DBDF5F5F12CE8C0D913D857B640865CD910BAFAE46A41FEE46F95909115A6CE4BB46B98FAC20D7214C643662170CF7323C858AFA27E2FA314452DD56DE2D4F4C9F94A6B1827AF862DF163FA65C8FD1C2C11EADCA80D716F09EC33DE42A6D1472A2824F861069AB502C331D3BCB3E55F3C412795FCE9A746D05DC6CB0CEEBBD4A9A6FF3399784BA4D445F8C9FF3931F9253FE507E61EA96B145E9F97B57919B426A6C402C4D3AF866C605D3E41BBE5BAFA5230FC32DB583B90724F865D8B930CC5BCB5A16375C55AC5578E98AB78AA8BFDA38AAA2F7C55A3D7156B155EADD315560D8ABFADEFF009C6EFF00D677FC84FF00CD73E56FFBA45AE5A39349E6F68C28762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF917B754FE45FBB34FC2ED532A855A803C71E1551370C0E3C2AADF5A26361ED863155D0DC52840DFC72C2A39A6893B3D0124F8D7205B423E172F227A6C401D86D9516714AB5F0D52FD80A139193607916A21A49641526A722125825DE9F335C1F4C75ED96C434929D699A6C96C434DD0F639708B519B2F86D62F8446DC7EB1F0971B52990CB0D9871BE87FCB0F341D3236D0D5B8C6E3E2981A1FBF28E17231CD35F3479E9A06B8D356666080FC7CAA4D6BDF0F0B9919BE7FD5BCC7246F2B3BB10C4EC4E3C2994DE7979AD87666AFDAE9921171331B2C0F56D4F996EFF003CB045C5931D8EF2A483F765822E3C93012875EBB1C988B4108794A034D865910C68BA38D5CFDACB0048895EF171AE4C36479A0A470A7A6580362A4738A2F877CC49C775A4724E0D32149016B354935AD71A4A1DD6BD77C7853689173C4000D282981B835EBA75A0AF8D30316FEB00F53CBE780C5810510B7617A1A7CB21C2C68DA6FA7EA011872EBE39021BC16463520EBC791A11BF8640867128FB5D46EECDD26B2B9642A6A021A6408660BEA2FCB2FCFDD73CBED676DA8D64D3C301752BEE42FCCE56437639517E927903F33746F37E9D6F35B5CC6CC69C5188FE272B21CE195EF305FA4D63FBD6531AAF4047864086E8E4B486CAF94DD3C40F0427627A64086D8CD96A5DDCDA59B81C5A07EAC065720CA52B09035922C373792515250683B641116276C6D6D61BBE6F53302029E9BE2DF179F4113DAEA124A8F54249515DB16F0CA34926E2FE49AEE5291C6018F7A54F860465161EA22FDB4CB9B1BE11ABC42946201CC9C45D46A60F8AFF00E73E6C935ED07CAFE6151B69FEA733E1CB966C6127479A3BBF21B5802DEF21BB07F772A9A2F6CB896A8B7129F492423FBC15197E12C7338F7CBDA14FB8C555475C55CDD7156B156A98AB78AA8B1353BE2AB6A7C7155E3156EA7C7157F5CBFF0038D9FF00ACEBF907FF009AE3CABFF748B5CB472693CDED5850EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF91688D3E8CD770BB55732D29BED5C3C2AA125C2826831E155317408208D8E02155E1B85AF4C8951CD358A7AEC0640B684DB436F56492BD16B9596615B5884496D2F7DF6CAE41B2245BC82F22E370C08A6F82213221279612262C82A46E7E599100E3CCAE5B8127C2F41C732621C6949524D42354F4A234917A1AE47306A2535D3BCDEBA4425797FA536DCC1FEDCC76FC4509A9F9B4384B899EB24952C6B5FE38D39824C1F50F30477E480698D24CAD8EDC5C8D85474E95C9C766360A4574C1CF635E9EF930D6404B3D13EA5541F0229BD72C01A26023D4B200A41E5D969BFDD9688B498A9C8A643CC30A7422BD32241B4C601C9CD0D0D4622D78559E6DA85BE9CB031A4148391EBD7EFC95A3758ABD0572A31B6611B1823A838384A95415A9D8EDDA98F09415600537EFDB070962DFD589EDEE477CA8B782D7D5877207CCE16629B36E3C4570F267C214BD23C80EE4F4C2427802690DB9461BF5CA484009D470F214276A6FE232B21980A5CBD19290CC580EB4DE83DF2B660332D27535212DEE8FFA3C9B487DB20591D9EA7E53F3E7983CB371CF47BA75B588D42722361ED5CACD22322FADFF002DBFE729E486FECA3F324A5B4F43C6EB7AD6B403F6B2B2DF0917D9AFF997E53F30E976D75A2CF1C72CA0114615DFE9C8172A322F48F2E1BBD5F477844BCCF02EA6BE032B937028CD596783CBD6EAE486690291DF2A6C890F3BF35C434FB6B478C9AC8A0B530D37C4879A9BFB99670B1D41AED5EFF2C0DE0B228A4BA8602D3935603D3A78FD1912CE554CFF004DBBBDD461B5B6949F4D3BB6DFAF32313ABD48BE4F08FF009CBD8E0D47F29B5582DF79B4BF4C311B9F889E99B081D9D0E789B7E39EB9664FE87818D4B86E5F7E5A1C5029425A239806C20F8732B0726190DA1CF7CC86A531D462AAABD7157375C55AC55D8ABB15506FB47156B155E3A0C55BC55FD72FFCE367FEB3AFE417FE6B8F2AFF00DD22D72D1C9A4F37B56143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFF00FFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF91745DBAE6B5DAA0270038DFBF4C557A007DF1B569937DBBE025217C042073DC654C931B4B9E50CA83AF638153ED2A660AE69F7614A367BA58ED6466DC57BE210497946B3748D33508EB930030322C65AED519B7A6D930C0A4B25EA976A1A6581AD2C9AE872FB4704D38D012DCEFD72B6D40CD37A94DCD07BE2CACA024DF7F0C56CA8140C8FF00BB32CBD8569F0F7CAB27369C922D18005B7B89916CE2B561340E5BAB2F4EB92C64B5D943DB5FDB89F5295B5385DAF559A094950D1CE7B04AEF99B8C2D95595FCC31E95FA5A2F2DEAB79369A4C971E6692CA54B64886DBBF1E1B7BE654698F1747A07953F25FF00357CD5E47B8FCC3F2CF921F5AF26DAC924BAB6B8AEDF686F20A01B713919D1482F2ED2EE9AFF0053F4EEAE9567B5774B4D35A8AB291B1591BF678F6AF5C85320537786D4C727D62D7D2746ACB0024AAB57E1A37BE16C1BA0A592333C1F5AB6956F6452D6B040A646283AFC2B8B2D91111FAC46DF5AD0357B7907FBCF3A594ADC97BBF4ED8D30F103505E5DC61A19341D524914F1B6B916725593FDF9C698D3094C2BC17577C9A69749D5DD17E004584A7930F1DB6C698198519259239ED629ECE48EE637F5EE6CEF94DAB7A0DF64D1E87010C6D165EFA492CFF437FCEC1AD7982636B06996E39B2843F0A8095272AE0B6424C9F59F2D79A3CADA9E9BA7F9E7C8D3686FAFA7FA33DD1786BC056A3985C85331262333C3A54F76F7BC648AB4B360D5AF80195D16C12513E60D2CD80917D45D661916331703F0A31DDBE818981642749BC3AE6906375955CC900F87512A429FE19594FE60222D35781D3EB68CAD1034926660015F9572B3129FCC840C7E65D1D24BEB4B581E196ED089EE402E021EE32070CAAD7F3211EFE61B4168AB6D299934E50F75301F184E8085EF90F02523417F34193E95ABBEB4AB7361A9FA4427EFD88A72403A53C6980E92498EA8047DBF9C7CB567070FAFC915C4CE561708580643F1127B54E0FCA49C886AA2CCF4DFCE48343113DBF98273C2842D0D36FA71FCA172A3AB8BE9FF00CBBFF9CDB4D1FD0B6D435C905A2AD18F0A9FF896425A434CA7AC14FAC348FF009CD2FC9CD42C53F4D798CACC46EA631D7FE0B043465C6FCE29EAFF00F393FF00939ABD91B3B0F33299266A44F22AA8527BD4B659F932D90D6EEC1B4AFF009C92FC948F548E1D6BCDBEB7D5488E20B10E0B20DAA581E9954B4843B0C5AC0FA4F4CF3168FABC116ADA4C424D0EE943D9DEF51231DCD3B653E018736EC9A8E21B326B4D5ED26FDD28E20EDB6D8D535DDBC63FE7231043F97F76F6EA5FD68D8B83BD681BAE646271B34453F1A350B93EBFD65BAC723023C3E239B1C54E8B53B294BF1309FB5C0E4332C38504337F1C2CD662AAABFC3155F8ABB15587AE2AD62AEE15DEB8ABB80F1C55DD36F0C55D8ABFAE5FF9C6CFFD675FC82FFCD71E55FF00BA45AE5A39349E6F6AC28762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF90949CD3DF35AED52FB89CF2AF7C55B86E091D7E78AA21AE147461F7E02A39A924D50F534F0CA99A334E620B96D86F4AE054F2D6EFD2473D3C3154A750D59DAD254AF1F88648734179BDEDD33C8C49D8E4C302C72E27356DE9930C6492F362E77DB26D6A44FC66A69B7538CD38D42523B1FA72B6E435483D7156B155F63A7CDACEBBE57F2EC774BA6C5E65D66CF4A9755A8E708BA9021600F600D6B93C70123BB466354FD9DB4FF9F49FE51E9B35B5DF9C7FE725E596D2C19259EC276B48A07400311EA48EBB7C865808ADA838F7BBEA8D27FE704FFE70B7F2CBC93ADFE62EB5E4ED2FCFDA1F97AD1F53B8D68CE66458A1AB332885B893F4E5D007DEA4EFDC5F217E7E7FCFC07FE716353FC95F327E49FE4E7952E35AD3FCD1A73D92DB5DE9DF548AD1A421898DE956229B1CBE2A0D9B7BB7FCFBAED6DAEFFE7DEDABC11C097125CEA9E608E58A641C5CAD0F02C474D8EF91931E27CE5FF386BF901FF38ADFF3939F947F9A5E46D4AC20D13F3DE4D4AFA3D5B502693D8AA5C31B692CB911EA29FDBA74FA3229E2A7E6C7E75FE48FE62FE43F9FB55FCAFF003FD93DAC31B9934FF32509B7B8B752CD0949A9C492805403D7037465694FE5779EB49FCAEFCC9F267E62EBFA147E6CF2EE9F1490DDE9774291BF26A6F8B2E2EAFDABFC97FF009CC4FCA9FCF4F37697E48F21FF00CE3B5ADDCD7052DF51BD5B3AD9D9DB103D4066084034DC570B1E305EEDFF00393BF9B5F917FF0038B736896771F9223CD9E6AD663E3A258D869FF588BEAA48520BA275A9D862C38DF24AFF00CFC07F285102BFFCE28EA5A6B472F2BE54D1A4243F7A829818193E5CF2EEB1F945FF0039A1FF0039BFA5493F92469FE42D5B4CB5D2E7D04C660314D0D43332281435070B0326BF337F28BCA9F919FF003F16F267E5FF00E5ED82683A169F269B7D6F08FDE822E95988A3D41D80C89E4CE25EFF00FF003F7E5B6B6D7BF216FAED56EE1B8866F5D446B172F848FD803B9CAEB9B3069F945A4FF8720F3D7902F7CCBA4407CA305FC0757B66735684CAA5B90FF572BE8DAFDCAB4F397FCFACEEF5A9560F2669B26AD218AD25536AE12579540001F528C7B5460970F716278AB9BE8CF3FF00E497FCE0E7E5E7E5C69FE7AF387E5468DA6F90EF8441677470419BEC02858124FCF23C239D7E3E4E29B7CC49E6EFF9F50CE925A2F93B4A8B4E1113C84120527C38FAB5AFD3938C224F543E93FC95FC95FF009C13FCF1F2BEA3E60FCB0FCA9D1F52D0EC646D3EEEE9E378D870A93D5DBC32D18815DDE0B79E65FF009F5A683AA6B3A4DE792B4AB0D6B4B9A7D36FE25B7763CE0AAB0E5EA53AFB75C9C71041BA7E10595CE9716ADE686D3E258B449B5DD4A4D123AD185A191BD15A7871A65BC23AB5926F67DC5FF3827AD7FCE27E9FA779F3FE865346B2D425D52E61FF000CC570865F4A341FBDA85652A49A1EB8384364645FA0ADE64FF9F54F0E2FE56D1154776B7907FCCEC7843689C97DBEBFFF003EAD240B6F2B684EC4D14185C024F6AFAD80C026539232EB55FF009F5B229F5BCA9A1AB20ABA7A7202A3DFF7DB618E30D5C459F7E5FF0090FF00E7DF7F9C370BA2790BF2B6D35DE5F66FADEDE6FAB236FD67595941DBBE1300CE32931BFF009CC4FF009C6AFF009C4DFC9FFF009C73FCD3D534FF0021E85E59F375CE85247E5495A765BA92F792F130866DD857B0CA66221CAC5399237D9E1DFF0038E06E1FF24BC8D6FA85CDC48EB67037D52742BC18C62AC09DCD7357A900BB9C123D5F4B695A658ED26C48DC819824539F1281FCC5F2E5A79A3C9FAA5A7107EAF03F15EE7E16E99386CD59793F083CE5A73699AFEB5A5BC6D088677081C52B466E999F8CBA3D4C5283B5B59A9054AA1A5733226C38110866FE39364B315545FE18AAFC55BC554CF5C55D8AB5CE9B53157733E18AB7B9DE9D715750F862AFEB97FE71B3FF0059D7F20FFF0035C7957FEE916B968E4D279BDAB0A1D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8EE4B96A57F0CD6BB54BEE6E4F2FD78AAE86E01502BB62AB1E41CC1F7C05211724A0FA7E1953247B5D0458CD771D302AC9756E0B4AE2AC5EFF0053332B28F1E9921CD058C5DDC9A1C98605209A72C684F7DB2618C96890713936B5093F7854786F8CD38DBF476F6CADB96AC542DF462ABFD318AAA691E55F3379E7CD9A1793FC99650DD799AF254BAD31DD8AC8AF1B7C3C69EF976201C4CE5F60F993FE70D3FE739BCCF6ED6FE636BAD4D0A7A0BA64974C55430141414FC72EA01A2CBF643F2F3F273CFDA2FF00CE0F79D3F27751D3560F3D6A7E56BAD3AD2D236678CCF22154058926A49DF7C9C764136FC52D1FFE701FFE72CB48B23A6C3E43D327E0C6E11A590B4CA587B532C0520D3F6A7FE70B3F25FF00307F2DBFE70CB52FCA9F37592691E78BBBFD6E6B3B02DD56EF8984D76EA01C892C0C9F94DF94FF00F389FF00F3995F93FF0099EDF98DE46F2ADA43ACF97B58B9B9BFF5A5741716D34DCA84568C0AD6981417AE7FCE7FAFFCE51FE6E79534AF35F9F3F2AB43D1FF002EBCAD1B32EA1612BCB7E1CA1F51AE118D5072AD074C590953F38FFE71DF41F23F993F327C9F61F9CBA9DDE9DF96FAE3B23DCB201688E1C22891CF40316CE27EE04FFF003953FF0038D3FF0038DF77A57E43FF00CE2AF97F45D77F31FCC37D67656F7518A6957724DF097B9BD427E3626800C58195B18FF9CAAFF9CCDFCF4FC80D7BCAFA67E687FCE3DF94F59BED56CC3691AFD83497715B5C16A2A4323861EA03B85271636FABFF002FFF0038507E40CFF9F1F9F7E46D13C8B05BDA9B9D3F4E4445935042AC10CEACBB48C69B0C5812FC5DFF009C15D5A2D67FE7381F58D3AD6DADAD7CCBAE5CEAD690D688B677723CB1C6941D830DB16264F56FF9CB6980FF009FA17962F26790A01A3C56CD08AFC512BA95FA2BBE25B81671FF003F8D92E6E35DFC86B458964B3FAB48F6A53790B537046408641F93B24769776F1C5A8D8BDC5E5005AA9AD32A21B8153B8D1ACADF59F24FD5E27B49CEB5640015A8FDE83B570034CB9BF793FE7E412ABFFCE01F962299A79644D5F458DA471425D6A77F6DF2C8C6A9818EE5F822DA1697A868B63A7E97A1869E3B413DF5D92F5F5075EF84367007EF97FCFA0C347FF38C9E7F8608E44B9FF10DDC61C0FDA11353EECB049AB806DF17E12F9AB47B1BAFCC0FCCEB8BA8DA7BDFF156A70A3396A02262C6BBF8938253D9B3806EB5E3B64FAAB416104933FC0DC4D7EC8CC7F14B1F0829A683A75C3FD616C230D727F7CA091B8F91C7C52918C27967E5AF2D4C04779A2A4DE22AFF00D71F14A784226DB4BF2FEA7AAD9F93BCA1F97771E62F34DF4C9FA3B4FB38E4964E75A091954D4283D4E4A323252007E937E427FCFA63CE7E6BD624F38FFCE42EA16DE50D126845CC1A268937AB24A08A94B9E6D5414EA2B970B69343DEFAFBF303FE72AFFE716BFE708FCA571F975F92DA2D9EB9E72107A561A1E8CA67B496FC01F15E4C0B1526A6B4C8CA7B338C093BF27E59F9CF5FFCCAFF009C81D6EDBF31FF003BB537D6EE21B8F5743FCBD8E42FA6D9467EC96E276A53BE60E6CF5C9D8E0C3B6E1F657E5BFE6369D731586871B246D63122D22A1B745A50468C07ECE61711939F5C2FA534DBE8E15925460EAE054D72328B7C24CC22D5AC61D12EA696CBEB7CA290B102BC7E13918AE4E4FC43FCFDBED3F5AF3EEAA2CE0F41D256DA94A6ED997093A7CE1E1FEA48CAB1C8FCFD01C57D8667623B3AF229D96A1D8ABB155CBDF15546ED8AAC1D3156F15770077AE2AEE03C7156B9D36A74DB157733E18ABFAE6FF9C6CDFF00E71D7F208F8FE5C7957FEE916B968E4D279BDAB0A1D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8D34BC51D4D0F8E6BA9DAA5D757887900C2A71A55B6D7229BB0AE34A897909F881D877C041506914D7085128E2A32AA2CB88285DDE7100A372E23B634578825125F3B6DE3DB0526D0734E0296AD08C9008291DD5D541EF920C0A4C65777FB27E7930C485FCC81BFD3930585174720E677DA9824530148B0F5141BE41B2D48B91D8FCF15B6BD4C56D917917CEFE6DFCBCF3E68FE74F25D8DA4FADE8686E21BDBD1FBA4E07970AF89F0CC8C3C9C5CFCDF7349FF3F5EFF9CA09EC6E2F7F40F96EDEE2C9C5CDC4A63A3948C50D169EDE39750B71DFAE1E46FF009C8DF3C7997FE70D7CC7F9F77D6D65079CF43D126F318B287FDE7668D4B05229D0853DB26021F945E58FF9FADFFCE5A79B35516DE55F22681E62D56E938AD859DA9967DABFB0057B78614BF5BFFE7113F3E7F303F3EFFE71A6FF00F34BCD7656107E6269DAB6B5A78B38978242DA7FD8475A7C2DB1A8C04354DF9BBF941FF3F0CFF9CA9FCDFF00CFA87F28345D1BCB916A1ABEA975A7BCB321458A3B4765E4CD4EDC70522934FF009CF1FCF6FF009CA8FCBB54FC9DF3CC3E4CD4B45FCDAB77FABC1A0167BE8560F85BD6561F0D6B51F2C5987E547903C85E7FFCE1F31E95F90DA75CE97A4DCC5234B6925EBFA51B3F2E410B7892705864FD16F257FCFA23F3F26D7B44BFF33F987CB7E4EB2D2CACD0DEF97A57FAFA5C210D14B5634E4AC01AD70A0BF577CE1E75FC81FCA3FCB5D0BCB7F9F7E7DD07F32353F23411CEF26B7325C6AD737D0F499537AB531624BF32FF00E7323CE1F9DBFF00392DF975379D3CA7E4D9FCB9FF0038CFE50893559ED75188C37B3C51FDA96341406234F84E1A60FCCAFC9CF2BF9BFCFDF9CDE47F2C7E5B79864F2479AFCE0F1D9F917558E43088E40A5C7AAC3703883D31E140897EB27E5E7FCFB5BFE727B4DFCF1F24FE6C7E63FE61689E709343BC49F53B89E691E765414DAB4AD6B5C69B86CC2FFE7EF9AD68DFE37FC9DF2EE8BA9A4BE67F2DDBDCBEAF6D237C36EB22928CDE0180DB2259805F3C7FCE28FF00CE04F9B3FE7277C8B379FED3CEF2E942398C3C1E42073EA05011EF913BF26775CDF5A68BFF003E80F35DB6ADA36B1ABFE62B5E3E957F6F77F57E65AA20903D055BA9A5300891D14CC17E927FCE507FCE31CDFF003909F913A5FE4E5B6A8FA49B39ECEFCDFF00EC97B4D954EFDF2C0C0CC9DDF9B23FE7CF5E6CB5D3445A7FE68CD0DDBB5651EA3052BDD766A644829F10BF463FE710FF00E717AF3FE717FF002A75DF235E6BF36A771A95F4DA9497719F84348A568373BEF954C90A245F9F1E67FF009F44F98B5AF37F9975FB0FCC06B6B3D7B50B8D4C42F211F1DCC86460C037515C4094BA289D17C03FF393DFF38B1AC7FCE29F9FBC85A4EA5AE9D56D3CC32395915890415F72727E1D06CF11E5115ADBC0BC0ACCAD3B3189E9B75DE990E109E22AF02A25CA98A4775EE7B60A0C6CBEB5FF009F6E24C3FE73B742F456393FE752D5CC824EC9C7E265F714DB2E8061225F657FCFCFFF003C3F35FC97E6DFCBAFC96F2179A27F2BE93E74D264D47CC3E6289FD3B9611CDE998B9D3BA8E9965048912FC9FF002E796B4CF2F6A1746D8A5FCB75704DF6B7A99ADCBC86A4C8A6BD77CAB200DF025ECD06A30454B7B39648D29FBFB81D251E0C7C335D962E7E29109E69570F65730DF69E56CA20DF15B45B4751D5BE6731E31A72B8ADF587E5979ECDFABDB5F5C28E2005E47AFE18261BE05F56793754B6B9B7BDD2DD11E3BD8D979B741B532B00B39BF217FE726FCA5FE19FCC1D4EE238EAB732332D3A1A96E999100EAF5043E588DA30F351BE22DF1A9EA0E67E2D83AD9157A1F0CB98BB7F0C3452D12075C08B5CA46FBE2AA8C46DBE2AB01DB156EA3C715773A6D4C16134EE67C31B5A6F883BD7AE3687701E3855FD72FF00CE367FEB3AFE417FE6B8F2AFFDD22D72D1C9A4F37B56143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87A96FEF941ACBF8E623B3B0963DF5F16DA5AE34B611515DDF541F571A5D9318B53BD42A1E4AA7ED2E0931927706A3CC0E9BE52D61166E7638B38A5E6EBE33D4E2D8103717049C52833FBC23155616C42F21DF15434B1D3B62A868D7E36F0F0C5894746A3DBE8C582BB461BE43155168462AC93F2FBF2CFCE1F9BDE7FD1FF2EBC9776965A8EAF19905C4E6900A1A7C7976271B31DDFA2BA47FCF9FFF003D7597F43CC9F997E58B4B688049A0B3123CA57AD18AEDF7E640F268B7EC4F907F217CA1F937FF0038F179F955E75D6E26F23C1A335AF9A359B87280447FBCAB9AD12872616DF1CEABFF003963FF00381FFF0038BD6BA743F96BE5BD23CF9A8D847C5755F29411C972838D2B3CEC2B5DC8241C905B5BFF003EB4FCC58FCF1F979F9AFA45BDB470DBEA3E63D675CB68140E5026A2EC544B4EFF00150E4BA2F0DB17FF009F7DFF00CE324FF963E63FCDAFCFEFCCDB44B09FF4B6ACDE5AB8B8F84DBC31CECCF23927F6C74DB010CB81F96DF9DDF9D7FF002BE3FE7253CE9F98D6F74D045A7CCD65A2E9970D50A9033C3CA01414E4054E56436C616CDFFE712FF28FF257F327CF9E6FF31FE72FE7149F963AB796353B69BCBCB6F75F56B8942307356F0AE459786FD51F38F93BFE71CFCE8D7B3AFF00CE71F98B48B4344BB5D3B5DE01005028483E032A32A3CDA4E2277A79DF977FE7DABFF388BF9C724FA9681F9DFE67FCC6BB8EA6F35A1A8ADDB0A750CEC07864F8C745E0A7D95F97BE4CFC8BFC81F25FFD0B3F99FF0036D7CCFA7F9B4496BA47977CC577EBDF4905C8A0B58A951C00FB2B931913C07B9F1A79AFFE7DDDFF003895F947E615F32F9A7F3CF59FCB2D465BD7B8F29DF0D405ACB64ECC4AADAB863C4A5683DB26269AA1C932B9FC9FFF009C6DD4E4B4D31BFE73E3CEE249C520787CC4435C9EA55985795326770C385F15FF00CE607FCE3DFF00CE35793FC93A9FE677927FE722AEFF0035BF320CB15BDBE9DABEA02FAEAE854A30AD6A780ED95C837C075A7C55E4DF31FE71792B429EDFCA5F991ADF923465E12CD616174D00776151B2E634B6673887D0DFF38F9E6FFF009C92F3EFE7CFE58F92FF00E576F99F50875F9E3D66F2CA5BF77A5BD9C80C9148A76A3807614C8F38DD72712517E82FFCFD9BF3BBCF5E43D53F2BFC95E46F376ABE50D47CC9A7CBA8CF75A64ED0168E297830629BF6F1CB9B7146F67E4949F9B1FF0039096D688A9F9E7E6F8AD66FDFCB72DA8CA013ECD5AFE382DCA9C283F6FBFE7D73E78F3479EFFE71CBCEFAC79EBCD5AC79AEFB4CF30DDC2BA85EDC34F27A0B192A159EA695A9EB8251704737E2FF009DFF003E7FE720AFBF30FF003022B5FCE7F34697A7DAEBF7D69A7410DF3A2450C729F4D68294016836C31A05CD8E332890C035DD5BCF3E67D574EF30FE64F9EEFF00CECFA07C7647509CCECBCF614AE4E73586911714D00BE54BA9663752A97D3E2AFEEC022A6A3E5989293951D3D2B5AB7A48ED5FDAFE3862584B1BEBEFF9F69C9EA7FCE75684C3FEA4ED647FC933991170F2C69EDFFF003F6FDFF3EFF26475FF0070139A7FD1C60916A897C11A8C06541DF8C9CA998F22E5C0A7769767EA8B08FD9ED9448B971679A34DCA08E393ECD77C85B75DB34B4D4A5D2EE2196CBA0209A64245CAC6FA93F2F7F32121B8B35B9702BC565AFBD3DB220B64F93C6BFE7316D6CB516B1D6AC183B3AD5A9F4E64C0BAAD445F9DD35A43EA99631F13EF2FCF2FE275A614A6E782D075CBE05690E243D3DF2EE882BC0E79596B937C78ED819479378B2762AEC55ACC79736D1C9BC4312578CB43592EEB920C097F5CDFF38D9FFACE9F905FF9AE3CABFF00748B5CB87260F6AC2AEC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF864BB048AACD51DB311CEB4BC2B147612EE01C56D17621DC0AC98ADA65E8307525EA2BB8C124129D5B45D38F4CA501306040229D716C8A58E28E49D862D88571C9F6DC629548E36A8DB154D235AAD08E9DB1541DC42687E1FA7154A1D5D0F43BE282A91C8E0F4C58F0A3519A9BEC3B62BC2B8891BA2938AF0B21F2279EFCFF00F94DE7DD13F323F2F2682DBCC7A3AFA3682ED3D5818B1AFC51F7CBB1CA9C4D40DDEF1F99FF00F39CFF00F3963F99305EDCF983CE163A19D22DDC48BE5D88D8BAB28AD1F89353F4E5DC40971E9FAFDF975AC7983CE1FF003EDFFCC2D67CC3ADCBADEAFAC793AF6692F6F5CCACAEC87A96F0C9476B5A7F387A7BE85A5F97E686F0C4B71A859A08A7863264776DF812A09AEFDF240AD3F707FE7D29F951F9A9E58D67F327CEFAF794AFFCB3F979E6FD06D2D74586F289F5A962209786B4A13D6A72D06C289D3D0FFE7EADF9D7E75FCBCFCB8F2D7E4FF95F4E9747D37F3126922D63CDB6EA5623146D510C6CA2AAC4FDADF7C494F1EEF91AF7FE70E3C83AA7FCE05F92BF3AEF2F5BC95F997E5B8A7B89B5A998AC37BEA48591645EA49A0A13EF9592D8323E2BFF009C7CFC90FCC1FF009C9CF3F6A1E5DF285F695A3F99A0478754D4EFA15781907C3CD50EF5A6F91B6632ECFD44F20FFCFAB7F26BF2AB40D53CD9FF003943F98B1EAF1E9122DD31D16E0D85B451A7C6C6757AB301DE83A60113F0419DBEF0F30F9D3F2CFF00297FE7157CDDF985FF0038E51E8F77A2E99A34D3E87AB584002CA507C25DB882FDF7A647C2A533B3BBF9ABD5FCC1E62BDBFD3BF38B52F3E1D5FF00332009E6CB79656692289588A59C486BC597C06110A62723F717F317CA1E4DFF009F8A7FCE1B683E61F29DEAA79DFC9B6E93C371250BAEACB12ADCC53746E24D763921162676F3AFF9C76FF9C4AFCACFF9C2AF256A3F9DDFF392BAF68DAC6BB25AC8348D2EEE1F56DE1050845489C37C66B4AE4B8D1C4FC98F3C683E4FF3F7E6A7987CE9A0F95AFF00CB7F97BAD5E493E91A34D227AD21690B17B72BF654D4103C32B3367E3523BCC3368DE5ED38437CCF631C29EAC4D72DCC48631544A0AD49A532A3BA7C6B7EAA7FCFAF3F21AF74CB6F31FF00CE577E62D9AE9F1EAD6D2BF93BD61C560D302B7AF32F2A5140527135F00C38ADF0C7FCE507E6BC1F9E5FF3923E60F3559336B1E4BF277ADA2792A590F34B9B5726B3475EC1AB91F136761A7C24EEF13D4A2B98216F556DEFED9374B08928CA3C0D76C7C47272E2D9FB2FFF003E9130C7FF0038CDF98534B0936D279BAFFD6B75D8AA08A9C7E8CB89DBE4EAB83D4FC42B8373FF002B0FF346E6C618E5B6B7F36EA8E7D75E4157D634041F0C864950761A68F34C66823B9FAC35E7A572978ABF0C0BC42537E99852CCEC052AAA97948E11B154E30923E3000EC721E2309CC24B3CE90432F3902146A3D4F427A65F09383972D3EC4FF9F65C7327FCE717971E44204FE4DD65E13D792F03BED9990E41D74F2713DD7FE7ED2449F9FF00F92E10F22FE5F9CA7B833E5732B10F862E5828B84A5655AD5331E45CBC6126D3AF59262937EEF7D81CC79172E2F55D2984B0523352AB5DB204B918E36C8F4C9E559024919604D2A72B949CA80A679663EAD494CBE9348414FA32024DB241F9EDEF35FB18ED9C35C431A101FA819938E4E0E585BE39D634AB9D26FEEA39A268D2662D096EE3DB2EE370E5898E354B6FF7E6463938D38D34D18D88FA73281D9C5915363C7A76C8940709053734381B03B9A78E2AB81077071575478E2ADD3C329237656EA1F0C900C495DD865803592DE4A9812FEB9BFE71B3FF0059D7F20BFF0035C7957FEE916B96057B562AEC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF86BB8802C67311CBE240DB460B32F63B1C57899159D94483A7CB15E25595139003AE465C97891F6DF00CA9215659878E2D914A669013D6A7B62D81411BE21E18A519137BE2A8C57A6F8AB52BD57DF154A6615A629014E35DFF008629A469140B8AD2262C5682DBA92EE182EA4897D5B636D227A6366F588F8581F6C944B46585BF40FF00E70A3FE70BFF0024FF00E723FF0029B5BF3279AFCCB2E85E6CD3F54FD1B7D1CF7CB1F2A8279716604E5FC449B718C2BA3F4B7F3B2EFF00283FE71B3FE7103CF7F9456BE72B18EF350F2ECFA4796A059D267BDB87144850231219B975392053E1DEEF8D7FE707F47FF9C0DF27FE58699F9D5E7A920D17CE3645F4EF33E9FE629BEBB0472C5F6A58ED983503B74A0C9DA38084AFFE7217FE7E75E7BF365C5E7907FE719F438FCB7A1D9BB245E63302FA77D68A6A9F554D84551B9A76C78E9A678492FBA3F267F36BF20BFE73B3F246D3F2E3CE2F6ADE69D3EDE14F3568B7AC8B7B693DB95E53432C86A3D4707ECFBE0333C988C469F327FCFCB3F353CABE53FCBAF26FFCE267E5CDFDA3C1E60317E9B4B77594D8C5624344ACC8763215A1191E3A663097E57797752F3AFE5FF9C6C7CCDF943A9C9A0F9CF47B37B69ADC3304BA5917E290814069EF94F8AD834F27D89FF38B3F927AAFFCE6FDA7E64798BF3E7F37350B297C93A92E973E8C3516B68AE39C65D8B46CE015A8A5732465003038E4FD4FB9B5FF009C5EFCACFC8DB6FC88F3D7E61E976FF96F73A61B09B4D8665F5F8B1DCB4D11635DFAD301CF148C322F85A05FF9F51FE534B65AAF9524BCF3E5F69779EB5A587D6E6BA592E54502399556AA41E94A644E701B069645F0D7E4CFE707E71FE4DFE6779E7F353F27ACA7B7F2BDEEB5777FAA7901F91B67D325959D04716C81E8C454657F9A8B68D148F47EAA587FCE5D7FCE207FCE64F97A0F277E7DD8CFE44D68148A2F29EA333219A56D898E455E2373B03F7E40E51F51E5E4C65A390DBABF3FBFE735BFE7193F2E7FE71D6EBCA4BF931E6B9F599BF306493F44F97BEB7F5C7B6892AE2854B05A8A0C81CA48B3C9A8E9A47A24BFF3851F919E41FCF8FCE08BCB7F9D5E708F47BEF273A5EE8BF97374C527BFF4784AEC676A2B2AF75EE36C1C6472EBD57F2E62FB7BFE738BFE730D64B5BBFF009C5CFF009C7DB8B4B9D4AE02D96BFAD6931886D349B68C7096D02A1E279A93BAED8F89E9B972FBDC8C7A49134FCD7F2F791E1F2BD8A68176E65963AD2F474AB6E687E67304EA2CBD060D36C9D5FF00962CDEC2E4C7290D6F197694577A7CB271CD6DB9F4FB3F537FE7D471DBC1FF0038CDF9836D79756F682FFCD9A8A47EB4F1C6DC425391566046FE39B033B1B793CF4F170CC03DE7EF7E383E88DABF9ABF332F6D2329158F9BF538A57471C64549295DBED5731F3CEA2ECB1E2B0B64B2FAB6DC295ED9A83959F8450935A7AA80863180EB571D695CB2191AE78497A47FCE3F7E527E5A7E66FF00CE4DE8BF97BF9B2B736BE4AF3858DC5D45789762D544B6F1928399200E4453E9CDB69C931BBE4EB73E120D53F757F207FE70C7FE718BFE71AFCE927E65F912FE4935C165716D61ACDEEB114F0C16970A04CA23671527C4666125C2188F73F2B3FE7E3BF9A5E4EFCDFF00F9C87FCBEB2FCB3F30C1E6A87C89E5FB8B0F31FD554D22B812972BEA74AD08CA67CADBF1E3DDF31DAC4D05A3A188C69337A8439E4E1BFD6CC69173A18924F4C0BA048EF98E4B950C4F51D01855631D5C01954E4E4C31D3D46DF46944493053B8AE63CA4DA229835ADEBC6A5222C22EBB64449245AB5B5C4A54C33C541EF9938E4C4C2DE59F9A3E5C4BCB14D4ADE3DED50AB53DF2D949A32627CC778E915B014A4B5A1CC8C527559E348462CA23AF565AE6C22EB25CDCA390DFB64CB3839A315C836114B7D35C50EA53618ABB155C1C014F0C8916825AF572403025BF532603025DEAE5822D64BFAEAFF9C6B35FF9C74FC823E3F971E55FFBA45AE0661ED78A5D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF881BC880420F5CC3E20E424B0A857A1EA4ED8F12B24B50B4218D31E20AA8F08670CBB9ED809B0A39AA03C41076395F0B604BE5662C6870F0B30504E1C9AD31E165C6D29E277FA7070B21205171354FC382928CF8A83E78D2B8D698D2A12406B8A625A8D0D7A6D8B2B45B23300145698AD85F1EDB1EB8ADAA4A225884FC4CD3AB045B6AD0153D5BE8C429A2868F47BB86133689E66D47465BDBA56BFD32CEE6585789FB529E0402465832531E005338FCBB6FA84CE751D4B53D461B17E766D79772CEAF20E8C15C9030F8BDE98E11D11F0795F499EFEDAF6F2C266B595F85D69026222007FBB997ECB57C30F8C19F83BDB33B782934292BC7E8C4ECB672C2823F4231B2D78815DB6CA67985B64710EA91EA7E5A437115F68D25EF97EEA32FFA4F52D3EE5EDCDE06FB3511914E390F1E9B3C081768DE5FD3ED8FD72E26BBD7F5E0DB6AB793BCAF427B1724ED909E7B671C1001E86B6EF2DC590922A49044CAFABA1E2483FB240DF7CA8E50DBE0C52883F2E6D4EB125EC771AA59E917959750B0B1BE96DBEB128E858C64549F7CAFF3738F271A7A404BD4B4AFCA0F20CF6D75A85CD86A90996129159DE6A12DD0527BD5C9CA726AF24F9B762D180ED0FC95E5FD05E19F4BD0603769271769155C18BC68475F7CC796693B0C7A50F52D3ED674B48A28163B4912E1A66754142ADFB0C0751EC72BF1A4E6474B163FABFE5BF943CCB3DC5D6B1A12EA3716A39D98B522DA49246EB491402299911D4D7544B4B1287F227E4F59E81AD5C7992EAE2F3EB112D346B2D46E1EF7EAA0FDA0A64AD36F0C9CB59C5CCB8FF901D143CF1F94BA579AAE96F74992E3CADE648DAA3CDF6933C721435E6948E8DF1034EB9286B047CC2FF2782ED17F2EB4DF2AD9BD969566F737F74419BCC533F3993F9B9330E4C5B213D582092E54745188D833187CB62EAD134DB88792A75D40F53FC73046716D98F152B4FE598AD6DD2D6CC85957EC5CB0A807C48EF96C753109CB8F886CF269BFE71FC4B7D793E99E62D5B4A17B5B899AC6F26B783EB2E7E26F4A3206F99B1ED1A157B3A89682E5C54CA34AFCA7D23CB5A63D9C76B2DDDC475996F4C86AD3B7DA771FB44FBE5597571946ADCAC7A7203CF75FD0E64998087A1F8B35E32370D384AE0D338C0E49F4DD47C2C45687B6646292CB00609E67F2EBEAF0D8DC5DC93DB6B36CFC6DEFEDA5689E28C9F8802BBEE336DA7CDC2E0E7D2C65CD8A6A3A0EA36B2B45A7F9B35FBC8C7C715BFE91B85141D57ED77CCD3A814EBB369611E5F794CB42B39B42944DA3DD8824D64F3D75661EACAA4ECCBEA1DCE512CE1A2189EBB1D84090ACF0C6C2D8AF0F58B16E4DF2CA279A2E6431A0534EF527AA2D77CA2598391114CF346D3E7867B762945E42B944B286FC71E3E4FA7F44D352F2C21089CCF115DBDB293305B0E0932587CBA3D27458E8CC3714FECC00B0F0A439B17D4FCA324619C2D3AF41FD99930986420C0753D0A76B1BCB59D39C12EE58F63965F1726196161F1E79A3CB8D63A8CFCA32B6FC8956CCBC669D26AB1960ECBCB91614543C53E599F09874D381054A46F4E95D81E872EE265034A4F2F1343D698096C26DAF52B8A1557703155C411D462A8377F888AE203592B79FBE48062EE7EF93B6041772F7C9713120BFAF7FF9C68FFD671FF9C7FF00FCD6FE54FF00BA45AE066393DB714BB15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFF00FFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8779E6690D0E6BDC86E0B62C55A9D0D6B8AA7B1454C555F8814C473508693A9CB59A5E480C49E98AB4CE9E18AA1A465236C05945742699536F1047092A078E2B61716DBF8E15B08391B7AD701482A9130DB026D16ADB1FD78ADADE4395715B5CC6A062A9859FD93912D914FACBEC37FAD95C9BA09CC1906F8A6B0A0645AF8E565124EE5B5E56640EE320531482C21F4EE0AFF29DF2B9330CEACE0F56829B9C8368E4CDAC6C8B24629D3205B22F49B3B43FA35969534C81722095DBD8BF3A50F5CA8B97065B6766FC7EC91909392190E9DA51E60F1A6F539510D822C94E9842EE2B4182922294BE9CBBFC3DF1A6D10413E9C95E9808D9918A3AD6C100A53AE505ABC3569B4C47A10BB8C57C3455AE9E00E9D714786EB9D2B9A3F14DE9B62830A0F10F35689708F3388C77C945800C0A1B0924B69D5D00DF7CCCC658CA2C43CC9A64B085600D3332127032879C5F44D1D651B3014CBB8DD6E68EC9569E93493D373BE4096BC707D0DE5ED224BCD3238D81217E2194CE4EC2186C27B0F95E4120223E9ED98F22CCE9CB2BB2D12485E12E869514DB2B3272F498082FA3BC976F1FA31C6CBD80C00B9B2C4F5CB6D22065A85FB5D72C12689E35F75E5C826423D304E4C16838DE5DE70F28FA16FCA18FE12A4B003FB332B0C9A270A0F913CE5E598A54B82F0FC42BDB33232B755A885BE51D7EC24D3EE1A3E344EA332A05D2E68531CB9903428D5E99931710841897950E5A98F25656C595ABAC805315B5479401F3C56D2F91FE33BE4C354B9ACE7EF8B1773F7C55DCFDF157F5FF00FF0038CFFF00ACE1FF0038FDFF009ADBCA9FF747B5C9ABDBB15762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88316FCCD45335EE42630C7C17891F11D862AAE55A3FB5F862AA7CC9751DB08E6A164BD4E58CD00D192493F67C71552654F1E98AA832AF6EB91290B797134C832564620D6BDB1557E551B1EBDB1542C8AC687016512BE3246C4E0656881228DBC715B5E11CEE294C56C2AD0D003D7B629B4CACC1A53B9C896D8B20B34655607B9A8CAE4DD04EA089F20DF14C63AA7153E3BE56512656AC0D9EFE1902CA2C7A01FE94CD4D89DB2B9320F44D1A22C54D36A641B43D174E882B0E4366E9902D917A259DB936C4506FD32B2E44175B598E7F677AEF95972E0CB2D2D502EE3225C90C86CA10A46DB6574E481B270E8A474ED8D360012CFAA73AF15EA71A6C0109258B03BAD3011B3202D523B609F6B314B2E04D22B3590034AD714702362B38D4D08A62BC08D92CE26864A0DC8D8E2C670D9E75E64D0627B39A4352789ED928B8A214F04F4608659A2058B96A71236EB97424C65040F9A34E89EC95C28AF1CC98CDC1CB17806B5108838E3DE800CB3C475D9216A7E5FD3A7B8B80238C31276180CD9E1C4FACFC9BA25D0B4F4DA1019539357C331E591DBE1C2F4FD1740FAEC9C12352E3AD76CC796473469D93DC796E24291B4604886A6991E2B6EC7800667E5DD3440E9F0D0786112672C6F5FB14B68E3024AD587C3B65824D13C69CA476CD41BFB6D9609B41C4A77FA1C17F6B22320208EF97E39B899A0F913F337CA4B6E2E0DBC436AD4D3E59970C8EB33E2B0F827CFDA605F55B851D76FD599B8E56E935189E113865050F8E66C0BAAC91A520A4003B66406B05554D3AFD38A6D706A1AF6C56C36D20A7738ADA14B124EF930D6D54E28754E2AEA9C55FD837FCE337FEB37FFCE3EFFE6B5F2A7FDD1ED726AF6EC55D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88EB5527A8EF9AF72138584000F862AB580622B8AAAAC0BC4B6D5188E6A1033851F3CB59A058A952062A876E3FD715506A74EF912BC90EDF6B2B5E254AD00A615E25367618AF129FAA5B6F0C0520ABC75207BE0648911B1236E98A11A88C314DB60126A7E8C59C135B35DC644B745925BAED95C9BE09EC0BF08C837C559C5181CACA24CA6DC7A9694F6C814C52DB7B73EBF4EF95C9987A5E876C788DBB641B4727A3E9B685DA314E832B2CE2F46B2B6263094C8172609D5AE983903E395972E0C8E1B001694C8C9C909A5A5A8AF4E9869B849363682831A6626BA2B44A7D9C69B04D42E2D12A76C8C86CCC4B74B4DA1AF4FA3304B7DA6F69684F1D8605B4CC590AD298ADA30598F4DB6ED8944B7093EA9610CB613A9515E27F5620B488BE60D5F4C486FA62ABD1F7FBF260B19063FE62F4CDA08EA2BC72624E0E58BC2354B0E572DC96A9434F9E4FC470B82CB37FCB4D05AEB534E70FC3CBA91F3C066E5E2C54FB7B4ED02D6DED144680394A1CA2537678A349BE91A6AD9CA5F852BDF2894DC8B013A36A269D98AEDDB0464D9020B26D2ECD232A4AF4CB049259A5BAC2FC435053A659C4D134E3D3B7420F2C989341451B988008879061BE4E33A71F246D83F9B7CB36BA8E997521505CA93FAB32F1C9C2CB8DF97BF9BDA0BD95D5CC623A0DC8FC336584BA6D563A7C937D66DEA91D2873658CBCEEA0520658FD26E077D81CC98F27094F0AB44818ADA8B362B6B7AE4C2BB15762AEC55FD837FCE337FEB377FCE3EFFE6B5F2A7FDD1ED726AF6FC55D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8A0B7896BB6D9AEB72116C388EA3DF1B553E05BEC918DAAA51C2F1E43082A94DDF21DF25C4CAD2AA495D8E3C4B68696564D88C78D6D62B926A41380C946EAA2367351B0F7C8DB2F0D50055346618DAF86B1E30C2AA7AE36BE1A82A146F886C7A6048852696F172DE98A784A64B1AAF5EA7A62BC255982D0D06F8AF0950E209D87CF10120F0A696686A36C0436C66192DBA1DBA6552722124FEDE2240E9ED951939312AB340C29B8391E69E1B647A78ADBF1EF4C8908BA46DB5848CE1C5284E57209137A2E836CCC428A5477CACB744BD434BB378594BD083E1954A5BB7443D074EB477A11403DF21C4E5420591431716F8874DB225CA8C29378D4505075C8916DDC48BB65A374DB0ADA6C232C36A615E3A44288D054A934C5B233B509551C6C0D7232E4DC0A10C04B0D866010DE3284DED6DD956B4191B65E222429077180C93C48F8684712363913365CD21D4D084954746072226BC34F9E7CCF02C124EED4249DA9F3C98935C9E37AF34B2B829F647519306DC3C91B49ECB467D52EE2855284EE49C375CDA31E2B2FA6BC83E4C86CA38E568D79F723227239F0C74F6EB4B13EA0DA8A17A1CA897240A4E96C569F0819598B2E1255C5B46801A6E3AE08C59C2242716691BF414A78E4D914D2283E3156D97240B4C85A36508CB452464F8DA4C54EDA89255892064E32B6990A446A33A3DABC22B475A0CC9C73A699C6C3E1FFCF3F2C7A51C978C8ACA63AFC3D7B66D304DD46AB1ECFCEAF30DB98DE4923015549DBBE6DB16EF2FAB86EC2259FD56E46B502999575B3AEE02A7CC7BE4830229631A9F6C242A91563DF224D32112570141BF6C226B54B4B81E3878969776AD7256B4B4B01D8E2B4FEC23FE71977FF9C6EFF9C7C3E3F96BE53FFBA3DAE4D0F6FC55D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8A889788DF6CD7539086B99B8D6871A5538666618D2A20C8C0548E98D2A0E6FDE0A8FBB1A54BC2D1E871A540DC805F7F1C6954F9704AD37E98D3387344464BAEC298D37F120A6B794B7C24E34BC48A855900E5DBC71A5E257DA62140DD37271A5E24C625E031A5E256049231A5E2452AF2071A5E25F1435AEDF4E4834CF74CAD63A1F6C816706456895207BE51272E0CAADE1A2034E994C9CA8F25EE9C8D298C4ECD80A75A5C7521299125AA4CB9221146AB4F880DF2A2562CC7CAEB59D790DAB9025CB845EC16F10E4B45DBC731E5CDC880671A58A28DB22E6E309C2A0E75A77C5CB099451A918B021116F1FC74F1C5253A48A806D8B54954A2D3F8E2D9050E0B5A78E097272A28848158F4E9984425191A0414CAC8641A6A02476CAC86C0BD245515F0C810CC1A63DADCE1629187864405E37CFDE636699A6205687A64C313BB03B7D224BFB9E012A2B97458F876F5CF2D792A38E786478A8DC7C32333B2638A8BDE749D320B481005008194B7009FC50C75D862D8023046A0785316C010F20500E29A57B324134C2A426292B066C0D642A3CAD4A62D6421C5C145624EF5DB2C816B942D0525F173C589A65F02C0E379AFE66E996FAAE8570B40CE10D3F0CD9603BBACD5637E4D7E6158C9A7DFDCC0148018FF000CDE603B3CA6B31EEF25039027DF326F7757214D85F965D171A4E2BF2C99601BE3D3A65127231A9323576E9910C64A0C8D920C56A96AD0E58155D0035AE495FD83FF00CE337FEB37FF00CE3EFF00E6B6F2A7FDD1ED72618BDBB15762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8A8F54702DC7305BD2695FD57E9424E2A898542F520E2A887914A15F11D71542A9AD452A7C7154BA7942494A6F8AA5D31323020D2B8AA22180135770569B8C5944EE8E411D28B8B6A1979198A114DBAE2AAF28F4D775E55F0C556DA71E4E7895A8C551EEEA829D7C062ABA08DA46240A0C55341198D6A472F962ADC13A92CBE99153D7161229DC101A06AFD19596704DECFED57C0F4CA24E5C198DB30740A053DF2993971E4ABE8947A7DAF7195B34F74A819650FD457A64496B3CD99C712CF2569C41A6C72B25B718671A0D9849148A139025CD84767AD58C01D5052847539496E8C7765768521A2952D4F0C0E66308812564340684EC3172404CE1734AF86282138B70052A7AEF4C5849368D948F0F6C5AA4B1C507DADB16D8286E7BF4380B9314744D4ED98A42551989208046F9590C8284C5BEEC810D810AEEDC0F6C8109972633AD4B5B765A9269D7234E3C4BCC24D39EE5DAA6B53D31AA72602D3FF2DE80AB74ACEA0EF5A530F139508BD86DECA389A3E294A0C04B394682747E08C7C3906B015ADA60E7885208DEB8B301184D462CC0404A18EDCA9E18B346D8802B5607160423FD440C7DB162434F32018B59096CD729E1B6374988492EAED549A0CB2326528B1CD465FADA3C4DF65D78D0E6CB049D5EAA2FCECFCE9D1E2B4D6AE233462E49047BD3377824F2BAB86EF96678C452C89D402733E06DD0E6145439004ED991170A4B8508AD29932C03741E19449C8C6DF25029C77C8863258789FD9C218A1D901AD053BE5A154591BB1A6495FD84FF00CE32FF00EB377FCE3E57AFFCAB5F29FF00DD1ED72618BDBF15762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF89F0E3D33BE6370B90957306400773B63C2A893C9057A531A5414B39AF107738D2A6360038353D31E154BB508584E08FB23BE34A81F41BAF8634ADD1B74AFBE3C2A0A32D41F9E0E14F1353B32CAA4277A63C2BC49B7C0D082CBBE3C2BC484A537514C785789A1148F22D477C78578995C16E91C4848DC8EB8F0AF122B8464531E15E2524B74120A0EB919041DD3B55548C6525BA2BAD1AAF41E3BE5127320CD6CD78C60D329939514DA35E681B8D77CA645927FA70008DBE79592C3AB2FB280C8E1E9B1E99025CAC4377A3E8500E6A4FD39592EC71C5EA7A75B5694E990655453A11716DB172F105648A86A7BE2E5008C8F614C58108849CD7AF4C5A64994539D8E2D5269AE0B122BF2C5B20A911248AF42712E5453D8A9435CA2412BCD011E195C83243CCCBE34AE564360404C418A403C36C8D32E618B5E219BE06DFC31A6B1043DBE9C88EAC57E795C9BE019569D6D142438503DF2A2E4C4D0653115722831059C8D84C245063E98B00142301771D7BE2CC05766DB16C010ACA64AD0D08C595226D548A8AE2C486D890C77C5AC8519588077C2C084B26734EB954CA46C92DC826BBED8C249914B48F8C0FA7365824EAB54762F867F3DA0FF738E69DBFA66EF049E5B56777C797AA05CCBB53739B5C6E833F34BD97AE65479B8325C3651932C02ECA24E441AA0C88632688184315608BC47CB2D0AA4C8BE1D724AFEBEFFE719F6FF9C70FF9C7E1E1F96DE54FFBA3DAE4C317B6E2AEC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88C69196127AED9570B9096DAC8649D09D80618F0AB26963050B571E15484A8697C287070AA610308CD41AF88C3C2ADCF2ACBFB1F11C78556B47C63E5C7B74C78552891B91200E27C7052A6FA64609DCD70213292D95E4A70A537AE0295CF6C586DB64789544DBB12AA13EC9DCF8E08CAD53386D49A129C6992B545CC68AA03538F6F1C6D54EDCB4A695C6D5368AD893CB9F4ED913BA2DB9A42B48C0DBC72A945BA324C34BB33248007EA6A4D328945CB8167013EAEA13EDD7BE5243951927DA622C90056D8D4E52609326436500120402BEF95CA34DB0C76F41D3AD38471ED94C8539B8F15335D2217F500029EF95172E269EA9A7A88E38EA2A69BE066237BA6E6207E2AEDE18B91134E238D3BF862DE26AF1C7CE9BF1C549B5AABF11F638B4C8A3D361F2C585371C7CBE2AD37C52364CED5796DE180B7C7226491D3F6EBF4640B3E25B27C3DEB4CAC864248095B96F5A65526D0508CFB78E479320503E887979D694ED9599B6844B5B86150DC699519338A6F6E802019592D802776D1D5280EFE3823CD289766E3403A65BC2B6A28581A15A0F1C7859892282961B1DCE3C2DA0A998985487EBD4E3C2C81B5D1A322B1E75A63C2B24BE49DCB9DBA63C2D6A4CEEFDE871E16B2859A1928183D699465145318DA477AF242A0D0B1390894CA0878F9B3A9A54115AE6C7049D4EAE3B3E30FCFBB7923D45AE78F2AEDC7EECDCE1C941E53571DDF1B6A16ACB3C8EC69CB7E3F3CDBE09ECE8B34774918D0D3C333625C1986E95EF97736B6CAD3BE5320DD02B9412064445245AF1193DF080BC2B0AB02464C15E1586363DF1B5E17F5F3FF38D029FF38E3FF38FE3C3F2DBCA9FF747B5CB03597B6E143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D98A8B7639072125B39544C3E78AB27797F75F462A948156A62A88A30C554E163F584E5F67154E27E0D0D075A62AC7648CA82701546E95381285F7C8143339230D1864153DF2252A0A08201195211D0C695078D7C718F34A39A48047C76072C548AEC8E69C0D41EB8AA63A7C144E647CF154CE175AB8F7C082B1A1F524141D4E466CE2CA34AB6F488246F98F37331B2066569157282E4C59269F6CC5D4A8F848D865726639B34D32D97EB09C865322E6620F41855508551B28DB289976100C974992928A8DF2825917A04120E2A7C70B743927B6EDEA003B62D88958F93118B64516917100E2CC287A7F113E2716B3156040F9628E144C4B45DF7C578530B55EB812228A1CA83205B5425E546C8499452E7E54CA4B70429EA3C32259072FDA39410CC146A005699021B01B4CD015400E4086C0532B7978AD46301452792351CB76CBD00A2163E54DB61BE2CC145A41B74A571660AC922A0DB6C5B6254516BC875C5050BF54E4EE695C581287961E1DBA62D64A02694468478E63E50CE058FEA24BAA9032B8865296CA5C9A28E33C7AED99D846EEAB57C9F257FCE4246D0C493B2ECDDE9F2CDCE11B3CAEAB9BE25D56759642C050140299B6C01D0E61BB139012C69F4667E37026BD010287AE5E1A951BF865726C8AE465005722DA15D5D7EEC2AA9453BD3AE2AEE2BE18ABFAEAFF009C6BFF00D674FC82FF00CD71E55FFBA45AE5A39349E6F6BC28762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87EBA9D962318514F1CA2DBD2086568E74237E4DBE36ACCE37F5223DB1B543A8E2F5EB4C6D51AC57D3E58DAA5F50581AD0D76C6D5305E4C8057E9C6D5053A108C08DBC71B5405BAFA73F20DBD712ACF6C6E8AA54A06DA943912944170E6BC403E195A15D2E022BD5474DB0412C76F2F19A4214D07B658AA96FCD8835E7E3ED8AB258A65861DE9B8E98AA5E977C653C68C09A9C5053DB39C33A92A0E426CE2CC6DD8351BA517A663CDCCC689854B5CA6F9417262F56D06C8488A37F63944CB7445A791C0F0DE2A28A8AF5CA497371067F6567249C7634A7DACC79173E316496561E8C80F224E524B221964638A281BED930D914DECE765A7C35C2CD3B81F93569D4E2D91473B6DD3166146353254D29BE285850F2229B62AAE8EE9B7018AA6768F4EA3AE2551ECCA83C72B2C908F286AED909328A11C8A74CA4B7041484006942476C052A51824824532B21368F8EB5DF61E390219829824818053B656436028F80A82141AD71884996C9BC4AAA011BE588053355AA29A52A77C5B01560CAA37C5982879248DC30AF4C59C4A0165547206F5C524A3519284FF375C5AC940DDAA052791C5812C62F1BE02C054A9E994E4512A414BC1EDD5C8A1F0C8C53C4E6712A20540420AE66E1165D76A4ECF9B3FE724B4F79FCBB1DD2C615947D91ED4CDCE10F35AA7E736A49E9BD1CD0F1155CDA61E4E833F349C0A0E43E23E199D8DD7CDD52DF115A13DB2F0D4D9A93D32B93645408F888AD37C8B6856403BB6154482B403962AA8154FED9C55FD75FFCE35FFEB3A7E417FE6B8F2AFF00DD22D72D1C9A4F37B5E143B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFF00FFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF876BBE87315C8485CF160DE06B8AB2DD364F561FA31555941018E2A88452D01F962A955584943E3B0C5538B771C77FBB156AE1D1D4A8EA70C552A02928F9E48A197589AAD3DB22528D63C72A4202EA7288287AE304B1A7959E5A7BE58A9D5BCAD12123BD3156E5D4491C4B6F8AA8C37039160D5A9C50591E9B745A5515EF909B38BD1ED5FECFB8CC79B998D3AB14E57287DF282E4C5ED7E598D43A46DE198D939B918D92DD411A5DA35075DB289173F10675A5BC7E94629BD331E45CF804DE8CCD551B786524B221388549550763DF2D8F2484DA24E00649926D6DDB16C8A3DFA62CC23ADE35E35F6C59D34235E67DF15A44FD5C374C569AF4590EDB602B4BDB91C8162A251B7ED909328A83A353AE525B8210A357A6053C9B58DC6FC71402AA0B1143B7BE408660AF5661DF2B219828BB695BD51BF6C88E6C89D936FAE84E35392620B23B5BB8E48802462D81AB89929B362D80A5227ABB0E58B3057AA826BE38A928D8CA804571604A1AEDD781DF16B258E3CF17C5191CB91A8CAE41ACC964482E3946A9B0ED8C42789A82CE732B2AA1E20666E1145C4D472795FE7CE96F2792E591A3FB009CDC610F39A97E4E6BCE5EF64A6C17E1A7CB3658DD06A39A5F08063DF3371BAF9B7407A65E393535C4572B9364541E262C48E9916D0B789185555436D8AABAAB53157F5DFF00F38D5FFACE7F903FF9AE3CABFF00748B5CB472693CDED7850EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87BBB5531935DF31E9C863A543921B61ED8D2A6FA4DD324A200070AD2A7AE1A54E75173071F4C0656FB44E0A54C34F904B0716006DB61A54B2E222B72A00AA93D7052A342468952C41231A543955506452491B50F4C2020A1A83D415385871325D3E4F8B8F6E38299DA6373444E4BD7DF0708674C7EE1E4956941B1C1C20208A4BE38BF780B57E8C076502D386904501E00134E87236C8458C4B2C92CA6A00DFB640C99885A3AC80F4E466635076C788B194190E94DFBE06BDF2066CE117A759CCC4AF4D865129B9708B35F2E446F2ED44BB007F67283272A317B669F69F5495668EA76037E998F236E4420C87D2378E1CECC3F972B21CA86CCC749B3A471835AF7CA65172A136530C25187C35CAF80368926D1C2397235A9ED92029B446D328E20C37AFB6165C29A4302A01427E9C590D915C1586F5C520A362015401DF167C4E650096A9AE2BC48A85CED418AF1225959874FA3015E25AB6EFD5853293265C2E3180694EBB642526622AA2C95C7C24D72A25B4042B69D3A49CB882A0EF91E25217B5A3FECA60E363C28792D66542C62F8475A6478923642B46C3A46D8136B6349C3AF08CF23DCF4C40DD0648EB9B19446B21A863D874C953113455A7AB1A0E47638D33135D7771E9AFDADFDF1A6CE341C127AA58D7A0C04536C668E499D76FC7032E26A5BBE20107AE2C52A9EFA490F1DA98A0C6DD691722CF4E4D5D864A301269C9B328D32131373F4D4D7AD4648630A192470DBC70BCC8A0DC336E87A53333144071353C982FE6C68306B9E45BF515F552363C53C7369843CDEAA443F147CC3A4CB6DAC5F5B5C03188A4603E55CD96301D2651658DB22C5550DF2CCB8389382C415515CB9A0C5771CA6452154283414C900D814E445180AA9C6C4B71A6C3224A53058C507BE438D5FD727FCE36EDFF0038EBF9063C3F2E7CADFF00748B5CCA8F20D079BDA70A1D8ABB15762AD374C55662AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AB97BE2ABB157FFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8749E4AC447B652DE9347F131A77DB1557B6AC3386E9BE2AC8AE5C5C427F9A9B62AABA7B98D4027154C4C625607BF6C550579CA31C7BE2ABA08CC916E31415268087C5AD34B4E51B03F462D8134B97E51FD18B68415AA076901F0C05126A5B602A5464648097CC484653D32B6C4A963A927C7BE565B62B91FD30E3C4E218493CD1A4E532FCF2126507A969FBBA8F1198E5CB83D5FC9B6D5BAE9DF292E541ED92A18ADD683282E54026BA2C2EC39B0D8644B7443D0F4C8D782E5527222190C71AF31B641BC04C52104834EBD316F8A67142076F9E2C9305B72698AAB0B638AA323B7341B62C9CF6C7154C2DAD136DF154E560840F703014851692255F88019410DA95CF7766A08E601F1C810C9049AA5BC469EA8DBDF2B21B0228EAD1488C88E3938F872045325B15C4951591720C11C2E55455DD597B8C55DF5AB56EC98AAEF5ED88FD95F7184319206EAF2071C3D41926210735CC7140A43D77ED8B20C62FB52E4E141F6C5B137D2C92189E8C36C05B2298CAC1109C8B3B486F2EC28DCECB8ADB1A7D7218A5E25FA75C56D33B7F355A5BD393800E5B02C08B4DD3CFDA7A2D0381F4E49908D2D5F3FDB199B8C808E1B0AF7CBB1C9C6CF0B4A0F9D67BAB6BFB49B7826560A0FBE6CB14DD0EA30597E74FE7368325B6B97B7B04748A51C811E273638A7B3A2D462E12F9DA60F1BA07EE73371C9D76514887015A83C065D6E21E6B7204A40554FE1841D99A9C9D701296E28F7AF8E564AA37A0CAED5FD707FCE36FFEB3AFE41FFE6B9F2B7FDD22D733A1F48683CDED39243B15762AEC55A6E98AACC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5572F7C55762AFFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8739D15050D6994B7A56838C8A53A72DEB8AAACCDFBCF87C7154E2DE86024FDA036C551B6EBB6E37C551F1CA237523B763EF8AADBF40F1FA83ED1DF156ACE5E30D0D3962828D58E3937A5716B5748B7A1FB23A62D817CA6A287718B68528488CB15EF80A24AE5CB0C8C9012BBB4F877CADB12C8C1DC0CACB6C50EC84B1E58B0927DA3C6038206F5C849941EA7A38F526507AD36CC72E5C1EDFE4F83D39B95329939507AE9ACD1A2D3E119417371F2659A55BFA70F151DB225BE219469B190429D88CAA4E44427D6EAED3053F6720DE032686D01A6C698B604D52D1788A038A5328ED8F15DB6C5518966081B7CF1555312203418B2433E2ABE3623BD3155579F82925BA024E2AC5AFF5A8F81A380720437304BEBDB89598A49F09CAC8641266B8946E646FBF2B21B436BAB4F1FC21CFCF2B904AE3ADDCAF499BEF394B05875FBCA504E7E938AAD5F30DE29DE7A62A887F32DC471971354F4A610C648287CCB712CB4692B9260190C1AB4D31285AB45A818B30EB6E7753FC7B8AF6C5B1E81A7C451197A055DB0499DD35773908DB8DB22BC4F3ED56EE5A482BB0AE2BC4F2DBFBB732B166A11E18AF124536A2EE4297240E94CB20CE25080DECB25217600F8EF8934D9619058E9FA9F2462599BB1DE9928CF76899B67B67A56AB7118528294EC3336191C2C98ADE51F9ABE4D9A5D3EF649E127D0879A353BD333F164D9D06B70D17E7FDFAC4CD70B30FDF40E4474D86C7BE67E2C8F3DA914972492301EA50B77CCC8C9D613BABAEF4AE025B61BAB2004D3240ECCCB6F12D2A7224AAC0FC7E8CAC9558F3B76A7CB236AFEBAFFE71A8D7FE71CFF2049EA7F2E3CAA4FF00DC22D73610FA47B9A0F37B5E490EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87FBA8BD4048194B7B1D95BD16A1EB8AAAC3FBC353F3C553112FA6A476C553AB12255A8EF8AAF9E321AA3B74C5549AE015F4D8EF8AAC4AD76E871414E6D7B138B5A6C801E9E18B6042DC6DF3C5B52DF50A927C701449176CFEA353B646480985DD972841A74195B63152BC1C8EC0E565B62A4D4635F0C430927FA42FEF07EBC849941EA7A0445AE51BB0EB98F272E0F7DF2E22D40194C9CA83D12CB99611F5DF6CA0B9F886CCEB4D0C38AD3E8C89E4E44597E9F0FF00A415237DB2A939116530DA8F5850641BC06490460103C31649B2C6B4C551491B6D41B62A8D446A0F871569EDCD09A6E7164817B73BED8AA1243C176ED8AA4379764065AD6A08C55874D652DCEC95381B9520D0AE08355241C890C9171F961E43464A8F97F66565B0233FC191B293C7E2ED954F932E88197C9A00E998CC12CB8F2B8B78DA5E05F8761DF156297F6621AD206DBBD31563176EC6328B0B29AF5C218C90964D289B743D724C0335D3999E52A548F8716619DE89680B863D716C6573968D2918DFF6A980A4A4924C4F20FB7BE4516C475754653C37AD6B8ADBCDEE74C92791A83A9C56D42CFCB5732CDC1622E09EB4CB22764893D3347F216E924D1F1EFB8FECC848A99BD2EC7CA9630AA82AAC7AE40163C4CA6DF4AB5800E2829F2CCA84940619F99FE5A1A8F937549EDE1065F4986C3C01CCFC52D9D36BA1BBF1A35FD2CD96A5A84338E327ACDC54FF00AC7363864F27AD0C6648CC52943D466C205D213BAF4C9B978BBD156E393D0E36CA489BB4E2A3B57204A1219A52B5C892A8749F91A57A6564A1FD7F7FCE346FFF0038E3FF0038FE7C7F2DBCA9FF00747B5CD9E3FA47B9A4F37B6E4D0EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88FE09E89206F986265C86217D18790FE1928CAD5640FE91DF2684C42ACA093B8C2A9DE95F01A1FB3E18AA6578547D8D81EB91B4A41229E7CB2064847C0EA29CB718612B2829FC7E888EAA37F1CBA835A26DDD016277DB225B0284E19CD17A65664DA103242114B30EBD30192D5AAE9FFDE644C9908B241CE489D4EE00E982D980C4F50B52AC59453DF06CD8024EBC91B89EF9125B238C1E6C93493FBD51DB2B914F000F5FD094ACC9C45052A731E45B601EE3E5621A401B7AE504B9B08BD5ECA14AAB22D189EB98B3951765860299FE9502D15996AD90E22DE22199E9F6D1FA9EA71F8CD2A7236DB10C9E1894B82474EF8B626B144ACE7E793114A611464B713D3C325C2153A8E3000DBA0D8E0E10AAAC0A8DBEFC7842AAB0F86BED8F085B40BF5C7842DA577708E2DC0531E15B62F3E9EF2B13C490712026D1765A5246DFDDED9512BC4C892DE355A7015A656496C125C88A29F08CAC96F8C95C01D29D72B2CEDC638DBED2D722634B4A0F676EC0D23049ED9548522987EABA7C6C5FF74BF2A0CA49626DE75A8E99C8B462314AF4A61848DEE903BD278F4708F511EF5CBEC32E109FDA69C435553E223AE3B278598E9903C34EC3BE1B09A4E652163241F88F5C8C9B211058B5DFED6FD6B9149800C6258DA4675AD47718B510A967A6C4CFF147518B512CE74AD3ECADD59961018904138AC4B2B49232B420500F9603BA69689A388D176AEFD71A452256FA9FB59206992716CF16A9A65E69B3012452C6FCA33DFE1399FA736EA759B97E31FE70E94BA6FE656B564578DAA3B3431781A939B9D3879AD6403C655A49EB34E794A5882DD36068336511B3A1C9000AA00074DB2B9158121176E55483DFBE52665CB8C6C29DF5C301B361E22A62C6E6766AF235C9B02104B38434069878583FB10FF9C653CBFE71BBFE71F1BC7F2D7CA67FEE4F6B9B087D23DCD079BDBF248762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D8DAB09F719801BD8DDC8A4849EC77CB2294B5C736AA9CB1531B69285636FDADB0A19143FBB5AD7A60554F53D45627B602942BA77F0CA8A143D4DC01E38C0D154FE02C621E19719288A362069F3CACC9B045308E2046FF007E5664D822A7731064000E991253C2A7A7DB7EF3A64789908B2BB5B6AF30456B8F1331143EA7A627A65A831E26C1160A6CF94D41DB6C04B6C62CA746D3099936AF8E5722A43DA746D302F134DC65132CE01E9BE5C88ADC5076398F22E7630F63D1A20F22A1F1CC69F376388507A3D9C3C19401F4641B87366D610D29B6F8B6C53A4401C7862CD31B789CB123A65A392A610C3273C2AC8228C71507AD3155EF18A53BE2AD32EDFC715413AEF8AA19A307AFD38AB696D19EC36C0555BD140360065549010B22D180F7C810C8158401F3CAC86624B3991B9391E16C8C9DEB0C4C5B4168DC8405ABD3299452C72F6F1199878E6398A6AD875ECAA5C90054E400A4F0D2053E371B77C3C48643656C6B5A6D4DB1E254E12260BB63C4AB5A195C30F0C902DB89239AD253CBF56499C9237B57491B6A16C5A24995AC0C083DF16892730C8D1EC72405A225182E881D70F0B2B51376A49E477C7856D512ED36DFDB1E15B649A15D1127C3FEEC0CB4FA0E6660D9D6EAB72FCAFF00F9C90B196DBF342F66238A4AC493F7E6E74E5E73561F394A156460BF66B9B2076745946EA7FC72A996B886BD42A72AE6E643925D7331634C980892493DCFDA51DB6C980C4A56CE49D8E4C06A7F645FF38C1FFACD3FF38F1FF9ACFCA5FF00746B4CCD8F20D079BDCF248762AEC55D8AB4DD315598ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC55D8AAE5EF8AAEC55FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D880E040E87B66006F492FA2F88803AE5914A1C5B7186BC7E2F1CB108781585C46CDBAA9C2ACB15E3684803738154955A35663F6476C05285594BC9C6B55AF4CA8A157D052E2837C8F567109CDB3AA808D8993688A6B0C44B0A7D93DB2064D8229EFA01635A2D09CACC9B0456081581F86A720649E15D6F6AC1C7A6BC71E26422C9AC6D242D535A63C4CC4532BFD3CBC068B534C789988B036D34C731F82849C789B04597E8765C654257204B121EB9648B18400509198F32CE019C688A82652A286BB9CA497371F27B068CA81D582D0F8E5122E7E3E4CF6CCF29D2BD3C322DA39B36B4AF3F84D17B62DB14D56A5B03364368E811011BD284E5D1E4A9B200402A389F1C2A8F49102AEDBD3738AAE3221EA31543927715DBB62A82949036C550F473FB471556457A801A82BB9F6C555E458A31549BD4F639021925B35DB80CA2DD189E8DE190216D45EFA38AD889621EA53EDF7C810C4C92A4BB8DA29A46958B28F8129D72206EB19A0D2EA607D53C9D3F9299618B68CADB5ECB7069E87A71D3E2343944E0DB1C9691DD7EF83F01C58577198F28B609B1B78A560C492483F6B299C68365DA2ACEDDCC82BBE55C28675676A0AA80BF1531E154D52C588E98F0AAE5B1707A75D8E4A21B71A12EF4F2A84AAD0F8E58CE4C4E6B26939A96E2F5F84E2D1256787D2B16319E52C42AD20F018B449E6F79F9A5E52B099ACF51BD4B6B984F16151BE5916B2692D93F387C822A1B5B2BF223254BC4BED3F363F2FAE24A0D6D9B7DEB4C697899345E73F28DE11F53D5D2A7A02C31A5E2665E5DD574C9264F4F555256A428208DF2FC45C2D46EF833FE7278423F31E36DA482653507A13439B7D39741AB8BE3F6565BCB84635556345EC01CD903B3A2CA374422214625457B65536B8849677605E86832500E4C521B8B97527E3CB00625262EEECC791DCD7260312B94E4C06A7F64DFF0038C1FF00ACD3FF0038F1FF009ACFCA5FF746B4CCA8F2683CDEE7850EC55D8ABB155AE683E9C554F97B62AEE5ED8ABB97B62AEE5ED8ABB97B62AEE5ED8ABB97B62ABB15762AEC55D8ABB15762AB97BE2ABB157FFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88BB53C866006F5971082E0D3BE5914A16E00488FCB2C42550D19E83AD76C2A98DBC844BC3DFA6054E2E29F56603BD3014A4515564CA8F3423C5C0420F7A65726DC7CD745704B8232B25C9AB6490DEA22A6E2B5CAC96C8C593433ACB1AEF9025B04535B4B70E188A1DB20649E14FAC6C55985464789970B281A7848C328C3C4D9108EB6D3DA78DAA2BB63C4CC063B71A3137205325129AA4EACB4A68DD6829824C4866305B32B2023B663C8B38B35D1A03EAA6D9492E6630F56B106109951E6E6C59D69DF100D81B073673A6FC4AA69D7BE2DB14E917E3C59A7D6B6C48461D0EF96C792A751A711BE155C57DF157703E38AB5F4E2A849BA62A82798462BBD71541B6A616B5AE2AC7F51F31D9C1192CC63A77C910D8C02F7F30F4BB763CB520A57AA93FD99590C0B179FF003C3CB167270BABC593DABFD99590D04A47A97FCE48793ED2A91852C47C27DFEEC111BB59953CAB56FF009CAA82DEE78DAC21A3AD1401FD99710C2390DA5F27FCE4A6BFA821FA8D89E0C09E407F665120DF1C858D45FF00391BAEDAFAED776C556BDC7F6663C839519BE91FC9EF3EBFE61426231805D4B961ED954837C256FA2AC7437AA9E3F4655C0DCC9EDB4C78CD48C3C0A9DC160481515C1C2AACFA79A834EF8914DD8D0379A79E1F7E06526177962A03963C585787BE2D1248D61963B1D453ABCB1B08FE74DB16893F32BF313F293CFDA8F9AB50BE8209A5B79A4668F8F402A7DF32314801C9A64C462FCACF385B6D73A4CF2D3BF13FD725B215D7F2EBCDF34CB1D9E893C751BB713D7EFC7657A0F977F243F30A7991E4967B343BD09A7F1C695F5FFE597E526B1A5DBC6DA96A0F34CAF5662D5D89E9D7251E6D1946CF1DFF009CCDF253680DA3EB11356474EBDFA1CDAE9CBA0D587E7F593C9373965D9DBAD73640ECE8B28DD16A7E07F1ED954DAE218BDCDC0124895E8694CBB1F26F88D9239EAE4E5A031921F8D065803595226876EB9301ADFD94FF00CE2FFF00EB347FCE3BFF00E6B2F297FDD1AD32F1C9A0F37BA6143B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88D8294F845330037B52D4B29AF7CB2294AAF6BB8CB152D8D5830A547CB0B13C930854870DBD7C72122C2253953585ABBE54496C4A1BFBC6F9E441552641238567283AD7F86322299E306D98F967CADABF98AEE2B5B3B62C8C69EA01DB31CC9D8E385BEDAF227FCE2EC17515B5D6B32155900A293FB5BFBE5129B9F8F102F43D57FE716EDA2B988E9CF5403EC76FF89663CA65BBC10F25F35FE476B9E5FB932C687D193E1555ED4FA72066C7C10F3E5D0750B0B8F4E5571C4D0D7009A462A6616D632491014AF11BE59C4CFC20BCD9DC4428B551ED931309F0D012DB49CD59C50F8E094BB94C02636B1134F1C8711699C53368A452A791E9B60B6BA65DA0C94750C706CE442DEA763229E15DF289F3765879334D35C55476F0C8B7B3EB023E103618AA7D18AB0DB02DB27B16F8554F6197479330980DDC006A3C30A551862AAB18046E31545718986C82B4C585A57716ACD5E228322B691CDA75D716F8493BD32B2516F3FD674BD4DCBA4523A73040A1E99124A5E3BAE7E4DF9C35F467835BBB8977AAABD3F8E4F88A402F1CD4BFE71BFCE6F3D06AD732B576ABF5F9EF82D9D16357DFF38C5E757A97919DFF0098FF00B78B218D8A5C7FCE3179D43F22BCCAF4AFFB78F2639310E1427FD0B379CC48ACF6EBB1EE3FB720723891C459FF00973F203CD715C436F39F4A0604391B536F9E4253722185E97A27FCE37581BC59757B812AF2ABC2C6A0EFE15CA8CDCA8627D4BE51F22F97BCA10C49A3D9C5048ABC4B462808EFDF2A9CB67271E3A2F52B68AA828287C72102DE629D5BD9B4AA07877CB835909BC569E9815CAE4C0AAB428C0D08F837CA81B402424B733060D18415E95C2DC0B0ED474F6A973B835A0F0C5B0C424F1C216A196B4C5A6710105736314EDCC205E1B103BE0B21C59C2F9249756B69103CE352464F88B5F865298A6B78A5A450278D699602BE19661653F35154070DAF8659369F7D0DA144745FF4821547BE46CDACA1B6EC37FE7263C99179DFF2D64586D926BCB088C826A55C0E2C7AD7365A693A4D5C03F119A136BA9CF6922F131C8D191EEA48FE19B8C727439318B5194888DD21D890787B6DDB26402B080616EB44AB6EE6BC98F5C9844A21047ED1CB034C821DBBFD3930D12438DC9AE58D25FD957FCE2FFF00EB347FCE3BFF00E6B2F297FDD1AD32F1C9A4F37B9E143B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF889B535CC00DEAF28EF9645528BA1C8E58AA10C60B8AE2A5181789C045B590982FF7669E191314829431FDE36532D9BE22D52D2C9F52BE82D233467351F466365C941CCC18ACBF49BF21FC950E9D6105EDCDB2BD003C88F9E611CAEDB1617D71FE2ED0B4B8E21385E286813C0E512C8EC71E057B7FCCAD0EF6EE386270A76037CA2591BCE165F7369A4EAD6CF2CC527122FC209AD32BF111E03C475DFCB0B2D424966B78946E69B63E22F80F329BF2DEE609654856A17ED532D195071525571E49BB86BCD0ED93191071A437BE567442CEB4206DB6113B6AC90628BA7345395F7E98789C5945329ADB8462BD69D71B6BE156D224E33818F137462F48B49887515EC32B917618850679A4C84B2FBE06D7A3E9CDBAD76C5594DB8E4C3154E6D5C89197C32D1C9984DE13571852886FE38AAAC7D3154C228BBF638A6959A21DC74C82D21999882380CAD692C9ECFD53BA0A9DABE1919288EEE8ADE6B65F4924EA29838DC8F0D092DACA8793C8013D1BC31E36631A5773F003CAF01FA4E3C4D831B1E96F2047DEE86DEE72139ECCBC2B496EF568492A9722A7E798A7228D3282B5C489EAC373561D1413959C8DD1D3A274EB7964B80D2C859ABB2E40E46E1829E8D6914CC1226B6E117FBF48C8F1DB2963A65B6D6E11176AED96C0B5109D4238A82075CBAD810ACFCB8E5732D6425CED22568763D72B896043A38A39012454F8E49B6297DC5AF21257A0FB38B7249FA3958B6DF4E2D3916C1A423ACBCBB1DB22538B1F131ED53CBE8FCA87AE20B6F82C364D0258A60231C93A93938C97C047C505C5BAF43B6596BE026B6B0C976D645BED452127E583AB899B1D3D3AD2DC6ABA76B1A6CC39ACF6C63507DD48FE399D84D3A3D5C2DF87DF9F7E50FF0003F9EB50B20BC6970CF4A7F3B31CDB6296CE872E3A2F1CD4855D641FB4BBE650DDC7E4C4AE362C3C32C6322969FB67241A64876EFF004E581C7921C753963497F655FF0038BFFF00ACD1FF0038EFFF009ACBCA5FF746B4CBC72692F73C28762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF889B360BD730037A3E4A1E9DB2C8A529BB42C76CB10A50AF165A8A625211B55C622D8494E52786C77A8C998ECD60EE83BCF8AE9388A506F4CC2CFB39F845BD1FF2CB493A96BD68047CB8CAA6B4ED5CD5E59DBB7D3E3DDFA93A3D8B69DA6DABA0F4D1211555D8663176D8A2F2BD72D751D7353962B3321084B1504D32893B1C71D952DBCBBABE9EA26657046F5EF98F26FE009EE8DE78D5B41BD1CCC9344D45911892001DE99595107B868DE79D1B5B31AC92FA5310014069BE36930658D6A8E1DD40E128F81BC72C69945299F4A421AAA0D7C46581A0B0DD574359012117A1ED9312A6A9C5E51A9E83C2524201BF618789C69C58F5D69DC410476C789AB85274B5E126DF0D0F518F137C22CBB4752028356DFA9C376E5C0507A4E974053B7F98C2C9E83A6CA15803DA98AB288E7008A62A9AC2E480C3BF5CB63C998E49A42E69D4E14A395BA62AACADEFB62A9BDA5D536206D8B35F7373CA945A5720B4840ACDD09CACAD221232BD6BF3C84CA62375D2AF53D0E637139A12B9622FF0D49E5B6F8789B404A2EB492CA69DB071338B1A9BCBCCEE47106BED919CB66D88DD47FC2E3FDF43EECC4B6F115787CB8D1C8A40200ED902590A661A6E841543155A8F6CA8A788271E97A478D6A3C3082899B08E801D854E5F02E3109AA82A0153990C4869A471EF954CB590A2D2B32B0200C840DB54C25A4B89050903C32D6714C963F5A3DCD0A8DB16E4B64B295492010B5C5A72289825D8455A7ED655334DDA734BBEA32CA28687E782DC9E2583494460B201C9B71F2CB014F10448D060906EA3EEC9028E25F1796914F258FE9196C5C4CC2D36B6B45B53B2B03DC8EF97E39EEE9F518DF9A7FF0039C1E5223518FCCD15B8F4EE295217F9797539B2C32743A8C6FCE1BE9B8832EE55968066CF13AB9ECC50124B549CB4B594249D4E48354940F4CB038F2511D4E58D25FD957FCE2FFF00EB347FCE3BFF00E6B2F297FDD1AD32F1C9A4BDCF0A1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF88089A86B9801BD338CD40F1CB22969E30C32C420D8713B6291CD6D4E4A00B1C8B58D46E69979AA681CD40FC7331F019ABD4BB4D353E9EFF009C74D21751D692A952B535A78669C9DDDDE101FA397F6CB6DA3D360563A01DF2120ECB0B7F96FE5B8F503777862ABD08AB0EDBF88CA661D96309BF99F4A3043246918E9D8663C8373C0752D0F50796436F096321A37C35DBEECAE99458DCB65AAF96E48EF4A4B50C0D02B78FCB101127D05E48F375D6BB6612E418CDA850BCB626B4F1CB0071E6CFEE66658C7BE594E34C149A525D1AA3AE46450030AD4EC83B31A6F91DDAA51613A958D0F4A6DD71DDA845864B6A56423EFC6CB7C629EE92854283D6B9747937067DA79A32E4959BE9FBB8FA31564A83718153C85A8A9F2DB2D076660A6B136DBE1B4A354EC3F1C2AAEA4D3154742E148AF862DC119556C8324C6DE256A74DB2B28559630B95CF92421D979661B900A87A7BF4C0CE25B30F2ED8B60696C4160481B74C05B224DAAFD487F2D7E8CACC5B812EFA90F0032042D9575FDD0E23E9CAC861C454DE124FA9D7DB214CA3225673F4CF4CB62C885C353111AB6E3A65CD65506A91C9DB2120C0B6F771D36DABD7238F9B44D0E675AD69972628BB797D571C7609D716F64B58A587871DE98B4CC162B7F21D31F8769872194E45C7612F87519A46A274F6C886EE229B44E646066D9874F96482F116416C22E23A64D788A756F2AAC4151030AF5CC989D98917BAA344D37488610E0E78BE6AFF009C9CFCBF4F347E5EEA73BC5FBCD3E2661B54FD963E199D824E83541F83FAA5A1856F216D9ADA5650A7AECC7B66EB09D9D0E6D8B0FEA58E5E5AC1D9072F7DF2516B9287ECF5ED96071E4A2A773963490FECABFE717FFF0059A3FE71DFFF00359794BFEE8D69978E4D279BDCF0A1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFF00FFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87EB35F7CC572138A74DB2415A634196054B67900E44E487355B1CE38D2B96EC8282B9AB74F1CA665880A912F22CBDE99ABCE5CCC5B3EEAFF9C4EB113DD988815E25AB4DF6CD69767A626DF647999435D5A5BAB1D880403FC32B93BDD3F37B97962CD6D34832280ACD0EF414CA245DC621B30AD5A77B849A302A6A77CA2459494B4AD209812478C115DC951FD32316A1CD3AD5F49D15B4D91DCC3EAAA134655EB4F964993E64BFF300D235631DB30085C8F82806C7DB1453D5F45D7E4D52D54D6A683F56147084DE0BB3C9C4B4DB619645A728A49EE7799DFB1E992A68A627A944199EBD4EF8D2D30A9AD944BF3C6928AB440B28A61566BA79A30C559A58BFC43E8AE2A9F46FF10C55388EE00551E191BDD04A3A3BAA8EB8414A363B91B6FF003CB1B02312E47CE993661328DEB43E3DB16E099C4E38FF000C82A2606A656528D2A5C0A75C84D5BF4CE6310DA1A2877C890DB169537E99590E405754DC572239B656C98C511A74AE4E419C10D7111A1F7CA64E4001085780DFE9CC793090097CD7263A953F17860873681CD26B8D4E58EB5A6F97AC92A7BD9A405877E9938B4C9AB7BC9D09CB29A095433DDC8E4AD7010D68F82EEE0515D6BEF819C5368272159E9C69DF16F82223F307A4425457A62DD1016DF39D5F871FB4A28299092C804A6386EB4B928C84A9EE722C53D86EA3640CFF0068E2A98DBCF0D462A9EC13D221C3ECD76CCBC5C94A6B67744115CB1C2CE16F9AB4B1E63F2E6A1A4900FD7A090014FF00248CC9C45D06AE0FE76FF3A3CA4FE54FCC6D7347652AA9348C05283AB1F6CD96293CFE68EEF03BA6FDE483A10732E25A2B648E7152773BE5C1C69F35315000AE6445A96E5A036C407F661FF38BBFFACCFF00F38EDFF9AC7CA3FF00746B4CB43833FA8FBDEEB8B1762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF884B31D2B98AE426E47C3920A8694EDB6F9605482ED982BD01AF864952E86670D435F961B414D50730396D909A0370370B8907B7539AECCE5637DD7FF388F76A359642E05617A0AF7CD74C53B4D3737DAD359B5DEB30730410C280E63C9DEE9F9BD5EE3513A558B2745F4F8E5327718B9304B6D4629D679188EA69BE63C99496DDF9DEDB46B231B952D355547707C72316A1CDE15E63F38EA77F2C896933047A8A2939364C22DF48D42E6669E7E72173553D7157A56853DCE9A1622080682A715672D70DE9A3AB862FB900D72D838F991729E56E186EC7A81D726D2905FC25CA9FF277C558B5CDA9049A6D8AA95AA71931565567B32EF8AB2BB37A11BEDDCE2A9E46FF0010A9C55358CD40DF21D58946C792641191E58D811B1D6993661368998003C316F4D6273415E990423918D4532B294EED8065DF6F0C849513C465242789A2828720437464B553C7AE4087263255A002B4E9910376CB454520E3D69ED864DD0536219C0EA3C7289736F082BB1C50D3AF8E63C98C986DE4AC3950EE0E087368EAC3758BA99281493F2CBD125897130B346FDA66A11938B4C9111C92B15A6FEC32C0E396596B6D298D0AA924F51896B45C92C36A959402E3AAF7C8B6458E5FF99A0A18E21E9F0AF3AED8B7C5875C7984090BF30057662716F8A79A379A8C328726A95EBDB2052433C9FCC16DA94694515EE46458F0B514913505413E0315E14EED0C408E5F0FCF6C53C29D2CD1A2715229D46F97E33B302AD6F78A1BA81F4E4F89AA70B09B5AEA9CAE91030658C153FECB327149D36AF13F22FF00E7387C9B168FE686F3446800BE6259C0DAA6BDF3618E4F37A8C745F9A7786B7136D4F8B3638DD7C85256E09ED9787167CD408A66445A9665A1BA2FECC3FE7177FF00599FFE71DBFF00358F947FEE8D6996B813FA8FBDEEB8B1762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF884B33D37AE62B909B337C24788C2150E57E1396854A665FDE0C4AA4F29A4D5F7C802828E0F545DFC36C24A02E97EDB53C33132072B13EABFF009C5AD5E3B7F345BC6ED40411BF8D735F986CED34BCDFA88F12B6A36578375E20D730A4EF74FCD11E719DCE8F2CD16E6BC49CA64EE317278326AB710C14E5BF2E998F26526317AF79AA5F3210480A29918F36A1CD97683E45BBB86599E33C4D0D48C9B27A641E5FB0D360637340C47C18AB1BBFB6B494B7A43A74C550FA4A869DA222841A0CB60E3E6667058D09A8EBBE4DA503358061257F9B154A27D257D36FE98AA4034E546229DFAE2A9D5B5B22815C5536854290A3A0A62A9E47D302A2524A151E191EAC4A670CC7EFC90649847374CB1B022D26AE4D984E6396A06FB5316F098C6D5A64109A44C2A32B294D216A50D76AE4648298FAAA76C890805BE55CAC86D896F9656439112E26A322DD12A65C0EFF003C8C9C9C6D7D657A65126F086BA9818CF8E63C98C983EA32F10EC3C7A6087368EAC1AF2F19A4A7865E8936B7C562A1DF261A648ED37518849CA4DA996071E48ED7BF31F4AF2D69F25D4AEA0A834AFB62583E6AD6FF00E7226C2732CF030660C40A1FECC8B38A3BCB7E7A8FCCF6F73717527A224A7A5BD2B8B7C53237D1CC5E2AF348F7AE2DF1627A87E63C76529B0869198F6A0EF902D8032DF2B79D6FEEDC0E478B7BE4534F6FD0F5096499096DCE2B4F4225E5E1F175C5691261936A4DC76E95C9034D13E6EF425FF968FC71E26C10B08CB698D9D02B739240791AE64E29381AAC3B3E3CFF009CDAD163D5FF002CA3BF02B25A30634EBB54E6C71CDE57578F77E2BCF2195DE43FB44FE1B66DB0F27479051428EA3E79921C39A1E41B9F9E6445A90A577CB83745FD98FF00CE2EFF00EB337FCE3B7FE6B1F28FFDD1AD32C7027F51F7BDD7162EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF87DB371502A2B98B4DF69C13D01C90554E3B7D19312094A275F8FF005609482B1EB8DA6F964225055E3625900049F0C9A014D110167327C238EC4FCB31F240B938E41EA3F935A8FE8AF31DB4AB2846332802BD413D7303340D3B3D2C85BF5FF4FBEFAC6876376AC1C7A43E319AF90A77DA722D34B906FF00CB573DDC3120663C8BB9C4367CFB35ACCF70B0202CDCF751D466392999A7A8E81E5AB7F53D69286408A48F0C62D319025EA16D3456B0085221D280E499DA4B7BA7C9A99268691EF418DA5E5DE60BA83439191E550D5FB24EF82D52DD0754FAE6A30B28E20FE396C0868CA1EC8564540C632011B31193B684ADC1624914DF738ADA94F1A985A8C09A614816C6CDB92C4D3BE282295046CBD01C55150EC77FC7154F22208EB8AAB2005AB9162533840A75C21923E351B64DB0146C69B64EC3208A89CF2A5698DB784EA17D86F90B54D616AD28722529AC6F414272242AA2CC01153898B0E0368F4DC546FF002C818B6C415F46F7CACC4B9116B953A9A7CF20634D91284B89000486195483978C84A4DC7C62AD4F7CA241C805B966AA1DFB65120C64C33569F843235413D86463CDA3917973DEF2BA615AEFD32FB6264159AF9A12ECC85815D864E2D5229249E6048924DC230AD01EB9686821F337E6C6A3ADEBF6F1D8D8A4A07A8DCD97B838086145E59E5AFCABD62E21ADDBBAF26E546FA0F8E459C43E91F2CF96E2D234B68AE18C92C207A2ABB9F7C69BA3B2AEA725EDB594AF65138918100537C5B8178FE9DE5BD66FF547BABE57ABBD547B64241B4483E92F2CE8A6CE18A886A06E70527883D934189F9AB73E86871A5E20F4069278F8904D3C7052F105C6ED89AB392DED80DB44B9B5F5BFF29B23BB91090554BE113C4C5AAA55AB5EDF3CC9C4D3A88D8D9E47F9E9A41F30FE5579801AC92C51B32C23AD02935A66C71BCAEB31905F8377711867B8848A18A46047FB239BAC3F4BCCE7152418076DBBE65C5C19858EBB9DB2F8B4A1580CB04806E8BFB2CFF9C5EFFD667FF9C76FFCD63E51FF00BA35A65A393813FA8FBDEE9858BB15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF874B265328CA593246507853C72259C513C07139596D0935D28562721D5279311BC929375CBA1C9A24BE0B90268E872F8F26013B9650F1EE72126FC68DD12FDAC351B5B98DB8B472015F6CC2CE36761A73BBF5CBF267CCA9AFF966D6D24903148C0A66AB287A1D2C9EF3A5AA3C9FA30AF10FBD735D91E8714B6615AEF96C695E6084C43E0998548F7CA29A739B7B3E89E598CC6B3247EA19D0034DE996403878B9B2EFF095959C5F5ABB70AA054A9C910E482F39F3AF9B340D26C192C028B85560E57B900D32B21982F8EB53D425F345F4CD2A91463C09F9E565B014C740B2D46CEEC39AF18D870F964E0C66FA4B4DB8379651ACB27C400D8E5B17166956A21E262886AA456B9634F54B2332383B6581C8835E9301B8DF0144D690475C0C694FF6CE2B48F85C01F2C569188D520D698AD2650B50628A4D633D314D23636DBE58A8B6D1C73A62E60E49A44FD37EF8A53BB6906D8AA64241B6298F36CB0241CB9C9149B5ACC1471AD2BB656BB2600823AE40ADA1A7038B37865534DA493C828731CB740A4B3C94AD3AE51272E054DAE1BD322BDB2892C8B17BEFDE73E7D32B71E658826968D7264A7535C21A2D327D2BD40401D475CC88AA42FE528A69099146F99015747E43B157E7243EA03D0787E38C92194DAF9574C8900FAA8DBDB20C9CFE5AD3CCA812D40F1DB154F57C91A75C45536C3953C3FB714A4375F9736C2559628F804EBFE75C8948445AF97520223A74C0A9FDBE962CF7036AD4E2A9DA32BC746C550727A49B03912CFA2025B845DAA3DB20D40EE827B9574702868466463E4E55EC93F9C6E3D6F27F986253F11B4947FC21CD962745AE2377E0879810C7ADEAA87AACEFFF001239B6C3C9E3B55F594A17F8E67637026B24077FD796171D02EBBE5526E0FECABFE717BFF599FF00E71DBFF358F947FEE8D6999D0FA47B9D7E4FA8FBDEE99260EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF861D3DE432034DBC72964CB565278537DF2259C51C6600152C2BE19516D093DDB13CBDF23D5279308D403ACA3623C32E8726892193D65742548F039911E4C03258F9B415E276CAE4DF8D49039755A94A1AD7E5989945873B0BEE7FF9C71F39AD95CC3652DE0EC3893F3CD7658BBAD34E8BF49F4D9C4C12F512944AAC9E39AEC90779872A6B75045AA442EA420CF17D95EFB653C2CF24AD9FF94B596B3B6953D1FAC49C6853BA8F1C9014D111458679F7CD173E94886630F20691FDF890DC0BE62BB6B9D42E984E8E6366F858F7A9C810CC1645A779360216644E04EF9510CC1640DA298978C49CDA9BD30C42C98F1D46F6C2F56DDB946A4F4CB22E3CDE84B492C83BB0666DF2D69EA81876276DB261C88372321620900D3A602C8A0E414DFB604521F73B8FBF15A5652C3A0C5691F1F2A2FEBC56933849A5295C569348C9D85715A46AB506FF004E290162960DD3E9C5BC724C627A002B4F1C529DDB38DA8698AA6AA4F615C5239ABA1EE72E6DB541280CA396F5DB2056D324B903E127E2CAD6D7CF23189FC29D72A9A82C7AE255E81B7F0CC72DF0293DC39505A9D3289B95028069DE9D0D3C7289A494AAE999D08A6F5DF2B699157B5B55600D37C21A93AB6B3ABD38D76DF3222AAB3D8AEC547CF3202A360B3429B10580F881C64C8223EAE9F6792FCB20C913069A240EC08057ECE2AEE735BBF02E062A8EE64C4C18862DD32254242E8166AB0A6F812A978C161AFE38AA412DD98E062AD52062AC4DB54B862C0D46E72126CE88492FE43B16A6443472288B5B8E714A7957A664636F32F4A035894C9A16BEA4D57EAD257FE04E6CB13A0D74DF86DE6D503CCFAE85DC0B96A7DE736D8793C9EA0DC98F053E199D8DC19B9CAD3AEF96171D06E2A76CAA4DC1FD92FF00CE2FFF00EB347FCE3BFF00E6B2F297FDD1AD333A1F48F73AFC9F51F7BDCF24C1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFF00FFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85DD35C96DF2964CB6023E1F9E44B20BA47FDE1DFBE5526E081BC7A2935E8323D527930FD45CB4887BE5D0E4D1253673CE1073223C9806516854C237CAE4DF8D12618E343336FDB31E4E66366BE45D767D175482E623455606BF7E61E58BB1C137EB17E5579EA1D734AB485E60D23280457E7EF9ADC917738723DF2D678ECAE638E75062946C7B6F94F0B9B1DD1535D496978CF6BF0C53281B63C283B21750D223D6007B800D7C7224282803E4ED387A4428AAE4086C05192E8F15BC5C621B814C810CC1632F697AB3858A22EA7A90322366576C7353F2BDE5CDCA4E632287241A26C820B0921B658DF7232D691CD4A5B431C6C476C9872209210DC8961BE02D94A72313B605E15200814C5695D6BBE2B48D4278A8F6C578531B76E9B62BC29B420938AD261E9B1FA714D2F119FA462D815D63DE98A53680D0818AA6F149414EB8AAE6980D89CB99F12D5605948EC6B5CACAF12A7D6292EE77C815E245CD755888F11954D416352CE7D43ED98E5BA086B89B92F1F1CA66E5C4A5D24C146E731E4A4A57713165F8776AE56D5229B69DCDA95C218B2AB5421AA4F514CC88AA37EAE5F71D33202A8C917A2373C6BB63264103FBB06A64A78E4192610DD451A37192BE3BE2A96DC319E4E4B21A0C551714AF1B202D514C894847CBE93A87D8918158F6AB72681074A74C55894D73C636538AB16B9999559972326D1F4A4C2EE477A1AD322E393BA716F70638580EAC4664636523B286BB2341E5FD79D8F10D6B21DFFD439B1C4F39AF93F11BCC8E24F30EB0E0D435C3107E939B5C3C9E6729B293667637126A2CB5AE5A5C7502BBE5526E0FEC8BFE7183FF0059A7FE71E3FF00359794BFEE8D6999D0FA47B9D7E4FA8FBDEE79260EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85EB109D5771E394B24EE19497451D3950E44B208D724C840F1CAA4DC1037B50A4378647AA4F263B7B18AC6C72E872689206EC51A2653D32F8F26B08F82E48A440FEF295E39093918D3FB23F595759AA225EA7FCAED94172E299412FD5A2223FB75F8328C9172B1CA9F48FE507E663685756D0DE5D985C90B1A13B577F7CD7648D3B4C3983F4F342F325A6BDA35B5C3DC235D7006300824FE398C767698B202C860D46692358E54A18B704E0B05BA62D35B6BD77DBA0C890C63128A79D8107964086D102AC932B00246DBB9C898ADD2296EE2B643E8C425E5BB31ED95914CE3BA167D4A2991B946108F6C431940B13BCBC049111AD32624D3E19B4ABEB13382AC2993130DD114974E3892186F8DDB6092041DCD46D8AF12E6EA4D36F1C57897091456A715E2565993615C578C2656CEA69EF8AF104EEDA4407738A6C27B132151BE295540189A62CD1023351B7D38AA24232D0914C55148DD29D31559373AEC32DB544DB9A0F8BE9C812869C7C7C874F1CAC94B6F229000395CCDA84AA68DB996A6DDCE63B7C36414CE95A577CA66E540A45792D09DEA331E6C88B41C2FCE4006E7C32B6B9448DD9259C81283A64C45AF8939FAD3715E0475DF2E0BC4135B7BD2A9566CBC49788295C6A1137DA3CA9D4612CE26D8F5F6A9044A5AAAB4DF761FD723C2CE982EA5E7AB3B105566524D796E36FC71E15A63C9F9AB6B1D47A8A5BE63FAE34B4A4DF9AE92238128592BFBB507AFE39190544695F99324B308EE25A027615FEDC8D26DE956DA8C1A8C0662D56E831A5B63BA93FC7C23A927B531A543456723C3FBD4A313F86464D8262A90ADA70462426F91693024ECB5ADCAA0929448D8733E19763906194F0879FFE736BA343F26EA571EA7A69776AE96EDFCCC54803EFCD86298797D76404BF1AE617524D2CB74BC6791D9A415AFED1CDBE290AD9D1CE24EEA7C18F419990C81C498A5AE8C06E2872EE305C7A52E058ECB5CACEEDC1FD8DFF00CE308A7FCE35FF00CE3C8F0FCB3F290FFB935AE67C3E91EE75D93EA3EF7B964983B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85BD3BEC652C938B6FEF53FD6C89641399500ABE5526E090DF3B32B11D40C8F549E4C7AFDE4E31FE397439344905217668C78E644793584C2C406BA27F9475CAE4E4637A6796B49FAFD95DBA0E455AB94D5B951E4C7EF266B5BC6B775A50D304A2CC4D7C26EBD7496194C7E97C48D5EF985931B918B23EA0FCA5FCDFD5B48BEB6B6D4EF19AD9481F131A537F139833C4EDB06601FA51E59F3FF9775DB2B764950C8CA3910457F5E53C1C2EC6192DE8D0C7672422789AA08AED91B72E050F34C84A88C74FB59025BC6ED2C8DF3F0C899354A289496A8CBD2BDB21236CF0C50922D011E390A6E314BA4B22416A634D44255246633BF638D3021033905B7F0E996C5ACA0682B5C92AC734247E18AA8337BFCF1572B74FD78B594CEDA6A0A62D914EADE652462CC325B6605462D89B4282A3BD71668EE031558ED8AB68DD3C3155772A77C9AD2989950100E56534A46707A9C84969A1254D474CA8A40DD648F5539516D8A4B350354E5126F821258048390DC0CA26DF14B638945C71E5C4D3AE463CD728B8A6358E05E4641B75DF2C70F850ADAC5B16E0660BC4D6B5C9C53C0D4FE71D2ACA2224B95A8F7FEDCB578581EB3F9B1A3DAC4FC640C4D42D3C7EFC945B2029F38F9ABF379DE597D19DD63A9E95FEB926C7906A1F99CD71EA52E9AABD4127FAE2AC557F301DE46ADD356BE3FDB8AA656BE75BD915E4B79CB2A1A36FDFEFC8C9059D7947CED24B7F18BB6661C803D7FAE455F61F973CE960B68891EE0EED5F1C559347E6182E6752B106A9C55939B95940658F8823A64486A3CD0CEF5AFC19590E4C64A2FE9B2FA4C2866EDF2C9C22E0EAA5B3E40FF9CA7F308B7D046911BFC71A9F86BF3CCEC71793D61DDF9A864E681D8FC4D527375861E975929521B91AEDE399020E26492E33A13C5B7236CB40A68B4D2CE28A4EAB9600DA0BFB03FF009C6A017FE71CBF2000E83F2DFCAA07FDC22D73363C83AFC9F51F7BDB324C1D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85CD3C715A1EB94B24E228DC303B75A8C896514F44323C058D3EFCA8B70499EDCB24B5A647AA4F2492F638CAAFF91F6B2E87268925A5E07F8D2B48F63F4E6447935854B3E4B73327ED15247DD95C9C8C6FA0BF26AC66D5D2F6D22512486A287A6541C8E8C6FCFDA1A586A5710950B75093EA0ED936B32A79F192E1220A4D2A39291954A1698CD562D4A485158BB2C8A762329389CFC59DEF9E40FCD2BDD0960334F2B46E42F52694FA7313538A83B3D36A1F767917F3C6C350B782092EC06A00431A78660183B5C595F44693E60D3F558D7D39939103BF8E53214E7C260B295B66E21832303D286B94CB666775DE8488EA781A1F6C31DD944D35246EC6A23603B922992A64648A11A98B8820B569418D06B24062FABC2D683D4996887A531A6B25884B751B92C84D0F8E16050F57FB5514C285BEA57BEF8AA9B9F7F962AEE42837C5ACA2612716C8A3A17756152298B30CB2CE71C455B16C64115C28A55A9418B3470B943D0E2AE3206E877C55B57A0F7C5548CFC07C677C9B6D20649CB37C26801CACAAAC659B71D32125562DC76AEE72A2AB7983515DF2A2CA2955F3D15941F88D29944B937C1A8A4021F8CEF4CA26DF1631A9DD7A3C9D1A86B4C8C39B29724A9EEE49E3203354E58D54C4758B3D4562F5226DCB5363BE4E2B4C2A5D0754BE722494F1EF56396AD05A7C8B6654FD61CBB1EC7A64A2B4935E7E5C79767564907C4C3C324AC0F51FC93D2262CF6E7883526BB62A935B7E45D8FA9B9A927AE2ACCB4AFC83B510CCFEA705E42A323241677A0FE53E83A5C81A62A597C722AF51B0F2BE90CEA96BB1514A2F4C55153583E99708A895DF638AB2386FDFD3459051FC3034CB9A2D6E19876C8F0AF1D34D342CF1CA4F1FAA2B35D7CBAED9918A3BB81AAC9B3F327FE7257CD967AF79AEF2D74D9DA48E0E4AEAFB6E2BD33638F1BCDEA4D97C82C5D763D3B66E30C7D2EA66511000C4572DE4E24CA356D6126BFB44EF86D880C92C2CE20061E26F8C5FD757FCE3780BFF0038EFF90AA3A2FE5D79580FA349B5CCE87D21D764FA8FBDED19260EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85BB6F848AE52C9904352B5F0C896514D3D7E36C77CA8B704AE3943AC9BEF91EA93C921BBFB2FF004E5D0E4D124854D2198FF94BFAF2F8F2601358471B966F18FF0086424DF8DF5BFF00CE24D8A6A7E63B9B6715564634F7198F234E544585BF9F9E5A934FD775396342055B7FBB1136B9C1F3100C614E4370B96036D06C20E54F8078D72756DB09334D247FA1C629B9EF9467C761CCC597853D827BFD3254B8B6BE642A6A141CC09E27698350F58F2A7E73EBFA4DC470CF3B988951CABE14CC49E276F872BECDF23FE7858DD476EB3DD8694815527E5ED9893839A26FA16D3F306DEFE28A55911380A28AF5FC32318A99D2723CCC6F10233A229FDAC970A0E54AEEB5C834C9030BD578C8E4C6BDF1E16A395E4DE74FCD485A64B586753DB63FD98F0B039103A66BAD7D124BCEBCBC30726C89B4F45EB1A2F2DCE2CD1092357AE2A88F888EB8AA834850D3C31605562B923BE2CE2895B86AD6BD716613AB4BB7000AF5C5B03238AF8FC3BEF4C59A651DD934DFAE2A9925C57BE2A89593E1FA314C79A065763BD726DF4A22BD7E9CACAF0AAACC13BE424BC2E6B8AFC43B6545045209AFB8BF5CA8AC50571741DAA4F5CA64DF04BE7D43802AA731E6DF148E59BD77A3FD9AD4E461CD390D04F6CCD998C291BD32C71B8D4EE6D2240640BCD5B6E3FC72617898FB58A732E4045EF962F1A51A8CB688AA88FF001D77C9C59C656C3A7748E4E6D27C35AE49938EB9A728547DC8C55522D734F520C7154FCB154C63F30B49F0C44411FED29DAB91920A5375AAF2B9553217A9ED9157A77945B99568C1E05A8D5F1C559B798AC80B713AA0E406F8ABCDE3D44F221CD181A53080E3CCA6D1EA238F5C20346495060DF999E7087CAFE5C17AB2859EED1C15AEFD08CCCC31757A89D87E54F99EE6EB5CF30EA3A93312B2B311F4939B5C50D9D266365881B52546D999195075F38DABDB5A9EC3194DA2504F60B08D8AF8F7CACE4651C4CBAC3490C071E9E190395CA8627F58BFF38EE9E9FF00CE3F7E45C7FEFBFCBDF2C2FDDA55B0CDCE23708FB83A3CC2B24BDE5EC59635BB15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85884F361C76CAF8536C96DC111904D6A301816424D4A58C5C075A65462CC6441C41E1560CA5C9E80654765394525D76A423B31095ECD96E336D12CA18F05E69242AC0B484107B6C733230348134E0FC0EA6849910814F964278C86F84DF60FF00CE1AFA8BE6C997890CB5427B6F98594539D8657B3DBBFE720B44F56E352AA8320526BF40CC7E2A679214FCFAD422F413895A713C4E591CAE14F6485D8351286B5CCA84C14464F40D0A20D0C709A127BE3964006F88E264377A544A16AC797E19892C81CEC008418B78D5951D6BE04662642EDF164A099E9E2EED2E565B29CA1435A1398920E58D500F50B0FCCBD6A0F4E192770F0ECBC4ED4F7DB2996CCBF3024CDED7F37358E01259E4E245363FD991B41CAA7A97E61EA7771FA705CCD461B963FD98DB0F1187DA6A375A8DFA09E4919F97DAED8DB1337D27E58965B5B3854B971D70F0DB958A7419A457ACD2AB576F0C781B78D90C37CB51F153E79122990926B15CABAECE3E9C090B6491493DCF8E282143D555F1F962C86C8859EB4A57DB1640A696D24840DF167C4C82094556B5C53E204EA09169DF14F1A3A19949FB58A414DE27523ED8F962C816DD4788A0C9B68CA165630A41EB4D8E40848CA12C9E4E3FB5F3C810CB8D086E9551AB5395485053249E694B3F2076F0CA0C911507B8526B5E998F2986E8EC954D3879295CA8FA999CA229B69D6F1CF22ABF422A708853564D4090A6616FA65804AFC5CA985C7F1107776E2353E99A8EC3240AF88C3AFC4EA181A508E8327C6BE2306BCB3672F20635EA41CB319B6DC79186DFC7292CA49A65DC2DBE284858411732558B1E95C7857C5097FE9178643C5453DF1E14F8A11BF5DFAF71E6CD13A0A2F1E87E795CC5309660CAF42B41EAA34BFBDDF6AE52649196DEDBA3AC91C6A21554A374C1E2065C69DEB17D722C9A3908269D7263763E2878E3CB28B89030DEB52C3A53261C7C99022CDF47042D34AFC238FED13964604B87972BE3AFCEAF345C799357B8D2F4F98FD4ECA80723B1AD6B4CD861C6EAB3650F99E6B7BA8AF3882BE99D9877CD9E3141D5E49A0EE2DC076006C3265C594952DA2A76CAE450236CA34DB28E591405353D7316526F8637AF68BA242630596BB6527239718BFA78FC884117E47FE4D443611F91BCBAA07B0D32DC674FA7FEEE3EE1F73CBEA3FBD97BCFDEF56CB9A5D8ABB15762AB1FA7D38AA962AEC55D8ABB15762AEC55D8AAA62AEC55D8ABB15762AEC5572F7C55762AFF00FFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF858B4FB432AE36EF0D94C1F6457A77C789061488210F4C89DD81539220637E0C15E9F0B7865660C244524D756F13A52693D47F019761C6E31905B67E5ABED43F756D68EAAFB89E9B0A66C61065198B425DE8B7BA7DD4114CFD036D95640E66220BEC4FF9C378DD7CE37019830E62A335D99D8E10017D63F9B7A7A6A3AB6A7005E47D36DBE8198526DCA6DF997E77B23A6EAD25AB0E2A0D7200D38138B07922A10C33271C9AC459B79526E573E9B7602992CC6C3958B67A75E5A7308CA3A8CC493B0C692359B89375DF28C8E6C792BD9C6E26238E63C925131C00CEC5C5083B644C6D946549998D5694C1C0CB8D1B1395E31815AEF8F02F1BD5FCABE5F49D16764F8BAE3C0BC4F5AD3A16810474D94ED86A9CAC4764E919B983F7E25B814DE201A8CCD4272A93644A690F1029C8E41B0146FAD40003B0C5928B4FEF8A559273F0D4F862A9A5B5DD286B8109DC57CA08DF0B105358F50403ED7CF16C051297B08228F8B3051E9A8A01B3F4E98B3B6CEAC4FED64896622B7F4913DF205988A067BD249F8B204B608A0FEB47725B6CAA6526282B8BC201A1CC599445269AF5FB5731E4DBD17DAFA93480F63928868996716303222B74DB2E1071AF74EA3770295230F84B6A53B32AF23BD71F097898EDED5C1F871F096D8D4F0121885EBD72CC71A6C81B63B77A6990310BB9AE5CDAC12FF4AB80CDC50D0F4C558EC9A3DE3BEC877F6C551B65A2DE8956B1B52BB653910636F54D134C962319910E6290D9183D46C42C7C05295DE980067253D7A655B76DFB665422E34CBC8AE2ED959C0FB3FCD968838939879279F7CD7E8594D630B9591C1008F7AE5F8E0E1E49BE6986D2E66B998CE4C8EC49673EF99D8F675D96492EA9A6709AA13BE65C4B8336373E9D234AD45EF84968E04759E8D2B015427299C9B61167BA2E84EAF1931F5CC6939110F67D1F4BE318F87B65326F8BFA35FC955E1F937F94A9FCBE4CD047DDA7419D5E9BFBA87B87DCF29A9FEF67EF3F7BD372E69762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF855B670AC2A330CB984B2785D5A23C4EE46C300938D3C8037146EC0D640A57B1EA73271C6DC59E7013FD17CBB7FAEB98A106353D588EB9991D3B85A8D50112F59D1BF2CACE1557BF01993724E5F8F03A39768B3EB2B0D26C08B45317A454D401F16C3C732BC3D971F68BE7EFCC1D3E117D1DCDBB5631C8041D7BE60E78D3B9D2EB789F437FCE1B5928F37DC48C4A6FCAADEDDB35595DFE973F117D6DE76B8853CCBA848F19742AC38FDD98530EC4EEFCF4FCE7D0AE26D50DF5BC263889E5D3AF4DB292D5283C3230655657431B275AF7CB612A6A31A651E578C9BE1C5A94A65933619E30F7148792460EFB653273F1AF4B1ABB029C89E87C3289B9B16EDB4CFF004A3F0F5F6CC7924A3C6845A6E741D7A530C5814DA3F2DB4D4A2D3DE99246E9B59F955DAE230141A7B62BBBD7748D35ACA255DABE00615B2C9238C83C9969ED95973709D912B1FC4081F31912E48298C7C788D8E552660A2D08A743EF90660AA7A95D80E9D31656EE05BF6A98A6D772A7C35E9B62B6898A4E3D4D7145A244DDF9D315B442DD11D1B1640A3E1B84A8AB6F8B65A6D1DC42109A9E98B3077524BA534DFE5809724144FAE0A9A1A6DB6449660A02595BFDF832A25B0150FAD71DB9572B994CCEC8692E958D28731A4D112BE38BD42083D7B656D97B325D36D5415240F965D8E2D332CC15408C00287C732E107189B2B7E207ED5465BC010A95E638B0AE3C0150B2DB07E8B8F00543A699CF9D453C32328D3762431D138B54D18785320DE8693CBF0CE45502D3C7156E2F295B83560A47CB154CE0F2E598707D351C4F865536704CA6D16085032D001945365A47724C33A90A7805EA3C7271834E4C94C63CC5A8C0B6EDC9E840DF3371E375B9333C3755D61A4574B67F1ABE640C4E064CFBBC875ED367D4A61297F881DC9C988538F3C96A56DA488630CD1737237232D8871A52B492FF00449A798BAC3B78658254D2425FFE1E666DE2A1F9644CD90C6C86C3CBDC40AC5DBC321C4D831B38D3F468D387EEF7A6F9592D822CEAD2C5234038819596603F7D7F27871FCA4FCAD5F0F286883FE9C21CEAB4FF00DD47DC3EE792D4FF007B3F79FBDE8D9734BB15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF84F5A9238F5CAA50699EA135805C2D19413C7A0F1CA841D7E5D4BD77C9FE4C9F5955BAB9531A36FB8A7F0CDAE9B15BABCBABA7B25BC363E568B8428AD2D3E13B75CDC474FE975F9B54642929D43CD1733551578D7C3251C4EA660B1C79EEA4E530908600D30CE1B231715A437D65F5A82269F76DFAE6A75317A5D04097D2BFF38A5603FC513C69B50F519A5C8F59A4818BDF3F32C8B6D7EE550D4EF503E8CC4C8EDA06DE35E6CD274ED634C8E1745F5C2F2DC6FDB31645BC45F0F79C3499F4CD4E482088842DD40C6326A9C55BCB703DBDDC25B66722B97036C22FA334EB45963889EE06093998D91DB69F1FA87E1AE5191CD8F245DB5845F5ADD46512E6929EC1A746D253877F0C8DD2631B66365A3C2101283071B2E0641A7E9B6AB2F22A0536C7897813496D944AA517E11D71E35E0463C28C415E94186DB63B2E580602DA0AA88B88F6F0CAA4DA0B74A641902A65A84D3164D09A9D7155F50686BD77C55DCC0DB90C554DA6A54F2C516A26EE9D1B16C06D552F883F6B0330518BA937F37E3859834AE97F4FDACAC96C1345AEA15523976C812CC4D4DAF474E5BE544B671A88B82CD4E5D72124CA7B2AAB0E42A7BE504B18B23B331FC3B8C039B6B27B39A35A6F9978A2D190A73F5D8F8D2BBF6CCBAA71495CB728DB636BC4ACB301B8F9636BC498DB3ABF518DAF126255154103AE4265C8C25AA467ED0CADC97048491B0C5556910EE31550692256A823DF2B9ADD216EEF23E34AE5643194D846AFA82420B33058E952C72EC637703365781F99FCDF662678397A8A4D0919B2C7074D9B3BCEE7D5EC0C9584D10EFF004E6588389E2DAFB7F4AF4811906B80C11C68897479C39A3D01E8BE19514A9FE8B71F69B204A695974DA007803EF4CA8C9CA84764C6DED78EDC0636CF85905B45180BF08069BE368213650028A0FA3157EEEFE50FFE4A6FCAFF00FC04B44FFA80873A8D3FF771F70FB9E4753FDECFFAC7EF7A265CD0EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFFD0FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF85F8ECA6827585979C84FD919084FC474B9674F6CF29F906F35036D773218A18D833A95FB437DB719B1C3A1E274FA8D43DA2E0C763686D6D2216DC06CE3BE6D30E8F81D5CF2F11610F34D76CEB212EC8767CCC3300535006ED2AB986E56455F4CB03FB54CC7F1699CB7539675B32AB335030DEBB532B9E76EC1877633ABEBD05BC4817E3095DEBE39AFCDEA7A8D0E301F49FFCE3379B34BD275C3753CAABF58466DC81C4F875CD5E6C342DE931914F72F3ADEC17BACC9A94171F5A4B9DFD21FB35CD7CE36E44254F2DD5E40F74AC253188D3E28FDB31A78CB951C8F3BD4BCA69E67B826C23334CBD471FEC395F0D322049E6DA8F93752D03518E4BC89E28D5BEC95207DF4C2254C6389EA9A33F3B58E48979851B8C4CDC884699569EEACE6BD5FF000CA652722324EBD158255940E61BB6545927D6AB1D43EDBEE47865728DB644D32A8A7884600F0C1C0CC4ADAFAC952283BE3C0DD18DB27B39964B73CA3DE9F6B1E06C186DD1A90A6A77A9C9C62D528515707AED84848D950380A2A37CAA41902A4CC7B0FA3214CC490EC549353438190921A42AB53CB14F1297390814AD3B62B6A2C66F7EB8ADA8BCAE2B5E98AD201E46AFDBA62C97FAE07FBB2A7BE2CB89DF5C03ABD0F8E02CB8DB5BFE34F8ABF4E506569051B1DF7203E2C896C89561755FDBC8D3305556E7810FCAB4ED8382D24A252ECBB57A7D39138523252716B7C626525B901DB11869978CC9ACEFD64A7C5C72F87A5AE53B4DE394935E5B53AE5BC77B35108B8EE82903957DF229114CEDEE62E5566E43C3DF1663127705DC0A2B8B3181305D460208A74F7C05B618F854A4BF87C46001B2D06DA8C6BBF2FA3088ADA1A4D622029CFF001C9702DA17F4AC4C681BE23D05729CB1A5AB405CDD731BBF0F01918C2DC7C9B3C4BF35FCCF268FA60B6B78DEE2F27158E34AF434DEA332F1E3751A8C8AFF00959F947A8F9D3433AAEA90B5BFAA2A3983B75F119B1C717499A565BD67FE71E6F9B549EC74CD4B9F04E6005EE7B74CCC842C38E254C2A7FCA7F39795E57730C9731C7D5829FE991941B23240C3785A76B4BD06DEE22DA507B6634E0E4455269E35902A2FAA9DE40731CC5B8056F5410027D9FD939518B9319505E8CE36AE222C84938B13CC8A8E9D708091BA7885760570A781FBABF947FF0092A3F2C7FF00013D17FEA061CE9F4FFDDC7DC3EE78DD57F7D3FEB1FBDE8797343B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD1FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8D2F23F91A57BA4BFD6D6BC0D68D99B874D4F199B556F6FBFF00315959471D8E9F1AA0A7024019B9C18F85D466CD6C59E49EF25615E4332A52A0D50DCB2DF2D6856B70C52551CE43DF31324DCEC38B88D3D353C81A7CB0F32AB551E03FA663193B0FCA3E7DFCCDF2B2E9FEABC7B2C7E1F4E53293938B4F4F9C6F56297D485DF7198E64EDB4F0E146E83AFDC797196E2DE46AA9A00091D7E9CC6CDB876719D3DDBCB3F9BD24F2C31DE0DA94AB1FEA7308C1B0667B4D94D69ACB0BD12208D97E3151D3289636F8657A07911B4F7D723B3D2E146909A39DB7CC738DBE395EB1F997F96F6BAE687305B355D42DA332D55454D47B0C8F03938A76F86AD6E6EFCB17173A65F42C9F1955E429DF0183920B2BB7BC4616F2C5B16DDC65660CC165F14EB3463B9032071B30532B566E2C3B83B65538D1499526D019BBD6990E16719A6917C5DBA63C2DF19A6D6F7CB1008C7E8C785BE395344BA420107AE061396E881329C892A0DAD663C8D0ED9592CC054571DF2BB6C017F2B7FDA1BF7C2A8599ADE876DBBE2A82372A361D3A0C5541AE7155295D4AD477C52934EC6BB6D8AA0C87F138AA8307DB7380A43AAD502BB7865062DA0A3E1269D7A60A6C0510A5BF98E34D80ABA3316515F98C69369846687AE16B4C2075322A96D8F7C55328A7F498156AD31564767AA23108EDDB08544B5EC55DA4FC724C81444378A5A9EA7BF5C2CC14C92F401F6F6F9E06C1277E9458EB593AEC37C84CB6715A849AC27412F4F7C8829421D6A25AF292BE1BE580AA026D6226DC3FE39602A841AA52B2C64B329A0032AC82DC5CF9B81E8FA0797EFF00CC36DF582AC8AA3AD30C62E064D45B23D2FF002E746D666FABEB1682EAEE27FDDB915A27874CCCC61C0CB2B7BEC5E5C8B43F2FBD969AA96E889B050053F01997034E0CE16C3BC93A446D35E6A37975CAECB32853E03A665C25B38C63BA6F3C3CEDB526BEB5496201B83151EFED82526710F942FF00F2B62F34EABAAEA1631FA2F393450294A57DB31E45C8807897983488FC97713E9FA8CA1A624844277CA0B7C52BB760F0C4E05030A819590C81A45AF5C14CC14D74F6F888F0C7859829D237C58D3305FBB9F945FF0092A3F2C3FF00012D17FEA021CE9F4FFDDC7DC3EE78DD57F7D3FEB1FBDE8796B43B15762AEC5563F4FA71552C55D8ABB15762AEC55D8ABB1554C55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD2FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF911B5D4CA4450905BBAE74F1C603E6D3B48AE67FACCEA900FDE16A6D9682038A626D9EF97B49957795D99CFEC9195CE6E660C6F47B1B58EC87AAF540BB961D4661CE4EE74B87D4191C5AFAAC65616692836E5B65066EFC600F1FF3E4D71AB45708F1045607E21F4E5329B64703E3BD62C6EECB539792FEE8B1F88FCF31CCDB04292D9AE9C158E18C484915AE57C56C8DA31A49596358E416F21EA54E44840059C68FE69D574B44824D51BD24151F16E7D8E63C837C6DEEDF96BF9A30697AC437F733A5B98C8A9E5D7E798F272216FBDBC9DF9E9E4FD7AF7EAF7B7111F56309212474C003978362F14FCFAD33C9F3EA31DFE8572B34929E46314A54D7C301736DE23636377C39471820F5F6C8101B0499559B2DBA8F59E847619590CC14FAC751B37996105CB374DB6CA671B613932D59618940988453D0F7C8F0A04D136CD6F2352390BA9FDA1BEFE18385B064A593E9D74D30689498C9DD8E3C2CC66461E76A80354A81BB65538EE9F15B87528DDB846416F0272B316C8E54DE3BC454025A06EF4CACC5C98E45296FE05DD4B37B5321C0DE24BA2BC570095007BF5C8B0326E59EDE9BB004E28B4B5E48F7A1A8C56D41A44DF7C56D45A4DB167C482797AEC315E24135C76A62BC4A06E0D7A75C4A89287AB43D72921B414545734A53E9C785B01B45ADC8F1C785982AE2E42D0D7A74C14C89D95D2EEA2B5DF035DAAADD92C17953DF15B4CE0BB006EDCBDF15B44BCE78F247A3FB6109050E2EEE01FB44FD392664A69697B329A93DB147123FF4A3014240FA71533A434FA846D148EF394310AA81DF018DB28654A6E3518B9DBAC324B2198D1CD3A60106CF142A299A4BC92D622F2040086237DF2622BE28651A77947CC5A9C4F35BD83945E8C4102996082F881EB3E4EFCA9BD9D44FA8388EA7918FE5898BAAD7E4DDF4559E9967A26872A40024C828A0EC09C9083AFE3280F24DBDEACF7535F5BA2C8EC5A075DFE1DE997C4522ED966AD33DA5A4934D2963312A213D299785E152D23CBF696D62D3ABB191FF007A2BDCB76CB449A670DD4BCD4C2CF402DC4249715529F3DAB80C944698D7953405B5B06BB9A61140D1C923CDFB5D2BD32B25980FCBFF003D4B379FFF003D6EF4486FCC7A769D39ACAA7ED509EB9021982FA1F54FC9D96DF4DB7BFD27525BB9258C39B52C28BB0D85322907778AEA52AE8F70D677C443780D046761F7E2C814CEC599516534A30AEDD30365A6515E425FE26A7B61676FDE9FCA021BF297F2B997756F28E8841F63610E74783FBB8FB87DCF21A9FEF67FD63F7BD172D68762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFD3FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8EF578F9B5C06FDDBF4CE9B8DE267A6651E54D1967BAFADC9BA72AAE552C8E29C1BBD62DE25826E4AB45AE56656E6E0C28FB8B9068427241F697C72B9F276F871F0EE94C97B4256184257314C9D96395B10D522BB9CC8D23058687965129399116F07F30E857BACDF35B58446572D4F8728949651A4E34BFC8DF335FC6918B3912693756A76FBF2B126118D966FA67FCE2079E3569D2425E28DBA7F9F2C4C9C918596BFF00CE13F9AA29D1E5BD255853896E9FF0D944A4DD0C4CC2CBFE706B5A9E359DB5858C2EECBCFF00E6ECA2526F8E265563FF0038CDA7E8171691DF7994594A8F4F50494E4476EB91126D11E17A95A7E487956EAE62177E6E597881D6407F8E032660A7179F957E4CD35D2DE1F32C444A28C798DA995993602A69F929E539E932F9A6293BF0120C812D82491F987F2C348D2EDBEB1A7EAA048836F886E7EFC46ED3964C33FE55FF009A2FECCDDDAAFD7517A01BFF001C970B57131F6D13CF3A6481DB4B6B5B68FED02A454F8E3C28F11136DE77BCB598417D6E4229A31A634BE2338B4D6B48D6A25442179EC6BE380C2D81CD45BBAF28385F5EC65A93B8A1C81C6DF0CC92CBA4EB91A13E9B3D3BE40E37331E54BCCDA9DAB7EF6D0923DB2BF0DCA8CD53F48A85E722346C7ED2F8661CC516CBB516D4ED5BED311F3C82AFFAEC741C5B63D3E58AA8B5E27F362AA2FA82F4AEC3BE2D96817BF5AF5DBBE29B59F5A88FED62B6DFAF19EF8A82A2648EBF6BAE45B42BC6CB4FB5BE1640AB2B8DB7C0D815B92D3AD70164AD1B0A75CA9AD7F2DC50EF8551D0135A5715477A8513956BBD310CA3CD6FD61BAD324CCA9BDF49F663EB8B548A3ED2C750BFA705635F0C5ACC99E795BF2D359D7F538AD591845506507C0FD396E38DB0964A7D476DF933A0D959033C4867812AEC7B6DF3CBBC363E2A47A4F937C9BA5DEDDEABA95DC29145BF027AF1FF006B2C18D7C5629E7CFF009C98F23796F4ABBD13CB56714B76A0A0950577029E1ED93F0D7C57937E5BFE746B779ACC375AC4AD0E9CC4B70EDC6BF2C84E14E3E63C6FAAB53FCD9F25F9AEDA0D1F43981BDA8590AF5AF7ED80069E07B5E83A3AD8E9B6019FD4927456E47B03DB260278588F9D0BDE6AB67A65BF660580C902ACD043E8C3A7C1D2A02B8F96264C4861FF00987FE997DA7E996FD178F35191E2470A335A8DA0F265F5869B66D3EA4B6CCA0815DD908C921F9C3F96FE439FCB3E7ED77CC1E77D224852EE6768DCA6F424FB9C3487D831793349D5EC21D5BCBBADB441E3E6D64ED4E1ED4C348B7E587FCE45F9ADF47F3B0D3667A346FC5A64F6A60E1660A1FCA5F98F1DD086D6693F771A84563DC78E34D80D3DA2CEEF4FBD50F1CC391F7C69982FE81FF26E83F287F2A829A81E4FD0E87DBF47C19D061FEEE3EE0F25A9FEF67EF3F7BD232C69762AEC55D8AAC7E9F4E2AA58ABB15762AEC55D8ABB15762AA98ABB15762AEC55D8ABB155CBDF155D8ABFFFD4FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF8BED12FA6D457F46FA12ADBC7F626A7C47E6736E72BCD5193DAFCA1772C114D67796EF4894FD51E31D5BB72F6C899A63A6B6609AB5DA32C37B6E2DD587C2E452BF2C78DCDC5A4012093CFBA5D95C5C4123AA9B7157693A7EAC13C9B39630D30CD5FF003318397B28A39E2ECD18AFF0CC4326D863A62B73E79D575085C4517A61F6F886DFAB29949CC807D51FF38D7F96B36BF24BE60D7A33C62F8E30BF64F43BEF98F29378C40BEE7D22C74E69E9269D0C76D07C10CB1AD18BF45AFB655C4CBC1E1DD9E8BFB58ED1ED0C421BF857933462881478E266DF8E16F93FCD9F9B3A89F3543E5DD06E92E5E7904370EC6BC2BD48DB2894DCD86114C9FCEFA4EB5E4DF2D0D6EE7CD33CD77749CA3B3864DAA7B5331A536C38E9E45E5CFCBEF30F9C746D43CE7E64D66E20B0B2532DB425E858FB0C89C94C463B64BF967F973179E359223BABDB6D26DEBEBDD31A6CBE06A32072527C17977E75585B796FCC53E81E54B9BBD4A68BE15773C8F2DC640E56DC7A7B286FCAFF002B79ECCC351D62C6F9A0421B801F0F1EB959CAEC31767F13E9997F303F2DADECC68BE67D3A6B4BB20707028DB6C4E1198863A8ECDA28BB3F307935EDBD0F2EEBB25AA1FB2B2BD313A92D3FC9A82D7B509BF41DC2C9AADB5F475256456ABFCB251CE4B4E4ECFE17805ADC68374F7306A7693F224F0940D8E644276EB3361314A5FCB71DB96BED36F0C31F22521734232F0C2386C59661A17994D9010DE5D212BB75C90882931A7AE681AF68FAB442DD6E22172B5E418ED919419C7210C953CBDA4EA2A566A7ADFB253ECE63CB6726398B18D47F2CE695A4789223131AA377A7DF98192365CC86434C1755FCB7B8B552CB1866F0EDFAF2A316CE379BDEE87A8DB170B6D29E269D36FA3234C81481A3BD462258245A7B645210F34BE98F8C1030B7525C6F2035058838B3115212F03591CFB53148802A6DA8C6845198E2C8E20022A1BB59A95241F6C5AC48A3967229C6B4F138B30510B73D37E98B74510B703ECD7AE03BA65B045070AB50D91E171BC42AA92EC64AEEB9131489A36DAEC3380DB0F118084892266BD759111006889DEBD71886C81DD1F0992E18C50C4657A7C210572CE16D243D3BC97F957AF798E482792310C524A1645E8426FBF5EB8F0B8D393EB1D1FF002374CD2AD24B8FACC8B2409CBF7A76247D38F0B8D29A0B4DF33689E5E3793CF730C571A7D79F134A8197618B10789E11F993FF003955A5E97697F6FA0A9BABC98323B49BA8EA36F87325AE669F188F3B7E6079FAF9ADAD2F561B59FD46B94534201A9006C3C70DB8E7290C5B4AD3E1D275392DB592F717334A03C8FBF53DB27C69194BEACF2AF93E2D6E1161A2A492974A3B36FC411DB2B99B6C8CDED5F941F9172796BCC4FA84AD2CE8CFCA6F537E277E9BE56CF8EDF74C691C2A8109E1025501F6C3652082F3BB1AEA1E6A7BCA739223C78FECED8DA683D06F1115A198578A312DEC72F8C0485A90180C006AFE6C9243F14708DA982500189A66B1C4F1488518AA4B5E5F25CAA52A6069E4DE67B48BCC7E67B6D2DADD25B585C7AEC07C54077CABC621AF64279ABF2B6EF4DD2B5AD5741BE6B484216823534A0A0EBBE4865B6C1005F879F9B02E6E7CD9A80D61E3BA9E1958073B9DB26269E00F3089B568627BAD3D12358E4A1E636E3ED960368D83D2B43F3C4F63259C11C9CE5968272DF641EF4DB274178807F551F9093B5D7E45FE4BDD350B5CF913CB92B11D2AFA65BB7F1CDEE1FA23EE0F2DA8DF2CBDE7EF7ACE58D2EC55D8ABB1558FD3E9C554B15762AEC55D8ABB15762AEC555315762AEC55D8ABB15762AB97BE2ABB157FFFD5FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF90FF00CB7F20CFABEB70F9784FE9BBB0569CD76FA727E3B8A3474FD1DD03F23BF2F3C93A589BCC5E60B79AEDE3E4119F707EFF007C7C76C8E969F1C7FCE4679DBCA97925BE9BE5331C6DA651649E3E8DC698F8EDF1C34F985743D435AB73A95C5AB244568B514F57DBDF01CEC8E34D74AFCB3F36EB344B1B65B6B63FB4C074FBF29395022CB74FFC91D5CEA169646F0CB792B82B02D6868457299657271C5FAB7F90DF96FA8695E588AC2E2331968C027E81941CAE6460F7693CA361A1E8F705C896E5DC145EFCBB1CA8E46E10BD9F397E76FE60587923CA7269D6404DE66D67F72186EC81E9BE0395C8861A60BF911F93BA0E956D75E74F38EA0B3DC5CC06ED8B36E8C6BB75CC79657371E36216F637DF9B9F9A52D868F792CBE5AD35E9287354A2FDDE198F2CACE589ED7E70F2C5C6B0963E42F2E5C1B68B4F939DEBC6688E0F63959CAD4715259E7AF3EE89F933F97B37956C5565F325C2141243F6F930237A57227236E3C5C4F19FC99FC9EF3579E357B2F3B7982E85BDACF299409BAB2F20DEDE390395DBE97476FA57F323F30348FCBD8C68F62F6D3B88F837115DC0A7BF8656723D0E9B40F813CDFACB79A75A6BE9E0145242328E80E0F11CC9F65F131F9AC2248F925F4B6EDE08D4C81CAC4F6481D1434AB8BFB2BB8A73AACD71A746F59A177AD4F7C9C32BAFD4F66D07D412F9D7C87AC79623B2D3ECA38F5644A34B415E599B8F23CE6A7414F0F3A6EAF7777248B3B95AD16253B53E599D8E6E8F2E1E03499E9BE5C56BD53AB192DE2247276DB2F127127165D7FA0E8F0317F2EEA85EF1002C8ADB93E18CA4D6229B7953F33A6D26F5346D6AD65424F1FACB0FA3AD330E72722117D2D63AAD9DEDB42F6B79CD5D4155AE634B9B971E48AF5AD89E372BCD7C72B219A1DACF4FB9E4A6C57813B371EA3C72B21B2293DE792B49BB25BD08D49F6FEDC810CC3CE75CFCAFB296390C4A2B534A7FB78B787916A1F965240EFC54D05698B6061D7DE50D42353FBB240E9F462CC31B974CB8B7E4B25B9AF8D31672E4848D2584EE87171AB74D61B9A8E25295D89C5B150951DF1640B4B28A80A773DB14CCEC9AC658A0DF22E2A3ED62326C7A1AE34B68D58A15824653F18E831E15B645E43F296A9E67D5E1B75899A194D2B966385948C9C2FB83CAFF0092FA779295353D5A2170654E410EF4AFD3991E1A0E77A4E82DA4699F5ED6AE255B3D3ADE3630C55A55C7418F86D32CCF3DD5BF33AE7CE574749D20496F6ECDE9B4E36047CE98F84E34B23CEBCF1A3792FCA7A1EA926BBAA97BEBD80FA4BCF7E743D725185328647E5DEBFA8C32EA57C2CE212DA991B8B91534AE4A98E4928F9735ABDD1EF0DD69B251CAB7A918F9634E29927A350BBF32B5ACB1DBB3DFB5C207A0DFED634CA2FD64FC8CF29C1E5EF2B0D66F63A5CC9081F17662A3010D8F77F23D85DCB6FA9DE5CC815667261AF8646922D3CD6EEC69D62189A168F8D7DF1A6C8DA43E4EB4FAB9B9D4661B4D5E24FBE34CF74E755BCFABE8B3C8C68ECEDC7E9E9878E934520F29DB1B2824D4E71BDC92149F7CAE5918905956A139B4B19A73B7D514927FD615CA253B6B312C23C8F6AD35FDFEB972B54B824424FBE5129313128BFCC0FD23079435716F2B969636A460F4DB2D8DD391189E17E06F9FFCB7AE0F34EA9A85FD94CD01998F220D2996C4B0902C7749D26F7CCD7834686136494AA9229C865824E348909B47E4F86CB506D288E57711A09325C6E399C83FAA2FF9C7E80DB7E437E495B375B7F20F96E33F34D2ED87F0CE9307F771F70FB9D2E5DE67DE5EBB96B0762AEC55D8AAC7DC7D38AA9D0E2AEA1C55D438ABA8715750E2AEA1C55D438AAFC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD6FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF233CB9FF0038C3E4DF2ADCBEB13B133D0B1911C0CC3B2E7ECFCE5FF9C95D5A6B1F346A1A7E95AB4E6C625612C5CC96E229B03DB1B29A0F89DEDDEF00926B97974FF5394909AFAA47BB63653C2FA87CC9AF797F56F2279774DD25D34B4D3A86707FBD6A0A751BE365061B337FC8DF2C5E79C752B7D26D6F2E0CAEEA11B937122A32B258460FD2CD3FF27FCBDE569F47B4B9B432EAF28065BC6DF8F427299172618DF40456B6BA6D9C16FA4C4102A51E4661D69EE72891A732106272C4F737978F79C94DADBC970B272AA1F4C578D077395D972B1E3DDF9E76B6317E6E7E6FDCDAEA08F69636B318F8CA7ED509FB35F96425272B829F42FE69F927CBDF977E4FBCB5B1FAE896F63315A4924C4A7A8694A0F0DF28916D8079F7E5DDB5EF907C986636E96B7BAC9ACD7DC6878B03D0E512939118A6307E6459E97A6DCD968086FF00CD3316F4EE07C67936DBD2A72B2594B0DBCCBCB9F94BE65F327999FCDDE78B1B8BA4327A932C95E0141AEC08DB2264E4E9F4FBBD4FCFBF9ABE57F2AE8F0E81E52E7677F0A18E242F550D420EC32B26DE8F498007C75AEDF5D6B378B7DADDCB4D7B292CBBEDD7C32B327A0C31012DFADC21FEAF343EABAED118C5283DF07139825174B3C0842B583B13D2B91254CE285BAB580C5EAADAB41714FB1FB253C699289759AA31AD93BF21FE5BF997CF5AA25B7942D9E390B52E6465257DE9B6676393CB6AA9FA0BF97DFF0038C4BE5FB186E7CD3299AF1497996BFC33618E5B3CA6B620CB646F9AFF0025F45F3497D3AC6330A8F843A6C7C3AE64893AE9C5E7FE59FF009C4B6F2E6BEFA88BA9E78B90608EE5878F4C12935883D1FCE5F91FA16B366919822B5BC450A27550A6B4CC49CB7722317CD97FE5DF30FE5DDECA90C72EAB696CD44F4EA683000D8F40F2E79AEC35E884733AD9DD746B7907C55FA7010CD983EA0F1AFA3C1404F85180EB9590D812B7FADCADCBD6F84F4036C810C834F248AB406BDB7CADBD21BA53293CD41C5B42552E936F221AA038B20C2B54F2BC3217A44BB83DB16C3BBCCF54F2A4B17F751803E58B5F0B13B9D1E5B706AB4FA314243346F1FDA53BF4C50A5146DC838041076C0589927F096E03236C0A6F6C582FC3D7FCEB931BB548A3A0D2A6BABC820B40CC2660251D7AE5A22D7C4FD0BFC98F24E95A1E9D6B7A2D985F08F90673515A7865F8A3BB4CF23D4FCD9E6780AC36D247CE78E3ACC3F6683AED99341C7F11F21DDF9E17CF3E61BCF275ADF243A7D94865BB8D0856A0EA2B5F6C691E25BD5935BFCBEF2569062B7A34F0A7266EADCBFD6C683025F9C1F9BBF987A879ABCC37C25AAE903E1B5727627C0602144A9E07E85AB1755670AC4D77C1C2C2791D64B67A6DC898BB2CBB8849355DFAD463C2D5C56FA7FF00203CAB16AFAF433BBC73C664570817DC1C785BE127EA25D4634ED0934E0BC04DC5828DBA0ED9121CDC401E6CF7CB6D0268F089CBAB2814A1A7DF829BC4425FE6C65BF8AD6D636E3561C877E38D331009F689666F74B92DE195614B215766F6C69B0462C1FCF9AFE876D2C3A341AA4308545370ECC0EE7AF7CC79C776C1008D7F39FE5FDA687656D77E64B5630D0944700D7DF7CACC14C22C4BCD5F9C5E4792D27B1D3F5980FD6C2ADC55813F08A0A6FB640E32D6718653E49F37F93EEB4286C1756817D221A3F8C035EBBEF90F08B0F0C273AE79A34C3A7CF099619CCBB2B5410C3DB7CC8863A0E44202980CDF92FE44F3BE963EBBA72FD6AF3ED3A902B5C3C0C258C3E78F3E7FCE23685A523EB9E5EBAFAB6A963FBB823076A2F4040EB83869C69E10F9423FC92F3C4BE66FAC49A6CB33C8D46B8553C4FE18971CE9EDFD0E7E5058CFA67E52FE5769B72A52E74EF28E896D7087A8786C21461F78CE9F4FFDD47DC3EE797D40AC921E67EF7A2E5CD4EC55D8ABB1568FBE2AD547862AEA8F0C55D51E18ABAA3C3157547862AEA8F0C55D51E18AADC55D8ABB15762AEC55D8AAE5EF8AAEC55FFFD7FBF98AAD6ED8AADC55D8ABB15762AEC55D8ABB15762AEC55D8ABB15762AEC5578E98AB78ABB15762AEC55D8ABF30EE7FE3932FFBDDF60FF7998EE4BF25BF357FE537D53FE39BFB5FEF6F5EDD716D0F1497FDDBFF001C5EA7FB9E98B745253FDFDAFF00BC7FDE0FEFBFB9EBDF23264793F4DFFE7143FE3BBA67FC707A2FFBC7FDE74194B18BF42B56FF0094853EC7D8FF0077F5E9FB194CDCA83C8BCEFF0061BFE521FB5FF1E7F63A9E9ED98F272E09DF927FDE49BEDFFBCB27FBDFFDEFFB2FE3953938DF1A59FF00E4EBB4FB1FEF50FF00783FBAEA7EDE464DF27D01FF00395FFF001CEF2EFDAFEF63FF008C1DBAFBE53258F3625F985FF92AB47EBFDCAFFBCDF6BA663C9CDC6F00FF009C69FF0094FA7FEE7ED0FF008EAFCFB65679B9717EA37997FE51BD43FDE3FF00799BFDE4E9F64F4C94F936E93EB7E3179D7FE531BAEBFEF43FF7DFEB9E998E5E9B4FC921BDFF008EDDB74E9FEEEFB1959E4EC23C936D3BFE3A375FEF1FDB1D3E5DB029F8ABEB3FDF27D8FF009E790973627E2AB17FBD717F75FDC8FF007A7EC7D39383AFD47C5FA1DFF389BD2EBFE385D0FF00BC3FDF77EB99F89E675DC8F37D49AFFF007D71FDF743F6FECE6645D066E6C5344FF7A24FEE3AFECFDBEB99117124CCAD3FBE7FB5FECFA60C9C9AC3CD3CF1F69BEDF5FF0075FD398736F8BCA74FFF00A587FBC7DFFE3A19747E9497CB7AEFFCA552FF00BCDF6FFE95B80A43D7A1FF00792DFF00BCFEEC7F7DD722DB1504FB67EDFD1959641565E9DF2A2DE12997ED606D0843F4E2C825571D5BED7D38B6B12BFF00DAFB3FECB14179F6ADF6BFDD78B51611A8F5FF00757D18B192017FBB3F67A603C9804543FDD8C805926D69DF2E8B44F9BD2FC89FF1D6B7FEEBED8FEF7E797C5A25CDFA15E5BFF8E6DA75EA9FDD7D1F865F071F27262BE69FF8EB6A5F6BFDE47EBFC32E71DF97FE4FFF00C995E66FF7BFFDEC93FBBFB7F4FF00938ABEC7BDFF00943F52FF008E77F70DFEF47F7FF47BE2AFCCCD7FFDEDBBFF007A3FBF7FB7F63FD8E1632E4C6A3FB4DF6FE8C0D3245C3FDD5C74EDFDEF5FF638583EDFFF009C50FF008E9C5FDCFDA5FB5D7A8C0E4C1FA41E6DFEFB48FB3FDCF7FB3909399064D63FF1CF8BFBBE9FB3D320DC3E2C67CC5FF1D0B0FB7F647F77FC71671F8A62DFF28CEB5FEF67F74DFEF07F7BF4E167F37E67FE65FF00C746F7FE526E9FEEDFB5903CDBA3F178737D97FF008EDFFCF6EB8127E2952FF7FF00F4B5EBDFAE2D72F8BD3FCA9F6D7FE526E9FF001EFF006708607E2FA3EDBFDE1D1FFE523E8BFDFF004FF65930DD1E5D5F5BF977FDE0D27FDEFF00B2BFDD7DAFA72054A7FE6EFF007960FF007B3A8EBF47DAC816324FB4DFF8E7D97FC733A0FB5FDEF4EF902C1F62796FFE51ED07ECFF00C73AD7ECFD9FEE57A7B674DA7FEEE3EE1F73C7EABFBD9FF58FDE9D65AD0EC55D8ABB1569BA62AB315762AEC55D8ABB15762AEC55D8ABB15762AEC55D8ABB155CBDF155D8ABFFD9, '1', '2021-03-13 12:29:10', '1', '2021-03-13 12:29:10', b'0'); | |
| 150 | +INSERT INTO `inf_file` VALUES ('add5ec1891a7d97d2cc1d60847e16294.jpg', NULL, 0xFFD8FFE000104A46494600010101006400640000FFDB00840006040506050406060506070706080A100A0A09090A140E0F0C1017141818171416161A1D251F1A1B231C1616202C20232627292A29191F2D302D283025282928010707070A080A130A0A13281A161A2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828FFC200110800EC00EC03012200021101031101FFC4001C0001000105010100000000000000000000000701020405060308FFDA0008010100000000FA14000707A3E3F659B5F2B3CB1B171B57BAFA2C000BE1ED8731AFC752DF3F2F2EF249DF79D0000A465CD59E5ACA5BE7B09BB71441BBB943DC0017447C95DB0D662E4FD0192237F3E17E8600039B8AF556E579647D0B60807A3DA49368003D7E7AC1A5B678CA3255081A4AEBC0007AFCF5834C6E8BA3DDF756902CE39200034908796C661E82C04073E003D755A4B2FC9F48A34CFA172404152C6F415C4E175B9BB879F8EAB41CEF792A807CF1B1903B7BADA5DC0737D47ADCB3CFCBCE3FD2CCBDB807CE35CAF5E8B3F0757D0FBF1BE7DD655B65965BF384B9397B80F9C6A2EDAF577F05A0F3D9499958BACF4E7F41D0654E168118657B59A8E2E43DD71B31FC73ED26EE33FB0E939E8FB135FEFBA9528007159783CAD981B8C5DDC9596A5231D6E17173476200BE26D8351AFCEB33FBED077DABE2FCFB6DB59CE463365A018B156E4A3CB7DDBC65CDE3EDF71259C945B3D7900725CFFB554C0EA3B769E32BEC92F794A4711EEE3BDEBAC0380C657CB7FD6E50F3D4E266EE0C2E2F9DE73CA6FD90237B7CB7FDB5F554B6911C8BB8A948CB039CE5E79E94230B3B6E98A82945453C225BB87D44E3DA158BB77DF56A00A1452DE6A2DB397D6FD5C562696B215002DAE1E4D29CCF8471C9DBF4E9EB03CF952A028A539DE8296E1C7DD3C2DABFA80BA3690760A5C28514A626A37F4A523DC2D7E9A772B1CC9B7733B2DA2828A2DB628EAFABA523DD1D29302BC6767EE88E46DB1414A29486A1A9CF8E99E2BD91303CF98ECE8897A9EC5428A294A70B04E5E279751D613038FECBD5485BA5911428A514530780D5E9B7B62930B93ED2D6A222EA64A28A514514ADB444FE56D7FFC4001B01000105010100000000000000000000000001020304050607FFDA0008010210000000F3600075E70AD558EA46005B9DCE33E1B7367000694AC32CD3CC00034E58A1A69A59A125A512766681ACD81C9A194D4F44E2B280D94750E8BD1383E7AE6F60E4D500BB0F4BB3CCEBAC540E7500BD5CB7D135B33E0E769342FA6DDE51AAADE56CD084D7DE780014F9CBF8EFEA2E800346D4CDC87F57654104628C38ED9DE6B841064EB5978DEAAEB155104115A7FFC4001A010002030101000000000000000000000000040102030506FFDA0008010310000000EB00043FBC939A35CE4003A76164A7AFC9800061CB22B8CAC0006EDA78C4B0B845F5B8C23981D3533A3CA742D7F0DDCD006A346783E07D9FA05BCFF7A92016EAF9EE0FABE6CE8DD4901AE84ADC1E8F5BCDE0CEC483FA72520BD62DE8D54E46B8F500019F429A318AC001259BEAF3215C8009BC1B5F4C55D2B5924B9560D55CB5AD2490B4EB3FFFC400451000010302020604090A050403010000000102030400110512061321314151203261711014222330425281D11524333440537291A1B116436292C16382E1F0357383A2FFDA0008010100013F01F4FC6B4931051908C2222ACB706690B1EAA397BE9E719811335B2B68D800AF9C4D735CB6D6A56E48036229BC3DF575B223BCDE861A84ED71E3F95AB53091D672FF00EEA270F1EA66FCE8C8863AB187F68A335A1D58C8FD2953F932DD2A628FF2DAFEDA3249FE5B5FD94F2D9BF94D3415FD236D4518C6ABE66D627A9E194AADF6047585622C3B866352E54C49533297E43C3684F61E54E105ACC946B788038D09B2E495063220A77802EAFD696EBF721C79DBF2BDABBC93DFD0346AF750424152D5B9291727DD587E8ACA9090B9EE888DFDDA76AFE02B0FC270FC3BEAB1939FEF17E52BF3AD62A9195C405B4B4B883B8A4DFEC0B4A1D694D3C80B6D42C526B12C39583FCE226672028F96DEF2DF68ECAC4594AB24A64EC3B7327876D22521D4E5988CDFEA246DA7A294A758C9D635CC784D1AC330F918A492D46B04A7E91D3B91F13D9585619170A6B2C74E678F59E575954493BFC3F25BB11CD6E1129C8CBF66FE49A8BA55221B89631D8BB0EC0F37C6A2BEC4C643B0DD4BAD9E47D38B1052B014856C20D488BF26622B84ADB11F1999278734D3CD969D520F034CBCB65576D56A25895BECCBDCFD534F34B65595C16FF346A0C473109CDC465594A815297ECA79D448CCC18A88D15395B4FEA799E88A75B43AD943A90B41DE0D3C1FD1F9499905CB344D8A4EEEEA0A0E34DB805B3A42BD3E92C432F075A9B1E7E3F9D47BAA7283A961F4FF00311E14485253915671BF655FE3952D0082B64E64F11C53DF583CD187630C495FD09F34E7603C6962C766E3D1462130904EA7F0E534EBB20A9A8F159D74B705F28DC91CCD46D185BEE25EC6A46B2DB7528DD4A3BAC2C06C03D3B7B4E53B8EC34B4EA98531F72FAD1E1357208209046E229C3981CC37EF238FBAB43315D73670D92BBBAD8BB2A3EB2797BBA3872017F32BAA819AB4559221393DC1E7652AE3B10370FB0B5D7152961C7A42C6E72438A1DD7B785C7128EB1DBC071350B02C5268CC969319BF69EDE7DD4DE8728FD63123DCDA2B0DD178906735284A7DC5B66E01B513737E824EAF0A98E0DE465FFBF9D436F5387C4680B656923F4FB0E3738C083E6B6CA7CEAD94F6F3F752C25166D06E940CA0F3EDF061B02462920B516C9423E91E56E4F6769AC2F0785860BB2DEB1FE2F39B5468A89DFD37ADF213DF8BFC8A1B59688DD907A20835227C18BF5898CA7B335E97A478524D92EBAE7E06C9AFE228C7A90A72FF00F9D0C7D9E302701FFAFF00E6938EE1C4F9C53CCF6BAD102A563586C58FAE3290E83D54B67328FBAA6CE7A64A54A90323A5391B6FEE91F13E04A1C79C6D9605DD7559103B6A2456A0446E2C74D9081B7B4F3F43193AF852637150B8AD1E942760CC281BB8D8D5AC768E9EFA992A3416F3CC7D0D0E00EF34EE9148977460F12C3EF9EDD4A87325FF00E47107560FA8DEC151F0B84C7518413CD5B68009EA803BA8D1A34F448EEAB32DA467F680B1FCEA4E1EA4254A64A9CE394EFF00CE92A0ADDDC472AD098E97A5CA9AB17D479A6FBCEF3E899714D38168DE2A338E33255270D7FC5DF59BADA5F51CA87A48CA9496B1465511FF006BD4FCE9BCAEA02D95A5C41E2937ACA7978522FDD5896901538A8B82A75AE8EB3C7AA9A630B0A735F3DC5497CEF2ADD49B0161B055EAF57A2689A346A7E30F78C29A8694D926C5445EF4EC85BEE053A84B723DB1D55F61F8D684048C04A13F4A1D59713C526FC7DDE910FAC2321F2DBF655B4530A61B5E646BE3AB9B2E5A9B9F3B66A31726DC1D40AF95B1C693B04391EEB1A5693632D9F3986B67B81A7B1BC5719CD0C3488ADFF3549BEEE550E3B71590DB42C07EB4FBEDC76CB8F2825239D3FA43B7E6D1CA87359B531A4842AD263651CD06A3496A4B41C616149357ABD5E8D71AC230886BD1790EBA91AE5A5C257C516BD4194BF18650B480D1010A17BDCF3A8921FC2DC131ABAB56723E8FBC4703DF51A435362B72A32B332E0B8F4C1453D524771A87AE7DD09D6B9946D3B69B425B4E540B0A52821254A3648173498F37185AA42595AA3A4F93C8549929662BEEB2DA17A95645051B1BD61AFB38A5D011AB780BE5E75143D86C82E37D5F593ED0A8B25B94C875955D06AF4FBEDB29CCF3894279A8D7CA71782D44730DAAD45F4C98EE884F20B994E5B1DC6B0275E6D9721C8D6A2DD64AB7281A7F4714257993E689BDEFBAA4C96A2CA4B6F1F36F2721ECEDAD1F9FF22622B8EF9F993EADBFD0AE7DD446E236A4EE3E813A1E7F9B89AFFDA9A4E8743DBAD99257EFB57F0861BF7D27FBA95A1F879EA48909F7D3FA21251730A685F24AC5AA6C79B86AAD3D8213EDA77560907E6F9DCD855B4FC29C8A35B649B0B5EB1D516F0C91CFABFAD600DB4CE09112DDB206C7FCD692869CD209EB67E8CBA77568FB4E7CA91CB69E3B7BAB1646469C58DA40D82B0D8A21450D5EEAEB28F6D3697E63C598405D3F48EABAADFC4F6541C1A24539D49D7C8E2EBBB4FBB97827E0F0A68BADA0DBBC1D6FC950AC423488032CE0A7A2DFC99281B53F8870EFAF9D048CA10F208D8A0ACA4D3985096A0E4C514AF8250760158AC44B91EE91D5163DD5A218C2B38C3272AE7F92BE7D94458FA4D2D7C96A261C9EB495E65F6213B6A23EDA59B28D88A724A8BAA523603B2B1B19B0C7EE6DB2FF00AD6172DF430194B8AD572A95804790F1790ACAA3BC54382C4004A7CA739D7D6E50B6D69A55D479AB9504392A408B1CD96A175AFEED3CFBF95458EDC5610CB09CADA770E8101408201076106BE4A5AF5AF610E06D21C524B0EED4120F03C29D5CA8DF5D82FB607AEDF9C4FE95F29C4E2EE5FC4922B15318A82E3C8403BD3955B8D68962C716C36CF2AF299D8AED1CFD1A05D429D77C771D9926F76DAF30DFBB7F87158AA99096CA1412A36DF50E0C88EC84A9E43847022DFAD66929D81827B42C56A243E7CFA836DF241BA8FBE965319901A6EFEAA109F589DC2B0F8660425EE724AAEB591EB2B977560788B8E4F991E5150505272E6EEF04B9CC450758BF2BD91BE9CD296028846AFF00326938FC89CBD461CC05BCA1D6DB64F693585C4F1282DB19B3A86D52B9A8ED27C0A48575803DE2B178EC2602C865B06E3720560EB30B4B5ACBB1124588A56C51F4531EF168129FE2DB648EFAC211920357DEBF2CFBFA4EB8865B52DD504A06F26B0684B71D4CD94828007996D5BC7F51EDF0694C53E3D16446506DE5052566DB176B581AF18C4BAB9503B75A6D5E28EBC6F31DCC9FBB46C1EFA4B6848B252903BAB01467C656B4F55A672ABBC9D9FB7431F72CDB4D73398D48F271BC29437EB2DFA8A77AE7D1697B9ABD1E75237BAA08A69391B4279003A3E3216EEA6321521EF61BDB6EF3C2B0DC1D7AE4C9C4B22DC4ED6D94ED4B7DBDA7C38AC3F1D8A50956475273215C8FC29D73C5DCD5CB4961CE4BDC7B8F1AD6200B95A2DCEF4D2D7255921365F5F31D51DE6B08802045C8559DD51CCE2FDA3D0C614A54F702BD5D83BAB16D6F8E40547FA54AC94F7D31A4B8CC43F3D8A1F6C6F396C7F31585E9161D889080B2C3E7D473E34A491DDE834CCDD186B1EDBD7E83257254530DB2F91BC8D891DEAA6B020ED8CF794E7FA4DF928F89A8EC351DB0DB0DA1B40F5522DD15A12E272B894A93C942E2BE4A819F3F8947CDCF2548C4998A754CB79B2F04EC02B0F9C8999C25252A45B37BFA12633525367537E4788A9FA3C1E19DA78EB1BBA9A047ADC298703CD058D97DE391E22B11C2DB76EB69365F2158563B3B082942FE710C6F4AB78EEAC33108B8AC7D7435DFDA41DE9E9E949CD8E618DFB282AB781D73265012A5AD46C94245CA8F6544C194F0CF89916E11D0760FC478FED484A5080942425237002C07A03B8D390714724A9866304241B78C38AF27BC0E3585406F0F881A412B513996E1DEB573E963917C46619681F357CD9DFE85FB5DC68D6251B617503F10A61C7F0F9224C0564706F1C0D6038DC7C659B27CDCA48F2DB3FB8ECE96909CDA551C704B17FDE9E73568D892B59395284EF51E558461BE2BE7E45973163CA57040F653FF0076FD89E6D0F34B6DD48536B16524F115219561B24457AFA93B1874FAC3D93DB46A5B3A978A7D5DE2BCE30FA64C45143E837045617A510254342E63896246E5A4F3ECE871AC636E972FFA628FDEB47E3090F2A7AF6A5376D81FBABFC7D931D85F2843530DB812F6F483B95DE3FCD403250B5C793B54DEC39B62D3D879F7D62A9F36857236F03B192B5E6B91D01BEB1F51563D2D2D6C756866324F6AAFF1A8ECA23B0DB2D0B21B4848FB03EFA59C80DCAD672A1237A8F431E694A845F66FAE8FE705B88E229C8AC6330997C9C8FDBC9751BD3F11D958C3722235AB9A80368CAEA7A8BF81ECE937D7AC3E5993A62DBE47997242F2F7E5B0FB0E1C7C664489AADC545A67B1037FE67A12D84C98EB6944A42B883623B6B47642E015E1F33638855BE07B8D62AB4BF065B4B48394849078D4A82B601531771AF638A7BB9D254142E9DA3A093635A4C9660CEC15D650869297944D85870BD4394DCC6CB8C056AAF64AC8B05768ECF4F2DCD54579C1BD0852BF4AC0148560B0CB7BB563F3E3D1D286836F44969EB156A17DA0EEFD69C92A5EB2FEBA520F78F03F874779C2B505051DF9556E86C00A9442509DA49E1410349E787568B6131890DF02F2B9F750000000B01E0C6F11F1169B4A7E95D5048ECEDAC35F5488895B9D6DA0FA4202810ADA0EC35A3B3061932561524D834B3AB27D9E0698D21C35F9DE2AD48BBB7CA0DBC951E40F434CDDB4161A1D653A93D2DE6B1B5AF11949C1A2A8A507CA94E0E09F6477D30CB71D9432CA421B40CA948E1E1D28CCAC563A6FB3589FDAF58565F106B29BECDBDFE974FE225296662363AA56AC914127665D878562DA4AE468AD371D00CB50DAB56D48EDA81A5188C792954B73C618BF969290081D94FE2D15A4660E67BEEB7C6B1A7D53A6C7521C4E6CF982378B749D7931A3BD217D56D2555A2ED2BC4972DEFA696B2E13D9C3A1A48ABCC2AF61D47C2B00776BAD1FC43D2E99435CBC1EED0BAD956B2DD9C6A1B49912DA6F7A6F750ECA95152C4D534D9590948B85F0A71BF20D3816E93AE56729397B36568FA72B4FEC1B17BFDDD2D2E51F929A8E8DF25E4B74CB619690DA3AA80123C24D85CEE158A2F5ADBCBE6ACDFF00EAB0755B104F6823D32224742CAD11D94ACF108158E600FC99CB950D6DDDCB67439B368E37A8BA332B5A15296C8037041BFBEBF86B1242F54DA1A5206E74AF7FBABC45386B288F9B3BBD65ABB4F4B1FDB3B0349DC645FA18B2CA2039978ECAC4C5E0BBB48D97D9583FD7D9FF00BC3EC4EAD4E3AA5ACDD4A37E87FFC40029100100010303030402030101000000000001110021314151611071812091A1B130C1D1E1F0F140FFDA0008010100013F10FCE0B032D36BD74B69C8F6137A56A00C92E87777A82C152709D05827769D2D7B9F63F9A46C9B00FBB58E2CDE53EC547DD0BE1FDB41C2099E4F34E5A46241F4524D856723E655FC52A491A36144F10CD40C4961A5C0BFF8103389AB0DE8E169ED3DA981205B99E90B6F348CE90978FE14B61C15F4914ABF205FBE8D3D0A94B3D0E3F81769770BD97397E02682183E43CB1E2294727B5459448C1F25223088FE782179644A756382AA57C9FE734AE1D0F0A701E6841A16200E50FD7B5470BDCBE9DFD05429E19C96DC9A7BA2ACC52D45BBE8705A90953D65F35AD3FEB99A590583939461F10F15A997264EE64F3F9E17411A4473255DDB9BE3538D3C6F578EB43B9A352612C991EE56128D1979DAA72568E4EC7A0FF3C04C5986AE87347DCF7BCBAC9AAEAFA548290C636AC7B65C8D3BACB3232B35930F151AC466D20FEFF3CC91C50966E4F24FB563C096344FFBF1D1A6C29CB58E56553F01BAFC6DB92D589215DF9F161A0910DE1309E91A4CDA29F33334A674442CB680DA8854C0743C2EDBC7BD0282100D0FCE0719341D4A99198FBCDA64FBA69E8D4598425190EC4041DDA3C5B82951A60E51DDAA3E3B52228E4F427CED4F18FF0071508A74B9647DCD673FF804D4A38B5059803F0D34D47E461BF60AD946AA06E1BD452466E708EEBFAAB2938A163582A6777A1B4417B4508404404130FF00C248277E82EF8177C57B207167C957A5BF1A24CFA0F83DE8309CD60E1D0E0A6EEF1EB825B909A199214F63F0E714B2520DDA2A51BE056E096A589D2F5DAD52A33D0919F769379F44C1EF4895CE0F71A1286E36727605FDE2A69EC7647BB3F36C7484A6721AF6095ED45888919D64EAADFF0009631BDD8FE42B946162DDFC47AC15012D0471C8F60CBE29652EC840344313C2BDAA752C85BC7FE2A0133A73F9A3E01C400FAA4EEFBD2A6A65A33146488BB82F4E5E9BB25DB53DE895948C8215B268D0F0109B429EF441E5A5555CBF85500DEC3C539B9C696FD37699342E895CEC3DCE6A18E24207DA93D5489913A5DE017574A0FEA8371DB47BE3BD33BB64287CE7EB8A02103004051428F502ABA8AEEEBD8331B14A11200C06CEFB7BA928269B796034B0FC8B944B27FF000F15DE8A43C9AD4025299D5E162529513F4BBBD642AC6592CDB6B71A6A54C1026E2FD19AD4FE9D56EF35A945965D8DDA621FED4050052E66C78735ACD18C8EC9A3D0D1A2A1B3BD4F34B5BCA10E9114E98389E470D4FF00E47B1F402D3C157ADC184DC4D11B27E1289EBDE9A95790A51C2F23B1B79A020917BAE5776A73848E819A57822058E3777A12C6DA4081E68776F0320E1A9FA41C6193BECD61BD6E422644D1A69C1E605290A6D53DD14EC582B905A4C9521224524A4A6F522922B4B3B54EE99E4108D4C94B898ECD23C8CFF544090121713F036F227061F745B95C4087DD76EDA1FC5679B4929F45446DA8786A51E4BC5E51984B7EF8A8C0B0846F3A781F3489EBB4DE198A24320B3903483C8D4C2A4A7CCD00A4B060A597DE69CE00BDD97A839230195B07BA5199B32B5613E2D1531C9029E2FD2F31449AC5B8EF06C780A2C416363151862F7BB7933D9A8BE618658C8659D948A121381A28DBD9A3492C7BB254BBC94292C0466187B944DCA59CBFE7DA9A24FC6498A56248B9542F760F7A04762C3ACBA525502026850E3C127540D1A046D2B454B42CA124F6A72E4227A1C5044809A72C1ECDDF053088498961EF606AF050512C64795755CAEAFA0CB140488E89519BAED8014AE5DB0516E50404E66EA9146859863DCA9B889A276AD83EA92922BEBA4BDA1FEFF1C1A9B1F9B6190EEFDF54A42870B38AB1438400D8FE454E3B10FF009A619765E0DAC03C5EA43892D4AA039573DDAB53A1BA0B0E1603F9ACD1C4C659243819B76A8767DAA22904D9477D03BD04082D2C5F21158C6E001E16580F35255E63C4C7DC3429853B50503EC0FDD36AD1A0667B50146DEACA90E39068C0E7F143BF432DF3152AC253754FA8CA2CAA029E88A344D93D1160C837BBD0AE5E121981C4B0E4A5489372FB2682C64C80ABCB2D1328C00A7B21306CD1EC9A8EA60376EC163E569366ABDE4FE684245BF12BC8C9E32CFEAA0500B670450FA1405C987F403BD4CF6F28F7AFBF834EB02C03C9074791669747BC1CD8C5A934A7A8C5065C622F3E1838A8357E2C4BB1B183D02C2003EC99F32D12B97A62200EB6D2B548D8B7163DCADBD6912F183F15756FB8FC196AC3F11FCD4CAF49A52B50B97C96F04B436BFA8F698F913B560E6C31FDFA1A6AD7922786818BB16A38A686076D63E8AC2D08B30CA09EC4F9A9A7A427A30DBB0D1B85CC0C6C5DA684C5487C95939192811733667B73C52E6B2309E593B62AF84360DB27EF1587D473B2270C3367E28A13D80DA61FBC1AD1449DD2ED0DDF047750AEB8181B018EB1D5E854861441DA94CA50A46CAEAB513914657E8D0F531347030F63D80F3DE8503721835E7F9A70B66248EA27EA99413CACFD9F4A4461B3E832536A2B57F4A8242424AF03BFC6684A9180FE006EE5758F5C7E078E92244C952415959E636E392F4444491B2351565ECD31F559963FDE6823CB5310D61A3E830A3321D4BA4FF4AB81AD22063BEA2381DFF2BE99A5A88064DF8934DB21BEF460EA448D0346C1868D8B794F09FD534A825C81AFA04939A2ED9229CC98E0978A80023E023FBE87E6C7179CC09B6C05D5B06685426CEA4D34B57E252E63F717EE14980B7EB539375AF53A83177B1BF23C539F4991382F4C8109B710E07708F7A3F24F468BB065A6BC089D4C23915EC14B4BD09FCC0E497026CC51B17AB362717045F4A0BA5FB81446803032E45CB4719A26D2613D17789DCA38AC640334FB538887699CCB7744DA7D735353D66A7A26084F09FD531AADD9CC899795A9E93D0F68921BA65DBED404EAF3B86FF153577EECA2BBC1AFA08B60AD002EAB57153A1D4B7060B7EDB1320400401B1D00ACA5ED58A4175E8081873E89A9A9A9E934B53445424371CD397D2CD97B1113FF006A2D52499E215FBD3ACD030B14386DFBA72F7A9F4020196808A198B6F7247C73446CF80054F4BB44F801C3EEA232129773254FA96A6A6A6A6A66A5FC86214473E2D5282BA08B43367DEA76ABB504090CAB36C77A304BC800B021331AD16000384BE2EB1506C402D1D5E0BDF55A5BFA6226592C4C18FD79A8F63DE31876CBE881F77D885F74D25600BE1FD749A9A5A9E9353D27AAA8B716502437B53C09D614035DAA5D36095370388A8ACDD20E56C548A3D3591858C050002B042E9063B7AAEFA600C4998F8286E0083608A9A9A1658095E299DC9DDD894C4D1AF69FD7A67A4D4F49F464C0062F98A8FF00F9260110139B5A9AB865D03BAD74D0A5F6D3000272E5344461216360FF006DD1A9EB6AA660E40FE6B4A73D1254306F0B0D43C7824E1B25081958925CB7F57AADFA36E8DBD0E29E8B03DAA75809A7AFFFC40032110002010204030506070100000000000001020300110412213105104113202232510630618191B11433425271A1C1F0FFDA0008010201013F00EFA005803589B860B6D0578F61615918EED5D98EA4D1897D2961075B576D147A5C9FE2BF0F14BF966C7D0D4913466CC3BF0CFA76726DF6AC96391BA55CAEF57BED4C5635CEF52CEF2EFB7A72EC581006F5886CB1647376F711B67456EA34E445B514F1F6C997A8DB996CAAEE3A69EE625C91853B9D68024D853CF147A6E6BF1A4795451249B9E4CA5A3751BEFDC8E1793CA2861E34FCC6B9F8566897CA9F5AED97F60FA50923BDCA0A570FE2AC4C9D9A651B9FB7776371BD3AA3EAEBAFA8A38788ECD6FE452E16306E5AE29E62DE14D052F02E20D1F682136FEFE9BD302A72B6879E178370F970CA81487B0F17C48F4DADFE5710C2BA390775D0FCBBA401BB0AF07EE1F5A319B5C548731D36AF66E34476C532E62B602FEA7AFF005A50C5BE6BB6D5ED7080CC9227988D7E3E9586C0BCE331D05370A005D5B5AC171AEC90418AF091B1E84563F1D1CF8C695366FF0005AB130766732EC7BD86F046D27CAAE6B807168B02AE92FEAB7CAD53F1BC3019CBDCFC2833F13C419A4F28FF00AD4B6B694580DEA444986561714FC2A33E52453E0F247949B8A6194907BAFE18517E7CF0D8379CFA0F5A862585422D30B1AD0520B0D79620D909352E14C84BC66F7A65286CC2DDCC568557D0561F0B24E7C234F5A8387C716ADA9AB5B99B2EC285EDAF2750E0A9A9E37C2CA452BA4EB6715340D11D76E428C071188C9D2911635CAA34F738BC3ACE963B8A40639321A5716B30B8E51AE670B58440333F527DC13D2AF47515361927B31D0D4913466CDCA30C5C64DEB0CAC175A26DDEBD1366157E43734543687970CC383E33D7EDC9F6A1CAF57ABF26B11AD48E249028DA9E219C2AD5AC4D5F9605408C7CB93EC6877C69A8ACCD7CD7D79FFFC4002E110002010302040503040300000000000001020300041121311012204105223251811330610614A1F02391C1FFDA0008010301013F00EB3B559AA88F2A7535A5647B566813524EB1FA8D3DC17D947CD3C3247B8C8A041EB5668CF325238750EBDEB7E13CE53C89BD63B9DF81914024ED52B2BBE536FB168D82C9F3C6EA3E53F507CF1BB3A2A7BFD9B55CB17F8A670832D4F72EDE8D053176F5371BC182ADD04814B148FB0C50B53DDA85A2FB9A365ECD4A81005153BF3BE3B0E9650C30C29AD31E83FEE8C128ED9AFA721D31515BAC63277A6F17B20FF4CCA33FDEFB50C30C8A0280AB8F16BF8AE1A4E60533A2FE33EFEF48EAEA255F4B6A3E7A4DDA7615FBB5F6349728DA6683A8D0D78FB3CAAB6A8DCA18124FE0634FE75A6B28D63F2EFEF5FA4DE6681D24F483A7FDA9EF121381A9A5F12D7CCBA55F7833BC867B3F306DC7706BC3EC1E1B25824DC7F1939AD54F2B6FD5127D4900A118049AFD41E152DF9478775CFCE6A0F04BD7C465703F34B1A786DB8863DCFF00734C083AD04276A46784E41C1A5F1271B8CD49389FCD8C11435E9B25C966E33DD2423DCD4B2B4ADCCD564904CBCB268C28086DD73A0AB997EB4A5C708572401455A3D185039DBA2C47F8F353DC2423CDBD4D7D249A0D07428E6DCD1C674E0AECA722A1916E2306A7B4C6A9BD03D8EFC6398416C1BBD3B973CCDBFD9B59DA17C8DAB21D79854F6E24D46FC09C0CD5C31C2A7B0FB0077E0A706A19DE1381A8A8E5590647038C6B5211A0EF4064E051D3A95720D6382EAA281236E12363845EAA61AF1C56382120E94A9CA849DE94F9799AB3A0C7190F08877A7DFAF359D318E3FFD9, '', '2021-01-13 17:15:36', '', '2021-03-12 20:30:44', b'1'); | |
| 151 | +COMMIT; | |
| 152 | + | |
| 153 | +-- ---------------------------- | |
| 128 | 154 | -- Table structure for inf_job |
| 129 | 155 | -- ---------------------------- |
| 130 | 156 | DROP TABLE IF EXISTS `inf_job`; |
| ... | ... | @@ -175,7 +201,7 @@ CREATE TABLE `inf_job_log` ( |
| 175 | 201 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 176 | 202 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 177 | 203 | PRIMARY KEY (`id`) USING BTREE |
| 178 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; | |
| 204 | +) ENGINE=InnoDB AUTO_INCREMENT=2322 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; | |
| 179 | 205 | |
| 180 | 206 | -- ---------------------------- |
| 181 | 207 | -- Records of inf_job_log |
| ... | ... | @@ -209,7 +235,7 @@ CREATE TABLE `sys_dept` ( |
| 209 | 235 | -- ---------------------------- |
| 210 | 236 | BEGIN; |
| 211 | 237 | INSERT INTO `sys_dept` VALUES (100, 'èŠ‹é“æºç ', 0, 0, 'è‹¥ä¾', '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-01-06 19:45:52', b'0'); |
| 212 | -INSERT INTO `sys_dept` VALUES (101, '深圳总公å¸', 100, 1, 'è‹¥ä¾', '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-01-05 17:03:47', b'0'); | |
| 238 | +INSERT INTO `sys_dept` VALUES (101, '深圳总公å¸', 100, 1, 'è‹¥ä¾', '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2021-03-14 20:26:18', b'0'); | |
| 213 | 239 | INSERT INTO `sys_dept` VALUES (102, '长沙分公å¸', 100, 2, 'è‹¥ä¾', '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-01-05 17:03:47', b'0'); |
| 214 | 240 | INSERT INTO `sys_dept` VALUES (103, 'ç ”å‘部门', 101, 1, 'è‹¥ä¾', '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-01-05 17:03:47', b'0'); |
| 215 | 241 | INSERT INTO `sys_dept` VALUES (104, '市场部门', 101, 2, 'è‹¥ä¾', '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-01-05 17:03:47', b'0'); |
| ... | ... | @@ -238,7 +264,7 @@ CREATE TABLE `sys_dict_data` ( |
| 238 | 264 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 239 | 265 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 240 | 266 | PRIMARY KEY (`id`) USING BTREE |
| 241 | -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COMMENT='å—典数æ®è¡¨'; | |
| 267 | +) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8mb4 COMMENT='å—典数æ®è¡¨'; | |
| 242 | 268 | |
| 243 | 269 | -- ---------------------------- |
| 244 | 270 | -- Records of sys_dict_data |
| ... | ... | @@ -298,6 +324,19 @@ INSERT INTO `sys_dict_data` VALUES (61, 2, '管ç†å‘˜', '2', 'user_type', 0, NUL |
| 298 | 324 | INSERT INTO `sys_dict_data` VALUES (62, 0, '未处ç†', '0', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:19', '', '2021-02-26 08:11:23', b'0'); |
| 299 | 325 | INSERT INTO `sys_dict_data` VALUES (63, 1, '已处ç†', '1', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:26', '', '2021-02-26 08:11:29', b'0'); |
| 300 | 326 | INSERT INTO `sys_dict_data` VALUES (64, 2, '已忽略', '2', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:34', '', '2021-02-26 08:11:34', b'0'); |
| 327 | +INSERT INTO `sys_dict_data` VALUES (65, 1, '云片', 'YUN_PIAN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:14', '1', '2021-04-13 00:20:13', b'0'); | |
| 328 | +INSERT INTO `sys_dict_data` VALUES (66, 2, '阿里云', 'ALIYUN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:26', '1', '2021-04-13 00:20:16', b'0'); | |
| 329 | +INSERT INTO `sys_dict_data` VALUES (67, 1, '验è¯ç ', '1', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:57', '1', '2021-04-05 21:50:57', b'0'); | |
| 330 | +INSERT INTO `sys_dict_data` VALUES (68, 2, '通知', '2', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:51:08', '1', '2021-04-05 21:51:08', b'0'); | |
| 331 | +INSERT INTO `sys_dict_data` VALUES (69, 0, 'è¥é”€', '3', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:51:15', '1', '2021-04-05 21:51:15', b'0'); | |
| 332 | +INSERT INTO `sys_dict_data` VALUES (70, 0, 'åˆå§‹åŒ–', '0', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:33', '1', '2021-04-11 09:30:18', b'0'); | |
| 333 | +INSERT INTO `sys_dict_data` VALUES (71, 1, 'å‘逿ˆåŠŸ', '10', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:43', '1', '2021-04-11 09:30:20', b'0'); | |
| 334 | +INSERT INTO `sys_dict_data` VALUES (72, 2, 'å‘é€å¤±è´¥', '20', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:49', '1', '2021-04-11 09:30:22', b'0'); | |
| 335 | +INSERT INTO `sys_dict_data` VALUES (73, 3, 'ä¸å‘é€', '30', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:19:44', '1', '2021-04-11 09:30:23', b'0'); | |
| 336 | +INSERT INTO `sys_dict_data` VALUES (74, 0, 'ç‰å¾…结果', '0', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:43', '1', '2021-04-17 23:05:19', b'0'); | |
| 337 | +INSERT INTO `sys_dict_data` VALUES (75, 1, '接收æˆåŠŸ', '10', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:29:25', '1', '2021-04-11 20:29:35', b'0'); | |
| 338 | +INSERT INTO `sys_dict_data` VALUES (76, 2, '接收失败', '20', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:29:31', '1', '2021-04-11 20:29:39', b'0'); | |
| 339 | +INSERT INTO `sys_dict_data` VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-13 00:20:37', '1', '2021-04-13 00:20:37', b'0'); | |
| 301 | 340 | COMMIT; |
| 302 | 341 | |
| 303 | 342 | -- ---------------------------- |
| ... | ... | @@ -317,7 +356,7 @@ CREATE TABLE `sys_dict_type` ( |
| 317 | 356 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 318 | 357 | PRIMARY KEY (`id`) USING BTREE, |
| 319 | 358 | UNIQUE KEY `dict_type` (`type`) |
| 320 | -) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb4 COMMENT='å—典类型表'; | |
| 359 | +) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8mb4 COMMENT='å—典类型表'; | |
| 321 | 360 | |
| 322 | 361 | -- ---------------------------- |
| 323 | 362 | -- Records of sys_dict_type |
| ... | ... | @@ -340,28 +379,10 @@ INSERT INTO `sys_dict_type` VALUES (107, '定时任务状æ€', 'inf_job_status', |
| 340 | 379 | INSERT INTO `sys_dict_type` VALUES (108, '定时任务日志状æ€', 'inf_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2021-02-08 10:03:51', b'0'); |
| 341 | 380 | INSERT INTO `sys_dict_type` VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', b'0'); |
| 342 | 381 | INSERT INTO `sys_dict_type` VALUES (110, 'API 异常数æ®çš„处ç†çжæ€', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2021-02-26 07:07:01', b'0'); |
| 343 | -COMMIT; | |
| 344 | - | |
| 345 | --- ---------------------------- | |
| 346 | --- Table structure for sys_file | |
| 347 | --- ---------------------------- | |
| 348 | -DROP TABLE IF EXISTS `sys_file`; | |
| 349 | -CREATE TABLE `sys_file` ( | |
| 350 | - `id` varchar(188) NOT NULL COMMENT '文件路径', | |
| 351 | - `content` blob NOT NULL COMMENT '文件内容', | |
| 352 | - `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 353 | - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 354 | - `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 355 | - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 356 | - `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 357 | - PRIMARY KEY (`id`) USING BTREE | |
| 358 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件表\n'; | |
| 359 | - | |
| 360 | --- ---------------------------- | |
| 361 | --- Records of sys_file | |
| 362 | --- ---------------------------- | |
| 363 | -BEGIN; | |
| 364 | -INSERT INTO `sys_file` VALUES ('add5ec1891a7d97d2cc1d60847e16294.jpg', 0xFFD8FFE000104A46494600010101006400640000FFDB00840006040506050406060506070706080A100A0A09090A140E0F0C1017141818171416161A1D251F1A1B231C1616202C20232627292A29191F2D302D283025282928010707070A080A130A0A13281A161A2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828FFC200110800EC00EC03012200021101031101FFC4001C0001000105010100000000000000000000000701020405060308FFDA0008010100000000FA14000707A3E3F659B5F2B3CB1B171B57BAFA2C000BE1ED8731AFC752DF3F2F2EF249DF79D0000A465CD59E5ACA5BE7B09BB71441BBB943DC0017447C95DB0D662E4FD0192237F3E17E8600039B8AF556E579647D0B60807A3DA49368003D7E7AC1A5B678CA3255081A4AEBC0007AFCF5834C6E8BA3DDF756902CE39200034908796C661E82C04073E003D755A4B2FC9F48A34CFA172404152C6F415C4E175B9BB879F8EAB41CEF792A807CF1B1903B7BADA5DC0737D47ADCB3CFCBCE3FD2CCBDB807CE35CAF5E8B3F0757D0FBF1BE7DD655B65965BF384B9397B80F9C6A2EDAF577F05A0F3D9499958BACF4E7F41D0654E168118657B59A8E2E43DD71B31FC73ED26EE33FB0E939E8FB135FEFBA9528007159783CAD981B8C5DDC9596A5231D6E17173476200BE26D8351AFCEB33FBED077DABE2FCFB6DB59CE463365A018B156E4A3CB7DDBC65CDE3EDF71259C945B3D7900725CFFB554C0EA3B769E32BEC92F794A4711EEE3BDEBAC0380C657CB7FD6E50F3D4E266EE0C2E2F9DE73CA6FD90237B7CB7FDB5F554B6911C8BB8A948CB039CE5E79E94230B3B6E98A82945453C225BB87D44E3DA158BB77DF56A00A1452DE6A2DB397D6FD5C562696B215002DAE1E4D29CCF8471C9DBF4E9EB03CF952A028A539DE8296E1C7DD3C2DABFA80BA3690760A5C28514A626A37F4A523DC2D7E9A772B1CC9B7733B2DA2828A2DB628EAFABA523DD1D29302BC6767EE88E46DB1414A29486A1A9CF8E99E2BD91303CF98ECE8897A9EC5428A294A70B04E5E279751D613038FECBD5485BA5911428A514530780D5E9B7B62930B93ED2D6A222EA64A28A514514ADB444FE56D7FFC4001B01000105010100000000000000000000000001020304050607FFDA0008010210000000F3600075E70AD558EA46005B9DCE33E1B7367000694AC32CD3CC00034E58A1A69A59A125A512766681ACD81C9A194D4F44E2B280D94750E8BD1383E7AE6F60E4D500BB0F4BB3CCEBAC540E7500BD5CB7D135B33E0E769342FA6DDE51AAADE56CD084D7DE780014F9CBF8EFEA2E800346D4CDC87F57654104628C38ED9DE6B841064EB5978DEAAEB155104115A7FFC4001A010002030101000000000000000000000000040102030506FFDA0008010310000000EB00043FBC939A35CE4003A76164A7AFC9800061CB22B8CAC0006EDA78C4B0B845F5B8C23981D3533A3CA742D7F0DDCD006A346783E07D9FA05BCFF7A92016EAF9EE0FABE6CE8DD4901AE84ADC1E8F5BCDE0CEC483FA72520BD62DE8D54E46B8F500019F429A318AC001259BEAF3215C8009BC1B5F4C55D2B5924B9560D55CB5AD2490B4EB3FFFC400451000010302020604090A050403010000000102030400110512061321314151203261711014222330425281D11524333440537291A1B116436292C16382E1F0357383A2FFDA0008010100013F01F4FC6B4931051908C2222ACB706690B1EAA397BE9E719811335B2B68D800AF9C4D735CB6D6A56E48036229BC3DF575B223BCDE861A84ED71E3F95AB53091D672FF00EEA270F1EA66FCE8C8863AB187F68A335A1D58C8FD2953F932DD2A628FF2DAFEDA3249FE5B5FD94F2D9BF94D3415FD236D4518C6ABE66D627A9E194AADF6047585622C3B866352E54C49533297E43C3684F61E54E105ACC946B788038D09B2E495063220A77802EAFD696EBF721C79DBF2BDABBC93DFD0346AF750424152D5B9291727DD587E8ACA9090B9EE888DFDDA76AFE02B0FC270FC3BEAB1939FEF17E52BF3AD62A9195C405B4B4B883B8A4DFEC0B4A1D694D3C80B6D42C526B12C39583FCE226672028F96DEF2DF68ECAC4594AB24A64EC3B7327876D22521D4E5988CDFEA246DA7A294A758C9D635CC784D1AC330F918A492D46B04A7E91D3B91F13D9585619170A6B2C74E678F59E575954493BFC3F25BB11CD6E1129C8CBF66FE49A8BA55221B89631D8BB0EC0F37C6A2BEC4C643B0DD4BAD9E47D38B1052B014856C20D488BF26622B84ADB11F1999278734D3CD969D520F034CBCB65576D56A25895BECCBDCFD534F34B65595C16FF346A0C473109CDC465594A815297ECA79D448CCC18A88D15395B4FEA799E88A75B43AD943A90B41DE0D3C1FD1F9499905CB344D8A4EEEEA0A0E34DB805B3A42BD3E92C432F075A9B1E7E3F9D47BAA7283A961F4FF00311E14485253915671BF655FE3952D0082B64E64F11C53DF583CD187630C495FD09F34E7603C6962C766E3D1462130904EA7F0E534EBB20A9A8F159D74B705F28DC91CCD46D185BEE25EC6A46B2DB7528DD4A3BAC2C06C03D3B7B4E53B8EC34B4EA98531F72FAD1E1357208209046E229C3981CC37EF238FBAB43315D73670D92BBBAD8BB2A3EB2797BBA3872017F32BAA819AB4559221393DC1E7652AE3B10370FB0B5D7152961C7A42C6E72438A1DD7B785C7128EB1DBC071350B02C5268CC969319BF69EDE7DD4DE8728FD63123DCDA2B0DD178906735284A7DC5B66E01B513737E824EAF0A98E0DE465FFBF9D436F5387C4680B656923F4FB0E3738C083E6B6CA7CEAD94F6F3F752C25166D06E940CA0F3EDF061B02462920B516C9423E91E56E4F6769AC2F0785860BB2DEB1FE2F39B5468A89DFD37ADF213DF8BFC8A1B59688DD907A20835227C18BF5898CA7B335E97A478524D92EBAE7E06C9AFE228C7A90A72FF00F9D0C7D9E302701FFAFF00E6938EE1C4F9C53CCF6BAD102A563586C58FAE3290E83D54B67328FBAA6CE7A64A54A90323A5391B6FEE91F13E04A1C79C6D9605DD7559103B6A2456A0446E2C74D9081B7B4F3F43193AF852637150B8AD1E942760CC281BB8D8D5AC768E9EFA992A3416F3CC7D0D0E00EF34EE9148977460F12C3EF9EDD4A87325FF00E47107560FA8DEC151F0B84C7518413CD5B68009EA803BA8D1A34F448EEAB32DA467F680B1FCEA4E1EA4254A64A9CE394EFF00CE92A0ADDDC472AD098E97A5CA9AB17D479A6FBCEF3E899714D38168DE2A338E33255270D7FC5DF59BADA5F51CA87A48CA9496B1465511FF006BD4FCE9BCAEA02D95A5C41E2937ACA7978522FDD5896901538A8B82A75AE8EB3C7AA9A630B0A735F3DC5497CEF2ADD49B0161B055EAF57A2689A346A7E30F78C29A8694D926C5445EF4EC85BEE053A84B723DB1D55F61F8D684048C04A13F4A1D59713C526FC7DDE910FAC2321F2DBF655B4530A61B5E646BE3AB9B2E5A9B9F3B66A31726DC1D40AF95B1C693B04391EEB1A5693632D9F3986B67B81A7B1BC5719CD0C3488ADFF3549BEEE550E3B71590DB42C07EB4FBEDC76CB8F2825239D3FA43B7E6D1CA87359B531A4842AD263651CD06A3496A4B41C616149357ABD5E8D71AC230886BD1790EBA91AE5A5C257C516BD4194BF18650B480D1010A17BDCF3A8921FC2DC131ABAB56723E8FBC4703DF51A435362B72A32B332E0B8F4C1453D524771A87AE7DD09D6B9946D3B69B425B4E540B0A52821254A3648173498F37185AA42595AA3A4F93C8549929662BEEB2DA17A95645051B1BD61AFB38A5D011AB780BE5E75143D86C82E37D5F593ED0A8B25B94C875955D06AF4FBEDB29CCF3894279A8D7CA71782D44730DAAD45F4C98EE884F20B994E5B1DC6B0275E6D9721C8D6A2DD64AB7281A7F4714257993E689BDEFBAA4C96A2CA4B6F1F36F2721ECEDAD1F9FF22622B8EF9F993EADBFD0AE7DD446E236A4EE3E813A1E7F9B89AFFDA9A4E8743DBAD99257EFB57F0861BF7D27FBA95A1F879EA48909F7D3FA21251730A685F24AC5AA6C79B86AAD3D8213EDA77560907E6F9DCD855B4FC29C8A35B649B0B5EB1D516F0C91CFABFAD600DB4CE09112DDB206C7FCD692869CD209EB67E8CBA77568FB4E7CA91CB69E3B7BAB1646469C58DA40D82B0D8A21450D5EEAEB28F6D3697E63C598405D3F48EABAADFC4F6541C1A24539D49D7C8E2EBBB4FBB97827E0F0A68BADA0DBBC1D6FC950AC423488032CE0A7A2DFC99281B53F8870EFAF9D048CA10F208D8A0ACA4D3985096A0E4C514AF8250760158AC44B91EE91D5163DD5A218C2B38C3272AE7F92BE7D94458FA4D2D7C96A261C9EB495E65F6213B6A23EDA59B28D88A724A8BAA523603B2B1B19B0C7EE6DB2FF00AD6172DF430194B8AD572A95804790F1790ACAA3BC54382C4004A7CA739D7D6E50B6D69A55D479AB9504392A408B1CD96A175AFEED3CFBF95458EDC5610CB09CADA770E8101408201076106BE4A5AF5AF610E06D21C524B0EED4120F03C29D5CA8DF5D82FB607AEDF9C4FE95F29C4E2EE5FC4922B15318A82E3C8403BD3955B8D68962C716C36CF2AF299D8AED1CFD1A05D429D77C771D9926F76DAF30DFBB7F87158AA99096CA1412A36DF50E0C88EC84A9E43847022DFAD66929D81827B42C56A243E7CFA836DF241BA8FBE965319901A6EFEAA109F589DC2B0F8660425EE724AAEB591EB2B977560788B8E4F991E5150505272E6EEF04B9CC450758BF2BD91BE9CD296028846AFF00326938FC89CBD461CC05BCA1D6DB64F693585C4F1282DB19B3A86D52B9A8ED27C0A48575803DE2B178EC2602C865B06E3720560EB30B4B5ACBB1124588A56C51F4531EF168129FE2DB648EFAC211920357DEBF2CFBFA4EB8865B52DD504A06F26B0684B71D4CD94828007996D5BC7F51EDF0694C53E3D16446506DE5052566DB176B581AF18C4BAB9503B75A6D5E28EBC6F31DCC9FBB46C1EFA4B6848B252903BAB01467C656B4F55A672ABBC9D9FB7431F72CDB4D73398D48F271BC29437EB2DFA8A77AE7D1697B9ABD1E75237BAA08A69391B4279003A3E3216EEA6321521EF61BDB6EF3C2B0DC1D7AE4C9C4B22DC4ED6D94ED4B7DBDA7C38AC3F1D8A50956475273215C8FC29D73C5DCD5CB4961CE4BDC7B8F1AD6200B95A2DCEF4D2D7255921365F5F31D51DE6B08802045C8559DD51CCE2FDA3D0C614A54F702BD5D83BAB16D6F8E40547FA54AC94F7D31A4B8CC43F3D8A1F6C6F396C7F31585E9161D889080B2C3E7D473E34A491DDE834CCDD186B1EDBD7E83257254530DB2F91BC8D891DEAA6B020ED8CF794E7FA4DF928F89A8EC351DB0DB0DA1B40F5522DD15A12E272B894A93C942E2BE4A819F3F8947CDCF2548C4998A754CB79B2F04EC02B0F9C8999C25252A45B37BFA12633525367537E4788A9FA3C1E19DA78EB1BBA9A047ADC298703CD058D97DE391E22B11C2DB76EB69365F2158563B3B082942FE710C6F4AB78EEAC33108B8AC7D7435DFDA41DE9E9E949CD8E618DFB282AB781D73265012A5AD46C94245CA8F6544C194F0CF89916E11D0760FC478FED484A5080942425237002C07A03B8D390714724A9866304241B78C38AF27BC0E3585406F0F881A412B513996E1DEB573E963917C46619681F357CD9DFE85FB5DC68D6251B617503F10A61C7F0F9224C0564706F1C0D6038DC7C659B27CDCA48F2DB3FB8ECE96909CDA551C704B17FDE9E73568D892B59395284EF51E558461BE2BE7E45973163CA57040F653FF0076FD89E6D0F34B6DD48536B16524F115219561B24457AFA93B1874FAC3D93DB46A5B3A978A7D5DE2BCE30FA64C45143E837045617A510254342E63896246E5A4F3ECE871AC636E972FFA628FDEB47E3090F2A7AF6A5376D81FBABFC7D931D85F2843530DB812F6F483B95DE3FCD403250B5C793B54DEC39B62D3D879F7D62A9F36857236F03B192B5E6B91D01BEB1F51563D2D2D6C756866324F6AAFF1A8ECA23B0DB2D0B21B4848FB03EFA59C80DCAD672A1237A8F431E694A845F66FAE8FE705B88E229C8AC6330997C9C8FDBC9751BD3F11D958C3722235AB9A80368CAEA7A8BF81ECE937D7AC3E5993A62DBE47997242F2F7E5B0FB0E1C7C664489AADC545A67B1037FE67A12D84C98EB6944A42B883623B6B47642E015E1F33638855BE07B8D62AB4BF065B4B48394849078D4A82B601531771AF638A7BB9D254142E9DA3A093635A4C9660CEC15D650869297944D85870BD4394DCC6CB8C056AAF64AC8B05768ECF4F2DCD54579C1BD0852BF4AC0148560B0CB7BB563F3E3D1D286836F44969EB156A17DA0EEFD69C92A5EB2FEBA520F78F03F874779C2B505051DF9556E86C00A9442509DA49E1410349E787568B6131890DF02F2B9F750000000B01E0C6F11F1169B4A7E95D5048ECEDAC35F5488895B9D6DA0FA4202810ADA0EC35A3B3061932561524D834B3AB27D9E0698D21C35F9DE2AD48BBB7CA0DBC951E40F434CDDB4161A1D653A93D2DE6B1B5AF11949C1A2A8A507CA94E0E09F6477D30CB71D9432CA421B40CA948E1E1D28CCAC563A6FB3589FDAF58565F106B29BECDBDFE974FE225296662363AA56AC914127665D878562DA4AE468AD371D00CB50DAB56D48EDA81A5188C792954B73C618BF969290081D94FE2D15A4660E67BEEB7C6B1A7D53A6C7521C4E6CF982378B749D7931A3BD217D56D2555A2ED2BC4972DEFA696B2E13D9C3A1A48ABCC2AF61D47C2B00776BAD1FC43D2E99435CBC1EED0BAD956B2DD9C6A1B49912DA6F7A6F750ECA95152C4D534D9590948B85F0A71BF20D3816E93AE56729397B36568FA72B4FEC1B17BFDDD2D2E51F929A8E8DF25E4B74CB619690DA3AA80123C24D85CEE158A2F5ADBCBE6ACDFF00EAB0755B104F6823D32224742CAD11D94ACF108158E600FC99CB950D6DDDCB67439B368E37A8BA332B5A15296C8037041BFBEBF86B1242F54DA1A5206E74AF7FBABC45386B288F9B3BBD65ABB4F4B1FDB3B0349DC645FA18B2CA2039978ECAC4C5E0BBB48D97D9583FD7D9FF00BC3EC4EAD4E3AA5ACDD4A37E87FFC40029100100010303030402030101000000000001110021314151611071812091A1B130C1D1E1F0F140FFDA0008010100013F10FCE0B032D36BD74B69C8F6137A56A00C92E87777A82C152709D05827769D2D7B9F63F9A46C9B00FBB58E2CDE53EC547DD0BE1FDB41C2099E4F34E5A46241F4524D856723E655FC52A491A36144F10CD40C4961A5C0BFF8103389AB0DE8E169ED3DA981205B99E90B6F348CE90978FE14B61C15F4914ABF205FBE8D3D0A94B3D0E3F81769770BD97397E02682183E43CB1E2294727B5459448C1F25223088FE782179644A756382AA57C9FE734AE1D0F0A701E6841A16200E50FD7B5470BDCBE9DFD05429E19C96DC9A7BA2ACC52D45BBE8705A90953D65F35AD3FEB99A590583939461F10F15A997264EE64F3F9E17411A4473255DDB9BE3538D3C6F578EB43B9A352612C991EE56128D1979DAA72568E4EC7A0FF3C04C5986AE87347DCF7BCBAC9AAEAFA548290C636AC7B65C8D3BACB3232B35930F151AC466D20FEFF3CC91C50966E4F24FB563C096344FFBF1D1A6C29CB58E56553F01BAFC6DB92D589215DF9F161A0910DE1309E91A4CDA29F33334A674442CB680DA8854C0743C2EDBC7BD0282100D0FCE0719341D4A99198FBCDA64FBA69E8D4598425190EC4041DDA3C5B82951A60E51DDAA3E3B52228E4F427CED4F18FF0071508A74B9647DCD673FF804D4A38B5059803F0D34D47E461BF60AD946AA06E1BD452466E708EEBFAAB2938A163582A6777A1B4417B4508404404130FF00C248277E82EF8177C57B207167C957A5BF1A24CFA0F83DE8309CD60E1D0E0A6EEF1EB825B909A199214F63F0E714B2520DDA2A51BE056E096A589D2F5DAD52A33D0919F769379F44C1EF4895CE0F71A1286E36727605FDE2A69EC7647BB3F36C7484A6721AF6095ED45888919D64EAADFF0009631BDD8FE42B946162DDFC47AC15012D0471C8F60CBE29652EC840344313C2BDAA752C85BC7FE2A0133A73F9A3E01C400FAA4EEFBD2A6A65A33146488BB82F4E5E9BB25DB53DE895948C8215B268D0F0109B429EF441E5A5555CBF85500DEC3C539B9C696FD37699342E895CEC3DCE6A18E24207DA93D5489913A5DE017574A0FEA8371DB47BE3BD33BB64287CE7EB8A02103004051428F502ABA8AEEEBD8331B14A11200C06CEFB7BA928269B796034B0FC8B944B27FF000F15DE8A43C9AD4025299D5E162529513F4BBBD642AC6592CDB6B71A6A54C1026E2FD19AD4FE9D56EF35A945965D8DDA621FED4050052E66C78735ACD18C8EC9A3D0D1A2A1B3BD4F34B5BCA10E9114E98389E470D4FF00E47B1F402D3C157ADC184DC4D11B27E1289EBDE9A95790A51C2F23B1B79A020917BAE5776A73848E819A57822058E3777A12C6DA4081E68776F0320E1A9FA41C6193BECD61BD6E422644D1A69C1E605290A6D53DD14EC582B905A4C9521224524A4A6F522922B4B3B54EE99E4108D4C94B898ECD23C8CFF544090121713F036F227061F745B95C4087DD76EDA1FC5679B4929F45446DA8786A51E4BC5E51984B7EF8A8C0B0846F3A781F3489EBB4DE198A24320B3903483C8D4C2A4A7CCD00A4B060A597DE69CE00BDD97A839230195B07BA5199B32B5613E2D1531C9029E2FD2F31449AC5B8EF06C780A2C416363151862F7BB7933D9A8BE618658C8659D948A121381A28DBD9A3492C7BB254BBC94292C0466187B944DCA59CBFE7DA9A24FC6498A56248B9542F760F7A04762C3ACBA525502026850E3C127540D1A046D2B454B42CA124F6A72E4227A1C5044809A72C1ECDDF053088498961EF606AF050512C64795755CAEAFA0CB140488E89519BAED8014AE5DB0516E50404E66EA9146859863DCA9B889A276AD83EA92922BEBA4BDA1FEFF1C1A9B1F9B6190EEFDF54A42870B38AB1438400D8FE454E3B10FF009A619765E0DAC03C5EA43892D4AA039573DDAB53A1BA0B0E1603F9ACD1C4C659243819B76A8767DAA22904D9477D03BD04082D2C5F21158C6E001E16580F35255E63C4C7DC3429853B50503EC0FDD36AD1A0667B50146DEACA90E39068C0E7F143BF432DF3152AC253754FA8CA2CAA029E88A344D93D1160C837BBD0AE5E121981C4B0E4A5489372FB2682C64C80ABCB2D1328C00A7B21306CD1EC9A8EA60376EC163E569366ABDE4FE684245BF12BC8C9E32CFEAA0500B670450FA1405C987F403BD4CF6F28F7AFBF834EB02C03C9074791669747BC1CD8C5A934A7A8C5065C622F3E1838A8357E2C4BB1B183D02C2003EC99F32D12B97A62200EB6D2B548D8B7163DCADBD6912F183F15756FB8FC196AC3F11FCD4CAF49A52B50B97C96F04B436BFA8F698F913B560E6C31FDFA1A6AD7922786818BB16A38A686076D63E8AC2D08B30CA09EC4F9A9A7A427A30DBB0D1B85CC0C6C5DA684C5487C95939192811733667B73C52E6B2309E593B62AF84360DB27EF1587D473B2270C3367E28A13D80DA61FBC1AD1449DD2ED0DDF047750AEB8181B018EB1D5E854861441DA94CA50A46CAEAB513914657E8D0F531347030F63D80F3DE8503721835E7F9A70B66248EA27EA99413CACFD9F4A4461B3E832536A2B57F4A8242424AF03BFC6684A9180FE006EE5758F5C7E078E92244C952415959E636E392F4444491B2351565ECD31F559963FDE6823CB5310D61A3E830A3321D4BA4FF4AB81AD22063BEA2381DFF2BE99A5A88064DF8934DB21BEF460EA448D0346C1868D8B794F09FD534A825C81AFA04939A2ED9229CC98E0978A80023E023FBE87E6C7179CC09B6C05D5B06685426CEA4D34B57E252E63F717EE14980B7EB539375AF53A83177B1BF23C539F4991382F4C8109B710E07708F7A3F24F468BB065A6BC089D4C23915EC14B4BD09FCC0E497026CC51B17AB362717045F4A0BA5FB81446803032E45CB4719A26D2613D17789DCA38AC640334FB538887699CCB7744DA7D735353D66A7A26084F09FD531AADD9CC899795A9E93D0F68921BA65DBED404EAF3B86FF153577EECA2BBC1AFA08B60AD002EAB57153A1D4B7060B7EDB1320400401B1D00ACA5ED58A4175E8081873E89A9A9A9E934B53445424371CD397D2CD97B1113FF006A2D52499E215FBD3ACD030B14386DFBA72F7A9F4020196808A198B6F7247C73446CF80054F4BB44F801C3EEA232129773254FA96A6A6A6A6A66A5FC86214473E2D5282BA08B43367DEA76ABB504090CAB36C77A304BC800B021331AD16000384BE2EB1506C402D1D5E0BDF55A5BFA6226592C4C18FD79A8F63DE31876CBE881F77D885F74D25600BE1FD749A9A5A9E9353D27AAA8B716502437B53C09D614035DAA5D36095370388A8ACDD20E56C548A3D3591858C050002B042E9063B7AAEFA600C4998F8286E0083608A9A9A1658095E299DC9DDD894C4D1AF69FD7A67A4D4F49F464C0062F98A8FF00F9260110139B5A9AB865D03BAD74D0A5F6D3000272E5344461216360FF006DD1A9EB6AA660E40FE6B4A73D1254306F0B0D43C7824E1B25081958925CB7F57AADFA36E8DBD0E29E8B03DAA75809A7AFFFC40032110002010204030506070100000000000001020300110412213105104113202232510630618191B11433425271A1C1F0FFDA0008010201013F00EFA005803589B860B6D0578F61615918EED5D98EA4D1897D2961075B576D147A5C9FE2BF0F14BF966C7D0D4913466CC3BF0CFA76726DF6AC96391BA55CAEF57BED4C5635CEF52CEF2EFB7A72EC581006F5886CB1647376F711B67456EA34E445B514F1F6C997A8DB996CAAEE3A69EE625C91853B9D68024D853CF147A6E6BF1A4795451249B9E4CA5A3751BEFDC8E1793CA2861E34FCC6B9F8566897CA9F5AED97F60FA50923BDCA0A570FE2AC4C9D9A651B9FB7776371BD3AA3EAEBAFA8A38788ECD6FE452E16306E5AE29E62DE14D052F02E20D1F682136FEFE9BD302A72B6879E178370F970CA81487B0F17C48F4DADFE5710C2BA390775D0FCBBA401BB0AF07EE1F5A319B5C548731D36AF66E34476C532E62B602FEA7AFF005A50C5BE6BB6D5ED7080CC9227988D7E3E9586C0BCE331D05370A005D5B5AC171AEC90418AF091B1E84563F1D1CF8C695366FF0005AB130766732EC7BD86F046D27CAAE6B807168B02AE92FEAB7CAD53F1BC3019CBDCFC2833F13C419A4F28FF00AD4B6B694580DEA444986561714FC2A33E52453E0F247949B8A6194907BAFE18517E7CF0D8379CFA0F5A862585422D30B1AD0520B0D79620D909352E14C84BC66F7A65286CC2DDCC568557D0561F0B24E7C234F5A8387C716ADA9AB5B99B2EC285EDAF2750E0A9A9E37C2CA452BA4EB6715340D11D76E428C071188C9D2911635CAA34F738BC3ACE963B8A40639321A5716B30B8E51AE670B58440333F527DC13D2AF47515361927B31D0D4913466CDCA30C5C64DEB0CAC175A26DDEBD1366157E43734543687970CC383E33D7EDC9F6A1CAF57ABF26B11AD48E249028DA9E219C2AD5AC4D5F9605408C7CB93EC6877C69A8ACCD7CD7D79FFFC4002E110002010302040503040300000000000001020300041121311012204105223251811330610614A1F02391C1FFDA0008010301013F00EB3B559AA88F2A7535A5647B566813524EB1FA8D3DC17D947CD3C3247B8C8A041EB5668CF325238750EBDEB7E13CE53C89BD63B9DF81914024ED52B2BBE536FB168D82C9F3C6EA3E53F507CF1BB3A2A7BFD9B55CB17F8A670832D4F72EDE8D053176F5371BC182ADD04814B148FB0C50B53DDA85A2FB9A365ECD4A81005153BF3BE3B0E9650C30C29AD31E83FEE8C128ED9AFA721D31515BAC63277A6F17B20FF4CCA33FDEFB50C30C8A0280AB8F16BF8AE1A4E60533A2FE33EFEF48EAEA255F4B6A3E7A4DDA7615FBB5F6349728DA6683A8D0D78FB3CAAB6A8DCA18124FE0634FE75A6B28D63F2EFEF5FA4DE6681D24F483A7FDA9EF121381A9A5F12D7CCBA55F7833BC867B3F306DC7706BC3EC1E1B25824DC7F1939AD54F2B6FD5127D4900A118049AFD41E152DF9478775CFCE6A0F04BD7C465703F34B1A786DB8863DCFF00734C083AD04276A46784E41C1A5F1271B8CD49389FCD8C11435E9B25C966E33DD2423DCD4B2B4ADCCD564904CBCB268C28086DD73A0AB997EB4A5C708572401455A3D185039DBA2C47F8F353DC2423CDBD4D7D249A0D07428E6DCD1C674E0AECA722A1916E2306A7B4C6A9BD03D8EFC6398416C1BBD3B973CCDBFD9B59DA17C8DAB21D79854F6E24D46FC09C0CD5C31C2A7B0FB0077E0A706A19DE1381A8A8E5590647038C6B5211A0EF4064E051D3A95720D6382EAA281236E12363845EAA61AF1C56382120E94A9CA849DE94F9799AB3A0C7190F08877A7DFAF359D318E3FFD9, '', '2021-01-13 17:15:36', '', '2021-01-13 17:15:36', b'0'); | |
| 382 | +INSERT INTO `sys_dict_type` VALUES (111, 'çŸä¿¡æ¸ é“ç¼–ç ', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2021-04-05 01:04:50', b'0'); | |
| 383 | +INSERT INTO `sys_dict_type` VALUES (112, 'çŸä¿¡æ¨¡æ¿çš„类型', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2021-04-05 21:50:43', b'0'); | |
| 384 | +INSERT INTO `sys_dict_type` VALUES (113, 'çŸä¿¡å‘é€çжæ€', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2021-04-11 09:30:02', b'0'); | |
| 385 | +INSERT INTO `sys_dict_type` VALUES (114, 'çŸä¿¡æŽ¥æ”¶çжæ€', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2021-04-11 20:27:14', b'0'); | |
| 365 | 386 | COMMIT; |
| 366 | 387 | |
| 367 | 388 | -- ---------------------------- |
| ... | ... | @@ -382,7 +403,7 @@ CREATE TABLE `sys_login_log` ( |
| 382 | 403 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 383 | 404 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 384 | 405 | PRIMARY KEY (`id`) USING BTREE |
| 385 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; | |
| 406 | +) ENGINE=InnoDB AUTO_INCREMENT=84 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; | |
| 386 | 407 | |
| 387 | 408 | -- ---------------------------- |
| 388 | 409 | -- Records of sys_login_log |
| ... | ... | @@ -411,7 +432,7 @@ CREATE TABLE `sys_menu` ( |
| 411 | 432 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 412 | 433 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 413 | 434 | PRIMARY KEY (`id`) USING BTREE |
| 414 | -) ENGINE=InnoDB AUTO_INCREMENT=1087 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨'; | |
| 435 | +) ENGINE=InnoDB AUTO_INCREMENT=1110 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨'; | |
| 415 | 436 | |
| 416 | 437 | -- ---------------------------- |
| 417 | 438 | -- Records of sys_menu |
| ... | ... | @@ -422,63 +443,63 @@ INSERT INTO `sys_menu` VALUES (2, '基础设施', '', 1, 2, 0, '/infra', 'monito |
| 422 | 443 | INSERT INTO `sys_menu` VALUES (3, 'ç ”å‘工具', '', 1, 3, 0, '/tool', 'tool', NULL, 0, 'admin', '2021-01-05 17:03:48', '', '2021-02-06 12:44:42', b'0'); |
| 423 | 444 | INSERT INTO `sys_menu` VALUES (4, 'è‹¥ä¾å®˜ç½‘', '', 1, 4, 0, 'http://ruoyi.vip', 'guide', NULL, 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-20 21:54:28', b'1'); |
| 424 | 445 | INSERT INTO `sys_menu` VALUES (100, '用户管ç†', 'system:user:list', 2, 1, 1, 'user', 'user', 'system/user/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); |
| 425 | -INSERT INTO `sys_menu` VALUES (101, '角色管ç†', 'system:role:list', 2, 2, 1, 'role', 'peoples', 'system/role/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); | |
| 426 | -INSERT INTO `sys_menu` VALUES (102, 'èœå•管ç†', 'system:menu:list', 2, 3, 1, 'menu', 'tree-table', 'system/menu/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); | |
| 427 | -INSERT INTO `sys_menu` VALUES (103, '部门管ç†', 'system:dept:list', 2, 4, 1, 'dept', 'tree', 'system/dept/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); | |
| 428 | -INSERT INTO `sys_menu` VALUES (104, 'å²—ä½ç®¡ç†', 'system:post:list', 2, 5, 1, 'post', 'post', 'system/post/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); | |
| 429 | -INSERT INTO `sys_menu` VALUES (105, 'å—典管ç†', 'system:dict:list', 2, 6, 1, 'dict', 'dict', 'system/dict/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); | |
| 430 | -INSERT INTO `sys_menu` VALUES (106, 'é…置管ç†', 'infra:config:list', 2, 1, 2, 'config', 'edit', 'infra/config/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-02-08 20:03:57', b'0'); | |
| 431 | -INSERT INTO `sys_menu` VALUES (107, '通知公告', 'system:notice:list', 2, 8, 1, 'notice', 'message', 'system/notice/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); | |
| 446 | +INSERT INTO `sys_menu` VALUES (101, '角色管ç†', '', 2, 2, 1, 'role', 'peoples', 'system/role/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:04:49', b'0'); | |
| 447 | +INSERT INTO `sys_menu` VALUES (102, 'èœå•管ç†', '', 2, 3, 1, 'menu', 'tree-table', 'system/menu/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:04:28', b'0'); | |
| 448 | +INSERT INTO `sys_menu` VALUES (103, '部门管ç†', '', 2, 4, 1, 'dept', 'tree', 'system/dept/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:25:19', b'0'); | |
| 449 | +INSERT INTO `sys_menu` VALUES (104, 'å²—ä½ç®¡ç†', '', 2, 5, 1, 'post', 'post', 'system/post/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:38:23', b'0'); | |
| 450 | +INSERT INTO `sys_menu` VALUES (105, 'å—典管ç†', '', 2, 6, 1, 'dict', 'dict', 'system/dict/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:16:58', b'0'); | |
| 451 | +INSERT INTO `sys_menu` VALUES (106, 'é…置管ç†', '', 2, 1, 2, 'config', 'edit', 'infra/config/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-10 01:12:10', b'0'); | |
| 452 | +INSERT INTO `sys_menu` VALUES (107, '通知公告', '', 2, 8, 1, 'notice', 'message', 'system/notice/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:51:39', b'0'); | |
| 432 | 453 | INSERT INTO `sys_menu` VALUES (108, '日志管ç†', '', 1, 9, 1, 'log', 'log', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:34:28', b'0'); |
| 433 | 454 | INSERT INTO `sys_menu` VALUES (109, '在线用户', 'system:user-session:list', 2, 10, 1, 'user-session', 'online', 'system/session/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-26 08:21:20', b'0'); |
| 434 | -INSERT INTO `sys_menu` VALUES (110, '定时任务', 'infra:job:query', 2, 2, 2, 'job', 'job', 'infra/job/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-02-07 13:01:36', b'0'); | |
| 455 | +INSERT INTO `sys_menu` VALUES (110, '定时任务', '', 2, 2, 2, 'job', 'job', 'infra/job/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-10 01:25:51', b'0'); | |
| 435 | 456 | INSERT INTO `sys_menu` VALUES (111, 'MySQL 监控', '', 2, 4, 2, 'druid', 'druid', 'infra/druid/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-02-26 02:18:32', b'0'); |
| 436 | 457 | INSERT INTO `sys_menu` VALUES (112, 'Java 监控', '', 2, 6, 2, 'admin-server', 'server', 'infra/server', 0, 'admin', '2021-01-05 17:03:48', '', '2021-02-26 02:18:41', b'0'); |
| 437 | 458 | INSERT INTO `sys_menu` VALUES (113, 'Redis 监控', '', 2, 5, 2, 'redis', 'redis', 'infra/redis/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-02-26 02:18:37', b'0'); |
| 438 | 459 | INSERT INTO `sys_menu` VALUES (114, 'è¡¨å•æž„建', 'tool:build:list', 2, 1, 3, 'build', 'build', 'tool/build/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); |
| 439 | 460 | INSERT INTO `sys_menu` VALUES (115, '代ç 生æˆ', 'tool:codegen:query', 2, 0, 3, 'codegen', 'code', 'tool/codegen/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-03-06 03:43:14', b'0'); |
| 440 | 461 | INSERT INTO `sys_menu` VALUES (116, '系统接å£', 'tool:swagger:list', 2, 3, 3, 'swagger', 'swagger', 'tool/swagger/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:45', b'0'); |
| 441 | -INSERT INTO `sys_menu` VALUES (500, 'æ“作日志', 'system:operate-log:list', 2, 1, 108, 'operate-log', 'form', 'system/operatelog/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-16 18:25:45', b'0'); | |
| 442 | -INSERT INTO `sys_menu` VALUES (501, '登录日志', 'system:login-log:list', 2, 2, 108, 'login-log', 'logininfor', 'system/loginlog/index', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-18 05:29:58', b'0'); | |
| 462 | +INSERT INTO `sys_menu` VALUES (500, 'æ“作日志', '', 2, 1, 108, 'operate-log', 'form', 'system/operatelog/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:42:56', b'0'); | |
| 463 | +INSERT INTO `sys_menu` VALUES (501, '登录日志', '', 2, 2, 108, 'login-log', 'logininfor', 'system/loginlog/index', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:39:23', b'0'); | |
| 443 | 464 | INSERT INTO `sys_menu` VALUES (1001, '用户查询', 'system:user:query', 3, 1, 100, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 444 | -INSERT INTO `sys_menu` VALUES (1002, '用户新增', 'system:user:add', 3, 2, 100, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 445 | -INSERT INTO `sys_menu` VALUES (1003, '用户修改', 'system:user:edit', 3, 3, 100, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 446 | -INSERT INTO `sys_menu` VALUES (1004, 'ç”¨æˆ·åˆ é™¤', 'system:user:remove', 3, 4, 100, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 465 | +INSERT INTO `sys_menu` VALUES (1002, '用户新增', 'system:user:create', 3, 2, 100, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:21:47', b'0'); | |
| 466 | +INSERT INTO `sys_menu` VALUES (1003, '用户修改', 'system:user:update', 3, 3, 100, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:21:54', b'0'); | |
| 467 | +INSERT INTO `sys_menu` VALUES (1004, 'ç”¨æˆ·åˆ é™¤', 'system:user:delete', 3, 4, 100, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:22:03', b'0'); | |
| 447 | 468 | INSERT INTO `sys_menu` VALUES (1005, '用户导出', 'system:user:export', 3, 5, 100, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 448 | 469 | INSERT INTO `sys_menu` VALUES (1006, '用户导入', 'system:user:import', 3, 6, 100, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 449 | -INSERT INTO `sys_menu` VALUES (1007, 'é‡ç½®å¯†ç ', 'system:user:resetPwd', 3, 7, 100, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 470 | +INSERT INTO `sys_menu` VALUES (1007, 'é‡ç½®å¯†ç ', 'system:user:update-password', 3, 7, 100, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:20:55', b'0'); | |
| 450 | 471 | INSERT INTO `sys_menu` VALUES (1008, '角色查询', 'system:role:query', 3, 1, 101, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 451 | -INSERT INTO `sys_menu` VALUES (1009, '角色新增', 'system:role:add', 3, 2, 101, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 452 | -INSERT INTO `sys_menu` VALUES (1010, '角色修改', 'system:role:edit', 3, 3, 101, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 453 | -INSERT INTO `sys_menu` VALUES (1011, 'è§’è‰²åˆ é™¤', 'system:role:remove', 3, 4, 101, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 472 | +INSERT INTO `sys_menu` VALUES (1009, '角色新增', 'system:role:create', 3, 2, 101, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:05:24', b'0'); | |
| 473 | +INSERT INTO `sys_menu` VALUES (1010, '角色修改', 'system:role:update', 3, 3, 101, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:05:14', b'0'); | |
| 474 | +INSERT INTO `sys_menu` VALUES (1011, 'è§’è‰²åˆ é™¤', 'system:role:delete', 3, 4, 101, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 22:05:05', b'0'); | |
| 454 | 475 | INSERT INTO `sys_menu` VALUES (1012, '角色导出', 'system:role:export', 3, 5, 101, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 455 | 476 | INSERT INTO `sys_menu` VALUES (1013, 'èœå•查询', 'system:menu:query', 3, 1, 102, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 456 | -INSERT INTO `sys_menu` VALUES (1014, 'èœå•新增', 'system:menu:add', 3, 2, 102, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 457 | -INSERT INTO `sys_menu` VALUES (1015, 'èœå•修改', 'system:menu:edit', 3, 3, 102, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 458 | -INSERT INTO `sys_menu` VALUES (1016, 'èœå•åˆ é™¤', 'system:menu:remove', 3, 4, 102, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 477 | +INSERT INTO `sys_menu` VALUES (1014, 'èœå•新增', 'system:menu:create', 3, 2, 102, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-03-13 15:49:36', b'0'); | |
| 478 | +INSERT INTO `sys_menu` VALUES (1015, 'èœå•修改', 'system:menu:update', 3, 3, 102, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-03-13 15:49:30', b'0'); | |
| 479 | +INSERT INTO `sys_menu` VALUES (1016, 'èœå•åˆ é™¤', 'system:menu:delete', 3, 4, 102, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-03-13 15:49:45', b'0'); | |
| 459 | 480 | INSERT INTO `sys_menu` VALUES (1017, '部门查询', 'system:dept:query', 3, 1, 103, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 460 | -INSERT INTO `sys_menu` VALUES (1018, '部门新增', 'system:dept:add', 3, 2, 103, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 461 | -INSERT INTO `sys_menu` VALUES (1019, '部门修改', 'system:dept:edit', 3, 3, 103, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 462 | -INSERT INTO `sys_menu` VALUES (1020, 'éƒ¨é—¨åˆ é™¤', 'system:dept:remove', 3, 4, 103, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 481 | +INSERT INTO `sys_menu` VALUES (1018, '部门新增', 'system:dept:create', 3, 2, 103, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:25:30', b'0'); | |
| 482 | +INSERT INTO `sys_menu` VALUES (1019, '部门修改', 'system:dept:update', 3, 3, 103, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:25:37', b'0'); | |
| 483 | +INSERT INTO `sys_menu` VALUES (1020, 'éƒ¨é—¨åˆ é™¤', 'system:dept:delete', 3, 4, 103, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:25:43', b'0'); | |
| 463 | 484 | INSERT INTO `sys_menu` VALUES (1021, '岗使Ÿ¥è¯¢', 'system:post:query', 3, 1, 104, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 464 | -INSERT INTO `sys_menu` VALUES (1022, '岗使–°å¢ž', 'system:post:add', 3, 2, 104, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 465 | -INSERT INTO `sys_menu` VALUES (1023, 'å²—ä½ä¿®æ”¹', 'system:post:edit', 3, 3, 104, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 466 | -INSERT INTO `sys_menu` VALUES (1024, 'å²—ä½åˆ 除', 'system:post:remove', 3, 4, 104, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 485 | +INSERT INTO `sys_menu` VALUES (1022, '岗使–°å¢ž', 'system:post:create', 3, 2, 104, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:38:34', b'0'); | |
| 486 | +INSERT INTO `sys_menu` VALUES (1023, 'å²—ä½ä¿®æ”¹', 'system:post:update', 3, 3, 104, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:38:41', b'0'); | |
| 487 | +INSERT INTO `sys_menu` VALUES (1024, 'å²—ä½åˆ 除', 'system:post:delete', 3, 4, 104, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:38:48', b'0'); | |
| 467 | 488 | INSERT INTO `sys_menu` VALUES (1025, 'å²—ä½å¯¼å‡º', 'system:post:export', 3, 5, 104, '', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 468 | 489 | INSERT INTO `sys_menu` VALUES (1026, 'å—典查询', 'system:dict:query', 3, 1, 105, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 469 | -INSERT INTO `sys_menu` VALUES (1027, 'å—典新增', 'system:dict:add', 3, 2, 105, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 470 | -INSERT INTO `sys_menu` VALUES (1028, 'å—典修改', 'system:dict:edit', 3, 3, 105, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 471 | -INSERT INTO `sys_menu` VALUES (1029, 'å—å…¸åˆ é™¤', 'system:dict:remove', 3, 4, 105, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 490 | +INSERT INTO `sys_menu` VALUES (1027, 'å—典新增', 'system:dict:create', 3, 2, 105, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:19:29', b'0'); | |
| 491 | +INSERT INTO `sys_menu` VALUES (1028, 'å—典修改', 'system:dict:update', 3, 3, 105, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:19:36', b'0'); | |
| 492 | +INSERT INTO `sys_menu` VALUES (1029, 'å—å…¸åˆ é™¤', 'system:dict:delete', 3, 4, 105, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:19:45', b'0'); | |
| 472 | 493 | INSERT INTO `sys_menu` VALUES (1030, 'å—典导出', 'system:dict:export', 3, 5, 105, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 473 | 494 | INSERT INTO `sys_menu` VALUES (1031, 'é…置查询', 'infra:config:query', 3, 1, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-20 14:34:00', b'0'); |
| 474 | -INSERT INTO `sys_menu` VALUES (1032, 'é…置新增', 'infra:config:add', 3, 2, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-20 14:34:05', b'0'); | |
| 475 | -INSERT INTO `sys_menu` VALUES (1033, 'é…置修改', 'infra:config:edit', 3, 3, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-20 14:33:52', b'0'); | |
| 476 | -INSERT INTO `sys_menu` VALUES (1034, 'é…ç½®åˆ é™¤', 'infra:config:remove', 3, 4, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-20 14:34:12', b'0'); | |
| 495 | +INSERT INTO `sys_menu` VALUES (1032, 'é…置新增', 'infra:config:create', 3, 2, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-10 01:12:18', b'0'); | |
| 496 | +INSERT INTO `sys_menu` VALUES (1033, 'é…置修改', 'infra:config:update', 3, 3, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-10 01:12:30', b'0'); | |
| 497 | +INSERT INTO `sys_menu` VALUES (1034, 'é…ç½®åˆ é™¤', 'infra:config:delete', 3, 4, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-10 01:12:36', b'0'); | |
| 477 | 498 | INSERT INTO `sys_menu` VALUES (1035, 'é…置导出', 'infra:config:export', 3, 5, 106, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-20 14:34:19', b'0'); |
| 478 | 499 | INSERT INTO `sys_menu` VALUES (1036, '公告查询', 'system:notice:query', 3, 1, 107, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); |
| 479 | -INSERT INTO `sys_menu` VALUES (1037, '公告新增', 'system:notice:add', 3, 2, 107, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 480 | -INSERT INTO `sys_menu` VALUES (1038, '公告修改', 'system:notice:edit', 3, 3, 107, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 481 | -INSERT INTO `sys_menu` VALUES (1039, 'å…¬å‘Šåˆ é™¤', 'system:notice:remove', 3, 4, 107, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-05 22:36:55', b'0'); | |
| 500 | +INSERT INTO `sys_menu` VALUES (1037, '公告新增', 'system:notice:create', 3, 2, 107, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:51:48', b'0'); | |
| 501 | +INSERT INTO `sys_menu` VALUES (1038, '公告修改', 'system:notice:update', 3, 3, 107, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:51:55', b'0'); | |
| 502 | +INSERT INTO `sys_menu` VALUES (1039, 'å…¬å‘Šåˆ é™¤', 'system:notice:delete', 3, 4, 107, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 21:52:01', b'0'); | |
| 482 | 503 | INSERT INTO `sys_menu` VALUES (1040, 'æ“作查询', 'system:operate-log:query', 3, 1, 500, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-16 18:28:10', b'0'); |
| 483 | 504 | INSERT INTO `sys_menu` VALUES (1042, '日志导出', 'system:operate-log:export', 3, 2, 500, '', '', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-16 18:28:23', b'0'); |
| 484 | 505 | INSERT INTO `sys_menu` VALUES (1043, '登录查询', 'system:login-log:query', 3, 1, 501, '#', '#', '', 0, 'admin', '2021-01-05 17:03:48', '', '2021-01-18 05:29:26', b'0'); |
| ... | ... | @@ -511,15 +532,37 @@ INSERT INTO `sys_menu` VALUES (1074, '测试示例表导出', 'tool:test-demo:ex |
| 511 | 532 | INSERT INTO `sys_menu` VALUES (1075, '任务触å‘', 'infra:job:trigger', 3, 8, 110, '', '', '', 0, '', '2021-02-07 13:03:10', '', '2021-02-07 13:03:10', b'0'); |
| 512 | 533 | INSERT INTO `sys_menu` VALUES (1076, 'æ•°æ®åº“文档', '', 2, 5, 3, 'db-doc', 'table', 'tool/dbDoc/index', 0, '', '2021-02-08 01:41:47', '', '2021-02-08 01:49:00', b'0'); |
| 513 | 534 | INSERT INTO `sys_menu` VALUES (1077, '链路追踪', '', 2, 7, 2, 'skywalking', 'eye-open', 'infra/skywalking', 0, '', '2021-02-08 20:41:31', '', '2021-02-26 02:18:45', b'0'); |
| 514 | -INSERT INTO `sys_menu` VALUES (1078, '访问日志', 'infra:api-access-log:query', 2, 1, 1083, 'api-access-log', 'log', 'infra/apiAccessLog/index', 0, '', '2021-02-26 01:32:59', '', '2021-02-26 07:53:46', b'0'); | |
| 535 | +INSERT INTO `sys_menu` VALUES (1078, '访问日志', '', 2, 1, 1083, 'api-access-log', 'log', 'infra/apiAccessLog/index', 0, '', '2021-02-26 01:32:59', '1', '2021-03-10 01:28:09', b'0'); | |
| 515 | 536 | INSERT INTO `sys_menu` VALUES (1079, 'API 访问日志表创建', 'system:api-access-log:create', 3, 1, 1078, '', '', '', 1, '', '2021-02-26 01:32:59', '', '2021-02-26 02:21:00', b'1'); |
| 516 | 537 | INSERT INTO `sys_menu` VALUES (1080, 'API 访问日志表更新', 'system:api-access-log:update', 3, 2, 1078, '', '', '', 1, '', '2021-02-26 01:32:59', '', '2021-02-26 02:21:08', b'1'); |
| 517 | 538 | INSERT INTO `sys_menu` VALUES (1081, 'API è®¿é—®æ—¥å¿—è¡¨åˆ é™¤', 'system:api-access-log:delete', 3, 3, 1078, '', '', '', 1, '', '2021-02-26 01:32:59', '', '2021-02-26 02:21:27', b'1'); |
| 518 | -INSERT INTO `sys_menu` VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 4, 1078, '', '', '', 0, '', '2021-02-26 01:32:59', '', '2021-02-26 02:23:22', b'0'); | |
| 539 | +INSERT INTO `sys_menu` VALUES (1082, '日志导出', 'infra:api-access-log:export', 3, 2, 1078, '', '', '', 0, '', '2021-02-26 01:32:59', '1', '2021-03-10 01:28:13', b'0'); | |
| 519 | 540 | INSERT INTO `sys_menu` VALUES (1083, 'API 日志', '', 2, 3, 2, 'log', 'log', NULL, 0, '', '2021-02-26 02:18:24', '', '2021-02-26 02:20:17', b'0'); |
| 520 | 541 | INSERT INTO `sys_menu` VALUES (1084, '错误日志', 'infra:api-error-log:query', 2, 2, 1083, 'api-error-log', 'log', 'infra/apiErrorLog/index', 0, '', '2021-02-26 07:53:20', '', '2021-02-26 07:54:40', b'0'); |
| 521 | -INSERT INTO `sys_menu` VALUES (1085, '日志处ç†', 'infra:api-error-log:update-status', 3, 1, 1084, '', '', '', 0, '', '2021-02-26 07:53:20', '', '2021-02-26 07:53:20', b'0'); | |
| 522 | -INSERT INTO `sys_menu` VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 4, 1084, '', '', '', 0, '', '2021-02-26 07:53:20', '', '2021-02-26 07:53:20', b'0'); | |
| 542 | +INSERT INTO `sys_menu` VALUES (1085, '日志处ç†', 'infra:api-error-log:update-status', 3, 2, 1084, '', '', '', 0, '', '2021-02-26 07:53:20', '1', '2021-03-10 01:28:18', b'0'); | |
| 543 | +INSERT INTO `sys_menu` VALUES (1086, '日志导出', 'infra:api-error-log:export', 3, 3, 1084, '', '', '', 0, '', '2021-02-26 07:53:20', '1', '2021-03-10 01:28:21', b'0'); | |
| 544 | +INSERT INTO `sys_menu` VALUES (1087, '任务查询', 'infra:job:query', 3, 1, 110, '', '', '', 0, '1', '2021-03-10 01:26:19', '1', '2021-03-10 01:26:19', b'0'); | |
| 545 | +INSERT INTO `sys_menu` VALUES (1088, '日志查询', 'infra:api-access-log:query', 3, 1, 1078, '', '', '', 0, '1', '2021-03-10 01:28:04', '1', '2021-03-10 01:29:38', b'0'); | |
| 546 | +INSERT INTO `sys_menu` VALUES (1089, '日志查询', 'infra:api-error-log:query', 3, 1, 1084, '', '', '', 0, '1', '2021-03-10 01:29:09', '1', '2021-03-10 01:29:09', b'0'); | |
| 547 | +INSERT INTO `sys_menu` VALUES (1090, '文件管ç†', '', 2, 0, 2, 'file', 'upload', 'infra/file/index', 0, '', '2021-03-12 20:16:20', '1', '2021-03-13 11:07:05', b'0'); | |
| 548 | +INSERT INTO `sys_menu` VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); | |
| 549 | +INSERT INTO `sys_menu` VALUES (1092, 'æ–‡ä»¶åˆ é™¤', 'infra:file:delete', 3, 4, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); | |
| 550 | +INSERT INTO `sys_menu` VALUES (1093, 'çŸä¿¡ç®¡ç†', '', 1, 11, 1, 'sms', 'validCode', NULL, 0, '1', '2021-04-05 01:10:16', '1', '2021-04-05 01:11:38', b'0'); | |
| 551 | +INSERT INTO `sys_menu` VALUES (1094, 'çŸä¿¡æ¸ é“', '', 2, 0, 1093, 'sms-channel', 'phone', 'system/sms/smsChannel', 0, '', '2021-04-01 11:07:15', '1', '2021-04-09 23:44:07', b'0'); | |
| 552 | +INSERT INTO `sys_menu` VALUES (1095, 'çŸä¿¡æ¸ 铿Ÿ¥è¯¢', 'system:sms-channel:query', 3, 1, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | |
| 553 | +INSERT INTO `sys_menu` VALUES (1096, 'çŸä¿¡æ¸ é“创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | |
| 554 | +INSERT INTO `sys_menu` VALUES (1097, 'çŸä¿¡æ¸ 铿›´æ–°', 'system:sms-channel:update', 3, 3, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | |
| 555 | +INSERT INTO `sys_menu` VALUES (1098, 'çŸä¿¡æ¸ é“åˆ é™¤', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | |
| 556 | +INSERT INTO `sys_menu` VALUES (1100, 'çŸä¿¡æ¨¡æ¿', '', 2, 1, 1093, 'sms-template', 'phone', 'system/sms/smsTemplate', 0, '', '2021-04-01 17:35:17', '1', '2021-04-11 19:34:21', b'0'); | |
| 557 | +INSERT INTO `sys_menu` VALUES (1101, 'çŸä¿¡æ¨¡æ¿æŸ¥è¯¢', 'system:sms-template:query', 3, 1, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 558 | +INSERT INTO `sys_menu` VALUES (1102, 'çŸä¿¡æ¨¡æ¿åˆ›å»º', 'system:sms-template:create', 3, 2, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 559 | +INSERT INTO `sys_menu` VALUES (1103, 'çŸä¿¡æ¨¡æ¿æ›´æ–°', 'system:sms-template:update', 3, 3, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 560 | +INSERT INTO `sys_menu` VALUES (1104, 'çŸä¿¡æ¨¡æ¿åˆ 除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 561 | +INSERT INTO `sys_menu` VALUES (1105, 'çŸä¿¡æ¨¡æ¿å¯¼å‡º', 'system:sms-template:export', 3, 5, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 562 | +INSERT INTO `sys_menu` VALUES (1106, 'å‘逿µ‹è¯•çŸä¿¡', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', 0, '1', '2021-04-11 00:26:40', '1', '2021-04-11 00:26:40', b'0'); | |
| 563 | +INSERT INTO `sys_menu` VALUES (1107, 'çŸä¿¡æ—¥å¿—', '', 2, 2, 1093, 'sms-log', 'phone', 'system/sms/smsLog', 0, '', '2021-04-11 08:37:05', '1', '2021-04-11 19:34:25', b'0'); | |
| 564 | +INSERT INTO `sys_menu` VALUES (1108, 'çŸä¿¡æ—¥å¿—查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', 0, '', '2021-04-11 08:37:05', '', '2021-04-11 08:37:05', b'0'); | |
| 565 | +INSERT INTO `sys_menu` VALUES (1109, 'çŸä¿¡æ—¥å¿—导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', 0, '', '2021-04-11 08:37:05', '', '2021-04-11 08:37:05', b'0'); | |
| 523 | 566 | COMMIT; |
| 524 | 567 | |
| 525 | 568 | -- ---------------------------- |
| ... | ... | @@ -579,7 +622,7 @@ CREATE TABLE `sys_operate_log` ( |
| 579 | 622 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 580 | 623 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 581 | 624 | PRIMARY KEY (`id`) USING BTREE |
| 582 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; | |
| 625 | +) ENGINE=InnoDB AUTO_INCREMENT=177 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; | |
| 583 | 626 | |
| 584 | 627 | -- ---------------------------- |
| 585 | 628 | -- Records of sys_operate_log |
| ... | ... | @@ -610,8 +653,8 @@ CREATE TABLE `sys_post` ( |
| 610 | 653 | -- Records of sys_post |
| 611 | 654 | -- ---------------------------- |
| 612 | 655 | BEGIN; |
| 613 | -INSERT INTO `sys_post` VALUES (1, 'ceo', '董事长', 1, 0, '', 'admin', '2021-01-05 17:03:48', '', '2021-01-05 17:03:48', b'0'); | |
| 614 | -INSERT INTO `sys_post` VALUES (2, 'se', '项目ç»ç†', 2, 0, '', 'admin', '2021-01-05 17:03:48', '', '2021-01-05 17:03:48', b'0'); | |
| 656 | +INSERT INTO `sys_post` VALUES (1, 'ceo', '董事长', 1, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:39:03', b'0'); | |
| 657 | +INSERT INTO `sys_post` VALUES (2, 'se', '项目ç»ç†', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2021-03-14 20:39:05', b'0'); | |
| 615 | 658 | INSERT INTO `sys_post` VALUES (3, 'hr', '人力资æº', 3, 0, '', 'admin', '2021-01-05 17:03:48', '', '2021-01-05 17:03:48', b'0'); |
| 616 | 659 | INSERT INTO `sys_post` VALUES (4, 'user', '普通员工', 4, 0, '', 'admin', '2021-01-05 17:03:48', '', '2021-01-05 17:03:48', b'0'); |
| 617 | 660 | INSERT INTO `sys_post` VALUES (5, 'test', '测试岗ä½', 0, 1, '132', '', '2021-01-07 15:07:44', '', '2021-01-07 15:10:35', b'1'); |
| ... | ... | @@ -645,7 +688,7 @@ CREATE TABLE `sys_role` ( |
| 645 | 688 | BEGIN; |
| 646 | 689 | INSERT INTO `sys_role` VALUES (1, '超级管ç†å‘˜', 'admin', 1, 1, '', 0, 1, '超级管ç†å‘˜', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 12:40:20', b'0'); |
| 647 | 690 | INSERT INTO `sys_role` VALUES (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 11:46:58', b'0'); |
| 648 | -INSERT INTO `sys_role` VALUES (101, '测试账å·', 'test', 0, 2, '[104]', 0, 2, '132', '', '2021-01-06 13:49:35', '', '2021-01-21 02:15:26', b'0'); | |
| 691 | +INSERT INTO `sys_role` VALUES (101, '测试账å·', 'test', 0, 2, '[104]', 0, 2, '132', '', '2021-01-06 13:49:35', '1', '2021-03-14 22:17:20', b'0'); | |
| 649 | 692 | COMMIT; |
| 650 | 693 | |
| 651 | 694 | -- ---------------------------- |
| ... | ... | @@ -825,6 +868,116 @@ INSERT INTO `sys_role_menu` VALUES (238, 101, 1065, '', '2021-01-21 03:23:27', ' |
| 825 | 868 | COMMIT; |
| 826 | 869 | |
| 827 | 870 | -- ---------------------------- |
| 871 | +-- Table structure for sys_sms_channel | |
| 872 | +-- ---------------------------- | |
| 873 | +DROP TABLE IF EXISTS `sys_sms_channel`; | |
| 874 | +CREATE TABLE `sys_sms_channel` ( | |
| 875 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | |
| 876 | + `signature` varchar(10) NOT NULL COMMENT 'çŸä¿¡ç¾å', | |
| 877 | + `code` varchar(63) NOT NULL COMMENT 'æ¸ é“ç¼–ç ', | |
| 878 | + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€', | |
| 879 | + `remark` varchar(255) DEFAULT NULL COMMENT '备注', | |
| 880 | + `api_key` varchar(128) NOT NULL COMMENT 'çŸä¿¡ API 的账å·', | |
| 881 | + `api_secret` varchar(128) DEFAULT NULL COMMENT 'çŸä¿¡ API 的秘钥', | |
| 882 | + `callback_url` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡å‘é€å›žè°ƒ URL', | |
| 883 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 884 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 885 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 886 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 887 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 888 | + PRIMARY KEY (`id`) USING BTREE | |
| 889 | +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ¸ é“'; | |
| 890 | + | |
| 891 | +-- ---------------------------- | |
| 892 | +-- Records of sys_sms_channel | |
| 893 | +-- ---------------------------- | |
| 894 | +BEGIN; | |
| 895 | +INSERT INTO `sys_sms_channel` VALUES (1, '芋é“', 'YUN_PIAN', 0, '呵呵呵哒', '1555a14277cb8a608cf45a9e6a80d510', NULL, 'http://java.nat300.top/api/system/sms/callback/sms/yunpian', '', '2021-03-31 06:12:20', '1', '2021-04-13 01:03:19', b'0'); | |
| 896 | +INSERT INTO `sys_sms_channel` VALUES (2, 'Ballcat', 'ALIYUN', 0, '啦啦啦', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2021-04-14 00:08:37', b'0'); | |
| 897 | +INSERT INTO `sys_sms_channel` VALUES (3, '测试', 'YUN_PIAN', 0, '哈哈哈', '23132', NULL, 'http://www.baidu.com', '1', '2021-04-05 21:10:34', '1', '2021-04-11 16:21:58', b'1'); | |
| 898 | +INSERT INTO `sys_sms_channel` VALUES (4, 'æµ‹è¯•æ¸ é“', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', '2021-04-13 00:23:14', '1', '2021-04-14 00:07:10', b'0'); | |
| 899 | +COMMIT; | |
| 900 | + | |
| 901 | +-- ---------------------------- | |
| 902 | +-- Table structure for sys_sms_log | |
| 903 | +-- ---------------------------- | |
| 904 | +DROP TABLE IF EXISTS `sys_sms_log`; | |
| 905 | +CREATE TABLE `sys_sms_log` ( | |
| 906 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | |
| 907 | + `channel_id` bigint(20) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–å·', | |
| 908 | + `channel_code` varchar(63) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–ç ', | |
| 909 | + `template_id` bigint(20) NOT NULL COMMENT '模æ¿ç¼–å·', | |
| 910 | + `template_code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ', | |
| 911 | + `template_type` tinyint(4) NOT NULL COMMENT 'çŸä¿¡ç±»åž‹', | |
| 912 | + `template_content` varchar(255) NOT NULL COMMENT 'çŸä¿¡å†…容', | |
| 913 | + `template_params` varchar(255) NOT NULL COMMENT 'çŸä¿¡å‚æ•°', | |
| 914 | + `api_template_id` varchar(63) NOT NULL COMMENT 'çŸä¿¡ API 的模æ¿ç¼–å·', | |
| 915 | + `mobile` varchar(11) NOT NULL COMMENT '手机å·', | |
| 916 | + `user_id` bigint(20) DEFAULT NULL COMMENT '用户编å·', | |
| 917 | + `user_type` tinyint(4) DEFAULT NULL COMMENT '用户类型', | |
| 918 | + `send_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'å‘é€çжæ€', | |
| 919 | + `send_time` datetime DEFAULT NULL COMMENT 'å‘逿—¶é—´', | |
| 920 | + `send_code` int(11) DEFAULT NULL COMMENT 'å‘é€ç»“果的编ç ', | |
| 921 | + `send_msg` varchar(255) DEFAULT NULL COMMENT 'å‘é€ç»“果的æç¤º', | |
| 922 | + `api_send_code` varchar(63) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€ç»“果的编ç ', | |
| 923 | + `api_send_msg` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€å¤±è´¥çš„æç¤º', | |
| 924 | + `api_request_id` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€è¿”回的唯一请求 ID', | |
| 925 | + `api_serial_no` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€è¿”回的åºå·', | |
| 926 | + `receive_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '接收状æ€', | |
| 927 | + `receive_time` datetime DEFAULT NULL COMMENT '接收时间', | |
| 928 | + `api_receive_code` varchar(63) DEFAULT NULL COMMENT 'API 接收结果的编ç ', | |
| 929 | + `api_receive_msg` varchar(255) DEFAULT NULL COMMENT 'API 接收结果的说明', | |
| 930 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 931 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 932 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 933 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 934 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 935 | + PRIMARY KEY (`id`) USING BTREE | |
| 936 | +) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ—¥å¿—'; | |
| 937 | + | |
| 938 | +-- ---------------------------- | |
| 939 | +-- Records of sys_sms_log | |
| 940 | +-- ---------------------------- | |
| 941 | +BEGIN; | |
| 942 | +COMMIT; | |
| 943 | + | |
| 944 | +-- ---------------------------- | |
| 945 | +-- Table structure for sys_sms_template | |
| 946 | +-- ---------------------------- | |
| 947 | +DROP TABLE IF EXISTS `sys_sms_template`; | |
| 948 | +CREATE TABLE `sys_sms_template` ( | |
| 949 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | |
| 950 | + `type` tinyint(4) NOT NULL COMMENT 'çŸä¿¡ç¾å', | |
| 951 | + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€', | |
| 952 | + `code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ', | |
| 953 | + `name` varchar(63) NOT NULL COMMENT '模æ¿åç§°', | |
| 954 | + `content` varchar(255) NOT NULL COMMENT '模æ¿å†…容', | |
| 955 | + `params` varchar(255) NOT NULL COMMENT '傿•°æ•°ç»„', | |
| 956 | + `remark` varchar(255) DEFAULT NULL COMMENT '备注', | |
| 957 | + `api_template_id` varchar(63) NOT NULL COMMENT 'çŸä¿¡ API 的模æ¿ç¼–å·', | |
| 958 | + `channel_id` bigint(20) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–å·', | |
| 959 | + `channel_code` varchar(63) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–ç ', | |
| 960 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 961 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 962 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 963 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 964 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 965 | + PRIMARY KEY (`id`) USING BTREE | |
| 966 | +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ¨¡æ¿'; | |
| 967 | + | |
| 968 | +-- ---------------------------- | |
| 969 | +-- Records of sys_sms_template | |
| 970 | +-- ---------------------------- | |
| 971 | +BEGIN; | |
| 972 | +INSERT INTO `sys_sms_template` VALUES (2, 1, 0, 'test_01', '测试验è¯ç çŸä¿¡', 'æ£åœ¨è¿›è¡Œç™»å½•æ“作{operation},您的验è¯ç 是{code}', '[\"operation\",\"code\"]', NULL, '4383920', 1, 'YUN_PIAN', '', '2021-03-31 10:49:38', '1', '2021-04-10 01:22:00', b'0'); | |
| 973 | +INSERT INTO `sys_sms_template` VALUES (3, 1, 0, 'test_02', '公告通知', '您的验è¯ç {code},该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '1', '2021-04-10 01:22:02', b'0'); | |
| 974 | +INSERT INTO `sys_sms_template` VALUES (4, 3, 0, 'test-ooxx', '测试模æ¿', 'biubiubiu', '[]', '1111', '110', 1, 'YUN_PIAN', '1', '2021-04-09 23:58:42', '1', '2021-04-02 07:47:12', b'1'); | |
| 975 | +INSERT INTO `sys_sms_template` VALUES (5, 3, 0, 'test-ooxx', '测试模æ¿', 'biubiubiu', '[]', '1111', '110', 1, 'YUN_PIAN', '1', '2021-04-09 23:58:42', '1', '2021-04-02 08:32:22', b'1'); | |
| 976 | +INSERT INTO `sys_sms_template` VALUES (6, 3, 0, 'test-01', '测试模æ¿', '哈哈哈 {name}', '[\"name\"]', 'f哈哈哈', '4383920', 1, 'YUN_PIAN', '1', '2021-04-10 01:07:21', '1', '2021-04-10 01:22:05', b'0'); | |
| 977 | +INSERT INTO `sys_sms_template` VALUES (7, 3, 0, 'test-04', '测试下', 'è€é¸¡{name},牛逼{code}', '[\"name\",\"code\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2021-04-13 00:29:53', '1', '2021-04-14 00:30:38', b'0'); | |
| 978 | +COMMIT; | |
| 979 | + | |
| 980 | +-- ---------------------------- | |
| 828 | 981 | -- Table structure for sys_user |
| 829 | 982 | -- ---------------------------- |
| 830 | 983 | DROP TABLE IF EXISTS `sys_user`; |
| ... | ... | @@ -855,11 +1008,11 @@ CREATE TABLE `sys_user` ( |
| 855 | 1008 | -- Records of sys_user |
| 856 | 1009 | -- ---------------------------- |
| 857 | 1010 | BEGIN; |
| 858 | -INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'è‹¥ä¾', '管ç†å‘˜', 103, '[1]', 'ry@163.com', '15888888888', 1, 'http://127.0.0.1:8080/api/system/file/get/add5ec1891a7d97d2cc1d60847e16294.jpg', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '', '2021-01-13 17:51:06', b'0'); | |
| 859 | -INSERT INTO `sys_user` VALUES (2, 'ry', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'è‹¥ä¾', '测试员', 105, '[2]', 'ry@qq.com', '15666666666', 1, '', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '', '2021-01-05 17:03:47', b'0'); | |
| 860 | -INSERT INTO `sys_user` VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋é“', 'ä¸è¦å“我', 100, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '', NULL, '', '2021-01-07 09:07:17', '', '2021-01-13 23:53:12', b'0'); | |
| 1011 | +INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', 'èŠ‹é“æºç ', '管ç†å‘˜', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://api-dashboard.yudao.iocoder.cn/api/infra/file/get/5e8609290e915c4fa8b08e67.jpg', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '1', '2021-04-05 02:16:10', b'0'); | |
| 1012 | +INSERT INTO `sys_user` VALUES (2, 'ry', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'è‹¥ä¾', '测试员', 105, '[2]', 'ry@qq.com', '15666666666', 1, '', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '', '2021-04-01 04:50:36', b'1'); | |
| 1013 | +INSERT INTO `sys_user` VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋é“', 'ä¸è¦å“我', 100, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '', NULL, '', '2021-01-07 09:07:17', '1', '2021-03-14 22:35:17', b'0'); | |
| 861 | 1014 | INSERT INTO `sys_user` VALUES (103, 'yuanma', '', 'æºç ', NULL, 100, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '', NULL, '', '2021-01-13 23:50:35', '', '2021-01-13 23:50:35', b'0'); |
| 862 | -INSERT INTO `sys_user` VALUES (104, 'test', '$2a$10$wTJ.1LVmhxcujss2NR2SMeBo8AaFsjkoDfDafHYsdHmitAiwmnvce', '测试å·', NULL, 100, '[]', '', '15601691200', 1, '', 0, '', NULL, '', '2021-01-21 02:13:53', '', '2021-01-21 02:14:13', b'0'); | |
| 1015 | +INSERT INTO `sys_user` VALUES (104, 'test', '$2a$10$.TOFpaIiI3PzEwkGrNq0Eu6Cc3rOqJMxTb1DqeSEM8StxaGPBRKoi', '测试å·', NULL, 100, '[]', '', '15601691200', 1, '', 0, '', NULL, '', '2021-01-21 02:13:53', '1', '2021-03-14 22:36:38', b'0'); | |
| 863 | 1016 | COMMIT; |
| 864 | 1017 | |
| 865 | 1018 | -- ---------------------------- |
| ... | ... | @@ -914,6 +1067,41 @@ CREATE TABLE `sys_user_session` ( |
| 914 | 1067 | -- Records of sys_user_session |
| 915 | 1068 | -- ---------------------------- |
| 916 | 1069 | BEGIN; |
| 1070 | +INSERT INTO `sys_user_session` VALUES ('04c6624c7bf14b1ba1a01cb976a9d876', 1, '2021-04-05 21:40:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 20:21:09', NULL, '2021-04-01 12:25:35', b'1'); | |
| 1071 | +INSERT INTO `sys_user_session` VALUES ('0e235ce5ae7342a09b372a00bd7d1b41', 1, '2021-04-05 01:43:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 00:51:03', NULL, '2021-04-01 04:18:06', b'1'); | |
| 1072 | +INSERT INTO `sys_user_session` VALUES ('0e6943f8ca9b4215a014843eb489ccc7', 1, '2021-04-05 22:53:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 21:43:59', NULL, '2021-04-01 17:25:41', b'1'); | |
| 1073 | +INSERT INTO `sys_user_session` VALUES ('12166cd28b4f448ea468d13c471dfc6e', 1, '2021-04-09 19:48:58', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-09 19:18:58', NULL, '2021-04-02 04:33:15', b'1'); | |
| 1074 | +INSERT INTO `sys_user_session` VALUES ('134d908ae33146bd9b5291471c04f604', 1, '2021-04-10 00:29:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-09 23:10:26', NULL, '2021-04-02 08:08:17', b'1'); | |
| 1075 | +INSERT INTO `sys_user_session` VALUES ('1407ce21e47947b9b8d93bff1b55c7d6', 1, '2021-04-07 01:09:11', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-07 00:07:43', NULL, '2021-04-01 18:10:44', b'1'); | |
| 1076 | +INSERT INTO `sys_user_session` VALUES ('1477c38290ff4cee8887ebfe593faa02', 1, '2021-04-17 23:34:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-17 23:04:57', NULL, '2021-04-12 21:36:29', b'1'); | |
| 1077 | +INSERT INTO `sys_user_session` VALUES ('3c75ea73e13b4857a18eb57ca2eea80f', 1, '2021-04-11 20:06:52', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 19:36:52', NULL, '2021-04-11 09:10:53', b'1'); | |
| 1078 | +INSERT INTO `sys_user_session` VALUES ('40d532d8900c43b791266429a7911751', 1, '2021-04-05 22:11:34', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 21:41:34', NULL, '2021-04-01 12:28:20', b'1'); | |
| 1079 | +INSERT INTO `sys_user_session` VALUES ('43676e85d0e04980b2a67181f8d9933b', 1, '2021-04-11 10:41:09', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 09:56:31', NULL, '2021-04-02 17:32:07', b'1'); | |
| 1080 | +INSERT INTO `sys_user_session` VALUES ('505b4e7d8b0d4b40aa23bf540da81234', 1, '2021-03-14 01:25:13', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 00:31:43', NULL, '2021-03-13 07:35:26', b'1'); | |
| 1081 | +INSERT INTO `sys_user_session` VALUES ('56a92b99f7b642ccbb7508042a25cb0c', 1, '2021-04-11 21:16:33', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 20:14:54', NULL, '2021-04-11 10:08:20', b'1'); | |
| 1082 | +INSERT INTO `sys_user_session` VALUES ('5a7248bf87d14e7e9f0578b05969986c', 1, '2021-03-13 10:42:50', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 09:37:36', NULL, '2021-03-12 19:53:07', b'1'); | |
| 1083 | +INSERT INTO `sys_user_session` VALUES ('5c30d80eb72048daa1a24d3d4f01317b', 1, '2021-04-12 00:31:10', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 23:08:14', NULL, '2021-04-11 15:47:45', b'1'); | |
| 1084 | +INSERT INTO `sys_user_session` VALUES ('5dca80a5c61541479a4dbb6e004c2e28', 1, '2021-04-14 00:57:25', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-13 23:38:30', NULL, '2021-04-12 17:33:48', b'1'); | |
| 1085 | +INSERT INTO `sys_user_session` VALUES ('7324a76b029a49ee95bf54ceb4164ba9', 1, '2021-04-13 01:29:14', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-13 00:18:40', NULL, '2021-04-11 23:15:00', b'1'); | |
| 1086 | +INSERT INTO `sys_user_session` VALUES ('749619894bc441bb9773902515f81e6a', 1, '2021-04-11 00:39:51', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 00:09:51', NULL, '2021-04-02 16:50:56', b'1'); | |
| 1087 | +INSERT INTO `sys_user_session` VALUES ('7768ae62ad974fd989f5159649a4be82', 1, '2021-04-11 00:53:39', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-10 23:23:27', NULL, '2021-04-02 17:04:58', b'1'); | |
| 1088 | +INSERT INTO `sys_user_session` VALUES ('79efcb8f64aa42af9f4b327fb383532f', 1, '2021-04-11 22:44:07', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 21:34:53', NULL, '2021-04-11 14:10:45', b'1'); | |
| 1089 | +INSERT INTO `sys_user_session` VALUES ('87d5b95fdad9447189a95abf8a5152df', 1, '2021-04-17 23:01:18', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-17 22:31:18', NULL, '2021-04-12 21:03:27', b'1'); | |
| 1090 | +INSERT INTO `sys_user_session` VALUES ('8b3eac5e4a104a4191c8070e03d553ea', 1, '2021-04-05 02:45:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 02:15:12', NULL, '2021-04-01 11:05:25', b'1'); | |
| 1091 | +INSERT INTO `sys_user_session` VALUES ('9ae27346d8b7491aad1385f51e8aa196', 1, '2021-03-13 14:02:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 10:43:06', NULL, '2021-03-13 06:40:35', b'1'); | |
| 1092 | +INSERT INTO `sys_user_session` VALUES ('a2fb443b31c049008975ff8ee5499db1', 1, '2021-04-11 09:42:09', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 09:12:09', NULL, '2021-04-02 17:16:00', b'1'); | |
| 1093 | +INSERT INTO `sys_user_session` VALUES ('a71a74adf9d141e2849d2a411d558205', 1, '2021-04-17 18:24:44', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-17 17:54:44', NULL, '2021-04-12 17:54:50', b'1'); | |
| 1094 | +INSERT INTO `sys_user_session` VALUES ('ae9ee7452ee54e4b983d658188c15c4d', 1, '2021-03-14 21:32:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 20:25:00', NULL, '2021-03-13 15:19:10', b'1'); | |
| 1095 | +INSERT INTO `sys_user_session` VALUES ('b727853eccea4c8589e006ffea985146', 1, '2021-04-12 01:36:00', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-12 01:06:00', NULL, '2021-04-11 19:40:07', b'1'); | |
| 1096 | +INSERT INTO `sys_user_session` VALUES ('c095616db95044c5bed66a3f84519b8b', 1, '2021-04-11 19:59:33', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 19:29:33', NULL, '2021-04-11 09:04:19', b'1'); | |
| 1097 | +INSERT INTO `sys_user_session` VALUES ('d0adf48f82914212b947e5ab04d9fb65', 1, '2021-03-21 19:16:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 18:13:37', NULL, '2021-03-15 05:53:20', b'1'); | |
| 1098 | +INSERT INTO `sys_user_session` VALUES ('dfbce0af867547f4bb01ac6f2e583337', 1, '2021-04-11 17:06:15', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 16:36:15', NULL, '2021-04-11 08:24:03', b'1'); | |
| 1099 | +INSERT INTO `sys_user_session` VALUES ('e5ecf10e40a5463b8f9b5b453cb1649b', 1, '2021-04-11 17:06:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 16:36:22', NULL, '2021-04-11 08:24:03', b'1'); | |
| 1100 | +INSERT INTO `sys_user_session` VALUES ('e80c2400724042a2ab73732166cde8fc', 1, '2021-03-21 21:17:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 20:47:12', NULL, '2021-03-15 08:18:56', b'1'); | |
| 1101 | +INSERT INTO `sys_user_session` VALUES ('e8872f5192584440a548641b83c877ef', 1, '2021-03-21 18:36:01', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 17:51:48', NULL, '2021-03-15 03:54:20', b'1'); | |
| 1102 | +INSERT INTO `sys_user_session` VALUES ('f1ab99b09b5a475795579ff99d60ac78', 1, '2021-03-14 23:04:31', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 21:12:44', NULL, '2021-03-15 03:32:38', b'1'); | |
| 1103 | +INSERT INTO `sys_user_session` VALUES ('f2a4182f62ea4559b9946a523928fac4', 1, '2021-04-10 01:45:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-10 00:55:38', NULL, '2021-04-02 14:52:48', b'1'); | |
| 1104 | +INSERT INTO `sys_user_session` VALUES ('f853b50d064340a581e9a49bba9411fc', 1, '2021-03-10 01:55:41', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-10 01:11:53', NULL, '2021-03-12 18:37:05', b'1'); | |
| 917 | 1105 | COMMIT; |
| 918 | 1106 | |
| 919 | 1107 | -- ---------------------------- |
| ... | ... | @@ -946,7 +1134,7 @@ CREATE TABLE `tool_codegen_column` ( |
| 946 | 1134 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 947 | 1135 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 948 | 1136 | PRIMARY KEY (`id`) USING BTREE |
| 949 | -) ENGINE=InnoDB AUTO_INCREMENT=381 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å—段定义'; | |
| 1137 | +) ENGINE=InnoDB AUTO_INCREMENT=447 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å—段定义'; | |
| 950 | 1138 | |
| 951 | 1139 | -- ---------------------------- |
| 952 | 1140 | -- Records of tool_codegen_column |
| ... | ... | @@ -1100,6 +1288,72 @@ INSERT INTO `tool_codegen_column` VALUES (377, 30, 'create_time', 'datetime', 'å |
| 1100 | 1288 | INSERT INTO `tool_codegen_column` VALUES (378, 30, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 10, 'String', 'updateBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); |
| 1101 | 1289 | INSERT INTO `tool_codegen_column` VALUES (379, 30, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 11, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); |
| 1102 | 1290 | INSERT INTO `tool_codegen_column` VALUES (380, 30, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 12, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); |
| 1291 | +INSERT INTO `tool_codegen_column` VALUES (381, 33, 'id', 'varchar(188)', '文件路径', b'0', b'1', '0', 1, 'String', 'id', '', NULL, b'0', b'0', b'1', 'LIKE', b'1', 'input', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1292 | +INSERT INTO `tool_codegen_column` VALUES (382, 33, 'type', 'varchar(63)', '文件类型', b'1', b'0', '0', 2, 'String', 'type', '', NULL, b'1', b'0', b'1', 'LIKE', b'1', 'select', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1293 | +INSERT INTO `tool_codegen_column` VALUES (383, 33, 'content', 'blob', '文件内容', b'0', b'0', '0', 3, 'byte[]', 'content', '', NULL, b'1', b'0', b'0', '=', b'1', 'fileUpload', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1294 | +INSERT INTO `tool_codegen_column` VALUES (384, 33, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 4, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1295 | +INSERT INTO `tool_codegen_column` VALUES (385, 33, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 5, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1296 | +INSERT INTO `tool_codegen_column` VALUES (386, 33, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 6, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1297 | +INSERT INTO `tool_codegen_column` VALUES (387, 33, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 7, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1298 | +INSERT INTO `tool_codegen_column` VALUES (388, 33, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 8, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1299 | +INSERT INTO `tool_codegen_column` VALUES (389, 34, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1300 | +INSERT INTO `tool_codegen_column` VALUES (390, 34, 'signature', 'varchar(8)', 'çŸä¿¡ç¾å', b'0', b'0', '0', 2, 'String', 'signature', '', 'èŠ‹é“æºç ', b'1', b'1', b'1', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1301 | +INSERT INTO `tool_codegen_column` VALUES (391, 34, 'code', 'varchar(63)', 'æ¸ é“ç¼–ç ', b'0', b'0', '0', 3, 'String', 'code', 'sys_sms_channel_code', 'YUN_PIAN', b'1', b'0', b'0', '=', b'1', 'select', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1302 | +INSERT INTO `tool_codegen_column` VALUES (392, 34, 'status', 'tinyint(4)', 'å¯ç”¨çжæ€', b'0', b'0', '0', 4, 'Integer', 'status', 'sys_common_status', '1', b'1', b'1', b'1', '=', b'1', 'radio', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1303 | +INSERT INTO `tool_codegen_column` VALUES (393, 34, 'remark', 'varchar(255)', '备注', b'1', b'0', '0', 5, 'String', 'remark', '', '好åƒï¼', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1304 | +INSERT INTO `tool_codegen_column` VALUES (394, 34, 'api_key', 'varchar(63)', 'çŸä¿¡ API 的账å·', b'0', b'0', '0', 6, 'String', 'apiKey', '', 'yudao', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1305 | +INSERT INTO `tool_codegen_column` VALUES (395, 34, 'api_secret', 'varchar(63)', 'çŸä¿¡ API 的秘钥', b'1', b'0', '0', 7, 'String', 'apiSecret', '', 'yuanma', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1306 | +INSERT INTO `tool_codegen_column` VALUES (396, 34, 'callback_url', 'varchar(255)', 'çŸä¿¡å‘é€å›žè°ƒ URL', b'1', b'0', '0', 8, 'String', 'callbackUrl', '', 'http://www.iocoder.cn', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1307 | +INSERT INTO `tool_codegen_column` VALUES (397, 34, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 9, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1308 | +INSERT INTO `tool_codegen_column` VALUES (398, 34, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 10, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1309 | +INSERT INTO `tool_codegen_column` VALUES (399, 34, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 11, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1310 | +INSERT INTO `tool_codegen_column` VALUES (400, 34, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 12, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1311 | +INSERT INTO `tool_codegen_column` VALUES (401, 34, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 13, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | |
| 1312 | +INSERT INTO `tool_codegen_column` VALUES (402, 35, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1313 | +INSERT INTO `tool_codegen_column` VALUES (403, 35, 'type', 'tinyint(4)', 'çŸä¿¡ç¾å', b'0', b'0', '0', 2, 'Integer', 'type', 'sys_sms_template_type', '1', b'1', b'1', b'1', '=', b'1', 'select', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1314 | +INSERT INTO `tool_codegen_column` VALUES (404, 35, 'status', 'tinyint(4)', 'å¼€å¯çжæ€', b'0', b'0', '0', 3, 'Integer', 'status', 'sys_common_status', '1', b'1', b'1', b'1', '=', b'1', 'radio', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1315 | +INSERT INTO `tool_codegen_column` VALUES (405, 35, 'code', 'varchar(63)', '模æ¿ç¼–ç ', b'0', b'0', '0', 4, 'String', 'code', '', 'test_01', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1316 | +INSERT INTO `tool_codegen_column` VALUES (406, 35, 'name', 'varchar(63)', '模æ¿åç§°', b'0', b'0', '0', 5, 'String', 'name', '', 'yudao', b'1', b'1', b'0', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1317 | +INSERT INTO `tool_codegen_column` VALUES (407, 35, 'content', 'varchar(255)', '模æ¿å†…容', b'0', b'0', '0', 6, 'String', 'content', '', 'ä½ å¥½ï¼Œ{name}ã€‚ä½ é•¿çš„å¤ª{like}啦ï¼', b'1', b'1', b'1', 'LIKE', b'1', 'editor', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1318 | +INSERT INTO `tool_codegen_column` VALUES (408, 35, 'params', 'varchar(255)', '傿•°æ•°ç»„', b'0', b'0', '0', 7, 'String', 'params', '', 'name,code', b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1319 | +INSERT INTO `tool_codegen_column` VALUES (409, 35, 'remark', 'varchar(255)', '备注', b'1', b'0', '0', 8, 'String', 'remark', '', '哈哈哈', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1320 | +INSERT INTO `tool_codegen_column` VALUES (410, 35, 'api_template_id', 'varchar(63)', 'çŸä¿¡ API 的模æ¿ç¼–å·', b'0', b'0', '0', 9, 'String', 'apiTemplateId', '', '4383920', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1321 | +INSERT INTO `tool_codegen_column` VALUES (411, 35, 'channel_id', 'bigint(20)', 'çŸä¿¡æ¸ é“ç¼–å·', b'0', b'0', '0', 10, 'Long', 'channelId', '', '10', b'1', b'1', b'1', '=', b'1', 'select', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1322 | +INSERT INTO `tool_codegen_column` VALUES (412, 35, 'channel_code', 'varchar(63)', 'çŸä¿¡æ¸ é“ç¼–ç ', b'0', b'0', '0', 11, 'String', 'channelCode', 'sys_sms_channel_code', 'ALIYUN', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1323 | +INSERT INTO `tool_codegen_column` VALUES (413, 35, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 12, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1324 | +INSERT INTO `tool_codegen_column` VALUES (414, 35, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 13, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1325 | +INSERT INTO `tool_codegen_column` VALUES (415, 35, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 14, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1326 | +INSERT INTO `tool_codegen_column` VALUES (416, 35, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 15, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1327 | +INSERT INTO `tool_codegen_column` VALUES (417, 35, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 16, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | |
| 1328 | +INSERT INTO `tool_codegen_column` VALUES (418, 36, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1329 | +INSERT INTO `tool_codegen_column` VALUES (419, 36, 'channel_id', 'bigint(20)', 'çŸä¿¡æ¸ é“ç¼–å·', b'0', b'0', '0', 2, 'Long', 'channelId', '', '10', b'0', b'0', b'1', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1330 | +INSERT INTO `tool_codegen_column` VALUES (420, 36, 'channel_code', 'varchar(63)', 'çŸä¿¡æ¸ é“ç¼–ç ', b'0', b'0', '0', 3, 'String', 'channelCode', '', 'ALIYUN', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1331 | +INSERT INTO `tool_codegen_column` VALUES (421, 36, 'template_id', 'bigint(20)', '模æ¿ç¼–å·', b'0', b'0', '0', 4, 'Long', 'templateId', '', '20', b'0', b'0', b'1', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1332 | +INSERT INTO `tool_codegen_column` VALUES (422, 36, 'template_code', 'varchar(63)', '模æ¿ç¼–ç ', b'0', b'0', '0', 5, 'String', 'templateCode', '', 'test-01', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1333 | +INSERT INTO `tool_codegen_column` VALUES (423, 36, 'template_type', 'tinyint(4)', 'çŸä¿¡ç±»åž‹', b'0', b'0', '0', 6, 'Integer', 'templateType', 'sys_sms_template_type', '1', b'0', b'0', b'0', '=', b'1', 'select', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1334 | +INSERT INTO `tool_codegen_column` VALUES (424, 36, 'template_content', 'varchar(255)', 'çŸä¿¡å†…容', b'0', b'0', '0', 7, 'String', 'templateContent', '', 'ä½ å¥½ï¼Œä½ çš„éªŒè¯ç 是 1024', b'0', b'0', b'0', '=', b'1', 'editor', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1335 | +INSERT INTO `tool_codegen_column` VALUES (425, 36, 'template_params', 'varchar(255)', 'çŸä¿¡å‚æ•°', b'0', b'0', '0', 8, 'String', 'templateParams', '', 'name,code', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1336 | +INSERT INTO `tool_codegen_column` VALUES (426, 36, 'api_template_id', 'varchar(63)', 'çŸä¿¡ API 的模æ¿ç¼–å·', b'0', b'0', '0', 9, 'String', 'apiTemplateId', '', 'SMS_207945135', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1337 | +INSERT INTO `tool_codegen_column` VALUES (427, 36, 'mobile', 'varchar(11)', '手机å·', b'0', b'0', '0', 10, 'String', 'mobile', '', '15601691300', b'0', b'0', b'1', 'LIKE', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1338 | +INSERT INTO `tool_codegen_column` VALUES (428, 36, 'user_id', 'bigint(20)', '用户编å·', b'1', b'0', '0', 11, 'Long', 'userId', '', '10', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1339 | +INSERT INTO `tool_codegen_column` VALUES (429, 36, 'user_type', 'tinyint(4)', '用户类型', b'1', b'0', '0', 12, 'Integer', 'userType', 'user_type', '1', b'0', b'0', b'0', '=', b'1', 'select', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1340 | +INSERT INTO `tool_codegen_column` VALUES (430, 36, 'send_status', 'tinyint(4)', 'å‘é€çжæ€', b'0', b'0', '0', 13, 'Integer', 'sendStatus', 'sys_sms_send_status', '1', b'0', b'0', b'1', '=', b'1', 'radio', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1341 | +INSERT INTO `tool_codegen_column` VALUES (431, 36, 'send_time', 'datetime', 'å‘逿—¶é—´', b'1', b'0', '0', 14, 'Date', 'sendTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1342 | +INSERT INTO `tool_codegen_column` VALUES (432, 36, 'send_code', 'int(11)', 'å‘é€ç»“果的编ç ', b'1', b'0', '0', 15, 'Integer', 'sendCode', '', '0', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1343 | +INSERT INTO `tool_codegen_column` VALUES (433, 36, 'send_msg', 'varchar(255)', 'å‘é€ç»“果的æç¤º', b'1', b'0', '0', 16, 'String', 'sendMsg', '', 'æˆåŠŸ', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1344 | +INSERT INTO `tool_codegen_column` VALUES (434, 36, 'api_send_code', 'varchar(63)', 'çŸä¿¡ API å‘é€ç»“果的编ç ', b'1', b'0', '0', 17, 'String', 'apiSendCode', '', 'SUCCESS', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1345 | +INSERT INTO `tool_codegen_column` VALUES (435, 36, 'api_send_msg', 'varchar(255)', 'çŸä¿¡ API å‘é€å¤±è´¥çš„æç¤º', b'1', b'0', '0', 18, 'String', 'apiSendMsg', '', 'æˆåŠŸ', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1346 | +INSERT INTO `tool_codegen_column` VALUES (436, 36, 'api_request_id', 'varchar(255)', 'çŸä¿¡ API å‘é€è¿”回的唯一请求 ID', b'1', b'0', '0', 19, 'String', 'apiRequestId', '', '3837C6D3-B96F-428C-BBB2-86135D4B5B99', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1347 | +INSERT INTO `tool_codegen_column` VALUES (437, 36, 'api_serial_no', 'varchar(255)', 'çŸä¿¡ API å‘é€è¿”回的åºå·', b'1', b'0', '0', 20, 'String', 'apiSerialNo', '', '62923244790', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1348 | +INSERT INTO `tool_codegen_column` VALUES (438, 36, 'receive_status', 'tinyint(4)', '接收状æ€', b'0', b'0', '0', 21, 'Integer', 'receiveStatus', 'sys_sms_receive_status', '0', b'0', b'0', b'1', '=', b'1', 'radio', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1349 | +INSERT INTO `tool_codegen_column` VALUES (439, 36, 'receive_time', 'datetime', '接收时间', b'1', b'0', '0', 22, 'Date', 'receiveTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1350 | +INSERT INTO `tool_codegen_column` VALUES (440, 36, 'api_receive_code', 'varchar(63)', 'API 接收结果的编ç ', b'1', b'0', '0', 23, 'String', 'apiReceiveCode', '', 'DELIVRD', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1351 | +INSERT INTO `tool_codegen_column` VALUES (441, 36, 'api_receive_msg', 'varchar(255)', 'API 接收结果的说明', b'1', b'0', '0', 24, 'String', 'apiReceiveMsg', '', 'DELIVRD', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1352 | +INSERT INTO `tool_codegen_column` VALUES (442, 36, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 25, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1353 | +INSERT INTO `tool_codegen_column` VALUES (443, 36, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 26, 'Date', 'createTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'1', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1354 | +INSERT INTO `tool_codegen_column` VALUES (444, 36, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 27, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1355 | +INSERT INTO `tool_codegen_column` VALUES (445, 36, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 28, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1356 | +INSERT INTO `tool_codegen_column` VALUES (446, 36, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 29, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1103 | 1357 | COMMIT; |
| 1104 | 1358 | |
| 1105 | 1359 | -- ---------------------------- |
| ... | ... | @@ -1125,7 +1379,7 @@ CREATE TABLE `tool_codegen_table` ( |
| 1125 | 1379 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 1126 | 1380 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 1127 | 1381 | PRIMARY KEY (`id`) USING BTREE |
| 1128 | -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å®šä¹‰'; | |
| 1382 | +) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å®šä¹‰'; | |
| 1129 | 1383 | |
| 1130 | 1384 | -- ---------------------------- |
| 1131 | 1385 | -- Records of tool_codegen_table |
| ... | ... | @@ -1142,6 +1396,10 @@ INSERT INTO `tool_codegen_table` VALUES (27, 1, 'inf_api_error_log', 'API 错误 |
| 1142 | 1396 | INSERT INTO `tool_codegen_table` VALUES (28, 1, 'sys_dict_type', 'å—典类型表', NULL, 'system', 'dictType', 'SysDictType', 'å—典类型', '芋艿', 1, NULL, '', '2021-03-06 03:45:55', '', '2021-03-06 03:51:02', b'1'); |
| 1143 | 1397 | INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', 'å—典类型表', NULL, 'system', 'dict', 'SysDictType', 'å—典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0'); |
| 1144 | 1398 | INSERT INTO `tool_codegen_table` VALUES (30, 1, 'sys_dict_data', 'å—典数æ®è¡¨', NULL, 'system', 'type', 'SysDictData', 'å—典数æ®', 'èŠ‹é“æºç ', 1, NULL, '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:47', b'0'); |
| 1399 | +INSERT INTO `tool_codegen_table` VALUES (33, 1, 'inf_file', '文件表', NULL, 'infra', 'file', 'InfFile', '文件', '芋艿', 1, 2, '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | |
| 1400 | +INSERT INTO `tool_codegen_table` VALUES (34, 1, 'sys_sms_channel', 'çŸä¿¡æ¸ é“', NULL, 'system', 'sms', 'SysSmsChannel', 'çŸä¿¡æ¸ é“', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-03 13:39:06', '1', '2021-04-05 20:52:09', b'0'); | |
| 1401 | +INSERT INTO `tool_codegen_table` VALUES (35, 1, 'sys_sms_template', 'çŸä¿¡æ¨¡æ¿', NULL, 'system', 'sms', 'SysSmsTemplate', 'çŸä¿¡æ¨¡æ¿', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-03 13:58:55', '1', '2021-04-05 22:23:38', b'0'); | |
| 1402 | +INSERT INTO `tool_codegen_table` VALUES (36, 1, 'sys_sms_log', 'çŸä¿¡æ—¥å¿—', NULL, 'system', 'sms', 'SysSmsLog', 'çŸä¿¡æ—¥å¿—', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-11 01:12:57', '1', '2021-04-11 20:33:54', b'0'); | |
| 1145 | 1403 | COMMIT; |
| 1146 | 1404 | |
| 1147 | 1405 | -- ---------------------------- | ... | ... |
src/main/java/cn/iocoder/dashboard/common/core/KeyValue.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.common.core; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.NoArgsConstructor; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * Key Value 的键值对 | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +@Data | |
| 13 | +@NoArgsConstructor | |
| 14 | +@AllArgsConstructor | |
| 15 | +public class KeyValue<K, V> { | |
| 16 | + | |
| 17 | + private K key; | |
| 18 | + private V value; | |
| 19 | + | |
| 20 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.common.enums; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 通用状态枚举 | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@Getter | |
| 12 | +@AllArgsConstructor | |
| 13 | +public enum DefaultBitFieldEnum { | |
| 14 | + | |
| 15 | + NO(0, "否"), | |
| 16 | + YES(1, "是"); | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 状态值 | |
| 20 | + */ | |
| 21 | + private final Integer val; | |
| 22 | + /** | |
| 23 | + * 状态名 | |
| 24 | + */ | |
| 25 | + private final String name; | |
| 26 | + | |
| 27 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/common/exception/ErrorCode.java
| 1 | 1 | package cn.iocoder.dashboard.common.exception; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 3 | 4 | import cn.iocoder.dashboard.common.exception.enums.ServiceErrorCodeRange; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| 6 | 7 | /** |
| 7 | 8 | * 错误码对象 |
| 8 | 9 | * |
| 9 | - * 全局错误码,占用 [0, 999],参见 {@link GlobalException} | |
| 10 | + * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants} | |
| 10 | 11 | * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange} |
| 11 | 12 | * |
| 12 | 13 | * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 |
| ... | ... | @@ -21,11 +22,11 @@ public class ErrorCode { |
| 21 | 22 | /** |
| 22 | 23 | * 错误提示 |
| 23 | 24 | */ |
| 24 | - private final String message; | |
| 25 | + private final String msg; | |
| 25 | 26 | |
| 26 | 27 | public ErrorCode(Integer code, String message) { |
| 27 | 28 | this.code = code; |
| 28 | - this.message = message; | |
| 29 | + this.msg = message; | |
| 29 | 30 | } |
| 30 | 31 | |
| 31 | 32 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/common/exception/GlobalException.java deleted
| 1 | -package cn.iocoder.dashboard.common.exception; | |
| 2 | - | |
| 3 | -import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 4 | -import lombok.Data; | |
| 5 | -import lombok.EqualsAndHashCode; | |
| 6 | - | |
| 7 | -/** | |
| 8 | - * 全局异常 Exception | |
| 9 | - */ | |
| 10 | -@Data | |
| 11 | -@EqualsAndHashCode(callSuper = true) | |
| 12 | -public class GlobalException extends RuntimeException { | |
| 13 | - | |
| 14 | - /** | |
| 15 | - * 全局错误码 | |
| 16 | - * | |
| 17 | - * @see GlobalErrorCodeConstants | |
| 18 | - */ | |
| 19 | - private Integer code; | |
| 20 | - /** | |
| 21 | - * 错误提示 | |
| 22 | - */ | |
| 23 | - private String message; | |
| 24 | - | |
| 25 | - /** | |
| 26 | - * 空构造方法,避免反序列化问题 | |
| 27 | - */ | |
| 28 | - public GlobalException() { | |
| 29 | - } | |
| 30 | - | |
| 31 | - public GlobalException(ErrorCode errorCode) { | |
| 32 | - this.code = errorCode.getCode(); | |
| 33 | - this.message = errorCode.getMessage(); | |
| 34 | - } | |
| 35 | - | |
| 36 | - public GlobalException(Integer code, String message) { | |
| 37 | - this.code = code; | |
| 38 | - this.message = message; | |
| 39 | - } | |
| 40 | - | |
| 41 | -} |
src/main/java/cn/iocoder/dashboard/common/exception/ServiceException.java
| ... | ... | @@ -30,7 +30,7 @@ public final class ServiceException extends RuntimeException { |
| 30 | 30 | |
| 31 | 31 | public ServiceException(ErrorCode errorCode) { |
| 32 | 32 | this.code = errorCode.getCode(); |
| 33 | - this.message = errorCode.getMessage(); | |
| 33 | + this.message = errorCode.getMsg(); | |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | public ServiceException(Integer code, String message) { | ... | ... |
src/main/java/cn/iocoder/dashboard/common/exception/enums/GlobalErrorCodeConstants.java
| ... | ... | @@ -32,6 +32,7 @@ public interface GlobalErrorCodeConstants { |
| 32 | 32 | |
| 33 | 33 | // ========== 自定义错误段 ========== |
| 34 | 34 | ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求 |
| 35 | + ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作"); | |
| 35 | 36 | |
| 36 | 37 | ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); |
| 37 | 38 | ... | ... |
src/main/java/cn/iocoder/dashboard/common/exception/util/ServiceExceptionUtil.java
| ... | ... | @@ -47,12 +47,12 @@ public class ServiceExceptionUtil { |
| 47 | 47 | // ========== 和 ServiceException 的集成 ========== |
| 48 | 48 | |
| 49 | 49 | public static ServiceException exception(ErrorCode errorCode) { |
| 50 | - String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage()); | |
| 50 | + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg()); | |
| 51 | 51 | return exception0(errorCode.getCode(), messagePattern); |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | public static ServiceException exception(ErrorCode errorCode, Object... params) { |
| 55 | - String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage()); | |
| 55 | + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg()); | |
| 56 | 56 | return exception0(errorCode.getCode(), messagePattern, params); |
| 57 | 57 | } |
| 58 | 58 | ... | ... |
src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
| 1 | 1 | package cn.iocoder.dashboard.common.pojo; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.common.exception.ErrorCode; |
| 4 | -import cn.iocoder.dashboard.common.exception.GlobalException; | |
| 5 | 4 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 6 | 5 | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; |
| 7 | 6 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| ... | ... | @@ -9,6 +8,7 @@ import lombok.Data; |
| 9 | 8 | import org.springframework.util.Assert; |
| 10 | 9 | |
| 11 | 10 | import java.io.Serializable; |
| 11 | +import java.util.Objects; | |
| 12 | 12 | |
| 13 | 13 | /** |
| 14 | 14 | * 通用返回 |
| ... | ... | @@ -16,7 +16,7 @@ import java.io.Serializable; |
| 16 | 16 | * @param <T> 数据泛型 |
| 17 | 17 | */ |
| 18 | 18 | @Data |
| 19 | -public final class CommonResult<T> implements Serializable { | |
| 19 | +public class CommonResult<T> implements Serializable { | |
| 20 | 20 | |
| 21 | 21 | /** |
| 22 | 22 | * 错误码 |
| ... | ... | @@ -31,7 +31,7 @@ public final class CommonResult<T> implements Serializable { |
| 31 | 31 | /** |
| 32 | 32 | * 错误提示,用户可阅读 |
| 33 | 33 | * |
| 34 | - * @see ErrorCode#getMessage() () | |
| 34 | + * @see ErrorCode#getMsg() () | |
| 35 | 35 | */ |
| 36 | 36 | private String msg; |
| 37 | 37 | |
| ... | ... | @@ -57,7 +57,7 @@ public final class CommonResult<T> implements Serializable { |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | 59 | public static <T> CommonResult<T> error(ErrorCode errorCode) { |
| 60 | - return error(errorCode.getCode(), errorCode.getMessage()); | |
| 60 | + return error(errorCode.getCode(), errorCode.getMsg()); | |
| 61 | 61 | } |
| 62 | 62 | |
| 63 | 63 | public static <T> CommonResult<T> success(T data) { |
| ... | ... | @@ -68,9 +68,13 @@ public final class CommonResult<T> implements Serializable { |
| 68 | 68 | return result; |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | + public static boolean isSuccess(Integer code) { | |
| 72 | + return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode()); | |
| 73 | + } | |
| 74 | + | |
| 71 | 75 | @JsonIgnore // 避免 jackson 序列化 |
| 72 | 76 | public boolean isSuccess() { |
| 73 | - return GlobalErrorCodeConstants.SUCCESS.getCode().equals(code); | |
| 77 | + return isSuccess(code); | |
| 74 | 78 | } |
| 75 | 79 | |
| 76 | 80 | @JsonIgnore // 避免 jackson 序列化 |
| ... | ... | @@ -81,16 +85,12 @@ public final class CommonResult<T> implements Serializable { |
| 81 | 85 | // ========= 和 Exception 异常体系集成 ========= |
| 82 | 86 | |
| 83 | 87 | /** |
| 84 | - * 判断是否有异常。如果有,则抛出 {@link GlobalException} 或 {@link ServiceException} 异常 | |
| 88 | + * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 | |
| 85 | 89 | */ |
| 86 | - public void checkError() throws GlobalException, ServiceException { | |
| 90 | + public void checkError() throws ServiceException { | |
| 87 | 91 | if (isSuccess()) { |
| 88 | 92 | return; |
| 89 | 93 | } |
| 90 | - // 全局异常 | |
| 91 | - if (GlobalErrorCodeConstants.isMatch(code)) { | |
| 92 | - throw new GlobalException(code, msg); | |
| 93 | - } | |
| 94 | 94 | // 业务异常 |
| 95 | 95 | throw new ServiceException(code, msg); |
| 96 | 96 | } |
| ... | ... | @@ -99,8 +99,4 @@ public final class CommonResult<T> implements Serializable { |
| 99 | 99 | return error(serviceException.getCode(), serviceException.getMessage()); |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | - public static <T> CommonResult<T> error(GlobalException globalException) { | |
| 103 | - return error(globalException.getCode(), globalException.getMessage()); | |
| 104 | - } | |
| 105 | - | |
| 106 | 102 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/datasource/config/DataSourceConfiguration.java
| 1 | 1 | package cn.iocoder.dashboard.framework.datasource.config; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.datasource.core.filter.DruidAdRemoveFilter; | |
| 4 | +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; | |
| 5 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 6 | +import org.springframework.boot.web.servlet.FilterRegistrationBean; | |
| 7 | +import org.springframework.context.annotation.Bean; | |
| 3 | 8 | import org.springframework.context.annotation.Configuration; |
| 4 | 9 | import org.springframework.transaction.annotation.EnableTransactionManagement; |
| 5 | 10 | |
| 6 | 11 | /** |
| 7 | - * 数据库匹配类 | |
| 12 | + * 数据库配置类 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 8 | 15 | */ |
| 9 | 16 | @Configuration |
| 10 | 17 | @EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理 |
| 11 | 18 | public class DataSourceConfiguration { |
| 19 | + | |
| 20 | + /** | |
| 21 | + * 创建 DruidAdRemoveFilter 过滤器,过滤 common.js 的广告 | |
| 22 | + */ | |
| 23 | + @Bean | |
| 24 | + @ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true") | |
| 25 | + public FilterRegistrationBean<DruidAdRemoveFilter> druidAdRemoveFilterFilter(DruidStatProperties properties) { | |
| 26 | + // 获取 druid web 监控页面的参数 | |
| 27 | + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); | |
| 28 | + // 提取 common.js 的配置路径 | |
| 29 | + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; | |
| 30 | + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); | |
| 31 | + // 创建 DruidAdRemoveFilter Bean | |
| 32 | + FilterRegistrationBean<DruidAdRemoveFilter> registrationBean = new FilterRegistrationBean<>(); | |
| 33 | + registrationBean.setFilter(new DruidAdRemoveFilter()); | |
| 34 | + registrationBean.addUrlPatterns(commonJsPattern); | |
| 35 | + return registrationBean; | |
| 36 | + } | |
| 37 | + | |
| 12 | 38 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/datasource/core/enums/DataSourceEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.datasource.core.enums; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * 对应于多数据源中不同数据源配置 | |
| 5 | + * | |
| 6 | + * 通过在方法上,使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解,设置使用的数据源。 | |
| 7 | + * 注意,默认是 {@link #MASTER} 数据源 | |
| 8 | + * | |
| 9 | + * 对应官方文档为 http://dynamic-datasource.com/guide/customize/Annotation.html | |
| 10 | + */ | |
| 11 | +public interface DataSourceEnum { | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 主库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Master} 注解 | |
| 15 | + */ | |
| 16 | + String MASTER = "master"; | |
| 17 | + /** | |
| 18 | + * 从库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Slave} 注解 | |
| 19 | + */ | |
| 20 | + String SLAVE = "slave"; | |
| 21 | + | |
| 22 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/datasource/core/filter/DruidAdRemoveFilter.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.datasource.core.filter; | |
| 2 | + | |
| 3 | +import com.alibaba.druid.util.Utils; | |
| 4 | +import org.springframework.web.filter.OncePerRequestFilter; | |
| 5 | + | |
| 6 | +import javax.servlet.FilterChain; | |
| 7 | +import javax.servlet.ServletException; | |
| 8 | +import javax.servlet.http.HttpServletRequest; | |
| 9 | +import javax.servlet.http.HttpServletResponse; | |
| 10 | +import java.io.IOException; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * Druid 底部广告过滤器 | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +public class DruidAdRemoveFilter extends OncePerRequestFilter { | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * common.js 的路径 | |
| 21 | + */ | |
| 22 | + private static final String COMMON_JS_ILE_PATH = "support/http/resources/js/common.js"; | |
| 23 | + | |
| 24 | + @Override | |
| 25 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | |
| 26 | + throws ServletException, IOException { | |
| 27 | + chain.doFilter(request, response); | |
| 28 | + // 重置缓冲区,响应头不会被重置 | |
| 29 | + response.resetBuffer(); | |
| 30 | + // 获取 common.js | |
| 31 | + String text = Utils.readFromResource(COMMON_JS_ILE_PATH); | |
| 32 | + // 正则替换 banner, 除去底部的广告信息 | |
| 33 | + text = text.replaceAll("<a.*?banner\"></a><br/>", ""); | |
| 34 | + text = text.replaceAll("powered.*?shrek.wang</a>", ""); | |
| 35 | + response.getWriter().write(text); | |
| 36 | + } | |
| 37 | + | |
| 38 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/excel/core/convert/DictConvert.java
| ... | ... | @@ -13,6 +13,11 @@ import com.alibaba.excel.metadata.GlobalConfiguration; |
| 13 | 13 | import com.alibaba.excel.metadata.property.ExcelContentProperty; |
| 14 | 14 | import lombok.extern.slf4j.Slf4j; |
| 15 | 15 | |
| 16 | +/** | |
| 17 | + * Excel {@link SysDictDataDO} 数据字典转换器 | |
| 18 | + * | |
| 19 | + * @author 芋道源码 | |
| 20 | + */ | |
| 16 | 21 | @Slf4j |
| 17 | 22 | public class DictConvert implements Converter<Object> { |
| 18 | 23 | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/excel/core/convert/JsonConvert.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.excel.core.convert; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 4 | +import com.alibaba.excel.converters.Converter; | |
| 5 | +import com.alibaba.excel.enums.CellDataTypeEnum; | |
| 6 | +import com.alibaba.excel.metadata.CellData; | |
| 7 | +import com.alibaba.excel.metadata.GlobalConfiguration; | |
| 8 | +import com.alibaba.excel.metadata.property.ExcelContentProperty; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * Excel Json 转换器 | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +public class JsonConvert implements Converter<Object> { | |
| 16 | + | |
| 17 | + @Override | |
| 18 | + public Class<?> supportJavaTypeKey() { | |
| 19 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | |
| 20 | + } | |
| 21 | + | |
| 22 | + @Override | |
| 23 | + public CellDataTypeEnum supportExcelTypeKey() { | |
| 24 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | |
| 25 | + } | |
| 26 | + | |
| 27 | + @Override | |
| 28 | + public Object convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { | |
| 29 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | |
| 30 | + } | |
| 31 | + | |
| 32 | + @Override | |
| 33 | + public CellData<String> convertToExcelData(Object value, ExcelContentProperty contentProperty, | |
| 34 | + GlobalConfiguration globalConfiguration) { | |
| 35 | + // 生成 Excel 小表格 | |
| 36 | + return new CellData<>(JsonUtils.toJsonString(value)); | |
| 37 | + } | |
| 38 | + | |
| 39 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/file/config/FileProperties.java
| 1 | 1 | package cn.iocoder.dashboard.framework.file.config; |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.modules.system.controller.common.SysFileController; | |
| 3 | +import cn.iocoder.dashboard.modules.infra.controller.file.InfFileController; | |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | import org.springframework.boot.context.properties.ConfigurationProperties; |
| 6 | 6 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull; |
| 13 | 13 | public class FileProperties { |
| 14 | 14 | |
| 15 | 15 | /** |
| 16 | - * 对应 {@link SysFileController#} | |
| 16 | + * 对应 {@link InfFileController#} | |
| 17 | 17 | */ |
| 18 | 18 | @NotNull(message = "基础文件路径不能为空") |
| 19 | 19 | private String basePath; | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/jackson/config/JacksonConfig.java
| 1 | 1 | package cn.iocoder.dashboard.framework.jackson.config; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.jackson.deser.LocalDateTimeDeserializer; | |
| 4 | +import cn.iocoder.dashboard.framework.jackson.ser.LocalDateTimeSerializer; | |
| 3 | 5 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 4 | 6 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 7 | +import com.fasterxml.jackson.databind.module.SimpleModule; | |
| 5 | 8 | import org.springframework.context.annotation.Bean; |
| 6 | 9 | import org.springframework.context.annotation.Configuration; |
| 7 | 10 | |
| 11 | +import java.time.LocalDateTime; | |
| 12 | + | |
| 8 | 13 | @Configuration |
| 9 | 14 | public class JacksonConfig { |
| 10 | 15 | |
| 11 | 16 | @Bean |
| 12 | 17 | @SuppressWarnings("InstantiationOfUtilityClass") |
| 13 | 18 | public JsonUtils jsonUtils(ObjectMapper objectMapper) { |
| 19 | + SimpleModule simpleModule = new SimpleModule(); | |
| 20 | + /* | |
| 21 | + * 1. 新增Long类型序列化规则,数值超过2^53-1,在JS会出现精度丢失问题,因此Long自动序列化为字符串类型 | |
| 22 | + * 2. 新增LocalDateTime序列化、反序列化规则 | |
| 23 | + */ | |
| 24 | + simpleModule | |
| 25 | +// .addSerializer(Long.class, ToStringSerializer.instance) | |
| 26 | +// .addSerializer(Long.TYPE, ToStringSerializer.instance) | |
| 27 | + .addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE) | |
| 28 | + .addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE); | |
| 29 | + | |
| 30 | + objectMapper.registerModules(simpleModule); | |
| 31 | + | |
| 14 | 32 | JsonUtils.init(objectMapper); |
| 15 | 33 | return new JsonUtils(); |
| 16 | 34 | } |
| 17 | - | |
| 18 | 35 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/jackson/deser/LocalDateTimeDeserializer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.jackson.deser; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.core.JsonParser; | |
| 4 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
| 5 | +import com.fasterxml.jackson.databind.DeserializationContext; | |
| 6 | +import com.fasterxml.jackson.databind.JsonDeserializer; | |
| 7 | + | |
| 8 | +import java.io.IOException; | |
| 9 | +import java.time.Instant; | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | +import java.time.ZoneId; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * LocalDateTime反序列化规则 | |
| 15 | + * <p> | |
| 16 | + * 会将毫秒级时间戳反序列化为LocalDateTime | |
| 17 | + */ | |
| 18 | +public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { | |
| 19 | + | |
| 20 | + public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer(); | |
| 21 | + | |
| 22 | + @Override | |
| 23 | + public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { | |
| 24 | + return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); | |
| 25 | + } | |
| 26 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/jackson/ser/LocalDateTimeSerializer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.jackson.ser; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.core.JsonGenerator; | |
| 4 | +import com.fasterxml.jackson.databind.JsonSerializer; | |
| 5 | +import com.fasterxml.jackson.databind.SerializerProvider; | |
| 6 | + | |
| 7 | +import java.io.IOException; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import java.time.ZoneId; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * LocalDateTime序列化规则 | |
| 13 | + * <p> | |
| 14 | + * 会将LocalDateTime序列化为毫秒级时间戳 | |
| 15 | + */ | |
| 16 | +public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { | |
| 17 | + | |
| 18 | + public static final LocalDateTimeSerializer INSTANCE = new LocalDateTimeSerializer(); | |
| 19 | + | |
| 20 | + @Override | |
| 21 | + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { | |
| 22 | + gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); | |
| 23 | + } | |
| 24 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/filter/ApiAccessLogFilter.java
| ... | ... | @@ -81,7 +81,7 @@ public class ApiAccessLogFilter extends OncePerRequestFilter { |
| 81 | 81 | Map<String, String> queryString, String requestBody, Exception ex) { |
| 82 | 82 | // 处理用户信息 |
| 83 | 83 | accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request)); |
| 84 | - accessLog.setUserType(WebFrameworkUtils.getUesrType(request)); | |
| 84 | + accessLog.setUserType(WebFrameworkUtils.getUserType(request)); | |
| 85 | 85 | // 设置访问结果 |
| 86 | 86 | CommonResult<?> result = WebFrameworkUtils.getCommonResult(request); |
| 87 | 87 | if (result != null) { | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiAccessLogFrameworkService.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.framework.logger.apilog.core.service; |
| 3 | 3 | import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO; |
| 4 | 4 | |
| 5 | 5 | import javax.validation.Valid; |
| 6 | +import java.util.concurrent.Future; | |
| 6 | 7 | |
| 7 | 8 | /** |
| 8 | 9 | * API 访问日志 Framework Service 接口 |
| ... | ... | @@ -15,7 +16,8 @@ public interface ApiAccessLogFrameworkService { |
| 15 | 16 | * 创建 API 访问日志 |
| 16 | 17 | * |
| 17 | 18 | * @param createDTO 创建信息 |
| 19 | + * @return 是否创建成功 | |
| 18 | 20 | */ |
| 19 | - void createApiAccessLogAsync(@Valid ApiAccessLogCreateDTO createDTO); | |
| 21 | + Future<Boolean> createApiAccessLogAsync(@Valid ApiAccessLogCreateDTO createDTO); | |
| 20 | 22 | |
| 21 | 23 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiErrorLogFrameworkService.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.framework.logger.apilog.core.service; |
| 3 | 3 | import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiErrorLogCreateDTO; |
| 4 | 4 | |
| 5 | 5 | import javax.validation.Valid; |
| 6 | +import java.util.concurrent.Future; | |
| 6 | 7 | |
| 7 | 8 | /** |
| 8 | 9 | * API 错误日志 Framework Service 接口 |
| ... | ... | @@ -15,7 +16,8 @@ public interface ApiErrorLogFrameworkService { |
| 15 | 16 | * 创建 API 错误日志 |
| 16 | 17 | * |
| 17 | 18 | * @param createDTO 创建信息 |
| 19 | + * @return 是否创建成功 | |
| 18 | 20 | */ |
| 19 | - void createApiErrorLogAsync(@Valid ApiErrorLogCreateDTO createDTO); | |
| 21 | + Future<Boolean> createApiErrorLogAsync(@Valid ApiErrorLogCreateDTO createDTO); | |
| 20 | 22 | |
| 21 | 23 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/service/OperateLogFrameworkService.java
| ... | ... | @@ -2,13 +2,16 @@ package cn.iocoder.dashboard.framework.logger.operatelog.core.service; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.modules.system.controller.logger.vo.operatelog.SysOperateLogCreateReqVO; |
| 4 | 4 | |
| 5 | +import java.util.concurrent.Future; | |
| 6 | + | |
| 5 | 7 | public interface OperateLogFrameworkService { |
| 6 | 8 | |
| 7 | 9 | /** |
| 8 | - * 要不记录操作日志 | |
| 10 | + * 异步记录操作日志 | |
| 9 | 11 | * |
| 10 | 12 | * @param reqVO 操作日志请求 |
| 13 | + * @return true: 记录成功,false: 记录失败 | |
| 11 | 14 | */ |
| 12 | - void createOperateLogAsync(SysOperateLogCreateReqVO reqVO); | |
| 15 | + Future<Boolean> createOperateLogAsync(SysOperateLogCreateReqVO reqVO); | |
| 13 | 16 | |
| 14 | 17 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/mybatis/core/handler/DefaultDBFieldHandler.java
| ... | ... | @@ -57,7 +57,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { |
| 57 | 57 | } |
| 58 | 58 | // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 |
| 59 | 59 | if (Objects.nonNull(loginUser) && Objects.isNull(modifier)) { |
| 60 | - setFieldValByName("updater", loginUser.getId(), metaObject); | |
| 60 | + setFieldValByName("updater", loginUser.getId().toString(), metaObject); | |
| 61 | 61 | } |
| 62 | 62 | } |
| 63 | 63 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/config/RedisConfig.java
| 1 | 1 | package cn.iocoder.dashboard.framework.redis.config; |
| 2 | 2 | |
| 3 | +import cn.hutool.system.SystemUtil; | |
| 3 | 4 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 5 | +import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; | |
| 4 | 6 | import lombok.extern.slf4j.Slf4j; |
| 5 | 7 | import org.springframework.context.annotation.Bean; |
| 6 | 8 | import org.springframework.context.annotation.Configuration; |
| 7 | 9 | import org.springframework.data.redis.connection.RedisConnectionFactory; |
| 10 | +import org.springframework.data.redis.connection.stream.Consumer; | |
| 11 | +import org.springframework.data.redis.connection.stream.ObjectRecord; | |
| 12 | +import org.springframework.data.redis.connection.stream.ReadOffset; | |
| 13 | +import org.springframework.data.redis.connection.stream.StreamOffset; | |
| 8 | 14 | import org.springframework.data.redis.core.RedisTemplate; |
| 9 | 15 | import org.springframework.data.redis.listener.ChannelTopic; |
| 10 | 16 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
| 11 | 17 | import org.springframework.data.redis.serializer.RedisSerializer; |
| 18 | +import org.springframework.data.redis.stream.StreamMessageListenerContainer; | |
| 12 | 19 | |
| 13 | 20 | import java.util.List; |
| 14 | 21 | |
| ... | ... | @@ -19,6 +26,9 @@ import java.util.List; |
| 19 | 26 | @Slf4j |
| 20 | 27 | public class RedisConfig { |
| 21 | 28 | |
| 29 | + /** | |
| 30 | + * 创建 RedisTemplate Bean,使用 JSON 序列化方式 | |
| 31 | + */ | |
| 22 | 32 | @Bean |
| 23 | 33 | public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { |
| 24 | 34 | // 创建 RedisTemplate 对象 |
| ... | ... | @@ -27,14 +37,19 @@ public class RedisConfig { |
| 27 | 37 | template.setConnectionFactory(factory); |
| 28 | 38 | // 使用 String 序列化方式,序列化 KEY 。 |
| 29 | 39 | template.setKeySerializer(RedisSerializer.string()); |
| 40 | + template.setHashKeySerializer(RedisSerializer.string()); | |
| 30 | 41 | // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 |
| 31 | 42 | template.setValueSerializer(RedisSerializer.json()); |
| 43 | + template.setHashValueSerializer(RedisSerializer.json()); | |
| 32 | 44 | return template; |
| 33 | 45 | } |
| 34 | 46 | |
| 47 | + /** | |
| 48 | + * 创建 Redis Pub/Sub 广播消费的容器 | |
| 49 | + */ | |
| 35 | 50 | @Bean |
| 36 | - public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory, | |
| 37 | - List<AbstractChannelMessageListener<?>> listeners) { | |
| 51 | + public RedisMessageListenerContainer redisMessageListenerContainer( | |
| 52 | + RedisConnectionFactory factory, List<AbstractChannelMessageListener<?>> listeners) { | |
| 38 | 53 | // 创建 RedisMessageListenerContainer 对象 |
| 39 | 54 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
| 40 | 55 | // 设置 RedisConnection 工厂。 |
| ... | ... | @@ -48,4 +63,57 @@ public class RedisConfig { |
| 48 | 63 | return container; |
| 49 | 64 | } |
| 50 | 65 | |
| 66 | + /** | |
| 67 | + * 创建 Redis Stream 集群消费的容器 | |
| 68 | + * | |
| 69 | + * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html | |
| 70 | + */ | |
| 71 | + @Bean(initMethod = "start", destroyMethod = "stop") | |
| 72 | + public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( | |
| 73 | + RedisTemplate<String, Object> redisTemplate, List<AbstractStreamMessageListener<?>> listeners) { | |
| 74 | + // 第一步,创建 StreamMessageListenerContainer 容器 | |
| 75 | + // 创建 options 配置 | |
| 76 | + StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions = | |
| 77 | + StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder() | |
| 78 | + .batchSize(10) // 一次性最多拉取多少条消息 | |
| 79 | + .targetType(String.class) // 目标类型。统一使用 String,通过自己封装的 AbstractStreamMessageListener 去反序列化 | |
| 80 | + .build(); | |
| 81 | + // 创建 container 对象 | |
| 82 | + StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = StreamMessageListenerContainer.create( | |
| 83 | + redisTemplate.getRequiredConnectionFactory(), containerOptions); | |
| 84 | + | |
| 85 | + // 第二步,注册监听器,消费对应的 Stream 主题 | |
| 86 | + String consumerName = buildConsumerName(); | |
| 87 | +// String consumerName = "110"; | |
| 88 | + listeners.forEach(listener -> { | |
| 89 | + // 创建 listener 对应的消费者分组 | |
| 90 | + try { | |
| 91 | + redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); | |
| 92 | + } catch (Exception ignore) {} | |
| 93 | + // 设置 listener 对应的 redisTemplate | |
| 94 | + listener.setRedisTemplate(redisTemplate); | |
| 95 | + // 创建 Consumer 对象 | |
| 96 | + Consumer consumer = Consumer.from(listener.getGroup(), consumerName); | |
| 97 | + // 设置 Consumer 消费进度,以最小消费进度为准 | |
| 98 | + StreamOffset<String> streamOffset = StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed()); | |
| 99 | + // 设置 Consumer 监听 | |
| 100 | + StreamMessageListenerContainer.StreamReadRequestBuilder<String> builder = StreamMessageListenerContainer.StreamReadRequest | |
| 101 | + .builder(streamOffset).consumer(consumer) | |
| 102 | + .autoAcknowledge(false) // 不自动 ack | |
| 103 | + .cancelOnError(throwable -> false); // 默认配置,发生异常就取消消费,显然不符合预期;因此,我们设置为 false | |
| 104 | + container.register(builder.build(), listener); | |
| 105 | + }); | |
| 106 | + return container; | |
| 107 | + } | |
| 108 | + | |
| 109 | + /** | |
| 110 | + * 构建消费者名字,使用本地 IP + 进程编号的方式。 | |
| 111 | + * 参考自 RocketMQ clientId 的实现 | |
| 112 | + * | |
| 113 | + * @return 消费者名字 | |
| 114 | + */ | |
| 115 | + private static String buildConsumerName() { | |
| 116 | + return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID()); | |
| 117 | + } | |
| 118 | + | |
| 51 | 119 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/pubsub/AbstractChannelMessageListener.java
| 1 | 1 | package cn.iocoder.dashboard.framework.redis.core.pubsub; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.ArrayUtil; | |
| 3 | +import cn.hutool.core.util.TypeUtil; | |
| 4 | 4 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 5 | 5 | import lombok.SneakyThrows; |
| 6 | 6 | import org.springframework.data.redis.connection.Message; |
| 7 | 7 | import org.springframework.data.redis.connection.MessageListener; |
| 8 | -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; | |
| 9 | 8 | |
| 10 | 9 | import java.lang.reflect.Type; |
| 11 | 10 | |
| ... | ... | @@ -62,21 +61,11 @@ public abstract class AbstractChannelMessageListener<T extends ChannelMessage> i |
| 62 | 61 | */ |
| 63 | 62 | @SuppressWarnings("unchecked") |
| 64 | 63 | private Class<T> getMessageClass() { |
| 65 | - Class<?> targetClass = getClass(); | |
| 66 | - while (targetClass.getSuperclass() != null) { | |
| 67 | - // 如果不是 AbstractMessageListener 父类,继续向上查找 | |
| 68 | - if (targetClass.getSuperclass() != AbstractChannelMessageListener.class) { | |
| 69 | - targetClass = targetClass.getSuperclass(); | |
| 70 | - continue; | |
| 71 | - } | |
| 72 | - // 如果是 AbstractMessageListener 父类,则解析泛型 | |
| 73 | - Type[] types = ((ParameterizedTypeImpl) targetClass.getGenericSuperclass()).getActualTypeArguments(); | |
| 74 | - if (ArrayUtil.isEmpty(types)) { | |
| 75 | - throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName())); | |
| 76 | - } | |
| 77 | - return (Class<T>) types[0]; | |
| 64 | + Type type = TypeUtil.getTypeArgument(getClass(), 0); | |
| 65 | + if (type == null) { | |
| 66 | + throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName())); | |
| 78 | 67 | } |
| 79 | - throw new IllegalStateException(String.format("类型(%s) 找不到 AbstractMessageListener 父类", getClass().getName())); | |
| 68 | + return (Class<T>) type; | |
| 80 | 69 | } |
| 81 | 70 | |
| 82 | 71 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/pubsub/ChannelMessage.java
| ... | ... | @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * Redis Channel Message 接口 |
| 7 | + * | |
| 8 | + * @author 芋道源码 | |
| 7 | 9 | */ |
| 8 | 10 | public interface ChannelMessage { |
| 9 | 11 | |
| ... | ... | @@ -12,7 +14,7 @@ public interface ChannelMessage { |
| 12 | 14 | * |
| 13 | 15 | * @return Channel |
| 14 | 16 | */ |
| 15 | - @JsonIgnore // 必须序列化 | |
| 17 | + @JsonIgnore // 避免序列化 | |
| 16 | 18 | String getChannel(); |
| 17 | 19 | |
| 18 | 20 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/stream/AbstractStreamMessageListener.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.redis.core.stream; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.TypeUtil; | |
| 4 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 5 | +import lombok.Getter; | |
| 6 | +import lombok.Setter; | |
| 7 | +import lombok.SneakyThrows; | |
| 8 | +import org.springframework.beans.factory.annotation.Value; | |
| 9 | +import org.springframework.data.redis.connection.stream.ObjectRecord; | |
| 10 | +import org.springframework.data.redis.core.RedisTemplate; | |
| 11 | +import org.springframework.data.redis.stream.StreamListener; | |
| 12 | + | |
| 13 | +import java.lang.reflect.Type; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * Redis Stream 监听器抽象类,用于实现集群消费 | |
| 17 | + * | |
| 18 | + * @param <T> 消息类型。一定要填写噢,不然会报错 | |
| 19 | + * | |
| 20 | + * @author 芋道源码 | |
| 21 | + */ | |
| 22 | +public abstract class AbstractStreamMessageListener<T extends StreamMessage> | |
| 23 | + implements StreamListener<String, ObjectRecord<String, String>> { | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 消息类型 | |
| 27 | + */ | |
| 28 | + private final Class<T> messageType; | |
| 29 | + /** | |
| 30 | + * Redis Channel | |
| 31 | + */ | |
| 32 | + @Getter | |
| 33 | + private final String streamKey; | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * Redis 消费者分组,默认使用 spring.application.name 名字 | |
| 37 | + */ | |
| 38 | + @Value("${spring.application.name}") | |
| 39 | + @Getter | |
| 40 | + private String group; | |
| 41 | + /** | |
| 42 | + * | |
| 43 | + */ | |
| 44 | + @Setter | |
| 45 | + private RedisTemplate<String, ?> redisTemplate; | |
| 46 | + | |
| 47 | + @SneakyThrows | |
| 48 | + protected AbstractStreamMessageListener() { | |
| 49 | + this.messageType = getMessageClass(); | |
| 50 | + this.streamKey = messageType.newInstance().getStreamKey(); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @Override | |
| 54 | + public void onMessage(ObjectRecord<String, String> message) { | |
| 55 | + // 消费消息 | |
| 56 | + T messageObj = JsonUtils.parseObject(message.getValue(), messageType); | |
| 57 | + this.onMessage(messageObj); | |
| 58 | + // ack 消息消费完成 | |
| 59 | + redisTemplate.opsForStream().acknowledge(group, message); | |
| 60 | + // TODO 芋艿:需要额外考虑以下几个点: | |
| 61 | + // 1. 处理异常的情况 | |
| 62 | + // 2. 发送日志;以及事务的结合 | |
| 63 | + // 3. 消费日志;以及通用的幂等性 | |
| 64 | + // 4. 消费失败的重试,https://zhuanlan.zhihu.com/p/60501638 | |
| 65 | + } | |
| 66 | + | |
| 67 | + /** | |
| 68 | + * 处理消息 | |
| 69 | + * | |
| 70 | + * @param message 消息 | |
| 71 | + */ | |
| 72 | + public abstract void onMessage(T message); | |
| 73 | + | |
| 74 | + /** | |
| 75 | + * 通过解析类上的泛型,获得消息类型 | |
| 76 | + * | |
| 77 | + * @return 消息类型 | |
| 78 | + */ | |
| 79 | + @SuppressWarnings("unchecked") | |
| 80 | + private Class<T> getMessageClass() { | |
| 81 | + Type type = TypeUtil.getTypeArgument(getClass(), 0); | |
| 82 | + if (type == null) { | |
| 83 | + throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName())); | |
| 84 | + } | |
| 85 | + return (Class<T>) type; | |
| 86 | + } | |
| 87 | + | |
| 88 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/stream/StreamMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.redis.core.stream; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Redis Stream Message 接口 | |
| 7 | + * | |
| 8 | + * @author 芋道源码 | |
| 9 | + */ | |
| 10 | +public interface StreamMessage { | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * 获得 Redis Stream Key | |
| 14 | + * | |
| 15 | + * @return Channel | |
| 16 | + */ | |
| 17 | + @JsonIgnore // 避免序列化 | |
| 18 | + String getStreamKey(); | |
| 19 | + | |
| 20 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/util/RedisMessageUtils.java
| 1 | 1 | package cn.iocoder.dashboard.framework.redis.core.util; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage; |
| 4 | +import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; | |
| 4 | 5 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 6 | +import org.springframework.data.redis.connection.stream.RecordId; | |
| 7 | +import org.springframework.data.redis.connection.stream.StreamRecords; | |
| 5 | 8 | import org.springframework.data.redis.core.RedisTemplate; |
| 6 | 9 | |
| 7 | 10 | /** |
| ... | ... | @@ -17,8 +20,21 @@ public class RedisMessageUtils { |
| 17 | 20 | * @param redisTemplate Redis 操作模板 |
| 18 | 21 | * @param message 消息 |
| 19 | 22 | */ |
| 20 | - public static <T extends ChannelMessage> void sendChannelMessage(RedisTemplate<?, ?> redisTemplate, T message) { | |
| 23 | + public static <T extends ChannelMessage> void sendChannelMessage(RedisTemplate<?, ?> redisTemplate, T message) { | |
| 21 | 24 | redisTemplate.convertAndSend(message.getChannel(), JsonUtils.toJsonString(message)); |
| 22 | 25 | } |
| 23 | 26 | |
| 27 | + /** | |
| 28 | + * 发送 Redis 消息,基于 Redis Stream 实现 | |
| 29 | + * | |
| 30 | + * @param redisTemplate Redis 操作模板 | |
| 31 | + * @param message 消息 | |
| 32 | + * @return 消息记录的编号对象 | |
| 33 | + */ | |
| 34 | + public static <T extends StreamMessage> RecordId sendStreamMessage(RedisTemplate<String, ?> redisTemplate, T message) { | |
| 35 | + return redisTemplate.opsForStream().add(StreamRecords.newRecord() | |
| 36 | + .ofObject(JsonUtils.toJsonString(message)) // 设置内容 | |
| 37 | + .withStreamKey(message.getStreamKey())); // 设置 stream key | |
| 38 | + } | |
| 39 | + | |
| 24 | 40 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
| ... | ... | @@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { |
| 128 | 128 | // 设置每个请求的权限 |
| 129 | 129 | .authorizeRequests() |
| 130 | 130 | // 登陆的接口,可匿名访问 |
| 131 | - .antMatchers(webProperties.getApiPrefix() + "/login").anonymous() | |
| 131 | + .antMatchers(api("/login")).anonymous() | |
| 132 | 132 | // 通用的接口,可匿名访问 |
| 133 | - .antMatchers( webProperties.getApiPrefix() + "/system/captcha/**").anonymous() | |
| 133 | + .antMatchers(api("/system/captcha/**")).anonymous() | |
| 134 | 134 | // 静态资源,可匿名访问 |
| 135 | 135 | .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() |
| 136 | 136 | // 文件的获取接口,可匿名访问 |
| 137 | - .antMatchers(webProperties.getApiPrefix() + "/system/file/get/**").anonymous() | |
| 137 | + .antMatchers(api("/infra/file/get/**")).anonymous() | |
| 138 | 138 | // Swagger 接口文档 |
| 139 | 139 | .antMatchers("/swagger-ui.html").anonymous() |
| 140 | 140 | .antMatchers("/swagger-resources/**").anonymous() |
| ... | ... | @@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { |
| 148 | 148 | .antMatchers("/actuator/**").anonymous() |
| 149 | 149 | // Druid 监控 |
| 150 | 150 | .antMatchers("/druid/**").anonymous() |
| 151 | + // 短信回调 API | |
| 152 | + .antMatchers(api("/system/sms/callback/**")).anonymous() | |
| 151 | 153 | // 除上面外的所有请求全部需要鉴权认证 |
| 152 | 154 | .anyRequest().authenticated() |
| 153 | 155 | .and() |
| 154 | 156 | .headers().frameOptions().disable(); |
| 155 | - httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler); | |
| 157 | + httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); | |
| 156 | 158 | // 添加 JWT Filter |
| 157 | 159 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
| 158 | 160 | } |
| 159 | 161 | |
| 162 | + private String api(String url) { | |
| 163 | + return webProperties.getApiPrefix() + url; | |
| 164 | + } | |
| 165 | + | |
| 160 | 166 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.SmsClientFactoryImpl; | |
| 5 | +import org.springframework.context.annotation.Bean; | |
| 6 | +import org.springframework.context.annotation.Configuration; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 短信配置类 | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +@Configuration | |
| 14 | +public class SmsConfiguration { | |
| 15 | + | |
| 16 | + @Bean | |
| 17 | + public SmsClientFactory smsClientFactory() { | |
| 18 | + return new SmsClientFactoryImpl(); | |
| 19 | + } | |
| 20 | + | |
| 21 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 7 | + | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 短信客户端接口 | |
| 12 | + * | |
| 13 | + * @author zzf | |
| 14 | + * @date 2021/1/25 14:14 | |
| 15 | + */ | |
| 16 | +public interface SmsClient { | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 获得渠道编号 | |
| 20 | + * | |
| 21 | + * @return 渠道编号 | |
| 22 | + */ | |
| 23 | + Long getId(); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 发送消息 | |
| 27 | + * | |
| 28 | + * @param logId 日志编号 | |
| 29 | + * @param mobile 手机号 | |
| 30 | + * @param apiTemplateId 短信 API 的模板编号 | |
| 31 | + * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序 | |
| 32 | + * @return 短信发送结果 | |
| 33 | + */ | |
| 34 | + SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId, | |
| 35 | + List<KeyValue<String, Object>> templateParams); | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 解析接收短信的接收结果 | |
| 39 | + * | |
| 40 | + * @param text 结果 | |
| 41 | + * @return 结果内容 | |
| 42 | + * @throws Throwable 当解析 text 发生异常时,则会抛出异常 | |
| 43 | + */ | |
| 44 | + List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable; | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 查询指定的短信模板 | |
| 48 | + * | |
| 49 | + * @param apiTemplateId 短信 API 的模板编号 | |
| 50 | + * @return 短信模板 | |
| 51 | + */ | |
| 52 | + SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId); | |
| 53 | + | |
| 54 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 短信客户端工厂接口 | |
| 7 | + * | |
| 8 | + * @author zzf | |
| 9 | + * @date 2021/1/28 14:01 | |
| 10 | + */ | |
| 11 | +public interface SmsClientFactory { | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 获得短信 Client | |
| 15 | + * | |
| 16 | + * @param channelId 渠道编号 | |
| 17 | + * @return 短信 Client | |
| 18 | + */ | |
| 19 | + SmsClient getSmsClient(Long channelId); | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 获得短信 Client | |
| 23 | + * | |
| 24 | + * @param channelCode 渠道编码 | |
| 25 | + * @return 短信 Client | |
| 26 | + */ | |
| 27 | + SmsClient getSmsClient(String channelCode); | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * 创建短信 Client | |
| 31 | + * | |
| 32 | + * @param properties 配置对象 | |
| 33 | + */ | |
| 34 | + void createOrUpdateSmsClient(SmsChannelProperties properties); | |
| 35 | + | |
| 36 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsCodeMapping.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; | |
| 5 | + | |
| 6 | +import java.util.function.Function; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 将 API 的错误码,转换为通用的错误码 | |
| 10 | + * | |
| 11 | + * @see SmsCommonResult | |
| 12 | + * @see SmsFrameworkErrorCodeConstants | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +public interface SmsCodeMapping extends Function<String, ErrorCode> { | |
| 17 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsCommonResult.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | |
| 2 | + | |
| 3 | +import cn.hutool.core.exceptions.ExceptionUtil; | |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 5 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | |
| 6 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; | |
| 8 | +import lombok.Data; | |
| 9 | +import lombok.EqualsAndHashCode; | |
| 10 | +import lombok.ToString; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 短信的 CommonResult 拓展类 | |
| 14 | + * | |
| 15 | + * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段 | |
| 16 | + * | |
| 17 | + * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段 | |
| 18 | + * | |
| 19 | + * @author 芋道源码 | |
| 20 | + */ | |
| 21 | +@Data | |
| 22 | +@EqualsAndHashCode(callSuper = true) | |
| 23 | +@ToString(callSuper = true) | |
| 24 | +public class SmsCommonResult<T> extends CommonResult<T> { | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * API 返回错误码 | |
| 28 | + * | |
| 29 | + * 由于第三方的错误码可能是字符串,所以使用 String 类型 | |
| 30 | + */ | |
| 31 | + private String apiCode; | |
| 32 | + /** | |
| 33 | + * API 返回提示 | |
| 34 | + */ | |
| 35 | + private String apiMsg; | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * API 请求编号 | |
| 39 | + */ | |
| 40 | + private String apiRequestId; | |
| 41 | + | |
| 42 | + private SmsCommonResult() { | |
| 43 | + } | |
| 44 | + | |
| 45 | + public static <T> SmsCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId, | |
| 46 | + T data, SmsCodeMapping codeMapping) { | |
| 47 | + Assert.notNull(codeMapping, "参数 codeMapping 不能为空"); | |
| 48 | + SmsCommonResult<T> result = new SmsCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId); | |
| 49 | + result.setData(data); | |
| 50 | + // 翻译错误码 | |
| 51 | + if (codeMapping != null) { | |
| 52 | + ErrorCode errorCode = codeMapping.apply(apiCode); | |
| 53 | + if (errorCode == null) { | |
| 54 | + errorCode = SmsFrameworkErrorCodeConstants.SMS_UNKNOWN; | |
| 55 | + } | |
| 56 | + result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg()); | |
| 57 | + } | |
| 58 | + return result; | |
| 59 | + } | |
| 60 | + | |
| 61 | + public static <T> SmsCommonResult<T> error(Throwable ex) { | |
| 62 | + SmsCommonResult<T> result = new SmsCommonResult<>(); | |
| 63 | + result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getCode()); | |
| 64 | + result.setMsg(ExceptionUtil.getRootCauseMessage(ex)); | |
| 65 | + return result; | |
| 66 | + } | |
| 67 | + | |
| 68 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.dto; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +import java.util.Date; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 消息接收 Response DTO | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +@Data | |
| 13 | +public class SmsReceiveRespDTO { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * 是否接收成功 | |
| 17 | + */ | |
| 18 | + private Boolean success; | |
| 19 | + /** | |
| 20 | + * API 接收结果的编码 | |
| 21 | + */ | |
| 22 | + private String errorCode; | |
| 23 | + /** | |
| 24 | + * API 接收结果的说明 | |
| 25 | + */ | |
| 26 | + private String errorMsg; | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * 手机号 | |
| 30 | + */ | |
| 31 | + private String mobile; | |
| 32 | + /** | |
| 33 | + * 用户接收时间 | |
| 34 | + */ | |
| 35 | + private Date receiveTime; | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 短信 API 发送返回的序号 | |
| 39 | + */ | |
| 40 | + private String serialNo; | |
| 41 | + /** | |
| 42 | + * 短信日志编号 | |
| 43 | + * | |
| 44 | + * 对应 SysSmsLogDO 的编号 | |
| 45 | + */ | |
| 46 | + private Long logId; | |
| 47 | + | |
| 48 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java
0 → 100644
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsTemplateRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.dto; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 短信模板 Response DTO | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@Data | |
| 12 | +public class SmsTemplateRespDTO { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 模板编号 | |
| 16 | + */ | |
| 17 | + private String id; | |
| 18 | + /** | |
| 19 | + * 短信内容 | |
| 20 | + */ | |
| 21 | + private String content; | |
| 22 | + /** | |
| 23 | + * 审核状态 | |
| 24 | + * | |
| 25 | + * 枚举 {@link SmsTemplateAuditStatusEnum} | |
| 26 | + */ | |
| 27 | + private Integer auditStatus; | |
| 28 | + /** | |
| 29 | + * 审核未通过的理由 | |
| 30 | + */ | |
| 31 | + private String auditReason; | |
| 32 | + | |
| 33 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 11 | +import lombok.extern.slf4j.Slf4j; | |
| 12 | + | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * 短信客户端抽象类 | |
| 17 | + * | |
| 18 | + * @author zzf | |
| 19 | + * @date 2021/2/1 9:28 | |
| 20 | + */ | |
| 21 | +@Slf4j | |
| 22 | +public abstract class AbstractSmsClient implements SmsClient { | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 短信渠道配置 | |
| 26 | + */ | |
| 27 | + protected volatile SmsChannelProperties properties; | |
| 28 | + /** | |
| 29 | + * 错误码枚举类 | |
| 30 | + */ | |
| 31 | + protected final SmsCodeMapping codeMapping; | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 短信客户端有参构造函数 | |
| 35 | + * | |
| 36 | + * @param properties 短信配置 | |
| 37 | + */ | |
| 38 | + public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) { | |
| 39 | + this.properties = properties; | |
| 40 | + this.codeMapping = codeMapping; | |
| 41 | + } | |
| 42 | + | |
| 43 | + /** | |
| 44 | + * 初始化 | |
| 45 | + */ | |
| 46 | + public final void init() { | |
| 47 | + doInit(); | |
| 48 | + log.info("[init][配置({}) 初始化完成]", properties); | |
| 49 | + } | |
| 50 | + | |
| 51 | + public final void refresh(SmsChannelProperties properties) { | |
| 52 | + // 判断是否更新 | |
| 53 | + if (properties.equals(this.properties)) { | |
| 54 | + return; | |
| 55 | + } | |
| 56 | + log.info("[refresh][配置({})发生变化,重新初始化]", properties); | |
| 57 | + this.properties = properties; | |
| 58 | + // 初始化 | |
| 59 | + this.init(); | |
| 60 | + } | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * 自定义初始化 | |
| 64 | + */ | |
| 65 | + protected abstract void doInit(); | |
| 66 | + | |
| 67 | + @Override | |
| 68 | + public Long getId() { | |
| 69 | + return properties.getId(); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @Override | |
| 73 | + public final SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, | |
| 74 | + String apiTemplateId, List<KeyValue<String, Object>> templateParams) { | |
| 75 | + // 执行短信发送 | |
| 76 | + SmsCommonResult<SmsSendRespDTO> result; | |
| 77 | + try { | |
| 78 | + result = doSendSms(logId, mobile, apiTemplateId, templateParams); | |
| 79 | + } catch (Throwable ex) { | |
| 80 | + // 打印异常日志 | |
| 81 | + log.error("[sendSms][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]", | |
| 82 | + logId, mobile, apiTemplateId, templateParams, ex); | |
| 83 | + // 封装返回 | |
| 84 | + return SmsCommonResult.error(ex); | |
| 85 | + } | |
| 86 | + return result; | |
| 87 | + } | |
| 88 | + | |
| 89 | + protected abstract SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, | |
| 90 | + String apiTemplateId, List<KeyValue<String, Object>> templateParams) | |
| 91 | + throws Throwable; | |
| 92 | + | |
| 93 | + @Override | |
| 94 | + public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable { | |
| 95 | + try { | |
| 96 | + return doParseSmsReceiveStatus(text); | |
| 97 | + } catch (Throwable ex) { | |
| 98 | + log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex); | |
| 99 | + throw ex; | |
| 100 | + } | |
| 101 | + } | |
| 102 | + | |
| 103 | + protected abstract List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable; | |
| 104 | + | |
| 105 | + @Override | |
| 106 | + public SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId) { | |
| 107 | + // 执行短信发送 | |
| 108 | + SmsCommonResult<SmsTemplateRespDTO> result; | |
| 109 | + try { | |
| 110 | + result = doGetSmsTemplate(apiTemplateId); | |
| 111 | + } catch (Throwable ex) { | |
| 112 | + // 打印异常日志 | |
| 113 | + log.error("[getSmsTemplate][获得短信模板({}) 发生异常]", apiTemplateId, ex); | |
| 114 | + // 封装返回 | |
| 115 | + return SmsCommonResult.error(ex); | |
| 116 | + } | |
| 117 | + return result; | |
| 118 | + } | |
| 119 | + | |
| 120 | + protected abstract SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable; | |
| 121 | + | |
| 122 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun.AliyunSmsClient; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian.YunpianSmsClient; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 10 | +import lombok.extern.slf4j.Slf4j; | |
| 11 | +import org.springframework.util.Assert; | |
| 12 | +import org.springframework.validation.annotation.Validated; | |
| 13 | + | |
| 14 | +import java.util.Arrays; | |
| 15 | +import java.util.concurrent.ConcurrentHashMap; | |
| 16 | +import java.util.concurrent.ConcurrentMap; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * 短信客户端工厂接口 | |
| 20 | + * | |
| 21 | + * @author zzf | |
| 22 | + */ | |
| 23 | +@Validated | |
| 24 | +@Slf4j | |
| 25 | +public class SmsClientFactoryImpl implements SmsClientFactory { | |
| 26 | + | |
| 27 | + /** | |
| 28 | + * 短信客户端 Map | |
| 29 | + * key:渠道编号,使用 {@link SmsChannelProperties#getId()} | |
| 30 | + */ | |
| 31 | + private final ConcurrentMap<Long, AbstractSmsClient> channelIdClients = new ConcurrentHashMap<>(); | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 短信客户端 Map | |
| 35 | + * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()} | |
| 36 | + * | |
| 37 | + * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。 | |
| 38 | + * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients} | |
| 39 | + */ | |
| 40 | + private final ConcurrentMap<String, AbstractSmsClient> channelCodeClients = new ConcurrentHashMap<>(); | |
| 41 | + | |
| 42 | + public SmsClientFactoryImpl() { | |
| 43 | + // 初始化 channelCodeClients 集合 | |
| 44 | + Arrays.stream(SmsChannelEnum.values()).forEach(channel -> { | |
| 45 | + // 创建一个空的 SmsChannelProperties 对象 | |
| 46 | + SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode()) | |
| 47 | + .setApiKey("default").setApiSecret("default"); | |
| 48 | + // 创建 Sms 客户端 | |
| 49 | + AbstractSmsClient smsClient = createSmsClient(properties); | |
| 50 | + channelCodeClients.put(channel.getCode(), smsClient); | |
| 51 | + }); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + public SmsClient getSmsClient(Long channelId) { | |
| 56 | + return channelIdClients.get(channelId); | |
| 57 | + } | |
| 58 | + | |
| 59 | + @Override | |
| 60 | + public SmsClient getSmsClient(String channelCode) { | |
| 61 | + return channelCodeClients.get(channelCode); | |
| 62 | + } | |
| 63 | + | |
| 64 | + @Override | |
| 65 | + public void createOrUpdateSmsClient(SmsChannelProperties properties) { | |
| 66 | + AbstractSmsClient client = channelIdClients.get(properties.getId()); | |
| 67 | + if (client == null) { | |
| 68 | + client = this.createSmsClient(properties); | |
| 69 | + client.init(); | |
| 70 | + channelIdClients.put(client.getId(), client); | |
| 71 | + } else { | |
| 72 | + client.refresh(properties); | |
| 73 | + } | |
| 74 | + } | |
| 75 | + | |
| 76 | + private AbstractSmsClient createSmsClient(SmsChannelProperties properties) { | |
| 77 | + SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode()); | |
| 78 | + Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum)); | |
| 79 | + // 创建客户端 | |
| 80 | + switch (channelEnum) { | |
| 81 | + case ALIYUN: return new AliyunSmsClient(properties); | |
| 82 | + case YUN_PIAN: return new YunpianSmsClient(properties); | |
| 83 | + case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties); | |
| 84 | + } | |
| 85 | + // 创建失败,错误日志 + 抛出异常 | |
| 86 | + log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties); | |
| 87 | + throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", properties)); | |
| 88 | + } | |
| 89 | + | |
| 90 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; | |
| 2 | + | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.hutool.core.util.ReflectUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 11 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum; | |
| 13 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 14 | +import cn.iocoder.dashboard.util.collection.MapUtils; | |
| 15 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 16 | +import com.aliyuncs.AcsRequest; | |
| 17 | +import com.aliyuncs.AcsResponse; | |
| 18 | +import com.aliyuncs.DefaultAcsClient; | |
| 19 | +import com.aliyuncs.IAcsClient; | |
| 20 | +import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; | |
| 21 | +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; | |
| 22 | +import com.aliyuncs.exceptions.ClientException; | |
| 23 | +import com.aliyuncs.profile.DefaultProfile; | |
| 24 | +import com.aliyuncs.profile.IClientProfile; | |
| 25 | +import com.fasterxml.jackson.annotation.JsonFormat; | |
| 26 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
| 27 | +import com.google.common.annotations.VisibleForTesting; | |
| 28 | +import lombok.Data; | |
| 29 | +import lombok.extern.slf4j.Slf4j; | |
| 30 | + | |
| 31 | +import java.util.Date; | |
| 32 | +import java.util.List; | |
| 33 | +import java.util.Objects; | |
| 34 | +import java.util.function.Function; | |
| 35 | +import java.util.stream.Collectors; | |
| 36 | + | |
| 37 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 38 | +import static cn.iocoder.dashboard.util.date.DateUtils.TIME_ZONE_DEFAULT; | |
| 39 | + | |
| 40 | +/** | |
| 41 | + * 阿里短信客户端的实现类 | |
| 42 | + * | |
| 43 | + * @author zzf | |
| 44 | + * @date 2021/1/25 14:17 | |
| 45 | + */ | |
| 46 | +@Slf4j | |
| 47 | +public class AliyunSmsClient extends AbstractSmsClient { | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * REGION, 使用杭州 | |
| 51 | + */ | |
| 52 | + private static final String ENDPOINT = "cn-hangzhou"; | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 阿里云客户端 | |
| 56 | + */ | |
| 57 | + private volatile IAcsClient client; | |
| 58 | + | |
| 59 | + public AliyunSmsClient(SmsChannelProperties properties) { | |
| 60 | + super(properties, new AliyunSmsCodeMapping()); | |
| 61 | + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); | |
| 62 | + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @Override | |
| 66 | + protected void doInit() { | |
| 67 | + IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret()); | |
| 68 | + client = new DefaultAcsClient(profile); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @Override | |
| 72 | + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, | |
| 73 | + String apiTemplateId, List<KeyValue<String, Object>> templateParams) { | |
| 74 | + // 构建参数 | |
| 75 | + SendSmsRequest request = new SendSmsRequest(); | |
| 76 | + request.setPhoneNumbers(mobile); | |
| 77 | + request.setSignName(properties.getSignature()); | |
| 78 | + request.setTemplateCode(apiTemplateId); | |
| 79 | + request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); | |
| 80 | + request.setOutId(String.valueOf(sendLogId)); | |
| 81 | + // 执行请求 | |
| 82 | + return invoke(request, response -> new SmsSendRespDTO().setSerialNo(response.getBizId())); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @Override | |
| 86 | + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { | |
| 87 | + List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); | |
| 88 | + return statuses.stream().map(status -> { | |
| 89 | + SmsReceiveRespDTO resp = new SmsReceiveRespDTO(); | |
| 90 | + resp.setSuccess(status.getSuccess()); | |
| 91 | + resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()); | |
| 92 | + resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime()); | |
| 93 | + resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId())); | |
| 94 | + return resp; | |
| 95 | + }).collect(Collectors.toList()); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Override | |
| 99 | + protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) { | |
| 100 | + // 构建参数 | |
| 101 | + QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); | |
| 102 | + request.setTemplateCode(apiTemplateId); | |
| 103 | + // 执行请求 | |
| 104 | + return invoke(request, response -> { | |
| 105 | + SmsTemplateRespDTO data = new SmsTemplateRespDTO(); | |
| 106 | + data.setId(response.getTemplateCode()).setContent(response.getTemplateContent()); | |
| 107 | + data.setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason()); | |
| 108 | + return data; | |
| 109 | + }); | |
| 110 | + } | |
| 111 | + | |
| 112 | + @VisibleForTesting | |
| 113 | + Integer convertSmsTemplateAuditStatus(Integer templateStatus) { | |
| 114 | + switch (templateStatus) { | |
| 115 | + case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); | |
| 116 | + case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); | |
| 117 | + case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); | |
| 118 | + default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); | |
| 119 | + } | |
| 120 | + } | |
| 121 | + | |
| 122 | + @VisibleForTesting | |
| 123 | + <T extends AcsResponse, R> SmsCommonResult<R> invoke(AcsRequest<T> request, Function<T, R> responseConsumer) { | |
| 124 | + try { | |
| 125 | + // 执行发送. 由于阿里云 sms 短信没有统一的 Response,但是有统一的 code、message、requestId 属性,所以只好反射 | |
| 126 | + T sendResult = client.getAcsResponse(request); | |
| 127 | + String code = (String) ReflectUtil.getFieldValue(sendResult, "code"); | |
| 128 | + String message = (String) ReflectUtil.getFieldValue(sendResult, "message"); | |
| 129 | + String requestId = (String) ReflectUtil.getFieldValue(sendResult, "requestId"); | |
| 130 | + // 解析结果 | |
| 131 | + R data = null; | |
| 132 | + if (Objects.equals(code, "OK")) { // 请求成功的情况下 | |
| 133 | + data = responseConsumer.apply(sendResult); | |
| 134 | + } | |
| 135 | + // 拼接结果 | |
| 136 | + return SmsCommonResult.build(code, message, requestId, data, codeMapping); | |
| 137 | + } catch (ClientException ex) { | |
| 138 | + return SmsCommonResult.build(ex.getErrCode(), formatResultMsg(ex), ex.getRequestId(), null, codeMapping); | |
| 139 | + } | |
| 140 | + } | |
| 141 | + | |
| 142 | + private static String formatResultMsg(ClientException ex) { | |
| 143 | + if (StrUtil.isEmpty(ex.getErrorDescription())) { | |
| 144 | + return ex.getErrMsg(); | |
| 145 | + } | |
| 146 | + return ex.getErrMsg() + " => " + ex.getErrorDescription(); | |
| 147 | + } | |
| 148 | + | |
| 149 | + /** | |
| 150 | + * 短信接收状态 | |
| 151 | + * | |
| 152 | + * 参见 https://help.aliyun.com/document_detail/101867.html 文档 | |
| 153 | + * | |
| 154 | + * @author 芋道源码 | |
| 155 | + */ | |
| 156 | + @Data | |
| 157 | + public static class SmsReceiveStatus { | |
| 158 | + | |
| 159 | + /** | |
| 160 | + * 手机号 | |
| 161 | + */ | |
| 162 | + @JsonProperty("phone_number") | |
| 163 | + private String phoneNumber; | |
| 164 | + /** | |
| 165 | + * 发送时间 | |
| 166 | + */ | |
| 167 | + @JsonProperty("send_time") | |
| 168 | + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) | |
| 169 | + private Date sendTime; | |
| 170 | + /** | |
| 171 | + * 状态报告时间 | |
| 172 | + */ | |
| 173 | + @JsonProperty("report_time") | |
| 174 | + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) | |
| 175 | + private Date reportTime; | |
| 176 | + /** | |
| 177 | + * 是否接收成功 | |
| 178 | + */ | |
| 179 | + private Boolean success; | |
| 180 | + /** | |
| 181 | + * 状态报告说明 | |
| 182 | + */ | |
| 183 | + @JsonProperty("err_msg") | |
| 184 | + private String errMsg; | |
| 185 | + /** | |
| 186 | + * 状态报告编码 | |
| 187 | + */ | |
| 188 | + @JsonProperty("err_code") | |
| 189 | + private String errCode; | |
| 190 | + /** | |
| 191 | + * 发送序列号 | |
| 192 | + */ | |
| 193 | + @JsonProperty("biz_id") | |
| 194 | + private String bizId; | |
| 195 | + /** | |
| 196 | + * 用户序列号 | |
| 197 | + * | |
| 198 | + * 这里我们传递的是 SysSmsLogDO 的日志编号 | |
| 199 | + */ | |
| 200 | + @JsonProperty("out_id") | |
| 201 | + private String outId; | |
| 202 | + /** | |
| 203 | + * 短信长度,例如说 1、2、3 | |
| 204 | + * | |
| 205 | + * 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送 | |
| 206 | + */ | |
| 207 | + @JsonProperty("sms_size") | |
| 208 | + private Integer smsSize; | |
| 209 | + | |
| 210 | + } | |
| 211 | + | |
| 212 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | |
| 4 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping; | |
| 6 | + | |
| 7 | +import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 阿里云的 SmsCodeMapping 实现类 | |
| 11 | + * | |
| 12 | + * 参见 https://help.aliyun.com/document_detail/101346.htm 文档 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +public class AliyunSmsCodeMapping implements SmsCodeMapping { | |
| 17 | + | |
| 18 | + @Override | |
| 19 | + public ErrorCode apply(String apiCode) { | |
| 20 | + switch (apiCode) { | |
| 21 | + case "OK": return GlobalErrorCodeConstants.SUCCESS; | |
| 22 | + case "isv.ACCOUNT_NOT_EXISTS": | |
| 23 | + case "isv.ACCOUNT_ABNORMAL": | |
| 24 | + case "MissingAccessKeyId": return SMS_ACCOUNT_INVALID; | |
| 25 | + case "isp.RAM_PERMISSION_DENY": return SMS_PERMISSION_DENY; | |
| 26 | + case "isv.INVALID_JSON_PARAM": | |
| 27 | + case "isv.INVALID_PARAMETERS": return SMS_API_PARAM_ERROR; | |
| 28 | + case "isv.BUSINESS_LIMIT_CONTROL": return SMS_SEND_BUSINESS_LIMIT_CONTROL; | |
| 29 | + case "isv.DAY_LIMIT_CONTROL": return SMS_SEND_DAY_LIMIT_CONTROL; | |
| 30 | + case "isv.SMS_CONTENT_ILLEGAL": return SMS_SEND_CONTENT_INVALID; | |
| 31 | + case "isv.SMS_TEMPLATE_ILLEGAL": return SMS_TEMPLATE_INVALID; | |
| 32 | + case "isv.SMS_SIGNATURE_ILLEGAL": | |
| 33 | + case "isv.SIGN_NAME_ILLEGAL": | |
| 34 | + case "isv.SMS_SIGN_ILLEGAL": return SMS_SIGN_INVALID; | |
| 35 | + case "isv.AMOUNT_NOT_ENOUGH": | |
| 36 | + case "isv.OUT_OF_SERVICE": return SMS_ACCOUNT_MONEY_NOT_ENOUGH; | |
| 37 | + case "isv.MOBILE_NUMBER_ILLEGAL": return SMS_MOBILE_INVALID; | |
| 38 | + case "isv.TEMPLATE_MISSING_PARAMETERS": return SMS_TEMPLATE_PARAM_ERROR; | |
| 39 | + } | |
| 40 | + return SMS_UNKNOWN; | |
| 41 | + } | |
| 42 | + | |
| 43 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.debug; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | |
| 4 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping; | |
| 6 | + | |
| 7 | +import java.util.Objects; | |
| 8 | + | |
| 9 | +import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.SMS_UNKNOWN; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 钉钉的 SmsCodeMapping 实现类 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +public class DebugDingTalkCodeMapping implements SmsCodeMapping { | |
| 17 | + | |
| 18 | + @Override | |
| 19 | + public ErrorCode apply(String apiCode) { | |
| 20 | + return Objects.equals(apiCode, "0") ? GlobalErrorCodeConstants.SUCCESS : SMS_UNKNOWN; | |
| 21 | + } | |
| 22 | + | |
| 23 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.debug; | |
| 2 | + | |
| 3 | +import cn.hutool.core.codec.Base64; | |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 5 | +import cn.hutool.core.map.MapUtil; | |
| 6 | +import cn.hutool.core.util.StrUtil; | |
| 7 | +import cn.hutool.crypto.digest.DigestUtil; | |
| 8 | +import cn.hutool.crypto.digest.HmacAlgorithm; | |
| 9 | +import cn.hutool.http.HttpUtil; | |
| 10 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 11 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 13 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 14 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 15 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; | |
| 16 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum; | |
| 17 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 18 | +import cn.iocoder.dashboard.util.collection.MapUtils; | |
| 19 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 20 | + | |
| 21 | +import java.util.HashMap; | |
| 22 | +import java.util.List; | |
| 23 | +import java.util.Map; | |
| 24 | + | |
| 25 | +/** | |
| 26 | + * 基于钉钉 WebHook 实现的调试的短信客户端实现类 | |
| 27 | + * | |
| 28 | + * 考虑到省钱,我们使用钉钉 WebHook 模拟发送短信,方便调试。 | |
| 29 | + * | |
| 30 | + * @author 芋道源码 | |
| 31 | + */ | |
| 32 | +public class DebugDingTalkSmsClient extends AbstractSmsClient { | |
| 33 | + | |
| 34 | + public DebugDingTalkSmsClient(SmsChannelProperties properties) { | |
| 35 | + super(properties, new DebugDingTalkCodeMapping()); | |
| 36 | + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); | |
| 37 | + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); | |
| 38 | + } | |
| 39 | + | |
| 40 | + @Override | |
| 41 | + protected void doInit() { | |
| 42 | + } | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, | |
| 46 | + String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable { | |
| 47 | + // 构建请求 | |
| 48 | + String url = buildUrl("robot/send"); | |
| 49 | + Map<String, Object> params = new HashMap<>(); | |
| 50 | + params.put("msgtype", "text"); | |
| 51 | + String content = String.format("【模拟短信】\n手机号:%s\n短信日志编号:%d\n模板参数:%s", | |
| 52 | + mobile, sendLogId, MapUtils.convertMap(templateParams)); | |
| 53 | + params.put("text", MapUtil.builder().put("content", content).build()); | |
| 54 | + // 执行请求 | |
| 55 | + String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params)); | |
| 56 | + // 解析结果 | |
| 57 | + Map<?, ?> responseObj = JsonUtils.parseObject(responseText, Map.class); | |
| 58 | + return SmsCommonResult.build(MapUtil.getStr(responseObj, "errcode"), MapUtil.getStr(responseObj, "errorMsg"), | |
| 59 | + null, new SmsSendRespDTO().setSerialNo(StrUtil.uuid()), codeMapping); | |
| 60 | + } | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * 构建请求地址 | |
| 64 | + * | |
| 65 | + * 参见 https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71 文档 | |
| 66 | + * | |
| 67 | + * @param path 请求路径 | |
| 68 | + * @return 请求地址 | |
| 69 | + */ | |
| 70 | + @SuppressWarnings("SameParameterValue") | |
| 71 | + private String buildUrl(String path) { | |
| 72 | + // 生成 timestamp | |
| 73 | + long timestamp = System.currentTimeMillis(); | |
| 74 | + // 生成 sign | |
| 75 | + String secret = properties.getApiSecret(); | |
| 76 | + String stringToSign = timestamp + "\n" + secret; | |
| 77 | + byte[] signData = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.bytes(secret)).digest(stringToSign); | |
| 78 | + String sign = Base64.encode(signData); | |
| 79 | + // 构建最终 URL | |
| 80 | + return String.format("https://oapi.dingtalk.com/%s?access_token=%s×tamp=%d&sign=%s", | |
| 81 | + path, properties.getApiKey(), timestamp, sign); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Override | |
| 85 | + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { | |
| 86 | + throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调"); | |
| 87 | + } | |
| 88 | + | |
| 89 | + @Override | |
| 90 | + protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) { | |
| 91 | + SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") | |
| 92 | + .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(""); | |
| 93 | + return SmsCommonResult.build("0", "success", null, data, codeMapping); | |
| 94 | + } | |
| 95 | + | |
| 96 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import cn.hutool.core.util.URLUtil; | |
| 7 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 11 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; | |
| 13 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum; | |
| 14 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 15 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 16 | +import com.fasterxml.jackson.annotation.JsonFormat; | |
| 17 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
| 18 | +import com.google.common.annotations.VisibleForTesting; | |
| 19 | +import com.yunpian.sdk.YunpianClient; | |
| 20 | +import com.yunpian.sdk.constant.YunpianConstant; | |
| 21 | +import com.yunpian.sdk.model.Result; | |
| 22 | +import com.yunpian.sdk.model.Template; | |
| 23 | +import lombok.Data; | |
| 24 | +import lombok.extern.slf4j.Slf4j; | |
| 25 | + | |
| 26 | +import java.util.*; | |
| 27 | +import java.util.function.Function; | |
| 28 | +import java.util.function.Supplier; | |
| 29 | +import java.util.stream.Collectors; | |
| 30 | + | |
| 31 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 32 | +import static cn.iocoder.dashboard.util.date.DateUtils.TIME_ZONE_DEFAULT; | |
| 33 | + | |
| 34 | +/** | |
| 35 | + * 云片短信客户端的实现类 | |
| 36 | + * | |
| 37 | + * @author zzf | |
| 38 | + * @date 9:48 2021/3/5 | |
| 39 | + */ | |
| 40 | +@Slf4j | |
| 41 | +public class YunpianSmsClient extends AbstractSmsClient { | |
| 42 | + | |
| 43 | + /** | |
| 44 | + * 云信短信客户端 | |
| 45 | + */ | |
| 46 | + private volatile YunpianClient client; | |
| 47 | + | |
| 48 | + public YunpianSmsClient(SmsChannelProperties properties) { | |
| 49 | + super(properties, new YunpianSmsCodeMapping()); | |
| 50 | + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @Override | |
| 54 | + public void doInit() { | |
| 55 | + YunpianClient oldClient = client; | |
| 56 | + // 初始化新的客户端 | |
| 57 | + YunpianClient newClient = new YunpianClient(properties.getApiKey()); | |
| 58 | + newClient.init(); | |
| 59 | + this.client = newClient; | |
| 60 | + // 销毁老的客户端 | |
| 61 | + if (oldClient != null) { | |
| 62 | + oldClient.close(); | |
| 63 | + } | |
| 64 | + } | |
| 65 | + | |
| 66 | + @Override | |
| 67 | + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, | |
| 68 | + String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable { | |
| 69 | + return invoke(() -> { | |
| 70 | + Map<String, String> request = new HashMap<>(); | |
| 71 | + request.put(YunpianConstant.MOBILE, mobile); | |
| 72 | + request.put(YunpianConstant.TPL_ID, apiTemplateId); | |
| 73 | + request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams)); | |
| 74 | + request.put(YunpianConstant.UID, String.valueOf(sendLogId)); | |
| 75 | + request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl()); | |
| 76 | + return client.sms().tpl_single_send(request); | |
| 77 | + }, response -> new SmsSendRespDTO().setSerialNo(String.valueOf(response.getSid()))); | |
| 78 | + } | |
| 79 | + | |
| 80 | + private static String formatTplValue(List<KeyValue<String, Object>> templateParams) { | |
| 81 | + if (CollUtil.isEmpty(templateParams)) { | |
| 82 | + return ""; | |
| 83 | + } | |
| 84 | + // 参考 https://www.yunpian.com/official/document/sms/zh_cn/introduction_demos_encode_sample 格式化 | |
| 85 | + StringJoiner joiner = new StringJoiner("&"); | |
| 86 | + templateParams.forEach(param -> joiner.add(String.format("#%s#=%s", param.getKey(), URLUtil.encode(String.valueOf(param.getValue()))))); | |
| 87 | + return joiner.toString(); | |
| 88 | + } | |
| 89 | + | |
| 90 | + @Override | |
| 91 | + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { | |
| 92 | + List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); | |
| 93 | + return statuses.stream().map(status -> { | |
| 94 | + SmsReceiveRespDTO resp = new SmsReceiveRespDTO(); | |
| 95 | + resp.setSuccess(Objects.equals(status.getReportStatus(), "SUCCESS")); | |
| 96 | + resp.setErrorCode(status.getErrorMsg()).setErrorMsg(status.getErrorDetail()); | |
| 97 | + resp.setMobile(status.getMobile()).setReceiveTime(status.getUserReceiveTime()); | |
| 98 | + resp.setSerialNo(String.valueOf(status.getSid())).setLogId(status.getUid()); | |
| 99 | + return resp; | |
| 100 | + }).collect(Collectors.toList()); | |
| 101 | + } | |
| 102 | + | |
| 103 | + @Override | |
| 104 | + protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable { | |
| 105 | + return invoke(() -> { | |
| 106 | + Map<String, String> request = new HashMap<>(); | |
| 107 | + request.put(YunpianConstant.APIKEY, properties.getApiKey()); | |
| 108 | + request.put(YunpianConstant.TPL_ID, apiTemplateId); | |
| 109 | + return client.tpl().get(request); | |
| 110 | + }, response -> { | |
| 111 | + Template template = response.get(0); | |
| 112 | + return new SmsTemplateRespDTO().setId(String.valueOf(template.getTpl_id())).setContent(template.getTpl_content()) | |
| 113 | + .setAuditStatus(convertSmsTemplateAuditStatus(template.getCheck_status())).setAuditReason(template.getReason()); | |
| 114 | + }); | |
| 115 | + } | |
| 116 | + | |
| 117 | + @VisibleForTesting | |
| 118 | + Integer convertSmsTemplateAuditStatus(String checkStatus) { | |
| 119 | + switch (checkStatus) { | |
| 120 | + case "CHECKING": return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); | |
| 121 | + case "SUCCESS": return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); | |
| 122 | + case "FAIL": return SmsTemplateAuditStatusEnum.FAIL.getStatus(); | |
| 123 | + default: throw new IllegalArgumentException(String.format("未知审核状态(%s)", checkStatus)); | |
| 124 | + } | |
| 125 | + } | |
| 126 | + | |
| 127 | + @VisibleForTesting | |
| 128 | + <T, R> SmsCommonResult<R> invoke(Supplier<Result<T>> requestConsumer, Function<T, R> responseConsumer) throws Throwable { | |
| 129 | + // 执行请求 | |
| 130 | + Result<T> result = requestConsumer.get(); | |
| 131 | + if (result.getThrowable() != null) { | |
| 132 | + throw result.getThrowable(); | |
| 133 | + } | |
| 134 | + // 解析结果 | |
| 135 | + R data = null; | |
| 136 | + if (result.getData() != null) { | |
| 137 | + data = responseConsumer.apply(result.getData()); | |
| 138 | + } | |
| 139 | + // 拼接结果 | |
| 140 | + return SmsCommonResult.build(String.valueOf(result.getCode()), formatResultMsg(result), null, data, codeMapping); | |
| 141 | + } | |
| 142 | + | |
| 143 | + private static String formatResultMsg(Result<?> sendResult) { | |
| 144 | + if (StrUtil.isEmpty(sendResult.getDetail())) { | |
| 145 | + return sendResult.getMsg(); | |
| 146 | + } | |
| 147 | + return sendResult.getMsg() + " => " + sendResult.getDetail(); | |
| 148 | + } | |
| 149 | + | |
| 150 | + /** | |
| 151 | + * 短信接收状态 | |
| 152 | + * | |
| 153 | + * 参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档 | |
| 154 | + * | |
| 155 | + * @author 芋道源码 | |
| 156 | + */ | |
| 157 | + @Data | |
| 158 | + public static class SmsReceiveStatus { | |
| 159 | + | |
| 160 | + /** | |
| 161 | + * 接收状态 | |
| 162 | + * | |
| 163 | + * 目前仅有 SUCCESS / FAIL,所以使用 Boolean 接收 | |
| 164 | + */ | |
| 165 | + @JsonProperty("report_status") | |
| 166 | + private String reportStatus; | |
| 167 | + /** | |
| 168 | + * 接收手机号 | |
| 169 | + */ | |
| 170 | + private String mobile; | |
| 171 | + /** | |
| 172 | + * 运营商返回的代码,如:"DB:0103" | |
| 173 | + * | |
| 174 | + * 由于不同运营商信息不同,此字段仅供参考; | |
| 175 | + */ | |
| 176 | + @JsonProperty("error_msg") | |
| 177 | + private String errorMsg; | |
| 178 | + /** | |
| 179 | + * 运营商反馈代码的中文解释 | |
| 180 | + * | |
| 181 | + * 默认不推送此字段,如需推送,请联系客服 | |
| 182 | + */ | |
| 183 | + @JsonProperty("error_detail") | |
| 184 | + private String errorDetail; | |
| 185 | + /** | |
| 186 | + * 短信编号 | |
| 187 | + */ | |
| 188 | + private Long sid; | |
| 189 | + /** | |
| 190 | + * 用户自定义 id | |
| 191 | + * | |
| 192 | + * 这里我们传递的是 SysSmsLogDO 的日志编号 | |
| 193 | + */ | |
| 194 | + private Long uid; | |
| 195 | + /** | |
| 196 | + * 用户接收时间 | |
| 197 | + */ | |
| 198 | + @JsonProperty("user_receive_time") | |
| 199 | + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) | |
| 200 | + private Date userReceiveTime; | |
| 201 | + | |
| 202 | + } | |
| 203 | + | |
| 204 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsCodeMapping.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping; | |
| 5 | + | |
| 6 | +import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.SUCCESS; | |
| 7 | +import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*; | |
| 8 | +import static com.yunpian.sdk.constant.Code.*; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 云片的 SmsCodeMapping 实现类 | |
| 12 | + * | |
| 13 | + * 参见 https://www.yunpian.com/official/document/sms/zh_CN/returnvalue_common 文档 | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +public class YunpianSmsCodeMapping implements SmsCodeMapping { | |
| 18 | + | |
| 19 | + @Override | |
| 20 | + public ErrorCode apply(String apiCode) { | |
| 21 | + int code = Integer.parseInt(apiCode); | |
| 22 | + switch (code) { | |
| 23 | + case OK: return SUCCESS; | |
| 24 | + case ARGUMENT_MISSING: return SMS_API_PARAM_ERROR; | |
| 25 | + case BAD_ARGUMENT_FORMAT: return SMS_TEMPLATE_PARAM_ERROR; | |
| 26 | + case TPL_NOT_FOUND: | |
| 27 | + case TPL_NOT_VALID: return SMS_TEMPLATE_INVALID; | |
| 28 | + case MONEY_NOT_ENOUGH: return SMS_ACCOUNT_MONEY_NOT_ENOUGH; | |
| 29 | + case BLACK_WORD: return SMS_SEND_CONTENT_INVALID; | |
| 30 | + case DUP_IN_SHORT_TIME: | |
| 31 | + case TOO_MANY_TIME_IN_5: | |
| 32 | + case DAY_LIMIT_PER_MOBILE: | |
| 33 | + case HOUR_LIMIT_PER_MOBILE: return SMS_SEND_BUSINESS_LIMIT_CONTROL; | |
| 34 | + case BLACK_PHONE_FILTER: return SMS_MOBILE_BLACK; | |
| 35 | + case SIGN_NOT_MATCH: | |
| 36 | + case BAD_SIGN_FORMAT: | |
| 37 | + case SIGN_NOT_VALID: return SMS_SIGN_INVALID; | |
| 38 | + case BAD_API_KEY: return SMS_ACCOUNT_INVALID; | |
| 39 | + case API_NOT_ALLOWED: return SMS_PERMISSION_DENY; | |
| 40 | + case IP_NOT_ALLOWED: return SMS_IP_DENY; | |
| 41 | + } | |
| 42 | + return SMS_UNKNOWN; | |
| 43 | + } | |
| 44 | + | |
| 45 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.enums; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ArrayUtil; | |
| 4 | +import lombok.AllArgsConstructor; | |
| 5 | +import lombok.Getter; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 短信渠道枚举 | |
| 9 | + * | |
| 10 | + * @author zzf | |
| 11 | + * @date 2021/1/25 10:56 | |
| 12 | + */ | |
| 13 | +@Getter | |
| 14 | +@AllArgsConstructor | |
| 15 | +public enum SmsChannelEnum { | |
| 16 | + | |
| 17 | + DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"), | |
| 18 | + YUN_PIAN("YUN_PIAN", "云片"), | |
| 19 | + ALIYUN("ALIYUN", "阿里云"), | |
| 20 | +// TENCENT("TENCENT", "腾讯云"), | |
| 21 | +// HUA_WEI("HUA_WEI", "华为云"), | |
| 22 | + ; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 编码 | |
| 26 | + */ | |
| 27 | + private final String code; | |
| 28 | + /** | |
| 29 | + * 名字 | |
| 30 | + */ | |
| 31 | + private final String name; | |
| 32 | + | |
| 33 | + public static SmsChannelEnum getByCode(String code) { | |
| 34 | + return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values()); | |
| 35 | + } | |
| 36 | + | |
| 37 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.enums; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 短信框架的错误码枚举 | |
| 7 | + * | |
| 8 | + * 短信框架,使用 2-001-000-000 段 | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +public interface SmsFrameworkErrorCodeConstants { | |
| 13 | + | |
| 14 | + ErrorCode SMS_UNKNOWN = new ErrorCode(2001000000, "未知错误,需要解析"); | |
| 15 | + | |
| 16 | + // ========== 权限 / 限流等相关 2001000100 ========== | |
| 17 | + | |
| 18 | + ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限"); | |
| 19 | + // 云片:可以配置 IP 白名单,只有在白名单中才可以发送短信 | |
| 20 | + ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信"); | |
| 21 | + | |
| 22 | + // 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。 | |
| 23 | + ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2001000102, "指定手机的发送限流"); | |
| 24 | + // 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。 | |
| 25 | + ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2001000103, "每天的发送限流"); | |
| 26 | + | |
| 27 | + ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2001000104, "短信内容有敏感词"); | |
| 28 | + | |
| 29 | + // ========== 模板相关 2001000200 ========== | |
| 30 | + ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2001000200, "短信模板不合法"); // 包括短信模板不存在 | |
| 31 | + ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2001000201, "模板参数不正确"); | |
| 32 | + | |
| 33 | + // ========== 签名相关 2001000300 ========== | |
| 34 | + ErrorCode SMS_SIGN_INVALID = new ErrorCode(2001000300, "短信签名不可用"); | |
| 35 | + | |
| 36 | + // ========== 账户相关 2001000400 ========== | |
| 37 | + ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2001000400, "账户余额不足"); | |
| 38 | + ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2001000401, "apiKey 不存在"); | |
| 39 | + | |
| 40 | + // ========== 其它相关 2001000900 开头 ========== | |
| 41 | + ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2001000900, "请求参数缺失"); | |
| 42 | + ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2001000901, "手机格式不正确"); | |
| 43 | + ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2001000902, "手机号在黑名单中"); | |
| 44 | + | |
| 45 | + ErrorCode EXCEPTION = new ErrorCode(2001000999, "调用异常"); | |
| 46 | + | |
| 47 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.enums; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 短信模板的审核状态枚举 | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@AllArgsConstructor | |
| 12 | +@Getter | |
| 13 | +public enum SmsTemplateAuditStatusEnum { | |
| 14 | + | |
| 15 | + CHECKING(1), | |
| 16 | + SUCCESS(2), | |
| 17 | + FAIL(3); | |
| 18 | + | |
| 19 | + private final Integer status; | |
| 20 | + | |
| 21 | +} | ... | ... |